@open-mercato/enterprise 0.6.6-develop.6465.1.019f0fb26f → 0.6.6-develop.6471.1.9ab5bdd79a

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 (66) hide show
  1. package/dist/modules/record_locks/subscribers/conflict-detected-notification.js +3 -1
  2. package/dist/modules/record_locks/subscribers/conflict-detected-notification.js.map +2 -2
  3. package/dist/modules/record_locks/subscribers/conflict-resolved-notification.js +3 -1
  4. package/dist/modules/record_locks/subscribers/conflict-resolved-notification.js.map +2 -2
  5. package/dist/modules/record_locks/subscribers/lock-contended-notification.js +3 -1
  6. package/dist/modules/record_locks/subscribers/lock-contended-notification.js.map +2 -2
  7. package/dist/modules/record_locks/subscribers/lock-force-released-notification.js +3 -1
  8. package/dist/modules/record_locks/subscribers/lock-force-released-notification.js.map +2 -2
  9. package/dist/modules/record_locks/widgets/injection/record-locking/widget.client.js +4 -2
  10. package/dist/modules/record_locks/widgets/injection/record-locking/widget.client.js.map +2 -2
  11. package/dist/modules/security/api/enforcement/_shared.js +3 -1
  12. package/dist/modules/security/api/enforcement/_shared.js.map +2 -2
  13. package/dist/modules/security/api/mfa/_shared.js +3 -1
  14. package/dist/modules/security/api/mfa/_shared.js.map +2 -2
  15. package/dist/modules/security/api/profile/password/route.js +3 -1
  16. package/dist/modules/security/api/profile/password/route.js.map +2 -2
  17. package/dist/modules/security/api/sudo/_shared.js +3 -1
  18. package/dist/modules/security/api/sudo/_shared.js.map +2 -2
  19. package/dist/modules/security/api/users/_shared.js +3 -1
  20. package/dist/modules/security/api/users/_shared.js.map +2 -2
  21. package/dist/modules/sso/api/callback/oidc/route.js +5 -3
  22. package/dist/modules/sso/api/callback/oidc/route.js.map +2 -2
  23. package/dist/modules/sso/api/error-handler.js +4 -2
  24. package/dist/modules/sso/api/error-handler.js.map +2 -2
  25. package/dist/modules/sso/api/hrd/route.js +3 -1
  26. package/dist/modules/sso/api/hrd/route.js.map +2 -2
  27. package/dist/modules/sso/api/initiate/route.js +4 -2
  28. package/dist/modules/sso/api/initiate/route.js.map +2 -2
  29. package/dist/modules/sso/backend/sso/config/[id]/page.js +2 -2
  30. package/dist/modules/sso/backend/sso/config/[id]/page.js.map +2 -2
  31. package/dist/modules/sso/i18n/de.json +2 -2
  32. package/dist/modules/sso/i18n/en.json +2 -2
  33. package/dist/modules/sso/i18n/es.json +2 -2
  34. package/dist/modules/sso/i18n/pl.json +2 -2
  35. package/dist/modules/sso/services/accountLinkingService.js +9 -46
  36. package/dist/modules/sso/services/accountLinkingService.js.map +2 -2
  37. package/dist/modules/sso/services/ssoConfigService.js +9 -7
  38. package/dist/modules/sso/services/ssoConfigService.js.map +2 -2
  39. package/dist/modules/sso/services/ssoService.js +4 -2
  40. package/dist/modules/sso/services/ssoService.js.map +2 -2
  41. package/dist/modules/system_status_overlays/subscribers/application-bootstrap-enterprise-warning.js +4 -2
  42. package/dist/modules/system_status_overlays/subscribers/application-bootstrap-enterprise-warning.js.map +2 -2
  43. package/package.json +5 -5
  44. package/src/modules/record_locks/subscribers/conflict-detected-notification.ts +4 -1
  45. package/src/modules/record_locks/subscribers/conflict-resolved-notification.ts +4 -1
  46. package/src/modules/record_locks/subscribers/lock-contended-notification.ts +4 -1
  47. package/src/modules/record_locks/subscribers/lock-force-released-notification.ts +4 -1
  48. package/src/modules/record_locks/widgets/injection/record-locking/widget.client.tsx +5 -2
  49. package/src/modules/security/api/enforcement/_shared.ts +4 -1
  50. package/src/modules/security/api/mfa/_shared.ts +4 -1
  51. package/src/modules/security/api/profile/password/route.ts +4 -1
  52. package/src/modules/security/api/sudo/_shared.ts +4 -1
  53. package/src/modules/security/api/users/_shared.ts +4 -1
  54. package/src/modules/sso/api/callback/oidc/route.ts +6 -3
  55. package/src/modules/sso/api/error-handler.ts +5 -2
  56. package/src/modules/sso/api/hrd/route.ts +4 -1
  57. package/src/modules/sso/api/initiate/route.ts +5 -2
  58. package/src/modules/sso/backend/sso/config/[id]/page.tsx +2 -2
  59. package/src/modules/sso/i18n/de.json +2 -2
  60. package/src/modules/sso/i18n/en.json +2 -2
  61. package/src/modules/sso/i18n/es.json +2 -2
  62. package/src/modules/sso/i18n/pl.json +2 -2
  63. package/src/modules/sso/services/accountLinkingService.ts +10 -52
  64. package/src/modules/sso/services/ssoConfigService.ts +10 -7
  65. package/src/modules/sso/services/ssoService.ts +5 -2
  66. package/src/modules/system_status_overlays/subscribers/application-bootstrap-enterprise-warning.ts +6 -2
