@open-mercato/enterprise 0.5.1-develop.2691.d8a0934b37 → 0.5.1-develop.2694.732417c5ec

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.
Files changed (32) hide show
  1. package/dist/modules/record_locks/data/entities.js +2 -1
  2. package/dist/modules/record_locks/data/entities.js.map +2 -2
  3. package/dist/modules/record_locks/lib/recordLockService.js +19 -15
  4. package/dist/modules/record_locks/lib/recordLockService.js.map +2 -2
  5. package/dist/modules/security/data/entities.js +1 -1
  6. package/dist/modules/security/data/entities.js.map +1 -1
  7. package/dist/modules/sso/data/entities.js +1 -1
  8. package/dist/modules/sso/data/entities.js.map +2 -2
  9. package/dist/modules/sso/services/accountLinkingService.js +4 -4
  10. package/dist/modules/sso/services/accountLinkingService.js.map +2 -2
  11. package/dist/modules/sso/services/hrdService.js +3 -2
  12. package/dist/modules/sso/services/hrdService.js.map +2 -2
  13. package/dist/modules/sso/services/scimService.js +7 -7
  14. package/dist/modules/sso/services/scimService.js.map +2 -2
  15. package/dist/modules/sso/services/scimTokenService.js +1 -1
  16. package/dist/modules/sso/services/scimTokenService.js.map +2 -2
  17. package/dist/modules/sso/services/ssoConfigService.js +1 -1
  18. package/dist/modules/sso/services/ssoConfigService.js.map +2 -2
  19. package/dist/modules/sso/setup.js +1 -1
  20. package/dist/modules/sso/setup.js.map +2 -2
  21. package/jest.config.cjs +4 -2
  22. package/package.json +5 -5
  23. package/src/modules/record_locks/data/entities.ts +2 -1
  24. package/src/modules/record_locks/lib/recordLockService.ts +33 -28
  25. package/src/modules/security/data/entities.ts +1 -1
  26. package/src/modules/sso/data/entities.ts +1 -1
  27. package/src/modules/sso/services/accountLinkingService.ts +4 -4
  28. package/src/modules/sso/services/hrdService.ts +10 -7
  29. package/src/modules/sso/services/scimService.ts +7 -7
  30. package/src/modules/sso/services/scimTokenService.ts +1 -1
  31. package/src/modules/sso/services/ssoConfigService.ts +1 -1
  32. package/src/modules/sso/setup.ts +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/security/data/entities.ts"],
4
- "sourcesContent": ["import { Entity, Index, PrimaryKey, Property } from '@mikro-orm/core'\nimport {\n ChallengeMethod,\n EnforcementScope,\n MfaMethodType,\n SudoChallengeMethodUsed,\n} from './constants'\n\nexport {\n ChallengeMethod,\n EnforcementScope,\n MfaMethodType,\n SudoChallengeMethodUsed,\n} from './constants'\n\n@Entity({ tableName: 'user_mfa_methods' })\n@Index({ name: 'idx_user_mfa_methods_user_type', properties: ['userId', 'type', 'isActive'] })\n@Index({ name: 'idx_user_mfa_methods_tenant', properties: ['tenantId'] })\nexport class UserMfaMethod {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ type: 'text' })\n type!: string\n\n @Property({ type: 'text', nullable: true })\n label?: string | null\n\n @Property({ type: 'text', nullable: true })\n secret?: string | null\n\n @Property({ name: 'provider_metadata', type: 'jsonb', nullable: true })\n providerMetadata?: Record<string, unknown> | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'last_used_at', type: Date, nullable: true })\n lastUsedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'mfa_recovery_codes' })\n@Index({ name: 'idx_mfa_recovery_codes_user', properties: ['userId', 'isUsed'] })\nexport class MfaRecoveryCode {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'code_hash', type: 'text' })\n codeHash!: string\n\n @Property({ name: 'is_used', type: 'boolean', default: false })\n isUsed: boolean = false\n\n @Property({ name: 'used_at', type: Date, nullable: true })\n usedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'mfa_enforcement_policies' })\n@Index({ name: 'idx_mfa_enforcement_scope', properties: ['scope', 'tenantId'] })\nexport class MfaEnforcementPolicy {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ type: 'text' })\n scope!: EnforcementScope\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'is_enforced', type: 'boolean', default: true })\n isEnforced: boolean = true\n\n @Property({ name: 'allowed_methods', type: 'jsonb', nullable: true })\n allowedMethods?: string[] | null\n\n @Property({ name: 'enforcement_deadline', type: Date, nullable: true })\n enforcementDeadline?: Date | null\n\n @Property({ name: 'enforced_by', type: 'uuid' })\n enforcedBy!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'sudo_challenge_configs' })\n@Index({ name: 'idx_sudo_configs_target', properties: ['targetIdentifier'] })\nexport class SudoChallengeConfig {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'label', type: 'text', nullable: true })\n label: string | null = null\n\n @Property({ name: 'target_identifier', type: 'text' })\n targetIdentifier!: string\n\n @Property({ name: 'is_enabled', type: 'boolean', default: true })\n isEnabled: boolean = true\n\n @Property({ name: 'is_developer_default', type: 'boolean', default: false })\n isDeveloperDefault: boolean = false\n\n @Property({ name: 'ttl_seconds', type: 'integer', default: 300 })\n ttlSeconds: number = 300\n\n @Property({ name: 'challenge_method', type: 'text', default: 'auto' })\n challengeMethod: ChallengeMethod = ChallengeMethod.AUTO\n\n @Property({ name: 'configured_by', type: 'uuid', nullable: true })\n configuredBy?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'sudo_sessions' })\n@Index({ name: 'idx_sudo_sessions_token', properties: ['sessionToken', 'expiresAt'] })\nexport class SudoSession {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'session_token', type: 'text' })\n sessionToken!: string\n\n @Property({ name: 'challenge_method', type: 'text' })\n challengeMethod!: string\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'mfa_challenges' })\n@Index({ name: 'idx_mfa_challenges_lookup', properties: ['id', 'expiresAt'] })\nexport class MfaChallenge {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'otp_code_hash', type: 'text', nullable: true })\n otpCodeHash?: string | null\n\n @Property({ name: 'method_type', type: 'text', nullable: true })\n methodType?: string | null\n\n @Property({ name: 'method_id', type: 'uuid', nullable: true })\n methodId?: string | null\n\n @Property({ name: 'provider_challenge', type: 'jsonb', nullable: true })\n providerChallenge?: Record<string, unknown> | null\n\n @Property({ type: 'integer', default: 0 })\n attempts: number = 0\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'verified_at', type: Date, nullable: true })\n verifiedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n"],
4
+ "sourcesContent": ["import { Entity, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legacy'\nimport {\n ChallengeMethod,\n EnforcementScope,\n MfaMethodType,\n SudoChallengeMethodUsed,\n} from './constants'\n\nexport {\n ChallengeMethod,\n EnforcementScope,\n MfaMethodType,\n SudoChallengeMethodUsed,\n} from './constants'\n\n@Entity({ tableName: 'user_mfa_methods' })\n@Index({ name: 'idx_user_mfa_methods_user_type', properties: ['userId', 'type', 'isActive'] })\n@Index({ name: 'idx_user_mfa_methods_tenant', properties: ['tenantId'] })\nexport class UserMfaMethod {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ type: 'text' })\n type!: string\n\n @Property({ type: 'text', nullable: true })\n label?: string | null\n\n @Property({ type: 'text', nullable: true })\n secret?: string | null\n\n @Property({ name: 'provider_metadata', type: 'jsonb', nullable: true })\n providerMetadata?: Record<string, unknown> | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'last_used_at', type: Date, nullable: true })\n lastUsedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'mfa_recovery_codes' })\n@Index({ name: 'idx_mfa_recovery_codes_user', properties: ['userId', 'isUsed'] })\nexport class MfaRecoveryCode {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'code_hash', type: 'text' })\n codeHash!: string\n\n @Property({ name: 'is_used', type: 'boolean', default: false })\n isUsed: boolean = false\n\n @Property({ name: 'used_at', type: Date, nullable: true })\n usedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'mfa_enforcement_policies' })\n@Index({ name: 'idx_mfa_enforcement_scope', properties: ['scope', 'tenantId'] })\nexport class MfaEnforcementPolicy {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ type: 'text' })\n scope!: EnforcementScope\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'is_enforced', type: 'boolean', default: true })\n isEnforced: boolean = true\n\n @Property({ name: 'allowed_methods', type: 'jsonb', nullable: true })\n allowedMethods?: string[] | null\n\n @Property({ name: 'enforcement_deadline', type: Date, nullable: true })\n enforcementDeadline?: Date | null\n\n @Property({ name: 'enforced_by', type: 'uuid' })\n enforcedBy!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'sudo_challenge_configs' })\n@Index({ name: 'idx_sudo_configs_target', properties: ['targetIdentifier'] })\nexport class SudoChallengeConfig {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid', nullable: true })\n organizationId?: string | null\n\n @Property({ name: 'label', type: 'text', nullable: true })\n label: string | null = null\n\n @Property({ name: 'target_identifier', type: 'text' })\n targetIdentifier!: string\n\n @Property({ name: 'is_enabled', type: 'boolean', default: true })\n isEnabled: boolean = true\n\n @Property({ name: 'is_developer_default', type: 'boolean', default: false })\n isDeveloperDefault: boolean = false\n\n @Property({ name: 'ttl_seconds', type: 'integer', default: 300 })\n ttlSeconds: number = 300\n\n @Property({ name: 'challenge_method', type: 'text', default: 'auto' })\n challengeMethod: ChallengeMethod = ChallengeMethod.AUTO\n\n @Property({ name: 'configured_by', type: 'uuid', nullable: true })\n configuredBy?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'sudo_sessions' })\n@Index({ name: 'idx_sudo_sessions_token', properties: ['sessionToken', 'expiresAt'] })\nexport class SudoSession {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'session_token', type: 'text' })\n sessionToken!: string\n\n @Property({ name: 'challenge_method', type: 'text' })\n challengeMethod!: string\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'mfa_challenges' })\n@Index({ name: 'idx_mfa_challenges_lookup', properties: ['id', 'expiresAt'] })\nexport class MfaChallenge {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'otp_code_hash', type: 'text', nullable: true })\n otpCodeHash?: string | null\n\n @Property({ name: 'method_type', type: 'text', nullable: true })\n methodType?: string | null\n\n @Property({ name: 'method_id', type: 'uuid', nullable: true })\n methodId?: string | null\n\n @Property({ name: 'provider_challenge', type: 'jsonb', nullable: true })\n providerChallenge?: Record<string, unknown> | null\n\n @Property({ type: 'integer', default: 0 })\n attempts: number = 0\n\n @Property({ name: 'expires_at', type: Date })\n expiresAt!: Date\n\n @Property({ name: 'verified_at', type: Date, nullable: true })\n verifiedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["ChallengeMethod", "EnforcementScope", "MfaMethodType", "SudoChallengeMethodUsed"]
7
7
  }
@@ -8,7 +8,7 @@ var __decorateClass = (decorators, target, key, kind) => {
8
8
  if (kind && result) __defProp(target, key, result);
9
9
  return result;
10
10
  };
