@things-factory/auth-base 10.1.192 → 10.1.202

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 (128) hide show
  1. package/dist-client/tsconfig.tsbuildinfo +1 -1
  2. package/dist-server/ai-entry/access-assistant.js +4 -3
  3. package/dist-server/ai-entry/access-assistant.js.map +1 -1
  4. package/dist-server/controllers/access-request.d.ts +8 -2
  5. package/dist-server/controllers/access-request.js +33 -6
  6. package/dist-server/controllers/access-request.js.map +1 -1
  7. package/dist-server/controllers/signin.d.ts +5 -0
  8. package/dist-server/controllers/signin.js +25 -0
  9. package/dist-server/controllers/signin.js.map +1 -1
  10. package/dist-server/index.js +7 -0
  11. package/dist-server/index.js.map +1 -1
  12. package/dist-server/middlewares/audit-scope-middleware.d.ts +7 -0
  13. package/dist-server/middlewares/audit-scope-middleware.js +15 -0
  14. package/dist-server/middlewares/audit-scope-middleware.js.map +1 -0
  15. package/dist-server/middlewares/index.js +3 -0
  16. package/dist-server/middlewares/index.js.map +1 -1
  17. package/dist-server/role-tasks.d.ts +2 -0
  18. package/dist-server/role-tasks.js +20 -0
  19. package/dist-server/role-tasks.js.map +1 -0
  20. package/dist-server/router/auth-checkin-router.js +3 -5
  21. package/dist-server/router/auth-checkin-router.js.map +1 -1
  22. package/dist-server/router/auth-public-process-router.js +7 -1
  23. package/dist-server/router/auth-public-process-router.js.map +1 -1
  24. package/dist-server/router/site-root-router.d.ts +2 -0
  25. package/dist-server/router/site-root-router.js +2 -0
  26. package/dist-server/router/site-root-router.js.map +1 -1
  27. package/dist-server/service/access-request/access-request-query.d.ts +7 -2
  28. package/dist-server/service/access-request/access-request-query.js +25 -4
  29. package/dist-server/service/access-request/access-request-query.js.map +1 -1
  30. package/dist-server/service/access-request/access-request-types.d.ts +7 -0
  31. package/dist-server/service/access-request/access-request-types.js +27 -1
  32. package/dist-server/service/access-request/access-request-types.js.map +1 -1
  33. package/dist-server/service/access-request/access-request.d.ts +1 -0
  34. package/dist-server/service/access-request/access-request.js +5 -0
  35. package/dist-server/service/access-request/access-request.js.map +1 -1
  36. package/dist-server/service/audit/audit-action.d.ts +36 -0
  37. package/dist-server/service/audit/audit-action.js +137 -0
  38. package/dist-server/service/audit/audit-action.js.map +1 -0
  39. package/dist-server/service/audit/audit-kinds.d.ts +19 -0
  40. package/dist-server/service/audit/audit-kinds.js +70 -0
  41. package/dist-server/service/audit/audit-kinds.js.map +1 -0
  42. package/dist-server/service/audit/audit-query.d.ts +11 -0
  43. package/dist-server/service/audit/audit-query.js +295 -0
  44. package/dist-server/service/audit/audit-query.js.map +1 -0
  45. package/dist-server/service/audit/audit-recorder.d.ts +77 -0
  46. package/dist-server/service/audit/audit-recorder.js +247 -0
  47. package/dist-server/service/audit/audit-recorder.js.map +1 -0
  48. package/dist-server/service/audit/audit-request.d.ts +28 -0
  49. package/dist-server/service/audit/audit-request.js +96 -0
  50. package/dist-server/service/audit/audit-request.js.map +1 -0
  51. package/dist-server/service/audit/audit-retention.d.ts +12 -0
  52. package/dist-server/service/audit/audit-retention.js +42 -0
  53. package/dist-server/service/audit/audit-retention.js.map +1 -0
  54. package/dist-server/service/audit/audit-subscriber.d.ts +39 -0
  55. package/dist-server/service/audit/audit-subscriber.js +191 -0
  56. package/dist-server/service/audit/audit-subscriber.js.map +1 -0
  57. package/dist-server/service/audit/audit-target.d.ts +19 -0
  58. package/dist-server/service/audit/audit-target.js +46 -0
  59. package/dist-server/service/audit/audit-target.js.map +1 -0
  60. package/dist-server/service/audit/audit-types.d.ts +44 -0
  61. package/dist-server/service/audit/audit-types.js +161 -0
  62. package/dist-server/service/audit/audit-types.js.map +1 -0
  63. package/dist-server/service/audit/index.d.ts +6 -0
  64. package/dist-server/service/audit/index.js +11 -0
  65. package/dist-server/service/audit/index.js.map +1 -0
  66. package/dist-server/service/domain-owner/domain-owner-mutation.js +3 -0
  67. package/dist-server/service/domain-owner/domain-owner-mutation.js.map +1 -1
  68. package/dist-server/service/index.d.ts +9 -2
  69. package/dist-server/service/index.js +19 -4
  70. package/dist-server/service/index.js.map +1 -1
  71. package/dist-server/service/open-fields.js +2 -1
  72. package/dist-server/service/open-fields.js.map +1 -1
  73. package/dist-server/service/privilege/privilege-directive.js +25 -1
  74. package/dist-server/service/privilege/privilege-directive.js.map +1 -1
  75. package/dist-server/service/role/role-mutation.js +8 -1
  76. package/dist-server/service/role/role-mutation.js.map +1 -1
  77. package/dist-server/service/role-template/role-task.d.ts +81 -0
  78. package/dist-server/service/role-template/role-task.js +129 -0
  79. package/dist-server/service/role-template/role-task.js.map +1 -0
  80. package/dist-server/service/role-template/role-template-gap-resolver.d.ts +9 -5
  81. package/dist-server/service/role-template/role-template-gap-resolver.js +41 -13
  82. package/dist-server/service/role-template/role-template-gap-resolver.js.map +1 -1
  83. package/dist-server/service/role-template/role-template-mutation.js +10 -1
  84. package/dist-server/service/role-template/role-template-mutation.js.map +1 -1
  85. package/dist-server/service/role-template/role-template-query.js +4 -2
  86. package/dist-server/service/role-template/role-template-query.js.map +1 -1
  87. package/dist-server/service/role-template/role-template-types.d.ts +24 -0
  88. package/dist-server/service/role-template/role-template-types.js +63 -1
  89. package/dist-server/service/role-template/role-template-types.js.map +1 -1
  90. package/dist-server/service/role-template/role-template.d.ts +63 -10
  91. package/dist-server/service/role-template/role-template.js +92 -18
  92. package/dist-server/service/role-template/role-template.js.map +1 -1
  93. package/dist-server/tsconfig.tsbuildinfo +1 -1
  94. package/dist-server/utils/access-request-review.d.ts +12 -2
  95. package/dist-server/utils/access-request-review.js +13 -3
  96. package/dist-server/utils/access-request-review.js.map +1 -1
  97. package/dist-server/utils/check-permission.d.ts +6 -0
  98. package/dist-server/utils/check-permission.js +23 -8
  99. package/dist-server/utils/check-permission.js.map +1 -1
  100. package/package.json +3 -2
  101. package/tests/access-assistant-db.test.ts +1 -1
  102. package/tests/access-request-db.test.ts +71 -0
  103. package/tests/access-request-review.test.ts +30 -0
  104. package/tests/audit-db.test.ts +394 -0
  105. package/tests/auth-base-role-tasks.test.ts +58 -0
  106. package/tests/list-inheritance-db.test.ts +0 -7
  107. package/tests/open-doors.test.ts +2 -2
  108. package/tests/refusal-cause.test.ts +1 -1
  109. package/tests/role-task.test.ts +168 -0
  110. package/tests/role-template-gap-db.test.ts +77 -2
  111. package/translations/en.json +14 -1
  112. package/translations/es.json +14 -1
  113. package/translations/ja.json +14 -1
  114. package/translations/ko.json +14 -1
  115. package/translations/ms.json +14 -1
  116. package/translations/zh.json +14 -1
  117. package/dist-server/service/login-history/index.d.ts +0 -4
  118. package/dist-server/service/login-history/index.js +0 -8
  119. package/dist-server/service/login-history/index.js.map +0 -1
  120. package/dist-server/service/login-history/login-history-query.d.ts +0 -10
  121. package/dist-server/service/login-history/login-history-query.js +0 -80
  122. package/dist-server/service/login-history/login-history-query.js.map +0 -1
  123. package/dist-server/service/login-history/login-history-type.d.ts +0 -5
  124. package/dist-server/service/login-history/login-history-type.js +0 -21
  125. package/dist-server/service/login-history/login-history-type.js.map +0 -1
  126. package/dist-server/service/login-history/login-history.d.ts +0 -12
  127. package/dist-server/service/login-history/login-history.js +0 -58
  128. package/dist-server/service/login-history/login-history.js.map +0 -1
