@things-factory/auth-base 10.1.278 → 10.1.280

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 (284) hide show
  1. package/package.json +24 -7
  2. package/CHANGELOG.md +0 -55
  3. package/dist-client/actions/auth.js.map +0 -1
  4. package/dist-client/apply-domain-theme.js.map +0 -1
  5. package/dist-client/auth.js.map +0 -1
  6. package/dist-client/bootstrap.js.map +0 -1
  7. package/dist-client/directive/privileged.js.map +0 -1
  8. package/dist-client/domain-required.js.map +0 -1
  9. package/dist-client/index.js.map +0 -1
  10. package/dist-client/profiled.js.map +0 -1
  11. package/dist-client/reducers/auth.js.map +0 -1
  12. package/dist-client/tsconfig.tsbuildinfo +0 -1
  13. package/dist-client/verify-webauthn.js.map +0 -1
  14. package/dist-server/ai-entry/access-assistant.js.map +0 -1
  15. package/dist-server/ai-entry/access-entry-targets.js.map +0 -1
  16. package/dist-server/ai-entry/entry-target-shape.js.map +0 -1
  17. package/dist-server/constants/error-code.js.map +0 -1
  18. package/dist-server/constants/error-message.js.map +0 -1
  19. package/dist-server/constants/max-age.js.map +0 -1
  20. package/dist-server/controllers/access-request.js.map +0 -1
  21. package/dist-server/controllers/account-mail.js.map +0 -1
  22. package/dist-server/controllers/auth.js.map +0 -1
  23. package/dist-server/controllers/change-pwd.js.map +0 -1
  24. package/dist-server/controllers/confirm-identity.js.map +0 -1
  25. package/dist-server/controllers/delete-user.js.map +0 -1
  26. package/dist-server/controllers/passkey.js.map +0 -1
  27. package/dist-server/controllers/password-setup.js.map +0 -1
  28. package/dist-server/controllers/profile.js.map +0 -1
  29. package/dist-server/controllers/reset-password.js.map +0 -1
  30. package/dist-server/controllers/signin.js.map +0 -1
  31. package/dist-server/controllers/signup.js.map +0 -1
  32. package/dist-server/controllers/unlock-user.js.map +0 -1
  33. package/dist-server/controllers/utils/find-verification-token.js.map +0 -1
  34. package/dist-server/controllers/utils/make-verification-token.js.map +0 -1
  35. package/dist-server/controllers/utils/password-rule.js.map +0 -1
  36. package/dist-server/controllers/utils/save-verification-token.js.map +0 -1
  37. package/dist-server/controllers/verification.js.map +0 -1
  38. package/dist-server/errors/auth-error.js.map +0 -1
  39. package/dist-server/errors/index.js.map +0 -1
  40. package/dist-server/errors/user-domain-not-match-error.js.map +0 -1
  41. package/dist-server/index.js.map +0 -1
  42. package/dist-server/middlewares/audit-scope-middleware.js.map +0 -1
  43. package/dist-server/middlewares/authenticate-401-middleware.js.map +0 -1
  44. package/dist-server/middlewares/bypass-signin-middleware.js.map +0 -1
  45. package/dist-server/middlewares/domain-authenticate-middleware.js.map +0 -1
  46. package/dist-server/middlewares/graphql-authenticate-middleware.js.map +0 -1
  47. package/dist-server/middlewares/index.js.map +0 -1
  48. package/dist-server/middlewares/jwt-authenticate-middleware.js.map +0 -1
  49. package/dist-server/middlewares/signin-middleware.js.map +0 -1
  50. package/dist-server/middlewares/verify-recaptcha-middleware.js.map +0 -1
  51. package/dist-server/migrations/1548206416130-SeedUser.js.map +0 -1
  52. package/dist-server/migrations/1566805283882-SeedPrivilege.js.map +0 -1
  53. package/dist-server/migrations/1745000000000-MigrateDomainOwnersToTable.js.map +0 -1
  54. package/dist-server/migrations/index.js.map +0 -1
  55. package/dist-server/role-tasks.js.map +0 -1
  56. package/dist-server/router/auth-checkin-router.js.map +0 -1
  57. package/dist-server/router/auth-private-process-router.js.map +0 -1
  58. package/dist-server/router/auth-public-process-router.js.map +0 -1
  59. package/dist-server/router/auth-signin-router.js.map +0 -1
  60. package/dist-server/router/auth-signup-router.js.map +0 -1
  61. package/dist-server/router/index.js.map +0 -1
  62. package/dist-server/router/oauth2/index.js.map +0 -1
  63. package/dist-server/router/oauth2/oauth2-authorize-router.js.map +0 -1
  64. package/dist-server/router/oauth2/oauth2-router.js.map +0 -1
  65. package/dist-server/router/oauth2/oauth2-server.js.map +0 -1
  66. package/dist-server/router/oauth2/passport-oauth2-client-password.js.map +0 -1
  67. package/dist-server/router/oauth2/passport-refresh-token.js.map +0 -1
  68. package/dist-server/router/path-base-domain-router.js.map +0 -1
  69. package/dist-server/router/site-root-router.js.map +0 -1
  70. package/dist-server/router/webauthn-router.js.map +0 -1
  71. package/dist-server/routes.js.map +0 -1
  72. package/dist-server/service/access-request/access-request-mutation.js.map +0 -1
  73. package/dist-server/service/access-request/access-request-query.js.map +0 -1
  74. package/dist-server/service/access-request/access-request-types.js.map +0 -1
  75. package/dist-server/service/access-request/access-request.js.map +0 -1
  76. package/dist-server/service/access-request/index.js.map +0 -1
  77. package/dist-server/service/app-binding/app-binding-mutation.js.map +0 -1
  78. package/dist-server/service/app-binding/app-binding-query.js.map +0 -1
  79. package/dist-server/service/app-binding/app-binding-types.js.map +0 -1
  80. package/dist-server/service/app-binding/app-binding.js.map +0 -1
  81. package/dist-server/service/app-binding/index.js.map +0 -1
  82. package/dist-server/service/appliance/appliance-mutation.js.map +0 -1
  83. package/dist-server/service/appliance/appliance-query.js.map +0 -1
  84. package/dist-server/service/appliance/appliance-types.js.map +0 -1
  85. package/dist-server/service/appliance/appliance.js.map +0 -1
  86. package/dist-server/service/appliance/index.js.map +0 -1
  87. package/dist-server/service/application/application-mutation.js.map +0 -1
  88. package/dist-server/service/application/application-query.js.map +0 -1
  89. package/dist-server/service/application/application-types.js.map +0 -1
  90. package/dist-server/service/application/application.js.map +0 -1
  91. package/dist-server/service/application/index.js.map +0 -1
  92. package/dist-server/service/audit/audit-action.js.map +0 -1
  93. package/dist-server/service/audit/audit-kinds.js.map +0 -1
  94. package/dist-server/service/audit/audit-query.js.map +0 -1
  95. package/dist-server/service/audit/audit-recorder.js.map +0 -1
  96. package/dist-server/service/audit/audit-request.js.map +0 -1
  97. package/dist-server/service/audit/audit-retention.js.map +0 -1
  98. package/dist-server/service/audit/audit-subscriber.js.map +0 -1
  99. package/dist-server/service/audit/audit-target.js.map +0 -1
  100. package/dist-server/service/audit/audit-types.js.map +0 -1
  101. package/dist-server/service/audit/audit-work.js.map +0 -1
  102. package/dist-server/service/audit/index.js.map +0 -1
  103. package/dist-server/service/auth-provider/auth-provider-mutation.js.map +0 -1
  104. package/dist-server/service/auth-provider/auth-provider-parameter-spec.js.map +0 -1
  105. package/dist-server/service/auth-provider/auth-provider-query.js.map +0 -1
  106. package/dist-server/service/auth-provider/auth-provider-type.js.map +0 -1
  107. package/dist-server/service/auth-provider/auth-provider.js.map +0 -1
  108. package/dist-server/service/auth-provider/index.js.map +0 -1
  109. package/dist-server/service/domain-generator/domain-generator-mutation.js.map +0 -1
  110. package/dist-server/service/domain-generator/domain-generator-types.js.map +0 -1
  111. package/dist-server/service/domain-generator/index.js.map +0 -1
  112. package/dist-server/service/domain-link/domain-link-mutation.js.map +0 -1
  113. package/dist-server/service/domain-link/domain-link-query.js.map +0 -1
  114. package/dist-server/service/domain-link/domain-link-types.js.map +0 -1
  115. package/dist-server/service/domain-link/domain-link.js.map +0 -1
  116. package/dist-server/service/domain-link/index.js.map +0 -1
  117. package/dist-server/service/domain-owner/domain-owner-mutation.js.map +0 -1
  118. package/dist-server/service/domain-owner/domain-owner-query.js.map +0 -1
  119. package/dist-server/service/domain-owner/domain-owner.js.map +0 -1
  120. package/dist-server/service/domain-owner/index.js.map +0 -1
  121. package/dist-server/service/granted-role/granted-role-mutation.js.map +0 -1
  122. package/dist-server/service/granted-role/granted-role-query.js.map +0 -1
  123. package/dist-server/service/granted-role/granted-role.js.map +0 -1
  124. package/dist-server/service/granted-role/index.js.map +0 -1
  125. package/dist-server/service/identity-confirmation/identity-confirmation.js.map +0 -1
  126. package/dist-server/service/index.js.map +0 -1
  127. package/dist-server/service/open-fields.js.map +0 -1
  128. package/dist-server/service/partner/index.js.map +0 -1
  129. package/dist-server/service/partner/partner-mutation.js.map +0 -1
  130. package/dist-server/service/partner/partner-query.js.map +0 -1
  131. package/dist-server/service/partner/partner-types.js.map +0 -1
  132. package/dist-server/service/partner/partner.js.map +0 -1
  133. package/dist-server/service/password-history/index.js.map +0 -1
  134. package/dist-server/service/password-history/password-history.js.map +0 -1
  135. package/dist-server/service/privilege/index.js.map +0 -1
  136. package/dist-server/service/privilege/open-field-rule.js.map +0 -1
  137. package/dist-server/service/privilege/privilege-axis.js.map +0 -1
  138. package/dist-server/service/privilege/privilege-deprecation.js.map +0 -1
  139. package/dist-server/service/privilege/privilege-directive.js.map +0 -1
  140. package/dist-server/service/privilege/privilege-mutation.js.map +0 -1
  141. package/dist-server/service/privilege/privilege-query.js.map +0 -1
  142. package/dist-server/service/privilege/privilege-rejection.js.map +0 -1
  143. package/dist-server/service/privilege/privilege-sync.js.map +0 -1
  144. package/dist-server/service/privilege/privilege-types.js.map +0 -1
  145. package/dist-server/service/privilege/privilege.js.map +0 -1
  146. package/dist-server/service/role/index.js.map +0 -1
  147. package/dist-server/service/role/role-mutation.js.map +0 -1
  148. package/dist-server/service/role/role-privilege-query.js.map +0 -1
  149. package/dist-server/service/role/role-query.js.map +0 -1
  150. package/dist-server/service/role/role-types.js.map +0 -1
  151. package/dist-server/service/role/role.js.map +0 -1
  152. package/dist-server/service/role-template/index.js.map +0 -1
  153. package/dist-server/service/role-template/role-task.js.map +0 -1
  154. package/dist-server/service/role-template/role-template-gap-resolver.js.map +0 -1
  155. package/dist-server/service/role-template/role-template-mutation.js.map +0 -1
  156. package/dist-server/service/role-template/role-template-query.js.map +0 -1
  157. package/dist-server/service/role-template/role-template-types.js.map +0 -1
  158. package/dist-server/service/role-template/role-template.js.map +0 -1
  159. package/dist-server/service/user/domain-query.js.map +0 -1
  160. package/dist-server/service/user/index.js.map +0 -1
  161. package/dist-server/service/user/user-mutation.js.map +0 -1
  162. package/dist-server/service/user/user-query.js.map +0 -1
  163. package/dist-server/service/user/user-types.js.map +0 -1
  164. package/dist-server/service/user/user.js.map +0 -1
  165. package/dist-server/service/users-auth-providers/index.js.map +0 -1
  166. package/dist-server/service/users-auth-providers/users-auth-providers.js.map +0 -1
  167. package/dist-server/service/verification-token/index.js.map +0 -1
  168. package/dist-server/service/verification-token/verification-token.js.map +0 -1
  169. package/dist-server/service/web-auth-credential/index.js.map +0 -1
  170. package/dist-server/service/web-auth-credential/web-auth-credential-mutation.js.map +0 -1
  171. package/dist-server/service/web-auth-credential/web-auth-credential-query.js.map +0 -1
  172. package/dist-server/service/web-auth-credential/web-auth-credential.js.map +0 -1
  173. package/dist-server/tsconfig.tsbuildinfo +0 -1
  174. package/dist-server/types.js.map +0 -1
  175. package/dist-server/utils/accepts.js.map +0 -1
  176. package/dist-server/utils/access-request-review.js.map +0 -1
  177. package/dist-server/utils/access-token-cookie.js.map +0 -1
  178. package/dist-server/utils/account-lock-rule.js.map +0 -1
  179. package/dist-server/utils/assignable-roles.js.map +0 -1
  180. package/dist-server/utils/build-auth-url.js.map +0 -1
  181. package/dist-server/utils/can-user-checkin-domain.js.map +0 -1
  182. package/dist-server/utils/check-permission.js.map +0 -1
  183. package/dist-server/utils/check-user-belongs-domain.js.map +0 -1
  184. package/dist-server/utils/check-user-has-role.js.map +0 -1
  185. package/dist-server/utils/checkin-domain-facts.js.map +0 -1
  186. package/dist-server/utils/checkin-domain-rule.js.map +0 -1
  187. package/dist-server/utils/credential-at-rest.js.map +0 -1
  188. package/dist-server/utils/credential-features.js.map +0 -1
  189. package/dist-server/utils/credential-serial-rule.js.map +0 -1
  190. package/dist-server/utils/domain-ownership.js.map +0 -1
  191. package/dist-server/utils/encrypt-state.js.map +0 -1
  192. package/dist-server/utils/get-aes-256-key.js.map +0 -1
  193. package/dist-server/utils/get-domain-users.js.map +0 -1
  194. package/dist-server/utils/get-secret.js.map +0 -1
  195. package/dist-server/utils/get-user-domains.js.map +0 -1
  196. package/dist-server/utils/lock-duration.js.map +0 -1
  197. package/dist-server/utils/password-reset-rule.js.map +0 -1
  198. package/dist-server/utils/profile-user.js.map +0 -1
  199. package/dist-server/utils/refuse-role-name.js.map +0 -1
  200. package/dist-server/utils/role-name-standing.js.map +0 -1
  201. package/dist-server/utils/same-origin-redirect.js.map +0 -1
  202. package/dist-server/utils/verification-token-rule.js.map +0 -1
  203. package/spec/unit/checkin-domain.spec.ts +0 -307
  204. package/spec/unit/checkin-privilege.spec.ts +0 -205
  205. package/spec/unit/domain-owner-entity.spec.ts +0 -179
  206. package/spec/unit/domain-owner-granted.spec.ts +0 -215
  207. package/spec/unit/domain-owner-migration.spec.ts +0 -236
  208. package/spec/unit/domain-owner-mutation.spec.ts +0 -337
  209. package/spec/unit/domain-ownership.spec.ts +0 -188
  210. package/tests/access-assistant-db.test.ts +0 -155
  211. package/tests/access-entry-targets-db.test.ts +0 -176
  212. package/tests/access-request-db.test.ts +0 -394
  213. package/tests/access-request-pg.test.ts +0 -83
  214. package/tests/access-request-review.test.ts +0 -193
  215. package/tests/account-lock-db.test.ts +0 -216
  216. package/tests/account-lock-rule.test.ts +0 -141
  217. package/tests/account-mail.test.ts +0 -69
  218. package/tests/activate-page-case.test.ts +0 -53
  219. package/tests/add-user-gates.test.ts +0 -34
  220. package/tests/app-binding-db.test.ts +0 -210
  221. package/tests/app-binding-delete-db.test.ts +0 -188
  222. package/tests/appliance-credential-state-db.test.ts +0 -207
  223. package/tests/appliance-delete-db.test.ts +0 -119
  224. package/tests/appliance-revoke-db.test.ts +0 -294
  225. package/tests/appliance-schema.test.ts +0 -129
  226. package/tests/application-delete-db.test.ts +0 -190
  227. package/tests/application-token-payload-db.test.ts +0 -185
  228. package/tests/assignable-roles-db.test.ts +0 -219
  229. package/tests/audit-db.test.ts +0 -410
  230. package/tests/audit-work.test.ts +0 -58
  231. package/tests/auth-base-role-tasks.test.ts +0 -58
  232. package/tests/checkin-domain-rule.test.ts +0 -128
  233. package/tests/checkin-privilege-seam-db.test.ts +0 -158
  234. package/tests/compiled.ts +0 -41
  235. package/tests/confirm-identity-db.test.ts +0 -85
  236. package/tests/credential-at-rest-db.test.ts +0 -246
  237. package/tests/credential-features-off-db.test.ts +0 -189
  238. package/tests/credential-serial-rule.test.ts +0 -84
  239. package/tests/declare-privileges.test.ts +0 -29
  240. package/tests/domain-assignees-db.test.ts +0 -95
  241. package/tests/domain-inheritance-sentinel.test.ts +0 -93
  242. package/tests/domain-required.test.ts +0 -127
  243. package/tests/has-privilege-db.test.ts +0 -198
  244. package/tests/identity-confirmation.test.ts +0 -146
  245. package/tests/list-inheritance-db.test.ts +0 -127
  246. package/tests/lock-recovery.test.ts +0 -206
  247. package/tests/mail-request-answers-db.test.ts +0 -142
  248. package/tests/open-doors.test.ts +0 -121
  249. package/tests/open-fields-enforcement.test.ts +0 -215
  250. package/tests/open-fields-framework.test.ts +0 -212
  251. package/tests/open-redirect-db.test.ts +0 -143
  252. package/tests/passkey-authenticator.ts +0 -104
  253. package/tests/passkey-db.test.ts +0 -295
  254. package/tests/passkey-signin-route-db.test.ts +0 -139
  255. package/tests/password-hashing.test.ts +0 -131
  256. package/tests/password-persistence.test.ts +0 -147
  257. package/tests/password-reset-rule-db.test.ts +0 -159
  258. package/tests/password-setup-db.test.ts +0 -215
  259. package/tests/permission-gate.test.ts +0 -228
  260. package/tests/picture-reducer.test.ts +0 -25
  261. package/tests/privilege-axis.test.ts +0 -157
  262. package/tests/privilege-deprecation.test.ts +0 -65
  263. package/tests/privilege-directive.test.ts +0 -332
  264. package/tests/privilege-rejection.test.ts +0 -104
  265. package/tests/privilege-sync-db.test.ts +0 -58
  266. package/tests/profile-user.test.ts +0 -24
  267. package/tests/refusal-cause.test.ts +0 -80
  268. package/tests/refusals-have-korean.test.ts +0 -24
  269. package/tests/request-holds.test.ts +0 -53
  270. package/tests/role-inheritance-db.test.ts +0 -286
  271. package/tests/role-mutation-db.test.ts +0 -293
  272. package/tests/role-name-lookup-sentinel.test.ts +0 -79
  273. package/tests/role-name-standing.test.ts +0 -83
  274. package/tests/role-privileges-db.test.ts +0 -252
  275. package/tests/role-task.test.ts +0 -168
  276. package/tests/role-template-gap-db.test.ts +0 -268
  277. package/tests/role-template-seed-db.test.ts +0 -180
  278. package/tests/role-template.test.ts +0 -162
  279. package/tests/role-users-gate.test.ts +0 -22
  280. package/tests/signin-policy.test.ts +0 -200
  281. package/tests/signup-duplicate-db.test.ts +0 -68
  282. package/tests/token-issuance.test.ts +0 -244
  283. package/tests/user-roles-field-db.test.ts +0 -134
  284. package/tests/verification-token-db.test.ts +0 -285
