@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.
- package/package.json +6 -6
- package/client/actions/auth.ts +0 -24
- package/client/auth.ts +0 -272
- package/client/bootstrap.ts +0 -47
- package/client/directive/privileged.ts +0 -28
- package/client/index.ts +0 -4
- package/client/profiled.ts +0 -83
- package/client/reducers/auth.ts +0 -31
- package/client/verify-webauthn.ts +0 -86
- package/server/constants/error-code.ts +0 -22
- package/server/constants/error-message.ts +0 -0
- package/server/constants/max-age.ts +0 -1
- package/server/controllers/auth.ts +0 -5
- package/server/controllers/change-pwd.ts +0 -100
- package/server/controllers/checkin.ts +0 -21
- package/server/controllers/delete-user.ts +0 -71
- package/server/controllers/invitation.ts +0 -163
- package/server/controllers/profile.ts +0 -55
- package/server/controllers/reset-password.ts +0 -126
- package/server/controllers/signin.ts +0 -98
- package/server/controllers/signup.ts +0 -72
- package/server/controllers/unlock-user.ts +0 -62
- package/server/controllers/utils/make-invitation-token.ts +0 -5
- package/server/controllers/utils/make-verification-token.ts +0 -4
- package/server/controllers/utils/password-rule.ts +0 -120
- package/server/controllers/utils/save-invitation-token.ts +0 -10
- package/server/controllers/utils/save-verification-token.ts +0 -12
- package/server/controllers/verification.ts +0 -84
- package/server/errors/auth-error.ts +0 -24
- package/server/errors/index.ts +0 -2
- package/server/errors/user-domain-not-match-error.ts +0 -29
- package/server/index.ts +0 -37
- package/server/middlewares/authenticate-401-middleware.ts +0 -114
- package/server/middlewares/domain-authenticate-middleware.ts +0 -78
- package/server/middlewares/graphql-authenticate-middleware.ts +0 -13
- package/server/middlewares/index.ts +0 -67
- package/server/middlewares/jwt-authenticate-middleware.ts +0 -84
- package/server/middlewares/signin-middleware.ts +0 -55
- package/server/middlewares/webauthn-middleware.ts +0 -126
- package/server/migrations/1548206416130-SeedUser.ts +0 -60
- package/server/migrations/1566805283882-SeedPrivilege.ts +0 -28
- package/server/migrations/index.ts +0 -9
- package/server/router/auth-checkin-router.ts +0 -113
- package/server/router/auth-private-process-router.ts +0 -114
- package/server/router/auth-public-process-router.ts +0 -314
- package/server/router/auth-signin-router.ts +0 -55
- package/server/router/auth-signup-router.ts +0 -95
- package/server/router/index.ts +0 -9
- package/server/router/oauth2/index.ts +0 -2
- package/server/router/oauth2/oauth2-authorize-router.ts +0 -81
- package/server/router/oauth2/oauth2-router.ts +0 -165
- package/server/router/oauth2/oauth2-server.ts +0 -262
- package/server/router/oauth2/passport-oauth2-client-password.ts +0 -87
- package/server/router/oauth2/passport-refresh-token.ts +0 -87
- package/server/router/path-base-domain-router.ts +0 -8
- package/server/router/site-root-router.ts +0 -48
- package/server/router/webauthn-router.ts +0 -149
- package/server/routes.ts +0 -80
- package/server/service/app-binding/app-binding-mutation.ts +0 -22
- package/server/service/app-binding/app-binding-query.ts +0 -92
- package/server/service/app-binding/app-binding-types.ts +0 -11
- package/server/service/app-binding/app-binding.ts +0 -17
- package/server/service/app-binding/index.ts +0 -4
- package/server/service/appliance/appliance-mutation.ts +0 -113
- package/server/service/appliance/appliance-query.ts +0 -76
- package/server/service/appliance/appliance-types.ts +0 -56
- package/server/service/appliance/appliance.ts +0 -133
- package/server/service/appliance/index.ts +0 -6
- package/server/service/application/application-mutation.ts +0 -104
- package/server/service/application/application-query.ts +0 -98
- package/server/service/application/application-types.ts +0 -76
- package/server/service/application/application.ts +0 -216
- package/server/service/application/index.ts +0 -6
- package/server/service/auth-provider/auth-provider-mutation.ts +0 -159
- package/server/service/auth-provider/auth-provider-parameter-spec.ts +0 -24
- package/server/service/auth-provider/auth-provider-query.ts +0 -88
- package/server/service/auth-provider/auth-provider-type.ts +0 -67
- package/server/service/auth-provider/auth-provider.ts +0 -155
- package/server/service/auth-provider/index.ts +0 -7
- package/server/service/domain-generator/domain-generator-mutation.ts +0 -117
- package/server/service/domain-generator/domain-generator-types.ts +0 -46
- package/server/service/domain-generator/index.ts +0 -3
- package/server/service/granted-role/granted-role-mutation.ts +0 -156
- package/server/service/granted-role/granted-role-query.ts +0 -60
- package/server/service/granted-role/granted-role.ts +0 -27
- package/server/service/granted-role/index.ts +0 -6
- package/server/service/index.ts +0 -90
- package/server/service/invitation/index.ts +0 -6
- package/server/service/invitation/invitation-mutation.ts +0 -78
- package/server/service/invitation/invitation-query.ts +0 -33
- package/server/service/invitation/invitation-types.ts +0 -11
- package/server/service/invitation/invitation.ts +0 -63
- package/server/service/login-history/index.ts +0 -5
- package/server/service/login-history/login-history-query.ts +0 -51
- package/server/service/login-history/login-history-type.ts +0 -12
- package/server/service/login-history/login-history.ts +0 -45
- package/server/service/partner/index.ts +0 -6
- package/server/service/partner/partner-mutation.ts +0 -61
- package/server/service/partner/partner-query.ts +0 -102
- package/server/service/partner/partner-types.ts +0 -11
- package/server/service/partner/partner.ts +0 -57
- package/server/service/password-history/index.ts +0 -3
- package/server/service/password-history/password-history.ts +0 -16
- package/server/service/privilege/index.ts +0 -6
- package/server/service/privilege/privilege-directive.ts +0 -77
- package/server/service/privilege/privilege-mutation.ts +0 -92
- package/server/service/privilege/privilege-query.ts +0 -94
- package/server/service/privilege/privilege-types.ts +0 -60
- package/server/service/privilege/privilege.ts +0 -102
- package/server/service/role/index.ts +0 -6
- package/server/service/role/role-mutation.ts +0 -109
- package/server/service/role/role-query.ts +0 -155
- package/server/service/role/role-types.ts +0 -81
- package/server/service/role/role.ts +0 -72
- package/server/service/user/domain-query.ts +0 -24
- package/server/service/user/index.ts +0 -7
- package/server/service/user/user-mutation.ts +0 -482
- package/server/service/user/user-query.ts +0 -145
- package/server/service/user/user-types.ts +0 -100
- package/server/service/user/user.ts +0 -381
- package/server/service/users-auth-providers/index.ts +0 -5
- package/server/service/users-auth-providers/users-auth-providers.ts +0 -71
- package/server/service/verification-token/index.ts +0 -3
- package/server/service/verification-token/verification-token.ts +0 -60
- package/server/service/web-auth-credential/index.ts +0 -3
- package/server/service/web-auth-credential/web-auth-credential.ts +0 -67
- package/server/templates/account-unlock-email.ts +0 -65
- package/server/templates/invitation-email.ts +0 -66
- package/server/templates/reset-password-email.ts +0 -65
- package/server/templates/verification-email.ts +0 -66
- package/server/types.ts +0 -21
- package/server/utils/accepts.ts +0 -11
- package/server/utils/access-token-cookie.ts +0 -61
- package/server/utils/check-permission.ts +0 -52
- package/server/utils/check-user-belongs-domain.ts +0 -19
- package/server/utils/check-user-has-role.ts +0 -29
- package/server/utils/encrypt-state.ts +0 -22
- package/server/utils/get-aes-256-key.ts +0 -13
- package/server/utils/get-domain-from-hostname.ts +0 -7
- package/server/utils/get-domain-users.ts +0 -38
- package/server/utils/get-secret.ts +0 -13
- 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
|
-
}
|