@@ -5,6 +5,9 @@ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
5
5
  import { SsoService } from '../../services/ssoService'
6
6
  import { ssoInitiateSchema } from '../../data/validators'
7
7
  import { emitSsoEvent } from '../../events'
8
+ import { createLogger } from '@open-mercato/shared/lib/logger'
9
+
10
+ const logger = createLogger('sso').child({ component: 'initiate' })
8
11
 
9
12
  export const metadata = {
10
13
  GET: { requireAuth: false },
@@ -50,10 +53,10 @@ export async function GET(req: Request) {
50
53
  })
51
54
  return res
52
55
  } catch (err) {
53
- console.error('[SSO Initiate] Error:', err)
56
+ logger.error('SSO initiate error', { err })
54
57
  void emitSsoEvent('sso.login.failed', {
55
58
  reason: err instanceof Error ? err.message : 'initiate_failed',
56
- }).catch((e) => console.error('[SSO Event]', e))
59
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
57
60
  return NextResponse.redirect(toAbsoluteUrl(req, '/login?error=sso_failed'))
58
61
  }
59
62
  }
@@ -647,7 +647,7 @@ function RoleMappingsTab({
647
647
  return (
648
648
  <div>
649
649
  <p className="text-sm text-muted-foreground mb-4">
650
- {t('sso.admin.roles.description', 'Map IdP app role names to local roles. On each SSO login, SSO-sourced roles are synced roles no longer sent by the IdP are removed, while manually-assigned roles are preserved.')}
650
+ {t('sso.admin.roles.description', 'Map IdP app role names to local roles. Only explicitly mapped IdP roles grant local roles; on each SSO login, SSO-sourced roles are synced while manually-assigned roles are preserved.')}
651
651
  </p>
652
652
  <div className="flex items-end gap-2 mb-4">
653
653
  <div className="flex-1">
@@ -696,7 +696,7 @@ function RoleMappingsTab({
696
696
  </div>
697
697
  ) : (
698
698
  <p className="text-sm text-muted-foreground py-4 text-center mb-4">
699
- {t('sso.admin.roles.empty', 'No role mappings configured. IdP role names will be matched directly against local role names.')}
699
+ {t('sso.admin.roles.empty', 'No role mappings configured. Users will not receive SSO-sourced roles until an IdP role is explicitly mapped to a local role.')}
700
700
  </p>
701
701
  )}
702
702
 
@@ -72,8 +72,8 @@
72
72
  "sso.admin.field.secretRequired": "Client-Geheimnis eingeben",
73
73
  "sso.admin.field.secretSet": "Client-Geheimnis ist konfiguriert",
74
74
  "sso.admin.new": "Neue SSO-Konfiguration",
75
- "sso.admin.roles.description": "Ordnen Sie IdP-App-Rollennamen lokalen Rollen zu. Bei jeder SSO-Anmeldung werden SSO-basierte Rollen synchronisiert — Rollen, die der IdP nicht mehr sendet, werden entfernt, während manuell zugewiesene Rollen erhalten bleiben.",
76
- "sso.admin.roles.empty": "Keine Rollenzuordnungen konfiguriert. IdP-Rollennamen werden direkt mit lokalen Rollennamen abgeglichen.",
75
+ "sso.admin.roles.description": "Ordnen Sie IdP-App-Rollennamen lokalen Rollen zu. Nur ausdrücklich zugeordnete IdP-Rollen vergeben lokale Rollen; bei jeder SSO-Anmeldung werden SSO-basierte Rollen synchronisiert, während manuell zugewiesene Rollen erhalten bleiben.",
76
+ "sso.admin.roles.empty": "Keine Rollenzuordnungen konfiguriert. Benutzer erhalten keine SSO-basierten Rollen, bis eine IdP-Rolle ausdrücklich einer lokalen Rolle zugeordnet ist.",
77
77
  "sso.admin.roles.error.duplicate": "Diese IdP-Rolle ist bereits zugeordnet",
78
78
  "sso.admin.roles.error.emptyIdpRole": "IdP-Rollenname ist erforderlich",
79
79
  "sso.admin.roles.error.emptyLocalRole": "Wählen Sie eine lokale Rolle",
@@ -72,8 +72,8 @@
72
72
  "sso.admin.field.secretRequired": "Enter client secret",
73
73
  "sso.admin.field.secretSet": "Client secret is configured",
74
74
  "sso.admin.new": "New SSO Config",
75
- "sso.admin.roles.description": "Map IdP app role names to local roles. On each SSO login, SSO-sourced roles are synced roles no longer sent by the IdP are removed, while manually-assigned roles are preserved.",
76
- "sso.admin.roles.empty": "No role mappings configured. IdP role names will be matched directly against local role names.",
75
+ "sso.admin.roles.description": "Map IdP app role names to local roles. Only explicitly mapped IdP roles grant local roles; on each SSO login, SSO-sourced roles are synced while manually-assigned roles are preserved.",
76
+ "sso.admin.roles.empty": "No role mappings configured. Users will not receive SSO-sourced roles until an IdP role is explicitly mapped to a local role.",
77
77
  "sso.admin.roles.error.duplicate": "This IdP role is already mapped",
78
78
  "sso.admin.roles.error.emptyIdpRole": "IdP role name is required",
79
79
  "sso.admin.roles.error.emptyLocalRole": "Select a local role",
@@ -72,8 +72,8 @@
72
72
  "sso.admin.field.secretRequired": "Ingrese el secreto de cliente",
73
73
  "sso.admin.field.secretSet": "El secreto de cliente está configurado",
74
74
  "sso.admin.new": "Nueva configuración SSO",
75
- "sso.admin.roles.description": "Asigne nombres de roles de aplicación IdP a roles locales. En cada inicio de sesión SSO, los roles de origen SSO se sincronizan los roles que el IdP ya no envía se eliminan, mientras que los roles asignados manualmente se conservan.",
76
- "sso.admin.roles.empty": "No hay asignaciones de roles configuradas. Los nombres de roles del IdP se compararán directamente con los nombres de roles locales.",
75
+ "sso.admin.roles.description": "Asigne nombres de roles de aplicación IdP a roles locales. Solo los roles de IdP asignados explícitamente conceden roles locales; en cada inicio de sesión SSO, los roles de origen SSO se sincronizan mientras se conservan los roles asignados manualmente.",
76
+ "sso.admin.roles.empty": "No hay asignaciones de roles configuradas. Los usuarios no recibirán roles de origen SSO hasta que un rol de IdP se asigne explícitamente a un rol local.",
77
77
  "sso.admin.roles.error.duplicate": "Este rol de IdP ya está asignado",
78
78
  "sso.admin.roles.error.emptyIdpRole": "El nombre del rol de IdP es obligatorio",
79
79
  "sso.admin.roles.error.emptyLocalRole": "Seleccione un rol local",
@@ -72,8 +72,8 @@
72
72
  "sso.admin.field.secretRequired": "Wprowadź client secret",
73
73
  "sso.admin.field.secretSet": "Client secret jest skonfigurowany",
74
74
  "sso.admin.new": "Nowa konfiguracja SSO",
75
- "sso.admin.roles.description": "Mapuj nazwy ról IdP na lokalne role. Przy każdym logowaniu SSO role źródłowe SSO są synchronizowane role nieotrzymane od IdP są usuwane, role przypisane ręcznie zachowane.",
76
- "sso.admin.roles.empty": "Brak skonfigurowanych mapowań ról. Nazwy ról IdP będą dopasowywane bezpośrednio do nazw lokalnych ról.",
75
+ "sso.admin.roles.description": "Mapuj nazwy ról IdP na lokalne role. Tylko jawnie zmapowane role IdP nadają lokalne role; przy każdym logowaniu SSO role źródłowe SSO są synchronizowane, a role przypisane ręcznie pozostają zachowane.",
76
+ "sso.admin.roles.empty": "Brak skonfigurowanych mapowań ról. Użytkownicy nie otrzymają ról źródłowych SSO, dopóki rola IdP nie zostanie jawnie zmapowana na lokalną rolę.",
77
77
  "sso.admin.roles.error.duplicate": "Ta rola IdP jest już zmapowana",
78
78
  "sso.admin.roles.error.emptyIdpRole": "Nazwa roli IdP jest wymagana",
79
79
  "sso.admin.roles.error.emptyLocalRole": "Wybierz lokalną rolę",
@@ -6,6 +6,9 @@ import { SsoConfig, SsoIdentity, SsoRoleGrant, ScimToken } from '../data/entitie
6
6
  import { emitSsoEvent } from '../events'
7
7
  import { EmailNotVerifiedError } from '../lib/errors'
8
8
  import type { SsoIdentityPayload } from '../lib/types'
9
+ import { createLogger } from '@open-mercato/shared/lib/logger'
10
+
11
+ const logger = createLogger('sso').child({ component: 'account-linking' })
9
12
 
10
13
  export class AccountLinkingService {
11
14
  constructor(private em: EntityManager) {}
@@ -127,7 +130,7 @@ export class AccountLinkingService {
127
130
  id: identity.id,
128
131
  tenantId,
129
132
  organizationId: config.organizationId,
130
- }).catch((e) => console.error('[SSO Event]', e))
133
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
131
134
 
132
135
  return { user, identity }
133
136
  }
@@ -174,7 +177,7 @@ export class AccountLinkingService {
174
177
  id: identity.id,
175
178
  tenantId,
176
179
  organizationId: config.organizationId,
177
- }).catch((e) => console.error('[SSO Event]', e))
180
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
178
181
 
179
182
  return { user, identity }
180
183
  })
