Files
pgmorbac-node/src/schemas.ts
T
marc 851dd389d7 feat(accounts): service-account management, generated passwords, perms-view fixes
Permissions view
- Restore the System Accounts listing path and audit related tabs.
- Lock the framework system_users deny rules (is_system_managed) and
  system-principal global rules; show them read-only.

System vs service accounts
- Only account_type='system' is immutable. service-bot is no longer a
  system principal; ensureInternalAccounts repairs existing rows.
- GET /users now surfaces system/service accounts (gated by
  read/system_users, read/service_users) and Type is the first column.

Service accounts
- UI to create/edit/delete service accounts (API-only, no login).
- Admin API-key management: GET/POST/DELETE /users/:id/keys, restricted
  to service accounts, scope-capped to the account's own permissions,
  with a key-management card on the user detail page.

Password policy
- Admins never set passwords. Creating a user account auto-generates a
  strong password (shown once) and forces a change on first login;
  admin "Reset password" does the same.
- Forced change flow: login returns password_change_required +
  challenge token; POST /auth/change-password validates strength,
  clears the flag, issues the session. Enforced after password and MFA.
- Strength: length 12-128, reject common passwords and email/name.

UI
- Lock indicators in action columns use a shared LockBtn (tooltip,
  aligned with other action buttons).
- Account-type selection in create is a segmented button selector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 08:52:39 +02:00

137 lines
5.3 KiB
TypeScript

export const orgResponse = {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
name: { type: 'string' },
parent_id: { type: 'string', format: 'uuid', nullable: true },
metadata: { type: 'object' },
},
};
export const roleResponse = {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
org_id: { type: 'string', format: 'uuid' },
org_name: { type: 'string' },
name: { type: 'string' },
description: { type: 'string', nullable: true },
},
};
export const ruleResponse = {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
role_id: { type: 'string', format: 'uuid' },
org_id: { type: 'string', format: 'uuid' },
org_name: { type: 'string' },
role_name: { type: 'string' },
activity: { type: 'string' },
view: { type: 'string' },
modality: { type: 'string', enum: ['permission', 'prohibition'] },
priority: { type: 'integer', nullable: true },
scope: { type: 'string' },
},
};
export const userResponse = {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
email: { type: 'string', format: 'email' },
name: { type: 'string' },
account_type: { type: 'string', enum: ['user', 'system', 'service'] },
is_active: { type: 'boolean' },
needs_placement: { type: 'boolean' },
created_at: { type: 'string', format: 'date-time' },
roles: {
type: 'array',
items: {
type: 'object',
properties: {
role_id: { type: 'string', format: 'uuid' },
role_name: { type: 'string' },
org_id: { type: 'string', format: 'uuid' },
org_name: { type: 'string' },
},
},
},
},
};
export const crossOrgRuleResponse = {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
source_org_id: { type: 'string', format: 'uuid' },
source_role_id: { type: 'string', format: 'uuid' },
target_org_id: { type: 'string', format: 'uuid' },
source_org_name: { type: 'string' },
source_role_name: { type: 'string' },
target_org_name: { type: 'string' },
activity: { type: 'string' },
view: { type: 'string' },
modality: { type: 'string', enum: ['permission', 'prohibition'] },
priority: { type: 'integer', nullable: true },
},
};
export const userRuleResponse = {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
user_id: { type: 'string', format: 'uuid' },
org_id: { type: 'string', format: 'uuid' },
user_name: { type: 'string' },
user_email: { type: 'string', nullable: true },
org_name: { type: 'string' },
activity: { type: 'string' },
view: { type: 'string' },
modality: { type: 'string', enum: ['permission', 'prohibition'] },
priority: { type: 'integer', nullable: true },
},
};
export const globalRuleResponse = {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
user_id: { type: 'string', format: 'uuid', nullable: true },
user_name: { type: 'string', nullable: true },
user_email: { type: 'string', nullable: true },
activity: { type: 'string', nullable: true },
view: { type: 'string', nullable: true },
modality: { type: 'string', enum: ['permission', 'prohibition'] },
priority: { type: 'integer', nullable: true },
is_system_principal: { type: 'boolean' },
is_system_managed: { type: 'boolean' },
},
};
export const delegationResponse = {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
delegator_id: { type: 'string', format: 'uuid' },
delegatee_id: { type: 'string', format: 'uuid' },
delegator_name: { type: 'string', nullable: true },
delegator_email: { type: 'string', nullable: true },
delegatee_name: { type: 'string', nullable: true },
delegatee_email: { type: 'string', nullable: true },
role_id: { type: 'string', format: 'uuid' },
role_name: { type: 'string' },
org_id: { type: 'string', format: 'uuid' },
org_name: { type: 'string' },
valid_from: { type: 'string', format: 'date-time' },
valid_until: { type: 'string', format: 'date-time' },
revoked: { type: 'boolean' },
created_at: { type: 'string', format: 'date-time' },
},
};
export const notFound = { description: 'Resource not found.' };
export const forbidden = { description: 'Insufficient permissions.' };
export const unauthorized = { description: 'Missing or invalid token.' };
export const badRequest = { description: 'X-Org-Id header is required.' };