Back to ER Diagram
RBAC & Permissions

RBAC & Permissions Logic

12-layer enterprise RBAC: identity, roles, ALLOW/DENY overrides, module gating, data scoping, delegation, escalation, SoD, policy exceptions, consent, status-based ACL, and audit

PostgreSQL + MongoDB
26 RDBMS Tables
3 Mongo Collections
12 Security Layers
19-Step Auth Flow

Overview

InfraTraq implements a Role-Based Access Control (RBAC) system with hierarchical permissions, multi-entity and multi-project scoping, MFA enforcement, session management, delegation of authority, and a comprehensive audit trail. The system governs access across all 1,220+ screens with granular per-feature, per-action permissions.


Login

MFA Check

Session

Role Lookup

Permission Check
Tenant Top-level organizational boundary
Entity Business entity / company within tenant
Project Specific construction project
Module Functional module (Procurement, Finance, etc.)
Feature Specific feature within a module
Action CRUD operation (create, read, update, delete, approve, export)
26
RDBMS Tables
12
Security Layers
19-Step
Auth Evaluation
3
Mongo Collections

Status States

StatusDescriptionAllowed ActionsNext States
ActiveUser account is active and can authenticateLogin, Access Resources, Update ProfileLocked, Suspended, Inactive
LockedAccount locked after failed login attemptsView Only (admin), Admin UnlockActive
SuspendedAccount temporarily suspended by admin actionView Only (admin), ReactivateActive, Inactive
InactiveAccount deactivated; no access permittedView Only (admin), ReactivateActive
MFA PendingMFA enabled but not yet verified on this sessionSubmit MFA Code, Cancel LoginActive (verified), Locked (failed)
DelegatedUser has active delegation granting additional permissionsAccess Delegated Resources, View DelegationActive (delegation expired)
Password ExpiredPassword age exceeds policy max_age_daysChange Password OnlyActive
TerminatedUser permanently removed from the systemAudit View Only

User-Role-Permission Model

User ↔ Role ↔ Permission Relationship

User
┌─────── N:M ───────┐
Role
── N:M ──
Permission
user_role (scoped by entity_id, project_id) role_permission

Key Principle

  • Users are assigned Roles scoped to a specific Entity and/or Project
  • Roles aggregate Permissions expressed as module.feature.action
  • A NULL entity/project scope means "all entities/projects"
  • Delegated permissions are merged at evaluation time, not permanently assigned

Database Schema

admin.user

  • user_id — PK, core user account identifier
  • username — Unique login name
  • email — User email address
  • first_name, last_name — User display name fields
  • password_hash — bcrypt-hashed password
  • is_active — Account active flag
  • is_locked — Account lockout flag
  • locked_until — Temporary lockout expiry (auto-unlock after cooldown)
  • mfa_enabled — Multi-factor authentication toggle
  • department_id — FK → organization.department (for data scoping)
  • reporting_to — FK → admin.user (self-referential, org hierarchy for escalation)

admin.role

  • role_id — PK, named role identifier
  • role_name — Display name of the role
  • role_code — Unique machine-readable code
  • is_system_role — Protected system role flag (cannot be deleted)

admin.permission

  • permission_id — PK, granular permission identifier
  • permission_code — Unique machine-readable code (e.g., PROC_PO_APPROVE)
  • permission_name — Human-readable display name
  • module — Functional module (procurement, finance, etc.)
  • feature — Feature within module (purchase_order, budget, etc.)
  • action — CRUD action (create, read, update, delete, approve, export)
  • resource — Target resource type

admin.role_permission

  • id — PK, many-to-many mapping
  • role_id — FK → admin.role
  • permission_id — FK → admin.permission

admin.user_role

  • id — PK, user-role assignment
  • user_id — FK → admin.user
  • role_id — FK → admin.role
  • entity_id — FK → organization.company_entity (scope)
  • project_id — FK → project.project (scope)
  • valid_from, valid_to — Validity period

admin.user_session

  • session_id — PK, active session tracker
  • user_id — FK → admin.user
  • token_hash — SHA-256 hash of JWT token
  • ip_address — Client IP address
  • device_type — Browser/device user-agent
  • expires_at — Session expiration timestamp

admin.mfa_config

  • mfa_id — PK, MFA configuration per user
  • user_id — FK → admin.user
  • mfa_type — TOTP, SMS, or backup code
  • is_verified — Whether MFA has been verified/activated

