@things-factory/auth-base 10.1.29 → 10.1.33

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 (108) hide show
  1. package/dist-server/controllers/invitation.d.ts +47 -10
  2. package/dist-server/controllers/invitation.js +159 -113
  3. package/dist-server/controllers/invitation.js.map +1 -1
  4. package/dist-server/controllers/profile.d.ts +1 -0
  5. package/dist-server/router/auth-public-process-router.js +36 -13
  6. package/dist-server/router/auth-public-process-router.js.map +1 -1
  7. package/dist-server/router/oauth2/oauth2-router.js +7 -2
  8. package/dist-server/router/oauth2/oauth2-router.js.map +1 -1
  9. package/dist-server/router/oauth2/oauth2-server.js +4 -4
  10. package/dist-server/router/oauth2/oauth2-server.js.map +1 -1
  11. package/dist-server/service/app-binding/app-binding-mutation.d.ts +27 -0
  12. package/dist-server/service/app-binding/app-binding-mutation.js +39 -6
  13. package/dist-server/service/app-binding/app-binding-mutation.js.map +1 -1
  14. package/dist-server/service/app-binding/app-binding-query.js +14 -7
  15. package/dist-server/service/app-binding/app-binding-query.js.map +1 -1
  16. package/dist-server/service/app-binding/app-binding.d.ts +9 -0
  17. package/dist-server/service/app-binding/app-binding.js +10 -1
  18. package/dist-server/service/app-binding/app-binding.js.map +1 -1
  19. package/dist-server/service/appliance/appliance-mutation.js +34 -1
  20. package/dist-server/service/appliance/appliance-mutation.js.map +1 -1
  21. package/dist-server/service/appliance/appliance-query.d.ts +2 -0
  22. package/dist-server/service/appliance/appliance-query.js +48 -0
  23. package/dist-server/service/appliance/appliance-query.js.map +1 -1
  24. package/dist-server/service/appliance/appliance.d.ts +1 -0
  25. package/dist-server/service/appliance/appliance.js +33 -3
  26. package/dist-server/service/appliance/appliance.js.map +1 -1
  27. package/dist-server/service/application/application-mutation.js +51 -6
  28. package/dist-server/service/application/application-mutation.js.map +1 -1
  29. package/dist-server/service/application/application.d.ts +9 -3
  30. package/dist-server/service/application/application.js +24 -8
  31. package/dist-server/service/application/application.js.map +1 -1
  32. package/dist-server/service/auth-provider/auth-provider-mutation.js +5 -0
  33. package/dist-server/service/auth-provider/auth-provider-mutation.js.map +1 -1
  34. package/dist-server/service/domain-generator/domain-generator-mutation.js +3 -0
  35. package/dist-server/service/domain-generator/domain-generator-mutation.js.map +1 -1
  36. package/dist-server/service/invitation/invitation-mutation.d.ts +17 -15
  37. package/dist-server/service/invitation/invitation-mutation.js +83 -56
  38. package/dist-server/service/invitation/invitation-mutation.js.map +1 -1
  39. package/dist-server/service/invitation/invitation-query.d.ts +20 -5
  40. package/dist-server/service/invitation/invitation-query.js +60 -19
  41. package/dist-server/service/invitation/invitation-query.js.map +1 -1
  42. package/dist-server/service/invitation/invitation.d.ts +14 -2
  43. package/dist-server/service/invitation/invitation.js +77 -8
  44. package/dist-server/service/invitation/invitation.js.map +1 -1
  45. package/dist-server/service/login-history/login-history-query.js +3 -0
  46. package/dist-server/service/login-history/login-history-query.js.map +1 -1
  47. package/dist-server/service/role/role-mutation.js +14 -15
  48. package/dist-server/service/role/role-mutation.js.map +1 -1
  49. package/dist-server/service/role/role-query.js +26 -4
  50. package/dist-server/service/role/role-query.js.map +1 -1
  51. package/dist-server/service/role/role-types.d.ts +2 -0
  52. package/dist-server/service/role/role-types.js +8 -0
  53. package/dist-server/service/role/role-types.js.map +1 -1
  54. package/dist-server/service/role-template/role-template-mutation.d.ts +17 -1
  55. package/dist-server/service/role-template/role-template-mutation.js +14 -3
  56. package/dist-server/service/role-template/role-template-mutation.js.map +1 -1
  57. package/dist-server/service/user/user-mutation.js +1 -0
  58. package/dist-server/service/user/user-mutation.js.map +1 -1
  59. package/dist-server/service/user/user.d.ts +1 -0
  60. package/dist-server/service/user/user.js +19 -4
  61. package/dist-server/service/user/user.js.map +1 -1
  62. package/dist-server/templates/invitation-email.d.ts +2 -1
  63. package/dist-server/templates/invitation-email.js +16 -4
  64. package/dist-server/templates/invitation-email.js.map +1 -1
  65. package/dist-server/tsconfig.tsbuildinfo +1 -1
  66. package/dist-server/utils/credential-at-rest.d.ts +28 -0
  67. package/dist-server/utils/credential-at-rest.js +36 -0
  68. package/dist-server/utils/credential-at-rest.js.map +1 -0
  69. package/dist-server/utils/credential-features.d.ts +35 -0
  70. package/dist-server/utils/credential-features.js +45 -0
  71. package/dist-server/utils/credential-features.js.map +1 -0
  72. package/dist-server/utils/credential-serial-rule.d.ts +41 -0
  73. package/dist-server/utils/credential-serial-rule.js +43 -0
  74. package/dist-server/utils/credential-serial-rule.js.map +1 -0
  75. package/dist-server/utils/invitation-state.d.ts +52 -0
  76. package/dist-server/utils/invitation-state.js +76 -0
  77. package/dist-server/utils/invitation-state.js.map +1 -0
  78. package/dist-server/utils/refuse-role-name.d.ts +42 -0
  79. package/dist-server/utils/refuse-role-name.js +76 -0
  80. package/dist-server/utils/refuse-role-name.js.map +1 -0
  81. package/dist-server/utils/role-name-standing.d.ts +58 -0
  82. package/dist-server/utils/role-name-standing.js +72 -0
  83. package/dist-server/utils/role-name-standing.js.map +1 -0
  84. package/package.json +4 -4
  85. package/tests/app-binding-delete-db.test.ts +188 -0
  86. package/tests/appliance-credential-state-db.test.ts +207 -0
  87. package/tests/appliance-delete-db.test.ts +119 -0
  88. package/tests/appliance-revoke-db.test.ts +73 -28
  89. package/tests/appliance-schema.test.ts +129 -0
  90. package/tests/application-delete-db.test.ts +190 -0
  91. package/tests/application-token-payload-db.test.ts +185 -0
  92. package/tests/checkin-privilege-seam-db.test.ts +158 -0
  93. package/tests/credential-at-rest-db.test.ts +246 -0
  94. package/tests/credential-features-off-db.test.ts +189 -0
  95. package/tests/credential-serial-rule.test.ts +84 -0
  96. package/tests/invitation-db.test.ts +416 -0
  97. package/tests/invitation-state.test.ts +92 -0
  98. package/tests/open-doors.test.ts +132 -0
  99. package/tests/role-inheritance-db.test.ts +286 -0
  100. package/tests/role-mutation-db.test.ts +21 -7
  101. package/tests/role-name-lookup-sentinel.test.ts +79 -0
  102. package/tests/role-name-standing.test.ts +83 -0
  103. package/tests/token-issuance.test.ts +7 -3
  104. package/translations/en.json +2 -0
  105. package/translations/ja.json +2 -0
  106. package/translations/ko.json +2 -0
  107. package/translations/ms.json +2 -0
  108. package/translations/zh.json +2 -0
@@ -3,9 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RoleTemplateMutation = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
+ const shell_1 = require("@things-factory/shell");
6
7
  const privilege_js_1 = require("../privilege/privilege.js");
8
+ const refuse_role_name_js_1 = require("../../utils/refuse-role-name.js");
7
9
  const role_js_1 = require("../role/role.js");
8
10
  const role_template_js_1 = require("./role-template.js");