@@ -16,9 +16,11 @@ const { User } = loadCompiled('service/user/user')
16
16
  const { RoleTemplateMutation } = loadCompiled('service/role-template/role-template-mutation')
17
17
  const { RoleTemplateGapResolver } = loadCompiled('service/role-template/role-template-gap-resolver')
18
18
  const { registerRoleTemplate } = loadCompiled('service/role-template/role-template')
19
+ const { registerRoleTask } = loadCompiled('service/role-template/role-task')
19
20
 
20
21
  const mutation = new RoleTemplateMutation()
21
- const gapOf = (role: any) => new RoleTemplateGapResolver().templateGap(role)
22
+ const gapOf = (role: any, templateId?: string) => new RoleTemplateGapResolver().templateGap(role, templateId)
23
+ const coverageOf = (role: any) => new RoleTemplateGapResolver().taskCoverage(role)
22
24
 
23
25
  let domain: any
24
26
  let actor: any
@@ -71,7 +73,9 @@ describe('the gap between a role and its template', () => {
71
73
  templateId: 'plant.qi',
72
74
  templateName: 'QUALITY-INSPECTOR',
73
75
  missing: [],
74
- missingCount: 0
76
+ missingCount: 0,
77
+ extra: [],
78
+ extraCount: 0
75
79
  })
76
80
  })
77
81
 
