@things-factory/auth-base 10.1.6 → 10.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist-server/index.js +8 -0
  3. package/dist-server/index.js.map +1 -1
  4. package/dist-server/service/index.d.ts +3 -1
  5. package/dist-server/service/index.js +15 -11
  6. package/dist-server/service/index.js.map +1 -1
  7. package/dist-server/service/privilege/privilege-directive.d.ts +10 -41
  8. package/dist-server/service/privilege/privilege-directive.js +14 -74
  9. package/dist-server/service/privilege/privilege-directive.js.map +1 -1
  10. package/dist-server/service/role/role-mutation.js +2 -2
  11. package/dist-server/service/role/role-mutation.js.map +1 -1
  12. package/dist-server/service/role/role-query.d.ts +43 -1
  13. package/dist-server/service/role/role-query.js +76 -33
  14. package/dist-server/service/role/role-query.js.map +1 -1
  15. package/dist-server/service/role-template/index.d.ts +4 -0
  16. package/dist-server/service/role-template/index.js +9 -0
  17. package/dist-server/service/role-template/index.js.map +1 -0
  18. package/dist-server/service/role-template/role-template-mutation.d.ts +39 -0
  19. package/dist-server/service/role-template/role-template-mutation.js +124 -0
  20. package/dist-server/service/role-template/role-template-mutation.js.map +1 -0
  21. package/dist-server/service/role-template/role-template-query.d.ts +11 -0
  22. package/dist-server/service/role-template/role-template-query.js +79 -0
  23. package/dist-server/service/role-template/role-template-query.js.map +1 -0
  24. package/dist-server/service/role-template/role-template-types.d.ts +51 -0
  25. package/dist-server/service/role-template/role-template-types.js +71 -0
  26. package/dist-server/service/role-template/role-template-types.js.map +1 -0
  27. package/dist-server/service/role-template/role-template.d.ts +110 -0
  28. package/dist-server/service/role-template/role-template.js +129 -0
  29. package/dist-server/service/role-template/role-template.js.map +1 -0
  30. package/dist-server/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +4 -4
  32. package/tests/privilege-directive.test.ts +1 -91
  33. package/tests/role-privileges-db.test.ts +98 -0
  34. package/tests/role-template-seed-db.test.ts +179 -0
  35. package/tests/role-template.test.ts +126 -0
package/CHANGELOG.md CHANGED
@@ -6,3 +6,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
6
  and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  <!-- ## [Unreleased] -->
9
+
10
+ ### Removed
11
+
12
+ - **`checkRolePreset` query.** It read `config.get('rolePreset')` and returned the value
13
+ unchanged while declaring `[Role]`, and it carried no `@privilege` — any signed-in user
14
+ could read whatever an installation had put there. No repository here set the key or
15
+ called the query, but this package ships to npm and consumers outside cannot be counted,
16
+ so the removal is recorded here. Role templates replace what it was reaching for.
17
+
18
+ ### Added
19
+
20
+ - **Role templates** (`registerRoleTemplate`, `roleTemplates`, `createRoleFromTemplate`).
21
+ An application declares the seats it offers — `{ category, axes }` per module — and an
22
+ administrator starts a role from one instead of picking privileges out of a list of 84.
23
+ Templates live in a boot registry rather than a table, and seeding runs once: the role
24
+ belongs to the administrator afterwards.
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const shell_1 = require("@things-factory/shell");
5
5
  const privilege_js_1 = require("./service/privilege/privilege.js");
6
+ const role_template_js_1 = require("./service/role-template/role-template.js");
6
7
  require("./routes.js");
7
8
  tslib_1.__exportStar(require("./service/index.js"), exports);
8
9
  tslib_1.__exportStar(require("./migrations/index.js"), exports);
@@ -32,5 +33,12 @@ process.on('bootstrap-module-start', async ({ app, config, client }) => {
32
33
  }
33
34
  }
34
35
  console.log('[auth-base:bootstrap] Synchronization for privilege master has just done.');
36
+ /*
37
+ * Templates name privileges by (category, axis), and a category can move. A template
38
+ * that survives a rename becomes a role that grants nothing — the administrator picks
39
+ * "quality inspector", gets a role, and no door opens. Check the names once, here, where
40
+ * the privilege registry has just been filled.
41
+ */
42
+ (0, role_template_js_1.reportUnknownTemplateGrants)();
35
43
  });
36
44
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,iDAAqD;AACrD,mEAA4D;AAE5D,uBAAoB;AAEpB,6DAAkC;AAClC,gEAAqC;AAErC,iEAAsC;AACtC,4DAAiC;AACjC,sDAA2B;AAE3B,sEAA2C;AAC3C,sEAA2C;AAC3C,yEAA8C;AAC9C,6EAAkD;AAClD,sEAA2C;AAC3C,gEAAqC;AACrC,+EAAoD;AACpD,yEAA8C;AAC9C,mEAAwC;AACxC,sEAA2C;AAC3C,yEAA8C;AAC9C,oEAAyC;AAEzC,4DAAiC;AAEjC,qDAA0B;AAE1B,OAAO,CAAC,EAAE,CAAC,wBAA+B,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAO,EAAE,EAAE;IACjF,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACxC,MAAM,mBAAmB,GAAG,IAAA,qBAAa,EAAC,wBAAS,CAAC,CAAA;IAEpD,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,UAA8B,CAAC,EAAE,CAAC;QAC7E,IAAI,CAAC,IAAI,CAAC,MAAM,mBAAmB,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;YAC1E,MAAM,mBAAmB,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;QACpD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAA;AAC1F,CAAC,CAAC,CAAA","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { Privilege } from './service/privilege/privilege.js'\n\nimport './routes.js'\n\nexport * from './service/index.js'\nexport * from './migrations/index.js'\n\nexport * from './middlewares/index.js'\nexport * from './router/index.js'\nexport * from './routes.js'\n\nexport * from './utils/get-domain-users.js'\nexport * from './utils/get-user-domains.js'\nexport * from './utils/checkin-domain-rule.js'\nexport * from './utils/can-user-checkin-domain.js'\nexport * from './utils/domain-ownership.js'\nexport * from './utils/get-secret.js'\nexport * from './utils/check-user-belongs-domain.js'\nexport * from './utils/access-token-cookie.js'\nexport * from './utils/encrypt-state.js'\nexport * from './utils/check-permission.js'\nexport * from './utils/check-user-has-role.js'\nexport * from './utils/build-auth-url.js'\n\nexport * from './errors/index.js'\n\nexport * from './types.js'\n\nprocess.on('bootstrap-module-start' as any, async ({ app, config, client }: any) => {\n const privileges = process['PRIVILEGES']\n const privilegeRepository = getRepository(Privilege)\n\n for (const [category, name] of Object.values(privileges as [string, string])) {\n if (0 == (await privilegeRepository.count({ where: { category, name } }))) {\n await privilegeRepository.save({ category, name })\n }\n }\n\n console.log('[auth-base:bootstrap] Synchronization for privilege master has just done.')\n})\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,iDAAqD;AACrD,mEAA4D;AAC5D,+EAAsF;AAEtF,uBAAoB;AAEpB,6DAAkC;AAClC,gEAAqC;AAErC,iEAAsC;AACtC,4DAAiC;AACjC,sDAA2B;AAE3B,sEAA2C;AAC3C,sEAA2C;AAC3C,yEAA8C;AAC9C,6EAAkD;AAClD,sEAA2C;AAC3C,gEAAqC;AACrC,+EAAoD;AACpD,yEAA8C;AAC9C,mEAAwC;AACxC,sEAA2C;AAC3C,yEAA8C;AAC9C,oEAAyC;AAEzC,4DAAiC;AAEjC,qDAA0B;AAE1B,OAAO,CAAC,EAAE,CAAC,wBAA+B,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAO,EAAE,EAAE;IACjF,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACxC,MAAM,mBAAmB,GAAG,IAAA,qBAAa,EAAC,wBAAS,CAAC,CAAA;IAEpD,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,UAA8B,CAAC,EAAE,CAAC;QAC7E,IAAI,CAAC,IAAI,CAAC,MAAM,mBAAmB,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;YAC1E,MAAM,mBAAmB,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;QACpD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAA;IAExF;;;;;OAKG;IACH,IAAA,8CAA2B,GAAE,CAAA;AAC/B,CAAC,CAAC,CAAA","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { Privilege } from './service/privilege/privilege.js'\nimport { reportUnknownTemplateGrants } from './service/role-template/role-template.js'\n\nimport './routes.js'\n\nexport * from './service/index.js'\nexport * from './migrations/index.js'\n\nexport * from './middlewares/index.js'\nexport * from './router/index.js'\nexport * from './routes.js'\n\nexport * from './utils/get-domain-users.js'\nexport * from './utils/get-user-domains.js'\nexport * from './utils/checkin-domain-rule.js'\nexport * from './utils/can-user-checkin-domain.js'\nexport * from './utils/domain-ownership.js'\nexport * from './utils/get-secret.js'\nexport * from './utils/check-user-belongs-domain.js'\nexport * from './utils/access-token-cookie.js'\nexport * from './utils/encrypt-state.js'\nexport * from './utils/check-permission.js'\nexport * from './utils/check-user-has-role.js'\nexport * from './utils/build-auth-url.js'\n\nexport * from './errors/index.js'\n\nexport * from './types.js'\n\nprocess.on('bootstrap-module-start' as any, async ({ app, config, client }: any) => {\n const privileges = process['PRIVILEGES']\n const privilegeRepository = getRepository(Privilege)\n\n for (const [category, name] of Object.values(privileges as [string, string])) {\n if (0 == (await privilegeRepository.count({ where: { category, name } }))) {\n await privilegeRepository.save({ category, name })\n }\n }\n\n console.log('[auth-base:bootstrap] Synchronization for privilege master has just done.')\n\n /*\n * Templates name privileges by (category, axis), and a category can move. A template\n * that survives a rename becomes a role that grants nothing — the administrator picks\n * \"quality inspector\", gets a role, and no door opens. Check the names once, here, where\n * the privilege registry has just been filled.\n */\n reportUnknownTemplateGrants()\n})\n"]}
@@ -4,6 +4,8 @@ export * from './application/application.js';
4
4
  export * from './appliance/appliance.js';
