@things-factory/auth-base 10.1.215 → 10.1.223

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 (132) hide show
  1. package/config/config.development.js +2 -4
  2. package/config/config.production.js +1 -2
  3. package/dist-client/actions/auth.d.ts +1 -0
  4. package/dist-client/actions/auth.js +6 -0
  5. package/dist-client/actions/auth.js.map +1 -1
  6. package/dist-client/reducers/auth.d.ts +8 -0
  7. package/dist-client/reducers/auth.js +8 -2
  8. package/dist-client/reducers/auth.js.map +1 -1
  9. package/dist-client/tsconfig.tsbuildinfo +1 -1
  10. package/dist-server/constants/error-code.d.ts +1 -0
  11. package/dist-server/constants/error-code.js +2 -1
  12. package/dist-server/constants/error-code.js.map +1 -1
  13. package/dist-server/controllers/auth.d.ts +0 -1
  14. package/dist-server/controllers/auth.js +0 -1
  15. package/dist-server/controllers/auth.js.map +1 -1
  16. package/dist-server/controllers/password-setup.d.ts +33 -0
  17. package/dist-server/controllers/password-setup.js +77 -0
  18. package/dist-server/controllers/password-setup.js.map +1 -0
  19. package/dist-server/controllers/reset-password.d.ts +8 -1
  20. package/dist-server/controllers/reset-password.js +21 -11
  21. package/dist-server/controllers/reset-password.js.map +1 -1
  22. package/dist-server/controllers/signin.js +10 -0
  23. package/dist-server/controllers/signin.js.map +1 -1
  24. package/dist-server/index.d.ts +1 -0
  25. package/dist-server/index.js +2 -0
  26. package/dist-server/index.js.map +1 -1
  27. package/dist-server/middlewares/jwt-authenticate-middleware.js +8 -25
  28. package/dist-server/middlewares/jwt-authenticate-middleware.js.map +1 -1
  29. package/dist-server/router/auth-public-process-router.js +3 -41
  30. package/dist-server/router/auth-public-process-router.js.map +1 -1
  31. package/dist-server/service/access-request/access-request.js +2 -2
  32. package/dist-server/service/access-request/access-request.js.map +1 -1
  33. package/dist-server/service/audit/audit-kinds.js +1 -0
  34. package/dist-server/service/audit/audit-kinds.js.map +1 -1
  35. package/dist-server/service/audit/audit-subscriber.d.ts +1 -7
  36. package/dist-server/service/audit/audit-subscriber.js +2 -33
  37. package/dist-server/service/audit/audit-subscriber.js.map +1 -1
  38. package/dist-server/service/audit/index.d.ts +1 -1
  39. package/dist-server/service/index.d.ts +2 -4
  40. package/dist-server/service/index.js +22 -27
  41. package/dist-server/service/index.js.map +1 -1
  42. package/dist-server/service/open-fields.js +0 -2
  43. package/dist-server/service/open-fields.js.map +1 -1
  44. package/dist-server/service/role/role-mutation.js +5 -0
  45. package/dist-server/service/role/role-mutation.js.map +1 -1
  46. package/dist-server/service/role/role-types.d.ts +1 -0
  47. package/dist-server/service/role/role-types.js +4 -0
  48. package/dist-server/service/role/role-types.js.map +1 -1
  49. package/dist-server/service/role/role.d.ts +10 -0
  50. package/dist-server/service/role/role.js +5 -0
  51. package/dist-server/service/role/role.js.map +1 -1
  52. package/dist-server/service/role-template/role-template-gap-resolver.d.ts +2 -2
  53. package/dist-server/service/role-template/role-template-gap-resolver.js +7 -13
  54. package/dist-server/service/role-template/role-template-gap-resolver.js.map +1 -1
  55. package/dist-server/service/role-template/role-template-mutation.d.ts +7 -4
  56. package/dist-server/service/role-template/role-template-mutation.js +15 -15
  57. package/dist-server/service/role-template/role-template-mutation.js.map +1 -1
  58. package/dist-server/service/role-template/role-template-types.d.ts +3 -3
  59. package/dist-server/service/role-template/role-template-types.js.map +1 -1
  60. package/dist-server/service/role-template/role-template.d.ts +14 -15
  61. package/dist-server/service/role-template/role-template.js +8 -8
  62. package/dist-server/service/role-template/role-template.js.map +1 -1
  63. package/dist-server/service/user/user-mutation.d.ts +27 -6
  64. package/dist-server/service/user/user-mutation.js +26 -114
  65. package/dist-server/service/user/user-mutation.js.map +1 -1
  66. package/dist-server/service/user/user-query.d.ts +3 -4
  67. package/dist-server/service/user/user-query.js +7 -32
  68. package/dist-server/service/user/user-query.js.map +1 -1
  69. package/dist-server/service/user/user-types.d.ts +6 -2
  70. package/dist-server/service/user/user-types.js +20 -9
  71. package/dist-server/service/user/user-types.js.map +1 -1
  72. package/dist-server/service/user/user.js +17 -8
  73. package/dist-server/service/user/user.js.map +1 -1
  74. package/dist-server/service/verification-token/verification-token.d.ts +1 -0
  75. package/dist-server/service/verification-token/verification-token.js +1 -0
  76. package/dist-server/service/verification-token/verification-token.js.map +1 -1
  77. package/dist-server/templates/reset-password-email.d.ts +10 -3
  78. package/dist-server/templates/reset-password-email.js +8 -4
  79. package/dist-server/templates/reset-password-email.js.map +1 -1
  80. package/dist-server/tsconfig.tsbuildinfo +1 -1
  81. package/dist-server/utils/verification-token-rule.js +3 -1
  82. package/dist-server/utils/verification-token-rule.js.map +1 -1
  83. package/package.json +2 -2
  84. package/tests/add-user-gates.test.ts +34 -0
  85. package/tests/list-inheritance-db.test.ts +0 -15
  86. package/tests/lock-recovery.test.ts +2 -2
  87. package/tests/open-doors.test.ts +2 -5
  88. package/tests/password-reset-rule-db.test.ts +17 -16
  89. package/tests/password-setup-db.test.ts +213 -0
  90. package/tests/picture-reducer.test.ts +25 -0
  91. package/tests/role-template-gap-db.test.ts +58 -7
  92. package/tests/role-template-seed-db.test.ts +5 -4
  93. package/tests/verification-token-db.test.ts +12 -7
  94. package/translations/en.json +2 -2
  95. package/translations/es.json +2 -2
  96. package/translations/ja.json +2 -2
  97. package/translations/ko.json +2 -2
  98. package/translations/ms.json +2 -2
  99. package/translations/zh.json +2 -2
  100. package/dist-server/controllers/invitation.d.ts +0 -75
  101. package/dist-server/controllers/invitation.js +0 -195
  102. package/dist-server/controllers/invitation.js.map +0 -1
  103. package/dist-server/controllers/utils/make-invitation-token.d.ts +0 -1
  104. package/dist-server/controllers/utils/make-invitation-token.js +0 -9
  105. package/dist-server/controllers/utils/make-invitation-token.js.map +0 -1
  106. package/dist-server/controllers/utils/save-invitation-token.d.ts +0 -5
  107. package/dist-server/controllers/utils/save-invitation-token.js +0 -12
  108. package/dist-server/controllers/utils/save-invitation-token.js.map +0 -1
  109. package/dist-server/service/invitation/index.d.ts +0 -5
  110. package/dist-server/service/invitation/index.js +0 -9
  111. package/dist-server/service/invitation/index.js.map +0 -1
  112. package/dist-server/service/invitation/invitation-mutation.d.ts +0 -20
  113. package/dist-server/service/invitation/invitation-mutation.js +0 -109
  114. package/dist-server/service/invitation/invitation-mutation.js.map +0 -1
  115. package/dist-server/service/invitation/invitation-query.d.ts +0 -23
  116. package/dist-server/service/invitation/invitation-query.js +0 -89
  117. package/dist-server/service/invitation/invitation-query.js.map +0 -1
  118. package/dist-server/service/invitation/invitation-types.d.ts +0 -5
  119. package/dist-server/service/invitation/invitation-types.js +0 -21
  120. package/dist-server/service/invitation/invitation-types.js.map +0 -1
  121. package/dist-server/service/invitation/invitation.d.ts +0 -26
  122. package/dist-server/service/invitation/invitation.js +0 -142
  123. package/dist-server/service/invitation/invitation.js.map +0 -1
  124. package/dist-server/templates/invitation-email.d.ts +0 -6
  125. package/dist-server/templates/invitation-email.js +0 -82
  126. package/dist-server/templates/invitation-email.js.map +0 -1
  127. package/dist-server/utils/invitation-state.d.ts +0 -52
  128. package/dist-server/utils/invitation-state.js +0 -76
  129. package/dist-server/utils/invitation-state.js.map +0 -1
  130. package/tests/invitation-db.test.ts +0 -416
  131. package/tests/invitation-route.test.ts +0 -116
  132. package/tests/invitation-state.test.ts +0 -92
