@things-factory/auth-base 8.0.0-beta.0 → 8.0.0-beta.2

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 (142) hide show
  1. package/package.json +6 -6
  2. package/client/actions/auth.ts +0 -24
  3. package/client/auth.ts +0 -272
  4. package/client/bootstrap.ts +0 -47
  5. package/client/directive/privileged.ts +0 -28
  6. package/client/index.ts +0 -4
  7. package/client/profiled.ts +0 -83
  8. package/client/reducers/auth.ts +0 -31
  9. package/client/verify-webauthn.ts +0 -86
  10. package/server/constants/error-code.ts +0 -22
  11. package/server/constants/error-message.ts +0 -0
  12. package/server/constants/max-age.ts +0 -1
  13. package/server/controllers/auth.ts +0 -5
  14. package/server/controllers/change-pwd.ts +0 -100
  15. package/server/controllers/checkin.ts +0 -21
  16. package/server/controllers/delete-user.ts +0 -71
  17. package/server/controllers/invitation.ts +0 -163
  18. package/server/controllers/profile.ts +0 -55
  19. package/server/controllers/reset-password.ts +0 -126
  20. package/server/controllers/signin.ts +0 -98
  21. package/server/controllers/signup.ts +0 -72
  22. package/server/controllers/unlock-user.ts +0 -62
  23. package/server/controllers/utils/make-invitation-token.ts +0 -5
  24. package/server/controllers/utils/make-verification-token.ts +0 -4
  25. package/server/controllers/utils/password-rule.ts +0 -120
  26. package/server/controllers/utils/save-invitation-token.ts +0 -10
  27. package/server/controllers/utils/save-verification-token.ts +0 -12
  28. package/server/controllers/verification.ts +0 -84
  29. package/server/errors/auth-error.ts +0 -24
  30. package/server/errors/index.ts +0 -2
  31. package/server/errors/user-domain-not-match-error.ts +0 -29
  32. package/server/index.ts +0 -37
  33. package/server/middlewares/authenticate-401-middleware.ts +0 -114
  34. package/server/middlewares/domain-authenticate-middleware.ts +0 -78
  35. package/server/middlewares/graphql-authenticate-middleware.ts +0 -13
  36. package/server/middlewares/index.ts +0 -67
  37. package/server/middlewares/jwt-authenticate-middleware.ts +0 -84
  38. package/server/middlewares/signin-middleware.ts +0 -55
  39. package/server/middlewares/webauthn-middleware.ts +0 -126
  40. package/server/migrations/1548206416130-SeedUser.ts +0 -60
  41. package/server/migrations/1566805283882-SeedPrivilege.ts +0 -28
  42. package/server/migrations/index.ts +0 -9
  43. package/server/router/auth-checkin-router.ts +0 -113
  44. package/server/router/auth-private-process-router.ts +0 -114
  45. package/server/router/auth-public-process-router.ts +0 -314
  46. package/server/router/auth-signin-router.ts +0 -55
  47. package/server/router/auth-signup-router.ts +0 -95
  48. package/server/router/index.ts +0 -9
  49. package/server/router/oauth2/index.ts +0 -2
  50. package/server/router/oauth2/oauth2-authorize-router.ts +0 -81
  51. package/server/router/oauth2/oauth2-router.ts +0 -165
  52. package/server/router/oauth2/oauth2-server.ts +0 -262
  53. package/server/router/oauth2/passport-oauth2-client-password.ts +0 -87
  54. package/server/router/oauth2/passport-refresh-token.ts +0 -87
  55. package/server/router/path-base-domain-router.ts +0 -8
  56. package/server/router/site-root-router.ts +0 -48
  57. package/server/router/webauthn-router.ts +0 -149
  58. package/server/routes.ts +0 -80
  59. package/server/service/app-binding/app-binding-mutation.ts +0 -22
  60. package/server/service/app-binding/app-binding-query.ts +0 -92
  61. package/server/service/app-binding/app-binding-types.ts +0 -11
  62. package/server/service/app-binding/app-binding.ts +0 -17
  63. package/server/service/app-binding/index.ts +0 -4
  64. package/server/service/appliance/appliance-mutation.ts +0 -113
  65. package/server/service/appliance/appliance-query.ts +0 -76
  66. package/server/service/appliance/appliance-types.ts +0 -56
  67. package/server/service/appliance/appliance.ts +0 -133
  68. package/server/service/appliance/index.ts +0 -6
  69. package/server/service/application/application-mutation.ts +0 -104
  70. package/server/service/application/application-query.ts +0 -98
  71. package/server/service/application/application-types.ts +0 -76
  72. package/server/service/application/application.ts +0 -216
  73. package/server/service/application/index.ts +0 -6
  74. package/server/service/auth-provider/auth-provider-mutation.ts +0 -159
  75. package/server/service/auth-provider/auth-provider-parameter-spec.ts +0 -24
  76. package/server/service/auth-provider/auth-provider-query.ts +0 -88
  77. package/server/service/auth-provider/auth-provider-type.ts +0 -67
  78. package/server/service/auth-provider/auth-provider.ts +0 -155
  79. package/server/service/auth-provider/index.ts +0 -7
  80. package/server/service/domain-generator/domain-generator-mutation.ts +0 -117
  81. package/server/service/domain-generator/domain-generator-types.ts +0 -46
  82. package/server/service/domain-generator/index.ts +0 -3
  83. package/server/service/granted-role/granted-role-mutation.ts +0 -156
  84. package/server/service/granted-role/granted-role-query.ts +0 -60
  85. package/server/service/granted-role/granted-role.ts +0 -27
  86. package/server/service/granted-role/index.ts +0 -6
  87. package/server/service/index.ts +0 -90
  88. package/server/service/invitation/index.ts +0 -6
  89. package/server/service/invitation/invitation-mutation.ts +0 -78
  90. package/server/service/invitation/invitation-query.ts +0 -33
  91. package/server/service/invitation/invitation-types.ts +0 -11
  92. package/server/service/invitation/invitation.ts +0 -63
  93. package/server/service/login-history/index.ts +0 -5
  94. package/server/service/login-history/login-history-query.ts +0 -51
  95. package/server/service/login-history/login-history-type.ts +0 -12
  96. package/server/service/login-history/login-history.ts +0 -45
  97. package/server/service/partner/index.ts +0 -6
  98. package/server/service/partner/partner-mutation.ts +0 -61
  99. package/server/service/partner/partner-query.ts +0 -102
  100. package/server/service/partner/partner-types.ts +0 -11
  101. package/server/service/partner/partner.ts +0 -57
  102. package/server/service/password-history/index.ts +0 -3
  103. package/server/service/password-history/password-history.ts +0 -16
  104. package/server/service/privilege/index.ts +0 -6
  105. package/server/service/privilege/privilege-directive.ts +0 -77
  106. package/server/service/privilege/privilege-mutation.ts +0 -92
  107. package/server/service/privilege/privilege-query.ts +0 -94
  108. package/server/service/privilege/privilege-types.ts +0 -60
  109. package/server/service/privilege/privilege.ts +0 -102
  110. package/server/service/role/index.ts +0 -6
  111. package/server/service/role/role-mutation.ts +0 -109
  112. package/server/service/role/role-query.ts +0 -155
  113. package/server/service/role/role-types.ts +0 -81
  114. package/server/service/role/role.ts +0 -72
  115. package/server/service/user/domain-query.ts +0 -24
  116. package/server/service/user/index.ts +0 -7
  117. package/server/service/user/user-mutation.ts +0 -482
  118. package/server/service/user/user-query.ts +0 -145
  119. package/server/service/user/user-types.ts +0 -100
  120. package/server/service/user/user.ts +0 -381
  121. package/server/service/users-auth-providers/index.ts +0 -5
  122. package/server/service/users-auth-providers/users-auth-providers.ts +0 -71
  123. package/server/service/verification-token/index.ts +0 -3
  124. package/server/service/verification-token/verification-token.ts +0 -60
  125. package/server/service/web-auth-credential/index.ts +0 -3
  126. package/server/service/web-auth-credential/web-auth-credential.ts +0 -67
  127. package/server/templates/account-unlock-email.ts +0 -65
  128. package/server/templates/invitation-email.ts +0 -66
  129. package/server/templates/reset-password-email.ts +0 -65
  130. package/server/templates/verification-email.ts +0 -66
  131. package/server/types.ts +0 -21
  132. package/server/utils/accepts.ts +0 -11
  133. package/server/utils/access-token-cookie.ts +0 -61
  134. package/server/utils/check-permission.ts +0 -52
  135. package/server/utils/check-user-belongs-domain.ts +0 -19
  136. package/server/utils/check-user-has-role.ts +0 -29
  137. package/server/utils/encrypt-state.ts +0 -22
  138. package/server/utils/get-aes-256-key.ts +0 -13
  139. package/server/utils/get-domain-from-hostname.ts +0 -7
  140. package/server/utils/get-domain-users.ts +0 -38
  141. package/server/utils/get-secret.ts +0 -13
  142. package/server/utils/get-user-domains.ts +0 -112