@@ -120,6 +124,77 @@ describe('the gap between a role and its template', () => {
120
124
  ]
121
125
  await getRepository(Role).save(stored)
122
126
 
127
+ const gap = await gapOf(role)
128
+ expect(gap.missingCount).toBe(0)
129
+ /* Shown as wider than the template - not a fault, and nothing takes it away. */
130
+ expect(gap.extra).toEqual([{ category: 'board', axes: ['mutation'] }])
131
+ expect(gap.extraCount).toBe(1)
132
+ })
133
+
134
+ it('★ lays a role against a template the administrator picks, whatever the role is called', async () => {
135
+ declareTemplate(['query', 'mutation'])
136
+ const role = await getRepository(Role).save({ domain, name: '품질검사', privileges: [await getRepository(Privilege).findOneBy({ category: 'board', name: 'query' })] })
137
+
138
+ expect(await gapOf(role)).toBeNull()
139
+ expect(await gapOf(role, 'plant.qi')).toMatchObject({ templateId: 'plant.qi', missing: [{ category: 'board', axes: ['mutation'] }], extraCount: 0 })
140
+ })
141
+
142
+ it('refuses a template id that does not exist rather than answering null', async () => {
143
+ declareTemplate(['query'])
144
+ const role = await mutation.createRoleFromTemplate('plant.qi', undefined, context())
145
+
146
+ await expect(gapOf(role, 'plant.nobody')).rejects.toThrow(/no role template "plant.nobody"/)
147
+ })
148
+ })
149
+
150
+ describe('a template built from tasks (ADR-0139 decision 11)', () => {
151
+ beforeEach(async () => {
152
+ ;(process as any)['ROLE_TASKS'] = {}
153
+ ;(process as any)['ROLE_TEMPLATES'] = {}
154
+ await getRepository(Privilege).save({ name: 'query', category: 'order' })
155
+ ;(process as any).PRIVILEGES['order query'] = ['order', 'query']
156
+ registerRoleTask({ id: 'board.reading', grants: [{ category: 'board', axes: ['query'] }] })
157
+ registerRoleTask({ id: 'order.reading', grants: [{ category: 'order', axes: ['query'] }, { category: 'board', axes: ['query'] }] })
158
+ })
159
+
160
+ it('★ holds every privilege of every task once, plus its own grants', async () => {
161
+ registerRoleTemplate({ id: 'plant.reader', name: 'READER', tasks: ['board.reading', 'order.reading'], grants: [{ category: 'board', axes: ['mutation'] }] })
162
+ const role = await mutation.createRoleFromTemplate('plant.reader', undefined, context())
163
+
164
+ const held = (await getRepository(Role).findOne({ where: { id: role.id }, relations: ['privileges'] })).privileges.map((p: any) => `${p.category}:${p.name}`).sort()
165
+ expect(held).toEqual(['board:mutation', 'board:query', 'order:query'])
123
166
  expect((await gapOf(role)).missingCount).toBe(0)
124
167
  })
168
+
169
+ it('★ is refused whole when a task is not registered - no role is made, no privilege added', async () => {
170
+ registerRoleTemplate({ id: 'plant.reader', name: 'READER', tasks: ['board.reading', 'board.writting'] })
171
+
172
+ await expect(mutation.createRoleFromTemplate('plant.reader', undefined, context())).rejects.toThrow(/task "board.writting" is not registered/)
173
+ expect(await getRepository(Role).findOneBy({ name: 'READER' })).toBeNull()
174
+ })
175
+
176
+ it('★ is refused whole when a task names a privilege this installation does not declare', async () => {
177
+ registerRoleTask({ id: 'board.exporting', grants: [{ category: 'board-export', axes: ['query'] }] })
178
+ registerRoleTemplate({ id: 'plant.reader', name: 'READER', tasks: ['board.reading', 'board.exporting'] })
179
+ const standing = await getRepository(Role).save({ domain, name: 'READER', privileges: [] })
180
+
181
+ await expect(mutation.createRoleFromTemplate('plant.reader', undefined, context())).rejects.toThrow(/board-export:query/)
182
+ expect((await getRepository(Role).findOne({ where: { id: standing.id }, relations: ['privileges'] })).privileges).toEqual([])
183
+ })
184
+
185
+ it('★ the role screen sees a role by task: all, some, none, and what no task names', async () => {
186
+ const role = await getRepository(Role).save({
187
+ domain,
188
+ name: '판독',
189
+ privileges: [await getRepository(Privilege).findOneBy({ category: 'board', name: 'query' }), await getRepository(Privilege).findOneBy({ category: 'board', name: 'mutation' })]
190
+ })
191
+
192
+ const coverage = await coverageOf(role)
193
+ expect(coverage.tasks.map((task: any) => [task.taskId, task.status])).toEqual([
194
+ ['board.reading', 'all'],
195
+ ['order.reading', 'some']
196
+ ])
197
+ expect(coverage.tasks[1].missing).toEqual([{ category: 'order', axes: ['query'] }])
198
+ expect(coverage.unclassified).toEqual([{ category: 'board', axes: ['mutation'] }])
199
+ })
125
200
  })
@@ -251,5 +251,18 @@
251
251
  "access-request.notify.denied.body": "{privilege}: {reason}",
252
252
  "error.access-request-needs-reason": "write why you need it.",
253
253
  "error.access-request-same-reason-as-denied": "this was denied lately for the same reason ({reason}). write what has changed.",
254
- "error.role-belongs-to-parent": "{roles}: a role of the parent domain. only someone who decides roles there can give or take it."
254
+ "error.role-belongs-to-parent": "{roles}: a role of the parent domain. only someone who decides roles there can give or take it.",
255
+ "task.auth-base.users-and-roles.name": "Keeping accounts and roles",
256
+ "task.auth-base.users-and-roles.description": "Reads and changes users, roles and who holds them, invitations, access requests, applications, devices and sign-in providers. Whoever holds this can give any role, to themselves as well.",
257
+ "task.auth-base.privilege-reading.name": "Reading the privilege list",
258
+ "task.auth-base.privilege-reading.description": "Reads the privileges roles are made of.",
259
+ "task.auth-base.security.name": "Keeping security settings",
260
+ "task.auth-base.security.description": "Reads and changes the domain's protected addresses and sign-in rules.",
261
+ "task.auth-base.env-vars.name": "Keeping environment variables",
262
+ "task.auth-base.env-vars.description": "Reads and changes the domain's environment variables.",
263
+ "privilege.category.audit": "audit log",
264
+ "privilege.allows.audit.query": "see everyone’s sign-ins, privilege use and refusals, and role changes",
265
+ "task.auth-base.audit-reading.name": "Reading the audit log",
266
+ "task.auth-base.audit-reading.description": "Reads everyone's sign-ins, privilege use and refusals, and role changes in the domain.",
267
+ "error.access-request-task-mismatch": "the work you picked does not include the refused privilege."
255
268
  }
@@ -251,5 +251,18 @@
251
251
  "access-request.notify.denied.body": "{privilege}: {reason}",
252
252
  "error.access-request-needs-reason": "indique por qué lo necesita.",
253
253
  "error.access-request-same-reason-as-denied": "se rechazó hace poco por el mismo motivo ({reason}). indique qué ha cambiado.",