admin.delegation

  • delegation_id — PK, temporary permission delegation
  • delegator_id — FK → admin.user (person granting)
  • delegate_id — FK → admin.user (person receiving)
  • module_scope — Restrict delegation to specific module
  • entity_types — Restrict to specific entity types (PO, WO, invoice, etc.)
  • amount_limit — Maximum monetary authority (INR) for delegated approvals
  • valid_from, valid_to — Delegation validity window
  • created_by — FK → admin.user (who created the delegation record)
  • revoked_at, revoke_reason — Early termination tracking

admin.login_audit

  • id — PK, immutable authentication event log
  • user_id — FK → admin.user
  • action — Event type (login, mfa, permission_denied, etc.)
  • ip_address — Source IP of the event
  • status — success, failed, locked, etc.

admin.password_policy

  • policy_id — PK, organization-wide password rules
  • min_length — Minimum password length
  • max_age_days — Maximum password age before forced change
  • lockout_threshold — Failed attempts before account lockout

Layer 1 Enhancement — Identity & Authentication

admin.password_history

  • id — PK, previous password hash record
  • user_id — FK → admin.user
  • password_hash — bcrypt hash of a previously used password
  • created_at — When the password was set

admin.sso_provider

  • id — PK, SSO identity provider configuration
  • tenant_id — FK → admin.tenant
  • provider_code — Unique code (e.g., azure_ad, okta, google)
  • provider_name — Display name of the IdP
  • protocol — SAML 2.0, OIDC, or OAuth 2.0
  • client_id, client_secret_vault_key — OAuth credentials (secret stored in vault)
  • metadata_url — SAML metadata or OIDC discovery URL
  • config — JSONB additional configuration (claim mappings, scopes)
  • is_active — Provider enabled/disabled flag

admin.user_sso_link

  • id — PK, user-to-SSO-provider mapping
  • user_id — FK → admin.user
  • provider_id — FK → admin.sso_provider
  • external_id — User identifier in the external IdP
  • external_email — Email from the IdP assertion
  • last_login_at — Last SSO-based login timestamp

admin.api_key

  • id — PK, API key for service-to-service authentication
  • user_id — FK → admin.user (owner)
  • key_hash — SHA-256 hash of the API key (plain key shown once at creation)
  • name — Descriptive label for the key
  • scopes — JSON array of permitted scopes (e.g., ["read:projects","write:reports"])
  • rate_limit — Requests per minute limit
  • expires_at — Expiry date (mandatory, max 1 year)
  • last_used_at — Last activity timestamp
  • is_active — Revocable active flag

Layer 4 — Application / Module Access

admin.application

  • id — PK, registered application/module
  • app_code — Unique machine-readable code (e.g., PROCUREMENT, FINANCE)
  • app_name — Display name for navigation
  • route_url — Base URL path for the application
  • icon — Font Awesome icon class
  • display_order — Sidebar sort order
  • is_active — Module enabled/disabled per deployment

admin.user_app_access

  • id — PK, user-to-application junction
  • user_id — FK → admin.user
  • app_id — FK → admin.application
  • granted_by — FK → admin.user (who granted access)
  • granted_at — Timestamp of grant
  • is_active — Revocable flag

admin.user_consent

  • id — PK, GDPR/DPDPA consent record
  • user_id — FK → admin.user
  • consent_type — Type: data_processing, marketing, analytics, third_party_sharing
  • purpose — Human-readable purpose description
  • is_consented — Current consent state
  • consent_text — Full legal text presented to user
  • consented_at — When consent was given
  • consent_ip_address, consent_user_agent — Evidence of consent origin
  • withdrawn_at, withdrawal_reason — Withdrawal tracking
  • version — Consent version for re-consent on policy updates

Layer 8 — SoD Remediation

fraud.sod_remediation

  • id — PK, remediation action for SoD violation
  • violation_id — FK → fraud.sod_violation
  • remediation_type — remove_role, add_compensating_control, accept_risk, reassign_duty
  • description — Remediation action details
  • assigned_to — FK → admin.user (responsible person)
  • target_date — Expected completion date
  • completion_date — Actual completion date
  • evidence_url — Link to supporting evidence
  • status — pending, in_progress, completed, overdue

Layer 9 — Compliance & Policy Exceptions

