@things-factory/auth-base 10.1.6 → 10.1.16
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/CHANGELOG.md +47 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/index.js +16 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/router/auth-private-process-router.js +20 -1
- package/dist-server/router/auth-private-process-router.js.map +1 -1
- package/dist-server/service/index.d.ts +3 -0
- package/dist-server/service/index.js +16 -11
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/privilege/privilege-axis.d.ts +72 -0
- package/dist-server/service/privilege/privilege-axis.js +188 -0
- package/dist-server/service/privilege/privilege-axis.js.map +1 -0
- package/dist-server/service/privilege/privilege-directive.d.ts +10 -41
- package/dist-server/service/privilege/privilege-directive.js +62 -74
- package/dist-server/service/privilege/privilege-directive.js.map +1 -1
- package/dist-server/service/role/role-mutation.js +2 -2
- package/dist-server/service/role/role-mutation.js.map +1 -1
- package/dist-server/service/role/role-query.d.ts +43 -1
- package/dist-server/service/role/role-query.js +76 -33
- package/dist-server/service/role/role-query.js.map +1 -1
- package/dist-server/service/role-template/index.d.ts +4 -0
- package/dist-server/service/role-template/index.js +9 -0
- package/dist-server/service/role-template/index.js.map +1 -0
- package/dist-server/service/role-template/role-template-mutation.d.ts +39 -0
- package/dist-server/service/role-template/role-template-mutation.js +124 -0
- package/dist-server/service/role-template/role-template-mutation.js.map +1 -0
- package/dist-server/service/role-template/role-template-query.d.ts +11 -0
- package/dist-server/service/role-template/role-template-query.js +79 -0
- package/dist-server/service/role-template/role-template-query.js.map +1 -0
- package/dist-server/service/role-template/role-template-types.d.ts +51 -0
- package/dist-server/service/role-template/role-template-types.js +71 -0
- package/dist-server/service/role-template/role-template-types.js.map +1 -0
- package/dist-server/service/role-template/role-template.d.ts +110 -0
- package/dist-server/service/role-template/role-template.js +147 -0
- package/dist-server/service/role-template/role-template.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/utils/check-permission.js +11 -2
- package/dist-server/utils/check-permission.js.map +1 -1
- package/package.json +4 -4
- package/tests/domain-inheritance-sentinel.test.ts +93 -0
- package/tests/permission-gate.test.ts +16 -12
- package/tests/privilege-axis.test.ts +157 -0
- package/tests/privilege-directive.test.ts +70 -81
- package/tests/role-privileges-db.test.ts +98 -0
- package/tests/role-template-seed-db.test.ts +179 -0
- package/tests/role-template.test.ts +162 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privilege-directive.js","sourceRoot":"","sources":["../../../server/service/privilege/privilege-directive.ts"],"names":[],"mappings":";;;;AAAA,qCAA6D;AAC7D,sEAA6B;AAE7B,gDAA0E;
|
|
1
|
+
{"version":3,"file":"privilege-directive.js","sourceRoot":"","sources":["../../../server/service/privilege/privilege-directive.ts"],"names":[],"mappings":"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","sourcesContent":["import { defaultFieldResolver, GraphQLSchema } from 'graphql'\nimport gql from 'graphql-tag'\n\nimport { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils'\nimport { logger } from '@things-factory/env'\n\nimport { checkPermission } from '../../utils/check-permission.js'\nimport { declaredAxes, isDeclaredAxis } from './privilege-axis.js'\nimport { privilegeRejection } from './privilege-rejection.js'\n\nprocess['PRIVILEGES'] = {}\n\nconst DIRECTIVE = 'privilege'\n\n/**\n * ⚠ **다음에 목록 인자를 붙이는 사람에게.**\n *\n * 여기 `legacy` 인자가 있었다 — 권한 이름을 옮기는 동안 옛 이름도 받아 주는 별칭이었다.\n * **걷어냈다.** 이 저장소는 대개조 중이고 **호환 계층을 끼우지 않는다**: 이름을 바꾸면 바꾼\n * 것이고, 부여는 관리자가 다시 놓는다. 별칭은 「한동안」이 영원이 되는 길이었다.\n *\n * 곧 필요해지는 목록 인자는 **AND** 다(`docs/design/auth-rebuild.md` §1-(3)): 외부에서\n * 당겨오면서 남의 데이터를 앉히는 호출은 `execute` **와** 그 모듈의 `mutation` 을 **둘 다**\n * 요구해야 한다.\n *\n * 그것을 `privileges: [...]` 처럼 중립적으로 이름 지으면 다음 사람이 「이 중 하나」로 읽고,\n * 그렇게 쓰는 날 게이트가 **오류 없이** 열린다. `requiresAll: [...]` 처럼 **이름이 AND 라고\n * 말하게** 한다.\n */\nexport const privilegeDirectiveTypeDefs = gql`\n directive @privilege(\n category: String\n privilege: String\n domainOwnerGranted: Boolean\n superUserGranted: Boolean\n ) on FIELD_DEFINITION\n`\nexport const privilegeDirectiveResolver = (schema: GraphQLSchema) =>\n mapSchema(schema, {\n [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName, schema) => {\n const privilegeDirective = getDirective(schema, fieldConfig, DIRECTIVE)?.[0]\n if (privilegeDirective) {\n const { resolve = defaultFieldResolver, args } = fieldConfig\n\n if (!args) {\n throw new Error(`Unexpected Error. args should be defined in @privilege directive for field ${fieldName}.`)\n }\n\n const { domainOwnerGranted, superUserGranted, category, privilege } = privilegeDirective\n const where = `${typeName}.${fieldName}`\n\n /*\n * A declaration has to name something.\n *\n * `category` and `privilege` are one thing, not two: together they name a row in the\n * privilege table, and either alone names no row, so `User.hasPrivilege` is never\n * reached and the field falls back on ownership flags it did not set.\n */\n if (!category !== !privilege) {\n throw new Error(\n `@privilege on ${where} gives ${category ? 'a category with no privilege' : 'a privilege with no category'}. ` +\n `The pair names the privilege to look for; one half names none.`\n )\n }\n\n /*\n * `@privilege()` with nothing in it reads like a gate and guards nothing. Until\n * 2026-09-15 what it did depended on where the caller sat: `checkPermission` answered\n * false from a safe address and refused everyone, true from an unsafe one and let\n * everyone through. Both are false now, so the shape is merely useless rather than\n * dangerous — and a field that is open should say so by carrying no directive, not by\n * carrying an empty one.\n *\n * It is also the shape someone reaches for wanting \"being signed in is enough\". That\n * is a real thing to want and this directive does not express it; the answer settled\n * on 2026-09-15 is a category for the published side of a module, `<module>-published`.\n *\n * Throwing stops the boot, which is what §6-2 of docs/design/auth-rebuild.md settled\n * for a resolver that declares nothing. A decorator that declares nothing is that\n * same resolver wearing one.\n */\n if (!category && !privilege && !domainOwnerGranted && !superUserGranted) {\n throw new Error(\n `@privilege on ${where} declares nothing. ` +\n `Name (category, privilege) for a privilege gate, or domainOwnerGranted / superUserGranted for an ` +\n `ownership gate. A field open to everyone carries no directive at all.`\n )\n }\n\n /*\n * The axis has to be one this installation declared.\n *\n * Without this, `privilege: \"mutaiton\"` registered a pair nobody would ever be granted\n * — a gate shut forever, with no error to read. The list is not the framework's: see\n * `privilege-axis.ts` for why, and for the fact that it must be declared at module\n * import time rather than on a bootstrap hook.\n */\n if (privilege && !isDeclaredAxis(privilege)) {\n throw new Error(\n `@privilege on ${where} uses axis \"${privilege}\", which this installation does not declare. ` +\n `Declared axes: ${[...declaredAxes()].sort().join(', ')}. ` +\n `Fix the spelling, or call declareAxes(['${privilege}']) at the top level of the module's ` +\n `server/index.ts — a bootstrap hook runs after the schema is built and is too late.`\n )\n }\n\n if (category && privilege) {\n process['PRIVILEGES'][`${category} ${privilege}`] = [category, privilege]\n }\n\n // 필드의 기존 description 가져오기\n const existingDescription = fieldConfig.description || ''\n\n // 권한 정보를 포함한 새로운 description 생성\n const privilegeDescription =\n `\\n\\n🔒 Requires privilege: ${category}:${privilege}` +\n (domainOwnerGranted ? ', Domain ownership' : '') +\n (superUserGranted ? ', System ownership' : '')\n\n // 기존 description과 결합\n fieldConfig.description = `${existingDescription} ${privilegeDescription}`.trim()\n\n fieldConfig.resolve = async function (source, args, context, info) {\n const { domain, user, unsafeIP, prohibitedPrivileges } = context.state\n\n const permitted = await checkPermission(\n {\n category,\n privilege,\n owner: domainOwnerGranted,\n super: superUserGranted\n },\n user,\n domain,\n unsafeIP,\n prohibitedPrivileges\n )\n\n if (permitted) {\n return await resolve.call(this, source, args, context, info)\n }\n\n /*\n * 안 열렸으면 **거기서 끝이다.**\n *\n * 여기 옛 이름으로 한 번 더 보는 길이 있었다. 그것을 걷어냈다 — 호환 계층을 끼우지\n * 않는다. 이름을 옮기면 부여도 옮기는 것이고, 그 일은 관리자가 역할 화면에서 한다.\n *\n * 문장 만들기는 `privilege-rejection.ts` 가 소유한다.\n */\n throw privilegeRejection(context, category, privilege)\n }\n\n return fieldConfig\n }\n }\n })\n"]}
|
|
@@ -13,8 +13,8 @@ let RoleMutation = class RoleMutation {
|
|
|
13
13
|
/*
|
|
14
14
|
* 선언이 `User` 였다 — 돌려주는 것은 `Role` 인데(아래 TS 반환형도 그렇다) 스키마에는 사용자로
|
|
15
15
|
* 적혀 있었다. 두 이름에 겹치는 칸(`id`·`name`·`description`)만 물어서 지금까지 드러나지
|
|
16
|
-
* 않았고, `email` 을 물으면 오류 없이 빈 값이 온다. 부르는 자리는
|
|
17
|
-
*
|
|
16
|
+
* 않았고, `email` 을 물으면 오류 없이 빈 값이 온다. 부르는 자리는 하나뿐이고 겹치는 칸만
|
|
17
|
+
* 쓴다(`role-list-page.ts` 의 `ox-record-creator`).
|
|
18
18
|
*/
|
|
19
19
|
async createRole(role, context) {
|
|
20
20
|
const { domain, user } = context.state;
|
|
@@ -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 { 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"]}
|
|
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 * 쓴다(`role-list-page.ts` 의 `ox-record-creator`).\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"]}
|
|
@@ -25,15 +25,57 @@ export declare class RoleQuery {
|
|
|
25
25
|
* 권한 마스터는 전체 배포에 84행 규모(도메인마다 늘지 않는다)이므로 관계를 그대로 읽는다.
|
|
26
26
|
*/
|
|
27
27
|
rolePrivileges(roleId: string, context: ResolverContext): Promise<RolePrivilege[]>;
|
|
28
|
+
/**
|
|
29
|
+
* 한 사람의 역할 — **부여할 수 있는 전체와 이 사람이 가진 것을 한 번에.**
|
|
30
|
+
*
|
|
31
|
+
* `rolePrivileges` 와 같은 모양이고, **같은 결함을 갖고 있었다.** 한 자리를 고쳤으면 두 번째
|
|
32
|
+
* 자리도 재야 한다는 것이 이 파일에서 그대로 났다.
|
|
33
|
+
*
|
|
34
|
+
* ① `UR.users_id` 자리에 `userId` 를 문자열로 이어 붙였다. `user:query` 를 가진
|
|
35
|
+
* 사람이 임의의 SQL 을 넣을 수 있다.
|
|
36
|
+
* ② `CASE WHEN … THEN true` 는 방언이다 — MSSQL 에 boolean 리터럴이 없다(DB 5종 지원).
|
|
37
|
+
* ③ `FROM roles` — **도메인을 안 봤다.** 남의 도메인 역할까지 전부 돌려줬다.
|
|
38
|
+
*
|
|
39
|
+
* ③ 을 고치면서 범위를 `role`·`rolePrivileges` 와 같게 뒀다 — 내 도메인과 부모 1단.
|
|
40
|
+
* 그것이 실제로 부여할 수 있는 범위다.
|
|
41
|
+
*
|
|
42
|
+
* 지금 이 질의를 부르는 화면은 없다(옛 `user-role-editor` 는 `user(email:)` 로 읽었다).
|
|
43
|
+
* 그래서 범위를 좁히는 것이 깨뜨릴 소비처가 없다.
|
|
44
|
+
*/
|
|
28
45
|
userRoles(userId: string, context: ResolverContext): Promise<UserRole[]>;
|
|
29
46
|
myRoles(context: ResolverContext): Promise<RoleBrief[]>;
|
|
30
|
-
checkRolePreset(context: ResolverContext): Promise<Role[]>;
|
|
31
47
|
domain(role: Role): Promise<Domain>;
|
|
32
48
|
/**
|
|
33
49
|
* Role의 users 필드를 위한 리졸버
|
|
34
50
|
* 사용자 조회 권한이 있는 경우에만 접근 가능
|
|
35
51
|
*/
|
|
36
52
|
users(role: Role, context: ResolverContext): Promise<User[]>;
|
|
53
|
+
/**
|
|
54
|
+
* 이 역할이 여는 권한이 **몇 개인가.**
|
|
55
|
+
*
|
|
56
|
+
* ── 왜 수만 따로 있나 ──────────────────────────────────────────────────────
|
|
57
|
+
* 목록 화면이 「이 역할이 무엇을 여나」를 말해야 하는데, 그것을 위해 권한 **행**을 받고 있었다.
|
|
58
|
+
*
|
|
59
|
+
* ```
|
|
60
|
+
* [확인함] roles(limit:20) 권한 포함 5,731 bytes · 69ms
|
|
61
|
+
* [확인함] roles(limit:20) 권한 없이 518 bytes · 7ms → 11.1배 · 10배
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* 역할 2개에 권한 행 60개를 받아서 화면에는 잘린 코드 용어 셋을 그렸다. 역할 40개 ×
|
|
65
|
+
* 권한 100개면 한 페이지에 4,000행이다. **`privileges` 필드 리졸버가 역할마다 한 번씩 도는
|
|
66
|
+
* 것**까지 함께 없어지는 것이 더 큰 쪽이다.
|
|
67
|
+
*
|
|
68
|
+
* 그래서 목록은 이 칸을 쓴다 — 행을 안 받고 수를 받는다. 권한을 실제로 고치는 화면은
|
|
69
|
+
* `rolePrivileges(roleId:)` 로 따로 읽는다(전체 목록 + 이 역할이 가진 것을 한 번에).
|
|
70
|
+
*
|
|
71
|
+
* ── 왜 관계를 세지 않고 질의로 세나 ────────────────────────────────────────
|
|
72
|
+
* `relation(...).loadMany()` 는 행을 다 읽고 길이를 재는 것이라 여기서 고치려는 것을 그대로
|
|
73
|
+
* 다시 한다. `getCount()` 는 DB 가 센다.
|
|
74
|
+
*
|
|
75
|
+
* 조인 테이블의 주인은 `Privilege` 쪽이지만(`privilege.ts:72` 의 `@JoinTable`), 양쪽이 관계를
|
|
76
|
+
* 선언하고 있으므로 어느 쪽에서 조인해도 같다.
|
|
77
|
+
*/
|
|
78
|
+
privilegeCount(role: Role): Promise<number>;
|
|
37
79
|
privileges(role: Role): Promise<Privilege[]>;
|
|
38
80
|
updater(role: Role): Promise<User>;
|
|
39
81
|
creator(role: Role): Promise<User>;
|
|
@@ -4,7 +4,6 @@ exports.RoleQuery = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const typeorm_1 = require("typeorm");
|
|
6
6
|
const type_graphql_1 = require("type-graphql");
|
|
7
|
-
const env_1 = require("@things-factory/env");
|
|
8
7
|
const shell_1 = require("@things-factory/shell");
|
|
9
8
|
const privilege_js_1 = require("../privilege/privilege.js");
|
|
10
9
|
const user_js_1 = require("../user/user.js");
|
|
@@ -68,28 +67,31 @@ let RoleQuery = class RoleQuery {
|
|
|
68
67
|
assigned: assigned.has(id)
|
|
69
68
|
}));
|
|
70
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* 한 사람의 역할 — **부여할 수 있는 전체와 이 사람이 가진 것을 한 번에.**
|
|
72
|
+
*
|
|
73
|
+
* `rolePrivileges` 와 같은 모양이고, **같은 결함을 갖고 있었다.** 한 자리를 고쳤으면 두 번째
|
|
74
|
+
* 자리도 재야 한다는 것이 이 파일에서 그대로 났다.
|
|
75
|
+
*
|
|
76
|
+
* ① `UR.users_id` 자리에 `userId` 를 문자열로 이어 붙였다. `user:query` 를 가진
|
|
77
|
+
* 사람이 임의의 SQL 을 넣을 수 있다.
|
|
78
|
+
* ② `CASE WHEN … THEN true` 는 방언이다 — MSSQL 에 boolean 리터럴이 없다(DB 5종 지원).
|
|
79
|
+
* ③ `FROM roles` — **도메인을 안 봤다.** 남의 도메인 역할까지 전부 돌려줬다.
|
|
80
|
+
*
|
|
81
|
+
* ③ 을 고치면서 범위를 `role`·`rolePrivileges` 와 같게 뒀다 — 내 도메인과 부모 1단.
|
|
82
|
+
* 그것이 실제로 부여할 수 있는 범위다.
|
|
83
|
+
*
|
|
84
|
+
* 지금 이 질의를 부르는 화면은 없다(옛 `user-role-editor` 는 `user(email:)` 로 읽었다).
|
|
85
|
+
* 그래서 범위를 좁히는 것이 깨뜨릴 소비처가 없다.
|
|
86
|
+
*/
|
|
71
87
|
async userRoles(userId, context) {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
R.id
|
|
80
|
-
FROM
|
|
81
|
-
roles R JOIN users_roles UR
|
|
82
|
-
ON
|
|
83
|
-
R.id = UR.roles_id
|
|
84
|
-
WHERE
|
|
85
|
-
UR.users_id = '${userId}'
|
|
86
|
-
) THEN true
|
|
87
|
-
ELSE false
|
|
88
|
-
END AS assigned
|
|
89
|
-
FROM
|
|
90
|
-
roles
|
|
91
|
-
`);
|
|
92
|
-
return userRoles;
|
|
88
|
+
const { domain } = context.state;
|
|
89
|
+
const user = await (0, shell_1.getRepository)(user_js_1.User).findOne({ where: { id: userId }, relations: ['roles'] });
|
|
90
|
+
const assigned = new Set((user?.roles || []).map(role => role.id));
|
|
91
|
+
const roles = await (0, shell_1.getRepository)(role_js_1.Role).find({
|
|
92
|
+
where: { domain: { id: (0, typeorm_1.In)([domain.id, domain.parentId].filter(Boolean)) } }
|
|
93
|
+
});
|
|
94
|
+
return roles.map(({ id, name, description }) => ({ id, name, description, assigned: assigned.has(id) }));
|
|
93
95
|
}
|
|
94
96
|
async myRoles(context) {
|
|
95
97
|
const { user, domain } = context.state;
|
|
@@ -103,10 +105,19 @@ let RoleQuery = class RoleQuery {
|
|
|
103
105
|
.getMany();
|
|
104
106
|
return roles;
|
|
105
107
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
/*
|
|
109
|
+
* `checkRolePreset` stood here and has been removed. It read `config.get('rolePreset')`
|
|
110
|
+
* and returned it unchanged, declared as `[Role]`.
|
|
111
|
+
*
|
|
112
|
+
* Nothing set that config key and nothing called the query, across every repository on
|
|
113
|
+
* this machine — and it carried no `@privilege`, so any signed-in user could read
|
|
114
|
+
* whatever an installation later put there. Empty today, quietly public the day someone
|
|
115
|
+
* fills it. Role templates (`service/role-template/`) do what this was reaching for, as
|
|
116
|
+
* a boot registry rather than a config blob.
|
|
117
|
+
*
|
|
118
|
+
* auth-base ships to npm, so consumers outside this repository cannot be counted. The
|
|
119
|
+
* removal is recorded in the changelog — that is the only channel they have.
|
|
120
|
+
*/
|
|
110
121
|
async domain(role) {
|
|
111
122
|
return await (0, shell_1.getRepository)(shell_1.Domain).findOneBy({ id: role.domainId });
|
|
112
123
|
}
|
|
@@ -121,6 +132,38 @@ let RoleQuery = class RoleQuery {
|
|
|
121
132
|
});
|
|
122
133
|
return roleData?.users || [];
|
|
123
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* 이 역할이 여는 권한이 **몇 개인가.**
|
|
137
|
+
*
|
|
138
|
+
* ── 왜 수만 따로 있나 ──────────────────────────────────────────────────────
|
|
139
|
+
* 목록 화면이 「이 역할이 무엇을 여나」를 말해야 하는데, 그것을 위해 권한 **행**을 받고 있었다.
|
|
140
|
+
*
|
|
141
|
+
* ```
|
|
142
|
+
* [확인함] roles(limit:20) 권한 포함 5,731 bytes · 69ms
|
|
143
|
+
* [확인함] roles(limit:20) 권한 없이 518 bytes · 7ms → 11.1배 · 10배
|
|
144
|
+
* ```
|
|
145
|
+
*
|
|
146
|
+
* 역할 2개에 권한 행 60개를 받아서 화면에는 잘린 코드 용어 셋을 그렸다. 역할 40개 ×
|
|
147
|
+
* 권한 100개면 한 페이지에 4,000행이다. **`privileges` 필드 리졸버가 역할마다 한 번씩 도는
|
|
148
|
+
* 것**까지 함께 없어지는 것이 더 큰 쪽이다.
|
|
149
|
+
*
|
|
150
|
+
* 그래서 목록은 이 칸을 쓴다 — 행을 안 받고 수를 받는다. 권한을 실제로 고치는 화면은
|
|
151
|
+
* `rolePrivileges(roleId:)` 로 따로 읽는다(전체 목록 + 이 역할이 가진 것을 한 번에).
|
|
152
|
+
*
|
|
153
|
+
* ── 왜 관계를 세지 않고 질의로 세나 ────────────────────────────────────────
|
|
154
|
+
* `relation(...).loadMany()` 는 행을 다 읽고 길이를 재는 것이라 여기서 고치려는 것을 그대로
|
|
155
|
+
* 다시 한다. `getCount()` 는 DB 가 센다.
|
|
156
|
+
*
|
|
157
|
+
* 조인 테이블의 주인은 `Privilege` 쪽이지만(`privilege.ts:72` 의 `@JoinTable`), 양쪽이 관계를
|
|
158
|
+
* 선언하고 있으므로 어느 쪽에서 조인해도 같다.
|
|
159
|
+
*/
|
|
160
|
+
async privilegeCount(role) {
|
|
161
|
+
return await (0, shell_1.getRepository)(privilege_js_1.Privilege)
|
|
162
|
+
.createQueryBuilder('PRIVILEGE')
|
|
163
|
+
.innerJoin('PRIVILEGE.roles', 'ROLE')
|
|
164
|
+
.where('ROLE.id = :id', { id: role.id })
|
|
165
|
+
.getCount();
|
|
166
|
+
}
|
|
124
167
|
async privileges(role) {
|
|
125
168
|
return (await (0, shell_1.getRepository)(role_js_1.Role).findOne({
|
|
126
169
|
where: { id: role.id },
|
|
@@ -178,13 +221,6 @@ tslib_1.__decorate([
|
|
|
178
221
|
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
179
222
|
tslib_1.__metadata("design:returntype", Promise)
|
|
180
223
|
], RoleQuery.prototype, "myRoles", null);
|
|
181
|
-
tslib_1.__decorate([
|
|
182
|
-
(0, type_graphql_1.Query)(returns => [role_js_1.Role], { description: 'To fetch the preset of role for new user' }),
|
|
183
|
-
tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
|
|
184
|
-
tslib_1.__metadata("design:type", Function),
|
|
185
|
-
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
186
|
-
tslib_1.__metadata("design:returntype", Promise)
|
|
187
|
-
], RoleQuery.prototype, "checkRolePreset", null);
|
|
188
224
|
tslib_1.__decorate([
|
|
189
225
|
(0, type_graphql_1.FieldResolver)(type => shell_1.Domain),
|
|
190
226
|
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
@@ -201,6 +237,13 @@ tslib_1.__decorate([
|
|
|
201
237
|
tslib_1.__metadata("design:paramtypes", [role_js_1.Role, Object]),
|
|
202
238
|
tslib_1.__metadata("design:returntype", Promise)
|
|
203
239
|
], RoleQuery.prototype, "users", null);
|
|
240
|
+
tslib_1.__decorate([
|
|
241
|
+
(0, type_graphql_1.FieldResolver)(type => type_graphql_1.Int),
|
|
242
|
+
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
243
|
+
tslib_1.__metadata("design:type", Function),
|
|
244
|
+
tslib_1.__metadata("design:paramtypes", [role_js_1.Role]),
|
|
245
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
246
|
+
], RoleQuery.prototype, "privilegeCount", null);
|
|
204
247
|
tslib_1.__decorate([
|
|
205
248
|
(0, type_graphql_1.FieldResolver)(type => [privilege_js_1.Privilege]),
|
|
206
249
|
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
@@ -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 /**\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"]}
|
|
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, Int, Query, Resolver, Root } from 'type-graphql'\nimport { SelectQueryBuilder } from 'typeorm'\n\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 /**\n * 한 사람의 역할 — **부여할 수 있는 전체와 이 사람이 가진 것을 한 번에.**\n *\n * `rolePrivileges` 와 같은 모양이고, **같은 결함을 갖고 있었다.** 한 자리를 고쳤으면 두 번째\n * 자리도 재야 한다는 것이 이 파일에서 그대로 났다.\n *\n * ① `UR.users_id` 자리에 `userId` 를 문자열로 이어 붙였다. `user:query` 를 가진\n * 사람이 임의의 SQL 을 넣을 수 있다.\n * ② `CASE WHEN … THEN true` 는 방언이다 — MSSQL 에 boolean 리터럴이 없다(DB 5종 지원).\n * ③ `FROM roles` — **도메인을 안 봤다.** 남의 도메인 역할까지 전부 돌려줬다.\n *\n * ③ 을 고치면서 범위를 `role`·`rolePrivileges` 와 같게 뒀다 — 내 도메인과 부모 1단.\n * 그것이 실제로 부여할 수 있는 범위다.\n *\n * 지금 이 질의를 부르는 화면은 없다(옛 `user-role-editor` 는 `user(email:)` 로 읽었다).\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 { domain } = context.state\n\n const user = await getRepository(User).findOne({ where: { id: userId }, relations: ['roles'] })\n const assigned = new Set((user?.roles || []).map(role => role.id))\n\n const roles = await getRepository(Role).find({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) } }\n })\n\n return roles.map(({ id, name, description }) => ({ id, name, description, assigned: assigned.has(id) }))\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 /*\n * `checkRolePreset` stood here and has been removed. It read `config.get('rolePreset')`\n * and returned it unchanged, declared as `[Role]`.\n *\n * Nothing set that config key and nothing called the query, across every repository on\n * this machine — and it carried no `@privilege`, so any signed-in user could read\n * whatever an installation later put there. Empty today, quietly public the day someone\n * fills it. Role templates (`service/role-template/`) do what this was reaching for, as\n * a boot registry rather than a config blob.\n *\n * auth-base ships to npm, so consumers outside this repository cannot be counted. The\n * removal is recorded in the changelog — that is the only channel they have.\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 /**\n * 이 역할이 여는 권한이 **몇 개인가.**\n *\n * ── 왜 수만 따로 있나 ──────────────────────────────────────────────────────\n * 목록 화면이 「이 역할이 무엇을 여나」를 말해야 하는데, 그것을 위해 권한 **행**을 받고 있었다.\n *\n * ```\n * [확인함] roles(limit:20) 권한 포함 5,731 bytes · 69ms\n * [확인함] roles(limit:20) 권한 없이 518 bytes · 7ms → 11.1배 · 10배\n * ```\n *\n * 역할 2개에 권한 행 60개를 받아서 화면에는 잘린 코드 용어 셋을 그렸다. 역할 40개 ×\n * 권한 100개면 한 페이지에 4,000행이다. **`privileges` 필드 리졸버가 역할마다 한 번씩 도는\n * 것**까지 함께 없어지는 것이 더 큰 쪽이다.\n *\n * 그래서 목록은 이 칸을 쓴다 — 행을 안 받고 수를 받는다. 권한을 실제로 고치는 화면은\n * `rolePrivileges(roleId:)` 로 따로 읽는다(전체 목록 + 이 역할이 가진 것을 한 번에).\n *\n * ── 왜 관계를 세지 않고 질의로 세나 ────────────────────────────────────────\n * `relation(...).loadMany()` 는 행을 다 읽고 길이를 재는 것이라 여기서 고치려는 것을 그대로\n * 다시 한다. `getCount()` 는 DB 가 센다.\n *\n * 조인 테이블의 주인은 `Privilege` 쪽이지만(`privilege.ts:72` 의 `@JoinTable`), 양쪽이 관계를\n * 선언하고 있으므로 어느 쪽에서 조인해도 같다.\n */\n @FieldResolver(type => Int)\n async privilegeCount(@Root() role: Role): Promise<number> {\n return await getRepository(Privilege)\n .createQueryBuilder('PRIVILEGE')\n .innerJoin('PRIVILEGE.roles', 'ROLE')\n .where('ROLE.id = :id', { id: role.id })\n .getCount()\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"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolvers = exports.entities = void 0;
|
|
4
|
+
const role_template_mutation_js_1 = require("./role-template-mutation.js");
|
|
5
|
+
const role_template_query_js_1 = require("./role-template-query.js");
|
|
6
|
+
/* Templates live in the boot registry, not in a table, so there is no entity here. */
|
|
7
|
+
exports.entities = [];
|
|
8
|
+
exports.resolvers = [role_template_query_js_1.RoleTemplateQuery, role_template_mutation_js_1.RoleTemplateMutation];
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/role-template/index.ts"],"names":[],"mappings":";;;AAAA,2EAAkE;AAClE,qEAA4D;AAE5D,sFAAsF;AACzE,QAAA,QAAQ,GAAG,EAAE,CAAA;AACb,QAAA,SAAS,GAAG,CAAC,0CAAiB,EAAE,gDAAoB,CAAC,CAAA","sourcesContent":["import { RoleTemplateMutation } from './role-template-mutation.js'\nimport { RoleTemplateQuery } from './role-template-query.js'\n\n/* Templates live in the boot registry, not in a table, so there is no entity here. */\nexport const entities = []\nexport const resolvers = [RoleTemplateQuery, RoleTemplateMutation]\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Role } from '../role/role.js';
|
|
2
|
+
export declare class RoleTemplateMutation {
|
|
3
|
+
/**
|
|
4
|
+
* Creates a role from a template — once per name.
|
|
5
|
+
*
|
|
6
|
+
* ── Why an existing name is used rather than replaced ────────────────────────
|
|
7
|
+
* Approval lines store a role's **id** (`worklist` resolves an approver of type `Role`
|
|
8
|
+
* by id). Creating a second QUALITY-INSPECTOR would leave every approval routed at the
|
|
9
|
+
* first one while the administrator edits the second, and nothing would report it: the
|
|
10
|
+
* screen shows two roles and the approvals go to the one nobody is looking at.
|
|
11
|
+
*
|
|
12
|
+
* So an existing role of that name is used as it stands. operato-plant's seed already
|
|
13
|
+
* behaves this way (`seed-demo-org.ts` looks the role up and only saves when absent);
|
|
14
|
+
* this is the same rule, stated once.
|
|
15
|
+
*
|
|
16
|
+
* ── Why privileges are added and never removed ───────────────────────────────
|
|
17
|
+
* Two applications may declare a template with the same role name on purpose, and the
|
|
18
|
+
* privileges are then the union — to a user "quality inspector" is one seat. The union
|
|
19
|
+
* only widens: if a template later drops a privilege it stays on roles already seeded,
|
|
20
|
+
* because the seed runs once and the role belongs to the administrator afterwards.
|
|
21
|
+
*
|
|
22
|
+
* A privilege that is no longer declared anywhere is already handled elsewhere and not
|
|
23
|
+
* here — `isDeprecatedPrivilege` marks it, the role screen strikes it through and refuses
|
|
24
|
+
* to switch it back on, and removing it is the administrator's call. Reclaiming it from
|
|
25
|
+
* this side would mean overwriting, and would be unable to tell a stale grant from one
|
|
26
|
+
* the administrator added on purpose.
|
|
27
|
+
*/
|
|
28
|
+
createRoleFromTemplate(templateId: string,
|
|
29
|
+
/**
|
|
30
|
+
* What this plant calls the seat. Defaults to the template's name.
|
|
31
|
+
*
|
|
32
|
+
* The template knows which seat exists in a factory running this product; it does not
|
|
33
|
+
* know what the people there call it. And because a role is also what an approval line
|
|
34
|
+
* points at, the same seat is sometimes needed twice — a day shift and a night shift
|
|
35
|
+
* inspector hold the same privileges and take different approvals. Without a name of
|
|
36
|
+
* their own a template could be used exactly once per domain.
|
|
37
|
+
*/
|
|
38
|
+
name: string | undefined, context: ResolverContext): Promise<Role>;
|
|
39
|
+
}
|