@@ -311,73 +314,28 @@ function resolveRoleNamesFromIdpGroups(
311
314
 
312
315
  for (const group of normalizedGroups) {
313
316
  const mapped = mergedMappings.get(group)
314
- if (mapped?.length) {
315
- for (const role of mapped) roleNames.add(role)
316
- continue
317
- }
317
+ if (!mapped?.length) continue
318
318
 
319
- roleNames.add(group)
320
- const segmented = group.split(/[\\/:]/).map((part) => normalizeToken(part)).filter((part): part is string => part !== null)
321
- for (const candidate of segmented) {
322
- roleNames.add(candidate)
323
- }
319
+ for (const role of mapped) roleNames.add(role)
324
320
  }
325
321
 
326
322
  return Array.from(roleNames)
327
323
  }
328
324
 
329
325
  function loadMergedMappings(configMappings?: Record<string, string>): Map<string, string[]> {
330
- const envMappings = loadGroupRoleMappingsFromEnv()
326
+ const mappings = new Map<string, string[]>()
331
327
 
332
- // Per-config mappings take precedence over env var
333
328
  if (configMappings && Object.keys(configMappings).length > 0) {
334
329
  for (const [group, roleName] of Object.entries(configMappings)) {
335
330
  const normalizedGroup = normalizeToken(group)
336
331
  if (!normalizedGroup) continue
337
332
  const normalizedRole = normalizeToken(roleName)
338
333
  if (!normalizedRole) continue
339
- envMappings.set(normalizedGroup, [normalizedRole])
340
- }
341
- }
342
-
343
- return envMappings
344
- }
345
-
346
- function loadGroupRoleMappingsFromEnv(): Map<string, string[]> {
347
- const raw = process.env.SSO_GROUP_ROLE_MAP
348
- if (!raw) return new Map()
349
-
350
- try {
351
- const parsed = JSON.parse(raw) as Record<string, unknown>
352
- const out = new Map<string, string[]>()
353
- for (const [group, roleValue] of Object.entries(parsed)) {
354
- const normalizedGroup = normalizeToken(group)
355
- if (!normalizedGroup) continue
356
- const roles = normalizeRoleList(roleValue)
357
- if (roles.length > 0) out.set(normalizedGroup, roles)
358
- }
359
- return out
360
- } catch {
361
- return new Map()
362
- }
363
- }
364
-
365
- function normalizeRoleList(value: unknown): string[] {
366
- if (typeof value === 'string') {
367
- const token = normalizeToken(value)
368
- return token ? [token] : []
369
- }
370
-
371
- if (Array.isArray(value)) {
372
- const out = new Set<string>()
373
- for (const entry of value) {
374
- const token = normalizeToken(entry)
375
- if (token) out.add(token)
334
+ mappings.set(normalizedGroup, [normalizedRole])
376
335
  }
377
- return Array.from(out)
378
336
  }
379
337
 
380
- return []
338
+ return mappings
381
339
  }