254
- "error.role-belongs-to-parent": "{roles}: un rol del dominio principal. solo quien decide los roles allí puede concederlo o quitarlo."
254
+ "error.role-belongs-to-parent": "{roles}: un rol del dominio principal. solo quien decide los roles allí puede concederlo o quitarlo.",
255
+ "task.auth-base.users-and-roles.name": "Mantener cuentas y roles",
256
+ "task.auth-base.users-and-roles.description": "Lee y modifica usuarios, roles y quién los tiene, invitaciones, solicitudes de acceso, aplicaciones, dispositivos y proveedores de acceso. Quien lo tiene puede dar cualquier rol, también a sí mismo.",
257
+ "task.auth-base.privilege-reading.name": "Leer la lista de privilegios",
258
+ "task.auth-base.privilege-reading.description": "Lee los privilegios de los que se componen los roles.",
259
+ "task.auth-base.security.name": "Mantener la seguridad",
260
+ "task.auth-base.security.description": "Lee y modifica las direcciones protegidas y las reglas de acceso del dominio.",
261
+ "task.auth-base.env-vars.name": "Mantener variables de entorno",
262
+ "task.auth-base.env-vars.description": "Lee y modifica las variables de entorno del dominio.",
263
+ "privilege.category.audit": "registro de auditoría",
264
+ "privilege.allows.audit.query": "ver los inicios de sesión, el uso y rechazo de privilegios y los cambios de rol de todos",
265
+ "task.auth-base.audit-reading.name": "Consultar el registro de auditoría",
266
+ "task.auth-base.audit-reading.description": "Lee los inicios de sesión, el uso y rechazo de privilegios y los cambios de rol de todos en el dominio.",
267
+ "error.access-request-task-mismatch": "la tarea elegida no incluye el privilegio rechazado."
255
268
  }
@@ -251,5 +251,18 @@
251
251
  "access-request.notify.denied.body": "{privilege}: {reason}",
252
252
  "error.access-request-needs-reason": "必要な理由を書いてください。",
253
253
  "error.access-request-same-reason-as-denied": "同じ理由で最近拒否されました({reason})。何が変わったかを書いてください。",
254
- "error.role-belongs-to-parent": "{roles}: 上位$t(term.domain)のロールです。その$t(term.domain)で権限を決める人だけが付与・解除できます。"
254
+ "error.role-belongs-to-parent": "{roles}: 上位$t(term.domain)のロールです。その$t(term.domain)で権限を決める人だけが付与・解除できます。",
255
+ "task.auth-base.users-and-roles.name": "アカウント・ロールの管理",
256
+ "task.auth-base.users-and-roles.description": "ユーザー・ロールとその付与・招待・アクセス申請・アプリ・機器・ログイン提供者を読み、変更する。自分にもどのロールでも付与できる。",
257
+ "task.auth-base.privilege-reading.name": "権限一覧の閲覧",
258
+ "task.auth-base.privilege-reading.description": "ロールを構成する権限の一覧を読む。",
259
+ "task.auth-base.security.name": "セキュリティ設定",
260
+ "task.auth-base.security.description": "ドメインの保護アドレスとログイン規則を読み、変更する。",
261
+ "task.auth-base.env-vars.name": "環境変数",
262
+ "task.auth-base.env-vars.description": "ドメインの環境変数を読み、変更する。",
263
+ "privilege.category.audit": "監査ログ",
264
+ "privilege.allows.audit.query": "全ユーザーのログイン、権限の使用と拒否、ロール変更の記録を見ることができます。",
265
+ "task.auth-base.audit-reading.name": "監査ログの閲覧",
266
+ "task.auth-base.audit-reading.description": "全ユーザーのログイン、権限の使用と拒否、ロール変更の記録を読む。",
267
+ "error.access-request-task-mismatch": "選んだ業務に拒否された権限が含まれていません。"
255
268
  }
@@ -251,5 +251,18 @@
251
251
  "access-request.notify.denied.body": "{privilege}: {reason}",
252
252
  "error.access-request-needs-reason": "필요한 사유를 입력해 주세요.",
253
253
  "error.access-request-same-reason-as-denied": "동일한 사유로 최근에 거부된 요청입니다({reason}). 다시 요청하려면 변경된 사항을 입력해 주세요.",
254
- "error.role-belongs-to-parent": "{roles}: 상위 $t(term.domain)의 역할입니다. 그 $t(term.domain)에서 권한을 정하는 사람만 주거나 뺄 수 있습니다."
254
+ "error.role-belongs-to-parent": "{roles}: 상위 $t(term.domain)의 역할입니다. 그 $t(term.domain)에서 권한을 정하는 사람만 주거나 뺄 수 있습니다.",
255
+ "task.auth-base.users-and-roles.name": "계정·역할 관리",
256
+ "task.auth-base.users-and-roles.description": "사용자·역할과 역할 부여·초대·접근 요청·앱·기기·로그인 제공자를 읽고 바꾼다. 자기에게도 어떤 역할이든 줄 수 있다.",
257
+ "task.auth-base.privilege-reading.name": "권한 목록 열람",
258
+ "task.auth-base.privilege-reading.description": "역할을 이루는 권한 목록을 읽는다.",
259
+ "task.auth-base.security.name": "보안 설정",
260
+ "task.auth-base.security.description": "도메인의 보호 주소와 로그인 규칙을 읽고 바꾼다.",
261
+ "task.auth-base.env-vars.name": "환경 변수",
262
+ "task.auth-base.env-vars.description": "도메인의 환경 변수를 읽고 바꾼다.",
263
+ "privilege.category.audit": "감사 기록",
264
+ "privilege.allows.audit.query": "모든 사용자의 로그인, 권한 사용과 거절, 역할 변경 기록을 볼 수 있습니다.",
265
+ "task.auth-base.audit-reading.name": "감사 기록 열람",
266
+ "task.auth-base.audit-reading.description": "모든 사용자의 로그인, 권한 사용과 거절, 역할 변경 기록을 읽는다.",
267
+ "error.access-request-task-mismatch": "고른 업무에 거절된 권한이 들어 있지 않습니다."
255
268
  }
@@ -251,5 +251,18 @@
251
251
  "access-request.notify.denied.body": "{privilege}: {reason}",
252
252
  "error.access-request-needs-reason": "tulis sebab anda memerlukannya.",
253
253
  "error.access-request-same-reason-as-denied": "ini ditolak baru-baru ini atas sebab yang sama ({reason}). tulis apa yang telah berubah.",