compliance.policy_exception

  • id — PK, time-bound policy bypass request
  • tenant_id — FK → admin.tenant
  • policy_code, policy_name — Which policy is being excepted
  • entity_type, entity_id — Target entity (user, role, transaction)
  • exception_reason — Why the exception is needed
  • business_justification — Business case for the exception
  • risk_assessment — Risk level: low, medium, high, critical
  • mitigating_controls — Compensating controls in place
  • requested_by, approved_by — FK → admin.user
  • status — requested, approved, rejected, expired, revoked
  • valid_from, valid_to — Exception validity window

compliance.compliance_rule

  • id — PK, configurable compliance rule per tenant
  • tenant_id — FK → admin.tenant
  • rule_code, rule_name — Unique rule identifier and display name
  • category — financial, operational, security, regulatory
  • severity — info, warning, critical, blocking
  • applies_to — Target scope (e.g., all_users, finance_roles, admin_roles)
  • threshold_amount — Monetary threshold for rule trigger (INR)
  • condition_expression — JSONB rule expression evaluated at runtime
  • action_on_violation — warn, block, escalate, log_only
  • is_active — Rule enabled/disabled toggle

Layer 11 — Status-Based Access Control

system.status_master

  • id — PK, configurable status definition per module
  • tenant_id — FK → admin.tenant
  • module_type — Which module (purchase_order, work_order, invoice, etc.)
  • code, name — Machine code and display name
  • is_initial — Whether this is the default starting status
  • is_terminal — Whether this status is a final state (no further transitions)
  • allows_edit — Whether the entity can be edited in this status
  • display_order — Sort order in UI dropdowns

system.status_transition

  • id — PK, allowed state machine transition
  • from_status_id — FK → system.status_master (source state)
  • to_status_id — FK → system.status_master (target state)
  • required_role — Role code required to perform this transition
  • requires_approval — Whether transition needs approval workflow
  • auto_notify — Auto-send notification on transition
  • is_active — Transition rule enabled/disabled

Workflow — Approval Withdrawal

workflow.approval_withdrawal

  • id — PK, recall/withdraw a submitted approval request
  • entity_type, entity_id — The entity being recalled (PO, WO, invoice, etc.)
  • withdrawal_reason — Why the approval is being withdrawn
  • withdrawn_by — FK → admin.user (who initiated withdrawal)
  • status — requested, approved, rejected
  • approved_by — FK → admin.user (who approved the withdrawal)
  • new_status — Status the entity reverts to after withdrawal

MongoDB Audit Collections

session_lifecycle_log MongoDB • TTL: 90 days

  • session_id — Reference to admin.user_session
  • user_id, tenant_id — User and tenant context
  • event_type — session_created, session_renewed, session_expired, session_revoked
  • ip_address, device_info — Client context at event time
  • timestamp — Event timestamp with millisecond precision

sod_violation_log MongoDB • TTL: 7 years

  • violation_id — Reference to fraud.sod_violation
  • user_id, tenant_id — Who triggered the violation
  • conflicting_roles — Array of conflicting role pairs
  • conflicting_actions — Array of conflicting permission tuples
  • risk_score — Computed risk score (0-100)
  • detection_method — real_time, scheduled_scan, access_review
  • remediation_status — Tracking of remediation progress

compliance_audit_log MongoDB • TTL: 7 years

  • event_type — policy_exception_requested, rule_triggered, compliance_check_passed, etc.
  • entity_type, entity_id — What entity was involved
  • rule_code — Which compliance rule was evaluated
  • result — passed, warned, blocked, exception_applied
  • actor_id, tenant_id — Who performed the action
  • details — Full event context (JSONB)

Process Flow

Authentication & Authorization Flow


LOGIN
username + password

MFA CHECK
TOTP / SMS / backup

SESSION
JWT + claims

PERMISSION
module.feature.action

LOCKOUT
5 fails + audit

DENIED
bad MFA + audit

ALLOW
access granted

DENY
audit log

Delegation Flow


Delegator

delegation table
module_scope + valid_from/to

Delegate
merged permissions

Step-by-Step Logic

1

User Login

User submits credentials. The system validates the username and password_hash against admin.user. Rate limiting is applied (e.g., 10 attempts per minute). If is_locked = true, login is immediately rejected. Failed attempts are recorded in admin.login_audit.

2

MFA Verification

If mfa_enabled = true on the user record, the system requires a second factor from admin.mfa_config. Supported types: TOTP (authenticator app), SMS OTP, or backup codes. The session is not created until MFA is verified.