382
340
 
383
341
  function normalizeToken(value: unknown): string | null {
@@ -8,6 +8,9 @@ import type { SsoConfigAdminCreateInput, SsoConfigAdminUpdateInput, SsoConfigLis
8
8
  import { emitSsoEvent } from '../events'
9
9
  import { validateDomain, normalizeDomain, uniqueDomains, checkDomainLimit } from '../lib/domains'
10
10
  import type { SsoProviderRegistry } from '../lib/registry'
11
+ import { createLogger } from '@open-mercato/shared/lib/logger'
12
+
13
+ const logger = createLogger('sso').child({ component: 'config' })
11
14
 
12
15
  export interface SsoAdminScope {
13
16
  isSuperAdmin: boolean
@@ -138,7 +141,7 @@ export class SsoConfigService {
138
141
  id: config.id,
139
142
  tenantId: config.tenantId,
140
143
  organizationId: config.organizationId,
141
- }).catch((e) => console.error('[SSO Event]', e))
144
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
142
145
 
143
146
  return this.toPublic(config)
144
147
  }
@@ -178,7 +181,7 @@ export class SsoConfigService {
178
181
  id: config.id,
179
182
  tenantId: config.tenantId,
180
183
  organizationId: config.organizationId,
181
- }).catch((e) => console.error('[SSO Event]', e))
184
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
182
185
 
