Skip to content

Security and Permissions ​

NGB uses Keycloak as the identity provider and keeps application authorization metadata in the NGB database.

Keycloak owns authentication, SSO sessions, external user identity, and enabled/disabled identity-provider users. NGB owns application roles, permission assignments, effective access snapshots, access-version invalidation, menu filtering, metadata capabilities, report access, and security audit records.

Core Model ​

The authorization model is deny-by-default.

An authenticated Keycloak user is projected into platform_users. Application access is resolved from:

  • active platform_users row;
  • active platform_roles;
  • platform_role_permissions;
  • platform_user_roles;
  • platform_user_access_versions.

Permission keys have three parts:

text
resource_kind.resource_code.action_code

The resource code may contain dots because NGB document, catalog, report, and page codes are namespaced, for example:

text
document.pm.lease.view
report.pm.occupancy.summary.execute
system.users.manage

Enforcement Boundary ​

Backend enforcement is the source of truth.

The UI may hide menu items, disable actions, and show access-denied states, but every sensitive operation is checked by the backend:

  • main menu contributors are filtered by permission;
  • document and catalog metadata are filtered and returned with permission-aware capabilities;
  • document and catalog list/read/create/update/post/delete-style actions are checked by runtime wrappers;
  • report definitions, execution, export, variants, and cell actions are checked by report controllers;
  • command palette results are filtered server-side;
  • user and role management APIs require system permissions.

Users ​

Users are created and updated through the NGB backend. The backend calls Keycloak Admin REST API and then updates platform_users.

The normal UI does not hard-delete users. Production user lifecycle is:

  • create;
  • update;
  • deactivate;
  • reactivate.

This preserves audit history, report variants, ownership links, and foreign-key integrity.

Roles ​

Roles and permissions are stored in NGB tables. Property Management seeds default application roles such as administrator, accountant, AR/AP clerk, property manager, maintenance coordinator, auditor, and read-only user.

Seeders are additive: existing roles are not overwritten.

Administrators ​

An active administrator has all application permissions. The trusted Keycloak role ngb-admin grants this access without requiring a pre-existing NGB user record. A platform identity is created when an audited mutation needs it.

Application administrator roles in the database also receive all permissions from the platform definition registry. Property Management registers pm-administrator; CRM registers crm.administrator. Other verticals can register their canonical application administrator role codes through NgbAdministratorOptions.ApplicationRoleCodes. This uses exact role codes with case-insensitive comparison, not editable display names. Nonadministrator roles continue to use their explicit grants.

Canonical administrator roles are protected by the Role Management API. Their code cannot be changed, they cannot be deactivated, and their permission matrix cannot be replaced. Registered administrator codes are reserved: ordinary roles cannot be created or renamed to use them. Names and descriptions remain editable, and an existing inactive administrator role can be reactivated.

CRM bootstrap provisions its canonical administrator role as a system role without explicit permission grants and preserves an existing role. The HTTP endpoint for applying CRM defaults requires both system.roles.manage and system.users.manage.

Role details return hasFullAccess: true and the current registered permissions for these roles. The editor displays “Full access to all current and future permissions” and makes the matrix read-only. When updating an administrator's name or description, omit permissions or send null; an unchanged full permission set is also accepted without persisting it. Attempts to change protected properties return HTTP 409 with error code ngb.security.administrator_role_protected. These restrictions apply to direct API requests, including requests made by administrators.

Inactive users and inactive role assignments do not gain administrator access. Feature flags control deployment availability independently of permissions. Permission caches retain database grants by access version; the trusted Keycloak administrator claim is evaluated for each request and is not inherited from a previous request's cached snapshot.

For regular users, Keycloak roles are not the source of application authorization. Use NGB roles and permissions.

Configuration ​

The API registers the Keycloak Admin client only when KeycloakAdminClientSettings is configured. Typical settings are:

text
KeycloakAdminClientSettings:BaseUrl
KeycloakAdminClientSettings:Realm
KeycloakAdminClientSettings:ClientId
KeycloakAdminClientSettings:ClientSecret

The client also has process-wide resource boundaries. Defaults are suitable for a single API instance and can be tuned per deployment:

text
KeycloakAdminClientSettings:AdminBatchConcurrency=8
KeycloakAdminClientSettings:MaxConcurrentAdminRequests=16
KeycloakAdminClientSettings:MaxQueuedAdminRequests=256
KeycloakAdminClientSettings:MaxPendingUserLookups=128
KeycloakAdminClientSettings:MaxResponseContentBytes=1048576

Keep batch concurrency at or below the global request limit. A full queue is rejected immediately instead of consuming unbounded memory; canceled callers leave the queue without canceling a shared cache population.

Do not expose Keycloak Admin REST credentials to the frontend.

Local Testing ​

For Property Management, run migrations and seed defaults before testing security UI:

bash
dotnet run --project NGB.PropertyManagement.Migrator -- seed-defaults --connection "$NGB_PM_CONNECTION"

Then open:

text
/admin/security/users
/admin/security/roles

The e2e suite has mocked UI coverage for the security routes. Full end-to-end user provisioning still requires a working Keycloak test environment because the backend intentionally owns Keycloak Admin REST calls.

Released under the Apache License 2.0.