Authentication Boundary
The Novusoft authentication boundary is a source-defined sequence: submitted credentials are verified against an active account and role, a successful result becomes session context, and Startup uses that context for selected module access checks. The source does not prove comprehensive route protection, per-record ownership checks, deployment reachability, or abuse controls.
Credential-to-session flow
flowchart TD
n1["Auth::sign_in_process<br/>Arrange post data"]
n2["AuthModel::authCredentials<br/>Extract credentials"]
n3["Account lookup<br/>User and role records"]
n4["Credential and status checks<br/>Password, account, role"]
n5["Authentication result<br/>Success or message"]
n6["Session establishment<br/>Store authentication context"]
n7["JSON response<br/>Return data"]
n1 --> n2
n2 --> n3
n3 --> n4
n4 --> n5
n5 -->|success| n6
n5 -->|failure| n7
n6 --> n7
In imspr/novusoft/modules/Auth/Controllers/Auth.php, Auth::sign_in_process obtains post data with Helper::arrangePost() and passes it to AuthModel::authCredentials. In imspr/novusoft/modules/Auth/Models/AuthModel.php, AuthModel::authCredentials extracts the submitted array before building the lookup.
The lookup joins tbl_users to tbl_user_roles, accepts either login_email or username, and requires both records to have deleted = 0. The supplied username is passed through two SQL placeholders. When a row exists, the supplied password is hashed with SHA-256 and the row's hash_key, then compared with the stored password. A matching password still requires both the user status (s1) and role status (s2) to be truthy.
On success, Auth::sign_in_process stores the returned session array, records a sign-in event, removes the nested session value from the response data, and returns the remaining data through setJSON. The source does not establish an exact HTTP method or a stable machine-facing endpoint contract.
Auth::sign_out captures the current session, destroys it, records a sign-out event with the captured data, and sets a Location header for auth/sign-in.
Session and result contract
The successful session context is constructed by AuthModel::authCredentials and consumed downstream through the loggedIn value.
| Session entry | Source-defined value |
|---|---|
loggedIn |
true |
user_role_id |
The matched role identifier |
user_id |
The matched user identifier |
user_id_hash |
The matched user identifier encoded with Hashids |
full_name |
The user's full name |
position |
The user's job title |
username |
The matched username |
manual_user_role |
The matched manual role value |
health_facility |
The user's facility value |
main_account |
true when health_facility is ALL FACILITY |
health_facility_arr |
The facility value split on | |
brgy |
The matched barangay value |
dashboard |
The dashboard value split on | |
div, section, unit, role, dept |
Organizational fields from the matched user row |
The result starts with success = false and the message Invalid Username or Password. Its source-defined outcomes are:
| Condition | Result |
|---|---|
| No lookup row or password comparison does not match | The initial failure result remains |
| Password matches, but either account or role status is falsy | success remains false and the message becomes Account/Role disabled |
| Password matches and both status values are truthy | success becomes true and the session context is added |
Startup access gate
imspr/app/Config/Filters.php aliases startup to \Filters\Startup::class and includes startup in the global before filter list. This is a source-defined registration; actual request execution and deployment reachability are not observed here.
imspr/novusoft/filters/Startup.php reads the first URI segment and applies these source-defined branches:
- For the empty/root segment, it first calls Startup::handleLogin(). An unauthenticated request exits from that call after session destruction and a
Locationredirect toauth/sign-in; if the call returns, the filter sets aLocationheader fordashboardand then continues into the non-authelsebranch. - For the
authsegment, it reads the second segment. Onlyauth/sign-incalls Startup::handleLogin(true). - In the non-
authelsebranch—including the root request after its dashboard-header step—the filter resolves component segments when applicable, normalizes an empty segment toDMODULE, performs module lookup, and then evaluatesmodule->beor whether the first segment isjson. - When that module or JSON condition is met, it calls Startup::handleLogin(), obtains permission with MainModel::getPermission, stores the result in
$GLOBALS['permission'], and throws PageNotFoundException whenpermission['all']is false.
Startup::handleLogin treats a missing loggedIn session key as false. A logged-in request with toDashboard redirects to BASEURL.DMODULE and exits. An unauthenticated request that is not targeting the sign-in page destroys the session, redirects to auth/sign-in, and exits.
Within the module/JSON branch, module lookup occurs before that branch's Startup::handleLogin() gate, while the permission lookup follows it. The branch conditions do not prove that every Auth method or every route is protected. Authentication also does not establish ownership of an individual record.
Role and stage permission presentation
The role editor in imspr/novusoft/include/user-role/editor.php presents the permission data used to configure a role. It is a presentation and configuration surface, not proof that every downstream operation enforces the displayed choices.
[
{
"title": "Module actions",
"body": "The editor selects modules where `be = 1` and orders them by `level` and `order`. For non-add actions, the role-permission join uses the selected `primary_id` or a null role-permission row; add mode selects rows with a null role-permission role. Each module's `default_permission` is split on `|`. In add mode, those actions are initialized as enabled. For other modes, decoded `actions` values overwrite the defaults when present. Add and edit contexts render controls; other contexts render each action as YES or NO."
},
{
"title": "Workflow stages",
"body": "The editor presents the stages `PR`, `OBR`, `RFQ`, `TWG`, `ABSTRACT`, `BAC`, `PO`, `CMO`, `PO_RELEASE`, `GSO`, `SUP_REQ`, `ACCTG`, `CHECK`, and `TREASURER`. Each stage has the actions `update`, `comment`, `pending`, `for-checking`, `approved`, and `denied`. The matrix starts with each declared stage/action value enabled. For non-add actions, decoded `main_data['stages']` values overwrite matching entries before the editor renders controls or YES/NO text."
}
]
[!WARNING] Several access-safety boundaries remain unresolved in the inspected source:
- The
csrfalias exists, but its entry in the globalbeforelist is commented out. This configuration does not activate CSRF through that list; enable or verify an intended CSRF boundary before exposing state-changing requests.Startupdoes not prove comprehensive protection for every route orAuthmethod, and no per-record ownership check is established here. Verify those boundaries separately before treating authentication as authorization for selected records.- The role editor concatenates
primary_idinto its role-permission SQL selector. The inspected source does not establish validation or a parameterized boundary for that value.- The inspected authentication sources do not establish attempt throttling, rate limiting, lockout behavior, or a fallback/default credential override. Add or verify those controls at the deployed access boundary before relying on this flow against credential abuse.
Updated