11
+ /* `refuseRoleNameIfStanding` throws a `Refusal`; this is the exit that turns it into a sentence. */
9
12
  let RoleTemplateMutation = class RoleTemplateMutation {
10
13
  /**
11
14
  * Creates a role from a template — once per name.
@@ -32,7 +35,7 @@ let RoleTemplateMutation = class RoleTemplateMutation {
32
35
  * this side would mean overwriting, and would be unable to tell a stale grant from one
33
36
  * the administrator added on purpose.
34
37
  */
35
- async createRoleFromTemplate(templateId, name, context) {
38
+ async createRoleFromTemplate(templateId, name, context, separately) {
36
39
  const { domain, user, tx } = context.state;
37
40
  const template = findTemplate(templateId);
38
41
  if (!template) {
@@ -48,6 +51,12 @@ let RoleTemplateMutation = class RoleTemplateMutation {
48
51
  relations: ['privileges']
49
52
  });
50
53
  if (!role) {
54
+ /*
55
+ * Only on the way to creating one. Topping up an existing role of ours is not a new name
56
+ * and must not be refused — running the same template twice is how a seat gets privileges
57
+ * the template gained since.
58
+ */
59
+ await (0, refuse_role_name_js_1.refuseRoleNameIfStanding)({ name: roleName, domain, separately, tx });
51
60
  role = await roleRepository.save({
52
61
  domain,
53
62
  name: roleName,
@@ -111,12 +120,14 @@ tslib_1.__decorate([
111
120
  tslib_1.__param(0, (0, type_graphql_1.Arg)('templateId')),
112
121
  tslib_1.__param(1, (0, type_graphql_1.Arg)('name', { nullable: true })),
113
122
  tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
123
+ tslib_1.__param(3, (0, type_graphql_1.Arg)('separately', { nullable: true })),
114
124
  tslib_1.__metadata("design:type", Function),
115
- tslib_1.__metadata("design:paramtypes", [String, String, Object]),
125
+ tslib_1.__metadata("design:paramtypes", [String, String, Object, Boolean]),
116
126
  tslib_1.__metadata("design:returntype", Promise)
117
127
  ], RoleTemplateMutation.prototype, "createRoleFromTemplate", null);
118
128
  exports.RoleTemplateMutation = RoleTemplateMutation = tslib_1.__decorate([
119
- (0, type_graphql_1.Resolver)(role_js_1.Role)
129
+ (0, type_graphql_1.Resolver)(role_js_1.Role),
130
+ (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
120
131
  ], RoleTemplateMutation);
121
132
  function findTemplate(id) {
122
133
  return (0, role_template_js_1.roleTemplateById)(id) || (0, role_template_js_1.computedRoleTemplates)().find(template => template.id === id);
@@ -1 +1 @@
1
- {"version":3,"file":"role-template-mutation.js","sourceRoot":"","sources":["../../../server/service/role-template/role-template-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { Privilege } from '../privilege/privilege.js'\nimport { Role } from '../role/role.js'\nimport { roleTemplateById, computedRoleTemplates, RegisteredRoleTemplate } from './role-template.js'\n\n@Resolver(Role)\nexport class RoleTemplateMutation {\n /**\n * Creates a role from a template — once per name.\n *\n * ── Why an existing name is used rather than replaced ────────────────────────\n * Approval lines store a role's **id** (`worklist` resolves an approver of type `Role`\n * by id). Creating a second QUALITY-INSPECTOR would leave every approval routed at the\n * first one while the administrator edits the second, and nothing would report it: the\n * screen shows two roles and the approvals go to the one nobody is looking at.\n *\n * So an existing role of that name is used as it stands. operato-plant's seed already\n * behaves this way (`seed-demo-org.ts` looks the role up and only saves when absent);\n * this is the same rule, stated once.\n *\n * ── Why privileges are added and never removed ───────────────────────────────\n * Two applications may declare a template with the same role name on purpose, and the\n * privileges are then the union — to a user \"quality inspector\" is one seat. The union\n * only widens: if a template later drops a privilege it stays on roles already seeded,\n * because the seed runs once and the role belongs to the administrator afterwards.\n *\n * A privilege that is no longer declared anywhere is already handled elsewhere and not\n * here — `isDeprecatedPrivilege` marks it, the role screen strikes it through and refuses\n * to switch it back on, and removing it is the administrator's call. Reclaiming it from\n * this side would mean overwriting, and would be unable to tell a stale grant from one\n * the administrator added on purpose.\n */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Role, { description: 'To create a role from a role template' })\n async createRoleFromTemplate(\n @Arg('templateId') templateId: string,\n /**\n * What this plant calls the seat. Defaults to the template's name.\n *\n * The template knows which seat exists in a factory running this product; it does not\n * know what the people there call it. And because a role is also what an approval line\n * points at, the same seat is sometimes needed twice — a day shift and a night shift\n * inspector hold the same privileges and take different approvals. Without a name of\n * their own a template could be used exactly once per domain.\n */\n @Arg('name', { nullable: true }) name: string | undefined,\n @Ctx() context: ResolverContext\n ): Promise<Role> {\n const { domain, user, tx } = context.state\n\n const template = findTemplate(templateId)\n\n if (!template) {\n throw new Error(`There is no role template \"${templateId}\" in this installation.`)\n }\n\n const roleName = (name || template.name).trim()\n\n if (!roleName) {\n throw new Error('A role needs a name.')\n }\n\n const roleRepository = tx.getRepository(Role)\n\n let role = await roleRepository.findOne({\n where: { domain: { id: domain.id }, name: roleName },\n relations: ['privileges']\n })\n\n if (!role) {\n role = await roleRepository.save({\n domain,\n name: roleName,\n description: template.description,\n creator: user,\n updater: user\n } as any)\n\n role = await roleRepository.findOne({\n where: { id: role!.id },\n relations: ['privileges']\n })\n }\n\n const held = new Set((role!.privileges || []).map(privilege => `${privilege.category} ${privilege.name}`))\n const wanted = template.grants.flatMap(grant => grant.axes.map(axis => ({ category: grant.category, name: axis })))\n\n /*\n * Privileges have no domain — the whole server shares one set and what differs per\n * domain is only which role holds them (`Privilege` has no domain column; auth-base's\n * boot creates them by category and name alone). Filtering by domain finds nothing.\n */\n const privilegeRepository = tx.getRepository(Privilege)\n const added: Privilege[] = []\n\n for (const one of wanted) {\n if (held.has(`${one.category} ${one.name}`)) {\n continue\n }\n\n const privilege = await privilegeRepository.findOne({ where: { category: one.category, name: one.name } })\n\n /*\n * A privilege this installation does not declare is skipped, never created. Creating\n * it here would make a row no `@privilege` asks for: the role would look as if it\n * had been granted something, and open nothing. Granted-but-closed is the worst of\n * the three outcomes. `roleTemplates` reports these as `missingCount` before the\n * administrator picks the template.\n */\n if (privilege) {\n added.push(privilege)\n }\n }\n\n /*\n * Nothing records that this role came from a template.\n *\n * A `templateVersions` column stood here so a later change to the template could be\n * reported. It bought less than it looked like: what an administrator needs is \"these\n * two privileges were added to the template\", and that is the template's current\n * grants against the role's current privileges — computed, not stored. Telling them\n * apart from a privilege the administrator removed on purpose would take the seeded\n * content itself, not a version of it, and no screen asks for either yet.\n *\n * So the seed ends here. If reporting template drift is designed later, what to store\n * follows from that design rather than being guessed at now.\n */\n role!.privileges = [...(role!.privileges || []), ...added]\n role!.updater = user\n\n return await roleRepository.save(role!)\n }\n}\n\nfunction findTemplate(id: string): RegisteredRoleTemplate | undefined {\n return roleTemplateById(id) || computedRoleTemplates().find(template => template.id === id)\n}\n"]}
1
+ {"version":3,"file":"role-template-mutation.js","sourceRoot":"","sources":["../../../server/service/role-template/role-template-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { getRepository, SpeakRefusals } from '@things-factory/shell'\n\nimport { Privilege } from '../privilege/privilege.js'\nimport { refuseRoleNameIfStanding } from '../../utils/refuse-role-name.js'\nimport { Role } from '../role/role.js'\nimport { roleTemplateById, computedRoleTemplates, RegisteredRoleTemplate } from './role-template.js'\n\n/* `refuseRoleNameIfStanding` throws a `Refusal`; this is the exit that turns it into a sentence. */\n@Resolver(Role)\n@UseMiddleware(SpeakRefusals)\nexport class RoleTemplateMutation {\n /**\n * Creates a role from a template — once per name.\n *\n * ── Why an existing name is used rather than replaced ────────────────────────\n * Approval lines store a role's **id** (`worklist` resolves an approver of type `Role`\n * by id). Creating a second QUALITY-INSPECTOR would leave every approval routed at the\n * first one while the administrator edits the second, and nothing would report it: the\n * screen shows two roles and the approvals go to the one nobody is looking at.\n *\n * So an existing role of that name is used as it stands. operato-plant's seed already\n * behaves this way (`seed-demo-org.ts` looks the role up and only saves when absent);\n * this is the same rule, stated once.\n *\n * ── Why privileges are added and never removed ───────────────────────────────\n * Two applications may declare a template with the same role name on purpose, and the\n * privileges are then the union — to a user \"quality inspector\" is one seat. The union\n * only widens: if a template later drops a privilege it stays on roles already seeded,\n * because the seed runs once and the role belongs to the administrator afterwards.\n *\n * A privilege that is no longer declared anywhere is already handled elsewhere and not\n * here — `isDeprecatedPrivilege` marks it, the role screen strikes it through and refuses\n * to switch it back on, and removing it is the administrator's call. Reclaiming it from\n * this side would mean overwriting, and would be unable to tell a stale grant from one\n * the administrator added on purpose.\n */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Role, { description: 'To create a role from a role template' })\n async createRoleFromTemplate(\n @Arg('templateId') templateId: string,\n /**\n * What this plant calls the seat. Defaults to the template's name.\n *\n * The template knows which seat exists in a factory running this product; it does not\n * know what the people there call it. And because a role is also what an approval line\n * points at, the same seat is sometimes needed twice — a day shift and a night shift\n * inspector hold the same privileges and take different approvals. Without a name of\n * their own a template could be used exactly once per domain.\n */\n @Arg('name', { nullable: true }) name: string | undefined,\n @Ctx() context: ResolverContext,\n /**\n * The operator saying this domain stands its own role of a name it already inherits\n * (ADR-0062 decision 3).\n *\n * ⚠ This is where the rule bites hardest, and stopping here is the point of it. A template\n * seeds the seats a product needs, and on a tenant with a parent those names are often\n * already visible through inheritance — so the template would quietly put a second row of\n * the same name beside the inherited one, which is the state the whole rule exists to\n * prevent.\n *\n * The name is not changed to get past it. Standing up the same names in this domain is the\n * first step of moving off an inherited structure (decision 2), so this says \"yes, that is\n * what I am doing\" rather than \"call it something else\". `name` above remains for the other\n * thing — a second seat with the same privileges under a name of its own.\n */\n @Arg('separately', { nullable: true }) separately: boolean | undefined\n ): Promise<Role> {\n const { domain, user, tx } = context.state\n\n const template = findTemplate(templateId)\n\n if (!template) {\n throw new Error(`There is no role template \"${templateId}\" in this installation.`)\n }\n\n const roleName = (name || template.name).trim()\n\n if (!roleName) {\n throw new Error('A role needs a name.')\n }\n\n const roleRepository = tx.getRepository(Role)\n\n let role = await roleRepository.findOne({\n where: { domain: { id: domain.id }, name: roleName },\n relations: ['privileges']\n })\n\n if (!role) {\n /*\n * Only on the way to creating one. Topping up an existing role of ours is not a new name\n * and must not be refused — running the same template twice is how a seat gets privileges\n * the template gained since.\n */\n await refuseRoleNameIfStanding({ name: roleName, domain, separately, tx })\n\n role = await roleRepository.save({\n domain,\n name: roleName,\n description: template.description,\n creator: user,\n updater: user\n } as any)\n\n role = await roleRepository.findOne({\n where: { id: role!.id },\n relations: ['privileges']\n })\n }\n\n const held = new Set((role!.privileges || []).map(privilege => `${privilege.category} ${privilege.name}`))\n const wanted = template.grants.flatMap(grant => grant.axes.map(axis => ({ category: grant.category, name: axis })))\n\n /*\n * Privileges have no domain — the whole server shares one set and what differs per\n * domain is only which role holds them (`Privilege` has no domain column; auth-base's\n * boot creates them by category and name alone). Filtering by domain finds nothing.\n */\n const privilegeRepository = tx.getRepository(Privilege)\n const added: Privilege[] = []\n\n for (const one of wanted) {\n if (held.has(`${one.category} ${one.name}`)) {\n continue\n }\n\n const privilege = await privilegeRepository.findOne({ where: { category: one.category, name: one.name } })\n\n /*\n * A privilege this installation does not declare is skipped, never created. Creating\n * it here would make a row no `@privilege` asks for: the role would look as if it\n * had been granted something, and open nothing. Granted-but-closed is the worst of\n * the three outcomes. `roleTemplates` reports these as `missingCount` before the\n * administrator picks the template.\n */\n if (privilege) {\n added.push(privilege)\n }\n }\n\n /*\n * Nothing records that this role came from a template.\n *\n * A `templateVersions` column stood here so a later change to the template could be\n * reported. It bought less than it looked like: what an administrator needs is \"these\n * two privileges were added to the template\", and that is the template's current\n * grants against the role's current privileges — computed, not stored. Telling them\n * apart from a privilege the administrator removed on purpose would take the seeded\n * content itself, not a version of it, and no screen asks for either yet.\n *\n * So the seed ends here. If reporting template drift is designed later, what to store\n * follows from that design rather than being guessed at now.\n */\n role!.privileges = [...(role!.privileges || []), ...added]\n role!.updater = user\n\n return await roleRepository.save(role!)\n }\n}\n\nfunction findTemplate(id: string): RegisteredRoleTemplate | undefined {\n return roleTemplateById(id) || computedRoleTemplates().find(template => template.id === id)\n}\n"]}
@@ -412,6 +412,7 @@ tslib_1.__decorate([
412
412
  ], UserMutation.prototype, "deleteUsers", null);
413
413
  tslib_1.__decorate([
414
414
  (0, type_graphql_1.Directive)('@transaction'),
415
+ (0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)'),
415
416
  (0, type_graphql_1.Mutation)(returns => Boolean, {
416
417
  description: 'Invites an existing user to join the current domain. Use this mutation to add a user to a domain if they already exist in the system.'
417
418
  }),
@@ -1 +1 @@
1
- {"version":3,"file":"user-mutation.js","sourceRoot":"","sources":["../../../server/service/user/user-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport { ILike, In, SelectQueryBuilder, EntityManager } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { Domain, getRepository, ObjectRef } from '@things-factory/shell'\n\nimport { deleteUser as commonDeleteUser, deleteUsers as commonDeleteUsers } from '../../controllers/delete-user.js'\nimport { buildDomainUsersQueryBuilder } from '../../utils/get-domain-users.js'\nimport { isDomainOwner, setPrimaryDomainOwner } from '../../utils/domain-ownership.js'\nimport { Role } from '../role/role.js'\nimport { User, UserStatus } from './user.js'\nimport { NewUser, UserPatch } from './user-types.js'\nimport { USERNAME_ALREADY_EXISTS, EMAIL_ALREADY_EXISTS, USERNAME_INVALID } from '../../constants/error-code.js'\n\n@Resolver(User)\nexport class UserMutation {\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => User, {\n description:\n 'Creates a new user in the system. Use this mutation to register a user with the specified credentials and domain information.'\n })\n async createUser(\n @Arg('user', { description: 'The input object containing new user information.' }) user: NewUser,\n @Ctx() context: ResolverContext\n ) {\n const { domain, tx } = context.state\n const { defaultPassword } = config.get('password')\n const { username, email } = user\n const userRepository = getRepository(User, tx)\n\n user.username = username.trim()\n user.email = email.trim()\n\n if (await userRepository.findOne({ where: { username: user.username } })) {\n throw new Error(context.t('error.' + USERNAME_ALREADY_EXISTS))\n }\n\n if (await userRepository.findOne({ where: { email: ILike(user.email) } })) {\n throw new Error(context.t('error.' + EMAIL_ALREADY_EXISTS))\n }\n\n if (!user.password && !defaultPassword) {\n throw new Error('initial password or default password should be supported.')\n }\n\n // TODO username은 다음 패턴을 따라야 한다. pattern=\"^[A-Za-z0-9._@-]+$\"\n if (!/^[A-Za-z0-9._@-]+$/.test(user.username)) {\n throw new Error(context.t('error.' + USERNAME_INVALID, { username: user.username }))\n }\n\n // consider if validation password rule is required\n /* check if password is following the rule */\n // User.validatePasswordByRule(user.password, context.lng)\n\n const salt = User.generateSalt()\n\n return await userRepository.save({\n creator: context.state.user,\n updater: context.state.user,\n ...user,\n domains: [domain],\n roles:\n user.roles && user.roles.length\n ? await getRepository(Role, tx).findBy({\n id: In(user.roles.map(role => role.id)),\n domain: { id: domain.id }\n })\n : [],\n salt,\n passwordUpdatedAt: new Date(),\n password: user.password ? User.encode(user.password, salt) : User.encode(defaultPassword, salt)\n })\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => User, {\n description:\n 'Updates the information of an existing user. Use this mutation to modify user profile details, such as name, email, or roles.'\n })\n async updateUser(\n @Arg('email', type => GraphQLEmailAddress, { description: 'The email address of the user to update.' })\n email: string,\n @Arg('patch', { description: 'The patch object containing updated user fields.' }) patch: UserPatch,\n @Ctx() context: ResolverContext\n ) {\n const { domain, user: updater, tx }: { domain: Domain; user: User; tx?: EntityManager } = context.state\n const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id, 'USER')\n const user: User = await qb\n .andWhere('LOWER(USER.email) = :email', { email: email?.toLowerCase().trim() || '' })\n .leftJoinAndSelect('USER.roles', 'ROLES')\n .leftJoinAndSelect('ROLES.domain', 'R_DOMAIN')\n .getOne()\n\n if (patch.roles) {\n patch.roles = await getRepository(Role, tx).find({\n where: { id: In(patch.roles.map((r: Partial<Role>) => r.id)) }\n })\n }\n\n if (patch.status && patch.status === 'activated') {\n user.status = UserStatus.ACTIVATED\n }\n\n return await getRepository(User, tx).save({\n ...user,\n ...patch,\n updater\n } as any)\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [User], {\n description:\n 'Updates or creates multiple users in a single operation. Use this mutation for batch processing of user records.'\n })\n async updateMultipleUser(\n @Arg('patches', type => [UserPatch], { description: 'An array of patch objects for batch update or creation.' })\n patches: UserPatch[],\n @Ctx() context: ResolverContext\n ) {\n const { domain, user, tx } = context.state\n const userRepo = getRepository(User, tx)\n\n let results = []\n const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')\n const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')\n\n if (_createRecords.length > 0) {\n for (let i = 0; i < _createRecords.length; i++) {\n const newRecord = _createRecords[i]\n\n // consider if validation password rule is required\n /* check if password is following the rule */\n // User.validatePasswordByRule(newRecord.password, context.lng)\n\n const salt = User.generateSalt()\n const result = await userRepo.save({\n ...(newRecord as any),\n domains: [domain],\n salt,\n password: User.encode(newRecord.password, salt),\n passwordUpdatedAt: new Date(),\n creator: user,\n updater: user\n })\n\n // repository api는 작동하지 않음.\n // await tx\n // .createQueryBuilder()\n // .insert()\n // .into('users_domains')\n // .values({\n // usersId: result.id,\n // domainsId: domain.id\n // })\n // .execute()\n\n results.push({ ...result, cuFlag: '+' })\n }\n }\n\n if (_updateRecords.length > 0) {\n for (let i = 0; i < _updateRecords.length; i++) {\n const updateRecord = _updateRecords[i]\n // consider if validation password rule is required\n /* check if password is following the rule */\n // User.validatePasswordByRule(updateRecord.password, context.lng)\n\n const user = await userRepo.findOne({ where: { id: updateRecord.id }, relations: ['domains'] })\n var domains = user.domains.find(d => d.id === domain.id) ? user.domains : [...user.domains, domain]\n\n const result = await userRepo.save({\n ...user,\n ...(updateRecord as any),\n domains,\n password: updateRecord.password ? User.encode(updateRecord.password, user.salt) : user.password,\n updater: user\n })\n\n if (!updateRecord.status) {\n continue\n }\n\n // const domain = await user.domain\n // if (!domain) {\n // continue\n // }\n\n // const domainId = domain.id\n // const domains = await user.domains\n // if (!domains.find(domain => domain.id == domainId)) {\n // await tx\n // .createQueryBuilder()\n // .insert()\n // .into('users_domains')\n // .values({\n // usersId: user.id,\n // domainsId: domain.id\n // })\n // .execute()\n // }\n\n results.push({ ...result, cuFlag: 'M' })\n }\n }\n\n return results\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Deletes a user from the system by their username. Use this mutation to permanently remove a user account and revoke their access.'\n })\n async deleteUser(\n @Arg('username', { description: 'The username of the user to delete.' }) username: string,\n @Ctx() context: ResolverContext\n ) {\n const { tx } = context.state\n\n await commonDeleteUser({ username }, tx)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Deletes multiple users from the system by their usernames. Use this mutation to remove several user accounts at once.'\n })\n async deleteUsers(\n @Arg('usernames', type => [String], { description: 'An array of usernames to delete.' }) usernames: string[],\n @Ctx() context: ResolverContext\n ) {\n const { tx } = context.state\n await commonDeleteUsers({ usernames }, tx)\n\n return true\n }\n\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Invites an existing user to join the current domain. Use this mutation to add a user to a domain if they already exist in the system.'\n })\n async inviteUser(\n @Arg('username', { description: 'The username or email of the user to invite.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, tx } = context.state\n const userRepository = getRepository(User, tx)\n\n var invitee: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!invitee && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n invitee = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!invitee) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n const existingDomains: Domain[] = invitee.domains\n if (existingDomains.find((d: Domain) => d.id === domain.id)) {\n throw new Error(context.t('error.x already exists in y', { x: context.t('field.user'), y: domain.name }))\n }\n\n invitee.domains = [...existingDomains, domain]\n await userRepository.save(invitee)\n\n return true\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, {\n description:\n \"Removes a user from the current domain. Use this mutation to revoke a user's access to a specific domain without deleting their account.\"\n })\n async deleteDomainUser(\n @Arg('username', { description: 'The username or email of the user to remove from the domain.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, domain } = context.state\n const userRepository = getRepository(User, tx)\n\n var user: User = await userRepository.findOne({\n where: { username },\n relations: ['domains', 'roles']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains', 'roles']\n })\n }\n\n if (!user) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n const targetDomainIdx: number = user.domains.findIndex((userDomain: Domain) => userDomain.id === domain.id)\n if (targetDomainIdx < 0) {\n throw new Error(context.t('error.x is not a member of y', { x: user.name, y: domain.name }))\n }\n\n // Remove domain relation with user\n user.domains.splice(targetDomainIdx, 1)\n\n // Remove domain's roles that user has\n user.roles = user.roles.filter((role: Role) => role.domainId !== domain.id)\n\n await userRepository.save(user)\n\n return true\n }\n\n @Directive('@privilege(domainOwnerGranted: true, superUserGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Transfers domain ownership to another user. Use this mutation to assign the owner role to a different user within the domain.'\n })\n async transferOwner(\n @Arg('username', { description: 'The username or email of the user to transfer ownership to.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, tx, user: actor } = context.state\n const userRepository = getRepository(User, tx)\n\n var user: User = await userRepository.findOne({\n where: { username },\n relations: ['domains', 'roles']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains', 'roles']\n })\n }\n\n if (!user) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (user.status !== UserStatus.ACTIVATED) {\n throw new Error('Only activated users are eligible to receive admin privileges.')\n }\n\n if (user.domains.map((d: Domain) => d.id).indexOf(domain.id) < 0) {\n throw new Error(`User is not belongs to current domain`)\n }\n\n /* 역할은 부모 도메인으로부터 상속될 수 있으므로 부여된 상속 역할도 자격으로 인정한다. */\n const grantableRoleDomainIds: string[] = [domain.id, domain.parentId].filter(Boolean)\n if (user.roles.filter((r: Role) => grantableRoleDomainIds.includes(r.domainId)).length == 0) {\n throw new Error(`Only users with at least one role in this domain are eligible to receive admin privileges.`)\n }\n\n /* 대표 오너 컬럼과 `DomainOwner` 테이블을 함께 갱신한다 (기존 오너 엔트리는 유지). */\n await setPrimaryDomainOwner(domain, user.id, actor, tx)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Activates a user account in the current domain. Use this mutation to enable a user who was previously inactive.'\n })\n async activateUser(\n @Arg('username', { description: 'The username or email of the user to activate.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, domain } = context.state\n const userRepository = getRepository(User, tx)\n\n var targetUser: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!targetUser && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n targetUser = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!targetUser) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {\n throw new Error('User is not belong to domain')\n }\n\n targetUser.failCount = 0\n targetUser.status = UserStatus.ACTIVATED\n\n await userRepository.save(targetUser)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Inactivates a user account in the current domain. Use this mutation to disable a user, making them unable to log in.'\n })\n async inactivateUser(\n @Arg('username', { description: 'The username or email of the user to inactivate.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, domain } = context.state\n const userRepository = getRepository(User, tx)\n\n var targetUser: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!targetUser && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n targetUser = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!targetUser) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {\n throw new Error('User is not belong to domain')\n }\n\n /* 공동 오너(`DomainOwner` 테이블)도 도메인 관리자이므로 같은 보호를 받는다. */\n if (targetUser.userType == 'admin' || (await isDomainOwner(domain, targetUser, tx))) {\n throw new Error('Admin deactivation not allowed')\n }\n\n targetUser.status = UserStatus.INACTIVE\n\n await userRepository.save(targetUser)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n \"Resets a user's password to the default value. Use this mutation to restore access to a user who has forgotten their password.\"\n })\n async resetPasswordToDefault(\n @Arg('username', { description: 'The username or email of the user whose password to reset.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, domain } = context.state\n\n const { defaultPassword } = config.get('password')\n if (!defaultPassword) {\n throw new Error('No default password found')\n }\n\n const userRepository = getRepository(User, tx)\n\n var targetUser: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!targetUser && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n targetUser = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!targetUser) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {\n throw new Error('User is not belong to domain')\n }\n\n targetUser.salt = User.generateSalt()\n targetUser.password = User.encode(defaultPassword, targetUser.salt)\n\n await userRepository.save(targetUser)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => User, {\n description:\n \"Updates the roles assigned to a user. Use this mutation to change a user's permissions within the domain.\"\n })\n async updateUserRoles(\n @Arg('username', { description: 'The username or email of the user whose roles to update.' }) username: string,\n @Arg('availableRoles', type => [ObjectRef], { description: 'The list of roles that can be assigned to the user.' })\n availableRoles: ObjectRef[],\n @Arg('selectedRoles', type => [ObjectRef], { description: 'The list of roles to be assigned to the user.' })\n selectedRoles: ObjectRef[],\n @Ctx() context: ResolverContext\n ) {\n const { domain, tx } = context.state\n const userRepository = getRepository(User, tx)\n\n var user: User = await userRepository.findOne({\n where: { username },\n relations: ['domains', 'roles']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains', 'roles']\n })\n }\n\n if (!user) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (user.domains.map((d: Domain) => d.id).indexOf(domain.id) < 0) {\n throw new Error(`User is not belongs to current domain`)\n }\n\n const availableRoleIds: string[] = availableRoles.map((r: Role) => r.id)\n user.roles = user.roles.filter((r: Role) => availableRoleIds.indexOf(r.id) < 0)\n user.roles = user.roles.concat(selectedRoles as Role[])\n\n return await userRepository.save(user)\n }\n}\n"]}
1
+ {"version":3,"file":"user-mutation.js","sourceRoot":"","sources":["../../../server/service/user/user-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport { ILike, In, SelectQueryBuilder, EntityManager } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { Domain, getRepository, ObjectRef } from '@things-factory/shell'\n\nimport { deleteUser as commonDeleteUser, deleteUsers as commonDeleteUsers } from '../../controllers/delete-user.js'\nimport { buildDomainUsersQueryBuilder } from '../../utils/get-domain-users.js'\nimport { isDomainOwner, setPrimaryDomainOwner } from '../../utils/domain-ownership.js'\nimport { Role } from '../role/role.js'\nimport { User, UserStatus } from './user.js'\nimport { NewUser, UserPatch } from './user-types.js'\nimport { USERNAME_ALREADY_EXISTS, EMAIL_ALREADY_EXISTS, USERNAME_INVALID } from '../../constants/error-code.js'\n\n@Resolver(User)\nexport class UserMutation {\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => User, {\n description:\n 'Creates a new user in the system. Use this mutation to register a user with the specified credentials and domain information.'\n })\n async createUser(\n @Arg('user', { description: 'The input object containing new user information.' }) user: NewUser,\n @Ctx() context: ResolverContext\n ) {\n const { domain, tx } = context.state\n const { defaultPassword } = config.get('password')\n const { username, email } = user\n const userRepository = getRepository(User, tx)\n\n user.username = username.trim()\n user.email = email.trim()\n\n if (await userRepository.findOne({ where: { username: user.username } })) {\n throw new Error(context.t('error.' + USERNAME_ALREADY_EXISTS))\n }\n\n if (await userRepository.findOne({ where: { email: ILike(user.email) } })) {\n throw new Error(context.t('error.' + EMAIL_ALREADY_EXISTS))\n }\n\n if (!user.password && !defaultPassword) {\n throw new Error('initial password or default password should be supported.')\n }\n\n // TODO username은 다음 패턴을 따라야 한다. pattern=\"^[A-Za-z0-9._@-]+$\"\n if (!/^[A-Za-z0-9._@-]+$/.test(user.username)) {\n throw new Error(context.t('error.' + USERNAME_INVALID, { username: user.username }))\n }\n\n // consider if validation password rule is required\n /* check if password is following the rule */\n // User.validatePasswordByRule(user.password, context.lng)\n\n const salt = User.generateSalt()\n\n return await userRepository.save({\n creator: context.state.user,\n updater: context.state.user,\n ...user,\n domains: [domain],\n roles:\n user.roles && user.roles.length\n ? await getRepository(Role, tx).findBy({\n id: In(user.roles.map(role => role.id)),\n domain: { id: domain.id }\n })\n : [],\n salt,\n passwordUpdatedAt: new Date(),\n password: user.password ? User.encode(user.password, salt) : User.encode(defaultPassword, salt)\n })\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => User, {\n description:\n 'Updates the information of an existing user. Use this mutation to modify user profile details, such as name, email, or roles.'\n })\n async updateUser(\n @Arg('email', type => GraphQLEmailAddress, { description: 'The email address of the user to update.' })\n email: string,\n @Arg('patch', { description: 'The patch object containing updated user fields.' }) patch: UserPatch,\n @Ctx() context: ResolverContext\n ) {\n const { domain, user: updater, tx }: { domain: Domain; user: User; tx?: EntityManager } = context.state\n const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id, 'USER')\n const user: User = await qb\n .andWhere('LOWER(USER.email) = :email', { email: email?.toLowerCase().trim() || '' })\n .leftJoinAndSelect('USER.roles', 'ROLES')\n .leftJoinAndSelect('ROLES.domain', 'R_DOMAIN')\n .getOne()\n\n if (patch.roles) {\n patch.roles = await getRepository(Role, tx).find({\n where: { id: In(patch.roles.map((r: Partial<Role>) => r.id)) }\n })\n }\n\n if (patch.status && patch.status === 'activated') {\n user.status = UserStatus.ACTIVATED\n }\n\n return await getRepository(User, tx).save({\n ...user,\n ...patch,\n updater\n } as any)\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [User], {\n description:\n 'Updates or creates multiple users in a single operation. Use this mutation for batch processing of user records.'\n })\n async updateMultipleUser(\n @Arg('patches', type => [UserPatch], { description: 'An array of patch objects for batch update or creation.' })\n patches: UserPatch[],\n @Ctx() context: ResolverContext\n ) {\n const { domain, user, tx } = context.state\n const userRepo = getRepository(User, tx)\n\n let results = []\n const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')\n const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')\n\n if (_createRecords.length > 0) {\n for (let i = 0; i < _createRecords.length; i++) {\n const newRecord = _createRecords[i]\n\n // consider if validation password rule is required\n /* check if password is following the rule */\n // User.validatePasswordByRule(newRecord.password, context.lng)\n\n const salt = User.generateSalt()\n const result = await userRepo.save({\n ...(newRecord as any),\n domains: [domain],\n salt,\n password: User.encode(newRecord.password, salt),\n passwordUpdatedAt: new Date(),\n creator: user,\n updater: user\n })\n\n // repository api는 작동하지 않음.\n // await tx\n // .createQueryBuilder()\n // .insert()\n // .into('users_domains')\n // .values({\n // usersId: result.id,\n // domainsId: domain.id\n // })\n // .execute()\n\n results.push({ ...result, cuFlag: '+' })\n }\n }\n\n if (_updateRecords.length > 0) {\n for (let i = 0; i < _updateRecords.length; i++) {\n const updateRecord = _updateRecords[i]\n // consider if validation password rule is required\n /* check if password is following the rule */\n // User.validatePasswordByRule(updateRecord.password, context.lng)\n\n const user = await userRepo.findOne({ where: { id: updateRecord.id }, relations: ['domains'] })\n var domains = user.domains.find(d => d.id === domain.id) ? user.domains : [...user.domains, domain]\n\n const result = await userRepo.save({\n ...user,\n ...(updateRecord as any),\n domains,\n password: updateRecord.password ? User.encode(updateRecord.password, user.salt) : user.password,\n updater: user\n })\n\n if (!updateRecord.status) {\n continue\n }\n\n // const domain = await user.domain\n // if (!domain) {\n // continue\n // }\n\n // const domainId = domain.id\n // const domains = await user.domains\n // if (!domains.find(domain => domain.id == domainId)) {\n // await tx\n // .createQueryBuilder()\n // .insert()\n // .into('users_domains')\n // .values({\n // usersId: user.id,\n // domainsId: domain.id\n // })\n // .execute()\n // }\n\n results.push({ ...result, cuFlag: 'M' })\n }\n }\n\n return results\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Deletes a user from the system by their username. Use this mutation to permanently remove a user account and revoke their access.'\n })\n async deleteUser(\n @Arg('username', { description: 'The username of the user to delete.' }) username: string,\n @Ctx() context: ResolverContext\n ) {\n const { tx } = context.state\n\n await commonDeleteUser({ username }, tx)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Deletes multiple users from the system by their usernames. Use this mutation to remove several user accounts at once.'\n })\n async deleteUsers(\n @Arg('usernames', type => [String], { description: 'An array of usernames to delete.' }) usernames: string[],\n @Ctx() context: ResolverContext\n ) {\n const { tx } = context.state\n await commonDeleteUsers({ usernames }, tx)\n\n return true\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, {\n description:\n 'Invites an existing user to join the current domain. Use this mutation to add a user to a domain if they already exist in the system.'\n })\n async inviteUser(\n @Arg('username', { description: 'The username or email of the user to invite.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, tx } = context.state\n const userRepository = getRepository(User, tx)\n\n var invitee: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!invitee && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n invitee = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!invitee) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n const existingDomains: Domain[] = invitee.domains\n if (existingDomains.find((d: Domain) => d.id === domain.id)) {\n throw new Error(context.t('error.x already exists in y', { x: context.t('field.user'), y: domain.name }))\n }\n\n invitee.domains = [...existingDomains, domain]\n await userRepository.save(invitee)\n\n return true\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, {\n description:\n \"Removes a user from the current domain. Use this mutation to revoke a user's access to a specific domain without deleting their account.\"\n })\n async deleteDomainUser(\n @Arg('username', { description: 'The username or email of the user to remove from the domain.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, domain } = context.state\n const userRepository = getRepository(User, tx)\n\n var user: User = await userRepository.findOne({\n where: { username },\n relations: ['domains', 'roles']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains', 'roles']\n })\n }\n\n if (!user) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n const targetDomainIdx: number = user.domains.findIndex((userDomain: Domain) => userDomain.id === domain.id)\n if (targetDomainIdx < 0) {\n throw new Error(context.t('error.x is not a member of y', { x: user.name, y: domain.name }))\n }\n\n // Remove domain relation with user\n user.domains.splice(targetDomainIdx, 1)\n\n // Remove domain's roles that user has\n user.roles = user.roles.filter((role: Role) => role.domainId !== domain.id)\n\n await userRepository.save(user)\n\n return true\n }\n\n @Directive('@privilege(domainOwnerGranted: true, superUserGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Transfers domain ownership to another user. Use this mutation to assign the owner role to a different user within the domain.'\n })\n async transferOwner(\n @Arg('username', { description: 'The username or email of the user to transfer ownership to.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain, tx, user: actor } = context.state\n const userRepository = getRepository(User, tx)\n\n var user: User = await userRepository.findOne({\n where: { username },\n relations: ['domains', 'roles']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains', 'roles']\n })\n }\n\n if (!user) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (user.status !== UserStatus.ACTIVATED) {\n throw new Error('Only activated users are eligible to receive admin privileges.')\n }\n\n if (user.domains.map((d: Domain) => d.id).indexOf(domain.id) < 0) {\n throw new Error(`User is not belongs to current domain`)\n }\n\n /* 역할은 부모 도메인으로부터 상속될 수 있으므로 부여된 상속 역할도 자격으로 인정한다. */\n const grantableRoleDomainIds: string[] = [domain.id, domain.parentId].filter(Boolean)\n if (user.roles.filter((r: Role) => grantableRoleDomainIds.includes(r.domainId)).length == 0) {\n throw new Error(`Only users with at least one role in this domain are eligible to receive admin privileges.`)\n }\n\n /* 대표 오너 컬럼과 `DomainOwner` 테이블을 함께 갱신한다 (기존 오너 엔트리는 유지). */\n await setPrimaryDomainOwner(domain, user.id, actor, tx)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Activates a user account in the current domain. Use this mutation to enable a user who was previously inactive.'\n })\n async activateUser(\n @Arg('username', { description: 'The username or email of the user to activate.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, domain } = context.state\n const userRepository = getRepository(User, tx)\n\n var targetUser: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!targetUser && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n targetUser = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!targetUser) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {\n throw new Error('User is not belong to domain')\n }\n\n targetUser.failCount = 0\n targetUser.status = UserStatus.ACTIVATED\n\n await userRepository.save(targetUser)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n 'Inactivates a user account in the current domain. Use this mutation to disable a user, making them unable to log in.'\n })\n async inactivateUser(\n @Arg('username', { description: 'The username or email of the user to inactivate.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, domain } = context.state\n const userRepository = getRepository(User, tx)\n\n var targetUser: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!targetUser && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n targetUser = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!targetUser) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {\n throw new Error('User is not belong to domain')\n }\n\n /* 공동 오너(`DomainOwner` 테이블)도 도메인 관리자이므로 같은 보호를 받는다. */\n if (targetUser.userType == 'admin' || (await isDomainOwner(domain, targetUser, tx))) {\n throw new Error('Admin deactivation not allowed')\n }\n\n targetUser.status = UserStatus.INACTIVE\n\n await userRepository.save(targetUser)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, {\n description:\n \"Resets a user's password to the default value. Use this mutation to restore access to a user who has forgotten their password.\"\n })\n async resetPasswordToDefault(\n @Arg('username', { description: 'The username or email of the user whose password to reset.' }) username: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { tx, domain } = context.state\n\n const { defaultPassword } = config.get('password')\n if (!defaultPassword) {\n throw new Error('No default password found')\n }\n\n const userRepository = getRepository(User, tx)\n\n var targetUser: User = await userRepository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!targetUser && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n targetUser = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n if (!targetUser) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {\n throw new Error('User is not belong to domain')\n }\n\n targetUser.salt = User.generateSalt()\n targetUser.password = User.encode(defaultPassword, targetUser.salt)\n\n await userRepository.save(targetUser)\n\n return true\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => User, {\n description:\n \"Updates the roles assigned to a user. Use this mutation to change a user's permissions within the domain.\"\n })\n async updateUserRoles(\n @Arg('username', { description: 'The username or email of the user whose roles to update.' }) username: string,\n @Arg('availableRoles', type => [ObjectRef], { description: 'The list of roles that can be assigned to the user.' })\n availableRoles: ObjectRef[],\n @Arg('selectedRoles', type => [ObjectRef], { description: 'The list of roles to be assigned to the user.' })\n selectedRoles: ObjectRef[],\n @Ctx() context: ResolverContext\n ) {\n const { domain, tx } = context.state\n const userRepository = getRepository(User, tx)\n\n var user: User = await userRepository.findOne({\n where: { username },\n relations: ['domains', 'roles']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await userRepository.findOne({\n where: { email: ILike(username) },\n relations: ['domains', 'roles']\n })\n }\n\n if (!user) {\n throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))\n }\n\n if (user.domains.map((d: Domain) => d.id).indexOf(domain.id) < 0) {\n throw new Error(`User is not belongs to current domain`)\n }\n\n const availableRoleIds: string[] = availableRoles.map((r: Role) => r.id)\n user.roles = user.roles.filter((r: Role) => availableRoleIds.indexOf(r.id) < 0)\n user.roles = user.roles.concat(selectedRoles as Role[])\n\n return await userRepository.save(user)\n }\n}\n"]}
@@ -21,6 +21,7 @@ export declare class User {
21
21
  roles?: Role[];
22
22
  userType: string;
23
23
  reference: string;
24
+ credentialSerial?: number;
24
25
  salt: string;
25
26
  locale: string;
26
27
  ssoId: string;
@@ -13,6 +13,7 @@ const typeorm_1 = require("typeorm");
13
13
  const env_1 = require("@things-factory/env");
14
14
  const shell_1 = require("@things-factory/shell");
15
15
  const password_rule_js_1 = require("../../controllers/utils/password-rule.js");
16
+ const credential_serial_rule_js_1 = require("../../utils/credential-serial-rule.js");
16
17
  const account_lock_rule_js_1 = require("../../utils/account-lock-rule.js");
17
18
  const lock_duration_js_1 = require("../../utils/lock-duration.js");
18
19
  const auth_error_js_1 = require("../../errors/auth-error.js");
@@ -194,6 +195,19 @@ let User = User_1 = class User {
194
195
  errorCode: auth_error_js_1.AuthError.ERROR_CODES.USER_DELETED
195
196
  });
196
197
  }
198
+ /*
199
+ * 이 토큰이 이 주체의 **지금 세대**를 가리키나 (ADR-0085 결정 4).
200
+ *
201
+ * 서명이 멀쩡한 토큰을 거절할 수 있는 유일한 줄이다. 판정은 `credential-serial-rule` 한
202
+ * 곳에 있고, 기록된 번호가 없으면 통과시킨다 — 현장에 이미 나가 있는 토큰이 그 모양이다.
203
+ *
204
+ * 여기서는 **읽기만 한다.** 매 요청 지나는 경로다.
205
+ */
206
+ if (!(0, credential_serial_rule_js_1.credentialSerialMatches)(user, decoded)) {
207
+ throw new auth_error_js_1.AuthError({
208
+ errorCode: auth_error_js_1.AuthError.ERROR_CODES.USER_NOT_FOUND
209
+ });
210
+ }
197
211
  /* 설정이 없으면 `{}` 로 — 기본값 없이 구조 분해하면 `password` 를 설정하지 않은 배포에서
198
212
  이 줄이 던진다. `password-rule.ts` 도 같은 자리에서 대비를 두고 있다. */
199
213
  const { defaultPassword } = env_1.config.get('password', {});
@@ -229,10 +243,7 @@ let User = User_1 = class User {
229
243
  .select(['privilege.name AS privilege', 'privilege.category AS category'])
230
244
  .where('user.id = :userId', { userId: user.id });
231
245
  (0, shell_1.applyDomainInheritanceFilter)(queryBuilder, 'role', domain, shell_1.InheritedValueType.Include);
232
- const result = await queryBuilder
233
- .orderBy('privilege.category')
234
- .addOrderBy('privilege.name')
235
- .getRawMany();
246
+ const result = await queryBuilder.orderBy('privilege.category').addOrderBy('privilege.name').getRawMany();
236
247
  const distinct = result.reduce((acc, current) => {
237
248
  const last = acc[acc.length - 1];
238
249
  if (!last || last.privilege !== current.privilege || last.category !== current.category) {
@@ -316,6 +327,10 @@ tslib_1.__decorate([
316
327
  (0, type_graphql_1.Field)({ nullable: true, description: 'Reference information for the user (external system, etc.).' }),
317
328
  tslib_1.__metadata("design:type", String)
318
329
  ], User.prototype, "reference", void 0);
330
+ tslib_1.__decorate([
331
+ (0, typeorm_1.Column)({ type: 'int', nullable: true }),
332
+ tslib_1.__metadata("design:type", Number)
333
+ ], User.prototype, "credentialSerial", void 0);
319
334
  tslib_1.__decorate([
320
335
  (0, type_graphql_1.Directive)('@privilege(category: "security", privilege: "query", domainOwnerGranted: true)'),
321
336
  (0, typeorm_1.Column)({ nullable: true }),
@@ -1 +1 @@
1
- {"version":3,"file":"user.js","sourceRoot":"","sources":["../../../server/service/user/user.ts"],"names":[],"mappings":"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","sourcesContent":["// This file defines the User entity and its GraphQL schema for authentication and authorization.\n// It includes user fields, status enum, and utility methods for password and privilege management.\n\nimport crypto from 'crypto'\nimport jwt from 'jsonwebtoken'\nimport { Directive, Field, ID, ObjectType } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport {\n Column,\n CreateDateColumn,\n Entity,\n ILike,\n Index,\n JoinTable,\n ManyToMany,\n ManyToOne,\n OneToMany,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { applyDomainInheritanceFilter, Domain, getRepository, InheritedValueType } from '@things-factory/shell'\n\nimport { validatePasswordByRule } from '../../controllers/utils/password-rule.js'\nimport { isAccountLocked } from '../../utils/account-lock-rule.js'\nimport { lockDurationMinutes } from '../../utils/lock-duration.js'\nimport { AuthError } from '../../errors/auth-error.js'\nimport { SECRET } from '../../utils/get-secret.js'\nimport { Role } from '../role/role.js'\nimport { Privilege } from '../privilege/privilege.js'\nimport { WebAuthCredential } from '../web-auth-credential/web-auth-credential.js'\nimport { UsersAuthProviders } from '../users-auth-providers/users-auth-providers.js'\n\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\nconst sessionExpirySeconds = Number(config.get('session/expirySeconds')) || '7d'\n\nexport enum UserStatus {\n INACTIVE = 'inactive', // The user account is inactive and cannot log in.\n ACTIVATED = 'activated', // The user account is active and can log in.\n DELETED = 'deleted', // The user account has been deleted.\n LOCKED = 'locked', // The user account is locked due to security reasons.\n BANNED = 'banned', // The user account is banned from the system.\n PWD_RESET_REQUIRED = 'password_reset_required' // The user must reset their password before logging in.\n}\n\n@Entity()\n@Index('ix_user_0', (user: User) => [user.email], {\n unique: true\n})\n@Index('ix_user_1', (user: User) => [user.username], {\n unique: true,\n where: '\"username\" IS NOT NULL'\n})\n@ObjectType({\n description: 'Represents a user account in the system, including authentication, roles, and domain associations.'\n})\nexport class User {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the user.' })\n readonly id: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Unique username for the user.' })\n username: string\n\n @Column()\n @Field({ nullable: true, description: 'Full name of the user.' })\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Optional description or note about the user.' })\n description: string\n\n @ManyToMany(type => Domain)\n @JoinTable({ name: 'users_domains' })\n @Field(type => [Domain], { description: 'Domains (organizations, companies, etc.) the user belongs to.' })\n domains?: Domain[]\n\n @Column()\n @Field(type => GraphQLEmailAddress, { description: 'Email address of the user.' })\n email: string\n\n @Directive('@privilege(category: \"security\", privilege: \"query\", domainOwnerGranted: true)')\n @Column({\n nullable: true,\n type:\n DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'longtext'\n : DATABASE_TYPE == 'oracle'\n ? 'clob'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined\n })\n password: string // Hashed password for authentication\n\n @ManyToMany(type => Role, role => role.users)\n @JoinTable({ name: 'users_roles' })\n @Field(type => [Role], { description: 'Roles assigned to the user.' })\n roles?: Role[]\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Type or category of the user (e.g., user, application, appliance).' })\n userType: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Reference information for the user (external system, etc.).' })\n reference: string\n\n @Directive('@privilege(category: \"security\", privilege: \"query\", domainOwnerGranted: true)')\n @Column({ nullable: true })\n salt: string // Salt used for password hashing\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Locale or language preference for the user.' })\n locale: string\n\n @Directive('@privilege(category: \"security\", privilege: \"query\", domainOwnerGranted: true)')\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Single sign-on (SSO) identifier for federated authentication.' })\n ssoId: string\n\n @Column({\n type:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'enum'\n : DATABASE_TYPE == 'oracle'\n ? 'varchar2'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n enum:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? UserStatus : undefined,\n length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined : 32,\n default: UserStatus.INACTIVE\n })\n @Field(type => String, { description: 'Current status of the user account.' })\n status: UserStatus\n\n @Column({ type: 'smallint', default: 0 })\n failCount: number // Number of failed login attempts\n\n @Column({ nullable: true })\n passwordUpdatedAt: Date // Last time the password was updated\n\n /*\n * When the account was locked, so the lock can lift on its own.\n *\n * The decision of whether a lock is still in effect lives in one place —\n * `utils/account-lock-rule.ts` — because two callers ask: `signin`, and `checkAuth` on every\n * request. A row locked before this column existed has no value here, and the rule keeps such a\n * lock in effect rather than releasing it, so nothing opens quietly.\n */\n @Column({ nullable: true })\n @Field({\n nullable: true,\n description: 'When the account was locked. With the configured lock duration this says when it lifts.'\n })\n lockedAt: Date\n\n @Field({ nullable: true, description: 'Whether the user is the owner of the domain (not persisted in DB).' })\n owner: boolean /* should not be a column */\n\n @OneToMany(() => WebAuthCredential, credential => credential.user)\n credentials: WebAuthCredential[]\n\n @OneToMany(() => UsersAuthProviders, usersAuthProviders => usersAuthProviders.user)\n @Field(type => [UsersAuthProviders], {\n nullable: true,\n description: 'External authentication providers linked to the user.'\n })\n usersAuthProviders: UsersAuthProviders[]\n\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'User who created this user account.' })\n creator: User\n\n @RelationId((user: User) => user.creator)\n creatorId: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'User who last updated this user account.' })\n updater: User\n\n @RelationId((user: User) => user.updater)\n updaterId: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'Timestamp when the user account was created.' })\n createdAt: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'Timestamp when the user account was last updated.' })\n updatedAt: Date\n\n /* signing for jsonwebtoken */\n async sign(options?) {\n var { expiresIn = sessionExpirySeconds } = options || {}\n\n var user = {\n username: this.username || this.email,\n email: this.email\n }\n\n return await jwt.sign(user, SECRET, {\n expiresIn,\n issuer: 'hatiolab.com',\n subject: 'user'\n })\n }\n\n /* validate password through password rule */\n static validatePasswordByRule(password, lng) {\n validatePasswordByRule(password, lng)\n }\n\n /* generate salt */\n static generateSalt() {\n return crypto.randomBytes(16).toString('hex')\n }\n\n /* encode password */\n static encode(password: string, salt) {\n return crypto\n .createHmac('sha256', salt || SECRET)\n .update(password)\n .digest('base64')\n }\n\n /* verify password */\n static verify(hashed, password, salt) {\n return (\n hashed ==\n crypto\n .createHmac('sha256', salt || SECRET)\n .update(password)\n .digest('base64') ||\n hashed ==\n crypto\n .createHmac('sha1', salt || SECRET)\n .update(password)\n .digest('base64')\n )\n }\n\n static async checkAuthWithEmail(decoded) {\n if (!decoded?.email) {\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_FOUND\n })\n }\n\n const repository = getRepository(User)\n var user = await repository.findOne({\n where: { email: decoded.email },\n relations: ['domains'],\n cache: true\n })\n\n if (!user)\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_FOUND\n })\n else {\n switch (user.status) {\n case UserStatus.INACTIVE:\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_ACTIVATED,\n detail: {\n email: user.email\n }\n })\n case UserStatus.LOCKED:\n /*\n * 잠금이 아직 유효한지는 `utils/account-lock-rule.ts` 한 곳이 판정한다 — `signin` 과\n * 이 자리가 각자 판단하면 로그인은 되는데 요청은 거절되는 상태가 생긴다.\n *\n * **여기서는 행을 고치지 않는다.** 매 요청 지나는 경로라 쓰기를 얹지 않는다. 판정이\n * 「풀렸다」면 통과시키고, 행 정리(`ACTIVATED` · `failCount` 0 · `lockedAt` 비움)는\n * 다음 `signin` 이 한다.\n */\n if (isAccountLocked(user, new Date(), lockDurationMinutes())) {\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_LOCKED,\n detail: {\n email: user.email\n }\n })\n }\n break\n case UserStatus.DELETED:\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_DELETED\n })\n }\n\n return user\n }\n }\n\n static async checkAuth(decoded) {\n // id 는 하위호환성을 위해 단기적으로 유지함\n const { id, username } = decoded || {}\n\n if (!id && !username) {\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_FOUND\n })\n }\n\n const repository = getRepository(User)\n if (id) {\n var user = await repository.findOne({\n where: { id },\n relations: ['domains', 'credentials'],\n cache: true\n })\n } else {\n var user = await repository.findOne({\n where: { username },\n relations: ['domains', 'credentials'],\n cache: true\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await repository.findOne({\n where: {\n email: ILike(username)\n },\n relations: ['domains', 'credentials'],\n cache: true\n })\n }\n }\n\n if (!user)\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_FOUND\n })\n else {\n switch (user.status) {\n case UserStatus.INACTIVE:\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_ACTIVATED,\n detail: {\n email: user.email\n }\n })\n case UserStatus.LOCKED:\n /*\n * 잠금이 아직 유효한지는 `utils/account-lock-rule.ts` 한 곳이 판정한다 — `signin` 과\n * 이 자리가 각자 판단하면 로그인은 되는데 요청은 거절되는 상태가 생긴다.\n *\n * **여기서는 행을 고치지 않는다.** 매 요청 지나는 경로라 쓰기를 얹지 않는다. 판정이\n * 「풀렸다」면 통과시키고, 행 정리(`ACTIVATED` · `failCount` 0 · `lockedAt` 비움)는\n * 다음 `signin` 이 한다.\n */\n if (isAccountLocked(user, new Date(), lockDurationMinutes())) {\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_LOCKED,\n detail: {\n email: user.email\n }\n })\n }\n break\n case UserStatus.DELETED:\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_DELETED\n })\n }\n\n /* 설정이 없으면 `{}` 로 — 기본값 없이 구조 분해하면 `password` 를 설정하지 않은 배포에서\n 이 줄이 던진다. `password-rule.ts` 도 같은 자리에서 대비를 두고 있다. */\n const { defaultPassword } = config.get('password', {})\n\n if (defaultPassword && user.password === this.encode(defaultPassword, user.salt)) {\n user.status = UserStatus.PWD_RESET_REQUIRED\n }\n\n return user\n }\n }\n\n /**\n * 역할은 부모 도메인으로부터 상속되므로, 권한 판정도 현재 도메인과 부모 도메인에 정의된\n * 역할을 함께 본다 (상속 규약 단일 소스: `applyDomainInheritanceFilter`).\n * 부모 역할이 형제 자식 도메인으로 번지지 않도록 하는 것은 체크인 게이트(`getUserDomains`)의\n * 멤버십 조건이 담당한다.\n */\n static async hasPrivilege(privilege: string, category: string, domain: Domain, user: User): Promise<boolean> {\n const queryBuilder = getRepository(Privilege)\n .createQueryBuilder('privilege')\n .innerJoin('privilege.roles', 'role')\n .innerJoin('role.users', 'user')\n .where('privilege.category = :category', { category })\n .andWhere('privilege.name = :privilege', { privilege })\n .andWhere('user.id = :userId', { userId: user.id })\n\n applyDomainInheritanceFilter(queryBuilder, 'role', domain, InheritedValueType.Include)\n\n const result = await queryBuilder.getCount()\n\n return result > 0\n }\n\n static async getPrivilegesByDomain(user: User, domain: Domain): Promise<{ category: string; privilege: string }[]> {\n const queryBuilder = getRepository(User)\n .createQueryBuilder('user')\n .leftJoinAndSelect('user.roles', 'role')\n .leftJoinAndSelect('role.privileges', 'privilege')\n .select(['privilege.name AS privilege', 'privilege.category AS category'])\n .where('user.id = :userId', { userId: user.id })\n\n applyDomainInheritanceFilter(queryBuilder, 'role', domain, InheritedValueType.Include)\n\n const result = await queryBuilder\n .orderBy('privilege.category')\n .addOrderBy('privilege.name')\n .getRawMany()\n\n const distinct = result.reduce((acc, current) => {\n const last = acc[acc.length - 1]\n if (!last || last.privilege !== current.privilege || last.category !== current.category) {\n acc.push(current)\n }\n return acc\n }, [])\n\n return distinct\n }\n\n /**\n * 엔티티에서 조회 유틸을 정적으로 import 하면 순환 참조가 된다\n * (user → get-user-domains → checkin-domain-facts → granted-role → role → user).\n * 순환 참조 상태에서 엔티티 데코레이터가 평가되면 type-graphql 이 타입을 추론하지 못해\n * 모듈 로드 자체가 실패하므로, 호출 시점에 지연 로드한다.\n */\n static async getDomainsWithPrivilege(privilege: string, category: string, user: User) {\n const { getDomainsWithPrivilege } = await import('../../utils/get-user-domains.js')\n\n return getDomainsWithPrivilege(user, privilege, category)\n }\n}\n"]}
1
+ {"version":3,"file":"user.js","sourceRoot":"","sources":["../../../server/service/user/user.ts"],"names":[],"mappings":"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","sourcesContent":["// This file defines the User entity and its GraphQL schema for authentication and authorization.\n// It includes user fields, status enum, and utility methods for password and privilege management.\n\nimport crypto from 'crypto'\nimport jwt from 'jsonwebtoken'\nimport { Directive, Field, ID, ObjectType } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport {\n Column,\n CreateDateColumn,\n Entity,\n ILike,\n Index,\n JoinTable,\n ManyToMany,\n ManyToOne,\n OneToMany,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { applyDomainInheritanceFilter, Domain, getRepository, InheritedValueType } from '@things-factory/shell'\n\nimport { validatePasswordByRule } from '../../controllers/utils/password-rule.js'\nimport { credentialSerialMatches } from '../../utils/credential-serial-rule.js'\nimport { isAccountLocked } from '../../utils/account-lock-rule.js'\nimport { lockDurationMinutes } from '../../utils/lock-duration.js'\nimport { AuthError } from '../../errors/auth-error.js'\nimport { SECRET } from '../../utils/get-secret.js'\nimport { Role } from '../role/role.js'\nimport { Privilege } from '../privilege/privilege.js'\nimport { WebAuthCredential } from '../web-auth-credential/web-auth-credential.js'\nimport { UsersAuthProviders } from '../users-auth-providers/users-auth-providers.js'\n\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\nconst sessionExpirySeconds = Number(config.get('session/expirySeconds')) || '7d'\n\nexport enum UserStatus {\n INACTIVE = 'inactive', // The user account is inactive and cannot log in.\n ACTIVATED = 'activated', // The user account is active and can log in.\n DELETED = 'deleted', // The user account has been deleted.\n LOCKED = 'locked', // The user account is locked due to security reasons.\n BANNED = 'banned', // The user account is banned from the system.\n PWD_RESET_REQUIRED = 'password_reset_required' // The user must reset their password before logging in.\n}\n\n@Entity()\n@Index('ix_user_0', (user: User) => [user.email], {\n unique: true\n})\n@Index('ix_user_1', (user: User) => [user.username], {\n unique: true,\n where: '\"username\" IS NOT NULL'\n})\n@ObjectType({\n description: 'Represents a user account in the system, including authentication, roles, and domain associations.'\n})\nexport class User {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the user.' })\n readonly id: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Unique username for the user.' })\n username: string\n\n @Column()\n @Field({ nullable: true, description: 'Full name of the user.' })\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Optional description or note about the user.' })\n description: string\n\n @ManyToMany(type => Domain)\n @JoinTable({ name: 'users_domains' })\n @Field(type => [Domain], { description: 'Domains (organizations, companies, etc.) the user belongs to.' })\n domains?: Domain[]\n\n @Column()\n @Field(type => GraphQLEmailAddress, { description: 'Email address of the user.' })\n email: string\n\n @Directive('@privilege(category: \"security\", privilege: \"query\", domainOwnerGranted: true)')\n @Column({\n nullable: true,\n type:\n DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'longtext'\n : DATABASE_TYPE == 'oracle'\n ? 'clob'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined\n })\n password: string // Hashed password for authentication\n\n @ManyToMany(type => Role, role => role.users)\n @JoinTable({ name: 'users_roles' })\n @Field(type => [Role], { description: 'Roles assigned to the user.' })\n roles?: Role[]\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Type or category of the user (e.g., user, application, appliance).' })\n userType: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Reference information for the user (external system, etc.).' })\n reference: string\n\n /*\n * Which generation of credential this subject is on (ADR-0085 decision 3).\n *\n * Empty means this subject has never been through the new path, and every check that reads\n * it does nothing — which is how a deployment can take this release without cutting off the\n * appliances already carrying a year-long token. It fills in the first time a credential is\n * issued or revoked, and from then on the token has to carry the same number.\n *\n * It is not exposed as a @Field. A caller has no use for it, and the revoke screen already\n * says what it needs to say.\n */\n @Column({ type: 'int', nullable: true })\n credentialSerial?: number\n\n @Directive('@privilege(category: \"security\", privilege: \"query\", domainOwnerGranted: true)')\n @Column({ nullable: true })\n salt: string // Salt used for password hashing\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Locale or language preference for the user.' })\n locale: string\n\n @Directive('@privilege(category: \"security\", privilege: \"query\", domainOwnerGranted: true)')\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Single sign-on (SSO) identifier for federated authentication.' })\n ssoId: string\n\n @Column({\n type:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'enum'\n : DATABASE_TYPE == 'oracle'\n ? 'varchar2'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n enum:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? UserStatus : undefined,\n length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined : 32,\n default: UserStatus.INACTIVE\n })\n @Field(type => String, { description: 'Current status of the user account.' })\n status: UserStatus\n\n @Column({ type: 'smallint', default: 0 })\n failCount: number // Number of failed login attempts\n\n @Column({ nullable: true })\n passwordUpdatedAt: Date // Last time the password was updated\n\n /*\n * When the account was locked, so the lock can lift on its own.\n *\n * The decision of whether a lock is still in effect lives in one place —\n * `utils/account-lock-rule.ts` — because two callers ask: `signin`, and `checkAuth` on every\n * request. A row locked before this column existed has no value here, and the rule keeps such a\n * lock in effect rather than releasing it, so nothing opens quietly.\n */\n @Column({ nullable: true })\n @Field({\n nullable: true,\n description: 'When the account was locked. With the configured lock duration this says when it lifts.'\n })\n lockedAt: Date\n\n @Field({ nullable: true, description: 'Whether the user is the owner of the domain (not persisted in DB).' })\n owner: boolean /* should not be a column */\n\n @OneToMany(() => WebAuthCredential, credential => credential.user)\n credentials: WebAuthCredential[]\n\n @OneToMany(() => UsersAuthProviders, usersAuthProviders => usersAuthProviders.user)\n @Field(type => [UsersAuthProviders], {\n nullable: true,\n description: 'External authentication providers linked to the user.'\n })\n usersAuthProviders: UsersAuthProviders[]\n\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'User who created this user account.' })\n creator: User\n\n @RelationId((user: User) => user.creator)\n creatorId: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'User who last updated this user account.' })\n updater: User\n\n @RelationId((user: User) => user.updater)\n updaterId: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'Timestamp when the user account was created.' })\n createdAt: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'Timestamp when the user account was last updated.' })\n updatedAt: Date\n\n /* signing for jsonwebtoken */\n async sign(options?) {\n var { expiresIn = sessionExpirySeconds } = options || {}\n\n var user = {\n username: this.username || this.email,\n email: this.email\n }\n\n return await jwt.sign(user, SECRET, {\n expiresIn,\n issuer: 'hatiolab.com',\n subject: 'user'\n })\n }\n\n /* validate password through password rule */\n static validatePasswordByRule(password, lng) {\n validatePasswordByRule(password, lng)\n }\n\n /* generate salt */\n static generateSalt() {\n return crypto.randomBytes(16).toString('hex')\n }\n\n /* encode password */\n static encode(password: string, salt) {\n return crypto\n .createHmac('sha256', salt || SECRET)\n .update(password)\n .digest('base64')\n }\n\n /* verify password */\n static verify(hashed, password, salt) {\n return (\n hashed ==\n crypto\n .createHmac('sha256', salt || SECRET)\n .update(password)\n .digest('base64') ||\n hashed ==\n crypto\n .createHmac('sha1', salt || SECRET)\n .update(password)\n .digest('base64')\n )\n }\n\n static async checkAuthWithEmail(decoded) {\n if (!decoded?.email) {\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_FOUND\n })\n }\n\n const repository = getRepository(User)\n var user = await repository.findOne({\n where: { email: decoded.email },\n relations: ['domains'],\n cache: true\n })\n\n if (!user)\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_FOUND\n })\n else {\n switch (user.status) {\n case UserStatus.INACTIVE:\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_ACTIVATED,\n detail: {\n email: user.email\n }\n })\n case UserStatus.LOCKED:\n /*\n * 잠금이 아직 유효한지는 `utils/account-lock-rule.ts` 한 곳이 판정한다 — `signin` 과\n * 이 자리가 각자 판단하면 로그인은 되는데 요청은 거절되는 상태가 생긴다.\n *\n * **여기서는 행을 고치지 않는다.** 매 요청 지나는 경로라 쓰기를 얹지 않는다. 판정이\n * 「풀렸다」면 통과시키고, 행 정리(`ACTIVATED` · `failCount` 0 · `lockedAt` 비움)는\n * 다음 `signin` 이 한다.\n */\n if (isAccountLocked(user, new Date(), lockDurationMinutes())) {\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_LOCKED,\n detail: {\n email: user.email\n }\n })\n }\n break\n case UserStatus.DELETED:\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_DELETED\n })\n }\n\n return user\n }\n }\n\n static async checkAuth(decoded) {\n // id 는 하위호환성을 위해 단기적으로 유지함\n const { id, username } = decoded || {}\n\n if (!id && !username) {\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_FOUND\n })\n }\n\n const repository = getRepository(User)\n if (id) {\n var user = await repository.findOne({\n where: { id },\n relations: ['domains', 'credentials'],\n cache: true\n })\n } else {\n var user = await repository.findOne({\n where: { username },\n relations: ['domains', 'credentials'],\n cache: true\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await repository.findOne({\n where: {\n email: ILike(username)\n },\n relations: ['domains', 'credentials'],\n cache: true\n })\n }\n }\n\n if (!user)\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_FOUND\n })\n else {\n switch (user.status) {\n case UserStatus.INACTIVE:\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_ACTIVATED,\n detail: {\n email: user.email\n }\n })\n case UserStatus.LOCKED:\n /*\n * 잠금이 아직 유효한지는 `utils/account-lock-rule.ts` 한 곳이 판정한다 — `signin` 과\n * 이 자리가 각자 판단하면 로그인은 되는데 요청은 거절되는 상태가 생긴다.\n *\n * **여기서는 행을 고치지 않는다.** 매 요청 지나는 경로라 쓰기를 얹지 않는다. 판정이\n * 「풀렸다」면 통과시키고, 행 정리(`ACTIVATED` · `failCount` 0 · `lockedAt` 비움)는\n * 다음 `signin` 이 한다.\n */\n if (isAccountLocked(user, new Date(), lockDurationMinutes())) {\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_LOCKED,\n detail: {\n email: user.email\n }\n })\n }\n break\n case UserStatus.DELETED:\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_DELETED\n })\n }\n\n /*\n * 이 토큰이 이 주체의 **지금 세대**를 가리키나 (ADR-0085 결정 4).\n *\n * 서명이 멀쩡한 토큰을 거절할 수 있는 유일한 줄이다. 판정은 `credential-serial-rule` 한\n * 곳에 있고, 기록된 번호가 없으면 통과시킨다 — 현장에 이미 나가 있는 토큰이 그 모양이다.\n *\n * 여기서는 **읽기만 한다.** 매 요청 지나는 경로다.\n */\n if (!credentialSerialMatches(user, decoded)) {\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_FOUND\n })\n }\n\n /* 설정이 없으면 `{}` 로 — 기본값 없이 구조 분해하면 `password` 를 설정하지 않은 배포에서\n 이 줄이 던진다. `password-rule.ts` 도 같은 자리에서 대비를 두고 있다. */\n const { defaultPassword } = config.get('password', {})\n\n if (defaultPassword && user.password === this.encode(defaultPassword, user.salt)) {\n user.status = UserStatus.PWD_RESET_REQUIRED\n }\n\n return user\n }\n }\n\n /**\n * 역할은 부모 도메인으로부터 상속되므로, 권한 판정도 현재 도메인과 부모 도메인에 정의된\n * 역할을 함께 본다 (상속 규약 단일 소스: `applyDomainInheritanceFilter`).\n * 부모 역할이 형제 자식 도메인으로 번지지 않도록 하는 것은 체크인 게이트(`getUserDomains`)의\n * 멤버십 조건이 담당한다.\n */\n static async hasPrivilege(privilege: string, category: string, domain: Domain, user: User): Promise<boolean> {\n const queryBuilder = getRepository(Privilege)\n .createQueryBuilder('privilege')\n .innerJoin('privilege.roles', 'role')\n .innerJoin('role.users', 'user')\n .where('privilege.category = :category', { category })\n .andWhere('privilege.name = :privilege', { privilege })\n .andWhere('user.id = :userId', { userId: user.id })\n\n applyDomainInheritanceFilter(queryBuilder, 'role', domain, InheritedValueType.Include)\n\n const result = await queryBuilder.getCount()\n\n return result > 0\n }\n\n static async getPrivilegesByDomain(user: User, domain: Domain): Promise<{ category: string; privilege: string }[]> {\n const queryBuilder = getRepository(User)\n .createQueryBuilder('user')\n .leftJoinAndSelect('user.roles', 'role')\n .leftJoinAndSelect('role.privileges', 'privilege')\n .select(['privilege.name AS privilege', 'privilege.category AS category'])\n .where('user.id = :userId', { userId: user.id })\n\n applyDomainInheritanceFilter(queryBuilder, 'role', domain, InheritedValueType.Include)\n\n const result = await queryBuilder.orderBy('privilege.category').addOrderBy('privilege.name').getRawMany()\n\n const distinct = result.reduce((acc, current) => {\n const last = acc[acc.length - 1]\n if (!last || last.privilege !== current.privilege || last.category !== current.category) {\n acc.push(current)\n }\n return acc\n }, [])\n\n return distinct\n }\n\n /**\n * 엔티티에서 조회 유틸을 정적으로 import 하면 순환 참조가 된다\n * (user → get-user-domains → checkin-domain-facts → granted-role → role → user).\n * 순환 참조 상태에서 엔티티 데코레이터가 평가되면 type-graphql 이 타입을 추론하지 못해\n * 모듈 로드 자체가 실패하므로, 호출 시점에 지연 로드한다.\n */\n static async getDomainsWithPrivilege(privilege: string, category: string, user: User) {\n const { getDomainsWithPrivilege } = await import('../../utils/get-user-domains.js')\n\n return getDomainsWithPrivilege(user, privilege, category)\n }\n}\n"]}
@@ -1,5 +1,6 @@
1
- export declare function getInvitationEmailForm({ username, email, acceptUrl }: {
1
+ export declare function getInvitationEmailForm({ username, email, acceptUrl, code }: {
2
2
  username: any;
3
3
  email: any;
4
4
  acceptUrl: any;
5
+ code: any;
5
6
  }): string;
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getInvitationEmailForm = getInvitationEmailForm;
4
- function getInvitationEmailForm({ username, email, acceptUrl }) {
4
+ /*
5
+ * The link carries the token in its fragment, which needs JavaScript to read. The same token is
6
+ * printed as text so somebody whose mail client or browser will not follow it can paste it into
7
+ * the page instead.
8
+ */
9
+ function getInvitationEmailForm({ username, email, acceptUrl, code }) {
5
10
  return `
6
11
  <html lang="en">
7
12
  <head>
@@ -44,15 +49,22 @@ function getInvitationEmailForm({ username, email, acceptUrl }) {
44
49
 
45
50
  <!--body begin-->
46
51
  <p style="padding:10px 20px;line-height:1.5;font-size:16px">
47
- You're invited from Operato.
48
- Simply click the button below to verify your email address.
52
+ You have been invited to join Operato.
53
+ Use the button below to accept, and you will be asked for this email address to confirm
54
+ it reached the right person.
49
55
  <br />
50
56
  <a
51
57
  href="${acceptUrl}"
52
58
  style="display:inline-block;margin:10px 5px 5px 0;border-radius:7px;background-color:#22a6a7;padding:7px 15px;color:#fff;font-size:18px;text-decoration:none;text-transform:capitalize;"
53
- >Verify</a
59
+ >Accept invitation</a
54
60
  >
55
61
  </p>
62
+
63
+ <p style="padding:0 20px 10px 20px;line-height:1.5;font-size:13px;color:#666">
64
+ If the button does not work, open the invitation page and paste this code:
65
+ <br />
66
+ <span style="font-family:monospace;font-size:15px;color:#222">${code}</span>
67
+ </p>
56
68
  <!--body end-->
57
69
 
58
70
  <!--footer begin-->
@@ -1 +1 @@
1
- {"version":3,"file":"invitation-email.js","sourceRoot":"","sources":["../../server/templates/invitation-email.ts"],"names":[],"mappings":";;AAAA,wDAiEC;AAjED,SAAgB,sBAAsB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE;IACnE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAgCS,KAAK;;;;;;;;;;;;;;oBAcH,SAAS;;;;;;;;;;;;;;;;;GAiB1B,CAAA;AACH,CAAC","sourcesContent":["export function getInvitationEmailForm({ username, email, acceptUrl }) {\n return `\n <html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n \n <title>Invitation from Operato</title>\n <meta name=\"description\" content=\"Invitation\" />\n <meta name=\"author\" content=\"hatiolab\" />\n <meta name=\"google\" content=\"notranslate\"/>\n </head>\n\n <body>\n <div style=\"background-color:#f6f6f6\">\n <!--header begin-->\n <div style=\"background-color:#fff;padding:0 10px;border-top: 2px solid #394e64;\">\n <a href=\"#\" target=\"_blank\"\n ><img\n src=\"http://www.hatiolab.com/assets/img/logo-operato.png\"\n style=\"max-height:50px\"\n /></a>\n </div>\n <!--header end-->\n\n <!--title begin-->\n <div\n style=\"background-color:#22a6a7;padding:12px 10px 10px 10px;min-height:50px;\"\n >\n <img\n src=\"http://www.hatiolab.com/assets/img/icon-mail.png\"\n style=\"float:left;margin:0 10px 0 40px\"\n />\n <span style=\"display:block;color:#fff;font-size:20px\"\n >Hi ${email}!</span\n >\n <span style=\"display:block;color:#fff;font-size:34px;font-weight:bold\"\n >Verify your email</span\n >\n </div>\n <!--title end-->\n\n <!--body begin-->\n <p style=\"padding:10px 20px;line-height:1.5;font-size:16px\">\n You're invited from Operato. \n Simply click the button below to verify your email address.\n <br />\n <a\n href=\"${acceptUrl}\"\n style=\"display:inline-block;margin:10px 5px 5px 0;border-radius:7px;background-color:#22a6a7;padding:7px 15px;color:#fff;font-size:18px;text-decoration:none;text-transform:capitalize;\"\n >Verify</a\n >\n </p>\n <!--body end-->\n\n <!--footer begin-->\n <div\n style=\"background-color:#3d5874;padding:7px 20px 5px 20px;font-size:12px;color:#efefef\"\n >\n © Hatio, Lab. Inc. All rights reserved.\n </div>\n <!--footer end-->\n </div>\n </body>\n </html>\n `\n}\n"]}
1
+ {"version":3,"file":"invitation-email.js","sourceRoot":"","sources":["../../server/templates/invitation-email.ts"],"names":[],"mappings":";;AAKA,wDAwEC;AA7ED;;;;GAIG;AACH,SAAgB,sBAAsB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE;IACzE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAgCS,KAAK;;;;;;;;;;;;;;;oBAeH,SAAS;;;;;;;;;0EAS6C,IAAI;;;;;;;;;;;;;;GAc3E,CAAA;AACH,CAAC","sourcesContent":["/*\n * The link carries the token in its fragment, which needs JavaScript to read. The same token is\n * printed as text so somebody whose mail client or browser will not follow it can paste it into\n * the page instead.\n */\nexport function getInvitationEmailForm({ username, email, acceptUrl, code }) {\n return `\n <html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n \n <title>Invitation from Operato</title>\n <meta name=\"description\" content=\"Invitation\" />\n <meta name=\"author\" content=\"hatiolab\" />\n <meta name=\"google\" content=\"notranslate\"/>\n </head>\n\n <body>\n <div style=\"background-color:#f6f6f6\">\n <!--header begin-->\n <div style=\"background-color:#fff;padding:0 10px;border-top: 2px solid #394e64;\">\n <a href=\"#\" target=\"_blank\"\n ><img\n src=\"http://www.hatiolab.com/assets/img/logo-operato.png\"\n style=\"max-height:50px\"\n /></a>\n </div>\n <!--header end-->\n\n <!--title begin-->\n <div\n style=\"background-color:#22a6a7;padding:12px 10px 10px 10px;min-height:50px;\"\n >\n <img\n src=\"http://www.hatiolab.com/assets/img/icon-mail.png\"\n style=\"float:left;margin:0 10px 0 40px\"\n />\n <span style=\"display:block;color:#fff;font-size:20px\"\n >Hi ${email}!</span\n >\n <span style=\"display:block;color:#fff;font-size:34px;font-weight:bold\"\n >Verify your email</span\n >\n </div>\n <!--title end-->\n\n <!--body begin-->\n <p style=\"padding:10px 20px;line-height:1.5;font-size:16px\">\n You have been invited to join Operato.\n Use the button below to accept, and you will be asked for this email address to confirm\n it reached the right person.\n <br />\n <a\n href=\"${acceptUrl}\"\n style=\"display:inline-block;margin:10px 5px 5px 0;border-radius:7px;background-color:#22a6a7;padding:7px 15px;color:#fff;font-size:18px;text-decoration:none;text-transform:capitalize;\"\n >Accept invitation</a\n >\n </p>\n\n <p style=\"padding:0 20px 10px 20px;line-height:1.5;font-size:13px;color:#666\">\n If the button does not work, open the invitation page and paste this code:\n <br />\n <span style=\"font-family:monospace;font-size:15px;color:#222\">${code}</span>\n </p>\n <!--body end-->\n\n <!--footer begin-->\n <div\n style=\"background-color:#3d5874;padding:7px 20px 5px 20px;font-size:12px;color:#efefef\"\n >\n © Hatio, Lab. Inc. All rights reserved.\n </div>\n <!--footer end-->\n </div>\n </body>\n </html>\n `\n}\n"]}