@things-factory/auth-base 10.1.213 → 10.1.218

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 (114) 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/user/user-mutation.d.ts +27 -6
  45. package/dist-server/service/user/user-mutation.js +32 -114
  46. package/dist-server/service/user/user-mutation.js.map +1 -1
  47. package/dist-server/service/user/user-query.d.ts +3 -2
  48. package/dist-server/service/user/user-query.js +17 -21
  49. package/dist-server/service/user/user-query.js.map +1 -1
  50. package/dist-server/service/user/user-types.d.ts +6 -2
  51. package/dist-server/service/user/user-types.js +20 -9
  52. package/dist-server/service/user/user-types.js.map +1 -1
  53. package/dist-server/service/user/user.js +17 -8
  54. package/dist-server/service/user/user.js.map +1 -1
  55. package/dist-server/service/verification-token/verification-token.d.ts +1 -0
  56. package/dist-server/service/verification-token/verification-token.js +1 -0
  57. package/dist-server/service/verification-token/verification-token.js.map +1 -1
  58. package/dist-server/templates/reset-password-email.d.ts +10 -3
  59. package/dist-server/templates/reset-password-email.js +8 -4
  60. package/dist-server/templates/reset-password-email.js.map +1 -1
  61. package/dist-server/tsconfig.tsbuildinfo +1 -1
  62. package/dist-server/utils/password-reset-rule.d.ts +15 -0
  63. package/dist-server/utils/password-reset-rule.js +34 -0
  64. package/dist-server/utils/password-reset-rule.js.map +1 -0
  65. package/dist-server/utils/verification-token-rule.js +3 -1
  66. package/dist-server/utils/verification-token-rule.js.map +1 -1
  67. package/package.json +2 -2
  68. package/tests/add-user-gates.test.ts +34 -0
  69. package/tests/list-inheritance-db.test.ts +0 -15
  70. package/tests/lock-recovery.test.ts +2 -2
  71. package/tests/open-doors.test.ts +2 -5
  72. package/tests/password-reset-rule-db.test.ts +159 -0
  73. package/tests/password-setup-db.test.ts +213 -0
  74. package/tests/picture-reducer.test.ts +25 -0
  75. package/tests/verification-token-db.test.ts +12 -7
  76. package/translations/en.json +4 -2
  77. package/translations/es.json +4 -2
  78. package/translations/ja.json +4 -2
  79. package/translations/ko.json +4 -2
  80. package/translations/ms.json +4 -2
  81. package/translations/zh.json +4 -2
  82. package/dist-server/controllers/invitation.d.ts +0 -75
  83. package/dist-server/controllers/invitation.js +0 -195
  84. package/dist-server/controllers/invitation.js.map +0 -1
  85. package/dist-server/controllers/utils/make-invitation-token.d.ts +0 -1
  86. package/dist-server/controllers/utils/make-invitation-token.js +0 -9
  87. package/dist-server/controllers/utils/make-invitation-token.js.map +0 -1
  88. package/dist-server/controllers/utils/save-invitation-token.d.ts +0 -5
  89. package/dist-server/controllers/utils/save-invitation-token.js +0 -12
  90. package/dist-server/controllers/utils/save-invitation-token.js.map +0 -1
  91. package/dist-server/service/invitation/index.d.ts +0 -5
  92. package/dist-server/service/invitation/index.js +0 -9
  93. package/dist-server/service/invitation/index.js.map +0 -1
  94. package/dist-server/service/invitation/invitation-mutation.d.ts +0 -20
  95. package/dist-server/service/invitation/invitation-mutation.js +0 -109
  96. package/dist-server/service/invitation/invitation-mutation.js.map +0 -1
  97. package/dist-server/service/invitation/invitation-query.d.ts +0 -23
  98. package/dist-server/service/invitation/invitation-query.js +0 -89
  99. package/dist-server/service/invitation/invitation-query.js.map +0 -1
  100. package/dist-server/service/invitation/invitation-types.d.ts +0 -5
  101. package/dist-server/service/invitation/invitation-types.js +0 -21
  102. package/dist-server/service/invitation/invitation-types.js.map +0 -1
  103. package/dist-server/service/invitation/invitation.d.ts +0 -26
  104. package/dist-server/service/invitation/invitation.js +0 -142
  105. package/dist-server/service/invitation/invitation.js.map +0 -1
  106. package/dist-server/templates/invitation-email.d.ts +0 -6
  107. package/dist-server/templates/invitation-email.js +0 -82
  108. package/dist-server/templates/invitation-email.js.map +0 -1
  109. package/dist-server/utils/invitation-state.d.ts +0 -52
  110. package/dist-server/utils/invitation-state.js +0 -76
  111. package/dist-server/utils/invitation-state.js.map +0 -1
  112. package/tests/invitation-db.test.ts +0 -416
  113. package/tests/invitation-route.test.ts +0 -116
  114. package/tests/invitation-state.test.ts +0 -92
@@ -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":"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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: '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"]}