Reference
The data-and-screen reference for the Security module's four admin tools — controllers, routes, models, views, migrations, tables, permission keys, menu entries, and audit-log events. Everything here is drawn from the live module source; see Research for the exact files.
Controllers
| Controller | Tool | Key methods |
|---|---|---|
Device_management |
Device Management — Entries tab (page shell) | index (tabbed shell), list_data, ban_device_modal_form, ban_device, restore_device_modal_form, restore_device |
Device_management_groups |
Device Management — Groups tab | index (ajax-tab partial), list_data, modal_form, save, delete |
OffsiteAccess |
Offsite Access | index, modalForm, save, listData, delete |
Locked_Accounts |
Locked Accounts + Active Sessions | index, locked_accounts_view, active_sessions_view, list_data, clear_attempts, list_sessions_data, view_sessions_modal, logout_user_all_devices, delete_session, get_session_statistics |
Access_logs |
Access Device Logs — Logs tab (page shell) | index (tabbed shell), view, list_data, log_action, delete |
Access_devices |
Access Device Logs — Devices tab | index, list_data, modal_form, modal_form_credential, generate_new_secret, save, delete, pass_list |
Access_device_categories |
Access Device Logs — Categories tab | index, list_data, modal_form, save, delete |
All seven extend App_Controller (staff, session-authenticated). Every constructor
opens with the module's single always-on guard with_module('security') — there are
no per-tool module_* sub-toggles left — and then applies its own permission:
Device Management on device_management; its Groups tab on
device_management_groups; Offsite Access on offsite_access; all three
Access Device controllers on access_logs; Locked Accounts on
locked_accounts (or the core staff_support).
Per-action gates sit on the individual methods: block_device on
ban_device_modal_form + ban_device, and unblock_device on
restore_device_modal_form + restore_device. The row builder
(make_row) checks the same two keys, so a user without them sees the row but no
action button.
Device_management::__construct() is a new gate. The core version of this
controller had no permission check on direct URL access — any signed-in staff member
could open /device_management by typing the address, and only the sidebar link was
gated, on a device_management key that no catalog row ever declared (so it was
ungrantable and the item was effectively admin-only). The key is now declared by this module
and enforced in the constructor, so it can be granted to a role and it actually blocks
direct access.
Routes
Declared in modules/Security/config/routes.php (mixed-case/underscore controllers
need explicit lowercase routes). The (:any) catch-alls map to controller methods.
| URL | Resolves to |
|---|---|
device_management | Device_management/index |
device_management/(:any) | Device_management/$1 |
device_management_groups | Device_management_groups/index |
device_management_groups/(:any) | Device_management_groups/$1 |
offsite_access | OffsiteAccess/index |
offsite_access/(:any) | OffsiteAccess/$1 |
locked_accounts | Locked_Accounts/index |
locked_accounts/(:any) · /(:any)/(:any) | Locked_Accounts/$1 · $1/$2 |
access_logs · access_logs/(:any) | Access_logs/index · $1 |
access_devices · access_devices/(:any) | Access_devices/index · $1 |
access_device_categories · …/(:any) | Access_device_categories/index · $1 |
/device_management and
/device_management_groups are byte-for-byte the addresses the core version used —
only the file that declares them moved (from application/config/routes.php
into modules/Security/config/routes.php). Existing links, bookmarks and in-view
get_uri() calls keep resolving. device_management_groups cannot be
swallowed by device_management/(:any) — CodeIgniter anchors each route key and
(:any) never matches across the missing /.
Models
Six models, all extending Crud_model and living in
modules/Security/models/. All SQL lives here — never in a controller or view.
| Model | Tool / table | Notes |
|---|---|---|
Banned_devices_model |
Device Management — banned_devices |
get_details, get_devices_with_ban_status. Also read by CORE
application/models/Users_model::authenticate() on every sign-in attempt —
do not rename or move it (see the callout below). |
Device_management_groups_model |
Device Management — device_management_groups |
get_details. Backs the Groups tab list and the group filter dropdown on the Entries tab. |
OffsiteAccessModel |
Offsite Access — offsite_access |
Also read by the core IP-restriction middleware when deciding whether to allow an off-network request. |
Access_logs_model |
Access Device Logs — access_logs |
The access-event history. |
Access_devices_model |
Access Device Logs — access_devices |
Registered physical devices and their API credentials. |
Access_device_categories_model |
Access Device Logs — access_device_categories |
Device categories. |
Banned_devices_model by name. The sign-in check in core
Users_model::authenticate() loads this module's model and looks up the
(user + user agent) pair on every sign-in attempt — that is what actually
rejects a banned device. Renaming the model, its get_details() signature, or the
banned_devices table would break sign-in for the whole tenant. In the other
direction the coupling was removed: get_devices_with_ban_status() absorbed
core's System_logs_model::get_system_logs_data_per_device(), so core no longer
queries this module's tables to build the list.
Views
19 view files under modules/Security/views/, namespaced one directory deep so the
loader cascade cannot collide with another module.
| View | Screen |
|---|---|
device_management/index.php | Device Management page shell + the Entries tab (filters, list, action buttons) |
device_management/ban_device_modal_form.php | Ban-device modal — the required Remarks/reason field |
device_management/restore_device_modal_form.php | Restore (unban) confirmation modal |
device_management/groups/index.php | Groups tab list (loaded as an ajax-tab partial) |
device_management/groups/modal_form.php | Group add / edit modal — Title, User Agent, Status |
offsite_access/index.php · modal_form.php | Offsite Access list and grant form |
locked_accounts/index.php · locked_accounts_tab.php · active_sessions_tab.php | Locked Accounts page shell and its two tabs |
active_sessions/view_sessions_modal.php | Per-user session detail modal |
access/index.php · logs/index.php · devices/* · categories/* | Access Device Logs shell and its Logs · Devices · Categories tabs (8 files) |
Database tables
Six tables, all shipping in the base-install schema and re-created idempotently by the
module migrations (tracked in migrations_security). All carry the standard
updated_at + deleted audit columns.
banned_devices
One row per active ban — a person plus the browser (user agent) they may no longer sign in from. Restoring a device soft-deletes its row.
| Column | Type | Notes |
|---|---|---|
id | int unsigned | PK |
user_id | int | The person who is blocked from this device |
http_agent | text | The browser / user-agent string being banned |
ip_address | varchar(45) | IP captured at the time of the ban |
remarks | text | The reason — required on the ban form |
created_by | int | Administrator who applied the ban |
date_created | datetime | UTC ban time |
updated_at · deleted | timestamp · tinyint | Audit + soft delete (a restore sets deleted) |
Indexed (user_id, http_agent(191), deleted) — that exact triple is the lookup core
Users_model::authenticate() runs on every sign-in attempt, so it carries a covering
index.
device_management_groups
Named user-agent groups used to label and filter the Entries list. A group grants nothing and blocks nothing.
| Column | Type | Notes |
|---|---|---|
id | int unsigned | PK |
uuid | varchar(64) | Per-group UUID |
title | varchar(150) | Group name (e.g. "Company Chromebooks") |
user_agent | text | The user-agent string the group matches |
status | varchar(20) | active / inactive |
created_by | int | Who created the group |
date_created | datetime | UTC create time |
updated_at · deleted | timestamp · tinyint | Audit + soft delete |
Indexed on (status, deleted) and on user_agent(191), which is joined
against system_logs.http_agent to bucket sign-in devices.
system_logs sign-in rows) and joining it to
banned_devices. The Active / Banned status is simply "does a live ban row exist for
this person + user agent". Nothing is written to any table when a device merely appears in the
list — only banning and restoring write, and they write to banned_devices alone.
offsite_access
| Column | Type | Notes |
|---|---|---|
id | int unsigned | PK |
uuid | varchar(36) | Per-grant UUID (v4) |
user_id | int | Staff member granted the window |
start_date / end_date | date | The exception window |
date_created | datetime | UTC create time |
created_by | int | Granting admin |
updated_at · deleted | timestamp · tinyint | Audit + soft delete |
access_device_categories
| Column | Type | Notes |
|---|---|---|
id | int unsigned | PK |
title | varchar(150) | Category name |
detail | text | Optional description |
status | tinyint | 1 = active |
updated_at · deleted | timestamp · tinyint | Audit + soft delete |
access_devices
| Column | Type | Notes |
|---|---|---|
id | int unsigned | PK |
api_key | varchar(36) | UUID the device presents |
api_secret | varchar(120) | Hashed secret (never shown/logged in clear) |
device_name | varchar(36) | Display name |
passes | text | Comma-separated staff user IDs allowed through |
remarks | text | Optional notes |
category_id | int | FK-by-value to access_device_categories |
labels | text | Labels |
status | tinyint | 1 = active |
updated_at · deleted | timestamp · tinyint | Audit + soft delete |
access_logs
| Column | Type | Notes |
|---|---|---|
id | int unsigned | PK |
device_id | int | Which device recorded the event |
user_id | int | Who accessed |
remarks | text | Optional (e.g. entry / exit) |
timestamp | datetime | UTC event time (shown local) |
updated_at · deleted | timestamp · tinyint | Audit + soft delete |
users (disable_login,
login_attempts, last_login_attempts); the Active Sessions tab
reads the core session store (ci_sessions) through the core session helper.
Migrations
Six module migrations in modules/Security/migrations/, run by
php erpat migrate:modules against the per-module migrations_security
version table. Every one is an idempotent guard: the tables already ship in the
base-install schema, so on an existing database each migration is a no-op
(table_exists() → return, then CREATE TABLE IF NOT EXISTS). They exist
so the module is self-contained for fresh installs and newly provisioned tenants. All are
reversible.
| Migration | Creates |
|---|---|
20260727100254_create_offsite_access_table.php | offsite_access |
20260727100255_create_access_device_categories_table.php | access_device_categories |
20260727100256_create_access_devices_table.php | access_devices |
20260727100257_create_access_logs_table.php | access_logs |
20260730213155_create_banned_devices_table.php | banned_devices (+ the covering sign-in index) |
20260730213156_create_device_management_groups_table.php | device_management_groups |
Retirement migrations live in CORE (they touch the shared settings table, so
they belong in application/migrations/, not here):
20260730213157_retire_device_management_setting.php carries any
module_device_management = 1 forward onto module_security and
then drops the orphaned row; 20260730204800_retire_core_submodule_settings.php did
the same for module_offsite_access and module_access in the previous
release. Nothing is lost — the tools simply stop having their own on/off rows.
Permission keys
| Key | Roles-editor group / action | Style |
|---|---|---|
device_management | Device Management: Module / Access | Module-level — new in 3.0.0 |
device_management_groups | Device Management: Groups / Enabled | Module-level |
device_management_groups_create · _update · _delete | Device Management: Groups / Create · Edit · Remove | Child |
block_device | Device Management: Block Device / Enabled | Simple (no children) |
unblock_device | Device Management: Unblock Device / Enabled | Simple (no children) |
offsite_access | Security: Offsite Access / Access | Dropdown (all / specific) |
offsite_access_create · _update · _delete | Security: Offsite Access / Create · Edit · Remove | Child |
locked_accounts | Security: Locked Accounts / View | Simple (no children) |
active_sessions | Security: Active Sessions / Manage | Module-level |
active_sessions_create · _update · _delete | Security: Active Sessions / Create · Edit · Remove | Child |
access_logs | Security: Access Logs / Enabled | Module-level |
access_logs_create · _update · _delete | Security: Access Logs / Create · Edit · Remove | Child |
Keys are unchanged from the core declaration. device_management_groups,
block_device and unblock_device keep the exact key names, categories and
actions they had as core rows, so serialized grants already stored in
users.permissions / roles.permissions keep resolving with no data
migration. The legacy rows were deleted from
application/helpers/permission_catalog_helper.php in the same change — a core row
shadows a module declaration, so leaving them would have kept the module's block inert.
Admins bypass every permission check.
Not owned here: staff_support (Staffing) and api_clients
(Security: API Clients) remain core-owned; the core IP-restriction block event
offsite_access:ip_security is emitted by core middleware, not this module.
Settings & menu
| Setting | Role |
|---|---|
module_security | The module's one and only enable setting — seeded on, can_disable=false, so it never appears as a switchable row in Manage Modules |
module_device_management | Retired in 3.0.0 — carried forward onto module_security, then dropped |
module_offsite_access | Retired in 2.0.0 — dropped |
module_access | Retired in 2.0.0 — dropped |
module_* row above is
gone from Settings → Manage Modules; the module has exactly one always-on setting,
module_security. Who sees Device Management, Offsite Access, Locked Accounts or
Access Logs is purely a permissions decision in Settings → Roles. Device
Management used to ship off for a brand-new tenant, so nobody could open it; it now
ships reachable by admins and by roles explicitly granted device_management.
No non-admin gains access on upgrade.
Menu entries (config/menu.php), in rendered order: Device Management
(fa-mobile, position 160) · Offsite Access (fa-globe, 161) ·
Locked Accounts (fa-shield, 162) · Access Logs
(fa-history, 163). Since Device Management moved in, this module owns the
whole Security sidebar section — the core slice in
application/config/left_menu.php was emptied and removed, so there is no longer a
core group to coalesce with (default_menu.php, position 160).
Audit-log events
Keyed action:component in config/system_logs.php, merged into the
core audit-log config. Written on every sensitive mutation.
| Event key | Severity | Meaning |
|---|---|---|
banned:banned_device | critical | A user's sign-in device was banned |
unbanned:banned_device | warning | A user's sign-in device ban was lifted |
created:device_management_group | info | Device group created |
updated:device_management_group | info | Device group updated |
deleted:device_management_group | warning | Device group deleted |
created:offsite_access | warning | Offsite window granted |
updated:offsite_access | warning | Offsite window updated |
deleted:offsite_access | warning | Offsite window revoked |
unlocked:locked_account | warning | Account unlocked (attempts cleared) |
logout:active_session | warning | All sessions terminated for a user |
deleted:active_session | warning | Single session terminated |
created:access_device | info | Device registered |
updated:access_device | info | Device updated |
deleted:access_device | warning | Device deleted |
rotated_secret:access_device | critical | Device API secret regenerated |
created:access_device_category | info | Category created |
updated:access_device_category | info | Category updated |
deleted:access_device_category | warning | Category deleted |
deleted:access_log | warning | Access log entry deleted |
banned:banned_device is rated critical). They appear in
Settings → System Logs under category Security, module Device Management.