183
186
  return this.toPublic(config)
184
187
  }
@@ -197,7 +200,7 @@ export class SsoConfigService {
197
200
  id: config.id,
198
201
  tenantId: config.tenantId,
199
202
  organizationId: config.organizationId,
200
- }).catch((e) => console.error('[SSO Event]', e))
203
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
201
204
  }
202
205
 
203
206
  async activate(scope: SsoAdminScope, id: string, active: boolean): Promise<SsoConfigPublic> {
@@ -229,13 +232,13 @@ export class SsoConfigService {
229
232
  id: config.id,
230
233
  tenantId: config.tenantId,
231
234
  organizationId: config.organizationId,
232
- }).catch((e) => console.error('[SSO Event]', e))
235
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
233
236
  } else if (!active && wasActive) {
234
237
  void emitSsoEvent('sso.config.deactivated', {
235
238
  id: config.id,
236
239
  tenantId: config.tenantId,
237
240
  organizationId: config.organizationId,
238
- }).catch((e) => console.error('[SSO Event]', e))
241
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
239
242
  }
240
243
 
241
244
  return this.toPublic(config)
@@ -276,7 +279,7 @@ export class SsoConfigService {
276
279
  tenantId: config.tenantId,
277
280
  organizationId: config.organizationId,
278
281
  domain: normalized,
279
- }).catch((e) => console.error('[SSO Event]', e))
282
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
280
283
 