@@ -1,482 +0,0 @@
1
- import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'
2
- import { GraphQLEmailAddress } from 'graphql-scalars'
3
- import { ILike, In, SelectQueryBuilder, EntityManager } from 'typeorm'
4
-
5
- import { config } from '@things-factory/env'
6
- import { Domain, getRepository, ObjectRef } from '@things-factory/shell'
7
-
8
- import { deleteUser as commonDeleteUser, deleteUsers as commonDeleteUsers } from '../../controllers/delete-user'
9
- import { buildDomainUsersQueryBuilder } from '../../utils/get-domain-users'
10
- import { Role } from '../role/role'
11
- import { User, UserStatus } from './user'
12
- import { NewUser, UserPatch } from './user-types'
13
- import { USERNAME_ALREADY_EXISTS, EMAIL_ALREADY_EXISTS } from '../../constants/error-code'
14
-
15
- @Resolver(User)
16
- export class UserMutation {
17
- @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
18
- @Directive('@transaction')
19
- @Mutation(returns => User, { description: 'To create new user' })
20
- async createUser(@Arg('user') user: NewUser, @Ctx() context: ResolverContext) {
21
- const { domain, tx } = context.state
22
- const { defaultPassword } = config.get('password')
23
- const { username, email } = user
24
- const userRepository = getRepository(User, tx)
25
-
26
- user.username = username.trim()
27
- user.email = email.trim()
28
-
29
- if (await userRepository.findOne({ where: { username: user.username } })) {
30
- throw new Error(context.t(USERNAME_ALREADY_EXISTS))
31
- }
32
-
33
- if (await userRepository.findOne({ where: { email: ILike(user.email) } })) {
34
- throw new Error(context.t(EMAIL_ALREADY_EXISTS))
35
- }
36
-
37
- if (!user.password && !defaultPassword) {
38
- throw new Error('initial password or default password should be supported.')
39
- }
40
-
41
- // TODO username은 다음 패턴을 따라야 한다. pattern="^[A-Za-z0-9]*$"
42
- if (!/^[A-Za-z0-9]*$/.test(user.username)) {
43
- throw new Error(context.t('error.invalid x', { x: context.t('field.username') }))
44
- }
45
-
46
- // consider if validation password rule is required
47
- /* check if password is following the rule */
48
- // User.validatePasswordByRule(user.password, context.lng)
49
-
50
- const salt = User.generateSalt()
51
-
52
- return await userRepository.save({
53
- creator: context.state.user,
54
- updater: context.state.user,
55
- ...user,
56
- domains: [domain],
57
- roles:
58
- user.roles && user.roles.length
59
- ? await getRepository(Role, tx).findBy({
60
- id: In(user.roles.map(role => role.id)),
61
- domain: { id: domain.id }
62
- })
63
- : [],
64
- salt,
65
- passwordUpdatedAt: new Date(),
66
- password: user.password ? User.encode(user.password, salt) : User.encode(defaultPassword, salt)
67
- })
68
- }
69
-
70
- @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
71
- @Directive('@transaction')
72
- @Mutation(returns => User, { description: 'To modify user information' })
73
- async updateUser(
74
- @Arg('email', type => GraphQLEmailAddress) email: string,
75
- @Arg('patch') patch: UserPatch,
76
- @Ctx() context: ResolverContext
77
- ) {
78
- const { domain, user: updater, tx }: { domain: Domain; user: User; tx?: EntityManager } = context.state
79
- const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id, 'USER')
80
- const user: User = await qb
81
- .andWhere('LOWER(USER.email) = :email', { email: email?.toLowerCase().trim() || '' })
82
- .leftJoinAndSelect('USER.roles', 'ROLES')
83
- .leftJoinAndSelect('ROLES.domain', 'R_DOMAIN')
84
- .getOne()
85
-
86
- if (patch.roles) {
87
- patch.roles = await getRepository(Role, tx).find({
88
- where: { id: In(patch.roles.map((r: Partial<Role>) => r.id)) }
89
- })
90
- }
91
-
92
- if (patch.status && patch.status === 'activated') {
93
- user.status = UserStatus.ACTIVATED
94
- }
95
-
96
- return await getRepository(User, tx).save({
97
- ...user,
98
- ...patch,
99
- updater
100
- } as any)
101
- }
102
-
103
- @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
104
- @Directive('@transaction')
105
- @Mutation(returns => [User], { description: 'To modify multiple users information' })
106
- async updateMultipleUser(@Arg('patches', type => [UserPatch]) patches: UserPatch[], @Ctx() context: ResolverContext) {
107
- const { domain, user, tx } = context.state
108
- const userRepo = getRepository(User, tx)
109
-
110
- let results = []
111
- const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')
112
- const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')
113
-
114
- if (_createRecords.length > 0) {
115
- for (let i = 0; i < _createRecords.length; i++) {
116
- const newRecord = _createRecords[i]
117
-
118
- // consider if validation password rule is required
119
- /* check if password is following the rule */
120
- // User.validatePasswordByRule(newRecord.password, context.lng)
121
-
122
- const salt = User.generateSalt()
123
- const result = await userRepo.save({
124
- ...(newRecord as any),
125
- domains: [domain],
126
- salt,
127
- password: User.encode(newRecord.password, salt),
128
- passwordUpdatedAt: new Date(),
129
- creator: user,
130
- updater: user
131
- })
132
-
133
- // repository api는 작동하지 않음.
134
- // await tx
135
- // .createQueryBuilder()
136
- // .insert()
137
- // .into('users_domains')
138
- // .values({
139
- // usersId: result.id,
140
- // domainsId: domain.id
141
- // })
142
- // .execute()
143
-
144
- results.push({ ...result, cuFlag: '+' })
145
- }
146
- }
147
-
148
- if (_updateRecords.length > 0) {
149
- for (let i = 0; i < _updateRecords.length; i++) {
150
- const updateRecord = _updateRecords[i]
151
- // consider if validation password rule is required
152
- /* check if password is following the rule */
153
- // User.validatePasswordByRule(updateRecord.password, context.lng)
154
-
155
- const user = await userRepo.findOne({ where: { id: updateRecord.id }, relations: ['domains'] })
156
- var domains = user.domains.find(d => d.id === domain.id) ? user.domains : [...user.domains, domain]
157
-
158
- const result = await userRepo.save({
159
- ...user,
160
- ...(updateRecord as any),
161
- domains,
162
- password: updateRecord.password ? User.encode(updateRecord.password, user.salt) : user.password,
163
- updater: user
164
- })
165
-
166
- if (!updateRecord.status) {
167
- continue
168
- }
169
-
170
- // const domain = await user.domain
171
- // if (!domain) {
172
- // continue
173
- // }
174
-
175
- // const domainId = domain.id
176
- // const domains = await user.domains
177
- // if (!domains.find(domain => domain.id == domainId)) {
178
- // await tx
179
- // .createQueryBuilder()
180
- // .insert()
181
- // .into('users_domains')
182
- // .values({
183
- // usersId: user.id,
184
- // domainsId: domain.id
185
- // })
186
- // .execute()
187
- // }
188
-
189
- results.push({ ...result, cuFlag: 'M' })
190
- }
191
- }
192
-
193
- return results
194
- }
195
-
196
- @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
197
- @Directive('@transaction')
198
- @Mutation(returns => Boolean, { description: 'To delete a user' })
199
- async deleteUser(@Arg('username') username: string, @Ctx() context: ResolverContext) {
200
- const { tx } = context.state
201
-
202
- await commonDeleteUser({ username }, tx)
203
-
204
- return true
205
- }
206
-
207
- @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
208
- @Directive('@transaction')
209
- @Mutation(returns => Boolean, { description: 'To delete some users' })
210
- async deleteUsers(@Arg('usernames', type => [String]) usernames: string[], @Ctx() context: ResolverContext) {
211
- const { tx } = context.state
212
- await commonDeleteUsers({ usernames }, tx)
213
-
214
- return true
215
- }
216
-
217
- @Directive('@transaction')
218
- @Mutation(returns => Boolean, { description: 'To invite new user' })
219
- async inviteUser(@Arg('username') username: string, @Ctx() context: ResolverContext): Promise<boolean> {
220
- const { domain, tx } = context.state
221
- const userRepository = getRepository(User, tx)
222
-
223
- var invitee: User = await userRepository.findOne({
224
- where: { username },
225
- relations: ['domains']
226
- })
227
-
228
- if (!invitee && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
229
- invitee = await userRepository.findOne({
230
- where: { email: ILike(username) },
231
- relations: ['domains']
232
- })
233
- }
234
-
235
- if (!invitee) {
236
- throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))
237
- }
238
-
239
- const existingDomains: Domain[] = invitee.domains
240
- if (existingDomains.find((d: Domain) => d.id === domain.id)) {
241
- throw new Error(context.t('error.x already exists in y', { x: context.t('field.user'), y: domain.name }))
242
- }
243
-
244
- invitee.domains = [...existingDomains, domain]
245
- await userRepository.save(invitee)
246
-
247
- return true
248
- }
249
-
250
- @Directive('@transaction')
251
- @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
252
- @Mutation(returns => Boolean, { description: 'To delete domain user' })
253
- async deleteDomainUser(@Arg('username') username: string, @Ctx() context: ResolverContext): Promise<boolean> {
254
- const { tx, domain } = context.state
255
- const userRepository = getRepository(User, tx)
256
-
257
- var user: User = await userRepository.findOne({
258
- where: { username },
259
- relations: ['domains', 'roles']
260
- })
261
-
262
- if (!user && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
263
- user = await userRepository.findOne({
264
- where: { email: ILike(username) },
265
- relations: ['domains', 'roles']
266
- })
267
- }
268
-
269
- if (!user) {
270
- throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))
271
- }
272
-
273
- const targetDomainIdx: number = user.domains.findIndex((userDomain: Domain) => userDomain.id === domain.id)
274
- if (targetDomainIdx < 0) {
275
- throw new Error(context.t('error.x is not a member of y', { x: user.name, y: domain.name }))
276
- }
277
-
278
- // Remove domain relation with user
279
- user.domains.splice(targetDomainIdx, 1)
280
-
281
- // Remove domain's roles that user has
282
- user.roles = user.roles.filter((role: Role) => role.domainId !== domain.id)
283
-
284
- await userRepository.save(user)
285
-
286
- return true
287
- }
288
-
289
- @Directive('@privilege(domainOwnerGranted: true, superUserGranted: true)')
290
- @Directive('@transaction')
291
- @Mutation(returns => Boolean, { description: 'To transfer owner of domain' })
292
- async transferOwner(@Arg('username') username: string, @Ctx() context: ResolverContext): Promise<boolean> {
293
- const { domain, tx } = context.state
294
- const userRepository = getRepository(User, tx)
295
-
296
- var user: User = await userRepository.findOne({
297
- where: { username },
298
- relations: ['domains']
299
- })
300
-
301
- if (!user && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
302
- user = await userRepository.findOne({
303
- where: { email: ILike(username) },
304
- relations: ['domains']
305
- })
306
- }
307
-
308
- if (!user) {
309
- throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))
310
- }
311
-
312
- if (user.status !== UserStatus.ACTIVATED) {
313
- throw new Error('Only activated users are eligible to receive admin privileges.')
314
- }
315
-
316
- if (user.domains.map((d: Domain) => d.id).indexOf(domain.id) < 0) {
317
- throw new Error(`User is not belongs to current domain`)
318
- }
319
-
320
- if (user.roles.filter((r: Role) => r.domainId == domain.id).length == 0) {
321
- throw new Error(`Only users with at least one role in this domain are eligible to receive admin privileges.`)
322
- }
323
-
324
- domain.owner = user.id
325
- await getRepository(Domain, tx).save(domain)
326
-
327
- return true
328
- }
329
-
330
- @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
331
- @Directive('@transaction')
332
- @Mutation(returns => Boolean, { description: 'To activate user' })
333
- async activateUser(@Arg('username') username: string, @Ctx() context: ResolverContext): Promise<boolean> {
334
- const { tx, domain } = context.state
335
- const userRepository = getRepository(User, tx)
336
-
337
- var targetUser: User = await userRepository.findOne({
338
- where: { username },
339
- relations: ['domains']
340
- })
341
-
342
- if (!targetUser && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
343
- targetUser = await userRepository.findOne({
344
- where: { email: ILike(username) },
345
- relations: ['domains']
346
- })
347
- }
348
-
349
- if (!targetUser) {
350
- throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))
351
- }
352
-
353
- if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {
354
- throw new Error('User is not belong to domain')
355
- }
356
-
357
- targetUser.failCount = 0
358
- targetUser.status = UserStatus.ACTIVATED
359
-
360
- await userRepository.save(targetUser)
361
-
362
- return true
363
- }
364
-
365
- @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
366
- @Directive('@transaction')
367
- @Mutation(returns => Boolean, { description: 'To inactivate user' })
368
- async inactivateUser(@Arg('username') username: string, @Ctx() context: ResolverContext): Promise<boolean> {
369
- const { tx, domain } = context.state
370
- const userRepository = getRepository(User, tx)
371
-
372
- var targetUser: User = await userRepository.findOne({
373
- where: { username },
374
- relations: ['domains']
375
- })
376
-
377
- if (!targetUser && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
378
- targetUser = await userRepository.findOne({
379
- where: { email: ILike(username) },
380
- relations: ['domains']
381
- })
382
- }
383
-
384
- if (!targetUser) {
385
- throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))
386
- }
387
-
388
- if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {
389
- throw new Error('User is not belong to domain')
390
- }
391
-
392
- if (targetUser.userType == 'admin' || targetUser.id === domain.owner) {
393
- throw new Error('Admin deactivation not allowed')
394
- }
395
-
396
- targetUser.status = UserStatus.INACTIVE
397
-
398
- await userRepository.save(targetUser)
399
-
400
- return true
401
- }
402
-
403
- @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
404
- @Directive('@transaction')
405
- @Mutation(returns => Boolean, { description: 'To reset password to default' })
406
- async resetPasswordToDefault(@Arg('username') username: string, @Ctx() context: ResolverContext): Promise<boolean> {
407
- const { tx, domain } = context.state
408
-
409
- const { defaultPassword } = config.get('password')
410
- if (!defaultPassword) {
411
- throw new Error('No default password found')
412
- }
413
-
414
- const userRepository = getRepository(User, tx)
415
-
416
- var targetUser: User = await userRepository.findOne({
417
- where: { username },
418
- relations: ['domains']
419
- })
420
-
421
- if (!targetUser && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
422
- targetUser = await userRepository.findOne({
423
- where: { email: ILike(username) },
424
- relations: ['domains']
425
- })
426
- }
427
-
428
- if (!targetUser) {
429
- throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))
430
- }
431
-
432
- if (!targetUser?.domains?.find((userDomain: Domain) => userDomain.id === domain.id)) {
433
- throw new Error('User is not belong to domain')
434
- }
435
-
436
- targetUser.salt = User.generateSalt()
437
- targetUser.password = User.encode(defaultPassword, targetUser.salt)
438
-
439
- await userRepository.save(targetUser)
440
-
441
- return true
442
- }
443
-
444
- @Directive('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)')
445
- @Directive('@transaction')
446
- @Mutation(returns => User, { description: 'To update roles for a user' })
447
- async updateUserRoles(
448
- @Arg('username') username: string,
449
- @Arg('availableRoles', type => [ObjectRef]) availableRoles: ObjectRef[],
450
- @Arg('selectedRoles', type => [ObjectRef]) selectedRoles: ObjectRef[],
451
- @Ctx() context: ResolverContext
452
- ) {
453
- const { domain, tx } = context.state
454
- const userRepository = getRepository(User, tx)
455
-
456
- var user: User = await userRepository.findOne({
457
- where: { username },
458
- relations: ['domains', 'roles']
459
- })
460
-
461
- if (!user && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
462
- user = await userRepository.findOne({
463
- where: { email: ILike(username) },
464
- relations: ['domains', 'roles']
465
- })
466
- }
467
-
468
- if (!user) {
469
- throw new Error(context.t('error.failed to find x', { x: context.t('field.user') }))
470
- }
471
-
472
- if (user.domains.map((d: Domain) => d.id).indexOf(domain.id) < 0) {
473
- throw new Error(`User is not belongs to current domain`)
474
- }
475
-
476
- const availableRoleIds: string[] = availableRoles.map((r: Role) => r.id)
477
- user.roles = user.roles.filter((r: Role) => availableRoleIds.indexOf(r.id) < 0)
478
- user.roles = user.roles.concat(selectedRoles as Role[])
479
-
480
- return await userRepository.save(user)
481
- }
482
- }
@@ -1,145 +0,0 @@
1
- import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'
2
- import { GraphQLEmailAddress } from 'graphql-scalars'
3
- import { ILike, SelectQueryBuilder } from 'typeorm'
4
-
5
- import { config } from '@things-factory/env'
6
- import { getRepository, ListParam, getQueryBuilderFromListParams } from '@things-factory/shell'
7
-
8
- import { checkUserBelongsDomain } from '../../utils/check-user-belongs-domain'
9
- import { buildDomainUsersQueryBuilder } from '../../utils/get-domain-users'
10
- import { User } from './user'
11
- import { PasswordRule, UserList } from './user-types'
12
-
13
- const passwordRule = config.get('password') || {
14
- lowerCase: true,
15
- upperCase: true,
16
- digit: true,
17
- specialCharacter: true,
18
- allowRepeat: false,
19
- useTightPattern: true,
20
- useLoosePattern: false,
21
- tightCharacterLength: 8,
22
- looseCharacterLength: 15
23
- }
24
-
25
- @Resolver(User)
26
- export class UserQuery {
27
- @Query(returns => PasswordRule, {
28
- description:
29
- 'Retrieves the current password rule configuration for the system, such as required character types and minimum length.'
30
- })
31
- passwordRule(@Ctx() context: ResolverContext): PasswordRule {
32
- return passwordRule
33
- }
34
-
35
- @Directive('@privilege(category: "user", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
36
- @Query(returns => User, { description: 'Fetches a user by their email address within the current domain.' })
37
- async user(@Arg('email', type => GraphQLEmailAddress) email: string, @Ctx() context: ResolverContext): Promise<User> {
38
- const { domain } = context.state
39
-
40
- const qb: SelectQueryBuilder<User> = buildDomainUsersQueryBuilder(domain.id, 'USER')
41
- qb.andWhere(`LOWER(USER.email) = :email`, { email: email.toLowerCase().trim() })
42
-
43
- return qb.getOne()
44
- }
45
-
46
- @Directive('@privilege(category: "user", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
47
- @Query(returns => UserList, {
48
- description: 'Fetches a list of users based on provided search parameters within the current domain.'
49
- })
50
- async users(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<UserList> {
51
- const { domain } = context.state
52
-
53
- const qb = getQueryBuilderFromListParams({
54
- repository: getRepository(User),
55
- params,
56
- alias: 'USER',
57
- searchables: ['name', 'email', 'description']
58
- })
59
-
60
- qb.select().andWhere(qb => {
61
- const subQuery = qb
62
- .subQuery()
63
- .select('USERS_DOMAINS.users_id')
64
- .from('users_domains', 'USERS_DOMAINS')
65
- .where('USERS_DOMAINS.domains_id = :domainId', { domainId: domain.id })
66
- .getQuery()
67
-
68
- return 'USER.id IN ' + subQuery
69
- })
70
-
71
- const [items, total] = await qb.getManyAndCount()
72
-
73
- const foundUsers: User[] = items.map((item: User) => {
74
- item.owner = item.id === domain.owner
75
- return item
76
- })
77
-
78
- return { items: foundUsers, total }
79
- }
80
-
81
- @Query(returns => Boolean, { description: 'Checks if the current authenticated user belongs to the current domain.' })
82
- async checkUserBelongsDomain(@Ctx() context: ResolverContext): Promise<Boolean> {
83
- const { user, domain } = context.state
84
-
85
- if (user) {
86
- return await checkUserBelongsDomain(domain, user)
87
- } else {
88
- throw new Error(`Failed to get current user information.`)
89
- }
90
- }
91
-
92
- @Query(returns => Boolean, {
93
- description: 'Determines whether the system provides a default password when creating a new user.'
94
- })
95
- async checkResettablePasswordToDefault(@Ctx() context: ResolverContext): Promise<Boolean> {
96
- const { defaultPassword } = config.get('password')
97
-
98
- return Boolean(defaultPassword)
99
- }
100
-
101
- @Query(returns => Boolean, {
102
- description: 'Checks if the system is configured to provide a default password for new users.'
103
- })
104
- async checkDefaultPassword(@Ctx() context: ResolverContext): Promise<Boolean> {
105
- const { defaultPassword } = config.get('password')
106
-
107
- return Boolean(defaultPassword)
108
- }
109
-
110
- @Directive('@privilege(category: "user", privilege: "query")')
111
- @Query(returns => Boolean, { description: 'Checks if a user with the given email address exists in the system.' })
112
- async checkUserExistence(@Arg('email', type => GraphQLEmailAddress) email: string): Promise<Boolean> {
113
- return Boolean(await getRepository(User).count({ where: { email: ILike(email) } }))
114
- }
115
-
116
- @FieldResolver()
117
- async domains(@Root() user: User) {
118
- return (
119
- await getRepository(User).findOne({
120
- where: { id: user.id },
121
- relations: ['domains']
122
- })
123
- ).domains
124
- }
125
-
126
- @FieldResolver()
127
- async roles(@Root() user: User) {
128
- return (
129
- await getRepository(User).findOne({
130
- where: { id: user.id },
131
- relations: ['roles']
132
- })
133
- ).roles
134
- }
135
-
136
- @FieldResolver()
137
- async updater(@Root() user: User): Promise<User> {
138
- return await getRepository(User).findOneBy({ id: user.updaterId })
139
- }
140
-
141
- @FieldResolver()
142
- async creator(@Root() user: User): Promise<User> {
143
- return await getRepository(User).findOneBy({ id: user.creatorId })
144
- }
145
- }