@portaidentity/server 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/applications/cache.d.ts +64 -0
- package/dist/applications/cache.d.ts.map +1 -0
- package/dist/applications/cache.js +161 -0
- package/dist/applications/cache.js.map +1 -0
- package/dist/applications/errors.d.ts +25 -0
- package/dist/applications/errors.d.ts.map +1 -0
- package/dist/applications/errors.js +31 -0
- package/dist/applications/errors.js.map +1 -0
- package/dist/applications/index.d.ts +17 -0
- package/dist/applications/index.d.ts.map +1 -0
- package/dist/applications/index.js +19 -0
- package/dist/applications/index.js.map +1 -0
- package/dist/applications/repository.d.ts +184 -0
- package/dist/applications/repository.d.ts.map +1 -0
- package/dist/applications/repository.js +367 -0
- package/dist/applications/repository.js.map +1 -0
- package/dist/applications/service.d.ts +160 -0
- package/dist/applications/service.d.ts.map +1 -0
- package/dist/applications/service.js +388 -0
- package/dist/applications/service.js.map +1 -0
- package/dist/applications/slugs.d.ts +61 -0
- package/dist/applications/slugs.d.ts.map +1 -0
- package/dist/applications/slugs.js +115 -0
- package/dist/applications/slugs.js.map +1 -0
- package/dist/applications/types.d.ts +129 -0
- package/dist/applications/types.d.ts.map +1 -0
- package/dist/applications/types.js +62 -0
- package/dist/applications/types.js.map +1 -0
- package/dist/auth/csrf.d.ts +71 -0
- package/dist/auth/csrf.d.ts.map +1 -0
- package/dist/auth/csrf.js +112 -0
- package/dist/auth/csrf.js.map +1 -0
- package/dist/auth/email-renderer.d.ts +37 -0
- package/dist/auth/email-renderer.d.ts.map +1 -0
- package/dist/auth/email-renderer.js +116 -0
- package/dist/auth/email-renderer.js.map +1 -0
- package/dist/auth/email-service.d.ts +154 -0
- package/dist/auth/email-service.d.ts.map +1 -0
- package/dist/auth/email-service.js +415 -0
- package/dist/auth/email-service.js.map +1 -0
- package/dist/auth/email-transport.d.ts +65 -0
- package/dist/auth/email-transport.d.ts.map +1 -0
- package/dist/auth/email-transport.js +82 -0
- package/dist/auth/email-transport.js.map +1 -0
- package/dist/auth/i18n.d.ts +81 -0
- package/dist/auth/i18n.d.ts.map +1 -0
- package/dist/auth/i18n.js +395 -0
- package/dist/auth/i18n.js.map +1 -0
- package/dist/auth/index.d.ts +28 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +31 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/interaction-authority.d.ts +39 -0
- package/dist/auth/interaction-authority.d.ts.map +1 -0
- package/dist/auth/interaction-authority.js +30 -0
- package/dist/auth/interaction-authority.js.map +1 -0
- package/dist/auth/magic-link-session.d.ts +81 -0
- package/dist/auth/magic-link-session.d.ts.map +1 -0
- package/dist/auth/magic-link-session.js +183 -0
- package/dist/auth/magic-link-session.js.map +1 -0
- package/dist/auth/rate-limiter.d.ts +143 -0
- package/dist/auth/rate-limiter.d.ts.map +1 -0
- package/dist/auth/rate-limiter.js +220 -0
- package/dist/auth/rate-limiter.js.map +1 -0
- package/dist/auth/recovery-crypto.d.ts +34 -0
- package/dist/auth/recovery-crypto.d.ts.map +1 -0
- package/dist/auth/recovery-crypto.js +103 -0
- package/dist/auth/recovery-crypto.js.map +1 -0
- package/dist/auth/recovery-job-processor.d.ts +24 -0
- package/dist/auth/recovery-job-processor.d.ts.map +1 -0
- package/dist/auth/recovery-job-processor.js +123 -0
- package/dist/auth/recovery-job-processor.js.map +1 -0
- package/dist/auth/recovery-job-repository.d.ts +219 -0
- package/dist/auth/recovery-job-repository.d.ts.map +1 -0
- package/dist/auth/recovery-job-repository.js +340 -0
- package/dist/auth/recovery-job-repository.js.map +1 -0
- package/dist/auth/recovery-service.d.ts +27 -0
- package/dist/auth/recovery-service.d.ts.map +1 -0
- package/dist/auth/recovery-service.js +53 -0
- package/dist/auth/recovery-service.js.map +1 -0
- package/dist/auth/recovery-worker.d.ts +158 -0
- package/dist/auth/recovery-worker.d.ts.map +1 -0
- package/dist/auth/recovery-worker.js +306 -0
- package/dist/auth/recovery-worker.js.map +1 -0
- package/dist/auth/template-engine.d.ts +134 -0
- package/dist/auth/template-engine.d.ts.map +1 -0
- package/dist/auth/template-engine.js +225 -0
- package/dist/auth/template-engine.js.map +1 -0
- package/dist/auth/token-repository.d.ts +238 -0
- package/dist/auth/token-repository.d.ts.map +1 -0
- package/dist/auth/token-repository.js +475 -0
- package/dist/auth/token-repository.js.map +1 -0
- package/dist/auth/tokens.d.ts +45 -0
- package/dist/auth/tokens.d.ts.map +1 -0
- package/dist/auth/tokens.js +51 -0
- package/dist/auth/tokens.js.map +1 -0
- package/dist/cli/bootstrap.d.ts +55 -0
- package/dist/cli/bootstrap.d.ts.map +1 -0
- package/dist/cli/bootstrap.js +90 -0
- package/dist/cli/bootstrap.js.map +1 -0
- package/dist/cli/commands/health.d.ts +20 -0
- package/dist/cli/commands/health.d.ts.map +1 -0
- package/dist/cli/commands/health.js +80 -0
- package/dist/cli/commands/health.js.map +1 -0
- package/dist/cli/commands/init.d.ts +48 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +349 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/migrate.d.ts +19 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +88 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/commands/seed.d.ts +19 -0
- package/dist/cli/commands/seed.d.ts.map +1 -0
- package/dist/cli/commands/seed.js +71 -0
- package/dist/cli/commands/seed.js.map +1 -0
- package/dist/cli/commands/user-2fa.d.ts +29 -0
- package/dist/cli/commands/user-2fa.d.ts.map +1 -0
- package/dist/cli/commands/user-2fa.js +113 -0
- package/dist/cli/commands/user-2fa.js.map +1 -0
- package/dist/cli/error-handler.d.ts +31 -0
- package/dist/cli/error-handler.d.ts.map +1 -0
- package/dist/cli/error-handler.js +65 -0
- package/dist/cli/error-handler.js.map +1 -0
- package/dist/cli/index.d.ts +48 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +99 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.d.ts +94 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +124 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/prompt.d.ts +48 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +96 -0
- package/dist/cli/prompt.js.map +1 -0
- package/dist/clients/cache.d.ts +63 -0
- package/dist/clients/cache.d.ts.map +1 -0
- package/dist/clients/cache.js +160 -0
- package/dist/clients/cache.js.map +1 -0
- package/dist/clients/crypto.d.ts +69 -0
- package/dist/clients/crypto.d.ts.map +1 -0
- package/dist/clients/crypto.js +83 -0
- package/dist/clients/crypto.js.map +1 -0
- package/dist/clients/errors.d.ts +27 -0
- package/dist/clients/errors.d.ts.map +1 -0
- package/dist/clients/errors.js +33 -0
- package/dist/clients/errors.js.map +1 -0
- package/dist/clients/index.d.ts +19 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +23 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/repository.d.ts +161 -0
- package/dist/clients/repository.d.ts.map +1 -0
- package/dist/clients/repository.js +318 -0
- package/dist/clients/repository.js.map +1 -0
- package/dist/clients/resolve-login-methods.d.ts +74 -0
- package/dist/clients/resolve-login-methods.d.ts.map +1 -0
- package/dist/clients/resolve-login-methods.js +68 -0
- package/dist/clients/resolve-login-methods.js.map +1 -0
- package/dist/clients/secret-repository.d.ts +109 -0
- package/dist/clients/secret-repository.d.ts.map +1 -0
- package/dist/clients/secret-repository.js +166 -0
- package/dist/clients/secret-repository.js.map +1 -0
- package/dist/clients/secret-service.d.ts +87 -0
- package/dist/clients/secret-service.d.ts.map +1 -0
- package/dist/clients/secret-service.js +204 -0
- package/dist/clients/secret-service.js.map +1 -0
- package/dist/clients/service.d.ts +158 -0
- package/dist/clients/service.d.ts.map +1 -0
- package/dist/clients/service.js +523 -0
- package/dist/clients/service.js.map +1 -0
- package/dist/clients/types.d.ts +214 -0
- package/dist/clients/types.d.ts.map +1 -0
- package/dist/clients/types.js +77 -0
- package/dist/clients/types.js.map +1 -0
- package/dist/clients/validators.d.ts +87 -0
- package/dist/clients/validators.d.ts.map +1 -0
- package/dist/clients/validators.js +164 -0
- package/dist/clients/validators.js.map +1 -0
- package/dist/config/index.d.ts +23 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +52 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +35 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +167 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/custom-claims/cache.d.ts +48 -0
- package/dist/custom-claims/cache.d.ts.map +1 -0
- package/dist/custom-claims/cache.js +122 -0
- package/dist/custom-claims/cache.js.map +1 -0
- package/dist/custom-claims/errors.d.ts +25 -0
- package/dist/custom-claims/errors.d.ts.map +1 -0
- package/dist/custom-claims/errors.js +31 -0
- package/dist/custom-claims/errors.js.map +1 -0
- package/dist/custom-claims/index.d.ts +14 -0
- package/dist/custom-claims/index.d.ts.map +1 -0
- package/dist/custom-claims/index.js +17 -0
- package/dist/custom-claims/index.js.map +1 -0
- package/dist/custom-claims/repository.d.ts +147 -0
- package/dist/custom-claims/repository.d.ts.map +1 -0
- package/dist/custom-claims/repository.js +338 -0
- package/dist/custom-claims/repository.js.map +1 -0
- package/dist/custom-claims/service.d.ts +129 -0
- package/dist/custom-claims/service.d.ts.map +1 -0
- package/dist/custom-claims/service.js +296 -0
- package/dist/custom-claims/service.js.map +1 -0
- package/dist/custom-claims/types.d.ts +132 -0
- package/dist/custom-claims/types.d.ts.map +1 -0
- package/dist/custom-claims/types.js +61 -0
- package/dist/custom-claims/types.js.map +1 -0
- package/dist/custom-claims/validators.d.ts +61 -0
- package/dist/custom-claims/validators.d.ts.map +1 -0
- package/dist/custom-claims/validators.js +136 -0
- package/dist/custom-claims/validators.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +91 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/admin-permissions.d.ts +147 -0
- package/dist/lib/admin-permissions.d.ts.map +1 -0
- package/dist/lib/admin-permissions.js +261 -0
- package/dist/lib/admin-permissions.js.map +1 -0
- package/dist/lib/audit-log.d.ts +45 -0
- package/dist/lib/audit-log.d.ts.map +1 -0
- package/dist/lib/audit-log.js +79 -0
- package/dist/lib/audit-log.js.map +1 -0
- package/dist/lib/branding-assets.d.ts +47 -0
- package/dist/lib/branding-assets.d.ts.map +1 -0
- package/dist/lib/branding-assets.js +93 -0
- package/dist/lib/branding-assets.js.map +1 -0
- package/dist/lib/bulk-operations.d.ts +54 -0
- package/dist/lib/bulk-operations.d.ts.map +1 -0
- package/dist/lib/bulk-operations.js +201 -0
- package/dist/lib/bulk-operations.js.map +1 -0
- package/dist/lib/cursor.d.ts +110 -0
- package/dist/lib/cursor.d.ts.map +1 -0
- package/dist/lib/cursor.js +115 -0
- package/dist/lib/cursor.js.map +1 -0
- package/dist/lib/data-export.d.ts +67 -0
- package/dist/lib/data-export.d.ts.map +1 -0
- package/dist/lib/data-export.js +272 -0
- package/dist/lib/data-export.js.map +1 -0
- package/dist/lib/data-import-plan.d.ts +50 -0
- package/dist/lib/data-import-plan.d.ts.map +1 -0
- package/dist/lib/data-import-plan.js +235 -0
- package/dist/lib/data-import-plan.js.map +1 -0
- package/dist/lib/data-import.d.ts +276 -0
- package/dist/lib/data-import.d.ts.map +1 -0
- package/dist/lib/data-import.js +1694 -0
- package/dist/lib/data-import.js.map +1 -0
- package/dist/lib/database.d.ts +22 -0
- package/dist/lib/database.d.ts.map +1 -0
- package/dist/lib/database.js +103 -0
- package/dist/lib/database.js.map +1 -0
- package/dist/lib/entity-history.d.ts +52 -0
- package/dist/lib/entity-history.d.ts.map +1 -0
- package/dist/lib/entity-history.js +121 -0
- package/dist/lib/entity-history.js.map +1 -0
- package/dist/lib/etag.d.ts +92 -0
- package/dist/lib/etag.d.ts.map +1 -0
- package/dist/lib/etag.js +155 -0
- package/dist/lib/etag.js.map +1 -0
- package/dist/lib/image-validator.d.ts +44 -0
- package/dist/lib/image-validator.d.ts.map +1 -0
- package/dist/lib/image-validator.js +204 -0
- package/dist/lib/image-validator.js.map +1 -0
- package/dist/lib/logger.d.ts +15 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +84 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/migrator.d.ts +14 -0
- package/dist/lib/migrator.d.ts.map +1 -0
- package/dist/lib/migrator.js +34 -0
- package/dist/lib/migrator.js.map +1 -0
- package/dist/lib/redis.d.ts +5 -0
- package/dist/lib/redis.d.ts.map +1 -0
- package/dist/lib/redis.js +29 -0
- package/dist/lib/redis.js.map +1 -0
- package/dist/lib/runtime-paths.d.ts +38 -0
- package/dist/lib/runtime-paths.d.ts.map +1 -0
- package/dist/lib/runtime-paths.js +50 -0
- package/dist/lib/runtime-paths.js.map +1 -0
- package/dist/lib/session-tracking.d.ts +81 -0
- package/dist/lib/session-tracking.d.ts.map +1 -0
- package/dist/lib/session-tracking.js +145 -0
- package/dist/lib/session-tracking.js.map +1 -0
- package/dist/lib/signing-key-crypto.d.ts +53 -0
- package/dist/lib/signing-key-crypto.d.ts.map +1 -0
- package/dist/lib/signing-key-crypto.js +108 -0
- package/dist/lib/signing-key-crypto.js.map +1 -0
- package/dist/lib/signing-keys.d.ts +141 -0
- package/dist/lib/signing-keys.d.ts.map +1 -0
- package/dist/lib/signing-keys.js +206 -0
- package/dist/lib/signing-keys.js.map +1 -0
- package/dist/lib/stats.d.ts +83 -0
- package/dist/lib/stats.d.ts.map +1 -0
- package/dist/lib/stats.js +187 -0
- package/dist/lib/stats.js.map +1 -0
- package/dist/lib/super-admin-protection.d.ts +69 -0
- package/dist/lib/super-admin-protection.d.ts.map +1 -0
- package/dist/lib/super-admin-protection.js +102 -0
- package/dist/lib/super-admin-protection.js.map +1 -0
- package/dist/lib/system-config.d.ts +79 -0
- package/dist/lib/system-config.d.ts.map +1 -0
- package/dist/lib/system-config.js +159 -0
- package/dist/lib/system-config.js.map +1 -0
- package/dist/middleware/admin-auth.d.ts +100 -0
- package/dist/middleware/admin-auth.d.ts.map +1 -0
- package/dist/middleware/admin-auth.js +241 -0
- package/dist/middleware/admin-auth.js.map +1 -0
- package/dist/middleware/admin-cors.d.ts +41 -0
- package/dist/middleware/admin-cors.d.ts.map +1 -0
- package/dist/middleware/admin-cors.js +83 -0
- package/dist/middleware/admin-cors.js.map +1 -0
- package/dist/middleware/admin-mutation-audit.d.ts +9 -0
- package/dist/middleware/admin-mutation-audit.d.ts.map +1 -0
- package/dist/middleware/admin-mutation-audit.js +76 -0
- package/dist/middleware/admin-mutation-audit.js.map +1 -0
- package/dist/middleware/admin-rate-limiter.d.ts +56 -0
- package/dist/middleware/admin-rate-limiter.d.ts.map +1 -0
- package/dist/middleware/admin-rate-limiter.js +97 -0
- package/dist/middleware/admin-rate-limiter.js.map +1 -0
- package/dist/middleware/client-secret-hash.d.ts +34 -0
- package/dist/middleware/client-secret-hash.d.ts.map +1 -0
- package/dist/middleware/client-secret-hash.js +74 -0
- package/dist/middleware/client-secret-hash.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +3 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +93 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/health.d.ts +3 -0
- package/dist/middleware/health.d.ts.map +1 -0
- package/dist/middleware/health.js +35 -0
- package/dist/middleware/health.js.map +1 -0
- package/dist/middleware/metrics.d.ts +40 -0
- package/dist/middleware/metrics.d.ts.map +1 -0
- package/dist/middleware/metrics.js +75 -0
- package/dist/middleware/metrics.js.map +1 -0
- package/dist/middleware/oidc-client-tenant.d.ts +37 -0
- package/dist/middleware/oidc-client-tenant.d.ts.map +1 -0
- package/dist/middleware/oidc-client-tenant.js +156 -0
- package/dist/middleware/oidc-client-tenant.js.map +1 -0
- package/dist/middleware/oidc-cors.d.ts +33 -0
- package/dist/middleware/oidc-cors.d.ts.map +1 -0
- package/dist/middleware/oidc-cors.js +68 -0
- package/dist/middleware/oidc-cors.js.map +1 -0
- package/dist/middleware/oidc-preflight-cors.d.ts +54 -0
- package/dist/middleware/oidc-preflight-cors.d.ts.map +1 -0
- package/dist/middleware/oidc-preflight-cors.js +267 -0
- package/dist/middleware/oidc-preflight-cors.js.map +1 -0
- package/dist/middleware/prompt-login-reset.d.ts +103 -0
- package/dist/middleware/prompt-login-reset.d.ts.map +1 -0
- package/dist/middleware/prompt-login-reset.js +204 -0
- package/dist/middleware/prompt-login-reset.js.map +1 -0
- package/dist/middleware/ready.d.ts +23 -0
- package/dist/middleware/ready.d.ts.map +1 -0
- package/dist/middleware/ready.js +60 -0
- package/dist/middleware/ready.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +27 -0
- package/dist/middleware/request-logger.d.ts.map +1 -0
- package/dist/middleware/request-logger.js +46 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/middleware/require-permission.d.ts +28 -0
- package/dist/middleware/require-permission.d.ts.map +1 -0
- package/dist/middleware/require-permission.js +63 -0
- package/dist/middleware/require-permission.js.map +1 -0
- package/dist/middleware/require-user-organization.d.ts +18 -0
- package/dist/middleware/require-user-organization.d.ts.map +1 -0
- package/dist/middleware/require-user-organization.js +53 -0
- package/dist/middleware/require-user-organization.js.map +1 -0
- package/dist/middleware/root-page.d.ts +62 -0
- package/dist/middleware/root-page.d.ts.map +1 -0
- package/dist/middleware/root-page.js +134 -0
- package/dist/middleware/root-page.js.map +1 -0
- package/dist/middleware/security-headers.d.ts +98 -0
- package/dist/middleware/security-headers.d.ts.map +1 -0
- package/dist/middleware/security-headers.js +159 -0
- package/dist/middleware/security-headers.js.map +1 -0
- package/dist/middleware/tenant-resolver.d.ts +37 -0
- package/dist/middleware/tenant-resolver.d.ts.map +1 -0
- package/dist/middleware/tenant-resolver.js +71 -0
- package/dist/middleware/tenant-resolver.js.map +1 -0
- package/dist/middleware/token-rate-limiter.d.ts +89 -0
- package/dist/middleware/token-rate-limiter.d.ts.map +1 -0
- package/dist/middleware/token-rate-limiter.js +189 -0
- package/dist/middleware/token-rate-limiter.js.map +1 -0
- package/dist/oidc/account-finder.d.ts +52 -0
- package/dist/oidc/account-finder.d.ts.map +1 -0
- package/dist/oidc/account-finder.js +134 -0
- package/dist/oidc/account-finder.js.map +1 -0
- package/dist/oidc/adapter-factory.d.ts +90 -0
- package/dist/oidc/adapter-factory.d.ts.map +1 -0
- package/dist/oidc/adapter-factory.js +165 -0
- package/dist/oidc/adapter-factory.js.map +1 -0
- package/dist/oidc/configuration.d.ts +155 -0
- package/dist/oidc/configuration.d.ts.map +1 -0
- package/dist/oidc/configuration.js +531 -0
- package/dist/oidc/configuration.js.map +1 -0
- package/dist/oidc/postgres-adapter.d.ts +159 -0
- package/dist/oidc/postgres-adapter.d.ts.map +1 -0
- package/dist/oidc/postgres-adapter.js +222 -0
- package/dist/oidc/postgres-adapter.js.map +1 -0
- package/dist/oidc/protocol-security-observer.d.ts +38 -0
- package/dist/oidc/protocol-security-observer.d.ts.map +1 -0
- package/dist/oidc/protocol-security-observer.js +93 -0
- package/dist/oidc/protocol-security-observer.js.map +1 -0
- package/dist/oidc/provider.d.ts +37 -0
- package/dist/oidc/provider.d.ts.map +1 -0
- package/dist/oidc/provider.js +71 -0
- package/dist/oidc/provider.js.map +1 -0
- package/dist/oidc/redis-adapter.d.ts +140 -0
- package/dist/oidc/redis-adapter.d.ts.map +1 -0
- package/dist/oidc/redis-adapter.js +344 -0
- package/dist/oidc/redis-adapter.js.map +1 -0
- package/dist/organizations/cache.d.ts +60 -0
- package/dist/organizations/cache.d.ts.map +1 -0
- package/dist/organizations/cache.js +157 -0
- package/dist/organizations/cache.js.map +1 -0
- package/dist/organizations/errors.d.ts +25 -0
- package/dist/organizations/errors.d.ts.map +1 -0
- package/dist/organizations/errors.js +31 -0
- package/dist/organizations/errors.js.map +1 -0
- package/dist/organizations/index.d.ts +15 -0
- package/dist/organizations/index.d.ts.map +1 -0
- package/dist/organizations/index.js +15 -0
- package/dist/organizations/index.js.map +1 -0
- package/dist/organizations/repository.d.ts +183 -0
- package/dist/organizations/repository.d.ts.map +1 -0
- package/dist/organizations/repository.js +354 -0
- package/dist/organizations/repository.js.map +1 -0
- package/dist/organizations/service.d.ts +168 -0
- package/dist/organizations/service.d.ts.map +1 -0
- package/dist/organizations/service.js +511 -0
- package/dist/organizations/service.js.map +1 -0
- package/dist/organizations/slugs.d.ts +57 -0
- package/dist/organizations/slugs.d.ts.map +1 -0
- package/dist/organizations/slugs.js +130 -0
- package/dist/organizations/slugs.js.map +1 -0
- package/dist/organizations/types.d.ts +146 -0
- package/dist/organizations/types.d.ts.map +1 -0
- package/dist/organizations/types.js +49 -0
- package/dist/organizations/types.js.map +1 -0
- package/dist/rbac/cache.d.ts +110 -0
- package/dist/rbac/cache.d.ts.map +1 -0
- package/dist/rbac/cache.js +271 -0
- package/dist/rbac/cache.js.map +1 -0
- package/dist/rbac/errors.d.ts +34 -0
- package/dist/rbac/errors.d.ts.map +1 -0
- package/dist/rbac/errors.js +43 -0
- package/dist/rbac/errors.js.map +1 -0
- package/dist/rbac/index.d.ts +17 -0
- package/dist/rbac/index.d.ts.map +1 -0
- package/dist/rbac/index.js +21 -0
- package/dist/rbac/index.js.map +1 -0
- package/dist/rbac/mapping-repository.d.ts +121 -0
- package/dist/rbac/mapping-repository.d.ts.map +1 -0
- package/dist/rbac/mapping-repository.js +224 -0
- package/dist/rbac/mapping-repository.js.map +1 -0
- package/dist/rbac/permission-repository.d.ts +100 -0
- package/dist/rbac/permission-repository.d.ts.map +1 -0
- package/dist/rbac/permission-repository.js +190 -0
- package/dist/rbac/permission-repository.js.map +1 -0
- package/dist/rbac/permission-service.d.ts +85 -0
- package/dist/rbac/permission-service.d.ts.map +1 -0
- package/dist/rbac/permission-service.js +179 -0
- package/dist/rbac/permission-service.js.map +1 -0
- package/dist/rbac/role-repository.d.ts +104 -0
- package/dist/rbac/role-repository.d.ts.map +1 -0
- package/dist/rbac/role-repository.js +202 -0
- package/dist/rbac/role-repository.js.map +1 -0
- package/dist/rbac/role-service.d.ts +111 -0
- package/dist/rbac/role-service.d.ts.map +1 -0
- package/dist/rbac/role-service.js +256 -0
- package/dist/rbac/role-service.js.map +1 -0
- package/dist/rbac/slugs.d.ts +116 -0
- package/dist/rbac/slugs.d.ts.map +1 -0
- package/dist/rbac/slugs.js +178 -0
- package/dist/rbac/slugs.js.map +1 -0
- package/dist/rbac/types.d.ts +187 -0
- package/dist/rbac/types.d.ts.map +1 -0
- package/dist/rbac/types.js +75 -0
- package/dist/rbac/types.js.map +1 -0
- package/dist/rbac/user-role-service.d.ts +101 -0
- package/dist/rbac/user-role-service.d.ts.map +1 -0
- package/dist/rbac/user-role-service.js +166 -0
- package/dist/rbac/user-role-service.js.map +1 -0
- package/dist/routes/applications.d.ts +40 -0
- package/dist/routes/applications.d.ts.map +1 -0
- package/dist/routes/applications.js +284 -0
- package/dist/routes/applications.js.map +1 -0
- package/dist/routes/audit.d.ts +31 -0
- package/dist/routes/audit.d.ts.map +1 -0
- package/dist/routes/audit.js +140 -0
- package/dist/routes/audit.js.map +1 -0
- package/dist/routes/branding.d.ts +17 -0
- package/dist/routes/branding.d.ts.map +1 -0
- package/dist/routes/branding.js +104 -0
- package/dist/routes/branding.js.map +1 -0
- package/dist/routes/bulk.d.ts +37 -0
- package/dist/routes/bulk.d.ts.map +1 -0
- package/dist/routes/bulk.js +94 -0
- package/dist/routes/bulk.js.map +1 -0
- package/dist/routes/clients.d.ts +44 -0
- package/dist/routes/clients.d.ts.map +1 -0
- package/dist/routes/clients.js +353 -0
- package/dist/routes/clients.js.map +1 -0
- package/dist/routes/config.d.ts +35 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +115 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/custom-claims.d.ts +38 -0
- package/dist/routes/custom-claims.d.ts.map +1 -0
- package/dist/routes/custom-claims.js +208 -0
- package/dist/routes/custom-claims.js.map +1 -0
- package/dist/routes/exports.d.ts +21 -0
- package/dist/routes/exports.d.ts.map +1 -0
- package/dist/routes/exports.js +98 -0
- package/dist/routes/exports.js.map +1 -0
- package/dist/routes/imports.d.ts +18 -0
- package/dist/routes/imports.d.ts.map +1 -0
- package/dist/routes/imports.js +72 -0
- package/dist/routes/imports.js.map +1 -0
- package/dist/routes/interactions.d.ts +111 -0
- package/dist/routes/interactions.d.ts.map +1 -0
- package/dist/routes/interactions.js +1074 -0
- package/dist/routes/interactions.js.map +1 -0
- package/dist/routes/invitation.d.ts +31 -0
- package/dist/routes/invitation.d.ts.map +1 -0
- package/dist/routes/invitation.js +342 -0
- package/dist/routes/invitation.js.map +1 -0
- package/dist/routes/keys.d.ts +28 -0
- package/dist/routes/keys.d.ts.map +1 -0
- package/dist/routes/keys.js +91 -0
- package/dist/routes/keys.js.map +1 -0
- package/dist/routes/magic-link.d.ts +43 -0
- package/dist/routes/magic-link.d.ts.map +1 -0
- package/dist/routes/magic-link.js +264 -0
- package/dist/routes/magic-link.js.map +1 -0
- package/dist/routes/organizations.d.ts +38 -0
- package/dist/routes/organizations.d.ts.map +1 -0
- package/dist/routes/organizations.js +347 -0
- package/dist/routes/organizations.js.map +1 -0
- package/dist/routes/password-reset.d.ts +65 -0
- package/dist/routes/password-reset.d.ts.map +1 -0
- package/dist/routes/password-reset.js +493 -0
- package/dist/routes/password-reset.js.map +1 -0
- package/dist/routes/permissions.d.ts +33 -0
- package/dist/routes/permissions.d.ts.map +1 -0
- package/dist/routes/permissions.js +166 -0
- package/dist/routes/permissions.js.map +1 -0
- package/dist/routes/roles.d.ts +37 -0
- package/dist/routes/roles.d.ts.map +1 -0
- package/dist/routes/roles.js +222 -0
- package/dist/routes/roles.js.map +1 -0
- package/dist/routes/sessions.d.ts +37 -0
- package/dist/routes/sessions.d.ts.map +1 -0
- package/dist/routes/sessions.js +121 -0
- package/dist/routes/sessions.js.map +1 -0
- package/dist/routes/stats.d.ts +28 -0
- package/dist/routes/stats.d.ts.map +1 -0
- package/dist/routes/stats.js +65 -0
- package/dist/routes/stats.js.map +1 -0
- package/dist/routes/two-factor-admin.d.ts +54 -0
- package/dist/routes/two-factor-admin.d.ts.map +1 -0
- package/dist/routes/two-factor-admin.js +344 -0
- package/dist/routes/two-factor-admin.js.map +1 -0
- package/dist/routes/two-factor.d.ts +31 -0
- package/dist/routes/two-factor.d.ts.map +1 -0
- package/dist/routes/two-factor.js +595 -0
- package/dist/routes/two-factor.js.map +1 -0
- package/dist/routes/user-roles.d.ts +33 -0
- package/dist/routes/user-roles.d.ts.map +1 -0
- package/dist/routes/user-roles.js +129 -0
- package/dist/routes/user-roles.js.map +1 -0
- package/dist/routes/users.d.ts +61 -0
- package/dist/routes/users.d.ts.map +1 -0
- package/dist/routes/users.js +868 -0
- package/dist/routes/users.js.map +1 -0
- package/dist/security/decision-context.d.ts +52 -0
- package/dist/security/decision-context.d.ts.map +1 -0
- package/dist/security/decision-context.js +150 -0
- package/dist/security/decision-context.js.map +1 -0
- package/dist/security/decision-event.d.ts +128 -0
- package/dist/security/decision-event.d.ts.map +1 -0
- package/dist/security/decision-event.js +225 -0
- package/dist/security/decision-event.js.map +1 -0
- package/dist/security/transport-decision.d.ts +6 -0
- package/dist/security/transport-decision.d.ts.map +1 -0
- package/dist/security/transport-decision.js +37 -0
- package/dist/security/transport-decision.js.map +1 -0
- package/dist/server.d.ts +50 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +491 -0
- package/dist/server.js.map +1 -0
- package/dist/two-factor/cache.d.ts +47 -0
- package/dist/two-factor/cache.d.ts.map +1 -0
- package/dist/two-factor/cache.js +122 -0
- package/dist/two-factor/cache.js.map +1 -0
- package/dist/two-factor/crypto.d.ts +41 -0
- package/dist/two-factor/crypto.d.ts.map +1 -0
- package/dist/two-factor/crypto.js +94 -0
- package/dist/two-factor/crypto.js.map +1 -0
- package/dist/two-factor/errors.d.ts +82 -0
- package/dist/two-factor/errors.d.ts.map +1 -0
- package/dist/two-factor/errors.js +112 -0
- package/dist/two-factor/errors.js.map +1 -0
- package/dist/two-factor/index.d.ts +18 -0
- package/dist/two-factor/index.d.ts.map +1 -0
- package/dist/two-factor/index.js +25 -0
- package/dist/two-factor/index.js.map +1 -0
- package/dist/two-factor/otp.d.ts +43 -0
- package/dist/two-factor/otp.d.ts.map +1 -0
- package/dist/two-factor/otp.js +57 -0
- package/dist/two-factor/otp.js.map +1 -0
- package/dist/two-factor/recovery.d.ts +45 -0
- package/dist/two-factor/recovery.d.ts.map +1 -0
- package/dist/two-factor/recovery.js +104 -0
- package/dist/two-factor/recovery.js.map +1 -0
- package/dist/two-factor/repository.d.ts +156 -0
- package/dist/two-factor/repository.d.ts.map +1 -0
- package/dist/two-factor/repository.js +251 -0
- package/dist/two-factor/repository.js.map +1 -0
- package/dist/two-factor/service.d.ts +232 -0
- package/dist/two-factor/service.d.ts.map +1 -0
- package/dist/two-factor/service.js +599 -0
- package/dist/two-factor/service.js.map +1 -0
- package/dist/two-factor/totp.d.ts +57 -0
- package/dist/two-factor/totp.d.ts.map +1 -0
- package/dist/two-factor/totp.js +96 -0
- package/dist/two-factor/totp.js.map +1 -0
- package/dist/two-factor/types.d.ts +152 -0
- package/dist/two-factor/types.d.ts.map +1 -0
- package/dist/two-factor/types.js +64 -0
- package/dist/two-factor/types.js.map +1 -0
- package/dist/users/cache.d.ts +49 -0
- package/dist/users/cache.d.ts.map +1 -0
- package/dist/users/cache.js +140 -0
- package/dist/users/cache.js.map +1 -0
- package/dist/users/claims.d.ts +67 -0
- package/dist/users/claims.d.ts.map +1 -0
- package/dist/users/claims.js +104 -0
- package/dist/users/claims.js.map +1 -0
- package/dist/users/errors.d.ts +25 -0
- package/dist/users/errors.d.ts.map +1 -0
- package/dist/users/errors.js +31 -0
- package/dist/users/errors.js.map +1 -0
- package/dist/users/gdpr.d.ts +110 -0
- package/dist/users/gdpr.d.ts.map +1 -0
- package/dist/users/gdpr.js +211 -0
- package/dist/users/gdpr.js.map +1 -0
- package/dist/users/index.d.ts +20 -0
- package/dist/users/index.d.ts.map +1 -0
- package/dist/users/index.js +19 -0
- package/dist/users/index.js.map +1 -0
- package/dist/users/password.d.ts +85 -0
- package/dist/users/password.d.ts.map +1 -0
- package/dist/users/password.js +138 -0
- package/dist/users/password.js.map +1 -0
- package/dist/users/repository.d.ts +263 -0
- package/dist/users/repository.d.ts.map +1 -0
- package/dist/users/repository.js +499 -0
- package/dist/users/repository.js.map +1 -0
- package/dist/users/service.d.ts +299 -0
- package/dist/users/service.d.ts.map +1 -0
- package/dist/users/service.js +698 -0
- package/dist/users/service.js.map +1 -0
- package/dist/users/types.d.ts +220 -0
- package/dist/users/types.d.ts.map +1 -0
- package/dist/users/types.js +77 -0
- package/dist/users/types.js.map +1 -0
- package/locales/default/en/common.json +10 -0
- package/locales/default/en/consent.json +13 -0
- package/locales/default/en/emails.json +17 -0
- package/locales/default/en/errors.json +21 -0
- package/locales/default/en/forgot-password.json +11 -0
- package/locales/default/en/invitation.json +18 -0
- package/locales/default/en/login.json +17 -0
- package/locales/default/en/logout.json +9 -0
- package/locales/default/en/magic-link.json +12 -0
- package/locales/default/en/reset-password.json +16 -0
- package/locales/default/en/two-factor.json +29 -0
- package/migrations/.gitkeep +0 -0
- package/migrations/001_extensions.sql +22 -0
- package/migrations/002_organizations.sql +43 -0
- package/migrations/003_applications.sql +50 -0
- package/migrations/004_clients.sql +74 -0
- package/migrations/005_users.sql +119 -0
- package/migrations/006_roles_permissions.sql +80 -0
- package/migrations/007_custom_claims.sql +55 -0
- package/migrations/008_config.sql +51 -0
- package/migrations/009_audit_log.sql +31 -0
- package/migrations/010_oidc_adapter.sql +34 -0
- package/migrations/011_seed.sql +53 -0
- package/migrations/012_two_factor.sql +88 -0
- package/migrations/013_client_secret_sha256.sql +22 -0
- package/migrations/014_login_methods.sql +34 -0
- package/migrations/015_signing_key_encryption.sql +20 -0
- package/migrations/016_failed_login_count.sql +27 -0
- package/migrations/017_audit_retention.sql +11 -0
- package/migrations/018_admin_api_enhancements.sql +78 -0
- package/migrations/019_invitation_details.sql +27 -0
- package/migrations/020_two_factor_admin_permission.sql +108 -0
- package/migrations/021_auth_recovery_jobs.sql +74 -0
- package/migrations/022_recovery_artifact_ownership.sql +25 -0
- package/migrations/023_magic_link_authority.sql +39 -0
- package/package.json +106 -0
- package/templates/default/emails/invitation.hbs +39 -0
- package/templates/default/emails/invitation.txt.hbs +18 -0
- package/templates/default/emails/magic-link.hbs +30 -0
- package/templates/default/emails/magic-link.txt.hbs +11 -0
- package/templates/default/emails/otp-code.hbs +31 -0
- package/templates/default/emails/otp-code.txt.hbs +15 -0
- package/templates/default/emails/password-changed.hbs +24 -0
- package/templates/default/emails/password-changed.txt.hbs +7 -0
- package/templates/default/emails/password-reset.hbs +30 -0
- package/templates/default/emails/password-reset.txt.hbs +11 -0
- package/templates/default/emails/welcome.hbs +25 -0
- package/templates/default/emails/welcome.txt.hbs +9 -0
- package/templates/default/layouts/main.hbs +49 -0
- package/templates/default/pages/accept-invite.hbs +25 -0
- package/templates/default/pages/consent.hbs +27 -0
- package/templates/default/pages/error.hbs +15 -0
- package/templates/default/pages/forgot-password.hbs +21 -0
- package/templates/default/pages/invite-expired.hbs +6 -0
- package/templates/default/pages/invite-success.hbs +9 -0
- package/templates/default/pages/login.hbs +108 -0
- package/templates/default/pages/logout-success.hbs +5 -0
- package/templates/default/pages/logout.hbs +24 -0
- package/templates/default/pages/magic-link-sent.hbs +10 -0
- package/templates/default/pages/magic-link-success.hbs +17 -0
- package/templates/default/pages/reset-password.hbs +20 -0
- package/templates/default/pages/reset-success.hbs +9 -0
- package/templates/default/pages/two-factor-setup.hbs +65 -0
- package/templates/default/pages/two-factor-verify.hbs +77 -0
- package/templates/default/partials/flash-messages.hbs +10 -0
- package/templates/default/partials/footer.hbs +4 -0
- package/templates/default/partials/header.hbs +8 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role service — business logic for role management.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates role CRUD operations with slug validation, uniqueness
|
|
5
|
+
* checks, deletion guards, cache management, and audit logging.
|
|
6
|
+
* Also handles role-permission assignment operations.
|
|
7
|
+
*
|
|
8
|
+
* All write operations follow the pattern:
|
|
9
|
+
* validate → DB operation → cache invalidate/re-cache → audit log
|
|
10
|
+
*
|
|
11
|
+
* Uses fire-and-forget audit logging — audit failures never block
|
|
12
|
+
* the primary operation.
|
|
13
|
+
*
|
|
14
|
+
* @see role-repository.ts — Database operations
|
|
15
|
+
* @see mapping-repository.ts — Role-permission join table operations
|
|
16
|
+
* @see cache.ts — Redis cache operations
|
|
17
|
+
*/
|
|
18
|
+
import { insertRole, findRoleById as repoFindRoleById, findRoleBySlug as repoFindRoleBySlug, updateRole as repoUpdateRole, deleteRole as repoDeleteRole, listRolesByApplication as repoListRolesByApplication, roleSlugExists, countUsersWithRole, } from './role-repository.js';
|
|
19
|
+
import { assignPermissionsToRole as repoAssignPermissions, removePermissionsFromRole as repoRemovePermissions, getPermissionsForRole as repoGetPermissionsForRole, } from './mapping-repository.js';
|
|
20
|
+
import { getCachedRole, setCachedRole, invalidateRoleCache, invalidateAllUserRbacCaches, } from './cache.js';
|
|
21
|
+
import { generateRoleSlug, validateRoleSlug } from './slugs.js';
|
|
22
|
+
import { RoleNotFoundError, RbacValidationError } from './errors.js';
|
|
23
|
+
import { writeAuditLog } from '../lib/audit-log.js';
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Create
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
/**
|
|
28
|
+
* Create a new role for an application.
|
|
29
|
+
*
|
|
30
|
+
* Auto-generates a slug from the name if not explicitly provided.
|
|
31
|
+
* Validates slug format and ensures uniqueness within the application.
|
|
32
|
+
*
|
|
33
|
+
* @param input - Role creation data
|
|
34
|
+
* @param actorId - Optional UUID of the admin performing the action
|
|
35
|
+
* @returns The newly created role
|
|
36
|
+
* @throws RbacValidationError if slug is invalid or already exists
|
|
37
|
+
*/
|
|
38
|
+
export async function createRole(input, actorId) {
|
|
39
|
+
// Generate slug from name if not provided
|
|
40
|
+
const slug = input.slug ?? generateRoleSlug(input.name);
|
|
41
|
+
// Validate slug format
|
|
42
|
+
if (!validateRoleSlug(slug)) {
|
|
43
|
+
throw new RbacValidationError(`Invalid role slug format: "${slug}". Must be 1-100 chars, lowercase alphanumeric and hyphens.`);
|
|
44
|
+
}
|
|
45
|
+
// Check slug uniqueness within the application
|
|
46
|
+
const exists = await roleSlugExists(input.applicationId, slug);
|
|
47
|
+
if (exists) {
|
|
48
|
+
throw new RbacValidationError(`Role slug "${slug}" already exists for this application.`);
|
|
49
|
+
}
|
|
50
|
+
// Insert with the validated slug
|
|
51
|
+
const role = await insertRole({ ...input, slug });
|
|
52
|
+
// Cache the new role
|
|
53
|
+
await setCachedRole(role);
|
|
54
|
+
// Audit log (fire-and-forget)
|
|
55
|
+
void writeAuditLog({
|
|
56
|
+
eventType: 'role.created',
|
|
57
|
+
eventCategory: 'admin',
|
|
58
|
+
actorId,
|
|
59
|
+
metadata: { roleId: role.id, applicationId: role.applicationId, slug: role.slug },
|
|
60
|
+
});
|
|
61
|
+
return role;
|
|
62
|
+
}
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Read
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
/**
|
|
67
|
+
* Find a role by ID. Cache-first, falls back to DB.
|
|
68
|
+
*
|
|
69
|
+
* @param id - Role UUID
|
|
70
|
+
* @returns Role or null if not found
|
|
71
|
+
*/
|
|
72
|
+
export async function findRoleById(id) {
|
|
73
|
+
// Try cache first
|
|
74
|
+
const cached = await getCachedRole(id);
|
|
75
|
+
if (cached)
|
|
76
|
+
return cached;
|
|
77
|
+
// Cache miss — query DB
|
|
78
|
+
const role = await repoFindRoleById(id);
|
|
79
|
+
if (role) {
|
|
80
|
+
// Cache for future lookups
|
|
81
|
+
await setCachedRole(role);
|
|
82
|
+
}
|
|
83
|
+
return role;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Find a role by application ID and slug.
|
|
87
|
+
*
|
|
88
|
+
* @param applicationId - Application UUID
|
|
89
|
+
* @param slug - Role slug
|
|
90
|
+
* @returns Role or null if not found
|
|
91
|
+
*/
|
|
92
|
+
export async function findRoleBySlug(applicationId, slug) {
|
|
93
|
+
return repoFindRoleBySlug(applicationId, slug);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* List all roles for an application.
|
|
97
|
+
*
|
|
98
|
+
* @param applicationId - Application UUID
|
|
99
|
+
* @returns Array of roles
|
|
100
|
+
*/
|
|
101
|
+
export async function listRolesByApplication(applicationId) {
|
|
102
|
+
return repoListRolesByApplication(applicationId);
|
|
103
|
+
}
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Update
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
/**
|
|
108
|
+
* Update a role by ID.
|
|
109
|
+
*
|
|
110
|
+
* If the slug is being changed, validates the new slug format and
|
|
111
|
+
* checks uniqueness within the application. Invalidates and re-caches
|
|
112
|
+
* the role after update.
|
|
113
|
+
*
|
|
114
|
+
* @param id - Role UUID
|
|
115
|
+
* @param input - Fields to update
|
|
116
|
+
* @param actorId - Optional UUID of the admin performing the action
|
|
117
|
+
* @returns Updated role
|
|
118
|
+
* @throws RoleNotFoundError if role doesn't exist
|
|
119
|
+
* @throws RbacValidationError if new slug is invalid or already exists
|
|
120
|
+
*/
|
|
121
|
+
export async function updateRole(id, input, actorId) {
|
|
122
|
+
// Verify role exists (needed for applicationId if slug is changing)
|
|
123
|
+
const existing = await repoFindRoleById(id);
|
|
124
|
+
if (!existing) {
|
|
125
|
+
throw new RoleNotFoundError(id);
|
|
126
|
+
}
|
|
127
|
+
// If slug is changing, validate format and uniqueness
|
|
128
|
+
if (input.slug !== undefined && input.slug !== existing.slug) {
|
|
129
|
+
if (!validateRoleSlug(input.slug)) {
|
|
130
|
+
throw new RbacValidationError(`Invalid role slug format: "${input.slug}". Must be 1-100 chars, lowercase alphanumeric and hyphens.`);
|
|
131
|
+
}
|
|
132
|
+
const slugTaken = await roleSlugExists(existing.applicationId, input.slug, id);
|
|
133
|
+
if (slugTaken) {
|
|
134
|
+
throw new RbacValidationError(`Role slug "${input.slug}" already exists for this application.`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Perform the update
|
|
138
|
+
const updated = await repoUpdateRole(id, input);
|
|
139
|
+
// Invalidate old cache and store updated role
|
|
140
|
+
await invalidateRoleCache(id);
|
|
141
|
+
await setCachedRole(updated);
|
|
142
|
+
// Audit log (fire-and-forget)
|
|
143
|
+
void writeAuditLog({
|
|
144
|
+
eventType: 'role.updated',
|
|
145
|
+
eventCategory: 'admin',
|
|
146
|
+
actorId,
|
|
147
|
+
metadata: { roleId: id, changes: input },
|
|
148
|
+
});
|
|
149
|
+
return updated;
|
|
150
|
+
}
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Delete
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
/**
|
|
155
|
+
* Delete a role by ID.
|
|
156
|
+
*
|
|
157
|
+
* By default (force=false), refuses to delete roles that have users
|
|
158
|
+
* assigned. Pass force=true to delete regardless — CASCADE will
|
|
159
|
+
* remove user_roles and role_permissions entries.
|
|
160
|
+
*
|
|
161
|
+
* @param id - Role UUID
|
|
162
|
+
* @param force - If true, delete even if users are assigned
|
|
163
|
+
* @param actorId - Optional UUID of the admin performing the action
|
|
164
|
+
* @throws RoleNotFoundError if role doesn't exist
|
|
165
|
+
* @throws RbacValidationError if users are assigned and force is false
|
|
166
|
+
*/
|
|
167
|
+
export async function deleteRole(id, force = false, actorId) {
|
|
168
|
+
// Verify role exists
|
|
169
|
+
const existing = await repoFindRoleById(id);
|
|
170
|
+
if (!existing) {
|
|
171
|
+
throw new RoleNotFoundError(id);
|
|
172
|
+
}
|
|
173
|
+
// Deletion guard: check for assigned users
|
|
174
|
+
if (!force) {
|
|
175
|
+
const userCount = await countUsersWithRole(id);
|
|
176
|
+
if (userCount > 0) {
|
|
177
|
+
throw new RbacValidationError(`Cannot delete role "${existing.slug}": ${userCount} user(s) still assigned. Use force=true to override.`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Delete the role (CASCADE handles related records)
|
|
181
|
+
await repoDeleteRole(id);
|
|
182
|
+
// Invalidate caches
|
|
183
|
+
await invalidateRoleCache(id);
|
|
184
|
+
// User caches may be stale if role had users (force=true case)
|
|
185
|
+
if (force) {
|
|
186
|
+
await invalidateAllUserRbacCaches();
|
|
187
|
+
}
|
|
188
|
+
// Audit log (fire-and-forget)
|
|
189
|
+
void writeAuditLog({
|
|
190
|
+
eventType: 'role.deleted',
|
|
191
|
+
eventCategory: 'admin',
|
|
192
|
+
actorId,
|
|
193
|
+
metadata: { roleId: id, slug: existing.slug, force },
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
// Role-Permission management
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
/**
|
|
200
|
+
* Assign permissions to a role.
|
|
201
|
+
*
|
|
202
|
+
* Delegates to the mapping repository for bulk insert. Invalidates
|
|
203
|
+
* all user RBAC caches since user permissions may have changed.
|
|
204
|
+
*
|
|
205
|
+
* @param roleId - Role UUID
|
|
206
|
+
* @param permissionIds - Array of permission UUIDs to assign
|
|
207
|
+
* @param actorId - Optional UUID of the admin performing the action
|
|
208
|
+
*/
|
|
209
|
+
export async function assignPermissionsToRole(roleId, permissionIds, actorId) {
|
|
210
|
+
if (permissionIds.length === 0)
|
|
211
|
+
return;
|
|
212
|
+
await repoAssignPermissions(roleId, permissionIds);
|
|
213
|
+
// Invalidate all user RBAC caches — permissions through roles may have changed
|
|
214
|
+
await invalidateAllUserRbacCaches();
|
|
215
|
+
// Audit log (fire-and-forget)
|
|
216
|
+
void writeAuditLog({
|
|
217
|
+
eventType: 'role.permissions.assigned',
|
|
218
|
+
eventCategory: 'admin',
|
|
219
|
+
actorId,
|
|
220
|
+
metadata: { roleId, permissionIds },
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Remove permissions from a role.
|
|
225
|
+
*
|
|
226
|
+
* Delegates to the mapping repository. Invalidates all user RBAC
|
|
227
|
+
* caches since user permissions may have changed.
|
|
228
|
+
*
|
|
229
|
+
* @param roleId - Role UUID
|
|
230
|
+
* @param permissionIds - Array of permission UUIDs to remove
|
|
231
|
+
* @param actorId - Optional UUID of the admin performing the action
|
|
232
|
+
*/
|
|
233
|
+
export async function removePermissionsFromRole(roleId, permissionIds, actorId) {
|
|
234
|
+
if (permissionIds.length === 0)
|
|
235
|
+
return;
|
|
236
|
+
await repoRemovePermissions(roleId, permissionIds);
|
|
237
|
+
// Invalidate all user RBAC caches
|
|
238
|
+
await invalidateAllUserRbacCaches();
|
|
239
|
+
// Audit log (fire-and-forget)
|
|
240
|
+
void writeAuditLog({
|
|
241
|
+
eventType: 'role.permissions.removed',
|
|
242
|
+
eventCategory: 'admin',
|
|
243
|
+
actorId,
|
|
244
|
+
metadata: { roleId, permissionIds },
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Get all permissions assigned to a role.
|
|
249
|
+
*
|
|
250
|
+
* @param roleId - Role UUID
|
|
251
|
+
* @returns Array of permissions
|
|
252
|
+
*/
|
|
253
|
+
export async function getPermissionsForRole(roleId) {
|
|
254
|
+
return repoGetPermissionsForRole(roleId);
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=role-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-service.js","sourceRoot":"","sources":["../../src/rbac/role-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slug generation and validation utilities for RBAC.
|
|
3
|
+
*
|
|
4
|
+
* Two types of slugs are used in the RBAC module:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Role slugs** — kebab-case identifiers (e.g., "crm-editor").
|
|
7
|
+
* Same format as organization/application slugs. Auto-generated
|
|
8
|
+
* from role names or provided manually.
|
|
9
|
+
*
|
|
10
|
+
* 2. **Permission slugs** — colon-separated namespaced identifiers
|
|
11
|
+
* following the `module:resource:action` format (e.g., "crm:contacts:read").
|
|
12
|
+
* Each segment must be lowercase alphanumeric with hyphens.
|
|
13
|
+
* Minimum 3 segments required.
|
|
14
|
+
*
|
|
15
|
+
* This module provides:
|
|
16
|
+
* - `generateRoleSlug(name)` — Derive a role slug from a role name
|
|
17
|
+
* - `validateRoleSlug(slug)` — Check role slug format (kebab-case, 1-100 chars)
|
|
18
|
+
* - `validatePermissionSlug(slug)` — Check permission slug follows module:resource:action
|
|
19
|
+
* - `parsePermissionSlug(slug)` — Decompose a permission slug into its parts
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Generate a URL-safe slug from a role name.
|
|
23
|
+
*
|
|
24
|
+
* Uses the same slugification logic as organizations/applications:
|
|
25
|
+
* 1. Convert to lowercase
|
|
26
|
+
* 2. Replace non-alphanumeric characters with hyphens
|
|
27
|
+
* 3. Collapse multiple consecutive hyphens into one
|
|
28
|
+
* 4. Trim leading and trailing hyphens
|
|
29
|
+
* 5. Truncate to 100 characters (trimming trailing hyphens after truncation)
|
|
30
|
+
*
|
|
31
|
+
* Returns an empty string for empty/whitespace-only input — the caller
|
|
32
|
+
* (service layer) is responsible for validation.
|
|
33
|
+
*
|
|
34
|
+
* @param name - Role name to derive a slug from
|
|
35
|
+
* @returns Generated slug string
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* generateRoleSlug('CRM Editor') // "crm-editor"
|
|
39
|
+
* generateRoleSlug('Invoice Approver') // "invoice-approver"
|
|
40
|
+
* generateRoleSlug('') // ""
|
|
41
|
+
*/
|
|
42
|
+
export declare function generateRoleSlug(name: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Validate a role slug format.
|
|
45
|
+
*
|
|
46
|
+
* Rules:
|
|
47
|
+
* - 1–100 characters long
|
|
48
|
+
* - Lowercase alphanumeric characters and hyphens only
|
|
49
|
+
* - Cannot start or end with a hyphen
|
|
50
|
+
* - Empty strings are invalid
|
|
51
|
+
*
|
|
52
|
+
* Does NOT check uniqueness — that's the repository/service layer's job.
|
|
53
|
+
*
|
|
54
|
+
* @param slug - Role slug string to validate
|
|
55
|
+
* @returns true if the slug format is valid, false otherwise
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* validateRoleSlug('crm-editor') // true
|
|
59
|
+
* validateRoleSlug('admin') // true
|
|
60
|
+
* validateRoleSlug('CRM Editor') // false (spaces, uppercase)
|
|
61
|
+
* validateRoleSlug('') // false (empty)
|
|
62
|
+
*/
|
|
63
|
+
export declare function validateRoleSlug(slug: string): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Validate a permission slug follows the module:resource:action format.
|
|
66
|
+
*
|
|
67
|
+
* Rules:
|
|
68
|
+
* - Must have at least 3 segments separated by colons
|
|
69
|
+
* - Each segment must be lowercase alphanumeric with hyphens
|
|
70
|
+
* - Each segment must start and end with an alphanumeric character
|
|
71
|
+
* - Empty segments are not allowed
|
|
72
|
+
* - Total length must not exceed 150 characters (matches DB column)
|
|
73
|
+
*
|
|
74
|
+
* @param slug - Permission slug to validate
|
|
75
|
+
* @returns true if the slug format is valid, false otherwise
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* validatePermissionSlug('crm:contacts:read') // true
|
|
79
|
+
* validatePermissionSlug('admin:system:manage') // true
|
|
80
|
+
* validatePermissionSlug('crm:sub-module:items:write') // true (4+ segments OK)
|
|
81
|
+
* validatePermissionSlug('contacts-read') // false (no colons)
|
|
82
|
+
* validatePermissionSlug('a:b') // false (only 2 segments)
|
|
83
|
+
* validatePermissionSlug('') // false (empty)
|
|
84
|
+
*/
|
|
85
|
+
export declare function validatePermissionSlug(slug: string): boolean;
|
|
86
|
+
/** Parsed components of a permission slug. */
|
|
87
|
+
export interface ParsedPermissionSlug {
|
|
88
|
+
module: string;
|
|
89
|
+
resource: string;
|
|
90
|
+
action: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Parse a permission slug into its module, resource, and action components.
|
|
94
|
+
*
|
|
95
|
+
* The first segment is the module, the last segment is the action,
|
|
96
|
+
* and everything in between is joined as the resource. This supports
|
|
97
|
+
* both 3-segment slugs (crm:contacts:read) and 4+ segment slugs
|
|
98
|
+
* (crm:sub-module:items:write → module="crm", resource="sub-module:items", action="write").
|
|
99
|
+
*
|
|
100
|
+
* Returns null if the slug is invalid (fails validatePermissionSlug).
|
|
101
|
+
*
|
|
102
|
+
* @param slug - Permission slug to parse
|
|
103
|
+
* @returns Parsed components or null if invalid
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* parsePermissionSlug('crm:contacts:read')
|
|
107
|
+
* // { module: 'crm', resource: 'contacts', action: 'read' }
|
|
108
|
+
*
|
|
109
|
+
* parsePermissionSlug('crm:sub:items:write')
|
|
110
|
+
* // { module: 'crm', resource: 'sub:items', action: 'write' }
|
|
111
|
+
*
|
|
112
|
+
* parsePermissionSlug('invalid')
|
|
113
|
+
* // null
|
|
114
|
+
*/
|
|
115
|
+
export declare function parsePermissionSlug(slug: string): ParsedPermissionSlug | null;
|
|
116
|
+
//# sourceMappingURL=slugs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slugs.d.ts","sourceRoot":"","sources":["../../src/rbac/slugs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA2BH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgBrD;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOtD;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAgB5D;AAMD,8CAA8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAY7E"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slug generation and validation utilities for RBAC.
|
|
3
|
+
*
|
|
4
|
+
* Two types of slugs are used in the RBAC module:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Role slugs** — kebab-case identifiers (e.g., "crm-editor").
|
|
7
|
+
* Same format as organization/application slugs. Auto-generated
|
|
8
|
+
* from role names or provided manually.
|
|
9
|
+
*
|
|
10
|
+
* 2. **Permission slugs** — colon-separated namespaced identifiers
|
|
11
|
+
* following the `module:resource:action` format (e.g., "crm:contacts:read").
|
|
12
|
+
* Each segment must be lowercase alphanumeric with hyphens.
|
|
13
|
+
* Minimum 3 segments required.
|
|
14
|
+
*
|
|
15
|
+
* This module provides:
|
|
16
|
+
* - `generateRoleSlug(name)` — Derive a role slug from a role name
|
|
17
|
+
* - `validateRoleSlug(slug)` — Check role slug format (kebab-case, 1-100 chars)
|
|
18
|
+
* - `validatePermissionSlug(slug)` — Check permission slug follows module:resource:action
|
|
19
|
+
* - `parsePermissionSlug(slug)` — Decompose a permission slug into its parts
|
|
20
|
+
*/
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Role slug format
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/**
|
|
25
|
+
* Role slug format regex: 1–100 characters, lowercase alphanumeric + hyphens.
|
|
26
|
+
* Must start and end with an alphanumeric character (no leading/trailing hyphens).
|
|
27
|
+
* Single character slugs are allowed (unlike organization slugs which require 3+).
|
|
28
|
+
*/
|
|
29
|
+
const ROLE_SLUG_REGEX = /^[a-z0-9]([a-z0-9-]{0,98}[a-z0-9])?$/;
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Permission slug format
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
/**
|
|
34
|
+
* Each segment of a permission slug: lowercase alphanumeric + hyphens,
|
|
35
|
+
* must start and end with an alphanumeric character.
|
|
36
|
+
*/
|
|
37
|
+
const PERMISSION_SEGMENT_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Role slug generation
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
/**
|
|
42
|
+
* Generate a URL-safe slug from a role name.
|
|
43
|
+
*
|
|
44
|
+
* Uses the same slugification logic as organizations/applications:
|
|
45
|
+
* 1. Convert to lowercase
|
|
46
|
+
* 2. Replace non-alphanumeric characters with hyphens
|
|
47
|
+
* 3. Collapse multiple consecutive hyphens into one
|
|
48
|
+
* 4. Trim leading and trailing hyphens
|
|
49
|
+
* 5. Truncate to 100 characters (trimming trailing hyphens after truncation)
|
|
50
|
+
*
|
|
51
|
+
* Returns an empty string for empty/whitespace-only input — the caller
|
|
52
|
+
* (service layer) is responsible for validation.
|
|
53
|
+
*
|
|
54
|
+
* @param name - Role name to derive a slug from
|
|
55
|
+
* @returns Generated slug string
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* generateRoleSlug('CRM Editor') // "crm-editor"
|
|
59
|
+
* generateRoleSlug('Invoice Approver') // "invoice-approver"
|
|
60
|
+
* generateRoleSlug('') // ""
|
|
61
|
+
*/
|
|
62
|
+
export function generateRoleSlug(name) {
|
|
63
|
+
if (!name || !name.trim()) {
|
|
64
|
+
return '';
|
|
65
|
+
}
|
|
66
|
+
const slug = name
|
|
67
|
+
.toLowerCase()
|
|
68
|
+
// Replace any character that's not a-z, 0-9, or hyphen with a hyphen
|
|
69
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
70
|
+
// Collapse consecutive hyphens into a single hyphen
|
|
71
|
+
.replace(/-+/g, '-')
|
|
72
|
+
// Remove leading and trailing hyphens
|
|
73
|
+
.replace(/^-+|-+$/g, '');
|
|
74
|
+
// Truncate to 100 characters, then trim any trailing hyphen caused by truncation
|
|
75
|
+
return slug.slice(0, 100).replace(/-+$/, '');
|
|
76
|
+
}
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Role slug validation
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
/**
|
|
81
|
+
* Validate a role slug format.
|
|
82
|
+
*
|
|
83
|
+
* Rules:
|
|
84
|
+
* - 1–100 characters long
|
|
85
|
+
* - Lowercase alphanumeric characters and hyphens only
|
|
86
|
+
* - Cannot start or end with a hyphen
|
|
87
|
+
* - Empty strings are invalid
|
|
88
|
+
*
|
|
89
|
+
* Does NOT check uniqueness — that's the repository/service layer's job.
|
|
90
|
+
*
|
|
91
|
+
* @param slug - Role slug string to validate
|
|
92
|
+
* @returns true if the slug format is valid, false otherwise
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* validateRoleSlug('crm-editor') // true
|
|
96
|
+
* validateRoleSlug('admin') // true
|
|
97
|
+
* validateRoleSlug('CRM Editor') // false (spaces, uppercase)
|
|
98
|
+
* validateRoleSlug('') // false (empty)
|
|
99
|
+
*/
|
|
100
|
+
export function validateRoleSlug(slug) {
|
|
101
|
+
// Must be 1–100 characters
|
|
102
|
+
if (!slug || slug.length > 100) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return ROLE_SLUG_REGEX.test(slug);
|
|
106
|
+
}
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Permission slug validation
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
/**
|
|
111
|
+
* Validate a permission slug follows the module:resource:action format.
|
|
112
|
+
*
|
|
113
|
+
* Rules:
|
|
114
|
+
* - Must have at least 3 segments separated by colons
|
|
115
|
+
* - Each segment must be lowercase alphanumeric with hyphens
|
|
116
|
+
* - Each segment must start and end with an alphanumeric character
|
|
117
|
+
* - Empty segments are not allowed
|
|
118
|
+
* - Total length must not exceed 150 characters (matches DB column)
|
|
119
|
+
*
|
|
120
|
+
* @param slug - Permission slug to validate
|
|
121
|
+
* @returns true if the slug format is valid, false otherwise
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* validatePermissionSlug('crm:contacts:read') // true
|
|
125
|
+
* validatePermissionSlug('admin:system:manage') // true
|
|
126
|
+
* validatePermissionSlug('crm:sub-module:items:write') // true (4+ segments OK)
|
|
127
|
+
* validatePermissionSlug('contacts-read') // false (no colons)
|
|
128
|
+
* validatePermissionSlug('a:b') // false (only 2 segments)
|
|
129
|
+
* validatePermissionSlug('') // false (empty)
|
|
130
|
+
*/
|
|
131
|
+
export function validatePermissionSlug(slug) {
|
|
132
|
+
// Must be non-empty and within DB column limit (VARCHAR(150))
|
|
133
|
+
if (!slug || slug.length > 150) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
// Split on colons — need at least 3 segments
|
|
137
|
+
const segments = slug.split(':');
|
|
138
|
+
if (segments.length < 3) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
// Each segment must match the segment format
|
|
142
|
+
return segments.every((segment) => segment.length > 0 && PERMISSION_SEGMENT_REGEX.test(segment));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Parse a permission slug into its module, resource, and action components.
|
|
146
|
+
*
|
|
147
|
+
* The first segment is the module, the last segment is the action,
|
|
148
|
+
* and everything in between is joined as the resource. This supports
|
|
149
|
+
* both 3-segment slugs (crm:contacts:read) and 4+ segment slugs
|
|
150
|
+
* (crm:sub-module:items:write → module="crm", resource="sub-module:items", action="write").
|
|
151
|
+
*
|
|
152
|
+
* Returns null if the slug is invalid (fails validatePermissionSlug).
|
|
153
|
+
*
|
|
154
|
+
* @param slug - Permission slug to parse
|
|
155
|
+
* @returns Parsed components or null if invalid
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* parsePermissionSlug('crm:contacts:read')
|
|
159
|
+
* // { module: 'crm', resource: 'contacts', action: 'read' }
|
|
160
|
+
*
|
|
161
|
+
* parsePermissionSlug('crm:sub:items:write')
|
|
162
|
+
* // { module: 'crm', resource: 'sub:items', action: 'write' }
|
|
163
|
+
*
|
|
164
|
+
* parsePermissionSlug('invalid')
|
|
165
|
+
* // null
|
|
166
|
+
*/
|
|
167
|
+
export function parsePermissionSlug(slug) {
|
|
168
|
+
if (!validatePermissionSlug(slug)) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const segments = slug.split(':');
|
|
172
|
+
// First segment = module, last = action, middle = resource
|
|
173
|
+
const module = segments[0];
|
|
174
|
+
const action = segments[segments.length - 1];
|
|
175
|
+
const resource = segments.slice(1, -1).join(':');
|
|
176
|
+
return { module, resource, action };
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=slugs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slugs.js","sourceRoot":"","sources":["../../src/rbac/slugs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,eAAe,GAAG,sCAAsC,CAAC;AAE/D,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,wBAAwB,GAAG,iCAAiC,CAAC;AAEnE,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,IAAI,GAAG,IAAI;SACd,WAAW,EAAE;QACd,qEAAqE;SACpE,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;QAC5B,oDAAoD;SACnD,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;QACpB,sCAAsC;SACrC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAE3B,iFAAiF;IACjF,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,2BAA2B;IAC3B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,8DAA8D;IAC9D,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,6CAA6C;IAC7C,OAAO,QAAQ,CAAC,KAAK,CACnB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,CAC1E,CAAC;AACJ,CAAC;AAaD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,2DAA2D;IAC3D,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEjD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AACtC,CAAC"}
|