3

JWT Session Creation

On successful authentication, a JWT is generated containing claims for user_id, entity_id, and project_id scope. A record is created in admin.user_session with the token_hash, ip_address, device_type, and expires_at.

4

Per-Request Authorization

On each API request: validate the JWT token, extract the user_id, load all active roles from admin.user_role (filtered by current entity/project scope and validity period), then aggregate all permissions from admin.role_permission joined to admin.permission.

5

Permission Check

The requested action is expressed as module.feature.action (e.g., procurement.purchase_order.approve). The system checks whether this tuple exists in the user's effective permission set. If not found, the request is denied and logged.

6

Entity & Project Scoping

A user's role assignment in admin.user_role may be scoped to a specific entity_id and/or project_id. A NULL scope means "all entities/projects." The system filters permissions based on the request's target entity and project context.

7

Delegation Check

If the user has an active delegation in admin.delegation (where valid_from ≤ now ≤ valid_to), the delegated user's permissions (filtered by module_scope) are merged into the effective permission set. This enables temporary authority transfer during leave or travel.

8

Audit Logging

Every login attempt (success/failure), permission denial, critical action (role change, user lock/unlock, delegation), and session lifecycle event is logged to admin.login_audit. Logs are immutable and retained per compliance policy (SOX, ISO 27001).

Code Implementation

class AuthService {

  /** Authenticate user — validate credentials + check lockout + audit */
  async authenticate(username, password) {
    const user = await User.findOne({ username });
    if (!user) { await this.auditLog(null, 'login', 'user_not_found'); throw new AuthError(); }
    if (user.is_locked) { await this.auditLog(user.user_id, 'login', 'account_locked'); throw new AccountLockedError(); }
    const valid = await bcrypt.compare(password, user.password_hash);
    if (!valid) {
      await this.incrementFailedAttempts(user.user_id);
      await this.auditLog(user.user_id, 'login', 'invalid_password');
      throw new AuthError();
    }
    await this.auditLog(user.user_id, 'login', 'success');
    return user;
  }

  /** Verify MFA — TOTP validation against user's mfa_config */
  async verifyMFA(userId, code) {
    const config = await MfaConfig.findOne({ user_id: userId, is_verified: true });
    if (!config) throw new MfaNotConfiguredError();
    const valid = totp.verify({ token: code, secret: config.secret });
    if (!valid) { await this.auditLog(userId, 'mfa', 'failed'); throw new MfaError(); }
    await this.auditLog(userId, 'mfa', 'success');
    return true;
  }

  /** Create session — JWT generation with entity/project claims */
  async createSession(userId) {
    const roles = await this.getEffectiveRoles(userId);
    const token = jwt.sign(
      { user_id: userId, roles: roles.map(r => r.role_code) },
      process.env.JWT_SECRET, { expiresIn: '8h' }
    );
    await UserSession.create({
      user_id: userId, token_hash: sha256(token),
      ip_address: req.ip, device_type: req.headers['user-agent'],
      expires_at: new Date(Date.now() + 8 * 3600000)
    });
    return token;
  }
}

class PermissionService {

  /** Core permission check — module.feature.action against user's effective permissions */
  async hasPermission(userId, module, feature, action, entityId, projectId) {
    const roles = await this.getEffectiveRoles(userId, entityId, projectId);
    const roleIds = roles.map(r => r.role_id);
    const perms = await RolePermission.findAll({
      role_id: { $in: roleIds },
      include: [{ model: Permission, where: { module, feature, action } }]
    });
    if (perms.length === 0) {
      await this.auditLog(userId, 'permission_denied', `${module}.${feature}.${action}`);
      return false;
    }
    return true;
  }

  /** Get effective roles — union of direct roles + delegated roles */
  async getEffectiveRoles(userId, entityId, projectId) {
    const now = new Date();
    // Direct roles scoped to entity/project
    const direct = await UserRole.findAll({
      user_id: userId,
      entity_id: { $in: [entityId, null] },
      project_id: { $in: [projectId, null] },
      valid_from: { $lte: now }, valid_to: { $gte: now }
    });
    // Delegated roles from active delegations
    const delegations = await Delegation.findAll({
      delegate_id: userId, valid_from: { $lte: now }, valid_to: { $gte: now }
    });
    const delegatedRoles = [];
    for (const d of delegations) {
      const roles = await UserRole.findAll({ user_id: d.delegator_id });
      delegatedRoles.push(...roles.filter(r => !d.module_scope || r.module === d.module_scope));
    }
    return [...direct, ...delegatedRoles];
  }

