@things-factory/auth-base 10.1.3 → 10.1.6
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/dist-client/auth.d.ts +9 -1
- package/dist-client/auth.js +12 -3
- package/dist-client/auth.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/controllers/profile.d.ts +1 -0
- package/dist-server/controllers/signin.js +33 -12
- package/dist-server/controllers/signin.js.map +1 -1
- package/dist-server/controllers/unlock-user.js +21 -2
- package/dist-server/controllers/unlock-user.js.map +1 -1
- package/dist-server/router/auth-public-process-router.js +32 -5
- package/dist-server/router/auth-public-process-router.js.map +1 -1
- package/dist-server/service/privilege/privilege-directive.d.ts +46 -0
- package/dist-server/service/privilege/privilege-directive.js +87 -6
- package/dist-server/service/privilege/privilege-directive.js.map +1 -1
- package/dist-server/service/role/index.d.ts +2 -1
- package/dist-server/service/role/index.js +2 -1
- package/dist-server/service/role/index.js.map +1 -1
- package/dist-server/service/role/role-mutation.d.ts +17 -1
- package/dist-server/service/role/role-mutation.js +126 -7
- package/dist-server/service/role/role-mutation.js.map +1 -1
- package/dist-server/service/role/role-privilege-query.d.ts +14 -0
- package/dist-server/service/role/role-privilege-query.js +37 -0
- package/dist-server/service/role/role-privilege-query.js.map +1 -0
- package/dist-server/service/role/role-query.d.ts +18 -0
- package/dist-server/service/role/role-query.js +38 -22
- package/dist-server/service/role/role-query.js.map +1 -1
- package/dist-server/service/user/user.d.ts +1 -0
- package/dist-server/service/user/user.js +55 -13
- package/dist-server/service/user/user.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/utils/account-lock-rule.d.ts +79 -0
- package/dist-server/utils/account-lock-rule.js +114 -0
- package/dist-server/utils/account-lock-rule.js.map +1 -0
- package/dist-server/utils/lock-duration.d.ts +3 -0
- package/dist-server/utils/lock-duration.js +36 -0
- package/dist-server/utils/lock-duration.js.map +1 -0
- package/package.json +4 -4
- package/tests/account-lock-db.test.ts +216 -0
- package/tests/account-lock-rule.test.ts +141 -0
- package/tests/compiled.ts +41 -0
- package/tests/has-privilege-db.test.ts +198 -0
- package/tests/lock-recovery.test.ts +204 -0
- package/tests/password-hashing.test.ts +131 -0
- package/tests/password-persistence.test.ts +147 -0
- package/tests/permission-gate.test.ts +226 -0
- package/tests/privilege-directive.test.ts +270 -0
- package/tests/role-mutation-db.test.ts +263 -0
- package/tests/role-privileges-db.test.ts +128 -0
- package/tests/signin-policy.test.ts +200 -0
- package/tests/token-issuance.test.ts +240 -0
- package/translations/en.json +161 -1
- package/translations/ko.json +161 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RoleMutation = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const typeorm_1 = require("typeorm");
|
|
5
6
|
const type_graphql_1 = require("type-graphql");
|
|
6
7
|
const shell_1 = require("@things-factory/shell");
|
|
7
8
|
const privilege_js_1 = require("../privilege/privilege.js");
|
|
@@ -9,6 +10,12 @@ const user_js_1 = require("../user/user.js");
|
|
|
9
10
|
const role_js_1 = require("./role.js");
|
|
10
11
|
const role_types_js_1 = require("./role-types.js");
|
|
11
12
|
let RoleMutation = class RoleMutation {
|
|
13
|
+
/*
|
|
14
|
+
* 선언이 `User` 였다 — 돌려주는 것은 `Role` 인데(아래 TS 반환형도 그렇다) 스키마에는 사용자로
|
|
15
|
+
* 적혀 있었다. 두 이름에 겹치는 칸(`id`·`name`·`description`)만 물어서 지금까지 드러나지
|
|
16
|
+
* 않았고, `email` 을 물으면 오류 없이 빈 값이 온다. 부르는 자리는 둘 뿐이며 둘 다 겹치는
|
|
17
|
+
* 칸만 쓴다(`create-role.ts`·`role-detail.ts`).
|
|
18
|
+
*/
|
|
12
19
|
async createRole(role, context) {
|
|
13
20
|
const { domain, user } = context.state;
|
|
14
21
|
const roleName = role.name.trim();
|
|
@@ -37,6 +44,15 @@ let RoleMutation = class RoleMutation {
|
|
|
37
44
|
where: { domain: { id: domain.id }, id },
|
|
38
45
|
relations: ['privileges', 'creator', 'updater']
|
|
39
46
|
});
|
|
47
|
+
/*
|
|
48
|
+
* **못 찾았으면 그렇게 말한다.** 이 범위는 `domain.id` 하나다 — 목록은 상속으로 부모 도메인의
|
|
49
|
+
* 역할까지 보여 주므로(`inherited: Include`) 관리자가 그것을 고치려 하면 여기로 온다. 예전에는
|
|
50
|
+
* 바로 아래 `role.name` 에서 TypeError 가 나 500 이 되었고, 화면은 왜 안 되는지 알 수 없었다.
|
|
51
|
+
*/
|
|
52
|
+
if (!role) {
|
|
53
|
+
throw new Error(context.t('error.failed to find x', { x: context.t('label.role') }) +
|
|
54
|
+
' — a role defined in a parent domain can only be edited in that domain.');
|
|
55
|
+
}
|
|
40
56
|
if (name && role.name !== name) {
|
|
41
57
|
const roleName = name.trim();
|
|
42
58
|
const existsRole = await roleRepository.findOne({ where: { name: roleName, domain: { id: domain.id } } });
|
|
@@ -51,36 +67,129 @@ let RoleMutation = class RoleMutation {
|
|
|
51
67
|
return await roleRepository.save({
|
|
52
68
|
...role,
|
|
53
69
|
...patch,
|
|
54
|
-
|
|
70
|
+
/*
|
|
71
|
+
* `privileges` 를 안 보낸 patch 는 **권한을 건드리지 않는다는 뜻**이다. 예전에는 여기서
|
|
72
|
+
* `patch.privileges.map` 이 TypeError 를 냈다 — 이름만 고치려는 호출이 500 이었다.
|
|
73
|
+
*/
|
|
74
|
+
privileges: privileges
|
|
75
|
+
? await (0, shell_1.getRepository)(privilege_js_1.Privilege).findBy({ id: (0, typeorm_1.In)(privileges.map(privilege => privilege.id)) })
|
|
76
|
+
: role.privileges,
|
|
55
77
|
updater: user
|
|
56
78
|
});
|
|
57
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* 격자에서 고친 여러 줄을 한 번에 저장한다.
|
|
82
|
+
*
|
|
83
|
+
* 이 저장소의 목록 화면 관례다 — `ox-grist` 의 `dirtyRecords` 를 `cuFlag` 와 함께 보내면
|
|
84
|
+
* `+` 는 만들고 `M` 은 고친다(`updateMultipleAuthProvider`·`updateMultipleUser`·
|
|
85
|
+
* `updateMultipleDomainLink` 가 같은 모양). 역할에만 이 자리가 없어서 이름·설명을 격자에서
|
|
86
|
+
* 고칠 수 없었다.
|
|
87
|
+
*
|
|
88
|
+
* **`privileges` 는 여기서 안 건드린다.** 격자는 이름과 설명만 고치고, 권한은 전용 화면이
|
|
89
|
+
* 쓴다(`updateRole`). 안 보낸 것을 「비우라」로 읽으면 격자에서 이름만 고친 순간 그 역할이
|
|
90
|
+
* 아무것도 안 여는 역할이 된다.
|
|
91
|
+
*
|
|
92
|
+
* 지우기는 이 자리에 없다 — 격자의 삭제는 `deleteRoles` 가 받고, 그쪽은 **쓰이는 역할을
|
|
93
|
+
* 남긴다**는 규칙을 따로 갖는다.
|
|
94
|
+
*/
|
|
95
|
+
async updateMultipleRole(patches, context) {
|
|
96
|
+
const { domain, user, tx } = context.state;
|
|
97
|
+
const roleRepository = tx.getRepository(role_js_1.Role);
|
|
98
|
+
const results = [];
|
|
99
|
+
/* `cuFlag` 가 없는 줄은 무엇을 하라는 것인지 알 수 없다 — 짐작해서 만들거나 고치지 않는다. */
|
|
100
|
+
const flagOf = (patch) => (patch.cuFlag || '').toUpperCase();
|
|
101
|
+
for (const patch of patches.filter(patch => flagOf(patch) === '+')) {
|
|
102
|
+
const name = patch.name?.trim();
|
|
103
|
+
if (!name) {
|
|
104
|
+
throw new Error(context.t('error.value is empty', { value: context.t('field.name') }));
|
|
105
|
+
}
|
|
106
|
+
if (await roleRepository.findOne({ where: { name, domain: { id: domain.id } } })) {
|
|
107
|
+
throw new Error('role name is duplicated');
|
|
108
|
+
}
|
|
109
|
+
results.push(await roleRepository.save({
|
|
110
|
+
domain,
|
|
111
|
+
creator: user,
|
|
112
|
+
updater: user,
|
|
113
|
+
name,
|
|
114
|
+
description: patch.description
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
for (const patch of patches.filter(patch => flagOf(patch) === 'M')) {
|
|
118
|
+
const role = await roleRepository.findOne({ where: { domain: { id: domain.id }, id: patch.id } });
|
|
119
|
+
/* 목록은 상속으로 부모 도메인 역할까지 보여 준다 — 그것을 고치려 하면 여기로 온다. */
|
|
120
|
+
if (!role) {
|
|
121
|
+
throw new Error(context.t('error.failed to find x', { x: context.t('label.role') }) +
|
|
122
|
+
' — a role defined in a parent domain can only be edited in that domain.');
|
|
123
|
+
}
|
|
124
|
+
const name = patch.name?.trim();
|
|
125
|
+
if (name && name !== role.name) {
|
|
126
|
+
if (await roleRepository.findOne({ where: { name, domain: { id: domain.id } } })) {
|
|
127
|
+
throw new Error('role name is duplicated');
|
|
128
|
+
}
|
|
129
|
+
role.name = name;
|
|
130
|
+
}
|
|
131
|
+
if (patch.description !== undefined) {
|
|
132
|
+
role.description = patch.description;
|
|
133
|
+
}
|
|
134
|
+
results.push(await roleRepository.save({ ...role, updater: user }));
|
|
135
|
+
}
|
|
136
|
+
return results;
|
|
137
|
+
}
|
|
58
138
|
async deleteRole(id, context) {
|
|
59
139
|
const { tx, domain } = context.state;
|
|
60
140
|
let role = await tx
|
|
61
141
|
.getRepository(role_js_1.Role)
|
|
62
142
|
.findOne({ where: { domain: { id: domain.id }, id }, relations: ['users', 'privileges'] });
|
|
143
|
+
/*
|
|
144
|
+
* **세 가지 실패가 한 문장으로 나가고 있었다.** 아래 try 의 catch 가 무엇이 났든
|
|
145
|
+
* 「사용 중이라 지울 수 없다」로 401 을 던졌다 — 못 찾은 것도(부모 도메인의 역할이 그렇다),
|
|
146
|
+
* DB 가 실패한 것도 그렇게 말했다. 관리자는 있지도 않은 사용자를 떼러 갔다.
|
|
147
|
+
*
|
|
148
|
+
* 그래서 판단할 수 있는 둘을 먼저 가른다.
|
|
149
|
+
*/
|
|
150
|
+
if (!role) {
|
|
151
|
+
throw new Error(context.t('error.failed to find x', { x: context.t('label.role') }) +
|
|
152
|
+
' — a role defined in a parent domain can only be deleted in that domain.');
|
|
153
|
+
}
|
|
154
|
+
if (role.users.length) {
|
|
155
|
+
throw new Error('cannot delete role because it is currently in use');
|
|
156
|
+
}
|
|
63
157
|
try {
|
|
64
|
-
if (role.users.length)
|
|
65
|
-
throw new Error('cannot delete role because it is currently in use');
|
|
66
158
|
role.privileges = [];
|
|
67
159
|
role = await tx.getRepository(role_js_1.Role).save(role);
|
|
68
160
|
await tx.getRepository(role_js_1.Role).delete(role.id);
|
|
69
161
|
return true;
|
|
70
162
|
}
|
|
71
163
|
catch (e) {
|
|
72
|
-
|
|
164
|
+
/* 여기까지 온 것은 지우는 동작 자체의 실패다 — 그 말을 그대로 올린다. */
|
|
165
|
+
context.throw(500, e.message);
|
|
73
166
|
}
|
|
74
167
|
}
|
|
75
|
-
async deleteRoles(ids,
|
|
76
|
-
|
|
168
|
+
async deleteRoles(ids, context) {
|
|
169
|
+
const { domain } = context.state;
|
|
170
|
+
/*
|
|
171
|
+
* **도메인을 봤어야 했다.** 예전에는 받은 id 를 그대로 지웠다 — `user:mutation` 을 가진
|
|
172
|
+
* 사람이 남의 도메인 역할을 id 만으로 지울 수 있었다. `deleteRole`(단수)은 처음부터
|
|
173
|
+
* 도메인으로 좁혀 찾았으므로, 둘의 규칙이 달랐다.
|
|
174
|
+
*
|
|
175
|
+
* 그리고 **사용 중인 역할은 남긴다** — 단수 쪽과 같은 규칙이다. 지운 것과 남은 것을 세어
|
|
176
|
+
* 돌려주지는 않는다(반환형이 `Boolean` 이다). 목록이 다시 읽으면 무엇이 남았는지 보인다.
|
|
177
|
+
*/
|
|
178
|
+
const roles = await (0, shell_1.getRepository)(role_js_1.Role).find({
|
|
179
|
+
where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(ids) },
|
|
180
|
+
relations: ['users']
|
|
181
|
+
});
|
|
182
|
+
const deletable = roles.filter(role => !role.users?.length).map(role => role.id);
|
|
183
|
+
if (deletable.length) {
|
|
184
|
+
await (0, shell_1.getRepository)(role_js_1.Role).delete(deletable);
|
|
185
|
+
}
|
|
77
186
|
return true;
|
|
78
187
|
}
|
|
79
188
|
};
|
|
80
189
|
exports.RoleMutation = RoleMutation;
|
|
81
190
|
tslib_1.__decorate([
|
|
82
191
|
(0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)'),
|
|
83
|
-
(0, type_graphql_1.Mutation)(returns =>
|
|
192
|
+
(0, type_graphql_1.Mutation)(returns => role_js_1.Role, { description: 'To create new role' }),
|
|
84
193
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('role')),
|
|
85
194
|
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
86
195
|
tslib_1.__metadata("design:type", Function),
|
|
@@ -97,6 +206,16 @@ tslib_1.__decorate([
|
|
|
97
206
|
tslib_1.__metadata("design:paramtypes", [String, role_types_js_1.RolePatch, Object]),
|
|
98
207
|
tslib_1.__metadata("design:returntype", Promise)
|
|
99
208
|
], RoleMutation.prototype, "updateRole", null);
|
|
209
|
+
tslib_1.__decorate([
|
|
210
|
+
(0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)'),
|
|
211
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
212
|
+
(0, type_graphql_1.Mutation)(returns => [role_js_1.Role], { description: 'To create or update multiple roles at once' }),
|
|
213
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('patches', type => [role_types_js_1.RolePatch])),
|
|
214
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
215
|
+
tslib_1.__metadata("design:type", Function),
|
|
216
|
+
tslib_1.__metadata("design:paramtypes", [Array, Object]),
|
|
217
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
218
|
+
], RoleMutation.prototype, "updateMultipleRole", null);
|
|
100
219
|
tslib_1.__decorate([
|
|
101
220
|
(0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation")'),
|
|
102
221
|
(0, type_graphql_1.Directive)('@transaction'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role-mutation.js","sourceRoot":"","sources":["../../../server/service/role/role-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { Privilege } from '../privilege/privilege.js'\nimport { User } from '../user/user.js'\nimport { Role } from './role.js'\nimport { NewRole, RolePatch } from './role-types.js'\n\n@Resolver(Role)\nexport class RoleMutation {\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => User, { description: 'To create new user' })\n async createRole(@Arg('role') role: NewRole, @Ctx() context: ResolverContext): Promise<Role> {\n const { domain, user } = context.state\n const roleName = role.name.trim()\n\n const existsRole: Role = await getRepository(Role).findOne({ where: { name: roleName, domain: { id: domain.id } } })\n\n if (existsRole) {\n throw new Error('role name is duplicated')\n }\n\n if (role.privileges && role.privileges.length) {\n role.privileges = await getRepository(Privilege).findByIds(role.privileges.map(privilege => privilege.id))\n }\n\n if (role.users && role.users.length) {\n role.users = await getRepository(User).findByIds(role.users.map(user => user.id))\n }\n\n return await getRepository(Role).save({\n domain,\n updater: user,\n creator: user,\n ...role\n })\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Role, { description: 'To modify role information' })\n async updateRole(\n @Arg('id') id: string,\n @Arg('patch') patch: RolePatch,\n @Ctx() context: ResolverContext\n ): Promise<Role> {\n const { domain, user } = context.state\n const { name, description, privileges } = patch\n const roleRepository = getRepository(Role)\n const role: Role = await roleRepository.findOne({\n where: { domain: { id: domain.id }, id },\n relations: ['privileges', 'creator', 'updater']\n })\n\n if (name && role.name !== name) {\n const roleName = name.trim()\n const existsRole: Role = await roleRepository.findOne({ where: { name: roleName, domain: { id: domain.id } } })\n if (existsRole) {\n throw new Error('role name is duplicated')\n }\n\n role.name = roleName\n }\n\n if (description) {\n role.description = description\n }\n\n return await roleRepository.save({\n ...role,\n ...patch,\n privileges: await getRepository(Privilege).findByIds(\n patch.privileges.map((privilege: Privilege) => privilege.id)\n ),\n updater: user\n } as any)\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\")')\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: 'To delete role' })\n async deleteRole(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Boolean> {\n const { tx, domain } = context.state\n let role: Role = await tx\n .getRepository(Role)\n .findOne({ where: { domain: { id: domain.id }, id }, relations: ['users', 'privileges'] })\n\n try {\n if (role.users.length) throw new Error('cannot delete role because it is currently in use')\n\n role.privileges = []\n\n role = await tx.getRepository(Role).save(role)\n\n await tx.getRepository(Role).delete(role.id)\n\n return true\n } catch (e) {\n context.throw(401, 'cannot delete role because it is currently in use')\n }\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete multiple roles' })\n async deleteRoles(@Arg('ids', type => [String]) ids: string[], @Ctx() _context: any) {\n await getRepository(Role).delete(ids)\n return true\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"role-mutation.js","sourceRoot":"","sources":["../../../server/service/role/role-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\nimport { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { Privilege } from '../privilege/privilege.js'\nimport { User } from '../user/user.js'\nimport { Role } from './role.js'\nimport { NewRole, RolePatch } from './role-types.js'\n\n@Resolver(Role)\nexport class RoleMutation {\n /*\n * 선언이 `User` 였다 — 돌려주는 것은 `Role` 인데(아래 TS 반환형도 그렇다) 스키마에는 사용자로\n * 적혀 있었다. 두 이름에 겹치는 칸(`id`·`name`·`description`)만 물어서 지금까지 드러나지\n * 않았고, `email` 을 물으면 오류 없이 빈 값이 온다. 부르는 자리는 둘 뿐이며 둘 다 겹치는\n * 칸만 쓴다(`create-role.ts`·`role-detail.ts`).\n */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Role, { description: 'To create new role' })\n async createRole(@Arg('role') role: NewRole, @Ctx() context: ResolverContext): Promise<Role> {\n const { domain, user } = context.state\n const roleName = role.name.trim()\n\n const existsRole: Role = await getRepository(Role).findOne({ where: { name: roleName, domain: { id: domain.id } } })\n\n if (existsRole) {\n throw new Error('role name is duplicated')\n }\n\n if (role.privileges && role.privileges.length) {\n role.privileges = await getRepository(Privilege).findByIds(role.privileges.map(privilege => privilege.id))\n }\n\n if (role.users && role.users.length) {\n role.users = await getRepository(User).findByIds(role.users.map(user => user.id))\n }\n\n return await getRepository(Role).save({\n domain,\n updater: user,\n creator: user,\n ...role\n })\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Role, { description: 'To modify role information' })\n async updateRole(\n @Arg('id') id: string,\n @Arg('patch') patch: RolePatch,\n @Ctx() context: ResolverContext\n ): Promise<Role> {\n const { domain, user } = context.state\n const { name, description, privileges } = patch\n const roleRepository = getRepository(Role)\n const role: Role = await roleRepository.findOne({\n where: { domain: { id: domain.id }, id },\n relations: ['privileges', 'creator', 'updater']\n })\n\n /*\n * **못 찾았으면 그렇게 말한다.** 이 범위는 `domain.id` 하나다 — 목록은 상속으로 부모 도메인의\n * 역할까지 보여 주므로(`inherited: Include`) 관리자가 그것을 고치려 하면 여기로 온다. 예전에는\n * 바로 아래 `role.name` 에서 TypeError 가 나 500 이 되었고, 화면은 왜 안 되는지 알 수 없었다.\n */\n if (!role) {\n throw new Error(\n context.t('error.failed to find x', { x: context.t('label.role') }) +\n ' — a role defined in a parent domain can only be edited in that domain.'\n )\n }\n\n if (name && role.name !== name) {\n const roleName = name.trim()\n const existsRole: Role = await roleRepository.findOne({ where: { name: roleName, domain: { id: domain.id } } })\n if (existsRole) {\n throw new Error('role name is duplicated')\n }\n\n role.name = roleName\n }\n\n if (description) {\n role.description = description\n }\n\n return await roleRepository.save({\n ...role,\n ...patch,\n /*\n * `privileges` 를 안 보낸 patch 는 **권한을 건드리지 않는다는 뜻**이다. 예전에는 여기서\n * `patch.privileges.map` 이 TypeError 를 냈다 — 이름만 고치려는 호출이 500 이었다.\n */\n privileges: privileges\n ? await getRepository(Privilege).findBy({ id: In(privileges.map(privilege => privilege.id)) })\n : role.privileges,\n updater: user\n } as any)\n }\n\n /**\n * 격자에서 고친 여러 줄을 한 번에 저장한다.\n *\n * 이 저장소의 목록 화면 관례다 — `ox-grist` 의 `dirtyRecords` 를 `cuFlag` 와 함께 보내면\n * `+` 는 만들고 `M` 은 고친다(`updateMultipleAuthProvider`·`updateMultipleUser`·\n * `updateMultipleDomainLink` 가 같은 모양). 역할에만 이 자리가 없어서 이름·설명을 격자에서\n * 고칠 수 없었다.\n *\n * **`privileges` 는 여기서 안 건드린다.** 격자는 이름과 설명만 고치고, 권한은 전용 화면이\n * 쓴다(`updateRole`). 안 보낸 것을 「비우라」로 읽으면 격자에서 이름만 고친 순간 그 역할이\n * 아무것도 안 여는 역할이 된다.\n *\n * 지우기는 이 자리에 없다 — 격자의 삭제는 `deleteRoles` 가 받고, 그쪽은 **쓰이는 역할을\n * 남긴다**는 규칙을 따로 갖는다.\n */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [Role], { description: 'To create or update multiple roles at once' })\n async updateMultipleRole(\n @Arg('patches', type => [RolePatch]) patches: RolePatch[],\n @Ctx() context: ResolverContext\n ): Promise<Role[]> {\n const { domain, user, tx } = context.state\n const roleRepository = tx.getRepository(Role)\n const results: Role[] = []\n\n /* `cuFlag` 가 없는 줄은 무엇을 하라는 것인지 알 수 없다 — 짐작해서 만들거나 고치지 않는다. */\n const flagOf = (patch: RolePatch) => (patch.cuFlag || '').toUpperCase()\n\n for (const patch of patches.filter(patch => flagOf(patch) === '+')) {\n const name = patch.name?.trim()\n if (!name) {\n throw new Error(context.t('error.value is empty', { value: context.t('field.name') }))\n }\n\n if (await roleRepository.findOne({ where: { name, domain: { id: domain.id } } })) {\n throw new Error('role name is duplicated')\n }\n\n results.push(\n await roleRepository.save({\n domain,\n creator: user,\n updater: user,\n name,\n description: patch.description\n })\n )\n }\n\n for (const patch of patches.filter(patch => flagOf(patch) === 'M')) {\n const role = await roleRepository.findOne({ where: { domain: { id: domain.id }, id: patch.id } })\n\n /* 목록은 상속으로 부모 도메인 역할까지 보여 준다 — 그것을 고치려 하면 여기로 온다. */\n if (!role) {\n throw new Error(\n context.t('error.failed to find x', { x: context.t('label.role') }) +\n ' — a role defined in a parent domain can only be edited in that domain.'\n )\n }\n\n const name = patch.name?.trim()\n if (name && name !== role.name) {\n if (await roleRepository.findOne({ where: { name, domain: { id: domain.id } } })) {\n throw new Error('role name is duplicated')\n }\n role.name = name\n }\n\n if (patch.description !== undefined) {\n role.description = patch.description\n }\n\n results.push(await roleRepository.save({ ...role, updater: user }))\n }\n\n return results\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\")')\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: 'To delete role' })\n async deleteRole(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Boolean> {\n const { tx, domain } = context.state\n let role: Role = await tx\n .getRepository(Role)\n .findOne({ where: { domain: { id: domain.id }, id }, relations: ['users', 'privileges'] })\n\n /*\n * **세 가지 실패가 한 문장으로 나가고 있었다.** 아래 try 의 catch 가 무엇이 났든\n * 「사용 중이라 지울 수 없다」로 401 을 던졌다 — 못 찾은 것도(부모 도메인의 역할이 그렇다),\n * DB 가 실패한 것도 그렇게 말했다. 관리자는 있지도 않은 사용자를 떼러 갔다.\n *\n * 그래서 판단할 수 있는 둘을 먼저 가른다.\n */\n if (!role) {\n throw new Error(\n context.t('error.failed to find x', { x: context.t('label.role') }) +\n ' — a role defined in a parent domain can only be deleted in that domain.'\n )\n }\n\n if (role.users.length) {\n throw new Error('cannot delete role because it is currently in use')\n }\n\n try {\n role.privileges = []\n\n role = await tx.getRepository(Role).save(role)\n\n await tx.getRepository(Role).delete(role.id)\n\n return true\n } catch (e) {\n /* 여기까지 온 것은 지우는 동작 자체의 실패다 — 그 말을 그대로 올린다. */\n context.throw(500, e.message)\n }\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete multiple roles' })\n async deleteRoles(@Arg('ids', type => [String]) ids: string[], @Ctx() context: ResolverContext) {\n const { domain } = context.state\n\n /*\n * **도메인을 봤어야 했다.** 예전에는 받은 id 를 그대로 지웠다 — `user:mutation` 을 가진\n * 사람이 남의 도메인 역할을 id 만으로 지울 수 있었다. `deleteRole`(단수)은 처음부터\n * 도메인으로 좁혀 찾았으므로, 둘의 규칙이 달랐다.\n *\n * 그리고 **사용 중인 역할은 남긴다** — 단수 쪽과 같은 규칙이다. 지운 것과 남은 것을 세어\n * 돌려주지는 않는다(반환형이 `Boolean` 이다). 목록이 다시 읽으면 무엇이 남았는지 보인다.\n */\n const roles = await getRepository(Role).find({\n where: { domain: { id: domain.id }, id: In(ids) },\n relations: ['users']\n })\n\n const deletable = roles.filter(role => !role.users?.length).map(role => role.id)\n if (deletable.length) {\n await getRepository(Role).delete(deletable)\n }\n\n return true\n }\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { RolePrivilege } from './role-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* `rolePrivileges` 가 돌려주는 행에도 **유령 여부**를 붙인다.
|
|
4
|
+
*
|
|
5
|
+
* `Privilege` 타입에는 이미 같은 칸이 있는데(`privilege-query.ts`), `RolePrivilege` 는 다른
|
|
6
|
+
* 타입이므로 그 칸을 못 쓴다. 권한 편집 화면이 드는 것은 이쪽이고, 유령을 못 보면 관리자는
|
|
7
|
+
* **붙여도 아무 일이 없는 권한**을 계속 붙인다.
|
|
8
|
+
*
|
|
9
|
+
* 저장하지 않고 매번 계산하는 이유는 `privilege-deprecation.ts` 에 적혀 있다 — 뜻은 「영원히
|
|
10
|
+
* 죽었다」가 아니라 **「지금 이 배포가 요구하지 않는다」**다.
|
|
11
|
+
*/
|
|
12
|
+
export declare class RolePrivilegeQuery {
|
|
13
|
+
deprecated(rolePrivilege: RolePrivilege): Promise<boolean>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RolePrivilegeQuery = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const privilege_deprecation_js_1 = require("../privilege/privilege-deprecation.js");
|
|
7
|
+
const role_types_js_1 = require("./role-types.js");
|
|
8
|
+
/**
|
|
9
|
+
* `rolePrivileges` 가 돌려주는 행에도 **유령 여부**를 붙인다.
|
|
10
|
+
*
|
|
11
|
+
* `Privilege` 타입에는 이미 같은 칸이 있는데(`privilege-query.ts`), `RolePrivilege` 는 다른
|
|
12
|
+
* 타입이므로 그 칸을 못 쓴다. 권한 편집 화면이 드는 것은 이쪽이고, 유령을 못 보면 관리자는
|
|
13
|
+
* **붙여도 아무 일이 없는 권한**을 계속 붙인다.
|
|
14
|
+
*
|
|
15
|
+
* 저장하지 않고 매번 계산하는 이유는 `privilege-deprecation.ts` 에 적혀 있다 — 뜻은 「영원히
|
|
16
|
+
* 죽었다」가 아니라 **「지금 이 배포가 요구하지 않는다」**다.
|
|
17
|
+
*/
|
|
18
|
+
let RolePrivilegeQuery = class RolePrivilegeQuery {
|
|
19
|
+
async deprecated(rolePrivilege) {
|
|
20
|
+
return (0, privilege_deprecation_js_1.isDeprecatedPrivilege)(process['PRIVILEGES'], rolePrivilege);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
exports.RolePrivilegeQuery = RolePrivilegeQuery;
|
|
24
|
+
tslib_1.__decorate([
|
|
25
|
+
(0, type_graphql_1.FieldResolver)(type => Boolean, {
|
|
26
|
+
description: 'True when no module in this deployment declares this privilege anymore (@privilege directive). ' +
|
|
27
|
+
'Computed per request — it answers for the asking deployment, not for all time.'
|
|
28
|
+
}),
|
|
29
|
+
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
30
|
+
tslib_1.__metadata("design:type", Function),
|
|
31
|
+
tslib_1.__metadata("design:paramtypes", [role_types_js_1.RolePrivilege]),
|
|
32
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
33
|
+
], RolePrivilegeQuery.prototype, "deprecated", null);
|
|
34
|
+
exports.RolePrivilegeQuery = RolePrivilegeQuery = tslib_1.__decorate([
|
|
35
|
+
(0, type_graphql_1.Resolver)(role_types_js_1.RolePrivilege)
|
|
36
|
+
], RolePrivilegeQuery);
|
|
37
|
+
//# sourceMappingURL=role-privilege-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-privilege-query.js","sourceRoot":"","sources":["../../../server/service/role/role-privilege-query.ts"],"names":[],"mappings":";;;;AAAA,+CAA4D;AAE5D,oFAA6E;AAC7E,mDAA+C;AAE/C;;;;;;;;;GASG;AAEI,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IAMvB,AAAN,KAAK,CAAC,UAAU,CAAS,aAA4B;QACnD,OAAO,IAAA,gDAAqB,EAAC,OAAO,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,CAAA;IACpE,CAAC;CACF,CAAA;AATY,gDAAkB;AAMvB;IALL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE;QAC9B,WAAW,EACT,iGAAiG;YACjG,gFAAgF;KACnF,CAAC;IACgB,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAgB,6BAAa;;oDAEpD;6BARU,kBAAkB;IAD9B,IAAA,uBAAQ,EAAC,6BAAa,CAAC;GACX,kBAAkB,CAS9B","sourcesContent":["import { FieldResolver, Resolver, Root } from 'type-graphql'\n\nimport { isDeprecatedPrivilege } from '../privilege/privilege-deprecation.js'\nimport { RolePrivilege } from './role-types.js'\n\n/**\n * `rolePrivileges` 가 돌려주는 행에도 **유령 여부**를 붙인다.\n *\n * `Privilege` 타입에는 이미 같은 칸이 있는데(`privilege-query.ts`), `RolePrivilege` 는 다른\n * 타입이므로 그 칸을 못 쓴다. 권한 편집 화면이 드는 것은 이쪽이고, 유령을 못 보면 관리자는\n * **붙여도 아무 일이 없는 권한**을 계속 붙인다.\n *\n * 저장하지 않고 매번 계산하는 이유는 `privilege-deprecation.ts` 에 적혀 있다 — 뜻은 「영원히\n * 죽었다」가 아니라 **「지금 이 배포가 요구하지 않는다」**다.\n */\n@Resolver(RolePrivilege)\nexport class RolePrivilegeQuery {\n @FieldResolver(type => Boolean, {\n description:\n 'True when no module in this deployment declares this privilege anymore (@privilege directive). ' +\n 'Computed per request — it answers for the asking deployment, not for all time.'\n })\n async deprecated(@Root() rolePrivilege: RolePrivilege): Promise<boolean> {\n return isDeprecatedPrivilege(process['PRIVILEGES'], rolePrivilege)\n }\n}\n"]}
|
|
@@ -6,6 +6,24 @@ import { RoleList, RolePrivilege, UserRole, RoleBrief } from './role-types.js';
|
|
|
6
6
|
export declare class RoleQuery {
|
|
7
7
|
role(name: string, context: ResolverContext): Promise<Role>;
|
|
8
8
|
roles(params: ListParam, context: ResolverContext): Promise<RoleList>;
|
|
9
|
+
/**
|
|
10
|
+
* 한 역할의 권한 — **전체 목록과 이 역할이 가진 것을 한 번에** 돌려준다.
|
|
11
|
+
*
|
|
12
|
+
* 권한 편집 화면이 필요한 것은 「무엇이 있나」와 「이 역할이 무엇을 갖나」 둘인데, 그것을
|
|
13
|
+
* 질의 둘로 가르면 화면이 둘을 짝지어야 하고 그 짝짓기가 역할 **이름**으로 이루어졌다
|
|
14
|
+
* (옛 `role-privilege-editor` — 같은 이름의 부모 도메인 역할과 헷갈렸다). 여기서 `roleId` 로
|
|
15
|
+
* 짝지어 주면 화면이 그 일을 안 한다.
|
|
16
|
+
*
|
|
17
|
+
* ── 원시 SQL 을 걷어냈다 ────────────────────────────────────────────────────
|
|
18
|
+
* 셋을 동시에 잃고 있었다.
|
|
19
|
+
*
|
|
20
|
+
* ① `roleId` 를 문자열로 이어 붙였다. `user:query` 를 가진 사람이 임의의 SQL 을 넣을 수 있다.
|
|
21
|
+
* ② `CASE WHEN … THEN true` 는 방언이다 — MSSQL 에 boolean 리터럴이 없다. 이 저장소는
|
|
22
|
+
* DB 5종을 지원한다.
|
|
23
|
+
* ③ 도메인을 안 봤다. 남의 도메인 역할의 `roleId` 로도 답했다.
|
|
24
|
+
*
|
|
25
|
+
* 권한 마스터는 전체 배포에 84행 규모(도메인마다 늘지 않는다)이므로 관계를 그대로 읽는다.
|
|
26
|
+
*/
|
|
9
27
|
rolePrivileges(roleId: string, context: ResolverContext): Promise<RolePrivilege[]>;
|
|
10
28
|
userRoles(userId: string, context: ResolverContext): Promise<UserRole[]>;
|
|
11
29
|
myRoles(context: ResolverContext): Promise<RoleBrief[]>;
|
|
@@ -28,29 +28,45 @@ let RoleQuery = class RoleQuery {
|
|
|
28
28
|
}).getManyAndCount();
|
|
29
29
|
return { items, total };
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* 한 역할의 권한 — **전체 목록과 이 역할이 가진 것을 한 번에** 돌려준다.
|
|
33
|
+
*
|
|
34
|
+
* 권한 편집 화면이 필요한 것은 「무엇이 있나」와 「이 역할이 무엇을 갖나」 둘인데, 그것을
|
|
35
|
+
* 질의 둘로 가르면 화면이 둘을 짝지어야 하고 그 짝짓기가 역할 **이름**으로 이루어졌다
|
|
36
|
+
* (옛 `role-privilege-editor` — 같은 이름의 부모 도메인 역할과 헷갈렸다). 여기서 `roleId` 로
|
|
37
|
+
* 짝지어 주면 화면이 그 일을 안 한다.
|
|
38
|
+
*
|
|
39
|
+
* ── 원시 SQL 을 걷어냈다 ────────────────────────────────────────────────────
|
|
40
|
+
* 셋을 동시에 잃고 있었다.
|
|
41
|
+
*
|
|
42
|
+
* ① `roleId` 를 문자열로 이어 붙였다. `user:query` 를 가진 사람이 임의의 SQL 을 넣을 수 있다.
|
|
43
|
+
* ② `CASE WHEN … THEN true` 는 방언이다 — MSSQL 에 boolean 리터럴이 없다. 이 저장소는
|
|
44
|
+
* DB 5종을 지원한다.
|
|
45
|
+
* ③ 도메인을 안 봤다. 남의 도메인 역할의 `roleId` 로도 답했다.
|
|
46
|
+
*
|
|
47
|
+
* 권한 마스터는 전체 배포에 84행 규모(도메인마다 늘지 않는다)이므로 관계를 그대로 읽는다.
|
|
48
|
+
*/
|
|
31
49
|
async rolePrivileges(roleId, context) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
`);
|
|
53
|
-
return rolePrivileges;
|
|
50
|
+
const { domain } = context.state;
|
|
51
|
+
/* 상속으로 보이는 부모 도메인 역할까지가 범위다 — `role` 질의와 같은 규칙. */
|
|
52
|
+
const role = await (0, shell_1.getRepository)(role_js_1.Role).findOne({
|
|
53
|
+
where: {
|
|
54
|
+
id: roleId,
|
|
55
|
+
domain: { id: (0, typeorm_1.In)([domain.id, domain.parentId].filter(Boolean)) }
|
|
56
|
+
},
|
|
57
|
+
relations: ['privileges']
|
|
58
|
+
});
|
|
59
|
+
if (!role) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
const assigned = new Set((role.privileges || []).map(privilege => privilege.id));
|
|
63
|
+
return (await (0, shell_1.getRepository)(privilege_js_1.Privilege).find()).map(({ id, name, category, description }) => ({
|
|
64
|
+
id,
|
|
65
|
+
name,
|
|
66
|
+
category,
|
|
67
|
+
description,
|
|
68
|
+
assigned: assigned.has(id)
|
|
69
|
+
}));
|
|
54
70
|
}
|
|
55
71
|
async userRoles(userId, context) {
|
|
56
72
|
const userRoles = await (0, shell_1.getRepository)(role_js_1.Role).query(`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role-query.js","sourceRoot":"","sources":["../../../server/service/role/role-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'\nimport { SelectQueryBuilder } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'\n\nimport { Privilege } from '../privilege/privilege.js'\nimport { User } from '../user/user.js'\nimport { Role } from './role.js'\nimport { RoleList, RolePrivilege, UserRole, RoleBrief } from './role-types.js'\n\n@Resolver(Role)\nexport class RoleQuery {\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => Role, { description: 'To fetch role' })\n async role(@Arg('name') name: string, @Ctx() context: ResolverContext): Promise<Role> {\n const { domain } = context.state\n return await getRepository(Role).findOneBy({\n domain: { id: In([domain.id, domain.parentId].filter(Boolean)) },\n name\n })\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => RoleList, { description: 'To fetch multiple users' })\n async roles(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<RoleList> {\n const { domain } = context.state\n\n const [items, total] = await getQueryBuilderFromListParams({\n repository: getRepository(Role),\n params,\n domain,\n searchables: ['name', 'description']\n }).getManyAndCount()\n\n return { items, total }\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => [RolePrivilege], { description: 'To fetch privileges of a role' })\n async rolePrivileges(@Arg('roleId') roleId: string, @Ctx() context: ResolverContext): Promise<RolePrivilege[]> {\n const rolePrivileges = await getRepository(Privilege).query(\n `\n SELECT\n id,\n name,\n category,\n description,\n CASE WHEN id IN (\n SELECT\n P.id\n FROM\n privileges P JOIN roles_privileges RP\n ON\n P.id = RP.privileges_id\n WHERE\n RP.roles_id = '${roleId}'\n ) THEN true\n ELSE false\n END AS assigned\n FROM\n privileges\n `\n )\n\n return rolePrivileges\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => [UserRole], { description: 'To fetch roles of a user' })\n async userRoles(@Arg('userId') userId: string, @Ctx() context: ResolverContext): Promise<UserRole[]> {\n const userRoles = await getRepository(Role).query(\n `\n SELECT\n id,\n name,\n description,\n CASE WHEN id IN (\n SELECT\n R.id\n FROM\n roles R JOIN users_roles UR\n ON\n R.id = UR.roles_id\n WHERE\n UR.users_id = '${userId}'\n ) THEN true\n ELSE false\n END AS assigned\n FROM\n roles\n `\n )\n\n return userRoles\n }\n\n @Query(returns => [RoleBrief], { description: 'To fetch roles of current user (id, name only)' })\n async myRoles(@Ctx() context: ResolverContext): Promise<RoleBrief[]> {\n const { user, domain } = context.state\n\n const roles = await getRepository(Role)\n .createQueryBuilder('ROLE')\n .leftJoin('ROLE.users', 'USER')\n .leftJoin('ROLE.domain', 'ROLE_DOMAIN')\n .where('USER.id = :userId', { userId: user.id })\n .andWhere('ROLE_DOMAIN.id In(:...domainIds)', { domainIds: [domain.id, domain.parentId].filter(Boolean) })\n .select(['ROLE.id', 'ROLE.name'])\n .getMany()\n\n return roles\n }\n\n @Query(returns => [Role], { description: 'To fetch the preset of role for new user' })\n async checkRolePreset(@Ctx() context: ResolverContext): Promise<Role[]> {\n const rolePreset = config.get('rolePreset') || []\n\n return rolePreset\n }\n\n @FieldResolver(type => Domain)\n async domain(@Root() role: Role) {\n return await getRepository(Domain).findOneBy({ id: role.domainId })\n }\n\n /**\n * Role의 users 필드를 위한 리졸버\n * 사용자 조회 권한이 있는 경우에만 접근 가능\n */\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @FieldResolver(type => [User])\n async users(@Root() role: Role, @Ctx() context: ResolverContext) {\n const roleData = await getRepository(Role).findOne({\n where: { id: role.id },\n relations: ['users']\n })\n\n return roleData?.users || []\n }\n\n @FieldResolver(type => [Privilege])\n async privileges(@Root() role: Role) {\n return (\n await getRepository(Role).findOne({\n where: { id: role.id },\n relations: ['privileges']\n })\n ).privileges\n }\n\n @FieldResolver(type => User)\n async updater(@Root() role: Role): Promise<User> {\n return await getRepository(User).findOneBy({ id: role.updaterId })\n }\n\n @FieldResolver(type => User)\n async creator(@Root() role: Role): Promise<User> {\n return await getRepository(User).findOneBy({ id: role.creatorId })\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"role-query.js","sourceRoot":"","sources":["../../../server/service/role/role-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'\nimport { SelectQueryBuilder } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'\n\nimport { Privilege } from '../privilege/privilege.js'\nimport { User } from '../user/user.js'\nimport { Role } from './role.js'\nimport { RoleList, RolePrivilege, UserRole, RoleBrief } from './role-types.js'\n\n@Resolver(Role)\nexport class RoleQuery {\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => Role, { description: 'To fetch role' })\n async role(@Arg('name') name: string, @Ctx() context: ResolverContext): Promise<Role> {\n const { domain } = context.state\n return await getRepository(Role).findOneBy({\n domain: { id: In([domain.id, domain.parentId].filter(Boolean)) },\n name\n })\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => RoleList, { description: 'To fetch multiple users' })\n async roles(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<RoleList> {\n const { domain } = context.state\n\n const [items, total] = await getQueryBuilderFromListParams({\n repository: getRepository(Role),\n params,\n domain,\n searchables: ['name', 'description']\n }).getManyAndCount()\n\n return { items, total }\n }\n\n /**\n * 한 역할의 권한 — **전체 목록과 이 역할이 가진 것을 한 번에** 돌려준다.\n *\n * 권한 편집 화면이 필요한 것은 「무엇이 있나」와 「이 역할이 무엇을 갖나」 둘인데, 그것을\n * 질의 둘로 가르면 화면이 둘을 짝지어야 하고 그 짝짓기가 역할 **이름**으로 이루어졌다\n * (옛 `role-privilege-editor` — 같은 이름의 부모 도메인 역할과 헷갈렸다). 여기서 `roleId` 로\n * 짝지어 주면 화면이 그 일을 안 한다.\n *\n * ── 원시 SQL 을 걷어냈다 ────────────────────────────────────────────────────\n * 셋을 동시에 잃고 있었다.\n *\n * ① `roleId` 를 문자열로 이어 붙였다. `user:query` 를 가진 사람이 임의의 SQL 을 넣을 수 있다.\n * ② `CASE WHEN … THEN true` 는 방언이다 — MSSQL 에 boolean 리터럴이 없다. 이 저장소는\n * DB 5종을 지원한다.\n * ③ 도메인을 안 봤다. 남의 도메인 역할의 `roleId` 로도 답했다.\n *\n * 권한 마스터는 전체 배포에 84행 규모(도메인마다 늘지 않는다)이므로 관계를 그대로 읽는다.\n */\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => [RolePrivilege], { description: 'To fetch privileges of a role' })\n async rolePrivileges(@Arg('roleId') roleId: string, @Ctx() context: ResolverContext): Promise<RolePrivilege[]> {\n const { domain } = context.state\n\n /* 상속으로 보이는 부모 도메인 역할까지가 범위다 — `role` 질의와 같은 규칙. */\n const role = await getRepository(Role).findOne({\n where: {\n id: roleId,\n domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }\n },\n relations: ['privileges']\n })\n\n if (!role) {\n return []\n }\n\n const assigned = new Set((role.privileges || []).map(privilege => privilege.id))\n\n return (await getRepository(Privilege).find()).map(({ id, name, category, description }) => ({\n id,\n name,\n category,\n description,\n assigned: assigned.has(id)\n }))\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => [UserRole], { description: 'To fetch roles of a user' })\n async userRoles(@Arg('userId') userId: string, @Ctx() context: ResolverContext): Promise<UserRole[]> {\n const userRoles = await getRepository(Role).query(\n `\n SELECT\n id,\n name,\n description,\n CASE WHEN id IN (\n SELECT\n R.id\n FROM\n roles R JOIN users_roles UR\n ON\n R.id = UR.roles_id\n WHERE\n UR.users_id = '${userId}'\n ) THEN true\n ELSE false\n END AS assigned\n FROM\n roles\n `\n )\n\n return userRoles\n }\n\n @Query(returns => [RoleBrief], { description: 'To fetch roles of current user (id, name only)' })\n async myRoles(@Ctx() context: ResolverContext): Promise<RoleBrief[]> {\n const { user, domain } = context.state\n\n const roles = await getRepository(Role)\n .createQueryBuilder('ROLE')\n .leftJoin('ROLE.users', 'USER')\n .leftJoin('ROLE.domain', 'ROLE_DOMAIN')\n .where('USER.id = :userId', { userId: user.id })\n .andWhere('ROLE_DOMAIN.id In(:...domainIds)', { domainIds: [domain.id, domain.parentId].filter(Boolean) })\n .select(['ROLE.id', 'ROLE.name'])\n .getMany()\n\n return roles\n }\n\n @Query(returns => [Role], { description: 'To fetch the preset of role for new user' })\n async checkRolePreset(@Ctx() context: ResolverContext): Promise<Role[]> {\n const rolePreset = config.get('rolePreset') || []\n\n return rolePreset\n }\n\n @FieldResolver(type => Domain)\n async domain(@Root() role: Role) {\n return await getRepository(Domain).findOneBy({ id: role.domainId })\n }\n\n /**\n * Role의 users 필드를 위한 리졸버\n * 사용자 조회 권한이 있는 경우에만 접근 가능\n */\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @FieldResolver(type => [User])\n async users(@Root() role: Role, @Ctx() context: ResolverContext) {\n const roleData = await getRepository(Role).findOne({\n where: { id: role.id },\n relations: ['users']\n })\n\n return roleData?.users || []\n }\n\n @FieldResolver(type => [Privilege])\n async privileges(@Root() role: Role) {\n return (\n await getRepository(Role).findOne({\n where: { id: role.id },\n relations: ['privileges']\n })\n ).privileges\n }\n\n @FieldResolver(type => User)\n async updater(@Root() role: Role): Promise<User> {\n return await getRepository(User).findOneBy({ id: role.updaterId })\n }\n\n @FieldResolver(type => User)\n async creator(@Root() role: Role): Promise<User> {\n return await getRepository(User).findOneBy({ id: role.creatorId })\n }\n}\n"]}
|
|
@@ -13,6 +13,8 @@ const typeorm_1 = require("typeorm");
|
|
|
13
13
|
const env_1 = require("@things-factory/env");
|
|
14
14
|
const shell_1 = require("@things-factory/shell");
|
|
15
15
|
const password_rule_js_1 = require("../../controllers/utils/password-rule.js");
|
|
16
|
+
const account_lock_rule_js_1 = require("../../utils/account-lock-rule.js");
|
|
17
|
+
const lock_duration_js_1 = require("../../utils/lock-duration.js");
|
|
16
18
|
const auth_error_js_1 = require("../../errors/auth-error.js");
|
|
17
19
|
const get_secret_js_1 = require("../../utils/get-secret.js");
|
|
18
20
|
const role_js_1 = require("../role/role.js");
|
|
@@ -99,12 +101,23 @@ let User = User_1 = class User {
|
|
|
99
101
|
}
|
|
100
102
|
});
|
|
101
103
|
case UserStatus.LOCKED:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
/*
|
|
105
|
+
* 잠금이 아직 유효한지는 `utils/account-lock-rule.ts` 한 곳이 판정한다 — `signin` 과
|
|
106
|
+
* 이 자리가 각자 판단하면 로그인은 되는데 요청은 거절되는 상태가 생긴다.
|
|
107
|
+
*
|
|
108
|
+
* **여기서는 행을 고치지 않는다.** 매 요청 지나는 경로라 쓰기를 얹지 않는다. 판정이
|
|
109
|
+
* 「풀렸다」면 통과시키고, 행 정리(`ACTIVATED` · `failCount` 0 · `lockedAt` 비움)는
|
|
110
|
+
* 다음 `signin` 이 한다.
|
|
111
|
+
*/
|
|
112
|
+
if ((0, account_lock_rule_js_1.isAccountLocked)(user, new Date(), (0, lock_duration_js_1.lockDurationMinutes)())) {
|
|
113
|
+
throw new auth_error_js_1.AuthError({
|
|
114
|
+
errorCode: auth_error_js_1.AuthError.ERROR_CODES.USER_LOCKED,
|
|
115
|
+
detail: {
|
|
116
|
+
email: user.email
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
108
121
|
case UserStatus.DELETED:
|
|
109
122
|
throw new auth_error_js_1.AuthError({
|
|
110
123
|
errorCode: auth_error_js_1.AuthError.ERROR_CODES.USER_DELETED
|
|
@@ -159,18 +172,31 @@ let User = User_1 = class User {
|
|
|
159
172
|
}
|
|
160
173
|
});
|
|
161
174
|
case UserStatus.LOCKED:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
175
|
+
/*
|
|
176
|
+
* 잠금이 아직 유효한지는 `utils/account-lock-rule.ts` 한 곳이 판정한다 — `signin` 과
|
|
177
|
+
* 이 자리가 각자 판단하면 로그인은 되는데 요청은 거절되는 상태가 생긴다.
|
|
178
|
+
*
|
|
179
|
+
* **여기서는 행을 고치지 않는다.** 매 요청 지나는 경로라 쓰기를 얹지 않는다. 판정이
|
|
180
|
+
* 「풀렸다」면 통과시키고, 행 정리(`ACTIVATED` · `failCount` 0 · `lockedAt` 비움)는
|
|
181
|
+
* 다음 `signin` 이 한다.
|
|
182
|
+
*/
|
|
183
|
+
if ((0, account_lock_rule_js_1.isAccountLocked)(user, new Date(), (0, lock_duration_js_1.lockDurationMinutes)())) {
|
|
184
|
+
throw new auth_error_js_1.AuthError({
|
|
185
|
+
errorCode: auth_error_js_1.AuthError.ERROR_CODES.USER_LOCKED,
|
|
186
|
+
detail: {
|
|
187
|
+
email: user.email
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
168
192
|
case UserStatus.DELETED:
|
|
169
193
|
throw new auth_error_js_1.AuthError({
|
|
170
194
|
errorCode: auth_error_js_1.AuthError.ERROR_CODES.USER_DELETED
|
|
171
195
|
});
|
|
172
196
|
}
|
|
173
|
-
|
|
197
|
+
/* 설정이 없으면 `{}` 로 — 기본값 없이 구조 분해하면 `password` 를 설정하지 않은 배포에서
|
|
198
|
+
이 줄이 던진다. `password-rule.ts` 도 같은 자리에서 대비를 두고 있다. */
|
|
199
|
+
const { defaultPassword } = env_1.config.get('password', {});
|
|
174
200
|
if (defaultPassword && user.password === this.encode(defaultPassword, user.salt)) {
|
|
175
201
|
user.status = UserStatus.PWD_RESET_REQUIRED;
|
|
176
202
|
}
|
|
@@ -329,8 +355,24 @@ tslib_1.__decorate([
|
|
|
329
355
|
tslib_1.__decorate([
|
|
330
356
|
(0, typeorm_1.Column)({ nullable: true }),
|
|
331
357
|
tslib_1.__metadata("design:type", Date // Last time the password was updated
|
|
358
|
+
/*
|
|
359
|
+
* When the account was locked, so the lock can lift on its own.
|
|
360
|
+
*
|
|
361
|
+
* The decision of whether a lock is still in effect lives in one place —
|
|
362
|
+
* `utils/account-lock-rule.ts` — because two callers ask: `signin`, and `checkAuth` on every
|
|
363
|
+
* request. A row locked before this column existed has no value here, and the rule keeps such a
|
|
364
|
+
* lock in effect rather than releasing it, so nothing opens quietly.
|
|
365
|
+
*/
|
|
332
366
|
)
|
|
333
367
|
], User.prototype, "passwordUpdatedAt", void 0);
|
|
368
|
+
tslib_1.__decorate([
|
|
369
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
370
|
+
(0, type_graphql_1.Field)({
|
|
371
|
+
nullable: true,
|
|
372
|
+
description: 'When the account was locked. With the configured lock duration this says when it lifts.'
|
|
373
|
+
}),
|
|
374
|
+
tslib_1.__metadata("design:type", Date)
|
|
375
|
+
], User.prototype, "lockedAt", void 0);
|
|
334
376
|
tslib_1.__decorate([
|
|
335
377
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Whether the user is the owner of the domain (not persisted in DB).' }),
|
|
336
378
|
tslib_1.__metadata("design:type", Boolean)
|