@@ -1,410 +0,0 @@
1
- /**
2
- * The audit record (ADR-0141) - what it keeps, and what it never keeps.
3
- *
4
- * The doors run through the real `@privilege` wrapper on an SDL schema (the shape
5
- * `privilege-directive.test.ts` uses - type-graphql resolvers cannot be loaded under jest), inside a
6
- * real audit scope, against the sqlite harness. Grants run through the real subscribers.
7
- */
8
-
9
- import { graphql } from 'graphql'
10
- import { makeExecutableSchema } from '@graphql-tools/schema'
11
-
12
- import { authDataSource, closeAuthDatabase, Domain, getRepository, openAuthDatabase, resetAuthDatabase } from './db'
13
- import { loadCompiled } from './compiled'
14
-
15
- const { privilegeDirectiveTypeDefs, privilegeDirectiveResolver } = loadCompiled('service/privilege/privilege-directive')
16
- const { auditTargetDirectiveTypeDefs } = loadCompiled('service/audit/audit-target')
17
- const { runInAuditScope, recordAuditAction, flushAudit, auditHealth } = loadCompiled('service/audit/audit-recorder')
18
- const { AuditAction } = loadCompiled('service/audit/audit-action')
19
- const { AuditRequest } = loadCompiled('service/audit/audit-request')
20
- const { AuditQuery } = loadCompiled('service/audit/audit-query')
21
- const { purgeExpiredAudit } = loadCompiled('service/audit/audit-retention')
22
- const { User } = loadCompiled('service/user/user')
23
- const { Role } = loadCompiled('service/role/role')
24
- const { Privilege } = loadCompiled('service/privilege/privilege')
25
- const { signin } = loadCompiled('controllers/signin')
26
-
27
- const PASSWORD = 'Passw0rd!'
28
-
29
- let domain: any
30
- let other: any
31
- let kim: any
32
- let lee: any
33
-
34
- const typeDefs = `
35
- type Item { id: String }
36
- type Query {
37
- payslip(employeeId: String): String
38
- @privilege(category: "payroll", privilege: "query")
39
- @auditTarget(kind: "Employee", arg: "employeeId")
40
- payslips: [Item] @privilege(category: "payroll", privilege: "query")
41
- broken: String @privilege(category: "payroll", privilege: "query")
42
- ownersOnly: String @privilege(superUserGranted: true)
43
- }
44
- `
45
-
46
- function buildSchema() {
47
- const schema = makeExecutableSchema({
48
- typeDefs: [privilegeDirectiveTypeDefs, auditTargetDirectiveTypeDefs, typeDefs],
49
- resolvers: {
50
- Query: {
51
- payslip: (_: any, { employeeId }: any) => `pay of ${employeeId}`,
52
- payslips: () => [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
53
- broken: () => {
54
- const error: any = new Error('salary 4,200,000 for kim')
55
- error.extensions = { code: 'INTERNAL_SERVER_ERROR' }
56
- throw error
57
- },
58
- ownersOnly: () => 'ok'
59
- }
60
- }
61
- })
62
- return privilegeDirectiveResolver(schema, { modules: [], mode: 'boot' })
63
- }
64
-
65
- /** A koa-like request: what the recorder reads from it and nothing more. */
66
- function koaContext(user: any, query: string, at = domain) {
67
- return {
68
- method: 'POST',
69
- path: '/graphql',
70
- state: { domain: at, user },
71
- req: { headers: { 'x-forwarded-for': '203.0.113.7, 10.0.0.1' }, socket: { remoteAddress: '10.0.0.1' } },
72
- request: { header: { 'user-agent': 'Mozilla/5.0 test' }, body: { query, operationName: 'Pay' } }
73
- }
74
- }
75
-
76
- /** One request through the schema, as the auth middleware runs it. */
77
- async function request(user: any, query: string, at = domain) {
78
- const context = koaContext(user, query, at)
79
- const result = await runInAuditScope(context, 'graphql', () => graphql({ schema: buildSchema(), source: query, contextValue: context }))
80
- await flushAudit()
81
- return result
82
- }
83
-
84
- const actions = (where: any = {}) => getRepository(AuditAction).find({ where, relations: ['actor', 'domain'] })
85
-
86
- async function seedUser(name: string, overrides: any = {}) {
87
- const salt = User.generateSalt()
88
- return await getRepository(User).save({
89
- username: name,
90
- email: `${name}@example.com`,
91
- name,
92
- salt,
93
- password: User.encode(PASSWORD, salt),
94
- status: 'activated',
95
- domains: [domain],
96
- ...overrides
97
- })
98
- }
99
-
100
- function allow(granted: boolean, { superUser = false } = {}) {
101
- User.hasPrivilege = jest.fn().mockResolvedValue(granted)
102
- ;(process as any).domainOwnerGranted = jest.fn().mockResolvedValue(false)
103
- ;(process as any).superUserGranted = jest.fn().mockResolvedValue(superUser)
104
- }
105
-
106
- const originalHasPrivilege = User.hasPrivilege
107
-
108
- beforeAll(async () => {
109
- await openAuthDatabase()
110
- })
111
-
112
- beforeEach(async () => {
113
- await resetAuthDatabase()
114
- User.hasPrivilege = originalHasPrivilege
115
- domain = await getRepository(Domain).save({ name: 'office', subdomain: 'office' })
116
- other = await getRepository(Domain).save({ name: 'plant', subdomain: 'plant' })
117
- kim = await seedUser('kim')
118
- lee = await seedUser('lee')
119
- })
120
-
121
- afterEach(() => {
122
- delete (process as any).domainOwnerGranted
123
- delete (process as any).superUserGranted
124
- })
125
-
126
- afterAll(async () => {
127
- await closeAuthDatabase()
128
- })
129
-
130
- describe('a request is one line, its doors are rows (§3.1 · §3.2)', () => {
131
- it('equal checks in one request become one row with a count, keeping each target', async () => {
132
- allow(true)
133
- await request(kim, '{ a: payslip(employeeId: "emp-one") b: payslip(employeeId: "emp-two") c: payslip(employeeId: "emp-one") }')
134
-
135
- const [line] = await getRepository(AuditRequest).find({ relations: ['actor', 'domain'] })
136
- expect(line).toMatchObject({ channel: 'graphql', operationType: 'query', operationName: 'Pay', fields: 'payslip', ip: '203.0.113.7', userAgent: 'Mozilla/5.0 test' })
137
- /* The root fields by schema name - the arguments asked never reach the line. */
138
- expect(JSON.stringify(line)).not.toMatch(/emp-one|emp-two/)
139
- expect(line.actor.id).toBe(kim.id)
140
- expect(line.domain.id).toBe(domain.id)
141
-
142
- const [check] = await actions({ kind: 'door.check' })
143
- expect(check).toMatchObject({ door: 'payroll:query', decision: 'granted', basis: 'role', count: 3, targetKind: 'Employee', targetRole: 'requested', requestId: line.id })
144
- expect([...check.targetIds].sort()).toEqual(['emp-one', 'emp-two'])
145
-
146
- const [result] = await actions({ kind: 'door.result' })
147
- expect(result).toMatchObject({ outcome: 'ok', count: 3 })
148
- })
149
-
150
- it('a check-in outside GraphQL is a line under the site and person its event named', async () => {
151
- const context = { method: 'GET', path: '/auth/checkin/office', state: {}, req: { headers: {}, socket: { remoteAddress: '10.0.0.9' } }, request: { header: {} }, _matchedRoute: '/auth/checkin/:subdomain?' }
152
- await runInAuditScope(context, 'http', () => recordAuditAction({ kind: 'auth.checkin', outcome: 'ok' }, { actorId: kim.id, domainId: domain.id }))
153
- await flushAudit()
154
-
155
- const [line] = await getRepository(AuditRequest).find({ relations: ['actor', 'domain'] })
156
- expect(line).toMatchObject({ channel: 'http', route: '/auth/checkin/:subdomain?', ip: '10.0.0.9' })
157
- expect(line.actor.id).toBe(kim.id)
158
- expect(line.domain.id).toBe(domain.id)
159
- })
160
-
161
- it('an http request that recorded nothing leaves no line', async () => {
162
- await runInAuditScope({ method: 'GET', path: '/theme.css', state: {} }, 'http', async () => undefined)
163
- await flushAudit()
164
- expect(await getRepository(AuditRequest).count()).toBe(0)
165
- })
166
-
167
- it('a list counts what it returned', async () => {
168
- allow(true)
169
- await request(kim, '{ payslips { id } }')
170
-
171
- const [result] = await actions({ kind: 'door.result' })
172
- expect(result.returned).toBe(3)
173
- })
174
-
175
- it('a refusal is a check with its cause and no result', async () => {
176
- allow(false)
177
- await request(kim, '{ payslip(employeeId: "e9") }')
178
-
179
- const rows = await actions()
180
- expect(rows).toHaveLength(1)
181
- expect(rows[0]).toMatchObject({ kind: 'door.check', decision: 'denied', basis: 'role', targetIds: ['e9'] })
182
- })
183
-
184
- it('passing the door is not doing the work - an error is a result with a code, never the message', async () => {
185
- allow(true)
186
- await request(kim, '{ broken }')
187
-
188
- const [result] = await actions({ kind: 'door.result' })
189
- expect(result).toMatchObject({ outcome: 'error', code: 'INTERNAL_SERVER_ERROR' })
190
- expect(JSON.stringify(await actions())).not.toMatch(/4,200,000|salary/)
191
- })
192
-
193
- it('a pass that rested on superuser says so', async () => {
194
- allow(false, { superUser: true })
195
- await request(kim, '{ ownersOnly }')
196
-
197
- const [check] = await actions({ kind: 'door.check' })
198
- expect(check).toMatchObject({ door: 'super', decision: 'granted', basis: 'superuser' })
199
- })
200
- })
201
-
202
- describe('who may do what is recorded where it is saved (§3.3 · §4-1)', () => {
203
- async function grant(user: any, roles: any[]) {
204
- const holder = await getRepository(User).findOne({ where: { id: user.id }, relations: ['roles'] })
205
- holder.roles = roles
206
- return await getRepository(User).save(holder)
207
- }
208
-
209
- it('a role given and taken back', async () => {
210
- const role = await getRepository(Role).save({ name: 'payroll', domain })
211
-
212
- await runInAuditScope(koaContext(lee, ''), 'http', () => grant(kim, [role]))
213
- await runInAuditScope(koaContext(lee, ''), 'http', () => grant(kim, []))
214
- await flushAudit()
215
-
216
- const granted = await actions({ kind: 'role.grant' })
217
- expect(granted).toHaveLength(1)
218
- expect(granted[0]).toMatchObject({ targetKind: 'User', targetIds: [kim.id], objectKind: 'Role', objectId: role.id })
219
- expect(granted[0].actor.id).toBe(lee.id)
220
- expect(granted[0].domain.id).toBe(domain.id)
221
-
222
- expect(await actions({ kind: 'role.revoke' })).toHaveLength(1)
223
- })
224
-
225
- it('a privilege added to a role, and removed', async () => {
226
- const privilege = await getRepository(Privilege).save({ name: 'query', category: 'payroll' })
227
- const role = await getRepository(Role).save({ name: 'payroll', domain, privileges: [privilege] })
228
- role.privileges = []
229
- await getRepository(Role).save(role)
230
-
231
- expect(await actions({ kind: 'role.privilege.add' })).toMatchObject([{ targetIds: [role.id], objectKind: 'Privilege', objectId: privilege.id }])
232
- expect(await actions({ kind: 'role.privilege.remove' })).toHaveLength(1)
233
- })
234
-
235
- it('a status change keeps the states, nothing else', async () => {
236
- kim.status = 'inactive'
237
- await getRepository(User).save(kim)
238
-
239
- const [row] = await actions({ kind: 'user.status' })
240
- expect(row.detail).toEqual({ from: 'activated', to: 'inactive' })
241
- })
242
-
243
- it('a grant rolled back leaves no record', async () => {
244
- const role = await getRepository(Role).save({ name: 'payroll', domain })
245
-
246
- await expect(
247
- authDataSource().transaction(async manager => {
248
- const holder = await manager.getRepository(User).findOne({ where: { id: kim.id }, relations: ['roles'] })
249
- holder.roles = [role]
250
- await manager.getRepository(User).save(holder)
251
- throw new Error('the change failed')
252
- })
253
- ).rejects.toThrow('the change failed')
254
-
255
- expect(await actions({ kind: 'role.grant' })).toHaveLength(0)
256
- })
257
-
258
- it('a grant whose record cannot be written does not happen', async () => {
259
- const role = await getRepository(Role).save({ name: 'payroll', domain })
260
- const table = authDataSource().getMetadata(AuditAction).tableName
261
- await authDataSource().query(`ALTER TABLE ${table} RENAME TO ${table}_away`)
262
-
263
- try {
264
- await expect(grant(kim, [role])).rejects.toThrow()
265
- } finally {
266
- await authDataSource().query(`ALTER TABLE ${table}_away RENAME TO ${table}`)
267
- }
268
-
269
- const holder = await getRepository(User).findOne({ where: { id: kim.id }, relations: ['roles'] })
270
- expect(holder.roles).toHaveLength(0)
271
- })
272
- })
273
-
274
- describe('only the fields it allows (§3.4)', () => {
275
- it('a detail key the kind did not register is refused', async () => {
276
- await expect(recordAuditAction({ kind: 'user.status', detail: { salary: 4200000 } })).rejects.toThrow(/does not allow detail "salary"/)
277
- })
278
-
279
- it('a kind nobody registered is refused', async () => {
280
- await expect(recordAuditAction({ kind: 'payroll.view' })).rejects.toThrow(/not registered/)
281
- })
282
- })
283
-
284
- describe('signing in (§3.3)', () => {
285
- it('a success, a wrong password, an account that does not exist', async () => {
286
- await signin({ username: 'kim', password: PASSWORD })
287
- await expect(signin({ username: 'kim', password: 'Wr0ngPass!' })).rejects.toThrow()
288
- await expect(signin({ username: 'nobody', password: PASSWORD })).rejects.toThrow()
289
- await flushAudit()
290
-
291
- const rows = await getRepository(AuditAction).find({ where: { kind: 'auth.signin' }, relations: ['actor'], order: { createdAt: 'ASC' } })
292
- expect(rows.map(row => [row.outcome, row.code, row.actor?.id ?? null, row.detail?.method])).toEqual([
293
- ['ok', null, kim.id, 'password'],
294
- ['error', 'password-not-matched', kim.id, 'password'],
295
- ['error', 'user not found', null, 'password']
296
- ])
297
- })
298
- })
299
-
300
- describe('a write that fails is counted apart and does not stop the answer (§4-4 · §4-5)', () => {
301
- it('the request is answered and the failure counted', async () => {
302
- allow(true)
303
- const before = auditHealth().failures
304
- const table = authDataSource().getMetadata(AuditRequest).tableName
305
- await authDataSource().query(`ALTER TABLE ${table} RENAME TO ${table}_away`)
306
-
307
- try {
308
- const result = await request(kim, '{ payslip(employeeId: "e1") }')
309
- expect(result.data).toEqual({ payslip: 'pay of e1' })
310
- } finally {
311
- await authDataSource().query(`ALTER TABLE ${table}_away RENAME TO ${table}`)
312
- }
313
-
314
- expect(auditHealth().failures).toBe(before + 1)
315
- expect(auditHealth().lastFailedAt).toBeInstanceOf(Date)
316
- })
317
- })
318
-
319
- describe('reading the audit (§5 · §6)', () => {
320
- const day = 24 * 60 * 60 * 1000
321
- const from = new Date(Date.now() - day)
322
- const to = new Date(Date.now() + day)
323
- const context = (user = lee, at = domain) => ({ state: { domain: at, user } })
324
-
325
- it("each member's checks, audit reads left out, and only this domain's", async () => {
326
- allow(true)
327
- await request(kim, '{ payslip(employeeId: "e1") }')
328
- await request(kim, '{ payslips { id } }', other)
329
- allow(false)
330
- await request(kim, '{ payslip(employeeId: "e2") }')
331
- await recordAuditAction({ kind: 'door.check', door: 'audit:query', decision: 'granted' }, { actorId: kim.id, domainId: domain.id })
332
- await recordAuditAction({ kind: 'auth.checkin', outcome: 'ok' }, { actorId: kim.id, domainId: domain.id })
333
-
334
- const people = await new AuditQuery().auditPeople(from, to, context())
335
- const row = people.find((one: any) => one.user.id === kim.id)
336
- expect(row).toMatchObject({ granted: 1, denied: 1, checkins: 1 })
337
- expect(row.lastCheckinAt).toBeInstanceOf(Date)
338
- /* The moment itself - a driver's zone-less text read as local time was hours off. */
339
- expect(Math.abs(row.lastCheckinAt.getTime() - Date.now())).toBeLessThan(60_000)
340
- expect(people.find((one: any) => one.user.id === lee.id)).toMatchObject({ granted: 0, denied: 0, checkins: 0 })
341
- })
342
-
343
- it("each member's last use is their latest request to this domain, and none before any", async () => {
344
- allow(true)
345
- await getRepository(AuditRequest).save([
346
- { id: '00000000-0000-4000-8000-000000000001', domain, actor: kim, channel: 'graphql', createdAt: new Date(Date.now() - 3 * 60 * 60 * 1000) },
347
- { id: '00000000-0000-4000-8000-000000000002', domain: other, actor: kim, channel: 'graphql', createdAt: new Date(Date.now() + 60 * 60 * 1000) }
348
- ])
349
- await request(kim, '{ payslip(employeeId: "e1") }')
350
-
351
- const people = await new AuditQuery().auditPeople(from, to, context())
352
- const row = people.find((one: any) => one.user.id === kim.id)
353
- expect(row.lastUsedAt).toBeInstanceOf(Date)
354
- /* The latest of this domain's - not the older one here, nor the later one in another domain. */
355
- expect(Math.abs(row.lastUsedAt.getTime() - Date.now())).toBeLessThan(60_000)
356
- expect(people.find((one: any) => one.user.id === lee.id).lastUsedAt).toBeUndefined()
357
- })
358
-
359
- it('every declared door, one with no record in the period too', async () => {
360
- await getRepository(Privilege).save([
361
- { name: 'query', category: 'payroll' },
362
- { name: 'mutation', category: 'payroll' },
363
- { name: 'query', category: 'audit' }
364
- ])
365
- allow(true)
366
- await request(kim, '{ payslip(employeeId: "e1") }')
367
-
368
- const doors = await new AuditQuery().auditDoors(from, to, context())
369
- expect(doors.map((one: any) => [one.door, one.people, one.granted])).toEqual([
370
- ['payroll:mutation', 0, 0],
371
- ['payroll:query', 1, 1]
372
- ])
373
- })
374
-
375
- it('records narrow by outcome, and never reach another domain', async () => {
376
- allow(true)
377
- await request(kim, '{ payslip(employeeId: "e1") }')
378
- await request(kim, '{ payslip(employeeId: "e1") }', other)
379
- allow(false)
380
- await request(lee, '{ payslip(employeeId: "e2") }')
381
-
382
- const all = await new AuditQuery().auditRecords({ from, to }, context())
383
- expect(all.total).toBe(2)
384
-
385
- const denied = await new AuditQuery().auditRecords({ from, to, outcome: 'denied' }, context())
386
- expect(denied.items.map((one: any) => one.request.actor.id)).toEqual([lee.id])
387
- expect(denied.items[0].actions.map((one: any) => one.kind)).toEqual(['door.check'])
388
- })
389
-
390
- it('my record is what I did, whatever is asked', async () => {
391
- await recordAuditAction({ kind: 'auth.checkin', outcome: 'ok' }, { actorId: kim.id, domainId: domain.id })
392
- await recordAuditAction(
393
- { kind: 'role.grant', targetKind: 'User', targetIds: [kim.id], objectKind: 'Role', objectId: 'r' },
394
- { actorId: lee.id, domainId: domain.id }
395
- )
396
-
397
- const mine = await new AuditQuery().myAuditActions(undefined, 10, context(kim))
398
- expect(mine.map((one: any) => one.kind)).toEqual(['auth.checkin'])
399
- })
400
- })
401
-
402
- describe('retention (§7)', () => {
403
- it('removes what is past a year and keeps the rest', async () => {
404
- await recordAuditAction({ kind: 'auth.checkin', outcome: 'ok' }, { actorId: kim.id, domainId: domain.id })
405
- const later = new Date(Date.now() + 366 * 24 * 60 * 60 * 1000)
406
-
407
- expect(await purgeExpiredAudit(new Date())).toEqual({ requests: 0, actions: 0 })
408
- expect(await purgeExpiredAudit(later)).toEqual({ requests: 0, actions: 1 })
409
- })
410
- })
@@ -1,58 +0,0 @@
1
- /**
2
- * Work done through the doors (ADR-0185 decision 6) - read from the audit: a declared mutation that went through is a
3
- * piece of the person's work on that day; an undeclared one, a failed one, another person's or another day's is not.
4
- */
5
-
6
- import { authDataSource, closeAuthDatabase, Domain, getRepository, openAuthDatabase, resetAuthDatabase } from './db'
7
- import { loadCompiled } from './compiled'
8
-
9
- const { registerWorkDoors, workDoneBy } = loadCompiled('service/audit/audit-work')
10
- const { AuditAction } = loadCompiled('service/audit/audit-action')
11
- const { AuditRequest } = loadCompiled('service/audit/audit-request')
12
- const { User } = loadCompiled('service/user/user')
13
-
14
- let domain: any, kim: any, lee: any
15
-
16
- beforeAll(async () => {
17
- await openAuthDatabase()
18
- })
19
- afterAll(async () => {
20
- await closeAuthDatabase()
21
- })
22
-
23
- beforeEach(async () => {
24
- await resetAuthDatabase()
25
- domain = await getRepository(Domain).save({ name: 'work', subdomain: 'work' })
26
- kim = await getRepository(User).save({ name: 'kim', email: 'kim@work.test', status: 'activated' })
27
- lee = await getRepository(User).save({ name: 'lee', email: 'lee@work.test', status: 'activated' })
28
- })
29
-
30
- const request = async (actor: any, at: string, fields: string, outcome: 'ok' | 'error' | 'both' | null, operationType = 'mutation') => {
31
- const one = await getRepository(AuditRequest).save({ id: require('crypto').randomUUID(), domain, actor, createdAt: new Date(at), channel: 'graphql', operationType, fields })
32
- for (const each of outcome === 'both' ? ['ok', 'error'] : outcome ? [outcome] : [])
33
- await getRepository(AuditAction).save({ domain, actor, requestId: one.id, createdAt: new Date(at), kind: 'door.result', door: 'office:mutation', outcome: each })
34
- return one
35
- }
36
-
37
- it('a declared mutation that went through is a piece of work, once per request and work', async () => {
38
- registerWorkDoors([
39
- { work: 'work.office.bank-lines', fields: ['updateMultipleCashTransaction', 'createCashTransaction'] },
40
- { work: 'work.office.tax-invoices', fields: ['importOfficeTaxInvoices'] }
41
- ])
42
- expect(() => registerWorkDoors([{ work: 'work.other', fields: ['importOfficeTaxInvoices'] }])).toThrow(/declared as the work/)
43
-
44
- await request(kim, '2026-11-10T01:00:00Z', 'updateMultipleCashTransaction,createCashTransaction', 'ok')
45
- await request(kim, '2026-11-10T02:00:00Z', 'importOfficeTaxInvoices', 'ok')
46
- await request(kim, '2026-11-10T03:00:00Z', 'importOfficeTaxInvoices', 'error')
47
- await request(kim, '2026-11-10T03:30:00Z', 'createCashTransaction,importOfficeTaxInvoices', 'both')
48
- await request(kim, '2026-11-10T04:00:00Z', 'saveMyDraft', 'ok')
49
- await request(kim, '2026-11-10T05:00:00Z', 'importOfficeTaxInvoices', 'ok', 'query')
50
- await request(lee, '2026-11-10T06:00:00Z', 'importOfficeTaxInvoices', 'ok')
51
- await request(kim, '2026-11-17T01:00:00Z', 'importOfficeTaxInvoices', 'ok')
52
-
53
- const done = await workDoneBy(authDataSource().manager, domain, kim.id, new Date('2026-11-09T00:00:00Z'), new Date('2026-11-16T00:00:00Z'))
54
- expect(done.map((one: any) => [one.at.toISOString(), one.work])).toEqual([
55
- ['2026-11-10T01:00:00.000Z', 'work.office.bank-lines'],
56
- ['2026-11-10T02:00:00.000Z', 'work.office.tax-invoices']
57
- ])
58
- })
@@ -1,58 +0,0 @@
1
- /**
2
- * auth-base's own tasks (ADR-0139 decision 11) - each names doors declared where auth-base runs
3
- * (its own server or shell's, which it depends on), and says what it is in all six languages.
4
- *
5
- * A task naming a category nobody declares is a definition error only when an application builds a
6
- * seat from it; a task with no words shows its id on the role screen. Both are caught here, where
7
- * the task is written.
8
- */
9
-
10
- import { readdirSync, readFileSync } from 'fs'
11
- import { join } from 'path'
12
-
13
- import { loadCompiled } from './compiled'
14
-
15
- const { ROLE_TASKS } = loadCompiled('role-tasks')
16
-
17
- const LANGUAGES = ['ko', 'en', 'ja', 'zh', 'es', 'ms']
18
- const SERVERS = [join(__dirname, '..', 'server'), join(__dirname, '..', '..', 'shell', 'server')]
19
-
20
- /** Every `@privilege(category: "x", privilege: "y")` written in auth-base's and shell's server source. */
21
- function declaredDoors(): Set<string> {
22
- const doors = new Set<string>()
23
- const walk = (dir: string) => {
24
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
25
- const path = join(dir, entry.name)
26
- if (entry.isDirectory()) walk(path)
27
- else if (entry.name.endsWith('.ts')) {
28
- const text = readFileSync(path, 'utf8')
29
- for (const match of text.matchAll(/@privilege\(category: \\?"([^"\\]+)\\?", privilege: \\?"([^"\\]+)\\?"/g)) {
30
- doors.add(`${match[1]}:${match[2]}`)
31
- }
32
- }
33
- }
34
- }
35
- SERVERS.forEach(walk)
36
- return doors
37
- }
38
-
39
- describe("auth-base's tasks", () => {
40
- const doors = declaredDoors()
41
-
42
- it.each(ROLE_TASKS.map((task: any) => [task.id, task]))('%s names doors declared where auth-base runs', (_id, task: any) => {
43
- for (const grant of task.grants) {
44
- for (const axis of grant.axes) expect(doors).toContain(`${grant.category}:${axis}`)
45
- }
46
- })
47
-
48
- it.each(LANGUAGES)('%s says what each task is', language => {
49
- const words = JSON.parse(readFileSync(join(__dirname, '..', 'translations', `${language}.json`), 'utf8'))
50
- for (const task of ROLE_TASKS) {
51
- expect({ task: task.id, name: !!words[`task.${task.id}.name`], description: !!words[`task.${task.id}.description`] }).toEqual({
52
- task: task.id,
53
- name: true,
54
- description: true
55
- })
56
- }
57
- })
58
- })
@@ -1,128 +0,0 @@
1
- /**
2
- * 체크인 대상 도메인 판정 규칙 테스트.
3
- *
4
- * 핵심 회귀: 부모 도메인에 정의된 역할을 자식 도메인에서 부여받은 사용자는
5
- * "역할을 부여한 도메인(자식)" 에 체크인해야 하고, "역할을 정의한 도메인(부모)" 에는 체크인하지 못한다.
6
- */
7
-
8
- import { CheckinFacts, checkinGrantsOf, isCheckinAllowed } from '../server/utils/checkin-domain-rule.js'
9
-
10
- const USER = 'user-1'
11
- const PARENT = { id: 'domain-parent', parentId: null }
12
- const CHILD = { id: 'domain-child', parentId: 'domain-parent' }
13
- const SIBLING = { id: 'domain-sibling', parentId: 'domain-parent' }
14
- const SUPPLIER = { id: 'domain-supplier', parentId: null }
15
-
16
- function facts(partial: Partial<CheckinFacts> = {}): CheckinFacts {
17
- return {
18
- userId: USER,
19
- memberDomainIds: [],
20
- roleDomainIds: [],
21
- ...partial
22
- }
23
- }
24
-
25
- describe('checkin domain rule', () => {
26
- describe('상속받은 역할을 부여한 도메인', () => {
27
- /* 자식 도메인 관리자가 부모 정의 역할(상속)을 자기 도메인 사용자에게 부여한 상황. */
28
- const inheritedGrant = facts({
29
- memberDomainIds: [CHILD.id],
30
- roleDomainIds: [PARENT.id]
31
- })
32
-
33
- it('부여가 일어난 자식 도메인에 체크인할 수 있다', () => {
34
- expect(isCheckinAllowed(CHILD, inheritedGrant)).toBe(true)
35
- expect(checkinGrantsOf(CHILD, inheritedGrant)).toEqual([{ via: 'inherited-role', roleDomainId: PARENT.id }])
36
- })
37
-
38
- it('역할을 정의한 부모 도메인에는 체크인할 수 없다', () => {
39
- expect(isCheckinAllowed(PARENT, inheritedGrant)).toBe(false)
40
- })
41
-
42
- it('멤버가 아닌 형제 자식 도메인에는 체크인할 수 없다', () => {
43
- /* 부모 역할은 모든 자식이 상속하므로, 멤버십 없이 열리면 테넌트 격리가 깨진다. */
44
- expect(isCheckinAllowed(SIBLING, inheritedGrant)).toBe(false)
45
- })
46
- })
47
-
48
- describe('자기 도메인에 정의된 역할', () => {
49
- const ownGrant = facts({
50
- memberDomainIds: [CHILD.id],
51
- roleDomainIds: [CHILD.id]
52
- })
53
-
54
- it('그 도메인에 체크인할 수 있다', () => {
55
- expect(checkinGrantsOf(CHILD, ownGrant)).toEqual([{ via: 'role', roleDomainId: CHILD.id }])
56
- })
57
-
58
- it('부모 도메인에는 체크인할 수 없다', () => {
59
- expect(isCheckinAllowed(PARENT, ownGrant)).toBe(false)
60
- })
61
- })
62
-
63
- describe('멤버십과 역할', () => {
64
- it('멤버이지만 어떤 역할도 없으면 체크인할 수 없다', () => {
65
- expect(isCheckinAllowed(CHILD, facts({ memberDomainIds: [CHILD.id] }))).toBe(false)
66
- })
67
-
68
- it('역할만 있고 멤버가 아니면 체크인할 수 없다', () => {
69
- expect(isCheckinAllowed(CHILD, facts({ roleDomainIds: [CHILD.id] }))).toBe(false)
70
- })
71
-
72
- it('최상위 도메인은 상속 경로가 없다', () => {
73
- const rootFacts = facts({ memberDomainIds: [PARENT.id], roleDomainIds: ['domain-other'] })
74
- expect(isCheckinAllowed(PARENT, rootFacts)).toBe(false)
75
- })
76
- })
77
-
78
- describe('파트너십으로 grant 된 역할', () => {
79
- /* 공급자 도메인이 자기 역할을 고객 도메인에 grant 하고, 고객 사용자가 그 역할을 부여받았다. */
80
- const partnerGrant = facts({
81
- memberDomainIds: [CHILD.id],
82
- roleDomainIds: [SUPPLIER.id],
83
- partnerRoleDomainIds: [SUPPLIER.id]
84
- })
85
-
86
- it('멤버가 아니어도 공급자 도메인에 체크인할 수 있다', () => {
87
- expect(checkinGrantsOf(SUPPLIER, partnerGrant)).toEqual([{ via: 'partner', roleDomainId: SUPPLIER.id }])
88
- })
89
-
90
- it('grant 되지 않은 역할은 파트너 경로를 열지 않는다', () => {
91
- const notGranted = facts({
92
- memberDomainIds: [CHILD.id],
93
- roleDomainIds: [SUPPLIER.id]
94
- })
95
- expect(isCheckinAllowed(SUPPLIER, notGranted)).toBe(false)
96
- })
97
- })
98
-
99
- describe('소유권', () => {
100
- it('하위 호환 owner 캐시 컬럼으로 체크인할 수 있다', () => {
101
- expect(checkinGrantsOf({ ...PARENT, owner: USER }, facts())).toEqual([{ via: 'owner' }])
102
- })
103
-
104
- it('DomainOwner 테이블 기준으로도 체크인할 수 있다', () => {
105
- expect(checkinGrantsOf(PARENT, facts({ ownedDomainIds: [PARENT.id] }))).toEqual([{ via: 'owner' }])
106
- })
107
-
108
- it('다른 사용자가 owner 인 도메인은 체크인할 수 없다', () => {
109
- expect(isCheckinAllowed({ ...PARENT, owner: 'user-2' }, facts())).toBe(false)
110
- })
111
- })
112
-
113
- describe('여러 근거가 동시에 성립하는 경우', () => {
114
- it('근거를 모두 반환한다 (권한 판정이 그중 권한 있는 역할을 골라야 한다)', () => {
115
- const both = facts({
116
- memberDomainIds: [CHILD.id],
117
- roleDomainIds: [CHILD.id, PARENT.id],
118
- ownedDomainIds: [CHILD.id]
119
- })
120
-
121
- expect(checkinGrantsOf(CHILD, both)).toEqual([
122
- { via: 'owner' },
123
- { via: 'role', roleDomainId: CHILD.id },
124
- { via: 'inherited-role', roleDomainId: PARENT.id }
125
- ])
126
- })
127
- })
128
- })