254
- "error.role-belongs-to-parent": "{roles}: peranan $t(term.domain) induk. hanya mereka yang memutuskan peranan di sana boleh memberi atau menariknya."
254
+ "error.role-belongs-to-parent": "{roles}: peranan $t(term.domain) induk. hanya mereka yang memutuskan peranan di sana boleh memberi atau menariknya.",
255
+ "task.auth-base.users-and-roles.name": "Menyelenggara akaun dan peranan",
256
+ "task.auth-base.users-and-roles.description": "Membaca dan mengubah pengguna, peranan dan pemegangnya, jemputan, permintaan akses, aplikasi, peranti dan penyedia log masuk. Pemegangnya boleh memberi apa-apa peranan, termasuk kepada diri sendiri.",
257
+ "task.auth-base.privilege-reading.name": "Membaca senarai keistimewaan",
258
+ "task.auth-base.privilege-reading.description": "Membaca keistimewaan yang membentuk peranan.",
259
+ "task.auth-base.security.name": "Menyelenggara tetapan keselamatan",
260
+ "task.auth-base.security.description": "Membaca dan mengubah alamat dilindungi dan peraturan log masuk domain.",
261
+ "task.auth-base.env-vars.name": "Menyelenggara pemboleh ubah persekitaran",
262
+ "task.auth-base.env-vars.description": "Membaca dan mengubah pemboleh ubah persekitaran domain.",
263
+ "privilege.category.audit": "log audit",
264
+ "privilege.allows.audit.query": "boleh melihat log masuk, penggunaan dan penolakan keistimewaan, serta perubahan peranan semua orang.",
265
+ "task.auth-base.audit-reading.name": "Membaca log audit",
266
+ "task.auth-base.audit-reading.description": "Membaca log masuk, penggunaan dan penolakan keistimewaan, serta perubahan peranan semua orang dalam domain.",
267
+ "error.access-request-task-mismatch": "tugas yang dipilih tidak termasuk keistimewaan yang ditolak."
255
268
  }
@@ -251,5 +251,18 @@
251
251
  "access-request.notify.denied.body": "{privilege}: {reason}",
252
252
  "error.access-request-needs-reason": "请填写需要的理由。",
253
253
  "error.access-request-same-reason-as-denied": "最近以相同理由被拒绝({reason})。请写明有何变化。",
254
- "error.role-belongs-to-parent": "{roles}: 上级$t(term.domain)的角色。只有在该$t(term.domain)负责权限的人才能授予或移除。"
254
+ "error.role-belongs-to-parent": "{roles}: 上级$t(term.domain)的角色。只有在该$t(term.domain)负责权限的人才能授予或移除。",
255
+ "task.auth-base.users-and-roles.name": "管理账户与角色",
256
+ "task.auth-base.users-and-roles.description": "读取并修改用户、角色及其授予、邀请、访问申请、应用、设备和登录提供方。持有者可以给任何人(包括自己)任何角色。",
257
+ "task.auth-base.privilege-reading.name": "查看权限列表",
258
+ "task.auth-base.privilege-reading.description": "读取构成角色的权限列表。",
259
+ "task.auth-base.security.name": "安全设置",
260
+ "task.auth-base.security.description": "读取并修改域的受保护地址和登录规则。",
261
+ "task.auth-base.env-vars.name": "环境变量",
262
+ "task.auth-base.env-vars.description": "读取并修改域的环境变量。",
263
+ "privilege.category.audit": "审计日志",
264
+ "privilege.allows.audit.query": "可以查看所有用户的登录、权限使用与拒绝以及角色变更记录。",
265
+ "task.auth-base.audit-reading.name": "查看审计日志",
266
+ "task.auth-base.audit-reading.description": "读取所有用户的登录、权限使用与拒绝以及角色变更记录。",
267
+ "error.access-request-task-mismatch": "所选业务不包含被拒绝的权限。"
255
268
  }
