Platform SysAdmin
Overview
The Platform SysAdmin applet is the console a BigLedger platform system administrator uses to
run the platform as a whole: create and edit tenants, put existing users into a tenant, look up any
platform user, manage applet catalogues, subscriptions and applet stores, and maintain the list of
system administrators itself. It is a ROOT-ADMIN applet that reads and writes the shared
akaun_master database; it belongs to no tenant module and no customer opens it.
Everything it does is authorised on the caller’s platform system-administrator rank, a claim carried in the login token, and not on the permission sets a tenant administrator manages. That model has one consequence a reader must know before relying on the SysAdmins screen: removing someone from the list, or setting them INACTIVE, does not take their access away until their token expires. The details are under Configuration.
micro-fe/src/app of
blg-applet-core-akaun-platform-sysadmin-applet is an empty scaffold. The real application — 28
routes, eight menus — is under micro-fe/projects/akaun-platform/applets/sysadmin-applet/. Anyone
grepping the obvious path concludes the applet is unbuilt. It is not.Where it fits
| Direction | What | Why |
|---|---|---|
| Sibling | Developer SysAdmin Applet | Registers applets, vendors and store listings. This applet places the registered applets into catalogues and stores and installs them for users. |
| Sibling | Developer | The older, smaller applet-registration screen. |
| Sibling | Tenants V2 | The older tenant-maintenance applet; overlaps with this applet’s Tenant menu but calls different endpoints. |
| Downstream | Tenant Admin Applet | Once a tenant exists and has an OWNER, that tenant’s own administrator takes over its users and roles from inside the tenant. |
| Downstream | Applet Store | The stores, catalogues and trending links maintained here are what the store shows to users. |
Screens and menus
The left menu has eight entries; the routing file has 28 routes. The table says which screens are wired to the backend and which are not, because the difference is not visible on screen.
| Menu | Route | What it does | Status |
|---|---|---|---|
| (tenant name) | dashboard | Four counters (catalogues, applets, applet users, tenants) from GET …/catalogs/active-counts/sysadmin-ep, and a Recent Activity grid from …/catalogs/activity-search/sysadmin-ep filtered by email, name, status. | Live (rebuilt March–April 2026, issues #5, #6, #9, #10, #11) |
| Subscription | subscription | Listing → Create / Edit. Edit has tabs Details, Akaun HQ Entity, Tenant, Custom Pricing (Edition / Plugin), Hostname, Catalogue, Resources. | Details and the listing are live (…/tenant-subscriptions/sysadmin-ep). The Tenant, Hostname and Resources tabs render hard-coded sample rows and call nothing; the two Custom Pricing grids never load (their data calls are commented out). |
| Catalogue | catalogue | Listing → Create / Edit with tabs Details, Applet, User. From Applet: applet details with Plugins, Permissions Template (Approved / Requests) and Installed By User; add an applet to the catalogue; install the applet for users. From User: add a user (verify e-mail or phone, then rank and status) and install applets for them. | Live (…/catalogs/sysadmin-ep, …/applet-to-catalog-links/sysadmin-ep, …/catalog-login-subject-links/sysadmin-ep/install, permission-template request endpoints) |
| Tenant | tenant | Listing (GET …/tenants/sysadmin-ep/query) → Create / Edit with tabs Details, Users, Applet, Installation. Users → Add verifies an e-mail or phone number against the platform, then links the user to the tenant with a rank. | Live, with two dead buttons (see Lifecycle) |
| User | user | Listing of every platform login (GET …/users/sysadmin-ep/query, counts of tenants / catalogues / applets per user) → Edit with tabs for the login’s principals, tenants, catalogues and installed applets; given / family name are editable; DELETE USER runs the platform user-deletion processor. | Live |
| Hostname | hostname | Listing (GET …/hostnames/sysadmin-ep/query) → Create / Edit. | Listing only. The create and edit forms have a Tenant drop-down with the literal options “tenant 1 / tenant 2”, a Type drop-down with “TYPE 1 / TYPE 2”, and onCreate() {} / onDelete() {} — the effects class is empty. Nothing can be saved. |
| SysAdmins | sysadmin | Listing of app_system_administrator joined to the login (e-mail, name, start / end date, rank, status) → Add (verify e-mail, then Add SysAdmin or Send Invitation) / Edit. | Live (…/sysadmin) |
| Applet Stores | appletStore | Listing → Create / Edit with tabs Details, Catalogue, Applet, Trending Applet. | Live (…/stores/sysadmin-ep, …/applet-to-store-trending-links/sysadmin-ep) |
Two routed screens have no menu entry and are reachable only by URL: settings/applet-log, an
Audit Trail grid over app_login_subject_to_tenant_link_audit_trail (table, action, actor, date,
description, status), and personalization/sidebar.
The Settings menu is partly broken. Its landing route redirects to
client-side-permission-listing, which is commented out of the routing file, so opening
Settings lands on the applet’s 404 page; the Permission Wizard entry points at
permission-wizard-listing, which is not routed; and the Personalization → Field Settings entry
points at field-settings under personalization, where only personal-default-selection and
sidebar exist. Permission Set, User Permission and Role Permission work.
Configuration
Before you can use it
- You must be a platform system administrator. Your login needs a row in
akaun_master.app_system_administratorwith status ACTIVE, created from this applet’s SysAdmins screen (or by another OWNER / ADMIN). There is no tenant-side permission that substitutes for it. - You must log in after the row exists. The rank is copied into the login token at sign-in
(
sysAdminRank,sysAdminGuid). A token issued before you were added carries no rank, and everysysadmin-ependpoint will answer not authorised until you sign in again. - The applet must be installed for your login through a catalogue. Measured 2026-09-16: the applet sits in 5 catalogues and is installed in 1 tenant.
How authorisation actually works
Every write in this applet goes to a …/sysadmin-ep endpoint, and every one of those authorises
with one of two static checks in UserPermissionService.java (L471–L487). Both read only the
token:
| Check | Passes when | Used by |
|---|---|---|
isPlatformSystemRankAdminOrOwner | token sysAdminRank is ADMIN or OWNER | create / update / delete of tenants, tenant-user links, catalogues, stores, subscriptions, trending links, hostnames, sysadmins; platform user deletion |
isPlatformSystemAdminUser | token sysAdminGuid is non-blank — any rank, including MEMBER | every sysadmin-ep read and query; catalogue install for user; demo-tenant creation (POST …/tenants/sysadmin-ep/demo) |
Consequences that the screens do not show:
- Revocation is not immediate. Neither check re-reads
app_system_administrator, so setting a sysadmin to INACTIVE, DELETED, or letting their End Date pass changes nothing until the token lapses (observed lifetime about 30 days). Measured 2026-09-16 inakaun_master: 28 INACTIVE or DELETED sysadmin rows alongside 61 ACTIVE ones. Filed as blg-intranet#5896. Do not tell a reader that removing a sysadmin here locks them out — today it does not. - A
MEMBERcan read everything and create demo tenants, because the read-side check never looks at rank. The applet’s own Rank drop-down offers only OWNER and ADMIN, so MEMBER rows come from the API (4 exist). - One screen is the exception. User → Edit → SAVE calls
PUT …/users/sysadmin-ep, whose handler usesisUserSystemAdmin(subjectGuid, …)— a database query that requires an ACTIVE sysadmin row and an ACTIVE login (UserPermissionService.javaL420–L435). It is the only action in this applet a revoked administrator loses at once. - Rank rules on the SysAdmins screen are enforced server-side (
SysadminService.javaL66–L89): an OWNER may create, edit or delete anyone but themselves; an ADMIN may create or edit ADMIN and MEMBER rows only, may not touch an OWNER row or promote to OWNER, and may not edit their own row. The client shows a DELETE button only to OWNERs, but the server applies the same rule to ADMINs who call the endpoint directly. The failure text is “This user does not have the minimum rank to do this action or create own sysadmin”. - The permission tables (
bl_applet_client_side_perm_dfn, permission sets, roles) play no part in any of the above.bl_applet_client_side_perm_dfnhas 0 rows forPlatformSysAdmin(queried 2026-09-16), and the applet reads noSHOW_*/HIDE_*key.
Applet settings
No exposed control found (routes and settings components checked at commit f015e510):
- Settings → Field Settings renders eight
mat-slide-toggles (Unit Discount, SST/VAT/GST, WHT, Blanket Order, Segment, G/L Dimension, Profit Center, Project) bound to no form control, with a SAVE button that has no handler; the component class is empty. It is the same unbound stub found in several other applets. - Settings → Default Selection and Personalization → Default Selection are routed directly,
yet declare
@Input() appletSettings$and@Output() save. Angular binds nothing on a directly-routed component, soappletContaineris never set and the first change to the branch or location drop-down throws onthis.appletContainer.bl_applet_exts; SAVE emits to nobody. Nothing is persisted. - No component reads
selectMasterSettingsfor a decision; the only subscription (the audit-trail listing) stores the observable and does not use it.
Settings in other applets that control this applet
None found. The applet’s behaviour is fixed by the backend authorisation described above.
Feature visibility / permissions
The applet declares no client-side permission codes and the registry seeds none. Visibility of menus and buttons does not vary by role; what varies is whether the backend accepts the call, per the two checks above.
Fields
SysAdmins → Add / Edit
| Field | Meaning | Required | Notes |
|---|---|---|---|
| Login e-mail of an existing platform user | Yes | Verify Email calls GET …/sysadmin/user/sysadmin-ep?email=. Not found → Send Invitation (POST …/iam/user-invitation/send-invite/sysadmin-ep, add_user_to_tenant: true). Already a sysadmin → form shows their current rank and status, no button. Max 255 characters. | |
| Full Name, Phone Number | From the login record | – | Read-only |
| Rank | OWNER or ADMIN | Yes | Defaults to ADMIN. MEMBER exists server-side but is not offered. |
| Status | ACTIVE / INACTIVE | – | Defaults to ACTIVE. Changing it does not end an existing session (see above). |
| Start Date, End Date | Validity window | – | Left empty on Add SysAdmin, they are filled client-side as today and today plus one year (main-details.component.ts L190–L200). Neither is checked by the token-based authorisation. |
Tenant → Create / Edit → Details
| Field | Meaning | Required | Notes |
|---|---|---|---|
| Owner | – | – | Rendered, bound to a control that is never read or saved. |
| Tenant Code | Database and login identifier | Yes | Backend accepts [_a-zA-Z0-9]+ only (TenantService.java L111–L116); lower-cased to name the database. |
| Tenant Name | Display name | Yes | Same pattern as the code — a name with a space is rejected with “Only alphanumerics and underscore allowed in tenant code/name !”. |
| Tenant Type | Free text → obj_type | – | |
| RDS Type Option | Archive / Dedicated / Deleted / Shared – Free Tier / Shared – Paying | Yes | On create it is sent inside property_json.rds_type; on update it is sent as the column rds_type (tenant.effects.ts L27–L31 vs L62). |
| Description | Free text | – | |
| Subscription ID | Read-only; click to pick from the Select Tenant Subscription column | – | Stored as subscription_guid |
| Status | ACTIVE / INACTIVE | – | Defaults to ACTIVE |
Tenant → Users → Add
| Field | Meaning | Required | Notes |
|---|---|---|---|
| E-mail or mobile number of an existing platform user | Yes | Regex accepts + in the local part. Verify calls GET …/tenant-users/tenant/sysadmin-ep/{tenantGuid} and answers registered user, already added into this tenant, or not found. For an unknown e-mail a Send Invitation button appears; for an unknown phone number it does not. | |
| Name | From the login | – | Read-only |
| Rank | OWNER, ADMIN, MEMBER, GUEST | Yes | Enabled only after a successful verify |
| Status | ACTIVE / INACTIVE | Yes | Defaults to ACTIVE |
The Users → Edit form exposes the same Rank and Status for an existing link, plus audit columns.
Catalogue → Create / Edit → Details
Catalogue Code (required), Catalogue Name (required), Catalogue Type (PRIVATE default,
required), Applet Store (required, from the store list), Tenant (required, searchable drop-down of
tenant codes), Description, Status (ACTIVE default), Subscription Name / Type (read-only), and three
true/false flags — Auto Install Login, Auto Install Entity Login, Auto Install Consolidated
Login — stored on bl_applet_catalog_hdr. Their effect is in the tenant-side login flow, not in
this applet.
Subscription → Create / Edit → Details
Subscription Code (required, ^[a-zA-Z0-9-_]+$), Subscription Name (required), Subscription Type
(default STANDARD, required), Currency (required), Credit Limit (number, default 0.00, required),
Country (required), Description, Status (ACTIVE default, required).
Applet Stores → Create / Edit → Details
Applet Store Code (required; read-only once created), Applet Store Name, Description, Status (required).
User → Edit
User GUID and Email are read-only; Given Name and Family Name are required and are the only two values SAVE writes.
Lifecycle and effects
This is a master-data applet on the platform database. Nothing here posts a journal or moves
stock; the effects are rows in akaun_master and, in two cases, a queued processor.
| Action | Endpoint | What it writes |
|---|---|---|
| SysAdmins → Add SysAdmin | POST /core2/platform/dm/sysadmin | One app_system_administrator row (login_subject_guid, rank, status, date_start, date_end). The rank takes effect for that person at their next sign-in. |
| SysAdmins → SAVE / DELETE | PUT / DELETE /core2/platform/dm/sysadmin/{guid} | Updates or deletes the row. Does not end the person’s current session (see Configuration). |
| Tenant → CREATE | POST /core2/platform/dm/tenants/sysadmin-ep (tenant.effects.ts L37) | TenantService.createTenant: validates code and name, inserts app_tenant_hdr, then provisions a database (allocating or creating an AWS RDS instance), registers it in app_generic_resources_hdr, links the creator as OWNER and queues DefaultTenantConfigProcessor. No audit-trail row is written by this endpoint — its sibling POST …/tenants is the only one of the four creation endpoints that writes one. |
| Tenant → SAVE | PUT /core2/platform/dm/tenants/sysadmin-ep | Updates app_tenant_hdr (code, name, type, rds_type, description, subscription, status). |
| Tenant → DELETE (Details tab) | — | Dead. The button dispatches deleteTenantInit; the effect that would handle it is commented out (tenant.effects.ts L82–L93). Nothing happens and no message is shown. |
| Tenant → Users → Add | POST /core2/platform/dm/login-tenant-links/sysadmin-ep (tenant.effects.ts L96) | Creates app_mst_link_login_subject_to_tenant, immediately runs PopulatingLoginSubjectAndPrincipalInTenantProcessor (RUN_NOW) to create the login’s app_login_subject / app_login_principal inside the tenant database, and writes an audit-trail row (PlatformLoginSubjectTenantLinkController.java L283–L337). Verified live on 2026-09-16: master link and tenant-side login both present within a second. |
| Tenant → Users → Edit → SAVE | PUT …/login-tenant-links/sysadmin-ep | Updates rank and status on the link. |
| Tenant → Users → Edit → DELETE | — | Dead. Dispatches deleteTenantInit with the link guid (user-edit/main-details.component.ts L99); same unhandled action as above. The deleteTenantUserInit action that would remove the link is dispatched by nothing. |
| User → Tenant tab → remove | DELETE …/login-tenant-links/<last endpoint used>/{guid} | The shared base delete() reuses whichever endpoint family the service last set (base-api-template-service.model.ts L281–L288); after the listing call that is sysadmin-ep, so the link is deleted with an audit-trail row. |
| User → Catalogue tab → remove | DELETE /core2/platform/dm/catalog-login-subject-links/{guid} | The non-sysadmin endpoint: authorised by the permission tables (MST_API_DM_CATALOG_LOGIN_SUBJECT_LINK_*) or isUserPlatformAdmin, not by sysadmin rank. A sysadmin with no permission rows gets not authorised here and nowhere else in the applet. |
| User → Installed Applet tab → uninstall | POST …/user-applet-links/sysadmin-ep/multi-uninstall | Marks the selected bl_applet_login_subject_link rows. |
| User → DELETE USER | GET /core2/platform/dm/iam/app-login/user-deletion/sysadmin-ep/{guid} | A GET that queues UserDeletionProcessor RUN_NOW (AppLoginController.java L201–L220) and returns “PROCESSOR RAN SUCCESSFULLY” before the processor has run. |
| Catalogue → CREATE / SAVE / add applet / add user / install for user | …/catalogs/sysadmin-ep, …/applet-to-catalog-links/sysadmin-ep, …/catalog-login-subject-links/sysadmin-ep/install | Catalogue header, applet-to-catalogue link, user-to-catalogue link; installs write bl_applet_login_subject_link. Catalogue writes carry an audit-trail row (AppletCatalogAuditTrailService). |
| Subscription → CREATE / SAVE / DELETE | …/tenant-subscriptions/sysadmin-ep | app_subscription_hdr, with audit-trail rows. |
| Applet Stores → CREATE / SAVE / DELETE | …/stores/sysadmin-ep; delete is PUT …/stores/inactivation/sysadmin-ep/{guid}?status=DELETED | Store header; “delete” is a status change to DELETED. |
| Hostname → CREATE / DELETE | — | Dead (onCreate() {}, onDelete() {}; empty effects class). |
Related applets
- Developer SysAdmin Applet — registers the applets, vendors and stores that this applet then catalogues, installs and trends.
- Tenants V2 — the earlier tenant-maintenance applet. It
creates tenants through
POST …/tenants(permission-table authorised, audit-trailed) rather than…/tenants/sysadmin-ep; both hit the same provisioning code and the same current failure. - Developer — the earlier applet-registration screen.
- Tenant Admin Applet — what a tenant’s own OWNER or ADMIN uses once the tenant and its first user exist.
- Applet Store — consumes the stores, catalogues and trending links.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Every save answers not authorised although you are listed under SysAdmins | Your token was issued before your sysadmin row existed, so it carries no sysAdminRank. | Sign out and sign in again. |
| Someone removed from SysAdmins (or set INACTIVE) can still create tenants and users | Authorisation reads the token, never the table (UserPermissionService.java L471–L487). | Nothing in the product ends the session today; the access lapses when the token expires. Tracked as blg-intranet#5896. |
| “This user does not have the minimum rank to do this action or create own sysadmin” | An ADMIN tried to create or edit an OWNER, promote to OWNER, or edit their own row; or any rank tried to add themselves. | Have an OWNER make the change. |
| Tenant → CREATE fails after several minutes with HTTP 417 “Failed to obtain JDBC Connection” | The newly provisioned RDS instance is placed in a security group the application cannot reach (RdsCreateDao.setRdsConfig); the app_tenant_hdr row is left ACTIVE with no database behind it, and a retry creates a second one. Two production attempts on 2026-09-16, both failed. | Do not retry blindly. Tracked as blg-intranet#5895; the orphaned rows need clearing by hand. |
| “Only alphanumerics and underscore allowed in tenant code/name !” | The name is validated with the same [_a-zA-Z0-9]+ as the code. | Use a name without spaces or punctuation; the human-readable name can be changed later only within the same rule. |
| “Users’ email is not verified !” on tenant create | TenantService.createTenant requires the creator’s own e-mail principal to be confirmed (L104–L109). | Confirm the sysadmin’s e-mail first. |
| Tenant → Users → Add: “User ‘x’ not found” | The address has no platform login. | Send Invitation. Note the invitation request carries the operator’s session tenant code (inviteUser → …/iam/user-invitation/send-invite/?tenantCode=), not the tenant being edited — check where the invited user lands. |
| “This user is already added into this tenant” | A link already exists, possibly INACTIVE. | Edit the existing link’s rank / status from the Users tab instead. |
| The DELETE button on a tenant, or on a tenant’s user, does nothing | Both dispatch an action no effect handles (tenant.effects.ts L82–L93). | Remove a user from a tenant from User → Edit → Tenant tab (which does call the delete endpoint). There is no working tenant delete in this applet. |
| Settings opens the 404 page; Permission Wizard and Personalization → Field Settings 404 | Redirect and menu entries point at routes that are commented out or absent. | Use Permission Set, User Permission, Role Permission directly. |
| Users report they cannot log in and their accounts show as deactivated (issue #1, three tenants) | Reported in the applet’s tracker; the cause was not recorded there. | Check app_login_subject.status and the tenant-side login before assuming a sysadmin action. |
| Dashboard Recent Activity is empty | Before April 2026 the grid had no data source (issue #5); it now reads …/catalogs/activity-search/sysadmin-ep with offset pagination (issue #10). | Confirm the deployed bundle is from April 2026 or later. |
Related documentation
- Platform applets — the section hub.
- Applet Catalog — the registry-generated list this applet’s Catalogue menu feeds.