281
284
  return this.toPublic(config)
282
285
  })
@@ -294,7 +297,7 @@ export class SsoConfigService {
294
297
  tenantId: config.tenantId,
295
298
  organizationId: config.organizationId,
296
299
  domain: normalized,
297
- }).catch((e) => console.error('[SSO Event]', e))
300
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
298
301
 
299
302
  return this.toPublic(config)
300
303
  }
@@ -10,6 +10,9 @@ import type { AccountLinkingService } from './accountLinkingService'
10
10
  import { encryptStateCookie, decryptStateCookie, createFlowState } from '../lib/state-cookie'
11
11
  import { emitSsoEvent } from '../events'
12
12
  import type { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'
13
+ import { createLogger } from '@open-mercato/shared/lib/logger'
14
+
15
+ const logger = createLogger('sso').child({ component: 'service' })
13
16
 
14
17
  export class SsoService {
15
18
  constructor(
@@ -60,7 +63,7 @@ export class SsoService {
60
63
  void emitSsoEvent('sso.login.initiated', {
61
64
  tenantId: config.tenantId,
62
65
  organizationId: config.organizationId,
63
- }).catch((e) => console.error('[SSO Event]', e))
66
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
64
67
 
65
68
  const authUrl = await provider.buildAuthUrl(config, {
66
69
  state: state.state,
@@ -144,7 +147,7 @@ export class SsoService {
144
147
  id: String(user.id),
145
148
  tenantId: config.tenantId,
146
149
  organizationId: config.organizationId,
147
- }).catch((e) => console.error('[SSO Event]', e))
150
+ }).catch((eventError) => logger.error('SSO event emit failed', { err: eventError }))
148
151
 
149
152
  return {
150
153
  token,
@@ -1,3 +1,7 @@
1
+ import { createLogger } from '@open-mercato/shared/lib/logger'
2
+
3
+ const logger = createLogger('system_status_overlays')
4
+
1
5
  const APP_BOOTSTRAP_EVENT = 'application.bootstrap.completed'
2
6
  const ENTERPRISE_WARNING_GLOBAL_KEY = '__openMercatoEnterpriseLicenseWarningShown__'
3
7
 
@@ -11,7 +15,7 @@ export default async function handle() {
11
15
  if ((globalThis as Record<string, unknown>)[ENTERPRISE_WARNING_GLOBAL_KEY] === true) return
12
16
 
13
17
  ;(globalThis as Record<string, unknown>)[ENTERPRISE_WARNING_GLOBAL_KEY] = true
14
- console.warn(
15
- '[enterprise] Enterprise modules are enabled. Developer preview is free, but production usage requires a commercial enterprise license. See: https://github.com/open-mercato/open-mercato/blob/main/packages/enterprise/README.md',
18
+ logger.warn(
19
+ 'Enterprise modules are enabled. Developer preview is free, but production usage requires a commercial enterprise license. See: https://github.com/open-mercato/open-mercato/blob/main/packages/enterprise/README.md',
16
20
  )
17
21
  }