@@ -1,4 +0,0 @@
1
- import { LoginHistory } from './login-history.js';
2
- import { LoginHistoryQuery } from './login-history-query.js';
3
- export declare const entities: (typeof LoginHistory)[];
4
- export declare const resolvers: (typeof LoginHistoryQuery)[];
@@ -1,8 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolvers = exports.entities = void 0;
4
- const login_history_js_1 = require("./login-history.js");
5
- const login_history_query_js_1 = require("./login-history-query.js");
6
- exports.entities = [login_history_js_1.LoginHistory];
7
- exports.resolvers = [login_history_query_js_1.LoginHistoryQuery];
8
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/login-history/index.ts"],"names":[],"mappings":";;;AAAA,yDAAiD;AACjD,qEAA4D;AAE/C,QAAA,QAAQ,GAAG,CAAC,+BAAY,CAAC,CAAA;AACzB,QAAA,SAAS,GAAG,CAAC,0CAAiB,CAAC,CAAA","sourcesContent":["import { LoginHistory } from './login-history.js'\nimport { LoginHistoryQuery } from './login-history-query.js'\n\nexport const entities = [LoginHistory]\nexport const resolvers = [LoginHistoryQuery]\n"]}
@@ -1,10 +0,0 @@
1
- import { Domain, ListParam } from '@things-factory/shell';
2
- import { User } from '../user/user.js';
3
- import { LoginHistory } from './login-history.js';
4
- import { LoginHistoryList } from './login-history-type.js';
5
- export declare class LoginHistoryQuery {
6
- loginHistories(params: ListParam, context: ResolverContext): Promise<LoginHistoryList>;
7
- myLoginHistories(limit: number, context: ResolverContext): Promise<LoginHistory[]>;
8
- accessDomain(loginHistory: LoginHistory): Promise<Domain>;
9
- accessUser(loginHistory: LoginHistory): Promise<User>;
10
- }
@@ -1,80 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LoginHistoryQuery = void 0;
4
- const tslib_1 = require("tslib");
5
- const type_graphql_1 = require("type-graphql");
6
- const shell_1 = require("@things-factory/shell");
7
- const user_js_1 = require("../user/user.js");
8
- const login_history_js_1 = require("./login-history.js");
9
- const login_history_type_js_1 = require("./login-history-type.js");
10
- let LoginHistoryQuery = class LoginHistoryQuery {
11
- /* Everybody's sign-ins. `myLoginHistories` below is the one that answers about the caller,
12
- and it exists precisely because this one should not be open. */
13
- async loginHistories(params, context) {
14
- const { domain } = context.state;
15
- /*
16
- * The row's domain is `accessDomain` - there is no `domain` column, so handing `domain` to the builder failed every
17
- * call (no such column). This domain's sign-ins only; `inherited` never widens it to the parent's.
18
- */
19
- const queryBuilder = (0, shell_1.getQueryBuilderFromListParams)({
20
- params: { ...params, inherited: shell_1.InheritedValueType.None },
21
- repository: (0, shell_1.getRepository)(login_history_js_1.LoginHistory),
22
- alias: 'loginHistory'
23
- }).andWhere('loginHistory.accessDomain = :accessDomainId', { accessDomainId: domain.id });
24
- const [items, total] = await queryBuilder.getManyAndCount();
25
- return { items, total };
26
- }
27
- async myLoginHistories(limit, context) {
28
- const user = context.state.user;
29
- if (!user) {
30
- throw new Error('No user specified');
31
- }
32
- return await (0, shell_1.getRepository)(login_history_js_1.LoginHistory).find({
33
- where: { accessUser: { id: user.id } },
34
- take: limit || 10,
35
- order: { accessedAt: 'DESC' }
36
- });
37
- }
38
- async accessDomain(loginHistory) {
39
- return await (0, shell_1.getRepository)(shell_1.Domain).findOneBy({ id: loginHistory.accessDomainId });
40
- }
41
- async accessUser(loginHistory) {
42
- return await (0, shell_1.getRepository)(user_js_1.User).findOneBy({ id: loginHistory.accessUserId });
43
- }
44
- };
45
- exports.LoginHistoryQuery = LoginHistoryQuery;
46
- tslib_1.__decorate([
47
- (0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "query", domainOwnerGranted: true, superUserGranted: true)'),
48
- (0, type_graphql_1.Query)(returns => login_history_type_js_1.LoginHistoryList, { description: 'To fetch multiple LoginHistories' }),
49
- tslib_1.__param(0, (0, type_graphql_1.Args)(type => shell_1.ListParam)),
50
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
51
- tslib_1.__metadata("design:type", Function),
52
- tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
53
- tslib_1.__metadata("design:returntype", Promise)
54
- ], LoginHistoryQuery.prototype, "loginHistories", null);
55
- tslib_1.__decorate([
56
- (0, type_graphql_1.Query)(returns => [login_history_js_1.LoginHistory]),
57
- tslib_1.__param(0, (0, type_graphql_1.Arg)('limit')),
58
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
59
- tslib_1.__metadata("design:type", Function),
60
- tslib_1.__metadata("design:paramtypes", [Number, Object]),
61
- tslib_1.__metadata("design:returntype", Promise)
62
- ], LoginHistoryQuery.prototype, "myLoginHistories", null);
63
- tslib_1.__decorate([
64
- (0, type_graphql_1.FieldResolver)(),
65
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
66
- tslib_1.__metadata("design:type", Function),
67
- tslib_1.__metadata("design:paramtypes", [login_history_js_1.LoginHistory]),
68
- tslib_1.__metadata("design:returntype", Promise)
69
- ], LoginHistoryQuery.prototype, "accessDomain", null);
70
- tslib_1.__decorate([
71
- (0, type_graphql_1.FieldResolver)(),
72
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
73
- tslib_1.__metadata("design:type", Function),
74
- tslib_1.__metadata("design:paramtypes", [login_history_js_1.LoginHistory]),
75
- tslib_1.__metadata("design:returntype", Promise)
76
- ], LoginHistoryQuery.prototype, "accessUser", null);
77
- exports.LoginHistoryQuery = LoginHistoryQuery = tslib_1.__decorate([
78
- (0, type_graphql_1.Resolver)(login_history_js_1.LoginHistory)
79
- ], LoginHistoryQuery);
80
- //# sourceMappingURL=login-history-query.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"login-history-query.js","sourceRoot":"","sources":["../../../server/service/login-history/login-history-query.ts"],"names":[],"mappings":";;;;AAAA,+CAA8F;AAE9F,iDAA2H;AAE3H,6CAAsC;AACtC,yDAAiD;AACjD,mEAA0D;AAGnD,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAC5B;sEACkE;IAG5D,AAAN,KAAK,CAAC,cAAc,CACO,MAAiB,EACnC,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC;;;WAGG;QACH,MAAM,YAAY,GAAG,IAAA,qCAA6B,EAAC;YACjD,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,0BAAkB,CAAC,IAAI,EAAE;YACzD,UAAU,EAAE,IAAA,qBAAa,EAAC,+BAAY,CAAC;YACvC,KAAK,EAAE,cAAc;SACtB,CAAC,CAAC,QAAQ,CAAC,6CAA6C,EAAE,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAA;QAEzF,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,YAAY,CAAC,eAAe,EAAE,CAAA;QAC3D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IACzB,CAAC;IAGK,AAAN,KAAK,CAAC,gBAAgB,CAAe,KAAa,EAAS,OAAwB;QACjF,MAAM,IAAI,GAAS,OAAO,CAAC,KAAK,CAAC,IAAI,CAAA;QACrC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAA;QACtC,CAAC;QAED,OAAO,MAAM,IAAA,qBAAa,EAAC,+BAAY,CAAC,CAAC,IAAI,CAAC;YAC5C,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE;YACtC,IAAI,EAAE,KAAK,IAAI,EAAE;YACjB,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;SAC9B,CAAC,CAAA;IACJ,CAAC;IAGK,AAAN,KAAK,CAAC,YAAY,CAAS,YAA0B;QACnD,OAAO,MAAM,IAAA,qBAAa,EAAC,cAAM,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,YAAY,CAAC,cAAc,EAAE,CAAC,CAAA;IACnF,CAAC;IAGK,AAAN,KAAK,CAAC,UAAU,CAAS,YAA0B;QACjD,OAAO,MAAM,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,YAAY,CAAC,YAAY,EAAE,CAAC,CAAA;IAC/E,CAAC;CACF,CAAA;AAhDY,8CAAiB;AAKtB;IAFL,IAAA,wBAAS,EAAC,oGAAoG,CAAC;IAC/G,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,wCAAgB,EAAE,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;IAErF,mBAAA,IAAA,mBAAI,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,CAAC,CAAA;IACvB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAD2B,iBAAS;;uDAiB3C;AAGK;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,+BAAY,CAAC,CAAC;IACT,mBAAA,IAAA,kBAAG,EAAC,OAAO,CAAC,CAAA;IAAiB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;yDAWzD;AAGK;IADL,IAAA,4BAAa,GAAE;IACI,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAe,+BAAY;;qDAEpD;AAGK;IADL,IAAA,4BAAa,GAAE;IACE,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAe,+BAAY;;mDAElD;4BA/CU,iBAAiB;IAD7B,IAAA,uBAAQ,EAAC,+BAAY,CAAC;GACV,iBAAiB,CAgD7B","sourcesContent":["import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { Domain, getQueryBuilderFromListParams, InheritedValueType, getRepository, ListParam } from '@things-factory/shell'\n\nimport { User } from '../user/user.js'\nimport { LoginHistory } from './login-history.js'\nimport { LoginHistoryList } from './login-history-type.js'\n\n@Resolver(LoginHistory)\nexport class LoginHistoryQuery {\n /* Everybody's sign-ins. `myLoginHistories` below is the one that answers about the caller,\n and it exists precisely because this one should not be open. */\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => LoginHistoryList, { description: 'To fetch multiple LoginHistories' })\n async loginHistories(\n @Args(type => ListParam) params: ListParam,\n @Ctx() context: ResolverContext\n ): Promise<LoginHistoryList> {\n const { domain } = context.state\n\n /*\n * The row's domain is `accessDomain` - there is no `domain` column, so handing `domain` to the builder failed every\n * call (no such column). This domain's sign-ins only; `inherited` never widens it to the parent's.\n */\n const queryBuilder = getQueryBuilderFromListParams({\n params: { ...params, inherited: InheritedValueType.None },\n repository: getRepository(LoginHistory),\n alias: 'loginHistory'\n }).andWhere('loginHistory.accessDomain = :accessDomainId', { accessDomainId: domain.id })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n return { items, total }\n }\n\n @Query(returns => [LoginHistory])\n async myLoginHistories(@Arg('limit') limit: number, @Ctx() context: ResolverContext): Promise<LoginHistory[]> {\n const user: User = context.state.user\n if (!user) {\n throw new Error('No user specified')\n }\n\n return await getRepository(LoginHistory).find({\n where: { accessUser: { id: user.id } },\n take: limit || 10,\n order: { accessedAt: 'DESC' }\n })\n }\n\n @FieldResolver()\n async accessDomain(@Root() loginHistory: LoginHistory) {\n return await getRepository(Domain).findOneBy({ id: loginHistory.accessDomainId })\n }\n\n @FieldResolver()\n async accessUser(@Root() loginHistory: LoginHistory) {\n return await getRepository(User).findOneBy({ id: loginHistory.accessUserId })\n }\n}\n"]}
@@ -1,5 +0,0 @@
1
- import { LoginHistory } from './login-history.js';
2
- export declare class LoginHistoryList {
3
- items: LoginHistory[];
4
- total: number;
5
- }
@@ -1,21 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LoginHistoryList = void 0;
4
- const tslib_1 = require("tslib");
5
- const type_graphql_1 = require("type-graphql");
6
- const login_history_js_1 = require("./login-history.js");
7
- let LoginHistoryList = class LoginHistoryList {
8
- };
9
- exports.LoginHistoryList = LoginHistoryList;
10
- tslib_1.__decorate([
11
- (0, type_graphql_1.Field)(type => [login_history_js_1.LoginHistory], { description: 'The list of login history items.' }),
12
- tslib_1.__metadata("design:type", Array)
13
- ], LoginHistoryList.prototype, "items", void 0);
14
- tslib_1.__decorate([
15
- (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'The total number of login history records.' }),
16
- tslib_1.__metadata("design:type", Number)
17
- ], LoginHistoryList.prototype, "total", void 0);
18
- exports.LoginHistoryList = LoginHistoryList = tslib_1.__decorate([
19
- (0, type_graphql_1.ObjectType)({ description: 'A paginated list of login history records.' })
20
- ], LoginHistoryList);
21
- //# sourceMappingURL=login-history-type.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"login-history-type.js","sourceRoot":"","sources":["../../../server/service/login-history/login-history-type.ts"],"names":[],"mappings":";;;;AAAA,+CAAoE;AAEpE,yDAAiD;AAG1C,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;CAM5B,CAAA;AANY,4CAAgB;AAE3B;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,+BAAY,CAAC,EAAE,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;;+CAC9D;AAGrB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;;+CACrE;2BALF,gBAAgB;IAD5B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;GAC7D,gBAAgB,CAM5B","sourcesContent":["import { Field, ID, InputType, Int, ObjectType } from 'type-graphql'\n\nimport { LoginHistory } from './login-history.js'\n\n@ObjectType({ description: 'A paginated list of login history records.' })\nexport class LoginHistoryList {\n @Field(type => [LoginHistory], { description: 'The list of login history items.' })\n items: LoginHistory[]\n\n @Field(type => Int, { description: 'The total number of login history records.' })\n total: number\n}\n"]}
@@ -1,12 +0,0 @@
1
- import { Domain } from '@things-factory/shell';
2
- import { User } from '../user/user.js';
3
- export declare class LoginHistory {
4
- readonly id: string;
5
- accessDomain?: Domain;
6
- accessDomainId: string;
7
- accessUser: User;
8
- accessUserId: string;
9
- accessorIp: string;
10
- accessedAt: Date;
11
- static stamp(accessDomain: Partial<Domain>, accessUser: User, accessorIp: string): Promise<void>;
12
- }
@@ -1,58 +0,0 @@
1
- "use strict";
2
- var LoginHistory_1;
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.LoginHistory = void 0;
5
- const tslib_1 = require("tslib");
6
- const type_graphql_1 = require("type-graphql");
7
- const typeorm_1 = require("typeorm");
8
- const shell_1 = require("@things-factory/shell");
9
- const user_js_1 = require("../user/user.js");
10
- let LoginHistory = LoginHistory_1 = class LoginHistory {
11
- static async stamp(accessDomain, accessUser, accessorIp) {
12
- await (0, shell_1.getRepository)(LoginHistory_1).save({
13
- accessDomain,
14
- accessUser,
15
- accessorIp
16
- });
17
- }
18
- };
19
- exports.LoginHistory = LoginHistory;
20
- tslib_1.__decorate([
21
- (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
22
- (0, type_graphql_1.Field)(type => type_graphql_1.ID, { description: 'Unique identifier for the login history record.' }),
23
- tslib_1.__metadata("design:type", String)
24
- ], LoginHistory.prototype, "id", void 0);
25
- tslib_1.__decorate([
26
- (0, typeorm_1.ManyToOne)(type => shell_1.Domain),
27
- (0, type_graphql_1.Field)(type => shell_1.Domain, { description: 'The domain the user logged into.' }),
28
- tslib_1.__metadata("design:type", shell_1.Domain)
29
- ], LoginHistory.prototype, "accessDomain", void 0);
30
- tslib_1.__decorate([
31
- (0, typeorm_1.RelationId)((loginHistory) => loginHistory.accessDomain),
32
- tslib_1.__metadata("design:type", String)
33
- ], LoginHistory.prototype, "accessDomainId", void 0);
34
- tslib_1.__decorate([
35
- (0, typeorm_1.ManyToOne)(type => user_js_1.User),
36
- (0, type_graphql_1.Field)(type => user_js_1.User, { description: 'The user who logged in.' }),
37
- tslib_1.__metadata("design:type", user_js_1.User)
38
- ], LoginHistory.prototype, "accessUser", void 0);
39
- tslib_1.__decorate([
40
- (0, typeorm_1.RelationId)((loginHistory) => loginHistory.accessUser),
41
- tslib_1.__metadata("design:type", String)
42
- ], LoginHistory.prototype, "accessUserId", void 0);
43
- tslib_1.__decorate([
44
- (0, typeorm_1.Column)({ nullable: true }),
45
- (0, type_graphql_1.Field)({ nullable: true, description: 'The IP address from which the login attempt was made.' }),
46
- tslib_1.__metadata("design:type", String)
47
- ], LoginHistory.prototype, "accessorIp", void 0);
48
- tslib_1.__decorate([
49
- (0, typeorm_1.CreateDateColumn)(),
50
- (0, type_graphql_1.Field)({ nullable: true, description: 'The timestamp of the login attempt.' }),
51
- tslib_1.__metadata("design:type", Date)
52
- ], LoginHistory.prototype, "accessedAt", void 0);
53
- exports.LoginHistory = LoginHistory = LoginHistory_1 = tslib_1.__decorate([
54
- (0, typeorm_1.Entity)(),
55
- (0, typeorm_1.Index)('ix_login_history_0', (loginHistory) => [loginHistory.accessDomain, loginHistory.accessUser]),
56
- (0, type_graphql_1.ObjectType)({ description: 'Records user login attempts.' })
57
- ], LoginHistory);
58
- //# sourceMappingURL=login-history.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"login-history.js","sourceRoot":"","sources":["../../../server/service/login-history/login-history.ts"],"names":[],"mappings":";;;;;AAAA,+CAAoD;AACpD,qCAAgH;AAEhH,iDAA6D;AAE7D,6CAAsC;AAK/B,IAAM,YAAY,oBAAlB,MAAM,YAAY;IA2BvB,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,YAA6B,EAAE,UAAgB,EAAE,UAAkB;QACpF,MAAM,IAAA,qBAAa,EAAC,cAAY,CAAC,CAAC,IAAI,CAAC;YACrC,YAAY;YACZ,UAAU;YACV,UAAU;SACX,CAAC,CAAA;IACJ,CAAC;CACF,CAAA;AAlCY,oCAAY;AAGd;IAFR,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,WAAW,EAAE,iDAAiD,EAAE,CAAC;;wCACnE;AAInB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IACzB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;sCAC5D,cAAM;kDAAA;AAGrB;IADC,IAAA,oBAAU,EAAC,CAAC,YAA0B,EAAE,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC;;oDAChD;AAItB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,CAAC;IACvB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC;sCACpD,cAAI;gDAAA;AAGhB;IADC,IAAA,oBAAU,EAAC,CAAC,YAA0B,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC;;kDAChD;AAIpB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;;gDAC9E;AAIlB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC;sCAClE,IAAI;gDAAA;uBAzBL,YAAY;IAHxB,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,EAAC,oBAAoB,EAAE,CAAC,YAA0B,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,YAAY,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;IACjH,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;GAC/C,YAAY,CAkCxB","sourcesContent":["import { Field, ID, ObjectType } from 'type-graphql'\nimport { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId } from 'typeorm'\n\nimport { Domain, getRepository } from '@things-factory/shell'\n\nimport { User } from '../user/user.js'\n\n@Entity()\n@Index('ix_login_history_0', (loginHistory: LoginHistory) => [loginHistory.accessDomain, loginHistory.accessUser])\n@ObjectType({ description: 'Records user login attempts.' })\nexport class LoginHistory {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the login history record.' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { description: 'The domain the user logged into.' })\n accessDomain?: Domain\n\n @RelationId((loginHistory: LoginHistory) => loginHistory.accessDomain)\n accessDomainId: string\n\n @ManyToOne(type => User)\n @Field(type => User, { description: 'The user who logged in.' })\n accessUser: User\n\n @RelationId((loginHistory: LoginHistory) => loginHistory.accessUser)\n accessUserId: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The IP address from which the login attempt was made.' })\n accessorIp: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'The timestamp of the login attempt.' })\n accessedAt: Date\n\n static async stamp(accessDomain: Partial<Domain>, accessUser: User, accessorIp: string): Promise<void> {\n await getRepository(LoginHistory).save({\n accessDomain,\n accessUser,\n accessorIp\n })\n }\n}\n"]}