11
- import { Entity, PrimaryKey, Property, Unique, Index } from "@mikro-orm/core";
11
+ import { Entity, Index, PrimaryKey, Property, Unique } from "@mikro-orm/decorators/legacy";
12
12
  let SsoConfig = class {
13
13
  constructor() {
14
14
  this.allowedDomains = [];
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sso/data/entities.ts"],
4
- "sourcesContent": ["import { Entity, PrimaryKey, Property, Unique, Index } from '@mikro-orm/core'\n\n@Entity({ tableName: 'sso_configs' })\n// Unique index on organization_id (partial: WHERE deleted_at IS NULL) \u2014 managed by migration\nexport class SsoConfig {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ type: 'text', nullable: true })\n name?: string | null\n\n @Property({ type: 'text' })\n protocol!: string\n\n @Property({ type: 'text', nullable: true })\n issuer?: string | null\n\n @Property({ name: 'client_id', type: 'text', nullable: true })\n clientId?: string | null\n\n @Property({ name: 'client_secret_enc', type: 'text', nullable: true })\n clientSecretEnc?: string | null\n\n @Property({ name: 'allowed_domains', type: 'jsonb', default: '[]' })\n allowedDomains: string[] = []\n\n @Property({ name: 'jit_enabled', type: 'boolean', default: true })\n jitEnabled: boolean = true\n\n @Property({ name: 'auto_link_by_email', type: 'boolean', default: true })\n autoLinkByEmail: boolean = true\n\n @Property({ name: 'is_active', type: 'boolean', default: false })\n isActive: boolean = false\n\n @Property({ name: 'sso_required', type: 'boolean', default: false })\n ssoRequired: boolean = false\n\n @Property({ name: 'app_role_mappings', type: 'jsonb', default: '{}' })\n appRoleMappings: Record<string, string> = {}\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'sso_identities' })\n// Unique indexes (partial: WHERE deleted_at IS NULL) \u2014 managed by migration\nexport class SsoIdentity {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'sso_config_id', type: 'uuid' })\n @Index({ name: 'sso_identities_config_id_idx' })\n ssoConfigId!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n @Index({ name: 'sso_identities_user_id_idx' })\n userId!: string\n\n @Property({ name: 'idp_subject', type: 'text' })\n idpSubject!: string\n\n @Property({ name: 'idp_email', type: 'text' })\n idpEmail!: string\n\n @Property({ name: 'idp_name', type: 'text', nullable: true })\n idpName?: string | null\n\n @Property({ name: 'idp_groups', type: 'jsonb', default: '[]' })\n idpGroups: string[] = []\n\n @Property({ name: 'external_id', type: 'text', nullable: true })\n externalId?: string | null\n\n @Property({ name: 'provisioning_method', type: 'text' })\n provisioningMethod!: string\n\n @Property({ name: 'first_login_at', type: Date, nullable: true })\n firstLoginAt?: Date | null\n\n @Property({ name: 'last_login_at', type: Date, nullable: true })\n lastLoginAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'scim_tokens' })\n@Index({ name: 'scim_tokens_token_prefix_idx', properties: ['tokenPrefix'] })\nexport class ScimToken {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'sso_config_id', type: 'uuid' })\n @Index({ name: 'scim_tokens_sso_config_id_idx' })\n ssoConfigId!: string\n\n @Property({ type: 'text' })\n name!: string\n\n @Property({ name: 'token_hash', type: 'text' })\n tokenHash!: string\n\n @Property({ name: 'token_prefix', type: 'text' })\n tokenPrefix!: string\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_by', type: 'uuid', nullable: true })\n createdBy?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n}\n\n@Entity({ tableName: 'sso_user_deactivations' })\n@Unique({ properties: ['userId', 'ssoConfigId'], name: 'sso_user_deactivations_user_config_unique' })\nexport class SsoUserDeactivation {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n @Index({ name: 'sso_user_deactivations_user_id_idx' })\n userId!: string\n\n @Property({ name: 'sso_config_id', type: 'uuid' })\n ssoConfigId!: string\n\n @Property({ name: 'deactivated_at', type: Date })\n deactivatedAt: Date = new Date()\n\n @Property({ name: 'reactivated_at', type: Date, nullable: true })\n reactivatedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'scim_provisioning_log' })\n@Index({ name: 'scim_provisioning_log_config_created_idx', properties: ['ssoConfigId', 'createdAt'] })\nexport class ScimProvisioningLog {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'sso_config_id', type: 'uuid' })\n ssoConfigId!: string\n\n @Property({ type: 'text' })\n operation!: string\n\n @Property({ name: 'resource_type', type: 'text' })\n resourceType!: string\n\n @Property({ name: 'resource_id', type: 'uuid', nullable: true })\n resourceId?: string | null\n\n @Property({ name: 'scim_external_id', type: 'text', nullable: true })\n scimExternalId?: string | null\n\n @Property({ name: 'response_status', type: 'integer' })\n responseStatus!: number\n\n @Property({ name: 'error_message', type: 'text', nullable: true })\n errorMessage?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'sso_role_grants' })\n@Unique({ properties: ['userId', 'roleId', 'ssoConfigId'], name: 'sso_role_grants_user_role_config_unique' })\nexport class SsoRoleGrant {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n @Index({ name: 'sso_role_grants_user_id_idx' })\n userId!: string\n\n @Property({ name: 'role_id', type: 'uuid' })\n roleId!: string\n\n @Property({ name: 'sso_config_id', type: 'uuid' })\n ssoConfigId!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Entity, Index, PrimaryKey, Property, Unique } from '@mikro-orm/decorators/legacy'\n\n@Entity({ tableName: 'sso_configs' })\n// Unique index on organization_id (partial: WHERE deleted_at IS NULL) \u2014 managed by migration\nexport class SsoConfig {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ type: 'text', nullable: true })\n name?: string | null\n\n @Property({ type: 'text' })\n protocol!: string\n\n @Property({ type: 'text', nullable: true })\n issuer?: string | null\n\n @Property({ name: 'client_id', type: 'text', nullable: true })\n clientId?: string | null\n\n @Property({ name: 'client_secret_enc', type: 'text', nullable: true })\n clientSecretEnc?: string | null\n\n @Property({ name: 'allowed_domains', type: 'jsonb', default: '[]' })\n allowedDomains: string[] = []\n\n @Property({ name: 'jit_enabled', type: 'boolean', default: true })\n jitEnabled: boolean = true\n\n @Property({ name: 'auto_link_by_email', type: 'boolean', default: true })\n autoLinkByEmail: boolean = true\n\n @Property({ name: 'is_active', type: 'boolean', default: false })\n isActive: boolean = false\n\n @Property({ name: 'sso_required', type: 'boolean', default: false })\n ssoRequired: boolean = false\n\n @Property({ name: 'app_role_mappings', type: 'jsonb', default: '{}' })\n appRoleMappings: Record<string, string> = {}\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'sso_identities' })\n// Unique indexes (partial: WHERE deleted_at IS NULL) \u2014 managed by migration\nexport class SsoIdentity {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'sso_config_id', type: 'uuid' })\n @Index({ name: 'sso_identities_config_id_idx' })\n ssoConfigId!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n @Index({ name: 'sso_identities_user_id_idx' })\n userId!: string\n\n @Property({ name: 'idp_subject', type: 'text' })\n idpSubject!: string\n\n @Property({ name: 'idp_email', type: 'text' })\n idpEmail!: string\n\n @Property({ name: 'idp_name', type: 'text', nullable: true })\n idpName?: string | null\n\n @Property({ name: 'idp_groups', type: 'jsonb', default: '[]' })\n idpGroups: string[] = []\n\n @Property({ name: 'external_id', type: 'text', nullable: true })\n externalId?: string | null\n\n @Property({ name: 'provisioning_method', type: 'text' })\n provisioningMethod!: string\n\n @Property({ name: 'first_login_at', type: Date, nullable: true })\n firstLoginAt?: Date | null\n\n @Property({ name: 'last_login_at', type: Date, nullable: true })\n lastLoginAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n@Entity({ tableName: 'scim_tokens' })\n@Index({ name: 'scim_tokens_token_prefix_idx', properties: ['tokenPrefix'] })\nexport class ScimToken {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'sso_config_id', type: 'uuid' })\n @Index({ name: 'scim_tokens_sso_config_id_idx' })\n ssoConfigId!: string\n\n @Property({ type: 'text' })\n name!: string\n\n @Property({ name: 'token_hash', type: 'text' })\n tokenHash!: string\n\n @Property({ name: 'token_prefix', type: 'text' })\n tokenPrefix!: string\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_by', type: 'uuid', nullable: true })\n createdBy?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n}\n\n@Entity({ tableName: 'sso_user_deactivations' })\n@Unique({ properties: ['userId', 'ssoConfigId'], name: 'sso_user_deactivations_user_config_unique' })\nexport class SsoUserDeactivation {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n @Index({ name: 'sso_user_deactivations_user_id_idx' })\n userId!: string\n\n @Property({ name: 'sso_config_id', type: 'uuid' })\n ssoConfigId!: string\n\n @Property({ name: 'deactivated_at', type: Date })\n deactivatedAt: Date = new Date()\n\n @Property({ name: 'reactivated_at', type: Date, nullable: true })\n reactivatedAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'scim_provisioning_log' })\n@Index({ name: 'scim_provisioning_log_config_created_idx', properties: ['ssoConfigId', 'createdAt'] })\nexport class ScimProvisioningLog {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'sso_config_id', type: 'uuid' })\n ssoConfigId!: string\n\n @Property({ type: 'text' })\n operation!: string\n\n @Property({ name: 'resource_type', type: 'text' })\n resourceType!: string\n\n @Property({ name: 'resource_id', type: 'uuid', nullable: true })\n resourceId?: string | null\n\n @Property({ name: 'scim_external_id', type: 'text', nullable: true })\n scimExternalId?: string | null\n\n @Property({ name: 'response_status', type: 'integer' })\n responseStatus!: number\n\n @Property({ name: 'error_message', type: 'text', nullable: true })\n errorMessage?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n\n@Entity({ tableName: 'sso_role_grants' })\n@Unique({ properties: ['userId', 'roleId', 'ssoConfigId'], name: 'sso_role_grants_user_role_config_unique' })\nexport class SsoRoleGrant {\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'tenant_id', type: 'uuid', nullable: true })\n tenantId?: string | null\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'user_id', type: 'uuid' })\n @Index({ name: 'sso_role_grants_user_id_idx' })\n userId!: string\n\n @Property({ name: 'role_id', type: 'uuid' })\n roleId!: string\n\n @Property({ name: 'sso_config_id', type: 'uuid' })\n ssoConfigId!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -92,7 +92,7 @@ class AccountLinkingService {
92
92
  createdAt: now,
93
93
  updatedAt: now
94
94
  });
95
- await this.em.persistAndFlush(identity);
95
+ await this.em.persist(identity).flush();
96
96
  void emitSsoEvent("sso.identity.linked", {
97
97
  id: identity.id,
98
98
  tenantId,
@@ -112,7 +112,7 @@ class AccountLinkingService {
112
112
  isConfirmed: true,
113
113
  createdAt: /* @__PURE__ */ new Date()
114
114
  });
115
- await txEm.persistAndFlush(user);
115
+ await txEm.persist(user).flush();
116
116
  await this.assignRolesFromSso(txEm, user, config, tenantId, idpPayload.groups);
117
117
  const now = /* @__PURE__ */ new Date();
118
118
  const identity = txEm.create(SsoIdentity, {
@@ -130,7 +130,7 @@ class AccountLinkingService {
130
130
  createdAt: now,
131
131
  updatedAt: now
132
132
  });
133
- await txEm.persistAndFlush(identity);
133
+ await txEm.persist(identity).flush();
134
134
  void emitSsoEvent("sso.identity.created", {
135
135
  id: identity.id,
136
136
  tenantId,
@@ -219,7 +219,7 @@ class AccountLinkingService {
219
219
  });
220
220
  if (existingLink) return;
221
221
  const userRole = em.create(UserRole, { user, role, createdAt: /* @__PURE__ */ new Date() });
222
- await em.persistAndFlush(userRole);
222
+ await em.persist(userRole).flush();
223
223
  }
224
224
  }
225
225
  function resolveRoleNamesFromIdpGroups(idpGroups, configMappings) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sso/services/accountLinkingService.ts"],
4
- "sourcesContent": ["import { EntityManager, type FilterQuery, type RequiredEntityData } from '@mikro-orm/postgresql'\nimport { User, UserRole, Role } from '@open-mercato/core/modules/auth/data/entities'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { computeEmailHash } from '@open-mercato/core/modules/auth/lib/emailHash'\nimport { SsoConfig, SsoIdentity, SsoRoleGrant, ScimToken } from '../data/entities'\nimport { emitSsoEvent } from '../events'\nimport type { SsoIdentityPayload } from '../lib/types'\n\nexport class AccountLinkingService {\n constructor(private em: EntityManager) {}\n\n async resolveUser(\n config: SsoConfig,\n idpPayload: SsoIdentityPayload,\n tenantId: string,\n ): Promise<{ user: User; identity: SsoIdentity }> {\n const existing = await this.findExistingLink(config.id, idpPayload.subject, tenantId, config.organizationId)\n if (existing) {\n await this.assignRolesFromSso(this.em, existing.user, config, tenantId, idpPayload.groups)\n return existing\n }\n\n if (idpPayload.emailVerified === false) {\n throw new Error('IdP explicitly reported email as unverified \u2014 cannot link or provision account')\n }\n\n const emailDomain = idpPayload.email.split('@')[1]?.toLowerCase()\n if (!emailDomain || !config.allowedDomains.some((d) => d.toLowerCase() === emailDomain)) {\n throw new Error('Email domain is not in the allowed domains for this SSO configuration')\n }\n\n const emailLinked = config.autoLinkByEmail\n ? await this.linkByEmail(config, idpPayload, tenantId)\n : null\n if (emailLinked) {\n await this.assignRolesFromSso(this.em, emailLinked.user, config, tenantId, idpPayload.groups)\n return emailLinked\n }\n\n if (config.jitEnabled) {\n const scimActive = await this.em.count(ScimToken, { ssoConfigId: config.id, isActive: true }) > 0\n if (scimActive) {\n throw new Error('JIT provisioning is disabled because SCIM directory sync is active')\n }\n return this.jitProvision(config, idpPayload, tenantId)\n }\n\n throw new Error('No matching user found and JIT provisioning is disabled')\n }\n\n private async findExistingLink(\n ssoConfigId: string,\n idpSubject: string,\n tenantId: string,\n organizationId: string,\n ): Promise<{ user: User; identity: SsoIdentity } | null> {\n const identity = await findOneWithDecryption(\n this.em,\n SsoIdentity,\n { ssoConfigId, idpSubject, deletedAt: null },\n {},\n { tenantId, organizationId },\n )\n if (!identity) return null\n\n const user = await findOneWithDecryption(\n this.em,\n User,\n { id: identity.userId, deletedAt: null },\n {},\n { tenantId, organizationId },\n )\n if (!user) {\n identity.deletedAt = new Date()\n await this.em.flush()\n return null\n }\n\n identity.lastLoginAt = new Date()\n await this.em.flush()\n\n return { user, identity }\n }\n\n private async linkByEmail(\n config: SsoConfig,\n idpPayload: SsoIdentityPayload,\n tenantId: string,\n ): Promise<{ user: User; identity: SsoIdentity } | null> {\n const emailHash = computeEmailHash(idpPayload.email)\n const user = await findOneWithDecryption(\n this.em,\n User,\n {\n organizationId: config.organizationId,\n deletedAt: null,\n $or: [\n { email: idpPayload.email },\n { emailHash },\n ],\n } as FilterQuery<User>,\n {},\n { tenantId, organizationId: config.organizationId },\n )\n if (!user) return null\n\n const now = new Date()\n const identity = this.em.create(SsoIdentity, {\n tenantId,\n organizationId: config.organizationId,\n ssoConfigId: config.id,\n userId: user.id,\n idpSubject: idpPayload.subject,\n idpEmail: idpPayload.email,\n idpName: idpPayload.name ?? null,\n idpGroups: idpPayload.groups ?? [],\n provisioningMethod: 'manual',\n firstLoginAt: now,\n lastLoginAt: now,\n createdAt: now,\n updatedAt: now,\n } as RequiredEntityData<SsoIdentity>)\n await this.em.persistAndFlush(identity)\n\n void emitSsoEvent('sso.identity.linked', {\n id: identity.id,\n tenantId,\n organizationId: config.organizationId,\n }).catch((e) => console.error('[SSO Event]', e))\n\n return { user, identity }\n }\n\n private async jitProvision(\n config: SsoConfig,\n idpPayload: SsoIdentityPayload,\n tenantId: string,\n ): Promise<{ user: User; identity: SsoIdentity }> {\n return this.em.transactional(async (txEm) => {\n const user = txEm.create(User, {\n tenantId,\n organizationId: config.organizationId,\n email: idpPayload.email,\n emailHash: computeEmailHash(idpPayload.email),\n name: idpPayload.name ?? null,\n passwordHash: null,\n isConfirmed: true,\n createdAt: new Date(),\n })\n await txEm.persistAndFlush(user)\n\n await this.assignRolesFromSso(txEm, user, config, tenantId, idpPayload.groups)\n\n const now = new Date()\n const identity = txEm.create(SsoIdentity, {\n tenantId,\n organizationId: config.organizationId,\n ssoConfigId: config.id,\n userId: user.id,\n idpSubject: idpPayload.subject,\n idpEmail: idpPayload.email,\n idpName: idpPayload.name ?? null,\n idpGroups: idpPayload.groups ?? [],\n provisioningMethod: 'jit',\n firstLoginAt: now,\n lastLoginAt: now,\n createdAt: now,\n updatedAt: now,\n } as RequiredEntityData<SsoIdentity>)\n await txEm.persistAndFlush(identity)\n\n void emitSsoEvent('sso.identity.created', {\n id: identity.id,\n tenantId,\n organizationId: config.organizationId,\n }).catch((e) => console.error('[SSO Event]', e))\n\n return { user, identity }\n })\n }\n\n private async assignRolesFromSso(\n em: EntityManager,\n user: User,\n config: SsoConfig,\n tenantId: string,\n idpGroups?: string[],\n ): Promise<void> {\n const hasMappings = config.appRoleMappings && Object.keys(config.appRoleMappings).length > 0\n if (!hasMappings) return\n\n await this.syncMappedRoles(em, user, config, tenantId, idpGroups)\n\n const hasAnySsoRole = await em.findOne(SsoRoleGrant, {\n userId: user.id,\n ssoConfigId: config.id,\n })\n if (!hasAnySsoRole) {\n throw new Error('No roles could be resolved from IdP groups \u2014 login denied. Configure role mappings or ensure the IdP sends matching group claims.')\n }\n }\n\n /**\n * Sync/replace SSO-sourced roles: on each login, SSO-managed roles are replaced\n * with what the IdP sends, while manually-assigned roles are preserved.\n */\n private async syncMappedRoles(\n em: EntityManager,\n user: User,\n config: SsoConfig,\n tenantId: string,\n idpGroups?: string[],\n ): Promise<void> {\n const resolvedTenantId = tenantId || user.tenantId || ''\n if (!resolvedTenantId) return\n\n const allRoles = await em.find(Role, { tenantId: resolvedTenantId, deletedAt: null } as FilterQuery<Role>)\n const roleByNormalizedName = new Map<string, Role>()\n for (const role of allRoles) {\n const normalized = normalizeToken(role.name)\n if (normalized) roleByNormalizedName.set(normalized, role)\n }\n\n // Resolve desired role IDs from IdP groups using merged mappings\n const desiredRoleNames = resolveRoleNamesFromIdpGroups(idpGroups, config.appRoleMappings)\n const desiredRoleIds = new Set<string>()\n for (const roleName of desiredRoleNames) {\n const role = roleByNormalizedName.get(roleName)\n if (role) desiredRoleIds.add(role.id)\n }\n\n // Query current SSO grants for this user+config\n const existingGrants = await em.find(SsoRoleGrant, {\n userId: user.id,\n ssoConfigId: config.id,\n })\n const existingGrantedRoleIds = new Set(existingGrants.map((g) => g.roleId))\n\n // Compute diff\n const toAdd = [...desiredRoleIds].filter((id) => !existingGrantedRoleIds.has(id))\n const toRemove = existingGrants.filter((g) => !desiredRoleIds.has(g.roleId))\n\n // Add new roles\n for (const roleId of toAdd) {\n const role = allRoles.find((r) => r.id === roleId)\n if (!role) continue\n await this.ensureUserRole(em, user, role)\n const grant = em.create(SsoRoleGrant, {\n tenantId: resolvedTenantId,\n organizationId: config.organizationId,\n userId: user.id,\n roleId,\n ssoConfigId: config.id,\n } as RequiredEntityData<SsoRoleGrant>)\n em.persist(grant)\n }\n\n // Remove stale SSO-sourced roles\n for (const grant of toRemove) {\n const userRole = await em.findOne(UserRole, {\n user: user.id,\n role: grant.roleId,\n deletedAt: null,\n } as FilterQuery<UserRole>)\n if (userRole) {\n em.remove(userRole)\n }\n em.remove(grant)\n }\n\n // Clean up orphaned soft-deleted UserRole rows (ghost rows from previous soft-delete logic)\n const allUserRoles = await em.find(UserRole, { user: user.id } as FilterQuery<UserRole>)\n for (const ur of allUserRoles) {\n if (ur.deletedAt) {\n em.remove(ur)\n }\n }\n\n if (toAdd.length > 0 || toRemove.length > 0 || allUserRoles.some((ur) => ur.deletedAt)) {\n await em.flush()\n }\n }\n\n private async ensureUserRole(em: EntityManager, user: User, role: Role): Promise<void> {\n const existingLink = await em.findOne(UserRole, {\n user: user.id,\n role: role.id,\n deletedAt: null,\n } as FilterQuery<UserRole>)\n if (existingLink) return\n\n const userRole = em.create(UserRole, { user, role, createdAt: new Date() })\n await em.persistAndFlush(userRole)\n }\n}\n\nfunction resolveRoleNamesFromIdpGroups(\n idpGroups?: string[],\n configMappings?: Record<string, string>,\n): string[] {\n if (!Array.isArray(idpGroups) || idpGroups.length === 0) return []\n\n const normalizedGroups = idpGroups\n .map((group) => normalizeToken(group))\n .filter((group): group is string => group !== null)\n if (normalizedGroups.length === 0) return []\n\n const mergedMappings = loadMergedMappings(configMappings)\n const roleNames = new Set<string>()\n\n for (const group of normalizedGroups) {\n const mapped = mergedMappings.get(group)\n if (mapped?.length) {\n for (const role of mapped) roleNames.add(role)\n continue\n }\n\n roleNames.add(group)\n const segmented = group.split(/[\\\\/:]/).map((part) => normalizeToken(part)).filter((part): part is string => part !== null)\n for (const candidate of segmented) {\n roleNames.add(candidate)\n }\n }\n\n return Array.from(roleNames)\n}\n\nfunction loadMergedMappings(configMappings?: Record<string, string>): Map<string, string[]> {\n const envMappings = loadGroupRoleMappingsFromEnv()\n\n // Per-config mappings take precedence over env var\n if (configMappings && Object.keys(configMappings).length > 0) {\n for (const [group, roleName] of Object.entries(configMappings)) {\n const normalizedGroup = normalizeToken(group)\n if (!normalizedGroup) continue\n const normalizedRole = normalizeToken(roleName)\n if (!normalizedRole) continue\n envMappings.set(normalizedGroup, [normalizedRole])\n }\n }\n\n return envMappings\n}\n\nfunction loadGroupRoleMappingsFromEnv(): Map<string, string[]> {\n const raw = process.env.SSO_GROUP_ROLE_MAP\n if (!raw) return new Map()\n\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>\n const out = new Map<string, string[]>()\n for (const [group, roleValue] of Object.entries(parsed)) {\n const normalizedGroup = normalizeToken(group)\n if (!normalizedGroup) continue\n const roles = normalizeRoleList(roleValue)\n if (roles.length > 0) out.set(normalizedGroup, roles)\n }\n return out\n } catch {\n return new Map()\n }\n}\n\nfunction normalizeRoleList(value: unknown): string[] {\n if (typeof value === 'string') {\n const token = normalizeToken(value)\n return token ? [token] : []\n }\n\n if (Array.isArray(value)) {\n const out = new Set<string>()\n for (const entry of value) {\n const token = normalizeToken(entry)\n if (token) out.add(token)\n }\n return Array.from(out)\n }\n\n return []\n}\n\nfunction normalizeToken(value: unknown): string | null {\n if (typeof value !== 'string') return null\n const normalized = value.trim().toLowerCase()\n return normalized.length > 0 ? normalized : null\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { EntityManager, type FilterQuery, type RequiredEntityData } from '@mikro-orm/postgresql'\nimport { User, UserRole, Role } from '@open-mercato/core/modules/auth/data/entities'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { computeEmailHash } from '@open-mercato/core/modules/auth/lib/emailHash'\nimport { SsoConfig, SsoIdentity, SsoRoleGrant, ScimToken } from '../data/entities'\nimport { emitSsoEvent } from '../events'\nimport type { SsoIdentityPayload } from '../lib/types'\n\nexport class AccountLinkingService {\n constructor(private em: EntityManager) {}\n\n async resolveUser(\n config: SsoConfig,\n idpPayload: SsoIdentityPayload,\n tenantId: string,\n ): Promise<{ user: User; identity: SsoIdentity }> {\n const existing = await this.findExistingLink(config.id, idpPayload.subject, tenantId, config.organizationId)\n if (existing) {\n await this.assignRolesFromSso(this.em, existing.user, config, tenantId, idpPayload.groups)\n return existing\n }\n\n if (idpPayload.emailVerified === false) {\n throw new Error('IdP explicitly reported email as unverified \u2014 cannot link or provision account')\n }\n\n const emailDomain = idpPayload.email.split('@')[1]?.toLowerCase()\n if (!emailDomain || !config.allowedDomains.some((d) => d.toLowerCase() === emailDomain)) {\n throw new Error('Email domain is not in the allowed domains for this SSO configuration')\n }\n\n const emailLinked = config.autoLinkByEmail\n ? await this.linkByEmail(config, idpPayload, tenantId)\n : null\n if (emailLinked) {\n await this.assignRolesFromSso(this.em, emailLinked.user, config, tenantId, idpPayload.groups)\n return emailLinked\n }\n\n if (config.jitEnabled) {\n const scimActive = await this.em.count(ScimToken, { ssoConfigId: config.id, isActive: true }) > 0\n if (scimActive) {\n throw new Error('JIT provisioning is disabled because SCIM directory sync is active')\n }\n return this.jitProvision(config, idpPayload, tenantId)\n }\n\n throw new Error('No matching user found and JIT provisioning is disabled')\n }\n\n private async findExistingLink(\n ssoConfigId: string,\n idpSubject: string,\n tenantId: string,\n organizationId: string,\n ): Promise<{ user: User; identity: SsoIdentity } | null> {\n const identity = await findOneWithDecryption(\n this.em,\n SsoIdentity,\n { ssoConfigId, idpSubject, deletedAt: null },\n {},\n { tenantId, organizationId },\n )\n if (!identity) return null\n\n const user = await findOneWithDecryption(\n this.em,\n User,\n { id: identity.userId, deletedAt: null },\n {},\n { tenantId, organizationId },\n )\n if (!user) {\n identity.deletedAt = new Date()\n await this.em.flush()\n return null\n }\n\n identity.lastLoginAt = new Date()\n await this.em.flush()\n\n return { user, identity }\n }\n\n private async linkByEmail(\n config: SsoConfig,\n idpPayload: SsoIdentityPayload,\n tenantId: string,\n ): Promise<{ user: User; identity: SsoIdentity } | null> {\n const emailHash = computeEmailHash(idpPayload.email)\n const user = await findOneWithDecryption(\n this.em,\n User,\n {\n organizationId: config.organizationId,\n deletedAt: null,\n $or: [\n { email: idpPayload.email },\n { emailHash },\n ],\n } as FilterQuery<User>,\n {},\n { tenantId, organizationId: config.organizationId },\n )\n if (!user) return null\n\n const now = new Date()\n const identity = this.em.create(SsoIdentity, {\n tenantId,\n organizationId: config.organizationId,\n ssoConfigId: config.id,\n userId: user.id,\n idpSubject: idpPayload.subject,\n idpEmail: idpPayload.email,\n idpName: idpPayload.name ?? null,\n idpGroups: idpPayload.groups ?? [],\n provisioningMethod: 'manual',\n firstLoginAt: now,\n lastLoginAt: now,\n createdAt: now,\n updatedAt: now,\n } as RequiredEntityData<SsoIdentity>)\n await this.em.persist(identity).flush()\n\n void emitSsoEvent('sso.identity.linked', {\n id: identity.id,\n tenantId,\n organizationId: config.organizationId,\n }).catch((e) => console.error('[SSO Event]', e))\n\n return { user, identity }\n }\n\n private async jitProvision(\n config: SsoConfig,\n idpPayload: SsoIdentityPayload,\n tenantId: string,\n ): Promise<{ user: User; identity: SsoIdentity }> {\n return this.em.transactional(async (txEm) => {\n const user = txEm.create(User, {\n tenantId,\n organizationId: config.organizationId,\n email: idpPayload.email,\n emailHash: computeEmailHash(idpPayload.email),\n name: idpPayload.name ?? null,\n passwordHash: null,\n isConfirmed: true,\n createdAt: new Date(),\n })\n await txEm.persist(user).flush()\n\n await this.assignRolesFromSso(txEm, user, config, tenantId, idpPayload.groups)\n\n const now = new Date()\n const identity = txEm.create(SsoIdentity, {\n tenantId,\n organizationId: config.organizationId,\n ssoConfigId: config.id,\n userId: user.id,\n idpSubject: idpPayload.subject,\n idpEmail: idpPayload.email,\n idpName: idpPayload.name ?? null,\n idpGroups: idpPayload.groups ?? [],\n provisioningMethod: 'jit',\n firstLoginAt: now,\n lastLoginAt: now,\n createdAt: now,\n updatedAt: now,\n } as RequiredEntityData<SsoIdentity>)\n await txEm.persist(identity).flush()\n\n void emitSsoEvent('sso.identity.created', {\n id: identity.id,\n tenantId,\n organizationId: config.organizationId,\n }).catch((e) => console.error('[SSO Event]', e))\n\n return { user, identity }\n })\n }\n\n private async assignRolesFromSso(\n em: EntityManager,\n user: User,\n config: SsoConfig,\n tenantId: string,\n idpGroups?: string[],\n ): Promise<void> {\n const hasMappings = config.appRoleMappings && Object.keys(config.appRoleMappings).length > 0\n if (!hasMappings) return\n\n await this.syncMappedRoles(em, user, config, tenantId, idpGroups)\n\n const hasAnySsoRole = await em.findOne(SsoRoleGrant, {\n userId: user.id,\n ssoConfigId: config.id,\n })\n if (!hasAnySsoRole) {\n throw new Error('No roles could be resolved from IdP groups \u2014 login denied. Configure role mappings or ensure the IdP sends matching group claims.')\n }\n }\n\n /**\n * Sync/replace SSO-sourced roles: on each login, SSO-managed roles are replaced\n * with what the IdP sends, while manually-assigned roles are preserved.\n */\n private async syncMappedRoles(\n em: EntityManager,\n user: User,\n config: SsoConfig,\n tenantId: string,\n idpGroups?: string[],\n ): Promise<void> {\n const resolvedTenantId = tenantId || user.tenantId || ''\n if (!resolvedTenantId) return\n\n const allRoles = await em.find(Role, { tenantId: resolvedTenantId, deletedAt: null } as FilterQuery<Role>)\n const roleByNormalizedName = new Map<string, Role>()\n for (const role of allRoles) {\n const normalized = normalizeToken(role.name)\n if (normalized) roleByNormalizedName.set(normalized, role)\n }\n\n // Resolve desired role IDs from IdP groups using merged mappings\n const desiredRoleNames = resolveRoleNamesFromIdpGroups(idpGroups, config.appRoleMappings)\n const desiredRoleIds = new Set<string>()\n for (const roleName of desiredRoleNames) {\n const role = roleByNormalizedName.get(roleName)\n if (role) desiredRoleIds.add(role.id)\n }\n\n // Query current SSO grants for this user+config\n const existingGrants = await em.find(SsoRoleGrant, {\n userId: user.id,\n ssoConfigId: config.id,\n })\n const existingGrantedRoleIds = new Set(existingGrants.map((g) => g.roleId))\n\n // Compute diff\n const toAdd = [...desiredRoleIds].filter((id) => !existingGrantedRoleIds.has(id))\n const toRemove = existingGrants.filter((g) => !desiredRoleIds.has(g.roleId))\n\n // Add new roles\n for (const roleId of toAdd) {\n const role = allRoles.find((r) => r.id === roleId)\n if (!role) continue\n await this.ensureUserRole(em, user, role)\n const grant = em.create(SsoRoleGrant, {\n tenantId: resolvedTenantId,\n organizationId: config.organizationId,\n userId: user.id,\n roleId,\n ssoConfigId: config.id,\n } as RequiredEntityData<SsoRoleGrant>)\n em.persist(grant)\n }\n\n // Remove stale SSO-sourced roles\n for (const grant of toRemove) {\n const userRole = await em.findOne(UserRole, {\n user: user.id,\n role: grant.roleId,\n deletedAt: null,\n } as FilterQuery<UserRole>)\n if (userRole) {\n em.remove(userRole)\n }\n em.remove(grant)\n }\n\n // Clean up orphaned soft-deleted UserRole rows (ghost rows from previous soft-delete logic)\n const allUserRoles = await em.find(UserRole, { user: user.id } as FilterQuery<UserRole>)\n for (const ur of allUserRoles) {\n if (ur.deletedAt) {\n em.remove(ur)\n }\n }\n\n if (toAdd.length > 0 || toRemove.length > 0 || allUserRoles.some((ur) => ur.deletedAt)) {\n await em.flush()\n }\n }\n\n private async ensureUserRole(em: EntityManager, user: User, role: Role): Promise<void> {\n const existingLink = await em.findOne(UserRole, {\n user: user.id,\n role: role.id,\n deletedAt: null,\n } as FilterQuery<UserRole>)\n if (existingLink) return\n\n const userRole = em.create(UserRole, { user, role, createdAt: new Date() })\n await em.persist(userRole).flush()\n }\n}\n\nfunction resolveRoleNamesFromIdpGroups(\n idpGroups?: string[],\n configMappings?: Record<string, string>,\n): string[] {\n if (!Array.isArray(idpGroups) || idpGroups.length === 0) return []\n\n const normalizedGroups = idpGroups\n .map((group) => normalizeToken(group))\n .filter((group): group is string => group !== null)\n if (normalizedGroups.length === 0) return []\n\n const mergedMappings = loadMergedMappings(configMappings)\n const roleNames = new Set<string>()\n\n for (const group of normalizedGroups) {\n const mapped = mergedMappings.get(group)\n if (mapped?.length) {\n for (const role of mapped) roleNames.add(role)\n continue\n }\n\n roleNames.add(group)\n const segmented = group.split(/[\\\\/:]/).map((part) => normalizeToken(part)).filter((part): part is string => part !== null)\n for (const candidate of segmented) {\n roleNames.add(candidate)\n }\n }\n\n return Array.from(roleNames)\n}\n\nfunction loadMergedMappings(configMappings?: Record<string, string>): Map<string, string[]> {\n const envMappings = loadGroupRoleMappingsFromEnv()\n\n // Per-config mappings take precedence over env var\n if (configMappings && Object.keys(configMappings).length > 0) {\n for (const [group, roleName] of Object.entries(configMappings)) {\n const normalizedGroup = normalizeToken(group)\n if (!normalizedGroup) continue\n const normalizedRole = normalizeToken(roleName)\n if (!normalizedRole) continue\n envMappings.set(normalizedGroup, [normalizedRole])\n }\n }\n\n return envMappings\n}\n\nfunction loadGroupRoleMappingsFromEnv(): Map<string, string[]> {\n const raw = process.env.SSO_GROUP_ROLE_MAP\n if (!raw) return new Map()\n\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>\n const out = new Map<string, string[]>()\n for (const [group, roleValue] of Object.entries(parsed)) {\n const normalizedGroup = normalizeToken(group)\n if (!normalizedGroup) continue\n const roles = normalizeRoleList(roleValue)\n if (roles.length > 0) out.set(normalizedGroup, roles)\n }\n return out\n } catch {\n return new Map()\n }\n}\n\nfunction normalizeRoleList(value: unknown): string[] {\n if (typeof value === 'string') {\n const token = normalizeToken(value)\n return token ? [token] : []\n }\n\n if (Array.isArray(value)) {\n const out = new Set<string>()\n for (const entry of value) {\n const token = normalizeToken(entry)\n if (token) out.add(token)\n }\n return Array.from(out)\n }\n\n return []\n}\n\nfunction normalizeToken(value: unknown): string | null {\n if (typeof value !== 'string') return null\n const normalized = value.trim().toLowerCase()\n return normalized.length > 0 ? normalized : null\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,4 @@
1
+ import { sql } from "kysely";
1
2
  import { SsoConfig } from "../data/entities.js";
2
3
  class HrdService {
3
4
  constructor(em) {
@@ -6,8 +7,8 @@ class HrdService {
6
7
  async findActiveConfigByEmailDomain(email) {
7
8
  const domain = email.split("@")[1]?.toLowerCase();
8
9
  if (!domain) return null;
9
- const knex = this.em.getKnex();
10
- const row = await knex("sso_configs").whereRaw("allowed_domains @> ?::jsonb", [JSON.stringify([domain])]).where("is_active", true).whereNull("deleted_at").first();
10
+ const db = this.em.getKysely();
11
+ const row = await db.selectFrom("sso_configs").selectAll().where(sql`allowed_domains @> ${JSON.stringify([domain])}::jsonb`).where("is_active", "=", true).where("deleted_at", "is", null).executeTakeFirst();
11
12
  if (!row) return null;
12
13
  return this.em.map(SsoConfig, row);
13
14
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sso/services/hrdService.ts"],
4
- "sourcesContent": ["import { EntityManager } from '@mikro-orm/postgresql'\nimport { SsoConfig } from '../data/entities'\n\nexport class HrdService {\n constructor(private em: EntityManager) {}\n\n async findActiveConfigByEmailDomain(email: string): Promise<SsoConfig | null> {\n const domain = email.split('@')[1]?.toLowerCase()\n if (!domain) return null\n\n const knex = this.em.getKnex()\n const row = await knex('sso_configs')\n .whereRaw(\"allowed_domains @> ?::jsonb\", [JSON.stringify([domain])])\n .where('is_active', true)\n .whereNull('deleted_at')\n .first()\n\n if (!row) return null\n\n return this.em.map(SsoConfig, row)\n }\n}\n"],
5
- "mappings": "AACA,SAAS,iBAAiB;AAEnB,MAAM,WAAW;AAAA,EACtB,YAAoB,IAAmB;AAAnB;AAAA,EAAoB;AAAA,EAExC,MAAM,8BAA8B,OAA0C;AAC5E,UAAM,SAAS,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,YAAY;AAChD,QAAI,CAAC,OAAQ,QAAO;AAEpB,UAAM,OAAO,KAAK,GAAG,QAAQ;AAC7B,UAAM,MAAM,MAAM,KAAK,aAAa,EACjC,SAAS,+BAA+B,CAAC,KAAK,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAClE,MAAM,aAAa,IAAI,EACvB,UAAU,YAAY,EACtB,MAAM;AAET,QAAI,CAAC,IAAK,QAAO;AAEjB,WAAO,KAAK,GAAG,IAAI,WAAW,GAAG;AAAA,EACnC;AACF;",
4
+ "sourcesContent": ["import { EntityManager } from '@mikro-orm/postgresql'\nimport { type Kysely, sql } from 'kysely'\nimport { SsoConfig } from '../data/entities'\n\nexport class HrdService {\n constructor(private em: EntityManager) {}\n\n async findActiveConfigByEmailDomain(email: string): Promise<SsoConfig | null> {\n const domain = email.split('@')[1]?.toLowerCase()\n if (!domain) return null\n\n const db = (this.em as any).getKysely() as Kysely<any>\n const row = await db\n .selectFrom('sso_configs' as any)\n .selectAll()\n .where(sql<boolean>`allowed_domains @> ${JSON.stringify([domain])}::jsonb`)\n .where('is_active' as any, '=', true)\n .where('deleted_at' as any, 'is', null as any)\n .executeTakeFirst()\n\n if (!row) return null\n\n return this.em.map(SsoConfig, row as Record<string, unknown>)\n }\n}\n"],
5
+ "mappings": "AACA,SAAsB,WAAW;AACjC,SAAS,iBAAiB;AAEnB,MAAM,WAAW;AAAA,EACtB,YAAoB,IAAmB;AAAnB;AAAA,EAAoB;AAAA,EAExC,MAAM,8BAA8B,OAA0C;AAC5E,UAAM,SAAS,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,YAAY;AAChD,QAAI,CAAC,OAAQ,QAAO;AAEpB,UAAM,KAAM,KAAK,GAAW,UAAU;AACtC,UAAM,MAAM,MAAM,GACf,WAAW,aAAoB,EAC/B,UAAU,EACV,MAAM,yBAAkC,KAAK,UAAU,CAAC,MAAM,CAAC,CAAC,SAAS,EACzE,MAAM,aAAoB,KAAK,IAAI,EACnC,MAAM,cAAqB,MAAM,IAAW,EAC5C,iBAAiB;AAEpB,QAAI,CAAC,IAAK,QAAO;AAEjB,WAAO,KAAK,GAAG,IAAI,WAAW,GAA8B;AAAA,EAC9D;AACF;",
6
6
  "names": []
7
7
  }
@@ -80,7 +80,7 @@ class ScimService {
80
80
  createdAt: now,
81
81
  updatedAt: now
82
82
  });
83
- await this.em.persistAndFlush(identity);
83
+ await this.em.persist(identity).flush();
84
84
  const deactivation = parsed.active === false ? await this.createDeactivation(existingUser.id, scope) : null;
85
85
  await this.log(scope, "CREATE", identity.id, parsed.externalId, 201);
86
86
  return {
@@ -99,7 +99,7 @@ class ScimService {
99
99
  isConfirmed: true,
100
100
  createdAt: /* @__PURE__ */ new Date()
101
101
  });
102
- await txEm.persistAndFlush(user);
102
+ await txEm.persist(user).flush();
103
103
  const now = /* @__PURE__ */ new Date();
104
104
  const identity = txEm.create(SsoIdentity, {
105
105
  tenantId: scope.tenantId ?? null,
@@ -115,7 +115,7 @@ class ScimService {
115
115
  createdAt: now,
116
116
  updatedAt: now
117
117
  });
118
- await txEm.persistAndFlush(identity);
118
+ await txEm.persist(identity).flush();
119
119
  const deactivation = parsed.active === false ? await this.createDeactivationTx(txEm, user.id, scope) : null;
120
120
  await this.logTx(txEm, scope, "CREATE", identity.id, parsed.externalId, 201);
121
121
  return {
@@ -311,7 +311,7 @@ class ScimService {
311
311
  ssoConfigId: scope.ssoConfigId,
312
312
  deactivatedAt: /* @__PURE__ */ new Date()
313
313
  });
314
- await this.em.persistAndFlush(deactivation);
314
+ await this.em.persist(deactivation).flush();
315
315
  return deactivation;
316
316
  }
317
317
  async createDeactivationTx(txEm, userId, scope) {
@@ -322,7 +322,7 @@ class ScimService {
322
322
  ssoConfigId: scope.ssoConfigId,
323
323
  deactivatedAt: /* @__PURE__ */ new Date()
324
324
  });
325
- await txEm.persistAndFlush(deactivation);
325
+ await txEm.persist(deactivation).flush();
326
326
  return deactivation;
327
327
  }
328
328
  async log(scope, operation, resourceId, externalId, responseStatus, errorMessage) {
@@ -337,7 +337,7 @@ class ScimService {
337
337
  responseStatus,
338
338
  errorMessage: errorMessage ?? null
339
339
  });
340
- await this.em.persistAndFlush(entry);
340
+ await this.em.persist(entry).flush();
341
341
  }
342
342
  async logTx(txEm, scope, operation, resourceId, externalId, responseStatus) {
343
343
  const entry = txEm.create(ScimProvisioningLog, {
@@ -350,7 +350,7 @@ class ScimService {
350
350
  scimExternalId: externalId ?? null,
351
351
  responseStatus
352
352
  });
353
- await txEm.persistAndFlush(entry);
353
+ await txEm.persist(entry).flush();
354
354
  }
355
355
  }
356
356
  function buildDisplayName(parsed) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sso/services/scimService.ts"],
4
- "sourcesContent": ["import { EntityManager, type FilterQuery, type RequiredEntityData } from '@mikro-orm/postgresql'\nimport { User, Session } from '@open-mercato/core/modules/auth/data/entities'\nimport { computeEmailHash } from '@open-mercato/core/modules/auth/lib/emailHash'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SsoIdentity, SsoUserDeactivation, ScimProvisioningLog } from '../data/entities'\nimport { toScimUserResource, fromScimUserPayload, type ScimUserResource, type ScimUserPayload } from '../lib/scim-mapper'\nimport { coerceBoolean } from '../lib/scim-utils'\nimport { parseScimFilter, scimFilterToWhere } from '../lib/scim-filter'\nimport { buildListResponse } from '../lib/scim-response'\nimport type { ScimScope } from '../api/scim/context'\nimport type { ScimPatchOperation } from '../lib/scim-patch'\n\nexport class ScimService {\n constructor(private em: EntityManager) {}\n\n async createUser(\n payload: Record<string, unknown>,\n scope: ScimScope,\n baseUrl: string,\n ): Promise<{ resource: ScimUserResource; status: number }> {\n const parsed = fromScimUserPayload(payload)\n const email = parsed.email ?? parsed.userName\n if (!email) {\n throw new ScimServiceError(400, 'userName or emails[0].value is required')\n }\n\n // Idempotency: if externalId already exists for this config, return existing\n if (parsed.externalId) {\n const existingIdentity = await this.em.findOne(SsoIdentity, {\n ssoConfigId: scope.ssoConfigId,\n externalId: parsed.externalId,\n deletedAt: null,\n })\n if (existingIdentity) {\n const existingUser = await findOneWithDecryption(\n this.em, User,\n { id: existingIdentity.userId, deletedAt: null },\n {},\n { tenantId: scope.tenantId ?? '', organizationId: scope.organizationId },\n )\n if (existingUser) {\n const deactivation = await this.em.findOne(SsoUserDeactivation, {\n userId: existingUser.id, ssoConfigId: scope.ssoConfigId,\n })\n await this.log(scope, 'CREATE', existingIdentity.id, parsed.externalId, 200)\n return {\n resource: toScimUserResource(existingUser, existingIdentity, baseUrl, deactivation),\n status: 200,\n }\n }\n }\n }\n\n // Check if user already exists by email\n const emailHash = computeEmailHash(email)\n const where: FilterQuery<User> = {\n organizationId: scope.organizationId,\n deletedAt: null,\n $or: [{ email }, { emailHash }],\n }\n const existingUser = await findOneWithDecryption(\n this.em, User,\n where,\n {},\n { tenantId: scope.tenantId ?? '', organizationId: scope.organizationId },\n )\n\n if (existingUser) {\n // Check if already linked to this SSO config\n const existingLink = await this.em.findOne(SsoIdentity, {\n ssoConfigId: scope.ssoConfigId,\n userId: existingUser.id,\n deletedAt: null,\n })\n if (existingLink) {\n throw new ScimServiceError(409, `User with email ${email} is already linked to this SSO configuration`)\n }\n\n // Auto-link: create SsoIdentity for existing user\n const now = new Date()\n const identity = this.em.create(SsoIdentity, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n ssoConfigId: scope.ssoConfigId,\n userId: existingUser.id,\n idpSubject: parsed.externalId ?? email,\n idpEmail: email,\n idpName: buildDisplayName(parsed),\n idpGroups: [],\n externalId: parsed.externalId ?? null,\n provisioningMethod: 'scim',\n createdAt: now,\n updatedAt: now,\n } as RequiredEntityData<SsoIdentity>)\n await this.em.persistAndFlush(identity)\n\n const deactivation = parsed.active === false\n ? await this.createDeactivation(existingUser.id, scope)\n : null\n\n await this.log(scope, 'CREATE', identity.id, parsed.externalId, 201)\n return {\n resource: toScimUserResource(existingUser, identity, baseUrl, deactivation),\n status: 201,\n }\n }\n\n // Create new user + identity\n return this.em.transactional(async (txEm) => {\n const user = txEm.create(User, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n email,\n emailHash: computeEmailHash(email),\n name: buildDisplayName(parsed) ?? undefined,\n passwordHash: null,\n isConfirmed: true,\n createdAt: new Date(),\n })\n await txEm.persistAndFlush(user)\n\n const now = new Date()\n const identity = txEm.create(SsoIdentity, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n ssoConfigId: scope.ssoConfigId,\n userId: user.id,\n idpSubject: parsed.externalId ?? email,\n idpEmail: email,\n idpName: buildDisplayName(parsed),\n idpGroups: [],\n externalId: parsed.externalId ?? null,\n provisioningMethod: 'scim',\n createdAt: now,\n updatedAt: now,\n } as RequiredEntityData<SsoIdentity>)\n await txEm.persistAndFlush(identity)\n\n const deactivation = parsed.active === false\n ? await this.createDeactivationTx(txEm, user.id, scope)\n : null\n\n await this.logTx(txEm, scope, 'CREATE', identity.id, parsed.externalId, 201)\n return {\n resource: toScimUserResource(user, identity, baseUrl, deactivation),\n status: 201,\n }\n })\n }\n\n async getUser(scimId: string, scope: ScimScope, baseUrl: string): Promise<ScimUserResource> {\n const identity = await this.em.findOne(SsoIdentity, {\n id: scimId,\n ssoConfigId: scope.ssoConfigId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (!identity) throw new ScimServiceError(404, 'User not found')\n\n const user = await findOneWithDecryption(\n this.em, User,\n { id: identity.userId, deletedAt: null },\n {},\n { tenantId: scope.tenantId ?? '', organizationId: scope.organizationId },\n )\n if (!user) throw new ScimServiceError(404, 'User not found')\n\n const deactivation = await this.em.findOne(SsoUserDeactivation, {\n userId: user.id, ssoConfigId: scope.ssoConfigId,\n })\n\n return toScimUserResource(user, identity, baseUrl, deactivation)\n }\n\n async listUsers(\n filter: string | null,\n startIndex: number,\n count: number,\n scope: ScimScope,\n baseUrl: string,\n ): Promise<Record<string, unknown>> {\n const conditions = parseScimFilter(filter)\n const where = scimFilterToWhere(conditions, scope.ssoConfigId, scope.organizationId)\n\n const offset = Math.max(0, startIndex - 1)\n const [identities, total] = await this.em.findAndCount(SsoIdentity, where, {\n orderBy: { createdAt: 'asc' },\n limit: count,\n offset,\n })\n\n const userIds = identities.map((i) => i.userId)\n\n const users = userIds.length > 0\n ? await findWithDecryption(\n this.em, User,\n { id: { $in: userIds }, deletedAt: null },\n {},\n { tenantId: scope.tenantId ?? '', organizationId: scope.organizationId },\n )\n : []\n const userMap = new Map(users.map((u) => [u.id, u]))\n\n const deactivations = userIds.length > 0\n ? await this.em.find(SsoUserDeactivation, {\n userId: { $in: userIds }, ssoConfigId: scope.ssoConfigId,\n })\n : []\n const deactivationMap = new Map(deactivations.map((d) => [d.userId, d]))\n\n const resources: ScimUserResource[] = []\n for (const identity of identities) {\n const user = userMap.get(identity.userId)\n if (!user) continue\n\n const deactivation = deactivationMap.get(user.id) ?? null\n resources.push(toScimUserResource(user, identity, baseUrl, deactivation))\n }\n\n return buildListResponse(resources, total, startIndex, resources.length)\n }\n\n async patchUser(\n scimId: string,\n operations: ScimPatchOperation[],\n scope: ScimScope,\n baseUrl: string,\n ): Promise<ScimUserResource> {\n const identity = await this.em.findOne(SsoIdentity, {\n id: scimId,\n ssoConfigId: scope.ssoConfigId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (!identity) throw new ScimServiceError(404, 'User not found')\n\n const user = await findOneWithDecryption(\n this.em, User,\n { id: identity.userId, deletedAt: null },\n {},\n { tenantId: scope.tenantId ?? '', organizationId: scope.organizationId },\n )\n if (!user) throw new ScimServiceError(404, 'User not found')\n\n for (const op of operations) {\n const normalizedOp = op.op.toLowerCase()\n if (normalizedOp === 'replace' || normalizedOp === 'add') {\n this.applyPatchValue(user, identity, op.path, op.value)\n }\n // 'remove' operations on optional fields \u2014 set to null\n if (normalizedOp === 'remove' && op.path) {\n this.applyPatchValue(user, identity, op.path, null)\n }\n }\n\n // Handle active status changes\n const activeOp = operations.find((op) =>\n op.path?.toLowerCase() === 'active' ||\n (!op.path && op.value && typeof op.value === 'object' && 'active' in (op.value as Record<string, unknown>)),\n )\n\n if (activeOp) {\n const activeValue = activeOp.path\n ? coerceBoolean(activeOp.value)\n : coerceBoolean((activeOp.value as Record<string, unknown>).active)\n\n if (activeValue === false) {\n await this.deactivateUser(user.id, scope)\n } else if (activeValue === true) {\n await this.reactivateUser(user.id, scope)\n }\n }\n\n await this.em.flush()\n\n const deactivation = await this.em.findOne(SsoUserDeactivation, {\n userId: user.id, ssoConfigId: scope.ssoConfigId,\n })\n\n await this.log(scope, 'PATCH', identity.id, identity.externalId, 200)\n return toScimUserResource(user, identity, baseUrl, deactivation)\n }\n\n async deleteUser(scimId: string, scope: ScimScope): Promise<void> {\n const identity = await this.em.findOne(SsoIdentity, {\n id: scimId,\n ssoConfigId: scope.ssoConfigId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (!identity) throw new ScimServiceError(404, 'User not found')\n\n await this.deactivateUser(identity.userId, scope)\n await this.log(scope, 'DELETE', identity.id, identity.externalId, 204)\n }\n\n private applyPatchValue(\n user: User,\n identity: SsoIdentity,\n path: string | undefined,\n value: unknown,\n ): void {\n if (!path) {\n // No path means value is an object with attribute keys\n if (value && typeof value === 'object') {\n const obj = value as Record<string, unknown>\n for (const [key, val] of Object.entries(obj)) {\n this.applyPatchValue(user, identity, key, val)\n }\n }\n return\n }\n\n const normalizedPath = path.toLowerCase()\n switch (normalizedPath) {\n case 'displayname':\n user.name = (value as string) || undefined\n identity.idpName = (value as string) ?? null\n break\n case 'name.givenname': {\n const currentParts = (user.name ?? '').split(' ')\n currentParts[0] = (value as string) ?? ''\n user.name = currentParts.join(' ').trim() || undefined\n break\n }\n case 'name.familyname': {\n const currentParts = (user.name ?? '').split(' ')\n const given = currentParts[0] ?? ''\n user.name = value ? `${given} ${value}`.trim() : given || undefined\n break\n }\n case 'username':\n identity.idpEmail = (value as string) ?? identity.idpEmail\n break\n case 'externalid':\n identity.externalId = (value as string) ?? null\n break\n case 'active':\n // Handled separately via deactivation logic\n break\n }\n }\n\n private async deactivateUser(userId: string, scope: ScimScope): Promise<void> {\n let deactivation = await this.em.findOne(SsoUserDeactivation, {\n userId, ssoConfigId: scope.ssoConfigId,\n })\n\n if (deactivation) {\n deactivation.deactivatedAt = new Date()\n deactivation.reactivatedAt = null\n } else {\n deactivation = this.em.create(SsoUserDeactivation, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n userId,\n ssoConfigId: scope.ssoConfigId,\n deactivatedAt: new Date(),\n } as RequiredEntityData<SsoUserDeactivation>)\n this.em.persist(deactivation)\n }\n await this.em.flush()\n\n // Revoke all active sessions\n const sessionWhere: FilterQuery<Session> = { user: userId }\n await this.em.nativeDelete(Session, sessionWhere)\n }\n\n private async reactivateUser(userId: string, scope: ScimScope): Promise<void> {\n const deactivation = await this.em.findOne(SsoUserDeactivation, {\n userId, ssoConfigId: scope.ssoConfigId,\n })\n if (deactivation && !deactivation.reactivatedAt) {\n deactivation.reactivatedAt = new Date()\n await this.em.flush()\n }\n }\n\n private async createDeactivation(userId: string, scope: ScimScope): Promise<SsoUserDeactivation> {\n const deactivation = this.em.create(SsoUserDeactivation, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n userId,\n ssoConfigId: scope.ssoConfigId,\n deactivatedAt: new Date(),\n } as RequiredEntityData<SsoUserDeactivation>)\n await this.em.persistAndFlush(deactivation)\n return deactivation\n }\n\n private async createDeactivationTx(txEm: EntityManager, userId: string, scope: ScimScope): Promise<SsoUserDeactivation> {\n const deactivation = txEm.create(SsoUserDeactivation, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n userId,\n ssoConfigId: scope.ssoConfigId,\n deactivatedAt: new Date(),\n } as RequiredEntityData<SsoUserDeactivation>)\n await txEm.persistAndFlush(deactivation)\n return deactivation\n }\n\n private async log(\n scope: ScimScope,\n operation: string,\n resourceId: string | null | undefined,\n externalId: string | null | undefined,\n responseStatus: number,\n errorMessage?: string,\n ): Promise<void> {\n const entry = this.em.create(ScimProvisioningLog, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n ssoConfigId: scope.ssoConfigId,\n operation,\n resourceType: 'User',\n resourceId: resourceId ?? null,\n scimExternalId: externalId ?? null,\n responseStatus,\n errorMessage: errorMessage ?? null,\n } as RequiredEntityData<ScimProvisioningLog>)\n await this.em.persistAndFlush(entry)\n }\n\n private async logTx(\n txEm: EntityManager,\n scope: ScimScope,\n operation: string,\n resourceId: string | null | undefined,\n externalId: string | null | undefined,\n responseStatus: number,\n ): Promise<void> {\n const entry = txEm.create(ScimProvisioningLog, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n ssoConfigId: scope.ssoConfigId,\n operation,\n resourceType: 'User',\n resourceId: resourceId ?? null,\n scimExternalId: externalId ?? null,\n responseStatus,\n } as RequiredEntityData<ScimProvisioningLog>)\n await txEm.persistAndFlush(entry)\n }\n}\n\nfunction buildDisplayName(parsed: ScimUserPayload): string | null {\n if (parsed.displayName) return parsed.displayName\n const parts = [parsed.givenName, parsed.familyName].filter(Boolean)\n return parts.length > 0 ? parts.join(' ') : null\n}\n\nexport class ScimServiceError extends Error {\n constructor(\n public readonly statusCode: number,\n message: string,\n ) {\n super(message)\n this.name = 'ScimServiceError'\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { EntityManager, type FilterQuery, type RequiredEntityData } from '@mikro-orm/postgresql'\nimport { User, Session } from '@open-mercato/core/modules/auth/data/entities'\nimport { computeEmailHash } from '@open-mercato/core/modules/auth/lib/emailHash'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SsoIdentity, SsoUserDeactivation, ScimProvisioningLog } from '../data/entities'\nimport { toScimUserResource, fromScimUserPayload, type ScimUserResource, type ScimUserPayload } from '../lib/scim-mapper'\nimport { coerceBoolean } from '../lib/scim-utils'\nimport { parseScimFilter, scimFilterToWhere } from '../lib/scim-filter'\nimport { buildListResponse } from '../lib/scim-response'\nimport type { ScimScope } from '../api/scim/context'\nimport type { ScimPatchOperation } from '../lib/scim-patch'\n\nexport class ScimService {\n constructor(private em: EntityManager) {}\n\n async createUser(\n payload: Record<string, unknown>,\n scope: ScimScope,\n baseUrl: string,\n ): Promise<{ resource: ScimUserResource; status: number }> {\n const parsed = fromScimUserPayload(payload)\n const email = parsed.email ?? parsed.userName\n if (!email) {\n throw new ScimServiceError(400, 'userName or emails[0].value is required')\n }\n\n // Idempotency: if externalId already exists for this config, return existing\n if (parsed.externalId) {\n const existingIdentity = await this.em.findOne(SsoIdentity, {\n ssoConfigId: scope.ssoConfigId,\n externalId: parsed.externalId,\n deletedAt: null,\n })\n if (existingIdentity) {\n const existingUser = await findOneWithDecryption(\n this.em, User,\n { id: existingIdentity.userId, deletedAt: null },\n {},\n { tenantId: scope.tenantId ?? '', organizationId: scope.organizationId },\n )\n if (existingUser) {\n const deactivation = await this.em.findOne(SsoUserDeactivation, {\n userId: existingUser.id, ssoConfigId: scope.ssoConfigId,\n })\n await this.log(scope, 'CREATE', existingIdentity.id, parsed.externalId, 200)\n return {\n resource: toScimUserResource(existingUser, existingIdentity, baseUrl, deactivation),\n status: 200,\n }\n }\n }\n }\n\n // Check if user already exists by email\n const emailHash = computeEmailHash(email)\n const where: FilterQuery<User> = {\n organizationId: scope.organizationId,\n deletedAt: null,\n $or: [{ email }, { emailHash }],\n }\n const existingUser = await findOneWithDecryption(\n this.em, User,\n where,\n {},\n { tenantId: scope.tenantId ?? '', organizationId: scope.organizationId },\n )\n\n if (existingUser) {\n // Check if already linked to this SSO config\n const existingLink = await this.em.findOne(SsoIdentity, {\n ssoConfigId: scope.ssoConfigId,\n userId: existingUser.id,\n deletedAt: null,\n })\n if (existingLink) {\n throw new ScimServiceError(409, `User with email ${email} is already linked to this SSO configuration`)\n }\n\n // Auto-link: create SsoIdentity for existing user\n const now = new Date()\n const identity = this.em.create(SsoIdentity, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n ssoConfigId: scope.ssoConfigId,\n userId: existingUser.id,\n idpSubject: parsed.externalId ?? email,\n idpEmail: email,\n idpName: buildDisplayName(parsed),\n idpGroups: [],\n externalId: parsed.externalId ?? null,\n provisioningMethod: 'scim',\n createdAt: now,\n updatedAt: now,\n } as RequiredEntityData<SsoIdentity>)\n await this.em.persist(identity).flush()\n\n const deactivation = parsed.active === false\n ? await this.createDeactivation(existingUser.id, scope)\n : null\n\n await this.log(scope, 'CREATE', identity.id, parsed.externalId, 201)\n return {\n resource: toScimUserResource(existingUser, identity, baseUrl, deactivation),\n status: 201,\n }\n }\n\n // Create new user + identity\n return this.em.transactional(async (txEm) => {\n const user = txEm.create(User, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n email,\n emailHash: computeEmailHash(email),\n name: buildDisplayName(parsed) ?? undefined,\n passwordHash: null,\n isConfirmed: true,\n createdAt: new Date(),\n })\n await txEm.persist(user).flush()\n\n const now = new Date()\n const identity = txEm.create(SsoIdentity, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n ssoConfigId: scope.ssoConfigId,\n userId: user.id,\n idpSubject: parsed.externalId ?? email,\n idpEmail: email,\n idpName: buildDisplayName(parsed),\n idpGroups: [],\n externalId: parsed.externalId ?? null,\n provisioningMethod: 'scim',\n createdAt: now,\n updatedAt: now,\n } as RequiredEntityData<SsoIdentity>)\n await txEm.persist(identity).flush()\n\n const deactivation = parsed.active === false\n ? await this.createDeactivationTx(txEm, user.id, scope)\n : null\n\n await this.logTx(txEm, scope, 'CREATE', identity.id, parsed.externalId, 201)\n return {\n resource: toScimUserResource(user, identity, baseUrl, deactivation),\n status: 201,\n }\n })\n }\n\n async getUser(scimId: string, scope: ScimScope, baseUrl: string): Promise<ScimUserResource> {\n const identity = await this.em.findOne(SsoIdentity, {\n id: scimId,\n ssoConfigId: scope.ssoConfigId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (!identity) throw new ScimServiceError(404, 'User not found')\n\n const user = await findOneWithDecryption(\n this.em, User,\n { id: identity.userId, deletedAt: null },\n {},\n { tenantId: scope.tenantId ?? '', organizationId: scope.organizationId },\n )\n if (!user) throw new ScimServiceError(404, 'User not found')\n\n const deactivation = await this.em.findOne(SsoUserDeactivation, {\n userId: user.id, ssoConfigId: scope.ssoConfigId,\n })\n\n return toScimUserResource(user, identity, baseUrl, deactivation)\n }\n\n async listUsers(\n filter: string | null,\n startIndex: number,\n count: number,\n scope: ScimScope,\n baseUrl: string,\n ): Promise<Record<string, unknown>> {\n const conditions = parseScimFilter(filter)\n const where = scimFilterToWhere(conditions, scope.ssoConfigId, scope.organizationId)\n\n const offset = Math.max(0, startIndex - 1)\n const [identities, total] = await this.em.findAndCount(SsoIdentity, where, {\n orderBy: { createdAt: 'asc' },\n limit: count,\n offset,\n })\n\n const userIds = identities.map((i) => i.userId)\n\n const users = userIds.length > 0\n ? await findWithDecryption(\n this.em, User,\n { id: { $in: userIds }, deletedAt: null },\n {},\n { tenantId: scope.tenantId ?? '', organizationId: scope.organizationId },\n )\n : []\n const userMap = new Map(users.map((u) => [u.id, u]))\n\n const deactivations = userIds.length > 0\n ? await this.em.find(SsoUserDeactivation, {\n userId: { $in: userIds }, ssoConfigId: scope.ssoConfigId,\n })\n : []\n const deactivationMap = new Map(deactivations.map((d) => [d.userId, d]))\n\n const resources: ScimUserResource[] = []\n for (const identity of identities) {\n const user = userMap.get(identity.userId)\n if (!user) continue\n\n const deactivation = deactivationMap.get(user.id) ?? null\n resources.push(toScimUserResource(user, identity, baseUrl, deactivation))\n }\n\n return buildListResponse(resources, total, startIndex, resources.length)\n }\n\n async patchUser(\n scimId: string,\n operations: ScimPatchOperation[],\n scope: ScimScope,\n baseUrl: string,\n ): Promise<ScimUserResource> {\n const identity = await this.em.findOne(SsoIdentity, {\n id: scimId,\n ssoConfigId: scope.ssoConfigId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (!identity) throw new ScimServiceError(404, 'User not found')\n\n const user = await findOneWithDecryption(\n this.em, User,\n { id: identity.userId, deletedAt: null },\n {},\n { tenantId: scope.tenantId ?? '', organizationId: scope.organizationId },\n )\n if (!user) throw new ScimServiceError(404, 'User not found')\n\n for (const op of operations) {\n const normalizedOp = op.op.toLowerCase()\n if (normalizedOp === 'replace' || normalizedOp === 'add') {\n this.applyPatchValue(user, identity, op.path, op.value)\n }\n // 'remove' operations on optional fields \u2014 set to null\n if (normalizedOp === 'remove' && op.path) {\n this.applyPatchValue(user, identity, op.path, null)\n }\n }\n\n // Handle active status changes\n const activeOp = operations.find((op) =>\n op.path?.toLowerCase() === 'active' ||\n (!op.path && op.value && typeof op.value === 'object' && 'active' in (op.value as Record<string, unknown>)),\n )\n\n if (activeOp) {\n const activeValue = activeOp.path\n ? coerceBoolean(activeOp.value)\n : coerceBoolean((activeOp.value as Record<string, unknown>).active)\n\n if (activeValue === false) {\n await this.deactivateUser(user.id, scope)\n } else if (activeValue === true) {\n await this.reactivateUser(user.id, scope)\n }\n }\n\n await this.em.flush()\n\n const deactivation = await this.em.findOne(SsoUserDeactivation, {\n userId: user.id, ssoConfigId: scope.ssoConfigId,\n })\n\n await this.log(scope, 'PATCH', identity.id, identity.externalId, 200)\n return toScimUserResource(user, identity, baseUrl, deactivation)\n }\n\n async deleteUser(scimId: string, scope: ScimScope): Promise<void> {\n const identity = await this.em.findOne(SsoIdentity, {\n id: scimId,\n ssoConfigId: scope.ssoConfigId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (!identity) throw new ScimServiceError(404, 'User not found')\n\n await this.deactivateUser(identity.userId, scope)\n await this.log(scope, 'DELETE', identity.id, identity.externalId, 204)\n }\n\n private applyPatchValue(\n user: User,\n identity: SsoIdentity,\n path: string | undefined,\n value: unknown,\n ): void {\n if (!path) {\n // No path means value is an object with attribute keys\n if (value && typeof value === 'object') {\n const obj = value as Record<string, unknown>\n for (const [key, val] of Object.entries(obj)) {\n this.applyPatchValue(user, identity, key, val)\n }\n }\n return\n }\n\n const normalizedPath = path.toLowerCase()\n switch (normalizedPath) {\n case 'displayname':\n user.name = (value as string) || undefined\n identity.idpName = (value as string) ?? null\n break\n case 'name.givenname': {\n const currentParts = (user.name ?? '').split(' ')\n currentParts[0] = (value as string) ?? ''\n user.name = currentParts.join(' ').trim() || undefined\n break\n }\n case 'name.familyname': {\n const currentParts = (user.name ?? '').split(' ')\n const given = currentParts[0] ?? ''\n user.name = value ? `${given} ${value}`.trim() : given || undefined\n break\n }\n case 'username':\n identity.idpEmail = (value as string) ?? identity.idpEmail\n break\n case 'externalid':\n identity.externalId = (value as string) ?? null\n break\n case 'active':\n // Handled separately via deactivation logic\n break\n }\n }\n\n private async deactivateUser(userId: string, scope: ScimScope): Promise<void> {\n let deactivation = await this.em.findOne(SsoUserDeactivation, {\n userId, ssoConfigId: scope.ssoConfigId,\n })\n\n if (deactivation) {\n deactivation.deactivatedAt = new Date()\n deactivation.reactivatedAt = null\n } else {\n deactivation = this.em.create(SsoUserDeactivation, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n userId,\n ssoConfigId: scope.ssoConfigId,\n deactivatedAt: new Date(),\n } as RequiredEntityData<SsoUserDeactivation>)\n this.em.persist(deactivation)\n }\n await this.em.flush()\n\n // Revoke all active sessions\n const sessionWhere: FilterQuery<Session> = { user: userId }\n await this.em.nativeDelete(Session, sessionWhere)\n }\n\n private async reactivateUser(userId: string, scope: ScimScope): Promise<void> {\n const deactivation = await this.em.findOne(SsoUserDeactivation, {\n userId, ssoConfigId: scope.ssoConfigId,\n })\n if (deactivation && !deactivation.reactivatedAt) {\n deactivation.reactivatedAt = new Date()\n await this.em.flush()\n }\n }\n\n private async createDeactivation(userId: string, scope: ScimScope): Promise<SsoUserDeactivation> {\n const deactivation = this.em.create(SsoUserDeactivation, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n userId,\n ssoConfigId: scope.ssoConfigId,\n deactivatedAt: new Date(),\n } as RequiredEntityData<SsoUserDeactivation>)\n await this.em.persist(deactivation).flush()\n return deactivation\n }\n\n private async createDeactivationTx(txEm: EntityManager, userId: string, scope: ScimScope): Promise<SsoUserDeactivation> {\n const deactivation = txEm.create(SsoUserDeactivation, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n userId,\n ssoConfigId: scope.ssoConfigId,\n deactivatedAt: new Date(),\n } as RequiredEntityData<SsoUserDeactivation>)\n await txEm.persist(deactivation).flush()\n return deactivation\n }\n\n private async log(\n scope: ScimScope,\n operation: string,\n resourceId: string | null | undefined,\n externalId: string | null | undefined,\n responseStatus: number,\n errorMessage?: string,\n ): Promise<void> {\n const entry = this.em.create(ScimProvisioningLog, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n ssoConfigId: scope.ssoConfigId,\n operation,\n resourceType: 'User',\n resourceId: resourceId ?? null,\n scimExternalId: externalId ?? null,\n responseStatus,\n errorMessage: errorMessage ?? null,\n } as RequiredEntityData<ScimProvisioningLog>)\n await this.em.persist(entry).flush()\n }\n\n private async logTx(\n txEm: EntityManager,\n scope: ScimScope,\n operation: string,\n resourceId: string | null | undefined,\n externalId: string | null | undefined,\n responseStatus: number,\n ): Promise<void> {\n const entry = txEm.create(ScimProvisioningLog, {\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId,\n ssoConfigId: scope.ssoConfigId,\n operation,\n resourceType: 'User',\n resourceId: resourceId ?? null,\n scimExternalId: externalId ?? null,\n responseStatus,\n } as RequiredEntityData<ScimProvisioningLog>)\n await txEm.persist(entry).flush()\n }\n}\n\nfunction buildDisplayName(parsed: ScimUserPayload): string | null {\n if (parsed.displayName) return parsed.displayName\n const parts = [parsed.givenName, parsed.familyName].filter(Boolean)\n return parts.length > 0 ? parts.join(' ') : null\n}\n\nexport class ScimServiceError extends Error {\n constructor(\n public readonly statusCode: number,\n message: string,\n ) {\n super(message)\n this.name = 'ScimServiceError'\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["existingUser"]
7
7
  }
@@ -30,7 +30,7 @@ class ScimTokenService {
30
30
  tenantId: scope.tenantId,
31
31
  organizationId: scope.organizationId
32
32
  });
33
- await this.em.persistAndFlush(token);
33
+ await this.em.persist(token).flush();
34
34
  return { id: token.id, token: raw, prefix: tokenPrefix, name };
35
35
  }
36
36
  async verifyToken(rawToken) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sso/services/scimTokenService.ts"],
4
- "sourcesContent": ["import { randomBytes } from 'node:crypto'\nimport type { RequiredEntityData } from '@mikro-orm/core'\nimport { EntityManager } from '@mikro-orm/postgresql'\nimport { hash, compare } from 'bcryptjs'\nimport { ScimToken, SsoConfig } from '../data/entities'\nimport type { SsoAdminScope } from './ssoConfigService'\n\nconst BCRYPT_COST = 10\nconst TOKEN_PREFIX = 'omscim_'\n\nexport interface ScimTokenPublic {\n id: string\n ssoConfigId: string\n name: string\n tokenPrefix: string\n isActive: boolean\n createdBy: string | null\n createdAt: Date\n}\n\nexport interface ScimTokenCreateResult {\n id: string\n token: string\n prefix: string\n name: string\n}\n\nexport class ScimTokenService {\n constructor(private em: EntityManager) {}\n\n async generateToken(\n ssoConfigId: string,\n name: string,\n scope: SsoAdminScope,\n ): Promise<ScimTokenCreateResult> {\n const where: Record<string, unknown> = { id: ssoConfigId, deletedAt: null }\n if (!scope.isSuperAdmin && scope.organizationId) {\n where.organizationId = scope.organizationId\n }\n const config = await this.em.findOne(SsoConfig, where)\n if (!config) throw new ScimTokenError('SSO configuration not found', 404)\n if (config.jitEnabled) {\n throw new ScimTokenError('Cannot create SCIM tokens while JIT provisioning is enabled. Disable JIT first.', 409)\n }\n\n const raw = TOKEN_PREFIX + randomBytes(32).toString('hex')\n const tokenHash = await hash(raw, BCRYPT_COST)\n const tokenPrefix = raw.slice(0, 12)\n\n const token = this.em.create(ScimToken, {\n ssoConfigId,\n name,\n tokenHash,\n tokenPrefix,\n isActive: true,\n createdBy: null,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId!,\n } as RequiredEntityData<ScimToken>)\n\n await this.em.persistAndFlush(token)\n\n return { id: token.id, token: raw, prefix: tokenPrefix, name }\n }\n\n async verifyToken(rawToken: string): Promise<{\n ssoConfigId: string\n organizationId: string\n tenantId: string | null\n } | null> {\n const prefix = rawToken.slice(0, 12)\n\n const candidates = await this.em.find(ScimToken, {\n tokenPrefix: prefix,\n isActive: true,\n })\n\n if (candidates.length === 0) {\n await hash(rawToken, BCRYPT_COST)\n return null\n }\n\n for (const candidate of candidates) {\n const isValid = await compare(rawToken, candidate.tokenHash)\n if (isValid) {\n return {\n ssoConfigId: candidate.ssoConfigId,\n organizationId: candidate.organizationId,\n tenantId: candidate.tenantId ?? null,\n }\n }\n }\n\n return null\n }\n\n async revokeToken(tokenId: string, scope: SsoAdminScope): Promise<void> {\n const where: Record<string, unknown> = { id: tokenId }\n if (!scope.isSuperAdmin) where.organizationId = scope.organizationId\n\n const token = await this.em.findOne(ScimToken, where)\n if (!token) throw new ScimTokenError('SCIM token not found', 404)\n\n token.isActive = false\n await this.em.flush()\n }\n\n async listTokens(ssoConfigId: string, scope: SsoAdminScope): Promise<ScimTokenPublic[]> {\n const where: Record<string, unknown> = { ssoConfigId }\n if (!scope.isSuperAdmin) where.organizationId = scope.organizationId\n\n const tokens = await this.em.find(ScimToken, where, {\n orderBy: { createdAt: 'desc' },\n })\n\n return tokens.map((t) => ({\n id: t.id,\n ssoConfigId: t.ssoConfigId,\n name: t.name,\n tokenPrefix: t.tokenPrefix,\n isActive: t.isActive,\n createdBy: t.createdBy ?? null,\n createdAt: t.createdAt,\n }))\n }\n}\n\nexport class ScimTokenError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n ) {\n super(message)\n this.name = 'ScimTokenError'\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { randomBytes } from 'node:crypto'\nimport type { RequiredEntityData } from '@mikro-orm/core'\nimport { EntityManager } from '@mikro-orm/postgresql'\nimport { hash, compare } from 'bcryptjs'\nimport { ScimToken, SsoConfig } from '../data/entities'\nimport type { SsoAdminScope } from './ssoConfigService'\n\nconst BCRYPT_COST = 10\nconst TOKEN_PREFIX = 'omscim_'\n\nexport interface ScimTokenPublic {\n id: string\n ssoConfigId: string\n name: string\n tokenPrefix: string\n isActive: boolean\n createdBy: string | null\n createdAt: Date\n}\n\nexport interface ScimTokenCreateResult {\n id: string\n token: string\n prefix: string\n name: string\n}\n\nexport class ScimTokenService {\n constructor(private em: EntityManager) {}\n\n async generateToken(\n ssoConfigId: string,\n name: string,\n scope: SsoAdminScope,\n ): Promise<ScimTokenCreateResult> {\n const where: Record<string, unknown> = { id: ssoConfigId, deletedAt: null }\n if (!scope.isSuperAdmin && scope.organizationId) {\n where.organizationId = scope.organizationId\n }\n const config = await this.em.findOne(SsoConfig, where)\n if (!config) throw new ScimTokenError('SSO configuration not found', 404)\n if (config.jitEnabled) {\n throw new ScimTokenError('Cannot create SCIM tokens while JIT provisioning is enabled. Disable JIT first.', 409)\n }\n\n const raw = TOKEN_PREFIX + randomBytes(32).toString('hex')\n const tokenHash = await hash(raw, BCRYPT_COST)\n const tokenPrefix = raw.slice(0, 12)\n\n const token = this.em.create(ScimToken, {\n ssoConfigId,\n name,\n tokenHash,\n tokenPrefix,\n isActive: true,\n createdBy: null,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId!,\n } as RequiredEntityData<ScimToken>)\n\n await this.em.persist(token).flush()\n\n return { id: token.id, token: raw, prefix: tokenPrefix, name }\n }\n\n async verifyToken(rawToken: string): Promise<{\n ssoConfigId: string\n organizationId: string\n tenantId: string | null\n } | null> {\n const prefix = rawToken.slice(0, 12)\n\n const candidates = await this.em.find(ScimToken, {\n tokenPrefix: prefix,\n isActive: true,\n })\n\n if (candidates.length === 0) {\n await hash(rawToken, BCRYPT_COST)\n return null\n }\n\n for (const candidate of candidates) {\n const isValid = await compare(rawToken, candidate.tokenHash)\n if (isValid) {\n return {\n ssoConfigId: candidate.ssoConfigId,\n organizationId: candidate.organizationId,\n tenantId: candidate.tenantId ?? null,\n }\n }\n }\n\n return null\n }\n\n async revokeToken(tokenId: string, scope: SsoAdminScope): Promise<void> {\n const where: Record<string, unknown> = { id: tokenId }\n if (!scope.isSuperAdmin) where.organizationId = scope.organizationId\n\n const token = await this.em.findOne(ScimToken, where)\n if (!token) throw new ScimTokenError('SCIM token not found', 404)\n\n token.isActive = false\n await this.em.flush()\n }\n\n async listTokens(ssoConfigId: string, scope: SsoAdminScope): Promise<ScimTokenPublic[]> {\n const where: Record<string, unknown> = { ssoConfigId }\n if (!scope.isSuperAdmin) where.organizationId = scope.organizationId\n\n const tokens = await this.em.find(ScimToken, where, {\n orderBy: { createdAt: 'desc' },\n })\n\n return tokens.map((t) => ({\n id: t.id,\n ssoConfigId: t.ssoConfigId,\n name: t.name,\n tokenPrefix: t.tokenPrefix,\n isActive: t.isActive,\n createdBy: t.createdBy ?? null,\n createdAt: t.createdAt,\n }))\n }\n}\n\nexport class ScimTokenError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n ) {\n super(message)\n this.name = 'ScimTokenError'\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -83,7 +83,7 @@ class SsoConfigService {
83
83
  ssoRequired: false,
84
84
  appRoleMappings: input.appRoleMappings ?? {}
85
85
  });
86
- await this.em.persistAndFlush(config);
86
+ await this.em.persist(config).flush();
87
87
  void emitSsoEvent("sso.config.created", {
88
88
  id: config.id,
89
89
  tenantId: config.tenantId,