5
5
  export * from './privilege/privilege.js';
6
6
  export * from './role/role.js';
7
+ export * from './role-template/role-template.js';
8
+ export * from './role-template/role-template-types.js';
7
9
  export * from './user/user.js';
8
10
  export * from './partner/partner.js';
9
11
  export * from './granted-role/granted-role.js';
@@ -25,7 +27,7 @@ export * from './privilege/privilege-types.js';
25
27
  export * from './role/role-types.js';
26
28
  export * from './user/user-types.js';
27
29
  export * from './domain-link/domain-link-types.js';
28
- export declare const entities: (typeof import("./web-auth-credential/web-auth-credential.js").WebAuthCredential | typeof import("./user/user.js").User | typeof import("./auth-provider/auth-provider.js").AuthProvider | typeof import("./users-auth-providers/users-auth-providers.js").UsersAuthProviders | typeof import("./domain-owner/domain-owner.js").DomainOwner | typeof import("./granted-role/granted-role.js").GrantedRole | typeof import("./role/role.js").Role | typeof import("./privilege/privilege.js").Privilege | typeof import("./verification-token/verification-token.js").VerificationToken | typeof import("./verification-token/verification-token.js").VerificationTokenType | typeof import("./password-history/password-history.js").PasswordHistory | typeof import("./invitation/invitation.js").Invitation | typeof import("./application/application.js").Application | typeof import("./login-history/login-history.js").LoginHistory | typeof import("./appliance/appliance.js").Appliance | typeof import("./partner/partner.js").Partner | typeof import("./domain-link/domain-link.js").DomainLink)[];
30
+ export declare const entities: (typeof import("./users-auth-providers/users-auth-providers.js").UsersAuthProviders | typeof import("./auth-provider/auth-provider.js").AuthProvider | typeof import("./application/application.js").Application | typeof import("./appliance/appliance.js").Appliance | typeof import("./privilege/privilege.js").Privilege | typeof import("./role/role.js").Role | typeof import("./user/user.js").User | typeof import("./partner/partner.js").Partner | typeof import("./granted-role/granted-role.js").GrantedRole | typeof import("./invitation/invitation.js").Invitation | typeof import("./password-history/password-history.js").PasswordHistory | typeof import("./verification-token/verification-token.js").VerificationToken | typeof import("./verification-token/verification-token.js").VerificationTokenType | typeof import("./login-history/login-history.js").LoginHistory | typeof import("./web-auth-credential/web-auth-credential.js").WebAuthCredential | typeof import("./domain-link/domain-link.js").DomainLink | typeof import("./domain-owner/domain-owner.js").DomainOwner)[];
29
31
  export declare const schema: {
30
32
  typeDefs: {
31
33
  privilegeDirectiveTypeDefs: import("graphql").DocumentNode;
@@ -17,11 +17,12 @@ const index_js_11 = require("./password-history/index.js");
17
17
  const index_js_12 = require("./privilege/index.js");
18
18
  const privilege_directive_js_1 = require("./privilege/privilege-directive.js");
19
19
  const index_js_13 = require("./role/index.js");
20
- const index_js_14 = require("./user/index.js");
21
- const index_js_15 = require("./verification-token/index.js");
22
- const index_js_16 = require("./web-auth-credential/index.js");
23
- const index_js_17 = require("./domain-link/index.js");
24
- const index_js_18 = require("./domain-owner/index.js");
20
+ const index_js_14 = require("./role-template/index.js");
21
+ const index_js_15 = require("./user/index.js");
22
+ const index_js_16 = require("./verification-token/index.js");
23
+ const index_js_17 = require("./web-auth-credential/index.js");
24
+ const index_js_18 = require("./domain-link/index.js");
25
+ const index_js_19 = require("./domain-owner/index.js");
25
26
  /* EXPORT ENTITY TYPES */
26
27
  tslib_1.__exportStar(require("./users-auth-providers/users-auth-providers.js"), exports);
27
28
  tslib_1.__exportStar(require("./auth-provider/auth-provider.js"), exports);
@@ -29,6 +30,8 @@ tslib_1.__exportStar(require("./application/application.js"), exports);
29
30
  tslib_1.__exportStar(require("./appliance/appliance.js"), exports);
30
31
  tslib_1.__exportStar(require("./privilege/privilege.js"), exports);
31
32
  tslib_1.__exportStar(require("./role/role.js"), exports);
33
+ tslib_1.__exportStar(require("./role-template/role-template.js"), exports);
34
+ tslib_1.__exportStar(require("./role-template/role-template-types.js"), exports);
32
35
  tslib_1.__exportStar(require("./user/user.js"), exports);
33
36
  tslib_1.__exportStar(require("./partner/partner.js"), exports);
34
37
  tslib_1.__exportStar(require("./granted-role/granted-role.js"), exports);
@@ -59,16 +62,16 @@ exports.entities = [
59
62
  ...index_js_4.entities,
60
63
  ...index_js_12.entities,
61
64
  ...index_js_13.entities,
62
- ...index_js_14.entities,
65
+ ...index_js_15.entities,
63
66
  ...index_js_10.entities,
64
67
  ...index_js_7.entities,
65
68
  ...index_js_8.entities,
66
69
  ...index_js_11.entities,
67
- ...index_js_15.entities,
68
- ...index_js_9.entities,
69
70
  ...index_js_16.entities,
71
+ ...index_js_9.entities,
70
72
  ...index_js_17.entities,
71
- ...index_js_18.entities
73
+ ...index_js_18.entities,
74
+ ...index_js_19.entities
72
75
  ];
73
76
  exports.schema = {
74
77
  typeDefs: {
@@ -83,14 +86,15 @@ exports.schema = {
83
86
  ...index_js_12.resolvers,
84
87
  ...index_js_13.resolvers,
85
88
  ...index_js_14.resolvers,
89
+ ...index_js_15.resolvers,
86
90
  ...index_js_10.resolvers,
87
91
  ...index_js_7.resolvers,
88
92
  ...index_js_8.resolvers,
89
93
  ...index_js_3.resolvers,
90
94
  ...index_js_6.resolvers,
91
95
  ...index_js_9.resolvers,
92
- ...index_js_17.resolvers,
93
- ...index_js_18.resolvers
96
+ ...index_js_18.resolvers,
97
+ ...index_js_19.resolvers
94
98
  ],
95
99
  directives: {
96
100
  privilege: privilege_directive_js_1.privilegeDirectiveResolver
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,mCAAmC;AACnC,8DAGwC;AACxC,uDAA+G;AAC/G,qDAAwE;AACxE,mDAAqG;AACrG,qDAA2G;AAC3G,0DAAkF;AAClF,sDAA2G;AAC3G,oDAAuG;AACvG,uDAA8G;AAC9G,kDAA+F;AAC/F,2DAAiF;AACjF,oDAAqG;AACrG,+EAA2G;AAC3G,+CAAsF;AACtF,+CAAsF;AACtF,6DAAqF;AACrF,8DAAsF;AACtF,sDAAwG;AACxG,uDAA4G;AAE5G,yBAAyB;AACzB,yFAA8D;AAC9D,2EAAgD;AAChD,uEAA4C;AAC5C,mEAAwC;AACxC,mEAAwC;AACxC,yDAA8B;AAC9B,yDAA8B;AAC9B,+DAAoC;AACpC,yEAA8C;AAC9C,qEAA0C;AAC1C,uEAA4C;AAC5C,iFAAsD;AACtD,qFAA0D;AAC1D,2EAAgD;AAChD,uFAA4D;AAC5D,uEAA4C;AAC5C,yEAA8C;AAE9C,kBAAkB;AAClB,6EAAkD;AAClD,yEAA8C;AAC9C,6EAAkD;AAClD,uFAA4D;AAC5D,2EAAgD;AAChD,qEAA0C;AAC1C,yEAA8C;AAC9C,+DAAoC;AACpC,+DAAoC;AACpC,6EAAkD;AAErC,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,mBAA0B;IAC7B,GAAG,mBAAoB;IACvB,GAAG,mBAAmB;IACtB,GAAG,mBAAiB;IACpB,GAAG,oBAAiB;IACpB,GAAG,oBAAY;IACf,GAAG,oBAAY;IACf,GAAG,oBAAe;IAClB,GAAG,mBAAmB;IACtB,GAAG,mBAAkB;IACrB,GAAG,oBAAuB;IAC1B,GAAG,oBAAyB;IAC5B,GAAG,mBAAoB;IACvB,GAAG,oBAAyB;IAC5B,GAAG,oBAAkB;IACrB,GAAG,oBAAmB;CACvB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,QAAQ,EAAE;QACR,0BAA0B,EAA1B,mDAA0B;KAC3B;IAED,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,oBAA2B;QAC9B,GAAG,oBAAqB;QACxB,GAAG,oBAAoB;QACvB,GAAG,oBAAkB;QACrB,GAAG,qBAAkB;QACrB,GAAG,qBAAa;QAChB,GAAG,qBAAa;QAChB,GAAG,qBAAgB;QACnB,GAAG,oBAAmB;QACtB,GAAG,oBAAkB;QACrB,GAAG,oBAAkB;QACrB,GAAG,oBAAuB;QAC1B,GAAG,oBAAoB;QACvB,GAAG,qBAAkB;QACrB,GAAG,qBAAoB;KACxB;IACD,UAAU,EAAE;QACV,SAAS,EAAE,mDAA0B;KACtC;CACF,CAAA","sourcesContent":["/* IMPORT ENTITIES AND RESOLVERS */\nimport {\n entities as UsersAuthProvidersEntities,\n resolvers as UsersAuthProvidersResolvers\n} from './users-auth-providers/index.js'\nimport { entities as AuthProviderEntities, resolvers as AuthProviderResolvers } from './auth-provider/index.js'\nimport { resolvers as AppbindingResolver } from './app-binding/index.js'\nimport { entities as ApplianceEntities, resolvers as ApplianceResolvers } from './appliance/index.js'\nimport { entities as ApplicationEntities, resolvers as ApplicationResolvers } from './application/index.js'\nimport { resolvers as DomainGeneratorResolver } from './domain-generator/index.js'\nimport { entities as GrantedRoleEntities, resolvers as GrantedRoleResolver } from './granted-role/index.js'\nimport { entities as InvitationEntities, resolvers as InvitationResolver } from './invitation/index.js'\nimport { entities as LoginHistoryEntities, resolvers as LoginHistoryResolver } from './login-history/index.js'\nimport { entities as PartnerEntities, resolvers as PartnerResolvers } from './partner/index.js'\nimport { entities as PasswordHistoryEntities } from './password-history/index.js'\nimport { entities as PrivilegeEntities, resolvers as PrivilegeResolvers } from './privilege/index.js'\nimport { privilegeDirectiveResolver, privilegeDirectiveTypeDefs } from './privilege/privilege-directive.js'\nimport { entities as RoleEntities, resolvers as RoleResolvers } from './role/index.js'\nimport { entities as UserEntities, resolvers as UserResolvers } from './user/index.js'\nimport { entities as VerificationTokenEntities } from './verification-token/index.js'\nimport { entities as WebAuthCredentialEntities } from './web-auth-credential/index.js'\nimport { entities as DomainLinkEntities, resolvers as DomainLinkResolver } from './domain-link/index.js'\nimport { entities as DomainOwnerEntities, resolvers as DomainOwnerResolvers } from './domain-owner/index.js'\n\n/* EXPORT ENTITY TYPES */\nexport * from './users-auth-providers/users-auth-providers.js'\nexport * from './auth-provider/auth-provider.js'\nexport * from './application/application.js'\nexport * from './appliance/appliance.js'\nexport * from './privilege/privilege.js'\nexport * from './role/role.js'\nexport * from './user/user.js'\nexport * from './partner/partner.js'\nexport * from './granted-role/granted-role.js'\nexport * from './invitation/invitation.js'\nexport * from './app-binding/app-binding.js'\nexport * from './password-history/password-history.js'\nexport * from './verification-token/verification-token.js'\nexport * from './login-history/login-history.js'\nexport * from './web-auth-credential/web-auth-credential.js'\nexport * from './domain-link/domain-link.js'\nexport * from './domain-owner/domain-owner.js'\n\n/* EXPORT TYPES */\nexport * from './app-binding/app-binding-types.js'\nexport * from './appliance/appliance-types.js'\nexport * from './application/application-types.js'\nexport * from './domain-generator/domain-generator-types.js'\nexport * from './invitation/invitation-types.js'\nexport * from './partner/partner-types.js'\nexport * from './privilege/privilege-types.js'\nexport * from './role/role-types.js'\nexport * from './user/user-types.js'\nexport * from './domain-link/domain-link-types.js'\n\nexport const entities = [\n /* ENTITIES */\n ...UsersAuthProvidersEntities,\n ...AuthProviderEntities,\n ...ApplicationEntities,\n ...ApplianceEntities,\n ...PrivilegeEntities,\n ...RoleEntities,\n ...UserEntities,\n ...PartnerEntities,\n ...GrantedRoleEntities,\n ...InvitationEntities,\n ...PasswordHistoryEntities,\n ...VerificationTokenEntities,\n ...LoginHistoryEntities,\n ...WebAuthCredentialEntities,\n ...DomainLinkEntities,\n ...DomainOwnerEntities\n]\n\nexport const schema = {\n typeDefs: {\n privilegeDirectiveTypeDefs\n },\n\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...UsersAuthProvidersResolvers,\n ...AuthProviderResolvers,\n ...ApplicationResolvers,\n ...ApplianceResolvers,\n ...PrivilegeResolvers,\n ...RoleResolvers,\n ...UserResolvers,\n ...PartnerResolvers,\n ...GrantedRoleResolver,\n ...InvitationResolver,\n ...AppbindingResolver,\n ...DomainGeneratorResolver,\n ...LoginHistoryResolver,\n ...DomainLinkResolver,\n ...DomainOwnerResolvers\n ],\n directives: {\n privilege: privilegeDirectiveResolver\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,mCAAmC;AACnC,8DAGwC;AACxC,uDAA+G;AAC/G,qDAAwE;AACxE,mDAAqG;AACrG,qDAA2G;AAC3G,0DAAkF;AAClF,sDAA2G;AAC3G,oDAAuG;AACvG,uDAA8G;AAC9G,kDAA+F;AAC/F,2DAAiF;AACjF,oDAAqG;AACrG,+EAA2G;AAC3G,+CAAsF;AACtF,wDAA6E;AAC7E,+CAAsF;AACtF,6DAAqF;AACrF,8DAAsF;AACtF,sDAAwG;AACxG,uDAA4G;AAE5G,yBAAyB;AACzB,yFAA8D;AAC9D,2EAAgD;AAChD,uEAA4C;AAC5C,mEAAwC;AACxC,mEAAwC;AACxC,yDAA8B;AAC9B,2EAAgD;AAChD,iFAAsD;AACtD,yDAA8B;AAC9B,+DAAoC;AACpC,yEAA8C;AAC9C,qEAA0C;AAC1C,uEAA4C;AAC5C,iFAAsD;AACtD,qFAA0D;AAC1D,2EAAgD;AAChD,uFAA4D;AAC5D,uEAA4C;AAC5C,yEAA8C;AAE9C,kBAAkB;AAClB,6EAAkD;AAClD,yEAA8C;AAC9C,6EAAkD;AAClD,uFAA4D;AAC5D,2EAAgD;AAChD,qEAA0C;AAC1C,yEAA8C;AAC9C,+DAAoC;AACpC,+DAAoC;AACpC,6EAAkD;AAErC,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,mBAA0B;IAC7B,GAAG,mBAAoB;IACvB,GAAG,mBAAmB;IACtB,GAAG,mBAAiB;IACpB,GAAG,oBAAiB;IACpB,GAAG,oBAAY;IACf,GAAG,oBAAY;IACf,GAAG,oBAAe;IAClB,GAAG,mBAAmB;IACtB,GAAG,mBAAkB;IACrB,GAAG,oBAAuB;IAC1B,GAAG,oBAAyB;IAC5B,GAAG,mBAAoB;IACvB,GAAG,oBAAyB;IAC5B,GAAG,oBAAkB;IACrB,GAAG,oBAAmB;CACvB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,QAAQ,EAAE;QACR,0BAA0B,EAA1B,mDAA0B;KAC3B;IAED,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,oBAA2B;QAC9B,GAAG,oBAAqB;QACxB,GAAG,oBAAoB;QACvB,GAAG,oBAAkB;QACrB,GAAG,qBAAkB;QACrB,GAAG,qBAAa;QAChB,GAAG,qBAAqB;QACxB,GAAG,qBAAa;QAChB,GAAG,qBAAgB;QACnB,GAAG,oBAAmB;QACtB,GAAG,oBAAkB;QACrB,GAAG,oBAAkB;QACrB,GAAG,oBAAuB;QAC1B,GAAG,oBAAoB;QACvB,GAAG,qBAAkB;QACrB,GAAG,qBAAoB;KACxB;IACD,UAAU,EAAE;QACV,SAAS,EAAE,mDAA0B;KACtC;CACF,CAAA","sourcesContent":["/* IMPORT ENTITIES AND RESOLVERS */\nimport {\n entities as UsersAuthProvidersEntities,\n resolvers as UsersAuthProvidersResolvers\n} from './users-auth-providers/index.js'\nimport { entities as AuthProviderEntities, resolvers as AuthProviderResolvers } from './auth-provider/index.js'\nimport { resolvers as AppbindingResolver } from './app-binding/index.js'\nimport { entities as ApplianceEntities, resolvers as ApplianceResolvers } from './appliance/index.js'\nimport { entities as ApplicationEntities, resolvers as ApplicationResolvers } from './application/index.js'\nimport { resolvers as DomainGeneratorResolver } from './domain-generator/index.js'\nimport { entities as GrantedRoleEntities, resolvers as GrantedRoleResolver } from './granted-role/index.js'\nimport { entities as InvitationEntities, resolvers as InvitationResolver } from './invitation/index.js'\nimport { entities as LoginHistoryEntities, resolvers as LoginHistoryResolver } from './login-history/index.js'\nimport { entities as PartnerEntities, resolvers as PartnerResolvers } from './partner/index.js'\nimport { entities as PasswordHistoryEntities } from './password-history/index.js'\nimport { entities as PrivilegeEntities, resolvers as PrivilegeResolvers } from './privilege/index.js'\nimport { privilegeDirectiveResolver, privilegeDirectiveTypeDefs } from './privilege/privilege-directive.js'\nimport { entities as RoleEntities, resolvers as RoleResolvers } from './role/index.js'\nimport { resolvers as RoleTemplateResolvers } from './role-template/index.js'\nimport { entities as UserEntities, resolvers as UserResolvers } from './user/index.js'\nimport { entities as VerificationTokenEntities } from './verification-token/index.js'\nimport { entities as WebAuthCredentialEntities } from './web-auth-credential/index.js'\nimport { entities as DomainLinkEntities, resolvers as DomainLinkResolver } from './domain-link/index.js'\nimport { entities as DomainOwnerEntities, resolvers as DomainOwnerResolvers } from './domain-owner/index.js'\n\n/* EXPORT ENTITY TYPES */\nexport * from './users-auth-providers/users-auth-providers.js'\nexport * from './auth-provider/auth-provider.js'\nexport * from './application/application.js'\nexport * from './appliance/appliance.js'\nexport * from './privilege/privilege.js'\nexport * from './role/role.js'\nexport * from './role-template/role-template.js'\nexport * from './role-template/role-template-types.js'\nexport * from './user/user.js'\nexport * from './partner/partner.js'\nexport * from './granted-role/granted-role.js'\nexport * from './invitation/invitation.js'\nexport * from './app-binding/app-binding.js'\nexport * from './password-history/password-history.js'\nexport * from './verification-token/verification-token.js'\nexport * from './login-history/login-history.js'\nexport * from './web-auth-credential/web-auth-credential.js'\nexport * from './domain-link/domain-link.js'\nexport * from './domain-owner/domain-owner.js'\n\n/* EXPORT TYPES */\nexport * from './app-binding/app-binding-types.js'\nexport * from './appliance/appliance-types.js'\nexport * from './application/application-types.js'\nexport * from './domain-generator/domain-generator-types.js'\nexport * from './invitation/invitation-types.js'\nexport * from './partner/partner-types.js'\nexport * from './privilege/privilege-types.js'\nexport * from './role/role-types.js'\nexport * from './user/user-types.js'\nexport * from './domain-link/domain-link-types.js'\n\nexport const entities = [\n /* ENTITIES */\n ...UsersAuthProvidersEntities,\n ...AuthProviderEntities,\n ...ApplicationEntities,\n ...ApplianceEntities,\n ...PrivilegeEntities,\n ...RoleEntities,\n ...UserEntities,\n ...PartnerEntities,\n ...GrantedRoleEntities,\n ...InvitationEntities,\n ...PasswordHistoryEntities,\n ...VerificationTokenEntities,\n ...LoginHistoryEntities,\n ...WebAuthCredentialEntities,\n ...DomainLinkEntities,\n ...DomainOwnerEntities\n]\n\nexport const schema = {\n typeDefs: {\n privilegeDirectiveTypeDefs\n },\n\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...UsersAuthProvidersResolvers,\n ...AuthProviderResolvers,\n ...ApplicationResolvers,\n ...ApplianceResolvers,\n ...PrivilegeResolvers,\n ...RoleResolvers,\n ...RoleTemplateResolvers,\n ...UserResolvers,\n ...PartnerResolvers,\n ...GrantedRoleResolver,\n ...InvitationResolver,\n ...AppbindingResolver,\n ...DomainGeneratorResolver,\n ...LoginHistoryResolver,\n ...DomainLinkResolver,\n ...DomainOwnerResolvers\n ],\n directives: {\n privilege: privilegeDirectiveResolver\n }\n}\n"]}
@@ -1,49 +1,18 @@
1
1
  import { GraphQLSchema } from 'graphql';
2
2
  /**
3
- * `legacy` — **권한을 옮기는 동안 옛 이름도 받아 준다.**
3
+ * ⚠ **다음에 목록 인자를 붙이는 사람에게.**
4
4
  *
5
- * ── 왜 필요한가 ─────────────────────────────────────────────────────────────
6
- * 리졸버의 `category` 를 바꾸는 순간 **역할이 들고 있던 기존 부여가 고아가 된다.** 새 이름을
7
- * 가진 사람이 아직 없으므로, 옮긴 그날 그 기능을 쓰던 사람들이 전부 막힌다.
5
+ * 여기 `legacy` 인자가 있었다 — 권한 이름을 옮기는 동안 옛 이름도 받아 주는 별칭이었다.
6
+ * **걷어냈다.** 이 저장소는 대개조 중이고 **호환 계층을 끼우지 않는다**: 이름을 바꾸면 바꾼
7
+ * 것이고, 부여는 관리자가 다시 놓는다. 별칭은 「한동안」이 영원이 되는 길이었다.
8
8
  *
9
- * 같은 자리가 두 곳에서 필요하다. 하나는 `board-ai` 의 비-보드 op 를 `ai-assistant` 로 옮기는
10
- * 일이고(공장 질문이 보드 편집 권한을 요구하던 문제), 다른 하나는 `execute` 축을 세울 때다
11
- * (`mutation` 을 들고 있던 역할이 승인 권한을 잃는다). 그래서 장치를 하나로 만든다.
9
+ * 곧 필요해지는 목록 인자는 **AND** 다(`docs/design/auth-rebuild.md` §1-(3)): 외부에서
10
+ * 당겨오면서 남의 데이터를 앉히는 호출은 `execute` **와** 그 모듈의 `mutation` 을 **둘 다**
11
+ * 요구해야 한다.
12
12
  *
13
- * ── 부여를 베끼지 않는다 ────────────────────────────────────────────────────
14
- * 옛 부여를 가진 역할에 새 권한을 써 넣는 길이 먼저 떠오르는데, 그것은 두 가지를 잃는다.
15
- * **조용하다** — 아무도 자기가 새 권한을 받은 것을 모른다. 그리고 **되돌릴 근거가 없어진다** —
16
- * 편집만 필요한 역할에서 나중에 그것을 뺄 때 누가 실제로 필요했는지 알 방법이 없다.
17
- *
18
- * 그래서 행은 건드리지 않고 **문에서 한동안 받아 준다.** 그러면 누가 이 문에 기대고 있는지
19
- * 로그에 쌓이고, 짐작이 아니라 **잰 것으로** 부여할 수 있다.
20
- *
21
- * ── 지울 시점은 눈에 보인다 ─────────────────────────────────────────────────
22
- * **옛 쌍은 `process['PRIVILEGES']` 에 등록하지 않는다.** 그래서 옛 권한 행이
23
- * `isDeprecatedPrivilege` 기준으로 유령이 되어 관리자 목록에 취소선으로 나타난다
24
- * (`privilege-deprecation.ts`). 별칭을 지울 때가 됐다는 신호를 새로 만들 필요가 없다.
25
- *
26
- * ⚠ **조건만으로 지우지 않는다.** 「N일간 legacy 통과 0건」은 그 업무를 아무도 안 한 조용한
27
- * 한 주로 충족된다. **고정 날짜**를 받아 그날 지운다.
28
- *
29
- * ── 푸는 순서 ───────────────────────────────────────────────────────────────
30
- * 지금은 둘이다. 모듈 기본값 시임이 서면 그 사이에 한 단이 더 들어간다
31
- * (`docs/design/auth-rebuild.md` §6-4).
32
- *
33
- * ```
34
- * 지금 필드 선언 → 필드의 legacy → 거절
35
- * 뒤에 필드 선언 → 필드의 legacy → 모듈 기본값 → DENY
36
- * ```
37
- *
38
- * ── ⚠ 다음에 목록 인자를 붙이는 사람에게 ────────────────────────────────────
39
- * **`legacy` 는 OR 다** — 새 이름이나 옛 이름, 어느 쪽이든 통과한다.
40
- *
41
- * 곧 필요해지는 것은 **AND** 다(`docs/design/auth-rebuild.md` §1-(3)): 외부에서 당겨오면서
42
- * 남의 데이터를 앉히는 호출은 `execute` **와** 그 모듈의 `mutation` 을 **둘 다** 요구해야 한다.
43
- *
44
- * 같은 모양의 인자가 정반대 뜻을 가지므로, 그것을 `privileges: [...]` 처럼 중립적으로 이름
45
- * 지으면 다음 사람이 「이 중 하나」로 읽는다. 그렇게 쓰는 날 게이트가 **오류 없이** 열린다.
46
- * 그러니 `requiresAll: [...]` 처럼 **이름이 AND 라고 말하게** 한다.
13
+ * 그것을 `privileges: [...]` 처럼 중립적으로 이름 지으면 다음 사람이 「이 중 하나」로 읽고,
14
+ * 그렇게 쓰는 날 게이트가 **오류 없이** 열린다. `requiresAll: [...]` 처럼 **이름이 AND 라고
15
+ * 말하게** 한다.
47
16
  */
48
17
  export declare const privilegeDirectiveTypeDefs: import("graphql").DocumentNode;
49
18
  export declare const privilegeDirectiveResolver: (schema: GraphQLSchema) => GraphQLSchema;
@@ -5,56 +5,24 @@ const tslib_1 = require("tslib");
5
5
  const graphql_1 = require("graphql");
6
6
  const graphql_tag_1 = tslib_1.__importDefault(require("graphql-tag"));
7
7
  const utils_1 = require("@graphql-tools/utils");
8
- const env_1 = require("@things-factory/env");
9
8
  const check_permission_js_1 = require("../../utils/check-permission.js");
10
9
  const privilege_rejection_js_1 = require("./privilege-rejection.js");
11
10
  process['PRIVILEGES'] = {};
12
11
  const DIRECTIVE = 'privilege';
13
12
  /**
14
- * `legacy` — **권한을 옮기는 동안 옛 이름도 받아 준다.**
13
+ * ⚠ **다음에 목록 인자를 붙이는 사람에게.**
15
14
  *
16
- * ── 왜 필요한가 ─────────────────────────────────────────────────────────────
17
- * 리졸버의 `category` 를 바꾸는 순간 **역할이 들고 있던 기존 부여가 고아가 된다.** 새 이름을
18
- * 가진 사람이 아직 없으므로, 옮긴 그날 그 기능을 쓰던 사람들이 전부 막힌다.
15
+ * 여기 `legacy` 인자가 있었다 — 권한 이름을 옮기는 동안 옛 이름도 받아 주는 별칭이었다.
16
+ * **걷어냈다.** 이 저장소는 대개조 중이고 **호환 계층을 끼우지 않는다**: 이름을 바꾸면 바꾼
17
+ * 것이고, 부여는 관리자가 다시 놓는다. 별칭은 「한동안」이 영원이 되는 길이었다.
19
18
  *
20
- * 같은 자리가 두 곳에서 필요하다. 하나는 `board-ai` 의 비-보드 op 를 `ai-assistant` 로 옮기는
21
- * 일이고(공장 질문이 보드 편집 권한을 요구하던 문제), 다른 하나는 `execute` 축을 세울 때다
22
- * (`mutation` 을 들고 있던 역할이 승인 권한을 잃는다). 그래서 장치를 하나로 만든다.
19
+ * 곧 필요해지는 목록 인자는 **AND** 다(`docs/design/auth-rebuild.md` §1-(3)): 외부에서
20
+ * 당겨오면서 남의 데이터를 앉히는 호출은 `execute` **와** 그 모듈의 `mutation` 을 **둘 다**
21
+ * 요구해야 한다.
23
22
  *
24
- * ── 부여를 베끼지 않는다 ────────────────────────────────────────────────────
25
- * 옛 부여를 가진 역할에 새 권한을 써 넣는 길이 먼저 떠오르는데, 그것은 두 가지를 잃는다.
26
- * **조용하다** — 아무도 자기가 새 권한을 받은 것을 모른다. 그리고 **되돌릴 근거가 없어진다** —
27
- * 편집만 필요한 역할에서 나중에 그것을 뺄 때 누가 실제로 필요했는지 알 방법이 없다.
28
- *
29
- * 그래서 행은 건드리지 않고 **문에서 한동안 받아 준다.** 그러면 누가 이 문에 기대고 있는지
30
- * 로그에 쌓이고, 짐작이 아니라 **잰 것으로** 부여할 수 있다.
31
- *
32
- * ── 지울 시점은 눈에 보인다 ─────────────────────────────────────────────────
33
- * **옛 쌍은 `process['PRIVILEGES']` 에 등록하지 않는다.** 그래서 옛 권한 행이
34
- * `isDeprecatedPrivilege` 기준으로 유령이 되어 관리자 목록에 취소선으로 나타난다
35
- * (`privilege-deprecation.ts`). 별칭을 지울 때가 됐다는 신호를 새로 만들 필요가 없다.
36
- *
37
- * ⚠ **조건만으로 지우지 않는다.** 「N일간 legacy 통과 0건」은 그 업무를 아무도 안 한 조용한
38
- * 한 주로 충족된다. **고정 날짜**를 받아 그날 지운다.
39
- *
40
- * ── 푸는 순서 ───────────────────────────────────────────────────────────────
41
- * 지금은 둘이다. 모듈 기본값 시임이 서면 그 사이에 한 단이 더 들어간다
42
- * (`docs/design/auth-rebuild.md` §6-4).
43
- *
44
- * ```
45
- * 지금 필드 선언 → 필드의 legacy → 거절
46
- * 뒤에 필드 선언 → 필드의 legacy → 모듈 기본값 → DENY
47
- * ```
48
- *
49
- * ── ⚠ 다음에 목록 인자를 붙이는 사람에게 ────────────────────────────────────
50
- * **`legacy` 는 OR 다** — 새 이름이나 옛 이름, 어느 쪽이든 통과한다.
51
- *
52
- * 곧 필요해지는 것은 **AND** 다(`docs/design/auth-rebuild.md` §1-(3)): 외부에서 당겨오면서
53
- * 남의 데이터를 앉히는 호출은 `execute` **와** 그 모듈의 `mutation` 을 **둘 다** 요구해야 한다.
54
- *
55
- * 같은 모양의 인자가 정반대 뜻을 가지므로, 그것을 `privileges: [...]` 처럼 중립적으로 이름
56
- * 지으면 다음 사람이 「이 중 하나」로 읽는다. 그렇게 쓰는 날 게이트가 **오류 없이** 열린다.
57
- * 그러니 `requiresAll: [...]` 처럼 **이름이 AND 라고 말하게** 한다.
23
+ * 그것을 `privileges: [...]` 처럼 중립적으로 이름 지으면 다음 사람이 「이 중 하나」로 읽고,
24
+ * 그렇게 쓰는 날 게이트가 **오류 없이** 열린다. `requiresAll: [...]` 처럼 **이름이 AND 라고
25
+ * 말하게** 한다.
58
26
  */
59
27
  exports.privilegeDirectiveTypeDefs = (0, graphql_tag_1.default) `
60
28
  directive @privilege(
@@ -62,7 +30,6 @@ exports.privilegeDirectiveTypeDefs = (0, graphql_tag_1.default) `
62
30
  privilege: String
63
31
  domainOwnerGranted: Boolean
64
32
  superUserGranted: Boolean
65
- legacy: String
66
33
  ) on FIELD_DEFINITION
67
34
  `;
68
35
  const privilegeDirectiveResolver = (schema) => (0, utils_1.mapSchema)(schema, {
@@ -73,19 +40,14 @@ const privilegeDirectiveResolver = (schema) => (0, utils_1.mapSchema)(schema, {
73
40
  if (!args) {
74
41
  throw new Error(`Unexpected Error. args should be defined in @privilege directive for field ${fieldName}.`);
75
42
  }
76
- const { domainOwnerGranted, superUserGranted, category, privilege, legacy } = privilegeDirective;
43
+ const { domainOwnerGranted, superUserGranted, category, privilege } = privilegeDirective;
77
44
  if (category && privilege) {
78
45
  process['PRIVILEGES'][`${category} ${privilege}`] = [category, privilege];
79
46
  }
80
- /*
81
- * 옛 쌍은 **일부러 등록하지 않는다** — 등록하면 옛 행이 유령으로 표시되지 않아
82
- * 별칭을 지울 시점을 아무도 못 본다. 위 주석 참조.
83
- */
84
47
  // 필드의 기존 description 가져오기
85
48
  const existingDescription = fieldConfig.description || '';
86
49
  // 권한 정보를 포함한 새로운 description 생성
87
50
  const privilegeDescription = `\n\n🔒 Requires privilege: ${category}:${privilege}` +
88
- (legacy ? ` (or ${legacy}:${privilege} while it is being moved)` : '') +
89
51
  (domainOwnerGranted ? ', Domain ownership' : '') +
90
52
  (superUserGranted ? ', System ownership' : '');
91
53
  // 기존 description과 결합
@@ -102,33 +64,11 @@ const privilegeDirectiveResolver = (schema) => (0, utils_1.mapSchema)(schema, {
102
64
  return await resolve.call(this, source, args, context, info);
103
65
  }
104
66
  /*
105
- * 새 이름으로 안 열렸다 — 옮기는 중이면 옛 이름으로 한 번 더 본다.
67
+ * 안 열렸으면 **거기서 끝이다.**
106
68
  *
107
- * 소유권 우회는 다시 묻지 않는다(`owner`·`super` 를 넘기지 않는다). 그것은 이미 위에서
108
- * 판정됐고, 여기서 다시 주면 옛 이름이 우회를 한 번 더 얻는 것이 된다.
69
+ * 여기 옛 이름으로 한 번 더 보는 길이 있었다. 그것을 걷어냈다 — 호환 계층을 끼우지
70
+ * 않는다. 이름을 옮기면 부여도 옮기는 것이고, 그 일은 관리자가 역할 화면에서 한다.
109
71
  *
110
- * **금지 목록에 새 쌍이 있으면 옛 이름도 보지 않는다.** 금지는 관리자가 「이 IP 에서는
111
- * 이것을 못 한다」고 명시적으로 적어 둔 것인데, 금지 목록은 옛 이름을 모른다. 별칭이
112
- * 금지를 되돌리면 그 설정이 조용히 무력해진다.
113
- */
114
- const prohibited = (prohibitedPrivileges || []).some(pp => pp.category == category && pp.privilege == privilege);
115
- if (legacy && category && privilege && !prohibited) {
116
- const byLegacy = await (0, check_permission_js_1.checkPermission)({ category: legacy, privilege }, user, domain, unsafeIP, prohibitedPrivileges);
117
- if (byLegacy) {
118
- /*
119
- * **누가 이 문에 기대고 있나.** 이 목록이 부여의 근거가 된다 — 짐작으로 얹지 않는다.
120
- *
121
- * 어느 역할이 그것을 줬는지는 남기지 않는다. 그것을 알려면 매 요청 부여 경로를
122
- * 되짚는 질의가 하나 더 붙고, 여기는 모든 요청이 지나는 자리다. 사람과 리졸버가
123
- * 있으면 관리자가 역할 화면에서 찾을 수 있다.
124
- */
125
- env_1.logger.warn(`privilege moved: ${typeName}.${fieldName} passed on the legacy name ` +
126
- `${legacy}:${privilege} (new name ${category}:${privilege}) for ${user?.email || user?.id}`);
127
- return await resolve.call(this, source, args, context, info);
128
- }
129
- }
130
- /*
131
- * 거절은 **새 이름**으로 말한다 — 옛 이름을 말하면 관리자가 사라질 권한을 부여한다.
132
72
  * 문장 만들기는 `privilege-rejection.ts` 가 소유한다.
133
73
  */
134
74
  throw (0, privilege_rejection_js_1.privilegeRejection)(context, category, privilege);
@@ -1 +1 @@
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 { privilegeRejection } from './privilege-rejection.js'\n\nprocess['PRIVILEGES'] = {}\n\nconst DIRECTIVE = 'privilege'\n\n/**\n * `legacy` — **권한을 옮기는 동안 옛 이름도 받아 준다.**\n *\n * ── 왜 필요한가 ─────────────────────────────────────────────────────────────\n * 리졸버의 `category` 를 바꾸는 순간 **역할이 들고 있던 기존 부여가 고아가 된다.** 새 이름을\n * 가진 사람이 아직 없으므로, 옮긴 그날 그 기능을 쓰던 사람들이 전부 막힌다.\n *\n * 같은 자리가 두 곳에서 필요하다. 하나는 `board-ai` 의 비-보드 op 를 `ai-assistant` 로 옮기는\n * 일이고(공장 질문이 보드 편집 권한을 요구하던 문제), 다른 하나는 `execute` 축을 세울 때다\n * (`mutation` 을 들고 있던 역할이 승인 권한을 잃는다). 그래서 장치를 하나로 만든다.\n *\n * ── 부여를 베끼지 않는다 ────────────────────────────────────────────────────\n * 옛 부여를 가진 역할에 새 권한을 써 넣는 길이 먼저 떠오르는데, 그것은 두 가지를 잃는다.\n * **조용하다** — 아무도 자기가 새 권한을 받은 것을 모른다. 그리고 **되돌릴 근거가 없어진다** —\n * 편집만 필요한 역할에서 나중에 그것을 뺄 때 누가 실제로 필요했는지 알 방법이 없다.\n *\n * 그래서 행은 건드리지 않고 **문에서 한동안 받아 준다.** 그러면 누가 이 문에 기대고 있는지\n * 로그에 쌓이고, 짐작이 아니라 **잰 것으로** 부여할 수 있다.\n *\n * ── 지울 시점은 눈에 보인다 ─────────────────────────────────────────────────\n * **옛 쌍은 `process['PRIVILEGES']` 에 등록하지 않는다.** 그래서 옛 권한 행이\n * `isDeprecatedPrivilege` 기준으로 유령이 되어 관리자 목록에 취소선으로 나타난다\n * (`privilege-deprecation.ts`). 별칭을 지울 때가 됐다는 신호를 새로 만들 필요가 없다.\n *\n * ⚠ **조건만으로 지우지 않는다.** 「N일간 legacy 통과 0건」은 그 업무를 아무도 안 한 조용한\n * 한 주로 충족된다. **고정 날짜**를 받아 그날 지운다.\n *\n * ── 푸는 순서 ───────────────────────────────────────────────────────────────\n * 지금은 둘이다. 모듈 기본값 시임이 서면 그 사이에 한 단이 더 들어간다\n * (`docs/design/auth-rebuild.md` §6-4).\n *\n * ```\n * 지금 필드 선언 → 필드의 legacy → 거절\n * 뒤에 필드 선언 → 필드의 legacy → 모듈 기본값 → DENY\n * ```\n *\n * ── ⚠ 다음에 목록 인자를 붙이는 사람에게 ────────────────────────────────────\n * **`legacy` 는 OR 다** — 새 이름이나 옛 이름, 어느 쪽이든 통과한다.\n *\n * 곧 필요해지는 것은 **AND** 다(`docs/design/auth-rebuild.md` §1-(3)): 외부에서 당겨오면서\n * 남의 데이터를 앉히는 호출은 `execute` **와** 그 모듈의 `mutation` 을 **둘 다** 요구해야 한다.\n *\n * 같은 모양의 인자가 정반대 뜻을 가지므로, 그것을 `privileges: [...]` 처럼 중립적으로 이름\n * 지으면 다음 사람이 「이 중 하나」로 읽는다. 그렇게 쓰는 날 게이트가 **오류 없이** 열린다.\n * 그러니 `requiresAll: [...]` 처럼 **이름이 AND 라고 말하게** 한다.\n */\nexport const privilegeDirectiveTypeDefs = gql`\n directive @privilege(\n category: String\n privilege: String\n domainOwnerGranted: Boolean\n superUserGranted: Boolean\n legacy: String\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, legacy } = privilegeDirective\n if (category && privilege) {\n process['PRIVILEGES'][`${category} ${privilege}`] = [category, privilege]\n }\n\n /*\n * 옛 쌍은 **일부러 등록하지 않는다** — 등록하면 옛 행이 유령으로 표시되지 않아\n * 별칭을 지울 시점을 아무도 못 본다. 위 주석 참조.\n */\n\n // 필드의 기존 description 가져오기\n const existingDescription = fieldConfig.description || ''\n\n // 권한 정보를 포함한 새로운 description 생성\n const privilegeDescription =\n `\\n\\n🔒 Requires privilege: ${category}:${privilege}` +\n (legacy ? ` (or ${legacy}:${privilege} while it is being moved)` : '') +\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 * 소유권 우회는 다시 묻지 않는다(`owner`·`super` 를 넘기지 않는다). 그것은 이미 위에서\n * 판정됐고, 여기서 다시 주면 옛 이름이 우회를 한 번 더 얻는 것이 된다.\n *\n * **금지 목록에 새 쌍이 있으면 옛 이름도 보지 않는다.** 금지는 관리자가 「이 IP 에서는\n * 이것을 못 한다」고 명시적으로 적어 둔 것인데, 금지 목록은 옛 이름을 모른다. 별칭이\n * 금지를 되돌리면 그 설정이 조용히 무력해진다.\n */\n const prohibited = (prohibitedPrivileges || []).some(\n pp => pp.category == category && pp.privilege == privilege\n )\n\n if (legacy && category && privilege && !prohibited) {\n const byLegacy = await checkPermission(\n { category: legacy, privilege },\n user,\n domain,\n unsafeIP,\n prohibitedPrivileges\n )\n\n if (byLegacy) {\n /*\n * **누가 이 문에 기대고 있나.** 이 목록이 부여의 근거가 된다 — 짐작으로 얹지 않는다.\n *\n * 어느 역할이 그것을 줬는지는 남기지 않는다. 그것을 알려면 매 요청 부여 경로를\n * 되짚는 질의가 하나 더 붙고, 여기는 모든 요청이 지나는 자리다. 사람과 리졸버가\n * 있으면 관리자가 역할 화면에서 찾을 수 있다.\n */\n logger.warn(\n `privilege moved: ${typeName}.${fieldName} passed on the legacy name ` +\n `${legacy}:${privilege} (new name ${category}:${privilege}) for ${user?.email || user?.id}`\n )\n\n return await resolve.call(this, source, args, context, info)\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"]}
1
+ {"version":3,"file":"privilege-directive.js","sourceRoot":"","sources":["../../../server/service/privilege/privilege-directive.ts"],"names":[],"mappings":";;;;AAAA,qCAA6D;AAC7D,sEAA6B;AAE7B,gDAA0E;AAG1E,yEAAiE;AACjE,qEAA6D;AAE7D,OAAO,CAAC,YAAY,CAAC,GAAG,EAAE,CAAA;AAE1B,MAAM,SAAS,GAAG,WAAW,CAAA;AAE7B;;;;;;;;;;;;;;GAcG;AACU,QAAA,0BAA0B,GAAG,IAAA,qBAAG,EAAA;;;;;;;CAO5C,CAAA;AACM,MAAM,0BAA0B,GAAG,CAAC,MAAqB,EAAE,EAAE,CAClE,IAAA,iBAAS,EAAC,MAAM,EAAE;IAChB,CAAC,kBAAU,CAAC,YAAY,CAAC,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE;QACtE,MAAM,kBAAkB,GAAG,IAAA,oBAAY,EAAC,MAAM,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAC5E,IAAI,kBAAkB,EAAE,CAAC;YACvB,MAAM,EAAE,OAAO,GAAG,8BAAoB,EAAE,IAAI,EAAE,GAAG,WAAW,CAAA;YAE5D,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,8EAA8E,SAAS,GAAG,CAAC,CAAA;YAC7G,CAAC;YAED,MAAM,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,kBAAkB,CAAA;YACxF,IAAI,QAAQ,IAAI,SAAS,EAAE,CAAC;gBAC1B,OAAO,CAAC,YAAY,CAAC,CAAC,GAAG,QAAQ,IAAI,SAAS,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;YAC3E,CAAC;YAED,0BAA0B;YAC1B,MAAM,mBAAmB,GAAG,WAAW,CAAC,WAAW,IAAI,EAAE,CAAA;YAEzD,gCAAgC;YAChC,MAAM,oBAAoB,GACxB,8BAA8B,QAAQ,IAAI,SAAS,EAAE;gBACrD,CAAC,kBAAkB,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChD,CAAC,gBAAgB,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;YAEhD,qBAAqB;YACrB,WAAW,CAAC,WAAW,GAAG,GAAG,mBAAmB,IAAI,oBAAoB,EAAE,CAAC,IAAI,EAAE,CAAA;YAEjF,WAAW,CAAC,OAAO,GAAG,KAAK,WAAW,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI;gBAC/D,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;gBAEtE,MAAM,SAAS,GAAG,MAAM,IAAA,qCAAe,EACrC;oBACE,QAAQ;oBACR,SAAS;oBACT,KAAK,EAAE,kBAAkB;oBACzB,KAAK,EAAE,gBAAgB;iBACxB,EACD,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,oBAAoB,CACrB,CAAA;gBAED,IAAI,SAAS,EAAE,CAAC;oBACd,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;gBAC9D,CAAC;gBAED;;;;;;;mBAOG;gBACH,MAAM,IAAA,2CAAkB,EAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAA;YACxD,CAAC,CAAA;YAED,OAAO,WAAW,CAAA;QACpB,CAAC;IACH,CAAC;CACF,CAAC,CAAA;AA9DS,QAAA,0BAA0B,8BA8DnC","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 { 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 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
- * 칸만 쓴다(`create-role.ts`·`role-detail.ts`).
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>;