@@ -71,12 +71,8 @@ let UserQuery = class UserQuery {
71
71
  throw new Error(`Failed to get current user information.`);
72
72
  }
73
73
  }
74
- async checkResettablePasswordToDefault(context) {
75
- const { defaultPassword } = env_1.config.get('password');
76
- return Boolean(defaultPassword);
77
- }
78
- /** The user screen shows the reset button only where `resetPasswordToDefault` would not refuse (`password-reset-rule.ts`). */
79
- async mayResetPasswordToDefault(id, context) {
74
+ /** The user screen offers a password setup link only where `issuePasswordSetupLink` would not refuse (`password-reset-rule.ts`). */
75
+ async mayIssuePasswordSetupLink(id, context) {
80
76
  const { domain } = context.state;
81
77
  const target = await (0, shell_1.getRepository)(user_js_1.User).findOne({ where: { id }, relations: ['domains'] });
82
78
  if (!target?.domains?.find(userDomain => userDomain.id === domain.id)) {
@@ -84,10 +80,7 @@ let UserQuery = class UserQuery {
84
80
  }
85
81
  return !(await (0, password_reset_rule_js_1.passwordResetRefusal)(target, domain));
86
82
  }
87
- async checkDefaultPassword(context) {
88
- const { defaultPassword } = env_1.config.get('password');
89
- return Boolean(defaultPassword);
90
- }
83
+ /** Asked only by the add-user popup, before inviteUser / createUser - so it is gated as they are. */
91
84
  async checkUserExistence(email) {
92
85
  return Boolean(await (0, shell_1.getRepository)(user_js_1.User).count({ where: { email: (0, typeorm_1.ILike)(email) } }));
93
86
  }
@@ -167,37 +160,19 @@ tslib_1.__decorate([
167
160
  tslib_1.__metadata("design:paramtypes", [Object]),
168
161
  tslib_1.__metadata("design:returntype", Promise)
169
162
  ], UserQuery.prototype, "checkUserBelongsDomain", null);
170
- tslib_1.__decorate([
171
- (0, type_graphql_1.Query)(returns => Boolean, {
172
- description: 'Determines whether the system provides a default password when creating a new user.'
173
- }),
174
- tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
175
- tslib_1.__metadata("design:type", Function),
176
- tslib_1.__metadata("design:paramtypes", [Object]),
177
- tslib_1.__metadata("design:returntype", Promise)
178
- ], UserQuery.prototype, "checkResettablePasswordToDefault", null);
179
163
  tslib_1.__decorate([
180
164
  (0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)'),
181
165
  (0, type_graphql_1.Query)(returns => Boolean, {
182
- description: "Whether this domain may reset the user's password to the default: the user works in no other domain, owns no domain and is not an administrator."
166
+ description: "Whether this domain may issue the user a password setup link: the user works in no other domain, owns no domain and is not an administrator."
183
167
  }),
184
- tslib_1.__param(0, (0, type_graphql_1.Arg)('id', { description: 'The id of the user whose password would be reset.' })),
168
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('id', { description: 'The id of the user who would be issued the link.' })),
185
169
  tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
186
170
  tslib_1.__metadata("design:type", Function),
187
171
  tslib_1.__metadata("design:paramtypes", [String, Object]),
188
172
  tslib_1.__metadata("design:returntype", Promise)
189
- ], UserQuery.prototype, "mayResetPasswordToDefault", null);
173
+ ], UserQuery.prototype, "mayIssuePasswordSetupLink", null);
190
174
  tslib_1.__decorate([
191
- (0, type_graphql_1.Query)(returns => Boolean, {
192
- description: 'Checks if the system is configured to provide a default password for new users.'
193
- }),
194
- tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
195
- tslib_1.__metadata("design:type", Function),
196
- tslib_1.__metadata("design:paramtypes", [Object]),
197
- tslib_1.__metadata("design:returntype", Promise)
198
- ], UserQuery.prototype, "checkDefaultPassword", null);
199
- tslib_1.__decorate([
200
- (0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "query")'),
175
+ (0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)'),
201
176
  (0, type_graphql_1.Query)(returns => Boolean, {
202
177
  description: 'Checks if a user with the given email address exists in the system.'
203
178
  }),
@@ -1 +1 @@
1
- {"version":3,"file":"user-query.js","sourceRoot":"","sources":["../../../server/service/user/user-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport { ILike, SelectQueryBuilder } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { getRepository, ListParam, getQueryBuilderFromListParams } from '@things-factory/shell'\n\nimport { whereAssignableBy } from '../../utils/assignable-roles.js'\nimport { checkUserBelongsDomain } from '../../utils/check-user-belongs-domain.js'\nimport { findDomainOwnerUserIds } from '../../utils/domain-ownership.js'\nimport { buildDomainUsersQueryBuilder } from '../../utils/get-domain-users.js'\nimport { passwordResetRefusal } from '../../utils/password-reset-rule.js'\nimport { Role } from '../role/role.js'\nimport { User } from './user.js'\nimport { PasswordRule, UserList } from './user-types.js'\n\nconst passwordRule = config.get('password') || {\n lowerCase: true,\n upperCase: true,\n digit: true,\n specialCharacter: true,\n allowRepeat: false,\n useTightPattern: true,\n useLoosePattern: false,\n tightCharacterLength: 8,\n looseCharacterLength: 15\n}\n\n@Resolver(User)\nexport class UserQuery {\n @Query(returns => PasswordRule, {\n description:\n 'Retrieves the current password rule configuration for the system, such as required character types and minimum length.'\n })\n passwordRule(@Ctx() context: ResolverContext): PasswordRule {\n return passwordRule\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => User, {\n description: 'Fetches a user by their email address within the current domain.'\n })\n async user(\n @Arg('email', type => GraphQLEmailAddress, { description: 'The email address of the user to fetch.' })\n email: string,\n @Ctx() context: ResolverContext\n ): Promise<User> {\n const { domain } = context.state\n\n const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id, 'USER')\n qb.andWhere(`LOWER(USER.email) = :email`, { email: email.toLowerCase().trim() })\n\n return qb.getOne()\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => UserList, {\n description: 'Fetches a list of users based on provided search parameters within the current domain.'\n })\n async users(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<UserList> {\n const { domain } = context.state\n\n const qb = getQueryBuilderFromListParams({\n repository: getRepository(User),\n params,\n alias: 'USER',\n searchables: ['name', 'email', 'description']\n })\n\n qb.select().andWhere(qb => {\n const subQuery = qb\n .subQuery()\n .select('USERS_DOMAINS.users_id')\n .from('users_domains', 'USERS_DOMAINS')\n .where('USERS_DOMAINS.domains_id = :domainId', { domainId: domain.id })\n .getQuery()\n\n return 'USER.id IN ' + subQuery\n })\n\n const [items, total] = await qb.getManyAndCount()\n\n /* 오너는 여러 명일 수 있다 — 대표 오너 컬럼만 보면 공동 오너가 일반 사용자로 표시된다. */\n const ownerIds: Set<string> = await findDomainOwnerUserIds(domain)\n\n const foundUsers: User[] = items.map((item: User) => {\n item.owner = ownerIds.has(item.id)\n return item\n })\n\n return { items: foundUsers, total }\n }\n\n @Query(returns => Boolean, {\n description: 'Checks if the current authenticated user belongs to the current domain.'\n })\n async checkUserBelongsDomain(@Ctx() context: ResolverContext): Promise<Boolean> {\n const { user, domain } = context.state\n\n if (user) {\n return await checkUserBelongsDomain(domain, user)\n } else {\n throw new Error(`Failed to get current user information.`)\n }\n }\n\n @Query(returns => Boolean, {\n description: 'Determines whether the system provides a default password when creating a new user.'\n })\n async checkResettablePasswordToDefault(@Ctx() context: ResolverContext): Promise<Boolean> {\n const { defaultPassword } = config.get('password')\n\n return Boolean(defaultPassword)\n }\n\n /** The user screen shows the reset button only where `resetPasswordToDefault` would not refuse (`password-reset-rule.ts`). */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Query(returns => Boolean, {\n description:\n \"Whether this domain may reset the user's password to the default: the user works in no other domain, owns no domain and is not an administrator.\"\n })\n async mayResetPasswordToDefault(\n @Arg('id', { description: 'The id of the user whose password would be reset.' }) id: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain } = context.state\n\n const target: User = await getRepository(User).findOne({ where: { id }, relations: ['domains'] })\n if (!target?.domains?.find(userDomain => userDomain.id === domain.id)) {\n return false\n }\n\n return !(await passwordResetRefusal(target, domain))\n }\n\n @Query(returns => Boolean, {\n description: 'Checks if the system is configured to provide a default password for new users.'\n })\n async checkDefaultPassword(@Ctx() context: ResolverContext): Promise<Boolean> {\n const { defaultPassword } = config.get('password')\n\n return Boolean(defaultPassword)\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\")')\n @Query(returns => Boolean, {\n description: 'Checks if a user with the given email address exists in the system.'\n })\n async checkUserExistence(\n @Arg('email', type => GraphQLEmailAddress, { description: 'The email address to check for existence.' })\n email: string\n ): Promise<Boolean> {\n return Boolean(await getRepository(User).count({ where: { email: ILike(email) } }))\n }\n\n @FieldResolver({ description: 'Returns the username of the user, or their email if username is not set.' })\n async username(@Root() user: User) {\n return user.username || user.email\n }\n\n @FieldResolver({ description: 'Returns the list of domains the user belongs to.' })\n async domains(@Root() user: User) {\n return (\n await getRepository(User).findOne({\n where: { id: user.id },\n relations: ['domains']\n })\n ).domains\n }\n\n /**\n * 이 도메인에서 본 역할 — 이 도메인이 부여할 수 있는 범위 안의 것만.\n *\n * 범위는 셋이다: 내 도메인의 역할, 부모 1단의 역할(`userRoles` · `myRoles` 와 같다),\n * 파트너가 이 도메인에 grant 한 역할(`grantedRoles`). 사용자 상세(`user-detail`)가 부여할\n * 수 있다고 내놓는 목록이 정확히 이 셋이고, 체크 상태를 이 필드로 채운다. 셋째를 빼면\n * 파트너 역할이 체크 안 된 채 그려지고, 저장하면 **떼어진다**.\n *\n * 예전에는 사용자가 가진 역할을 **전부** 돌려줬다. 도메인 A 와 B 에 함께 속한 사람을\n * A 의 사용자 목록으로 읽으면 B 의 역할 이름이 보였다(`role-query.ts` 의 ③ 과 같은 결함).\n */\n @FieldResolver({\n description: 'Returns the roles the user holds that the current domain can assign: its own, its parent domain\\'s, and roles granted to it by partners.'\n })\n async roles(@Root() user: User, @Ctx() context: ResolverContext): Promise<Role[]> {\n const { domain } = context.state\n\n const qb = getRepository(Role)\n .createQueryBuilder('ROLE')\n .innerJoin('ROLE.users', 'USER', 'USER.id = :userId', { userId: user.id })\n\n /* The same scope the roles are assigned in (utils/assignable-roles.ts) - what is shown is what can be saved. */\n return await whereAssignableBy(qb, 'ROLE', domain).getMany()\n }\n\n @FieldResolver({ description: 'Returns the user who last updated this user record.' })\n async updater(@Root() user: User): Promise<User> {\n return await getRepository(User).findOneBy({ id: user.updaterId })\n }\n\n @FieldResolver({ description: 'Returns the user who originally created this user record.' })\n async creator(@Root() user: User): Promise<User> {\n return await getRepository(User).findOneBy({ id: user.creatorId })\n }\n}\n"]}
1
+ {"version":3,"file":"user-query.js","sourceRoot":"","sources":["../../../server/service/user/user-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport { ILike, SelectQueryBuilder } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { getRepository, ListParam, getQueryBuilderFromListParams } from '@things-factory/shell'\n\nimport { whereAssignableBy } from '../../utils/assignable-roles.js'\nimport { checkUserBelongsDomain } from '../../utils/check-user-belongs-domain.js'\nimport { findDomainOwnerUserIds } from '../../utils/domain-ownership.js'\nimport { buildDomainUsersQueryBuilder } from '../../utils/get-domain-users.js'\nimport { passwordResetRefusal } from '../../utils/password-reset-rule.js'\nimport { Role } from '../role/role.js'\nimport { User } from './user.js'\nimport { PasswordRule, UserList } from './user-types.js'\n\nconst passwordRule = config.get('password') || {\n lowerCase: true,\n upperCase: true,\n digit: true,\n specialCharacter: true,\n allowRepeat: false,\n useTightPattern: true,\n useLoosePattern: false,\n tightCharacterLength: 8,\n looseCharacterLength: 15\n}\n\n@Resolver(User)\nexport class UserQuery {\n @Query(returns => PasswordRule, {\n description:\n 'Retrieves the current password rule configuration for the system, such as required character types and minimum length.'\n })\n passwordRule(@Ctx() context: ResolverContext): PasswordRule {\n return passwordRule\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => User, {\n description: 'Fetches a user by their email address within the current domain.'\n })\n async user(\n @Arg('email', type => GraphQLEmailAddress, { description: 'The email address of the user to fetch.' })\n email: string,\n @Ctx() context: ResolverContext\n ): Promise<User> {\n const { domain } = context.state\n\n const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id, 'USER')\n qb.andWhere(`LOWER(USER.email) = :email`, { email: email.toLowerCase().trim() })\n\n return qb.getOne()\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => UserList, {\n description: 'Fetches a list of users based on provided search parameters within the current domain.'\n })\n async users(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<UserList> {\n const { domain } = context.state\n\n const qb = getQueryBuilderFromListParams({\n repository: getRepository(User),\n params,\n alias: 'USER',\n searchables: ['name', 'email', 'description']\n })\n\n qb.select().andWhere(qb => {\n const subQuery = qb\n .subQuery()\n .select('USERS_DOMAINS.users_id')\n .from('users_domains', 'USERS_DOMAINS')\n .where('USERS_DOMAINS.domains_id = :domainId', { domainId: domain.id })\n .getQuery()\n\n return 'USER.id IN ' + subQuery\n })\n\n const [items, total] = await qb.getManyAndCount()\n\n /* 오너는 여러 명일 수 있다 — 대표 오너 컬럼만 보면 공동 오너가 일반 사용자로 표시된다. */\n const ownerIds: Set<string> = await findDomainOwnerUserIds(domain)\n\n const foundUsers: User[] = items.map((item: User) => {\n item.owner = ownerIds.has(item.id)\n return item\n })\n\n return { items: foundUsers, total }\n }\n\n @Query(returns => Boolean, {\n description: 'Checks if the current authenticated user belongs to the current domain.'\n })\n async checkUserBelongsDomain(@Ctx() context: ResolverContext): Promise<Boolean> {\n const { user, domain } = context.state\n\n if (user) {\n return await checkUserBelongsDomain(domain, user)\n } else {\n throw new Error(`Failed to get current user information.`)\n }\n }\n\n /** The user screen offers a password setup link only where `issuePasswordSetupLink` would not refuse (`password-reset-rule.ts`). */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Query(returns => Boolean, {\n description:\n \"Whether this domain may issue the user a password setup link: the user works in no other domain, owns no domain and is not an administrator.\"\n })\n async mayIssuePasswordSetupLink(\n @Arg('id', { description: 'The id of the user who would be issued the link.' }) id: string,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n const { domain } = context.state\n\n const target: User = await getRepository(User).findOne({ where: { id }, relations: ['domains'] })\n if (!target?.domains?.find(userDomain => userDomain.id === domain.id)) {\n return false\n }\n\n return !(await passwordResetRefusal(target, domain))\n }\n\n /** Asked only by the add-user popup, before inviteUser / createUser - so it is gated as they are. */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Query(returns => Boolean, {\n description: 'Checks if a user with the given email address exists in the system.'\n })\n async checkUserExistence(\n @Arg('email', type => GraphQLEmailAddress, { description: 'The email address to check for existence.' })\n email: string\n ): Promise<Boolean> {\n return Boolean(await getRepository(User).count({ where: { email: ILike(email) } }))\n }\n\n @FieldResolver({ description: 'Returns the username of the user, or their email if username is not set.' })\n async username(@Root() user: User) {\n return user.username || user.email\n }\n\n @FieldResolver({ description: 'Returns the list of domains the user belongs to.' })\n async domains(@Root() user: User) {\n return (\n await getRepository(User).findOne({\n where: { id: user.id },\n relations: ['domains']\n })\n ).domains\n }\n\n /**\n * 이 도메인에서 본 역할 — 이 도메인이 부여할 수 있는 범위 안의 것만.\n *\n * 범위는 셋이다: 내 도메인의 역할, 부모 1단의 역할(`userRoles` · `myRoles` 와 같다),\n * 파트너가 이 도메인에 grant 한 역할(`grantedRoles`). 사용자 상세(`user-detail`)가 부여할\n * 수 있다고 내놓는 목록이 정확히 이 셋이고, 체크 상태를 이 필드로 채운다. 셋째를 빼면\n * 파트너 역할이 체크 안 된 채 그려지고, 저장하면 **떼어진다**.\n *\n * 예전에는 사용자가 가진 역할을 **전부** 돌려줬다. 도메인 A 와 B 에 함께 속한 사람을\n * A 의 사용자 목록으로 읽으면 B 의 역할 이름이 보였다(`role-query.ts` 의 ③ 과 같은 결함).\n */\n @FieldResolver({\n description: 'Returns the roles the user holds that the current domain can assign: its own, its parent domain\\'s, and roles granted to it by partners.'\n })\n async roles(@Root() user: User, @Ctx() context: ResolverContext): Promise<Role[]> {\n const { domain } = context.state\n\n const qb = getRepository(Role)\n .createQueryBuilder('ROLE')\n .innerJoin('ROLE.users', 'USER', 'USER.id = :userId', { userId: user.id })\n\n /* The same scope the roles are assigned in (utils/assignable-roles.ts) - what is shown is what can be saved. */\n return await whereAssignableBy(qb, 'ROLE', domain).getMany()\n }\n\n @FieldResolver({ description: 'Returns the user who last updated this user record.' })\n async updater(@Root() user: User): Promise<User> {\n return await getRepository(User).findOneBy({ id: user.updaterId })\n }\n\n @FieldResolver({ description: 'Returns the user who originally created this user record.' })\n async creator(@Root() user: User): Promise<User> {\n return await getRepository(User).findOneBy({ id: user.creatorId })\n }\n}\n"]}
@@ -16,7 +16,6 @@ export declare class NewUser {
16
16
  name: string;
17
17
  description?: string;
18
18
  email: string;
19
- password?: string;
20
19
  userType?: string;
21
20
  roles?: ObjectRef[];
22
21
  }
@@ -26,12 +25,17 @@ export declare class UserPatch {
26
25
  domains?: [ObjectRef];
27
26
  description?: string;
28
27
  email?: string;
29
- password?: string;
30
28
  status?: string;
31
29
  roles?: ObjectRef[];
32
30
  userType?: string;
33
31
  cuFlag?: string;
34
32
  }
33
+ /** What issuing a password setup link answers (ADR-0161). The link is here only when it was not mailed - and only now. */
34
+ export declare class PasswordSetupLink {
35
+ link?: string;
36
+ mailedTo?: string;
37
+ expiresAt: Date;
38
+ }
35
39
  export declare class UserList {
36
40
  items: User[];
37
41
  total: number;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.UserList = exports.UserPatch = exports.NewUser = exports.PasswordRule = void 0;
3
+ exports.UserList = exports.PasswordSetupLink = exports.UserPatch = exports.NewUser = exports.PasswordRule = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
6
  const graphql_scalars_1 = require("graphql-scalars");
@@ -70,10 +70,6 @@ tslib_1.__decorate([
70
70
  (0, type_graphql_1.Field)(type => graphql_scalars_1.GraphQLEmailAddress, { description: 'Email address of the user.' }),
71
71
  tslib_1.__metadata("design:type", String)
72
72
  ], NewUser.prototype, "email", void 0);
73
- tslib_1.__decorate([
74
- (0, type_graphql_1.Field)({ nullable: true, description: 'Password for the user account.' }),
75
- tslib_1.__metadata("design:type", String)
76
- ], NewUser.prototype, "password", void 0);
77
73
  tslib_1.__decorate([
78
74
  (0, type_graphql_1.Field)({ nullable: true, description: 'Type or category of the user (e.g., admin, operator, guest).' }),
79
75
  tslib_1.__metadata("design:type", String)
@@ -108,10 +104,6 @@ tslib_1.__decorate([
108
104
  (0, type_graphql_1.Field)(type => graphql_scalars_1.GraphQLEmailAddress, { nullable: true, description: 'Email address of the user.' }),
109
105
  tslib_1.__metadata("design:type", String)
110
106
  ], UserPatch.prototype, "email", void 0);
111
- tslib_1.__decorate([
112
- (0, type_graphql_1.Field)({ nullable: true, description: 'Password for the user account.' }),
113
- tslib_1.__metadata("design:type", String)
114
- ], UserPatch.prototype, "password", void 0);
115
107
  tslib_1.__decorate([
116
108
  (0, type_graphql_1.Field)({ nullable: true, description: 'Status of the user account (e.g., active, inactive).' }),
117
109
  tslib_1.__metadata("design:type", String)
@@ -131,6 +123,25 @@ tslib_1.__decorate([
131
123
  exports.UserPatch = UserPatch = tslib_1.__decorate([
132
124
  (0, type_graphql_1.InputType)({ description: 'Input type for updating (patching) an existing user account.' })
133
125
  ], UserPatch);
126
+ /** What issuing a password setup link answers (ADR-0161). The link is here only when it was not mailed - and only now. */
127
+ let PasswordSetupLink = class PasswordSetupLink {
128
+ };
129
+ exports.PasswordSetupLink = PasswordSetupLink;
130
+ tslib_1.__decorate([
131
+ (0, type_graphql_1.Field)({ nullable: true, description: 'The link, when it was not mailed. It is not kept and cannot be read again.' }),
132
+ tslib_1.__metadata("design:type", String)
133
+ ], PasswordSetupLink.prototype, "link", void 0);
134
+ tslib_1.__decorate([
135
+ (0, type_graphql_1.Field)({ nullable: true, description: 'The address the link was mailed to, when it was mailed.' }),
136
+ tslib_1.__metadata("design:type", String)
137
+ ], PasswordSetupLink.prototype, "mailedTo", void 0);
138
+ tslib_1.__decorate([
139
+ (0, type_graphql_1.Field)({ description: 'When the link stops working.' }),
140
+ tslib_1.__metadata("design:type", Date)
141
+ ], PasswordSetupLink.prototype, "expiresAt", void 0);
142
+ exports.PasswordSetupLink = PasswordSetupLink = tslib_1.__decorate([
143
+ (0, type_graphql_1.ObjectType)({ description: 'The password setup link just issued: mailed, or returned once to be handed over.' })
144
+ ], PasswordSetupLink);
134
145
  let UserList = class UserList {
135
146
  };
136
147
  exports.UserList = UserList;
@@ -1 +1 @@
1
- {"version":3,"file":"user-types.js","sourceRoot":"","sources":["../../../server/service/user/user-types.ts"],"names":[],"mappings":"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","sourcesContent":["import { ObjectType, InputType, Field, ID, Int } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport { ObjectRef } from '@things-factory/shell'\nimport { User } from './user.js'\n\n// This file defines GraphQL types for user management, including password rules, user creation, user patching, and user list results.\n\n@ObjectType({\n description: 'Password rule configuration for user accounts, specifying required character types and patterns.'\n})\nexport class PasswordRule {\n @Field({ nullable: true, description: 'Whether at least one lowercase letter is required.' })\n lowerCase?: boolean\n\n @Field({ nullable: true, description: 'Whether at least one uppercase letter is required.' })\n upperCase?: boolean\n\n @Field({ nullable: true, description: 'Whether at least one digit is required.' })\n digit?: boolean\n\n @Field({ nullable: true, description: 'Whether at least one special character is required.' })\n specialCharacter?: boolean\n\n @Field({ nullable: true, description: 'Whether repeated characters are allowed in the password.' })\n allowRepeat?: boolean\n\n @Field({ nullable: true, description: 'Whether to enforce a strict password pattern.' })\n useTightPattern?: boolean\n\n @Field({ nullable: true, description: 'Whether to allow a loose password pattern.' })\n useLoosePattern?: boolean\n\n @Field({ nullable: true, description: 'Minimum length required for a strict password pattern.' })\n tightCharacterLength?: number\n\n @Field({ nullable: true, description: 'Minimum length required for a loose password pattern.' })\n looseCharacterLength?: number\n}\n\n@InputType({ description: 'Input type for creating a new user account.' })\nexport class NewUser {\n @Field({ description: 'Unique username for the user.' })\n username: string\n\n @Field({ description: 'Full name of the user.' })\n name: string\n\n @Field({ nullable: true, description: 'Optional description or note about the user.' })\n description?: string\n\n @Field(type => GraphQLEmailAddress, { description: 'Email address of the user.' })\n email: string\n\n @Field({ nullable: true, description: 'Password for the user account.' })\n password?: string\n\n @Field({ nullable: true, description: 'Type or category of the user (e.g., admin, operator, guest).' })\n userType?: string\n\n @Field(type => [ObjectRef], { nullable: true, description: 'Roles assigned to the user.' })\n roles?: ObjectRef[]\n}\n\n@InputType({ description: 'Input type for updating (patching) an existing user account.' })\nexport class UserPatch {\n @Field(type => ID, { nullable: true, description: 'Unique identifier of the user to update.' })\n id?: string\n\n @Field({ nullable: true, description: 'Full name of the user.' })\n name?: string\n\n @Field(type => [ObjectRef], { nullable: true, description: 'Domains associated with the user.' })\n domains?: [ObjectRef]\n\n @Field({ nullable: true, description: 'Optional description or note about the user.' })\n description?: string\n\n @Field(type => GraphQLEmailAddress, { nullable: true, description: 'Email address of the user.' })\n email?: string\n\n @Field({ nullable: true, description: 'Password for the user account.' })\n password?: string\n\n @Field({ nullable: true, description: 'Status of the user account (e.g., active, inactive).' })\n status?: string\n\n @Field(type => [ObjectRef], { nullable: true, description: 'Roles assigned to the user.' })\n roles?: ObjectRef[]\n\n @Field({ nullable: true, description: 'Type or category of the user (e.g., admin, operator, guest).' })\n userType?: string\n\n @Field({ nullable: true, description: 'Change flag for tracking updates (e.g., C/U/D for create/update/delete).' })\n cuFlag?: string\n}\n\n@ObjectType({ description: 'A paginated list of users and the total count.' })\nexport class UserList {\n @Field(type => [User], { nullable: true, description: 'List of user objects.' })\n items: User[]\n\n @Field(type => Int, { nullable: true, description: 'Total number of users matching the query.' })\n total: number\n}\n"]}
1
+ {"version":3,"file":"user-types.js","sourceRoot":"","sources":["../../../server/service/user/user-types.ts"],"names":[],"mappings":";;;;AAAA,+CAAoE;AACpE,qDAAqD;AACrD,iDAAiD;AACjD,uCAAgC;AAEhC,sIAAsI;AAK/H,IAAM,YAAY,GAAlB,MAAM,YAAY;CA2BxB,CAAA;AA3BY,oCAAY;AAEvB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC;;+CAC1E;AAGnB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC;;+CAC1E;AAGnB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,yCAAyC,EAAE,CAAC;;2CACnE;AAGf;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qDAAqD,EAAE,CAAC;;sDACpE;AAG1B;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0DAA0D,EAAE,CAAC;;iDAC9E;AAGrB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;;qDAC/D;AAGzB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;;qDAC5D;AAGzB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wDAAwD,EAAE,CAAC;;0DACpE;AAG7B;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;;0DACnE;uBA1BlB,YAAY;IAHxB,IAAA,yBAAU,EAAC;QACV,WAAW,EAAE,kGAAkG;KAChH,CAAC;GACW,YAAY,CA2BxB;AAGM,IAAM,OAAO,GAAb,MAAM,OAAO;CAkBnB,CAAA;AAlBY,0BAAO;AAElB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC;;yCACxC;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,wBAAwB,EAAE,CAAC;;qCACrC;AAGZ;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;;4CACnE;AAGpB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,qCAAmB,EAAE,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;;sCACrE;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC;;yCACtF;AAGjB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,iBAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;;sCACxE;kBAjBR,OAAO;IADnB,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,6CAA6C,EAAE,CAAC;GAC7D,OAAO,CAkBnB;AAGM,IAAM,SAAS,GAAf,MAAM,SAAS;CA2BrB,CAAA;AA3BY,8BAAS;AAEpB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC;;qCACpF;AAGX;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wBAAwB,EAAE,CAAC;;uCACpD;AAGb;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,iBAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,mCAAmC,EAAE,CAAC;;0CAC5E;AAGrB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;;8CACnE;AAGpB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,qCAAmB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;;wCACpF;AAGd;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sDAAsD,EAAE,CAAC;;yCAChF;AAGf;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,iBAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;;wCACxE;AAGnB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC;;2CACtF;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0EAA0E,EAAE,CAAC;;yCACpG;oBA1BJ,SAAS;IADrB,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC;GAC9E,SAAS,CA2BrB;AAED,0HAA0H;AAEnH,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAS7B,CAAA;AATY,8CAAiB;AAE5B;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4EAA4E,EAAE,CAAC;;+CACxG;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC;;mDACjF;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;sCAC5C,IAAI;oDAAA;4BARJ,iBAAiB;IAD7B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,kFAAkF,EAAE,CAAC;GACnG,iBAAiB,CAS7B;AAGM,IAAM,QAAQ,GAAd,MAAM,QAAQ;CAMpB,CAAA;AANY,4BAAQ;AAEnB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,cAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;;uCACnE;AAGb;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,2CAA2C,EAAE,CAAC;;uCACpF;mBALF,QAAQ;IADpB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;GACjE,QAAQ,CAMpB","sourcesContent":["import { ObjectType, InputType, Field, ID, Int } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport { ObjectRef } from '@things-factory/shell'\nimport { User } from './user.js'\n\n// This file defines GraphQL types for user management, including password rules, user creation, user patching, and user list results.\n\n@ObjectType({\n description: 'Password rule configuration for user accounts, specifying required character types and patterns.'\n})\nexport class PasswordRule {\n @Field({ nullable: true, description: 'Whether at least one lowercase letter is required.' })\n lowerCase?: boolean\n\n @Field({ nullable: true, description: 'Whether at least one uppercase letter is required.' })\n upperCase?: boolean\n\n @Field({ nullable: true, description: 'Whether at least one digit is required.' })\n digit?: boolean\n\n @Field({ nullable: true, description: 'Whether at least one special character is required.' })\n specialCharacter?: boolean\n\n @Field({ nullable: true, description: 'Whether repeated characters are allowed in the password.' })\n allowRepeat?: boolean\n\n @Field({ nullable: true, description: 'Whether to enforce a strict password pattern.' })\n useTightPattern?: boolean\n\n @Field({ nullable: true, description: 'Whether to allow a loose password pattern.' })\n useLoosePattern?: boolean\n\n @Field({ nullable: true, description: 'Minimum length required for a strict password pattern.' })\n tightCharacterLength?: number\n\n @Field({ nullable: true, description: 'Minimum length required for a loose password pattern.' })\n looseCharacterLength?: number\n}\n\n@InputType({ description: 'Input type for creating a new user account.' })\nexport class NewUser {\n @Field({ description: 'Unique username for the user.' })\n username: string\n\n @Field({ description: 'Full name of the user.' })\n name: string\n\n @Field({ nullable: true, description: 'Optional description or note about the user.' })\n description?: string\n\n @Field(type => GraphQLEmailAddress, { description: 'Email address of the user.' })\n email: string\n\n @Field({ nullable: true, description: 'Type or category of the user (e.g., admin, operator, guest).' })\n userType?: string\n\n @Field(type => [ObjectRef], { nullable: true, description: 'Roles assigned to the user.' })\n roles?: ObjectRef[]\n}\n\n@InputType({ description: 'Input type for updating (patching) an existing user account.' })\nexport class UserPatch {\n @Field(type => ID, { nullable: true, description: 'Unique identifier of the user to update.' })\n id?: string\n\n @Field({ nullable: true, description: 'Full name of the user.' })\n name?: string\n\n @Field(type => [ObjectRef], { nullable: true, description: 'Domains associated with the user.' })\n domains?: [ObjectRef]\n\n @Field({ nullable: true, description: 'Optional description or note about the user.' })\n description?: string\n\n @Field(type => GraphQLEmailAddress, { nullable: true, description: 'Email address of the user.' })\n email?: string\n\n @Field({ nullable: true, description: 'Status of the user account (e.g., active, inactive).' })\n status?: string\n\n @Field(type => [ObjectRef], { nullable: true, description: 'Roles assigned to the user.' })\n roles?: ObjectRef[]\n\n @Field({ nullable: true, description: 'Type or category of the user (e.g., admin, operator, guest).' })\n userType?: string\n\n @Field({ nullable: true, description: 'Change flag for tracking updates (e.g., C/U/D for create/update/delete).' })\n cuFlag?: string\n}\n\n/** What issuing a password setup link answers (ADR-0161). The link is here only when it was not mailed - and only now. */\n@ObjectType({ description: 'The password setup link just issued: mailed, or returned once to be handed over.' })\nexport class PasswordSetupLink {\n @Field({ nullable: true, description: 'The link, when it was not mailed. It is not kept and cannot be read again.' })\n link?: string\n\n @Field({ nullable: true, description: 'The address the link was mailed to, when it was mailed.' })\n mailedTo?: string\n\n @Field({ description: 'When the link stops working.' })\n expiresAt: Date\n}\n\n@ObjectType({ description: 'A paginated list of users and the total count.' })\nexport class UserList {\n @Field(type => [User], { nullable: true, description: 'List of user objects.' })\n items: User[]\n\n @Field(type => Int, { nullable: true, description: 'Total number of users matching the query.' })\n total: number\n}\n"]}
@@ -32,7 +32,7 @@ var UserStatus;
32
32
  UserStatus["DELETED"] = "deleted";
33
33
  UserStatus["LOCKED"] = "locked";
34
34
  UserStatus["BANNED"] = "banned";
35
- UserStatus["PWD_RESET_REQUIRED"] = "password_reset_required"; // The user must reset their password before logging in.
35
+ UserStatus["PWD_RESET_REQUIRED"] = "password_reset_required"; // Waiting for the password its person sets through a setup link (ADR-0161).
36
36
  })(UserStatus || (exports.UserStatus = UserStatus = {}));
37
37
  let User = User_1 = class User {
38
38
  /* signing for jsonwebtoken */
@@ -40,7 +40,12 @@ let User = User_1 = class User {
40
40
  var { expiresIn = sessionExpirySeconds } = options || {};
41
41
  var user = {
42
42
  username: this.username || this.email,
43
- email: this.email
43
+ email: this.email,
44
+ /*
45
+ * The credential's generation (ADR-0085 decision 4), once the person has one. A password setup link moves it on,
46
+ * so a token signed before the link stops working (ADR-0161 decision 4).
47
+ */
48
+ ...(this.credentialSerial !== null && this.credentialSerial !== undefined ? { cs: this.credentialSerial } : {})
44
49
  };
45
50
  return await jsonwebtoken_1.default.sign(user, get_secret_js_1.SECRET, {
46
51
  expiresIn,
@@ -123,6 +128,11 @@ let User = User_1 = class User {
123
128
  throw new auth_error_js_1.AuthError({
124
129
  errorCode: auth_error_js_1.AuthError.ERROR_CODES.USER_DELETED
125
130
  });
131
+ /* Waiting for the password its person sets through a setup link - nothing opens until then (ADR-0161). */
132
+ case UserStatus.PWD_RESET_REQUIRED:
133
+ throw new auth_error_js_1.AuthError({
134
+ errorCode: auth_error_js_1.AuthError.ERROR_CODES.PASSWORD_SETUP_PENDING
135
+ });
126
136
  }
127
137
  return user;
128
138
  }
@@ -194,6 +204,11 @@ let User = User_1 = class User {
194
204
  throw new auth_error_js_1.AuthError({
195
205
  errorCode: auth_error_js_1.AuthError.ERROR_CODES.USER_DELETED
196
206
  });
207
+ /* Waiting for the password its person sets through a setup link - nothing opens until then (ADR-0161). */
208
+ case UserStatus.PWD_RESET_REQUIRED:
209
+ throw new auth_error_js_1.AuthError({
210
+ errorCode: auth_error_js_1.AuthError.ERROR_CODES.PASSWORD_SETUP_PENDING
211
+ });
197
212
  }
198
213
  /*
199
214
  * 이 토큰이 이 주체의 **지금 세대**를 가리키나 (ADR-0085 결정 4).
@@ -208,12 +223,6 @@ let User = User_1 = class User {
208
223
  errorCode: auth_error_js_1.AuthError.ERROR_CODES.USER_NOT_FOUND
209
224
  });
210
225
  }
211
- /* 설정이 없으면 `{}` 로 — 기본값 없이 구조 분해하면 `password` 를 설정하지 않은 배포에서
212
- 이 줄이 던진다. `password-rule.ts` 도 같은 자리에서 대비를 두고 있다. */
213
- const { defaultPassword } = env_1.config.get('password', {});
214
- if (defaultPassword && user.password === this.encode(defaultPassword, user.salt)) {
215
- user.status = UserStatus.PWD_RESET_REQUIRED;
216
- }
217
226
  return user;
218
227
  }
219
228
  }
@@ -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 { 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
+ {"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' // Waiting for the password its person sets through a setup link (ADR-0161).\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 * The credential's generation (ADR-0085 decision 4), once the person has one. A password setup link moves it on,\n * so a token signed before the link stops working (ADR-0161 decision 4).\n */\n ...(this.credentialSerial !== null && this.credentialSerial !== undefined ? { cs: this.credentialSerial } : {})\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 /* Waiting for the password its person sets through a setup link - nothing opens until then (ADR-0161). */\n case UserStatus.PWD_RESET_REQUIRED:\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.PASSWORD_SETUP_PENDING\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 /* Waiting for the password its person sets through a setup link - nothing opens until then (ADR-0161). */\n case UserStatus.PWD_RESET_REQUIRED:\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.PASSWORD_SETUP_PENDING\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 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,6 +1,7 @@
1
1
  export declare enum VerificationTokenType {
2
2
  ACTIVATION = "activation",// Token to activate a new user account.
3
3
  PASSWORD_RESET = "password-reset",// Token to reset a user's password.
4
+ PASSWORD_SETUP = "password-setup",// Token an administrator issues so the person sets their own password (ADR-0161).
4
5
  UNLOCK = "unlock",// Token to unlock a locked user account.
5
6
  REQUEST_ACCESS_TOKEN = "access-token"
6
7
  }
@@ -11,6 +11,7 @@ var VerificationTokenType;
11
11
  (function (VerificationTokenType) {
12
12
  VerificationTokenType["ACTIVATION"] = "activation";
13
13
  VerificationTokenType["PASSWORD_RESET"] = "password-reset";
14
+ VerificationTokenType["PASSWORD_SETUP"] = "password-setup";
14
15
  VerificationTokenType["UNLOCK"] = "unlock";
15
16
  VerificationTokenType["REQUEST_ACCESS_TOKEN"] = "access-token"; // Token to request a new access token.
16
17
  })(VerificationTokenType || (exports.VerificationTokenType = VerificationTokenType = {}));
@@ -1 +1 @@
1
- {"version":3,"file":"verification-token.js","sourceRoot":"","sources":["../../../server/service/verification-token/verification-token.ts"],"names":[],"mappings":";;;;AAAA,qCAA2F;AAC3F,6CAA4C;AAC5C,+CAAoD;AACpD,MAAM,SAAS,GAAG,YAAM,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AAC7C,MAAM,aAAa,GAAG,SAAS,CAAC,IAAI,CAAA;AAEpC,IAAY,qBAKX;AALD,WAAY,qBAAqB;IAC/B,kDAAyB,CAAA;IACzB,0DAAiC,CAAA;IACjC,0CAAiB,CAAA;IACjB,8DAAqC,CAAA,CAAC,uCAAuC;AAC/E,CAAC,EALW,qBAAqB,qCAArB,qBAAqB,QAKhC;AAIM,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAoD7B,CAAA;AApDY,8CAAiB;AAG5B;IAFC,IAAA,uBAAa,GAAE;IACf,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;;iDACvE;AAMd;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,KAAK;KAChB,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,yEAAyE,EAAE,CAAC;;gDACrF;AAoBb;IAlBC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,KAAK;QACf,IAAI,EACF,aAAa,IAAI,UAAU,IAAI,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS;YACnF,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,aAAa,IAAI,QAAQ;gBACzB,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,aAAa,IAAI,OAAO;oBACxB,CAAC,CAAC,UAAU;oBACZ,CAAC,CAAC,SAAS;QACnB,IAAI,EACF,aAAa,IAAI,UAAU,IAAI,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS;YACnF,CAAC,CAAC,qBAAqB;YACvB,CAAC,CAAC,SAAS;QACf,MAAM,EAAE,aAAa,IAAI,UAAU,IAAI,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;QAC9G,OAAO,EAAE,qBAAqB,CAAC,UAAU;KAC1C,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,uCAAuC,EAAE,CAAC;;+CACrC;AAM3B;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;;qDACtE;AAQlB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,gCAAgC,EAAE,CAAC;sCAC7D,IAAI;oDAAA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,2CAA2C,EAAE,CAAC;sCACzD,IAAI;oDAAA;AAIf;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;sCAC9D,IAAI;oDAAA;4BAnDJ,iBAAiB;IAF7B,IAAA,gBAAM,GAAE;IACR,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,oFAAoF,EAAE,CAAC;GACrG,iBAAiB,CAoD7B","sourcesContent":["import { Column, CreateDateColumn, Entity, PrimaryColumn, UpdateDateColumn } from 'typeorm'\nimport { config } from '@things-factory/env'\nimport { ObjectType, Field, ID } from 'type-graphql'\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\nexport enum VerificationTokenType {\n ACTIVATION = 'activation', // Token to activate a new user account.\n PASSWORD_RESET = 'password-reset', // Token to reset a user's password.\n UNLOCK = 'unlock', // Token to unlock a locked user account.\n REQUEST_ACCESS_TOKEN = 'access-token' // Token to request a new access token.\n}\n\n@Entity()\n@ObjectType({ description: 'A token used for verifying user actions like account activation or password reset.' })\nexport class VerificationToken {\n @PrimaryColumn()\n @Field(type => ID, { description: 'The ID of the user to whom this token belongs.' })\n userId: string\n\n @Column({\n nullable: false\n })\n @Field({ description: 'The sha256 of the token sent in the link. The token itself is not kept.' })\n token: string\n\n @Column({\n nullable: false,\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'\n ? VerificationTokenType\n : undefined,\n length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined : 32,\n default: VerificationTokenType.ACTIVATION\n })\n @Field({ description: 'The type of action this token is for.' })\n type: VerificationTokenType\n\n @Column({\n nullable: true\n })\n @Field({ nullable: true, description: 'Supplementary data associated with the token.' })\n suppliment: string\n\n /**\n * When the token stops working. Written once, with the token — see `utils/verification-token-rule.ts`.\n * A row without it reads as expired.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When this token stops working.' })\n expiresAt?: Date\n\n @CreateDateColumn()\n @Field({ description: 'The timestamp when the token was created.' })\n createdAt: Date\n\n @UpdateDateColumn()\n @Field({ description: 'The timestamp when the token was last updated.' })\n updatedAt: Date\n}\n"]}
1
+ {"version":3,"file":"verification-token.js","sourceRoot":"","sources":["../../../server/service/verification-token/verification-token.ts"],"names":[],"mappings":";;;;AAAA,qCAA2F;AAC3F,6CAA4C;AAC5C,+CAAoD;AACpD,MAAM,SAAS,GAAG,YAAM,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AAC7C,MAAM,aAAa,GAAG,SAAS,CAAC,IAAI,CAAA;AAEpC,IAAY,qBAMX;AAND,WAAY,qBAAqB;IAC/B,kDAAyB,CAAA;IACzB,0DAAiC,CAAA;IACjC,0DAAiC,CAAA;IACjC,0CAAiB,CAAA;IACjB,8DAAqC,CAAA,CAAC,uCAAuC;AAC/E,CAAC,EANW,qBAAqB,qCAArB,qBAAqB,QAMhC;AAIM,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAoD7B,CAAA;AApDY,8CAAiB;AAG5B;IAFC,IAAA,uBAAa,GAAE;IACf,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;;iDACvE;AAMd;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,KAAK;KAChB,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,yEAAyE,EAAE,CAAC;;gDACrF;AAoBb;IAlBC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,KAAK;QACf,IAAI,EACF,aAAa,IAAI,UAAU,IAAI,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS;YACnF,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,aAAa,IAAI,QAAQ;gBACzB,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,aAAa,IAAI,OAAO;oBACxB,CAAC,CAAC,UAAU;oBACZ,CAAC,CAAC,SAAS;QACnB,IAAI,EACF,aAAa,IAAI,UAAU,IAAI,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS;YACnF,CAAC,CAAC,qBAAqB;YACvB,CAAC,CAAC,SAAS;QACf,MAAM,EAAE,aAAa,IAAI,UAAU,IAAI,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;QAC9G,OAAO,EAAE,qBAAqB,CAAC,UAAU;KAC1C,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,uCAAuC,EAAE,CAAC;;+CACrC;AAM3B;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;;qDACtE;AAQlB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,gCAAgC,EAAE,CAAC;sCAC7D,IAAI;oDAAA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,2CAA2C,EAAE,CAAC;sCACzD,IAAI;oDAAA;AAIf;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;sCAC9D,IAAI;oDAAA;4BAnDJ,iBAAiB;IAF7B,IAAA,gBAAM,GAAE;IACR,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,oFAAoF,EAAE,CAAC;GACrG,iBAAiB,CAoD7B","sourcesContent":["import { Column, CreateDateColumn, Entity, PrimaryColumn, UpdateDateColumn } from 'typeorm'\nimport { config } from '@things-factory/env'\nimport { ObjectType, Field, ID } from 'type-graphql'\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\nexport enum VerificationTokenType {\n ACTIVATION = 'activation', // Token to activate a new user account.\n PASSWORD_RESET = 'password-reset', // Token to reset a user's password.\n PASSWORD_SETUP = 'password-setup', // Token an administrator issues so the person sets their own password (ADR-0161).\n UNLOCK = 'unlock', // Token to unlock a locked user account.\n REQUEST_ACCESS_TOKEN = 'access-token' // Token to request a new access token.\n}\n\n@Entity()\n@ObjectType({ description: 'A token used for verifying user actions like account activation or password reset.' })\nexport class VerificationToken {\n @PrimaryColumn()\n @Field(type => ID, { description: 'The ID of the user to whom this token belongs.' })\n userId: string\n\n @Column({\n nullable: false\n })\n @Field({ description: 'The sha256 of the token sent in the link. The token itself is not kept.' })\n token: string\n\n @Column({\n nullable: false,\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'\n ? VerificationTokenType\n : undefined,\n length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined : 32,\n default: VerificationTokenType.ACTIVATION\n })\n @Field({ description: 'The type of action this token is for.' })\n type: VerificationTokenType\n\n @Column({\n nullable: true\n })\n @Field({ nullable: true, description: 'Supplementary data associated with the token.' })\n suppliment: string\n\n /**\n * When the token stops working. Written once, with the token — see `utils/verification-token-rule.ts`.\n * A row without it reads as expired.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When this token stops working.' })\n expiresAt?: Date\n\n @CreateDateColumn()\n @Field({ description: 'The timestamp when the token was created.' })\n createdAt: Date\n\n @UpdateDateColumn()\n @Field({ description: 'The timestamp when the token was last updated.' })\n updatedAt: Date\n}\n"]}
@@ -1,4 +1,11 @@
1
- export declare function getResetPasswordEmailForm({ name, resetUrl }: {
2
- name: any;
3
- resetUrl: any;
1
+ /**
2
+ * The mail that carries a link to the password page. A person who forgot theirs gets it (`Reset password`), and so
3
+ * does a person whose administrator issued a password setup link (ADR-0161) - one page, one mail, two headings.
4
+ */
5
+ export declare function getResetPasswordEmailForm({ name, resetUrl, heading, action, note }: {
6
+ name: string;
7
+ resetUrl: string;
8
+ heading?: string;
9
+ action?: string;
10
+ note?: string;
4
11
  }): string;
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getResetPasswordEmailForm = getResetPasswordEmailForm;
4
- function getResetPasswordEmailForm({ name, resetUrl }) {
4
+ /**
5
+ * The mail that carries a link to the password page. A person who forgot theirs gets it (`Reset password`), and so
6
+ * does a person whose administrator issued a password setup link (ADR-0161) - one page, one mail, two headings.
7
+ */
8
+ function getResetPasswordEmailForm({ name, resetUrl, heading = 'Reset password', action = 'reset password', note = '' }) {
5
9
  return `
6
10
  <html lang="en">
7
11
  <head>
@@ -37,19 +41,19 @@ function getResetPasswordEmailForm({ name, resetUrl }) {
37
41
  >Hi ${name}!</span
38
42
  >
39
43
  <span style="display:block;color:#fff;font-size:34px;font-weight:bold"
40
- >Reset password</span
44
+ >${heading}</span
41
45
  >
42
46
  </div>
43
47
  <!--title end-->
44
48
 
45
49
  <!--body begin-->
46
50
  <p style="padding:10px 20px;line-height:1.5;font-size:16px">
47
- Click the button below to reset password.
51
+ Click the button below to ${action}.${note ? ` ${note}` : ''}
48
52
  <br />
49
53
  <a
50
54
  href="${resetUrl}"
51
55
  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;"
52
- >reset password</a
56
+ >${action}</a
53
57
  >
54
58
  </p>
55
59
  <!--body end-->
@@ -1 +1 @@
1
- {"version":3,"file":"reset-password-email.js","sourceRoot":"","sources":["../../server/templates/reset-password-email.ts"],"names":[],"mappings":";;AAAA,8DAgEC;AAhED,SAAgB,yBAAyB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC1D,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAgCS,IAAI;;;;;;;;;;;;;oBAaF,QAAQ;;;;;;;;;;;;;;;;;GAiBzB,CAAA;AACH,CAAC","sourcesContent":["export function getResetPasswordEmailForm({ name, resetUrl }) {\n return `\n <html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n \n <title>reset password</title>\n <meta name=\"description\" content=\"Password Reset\" />\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 ${name}!</span\n >\n <span style=\"display:block;color:#fff;font-size:34px;font-weight:bold\"\n >Reset password</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 Click the button below to reset password.\n <br />\n <a\n href=\"${resetUrl}\"\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 >reset password</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":"reset-password-email.js","sourceRoot":"","sources":["../../server/templates/reset-password-email.ts"],"names":[],"mappings":";;AAIA,8DA4EC;AAhFD;;;GAGG;AACH,SAAgB,yBAAyB,CAAC,EACxC,IAAI,EACJ,QAAQ,EACR,OAAO,GAAG,gBAAgB,EAC1B,MAAM,GAAG,gBAAgB,EACzB,IAAI,GAAG,EAAE,EAOV;IACC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAgCS,IAAI;;;eAGP,OAAO;;;;;;;sCAOgB,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;;;oBAGlD,QAAQ;;eAEb,MAAM;;;;;;;;;;;;;;;GAelB,CAAA;AACH,CAAC","sourcesContent":["/**\n * The mail that carries a link to the password page. A person who forgot theirs gets it (`Reset password`), and so\n * does a person whose administrator issued a password setup link (ADR-0161) - one page, one mail, two headings.\n */\nexport function getResetPasswordEmailForm({\n name,\n resetUrl,\n heading = 'Reset password',\n action = 'reset password',\n note = ''\n}: {\n name: string\n resetUrl: string\n heading?: string\n action?: string\n note?: string\n}) {\n return `\n <html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n \n <title>reset password</title>\n <meta name=\"description\" content=\"Password Reset\" />\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 ${name}!</span\n >\n <span style=\"display:block;color:#fff;font-size:34px;font-weight:bold\"\n >${heading}</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 Click the button below to ${action}.${note ? ` ${note}` : ''}\n <br />\n <a\n href=\"${resetUrl}\"\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 >${action}</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"]}