  /** Enforce password policy — complexity + history check */
  async enforcePasswordPolicy(userId, newPassword) {
    const policy = await PasswordPolicy.findOne({ is_active: true });
    if (newPassword.length < policy.min_length) throw new PolicyError('too_short');
    if (!/[A-Z]/.test(newPassword)) throw new PolicyError('needs_uppercase');
    if (!/[0-9]/.test(newPassword)) throw new PolicyError('needs_digit');
    if (!/[!@#$%]/.test(newPassword)) throw new PolicyError('needs_special');
    // Check password history (last 5 passwords)
    const history = await PasswordHistory.findAll({ user_id: userId, limit: 5 });
    for (const h of history) {
      if (await bcrypt.compare(newPassword, h.hash)) throw new PolicyError('reused');
    }
    return true;
  }
}

Validation Rules

RuleConditionAction
Password ComplexityNew password does not meet policy (min length, uppercase, digit, special char)Reject with specific guidance
Session TimeoutSession exceeds configurable idle/absolute timeoutInvalidate token, require re-login
Max Concurrent SessionsUser exceeds max allowed active sessionsTerminate oldest session
IP-Based Access ControlLogin from non-whitelisted IP (for admin roles)Block access + notify security team
Failed Login Lockout5 consecutive failed login attemptsSet is_locked = true, require admin unlock
Self-Grant PreventionUser attempts to assign permissions to themselvesBlock action, log to audit trail
System Role ProtectionAttempt to delete a role where is_system_role = trueReject deletion, display warning

Automated Actions & Triggers

EventTrigger ConditionAuto Action
Failed Login (5x)5 consecutive failed password attemptsLock account (is_locked = true) + email admin notification
Password ExpiryPassword age exceeds max_age_days from policyForce password change on next login
Delegation CreatedNew record inserted in admin.delegationNotify delegate via email/SMS + log audit entry
Session Expiredexpires_at < NOW() on user_sessionCleanup token, remove session record
Role ChangedInsert/update/delete on admin.user_roleInvalidate cached permissions, force re-evaluation on next request
MFA EnabledUser enables MFA in profile settingsRequire MFA verification on next login before session creation

Integration Points

Authorization Gateway

  • Every Module — All 20+ modules query PermissionService before rendering screens and processing API calls
  • Workflow Engine — Approval routing uses roles to determine next approver in chain
  • Sidebar/Navigation — Menu items shown/hidden based on user's effective permissions

Compliance & Monitoring

  • Gamification — Security compliance scoring (MFA adoption, strong passwords) feeds the gamification engine
  • Audit Trail — SOX/ISO 27001 compliance via immutable login_audit records
  • Analytics — User activity dashboards, login patterns, permission usage heat maps
  • Notifications — Lockout alerts, delegation confirmations, password expiry reminders

Best Practices

Implementation Guidelines

  • Principle of Least Privilege — Assign the minimum permissions required for each role; avoid broad wildcard grants
  • Regular Access Reviews — Quarterly audit of user-role assignments; remove stale/orphaned roles promptly
  • MFA Enforcement — Mandatory MFA for all finance, admin, and approval-authority roles
  • Session Management — Configure idle timeout (30 min), absolute timeout (8 hrs), and max concurrent sessions (3)
  • Delegation Time-Boxing — All delegations must have explicit valid_from and valid_to dates; auto-expire when period ends
  • Cache permissions in Redis with short TTL (5 min); invalidate on role change events

Common Pitfalls

  • Granting entity-wide roles when project-scoped roles would suffice — violates least privilege
  • Forgetting to invalidate cached permissions after role changes — stale access for up to TTL duration
  • Open-ended delegations without valid_to — creates permanent shadow access
  • Not logging permission denials — missed indicator of potential security probing or misconfiguration

12-Layer Enterprise RBAC Model

InfraTraq implements a comprehensive 12-layer RBAC framework aligned with enterprise security standards (SOX, ISO 27001, DPDPA). Each layer builds on the previous, evaluated in sequence during every access request.

Layer 1: Identity & Authentication Password, SSO, API Keys, MFA
Layer 2: Roles & Permissions Hierarchical RBAC with entity/project scoping
Layer 3: ALLOW/DENY Overrides Explicit per-user permission overrides (DENY wins)
Layer 4: Application/Module Access Module-level navigation gating
Layer 5: Data Scoping (Row-Level) Tenant → Company → Department → Location → Own Record
Layer 6: Approval Delegation Temporary authority transfer with amount limits
Layer 7: Approval Escalation Timeout-based auto-escalation to next approver
Layer 8: Segregation of Duties Conflicting role detection + remediation
Layer 9: Policy Exceptions Time-bound bypass with risk assessment
Layer 10: Consent Tracking GDPR/DPDPA consent collection & withdrawal
Layer 11: Status-Based ACL State machine gating (edit/transition per status)
Layer 12: Audit & Compliance Immutable RDBMS + MongoDB audit trails

Layer 4 — Application / Module Access

Before checking fine-grained permissions, the system verifies whether the user has been granted access to the application/module itself. This controls sidebar visibility and navigation gating — users who don't have access to a module cannot see it in the sidebar or navigate to its screens.


User Request

Check
user_app_access

Module Not Granted?
Hide from sidebar

Module Granted
Proceed to Layer 5

Module Access Rules

  • Navigation Gating — Sidebar menu items are rendered only for modules where user_app_access.is_active = true for the current user
  • Feature Flagssystem.feature_flag can override module availability (e.g., disable ESG module for tenants without license)
  • Module Configsystem.module_config controls license_type and max_users per module per tenant
  • Additive Grant — No module access by default; must be explicitly granted by admin

Layer 5 — Data Scoping (Row-Level Security)

After verifying module access and permissions, the system applies row-level security to filter data based on the user's organizational scope. This ensures users only see records belonging to their authorized scope level.

Tenant Scope Top-level: isolates all data per tenant (SaaS boundary)
Company/Entity Scope User sees data for assigned entities via user_role.entity_id
Department Scope User restricted to own department via user.department_id
Location Scope Site/location filtering for field-based roles
Own Record Scope User sees only records they created (created_by = user_id)
// Row-Level Security Middleware
function applyDataScope(query, user, scopeLevel) {
  // Always apply tenant isolation (mandatory)
  query.where.tenant_id = user.tenant_id;

  switch (scopeLevel) {
    case 'entity':
      query.where.entity_id = { $in: user.assigned_entity_ids };
      break;
    case 'department':
      query.where.department_id = user.department_id;
      break;
    case 'location':
      query.where.location_id = { $in: user.assigned_location_ids };
      break;
    case 'own':
      query.where.created_by = user.user_id;
      break;
    // 'all' = no additional filter (tenant scope only)
  }
  return query;
}

Scope Inheritance

  • Scope is determined by the narrowest level assigned to the user's role
  • A user with entity scope cannot see records from other entities even if they have admin permissions
  • Super Admin role bypasses data scoping (tenant scope only)
  • Reporting roles may have wider scope than operational roles for the same module

Layer 8 — Segregation of Duties (Enhanced)

SoD enforcement prevents users from holding conflicting roles (e.g., "Create PO" + "Approve PO"). When conflicts are detected, they are logged, flagged for remediation, and tracked through completion. Enhanced with fraud.sod_violation columns and the new fraud.sod_remediation table.


Role Assignment
assign new role

SoD Check
compare conflict matrix

Conflict Found?
log violation

Remediation
assign action

Enhanced SoD Violation Fields

  • conflicting_actions — Array of conflicting permission tuples
  • entity_type / entity_id — What entity triggered the violation
  • violation_type — real_time, scheduled_scan, access_review
  • exception_granted — Whether a policy exception overrides this violation
  • exception_reason — Business justification for the exception

Remediation Workflow

  • remove_role — Remove one of the conflicting roles
  • add_compensating_control — Add dual-approval, additional audit, etc.
  • accept_risk — Document risk acceptance with management sign-off
  • reassign_duty — Reassign one of the conflicting duties to another user
  • All remediations are tracked with target_date, completion_date, and evidence_url

Layer 9 — Policy Exceptions

Policy exceptions provide a controlled, auditable mechanism to temporarily bypass security rules. Each exception requires business justification, risk assessment, management approval, and has an explicit validity window. Managed through the compliance schema.


Request
exception needed

Risk Assessment
low/med/high/critical

Approval
manager sign-off

Time-Bound Grant
valid_from → valid_to

Exception Lifecycle

  • requested — User submits exception with business justification
  • approved — Manager/compliance officer approves with mitigating controls
  • active — Exception is in effect (valid_from ≤ now ≤ valid_to)
  • expired — Validity window has passed; auto-revoked
  • revoked — Manually revoked before expiry

Compliance Rules Engine

  • compliance_rule defines configurable rules per tenant
  • Categories: financial, operational, security, regulatory
  • Severity levels: info, warning, critical, blocking
  • Actions: warn (allow + alert), block (deny), escalate (route to compliance officer), log_only
  • Threshold-based: e.g., block PO approval if amount > ₹50,00,000 without dual sign-off

Layer 10 — Consent Tracking

GDPR and India's Digital Personal Data Protection Act (DPDPA) 2023 require explicit, informed consent before processing personal data. InfraTraq tracks consent at the individual level with full audit trail of consent and withdrawal events.

Consent Types

  • data_processing — Core system data processing (required for system use)
  • marketing — Marketing communications and promotions
  • analytics — Usage analytics and performance tracking
  • third_party_sharing — Data sharing with external partners
  • biometric — Biometric data collection (site attendance)

Consent Requirements

  • Consent must be explicit (opt-in, not pre-checked)
  • Full consent text must be recorded verbatim
  • consent_ip_address and consent_user_agent provide evidence of origin
  • Withdrawal must be as easy as granting consent
  • Policy version changes trigger re-consent requests
  • 7-year retention per DPDPA compliance

DPDPA & GDPR Alignment

The system presents consent collection UI at first login and on policy version changes. Users can view and manage their consents from Account Settings → Privacy & Consent. Withdrawal of required consents triggers account restriction (read-only mode) until consent is re-granted or data deletion is requested.

Layer 11 — Status-Based Access Control

Beyond role-based permissions, entity access is further gated by the current status of the entity. A Purchase Order in "Approved" status cannot be edited by anyone, regardless of their role. Status transitions are governed by a configurable state machine stored in system.status_master and system.status_transition.


Draft
allows_edit: true

Submitted
allows_edit: false

Approved
allows_edit: false

Closed
is_terminal: true
// Status-Based ACL Check
async function canTransition(userId, entityType, entityId, targetStatus) {
  const entity = await getEntity(entityType, entityId);
  const currentStatus = await StatusMaster.findById(entity.status_id);

  // Terminal status — no further transitions allowed
  if (currentStatus.is_terminal) return { allowed: false, reason: 'terminal_status' };

  // Check if transition is defined in state machine
  const transition = await StatusTransition.findOne({
    from_status_id: currentStatus.id,
    to_status_id: targetStatus.id,
    is_active: true
  });
  if (!transition) return { allowed: false, reason: 'invalid_transition' };

  // Verify user has required role for this transition
  const userRoles = await getUserRoleCodes(userId);
  if (!userRoles.includes(transition.required_role))
    return { allowed: false, reason: 'insufficient_role' };

  // Check if transition requires approval workflow
  if (transition.requires_approval)
    return { allowed: true, requires_approval: true };

  return { allowed: true, requires_approval: false };
}

Key Rules

  • allows_edit determines if fields can be modified in a given status
  • is_terminal marks end-of-lifecycle statuses (no further transitions)
  • Transitions require specific required_role (e.g., only Approver can move to "Approved")
  • auto_notify sends notifications on status change to relevant stakeholders
  • The state machine is configurable per tenant via tenant_id scoping

Layer 12 — Audit & MongoDB Mapping

InfraTraq uses a dual-database audit architecture: PostgreSQL for structured, transactional audit records and MongoDB for high-volume, JSON-rich event logs. This provides both relational integrity and the flexibility of document-based storage for complex audit payloads.

Audit DomainPostgreSQL (RDBMS)MongoDB CollectionRetention
Authentication Eventsadmin.login_auditauth_events2 years / 7 years
Session Lifecycleadmin.user_sessionsession_lifecycle_logActive / 90 days
Permission Denialsadmin.login_audit (action=denied)auth_events2 years / 7 years
Role & Permission Changesadmin.user_role, admin.role_permissionentity_change_logPermanent / 7 years
SoD Violationsfraud.sod_violationsod_violation_logPermanent / 7 years
Compliance Eventscompliance.policy_exception, compliance.compliance_rulecompliance_audit_logPermanent / 7 years
Entity CRUD OperationsPer-module tables (history records)entity_change_logPermanent / 7 years
Consent Eventsadmin.user_consentcompliance_audit_log7 years (DPDPA)
API Key Usageadmin.api_key.last_used_atapi_request_logRolling / 90 days

Audit Immutability

  • MongoDB audit collections are append-only — no update or delete operations permitted
  • PostgreSQL audit tables use INSERT-only triggers; updates create new history rows
  • All audit records include actor_id, ip_address, timestamp, and tenant_id
  • JSON old_values / new_values stored for field-level change tracking
  • Cross-cutting audit viewer (Screen 1033) provides unified search across both databases

19-Step Permission Evaluation Flowchart

Every access request passes through this complete 19-step evaluation sequence. The flow implements a DENY-wins algorithm where explicit denials at any layer override all grants.

1

Authenticate Identity

Validate credentials (password / SSO token / API key) against admin.user, admin.sso_provider, or admin.api_key. Check is_active, is_locked, and locked_until.

2

Check Password Expiry

Compare password_updated_at against password_policy.max_age_days. If expired, force password change. Check admin.password_history to prevent reuse of last N passwords.

3

Verify MFA

If mfa_enabled = true, require TOTP/SMS/backup code from admin.mfa_config. Fail → increment attempts → lock if threshold exceeded.

4

Establish Session

Create JWT with claims (user_id, tenant_id, entity_ids, role_codes). Insert into admin.user_session. Log to MongoDB session_lifecycle_log.

5

Check Consent Status

Verify required consents exist in admin.user_consent. If consent version mismatch, present re-consent UI. Block access to personal data features until consent is granted.

6

Resolve Tenant Context

Extract tenant_id from JWT claims. All subsequent queries are filtered by tenant (absolute isolation boundary for SaaS).

7

Check Module Access

Query admin.user_app_access for the target module. If no active grant exists, deny access and hide module from navigation. Also check system.feature_flag and system.module_config.license_type.

8

Load User Roles

Fetch all active roles from admin.user_role where valid_from ≤ now ≤ valid_to, scoped to the current entity and project context.

9

Merge Delegated Roles

Check admin.delegation for active delegations where user is the delegate. Merge delegated permissions (filtered by module_scope and amount_limit) into the effective set.

10

Aggregate Permissions

Join admin.role_permissionadmin.permission to build the full set of module.feature.action tuples for all effective roles.

11

Apply ALLOW/DENY Overrides

Check admin.user_permission_override for explicit ALLOW or DENY entries. DENY always wins — an explicit DENY overrides any role-based ALLOW. Priority: Explicit DENY > Explicit ALLOW > Role Grant > Default Deny.

12

Check Permission Tuple

Verify the requested module.feature.action exists in the resolved permission set. If not found → DENY and log to admin.login_audit.

13

Apply Data Scoping

Apply row-level security filter (tenant → entity → department → location → own record) based on the user's scope level. Filter query results accordingly.

14

Check SoD Constraints

For sensitive operations (approvals, financial transactions), run SoD check against fraud.sod_rule. If conflict detected, log to fraud.sod_violation and MongoDB sod_violation_log. Block or warn per rule configuration.

15

Evaluate Compliance Rules

Run active rules from compliance.compliance_rule. Check thresholds (e.g., PO amount > ₹50L requires dual approval). Action: warn, block, escalate, or log_only per rule severity.

16

Check Policy Exceptions

If blocked by SoD or compliance rule, check compliance.policy_exception for an active, approved exception covering this entity/action within the validity window.

17

Check Entity Status

For write/transition operations, verify the entity's current status allows the action via system.status_master.allows_edit and system.status_transition. Terminal statuses block all modifications.

18

Execute Action

All 17 checks passed — execute the requested operation. For state transitions, update the entity status and trigger auto_notify if configured on the transition rule.

19

Audit Trail

Log the complete action to both PostgreSQL (admin.login_audit + entity history tables) and MongoDB (entity_change_log with JSON old_values/new_values). Include actor, timestamp, IP, and full change payload.

DENY-Wins Resolution Algorithm

  • Priority 1 (Highest): Explicit DENY override in user_permission_override → immediate deny, no further checks
  • Priority 2: Explicit ALLOW override in user_permission_override → grant access (unless blocked by Priority 1)
  • Priority 3: Role-based ALLOW from role_permission aggregation → grant access
  • Priority 4 (Default): If no match at any priority → Default Deny (closed-world assumption)