@things-factory/auth-base 10.1.3 → 10.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/auth.d.ts +9 -1
- package/dist-client/auth.js +12 -3
- package/dist-client/auth.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/controllers/profile.d.ts +1 -0
- package/dist-server/controllers/signin.js +33 -12
- package/dist-server/controllers/signin.js.map +1 -1
- package/dist-server/controllers/unlock-user.js +21 -2
- package/dist-server/controllers/unlock-user.js.map +1 -1
- package/dist-server/router/auth-public-process-router.js +32 -5
- package/dist-server/router/auth-public-process-router.js.map +1 -1
- package/dist-server/service/privilege/privilege-directive.d.ts +46 -0
- package/dist-server/service/privilege/privilege-directive.js +87 -6
- package/dist-server/service/privilege/privilege-directive.js.map +1 -1
- package/dist-server/service/role/index.d.ts +2 -1
- package/dist-server/service/role/index.js +2 -1
- package/dist-server/service/role/index.js.map +1 -1
- package/dist-server/service/role/role-mutation.d.ts +17 -1
- package/dist-server/service/role/role-mutation.js +126 -7
- package/dist-server/service/role/role-mutation.js.map +1 -1
- package/dist-server/service/role/role-privilege-query.d.ts +14 -0
- package/dist-server/service/role/role-privilege-query.js +37 -0
- package/dist-server/service/role/role-privilege-query.js.map +1 -0
- package/dist-server/service/role/role-query.d.ts +18 -0
- package/dist-server/service/role/role-query.js +38 -22
- package/dist-server/service/role/role-query.js.map +1 -1
- package/dist-server/service/user/user.d.ts +1 -0
- package/dist-server/service/user/user.js +55 -13
- package/dist-server/service/user/user.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/utils/account-lock-rule.d.ts +79 -0
- package/dist-server/utils/account-lock-rule.js +114 -0
- package/dist-server/utils/account-lock-rule.js.map +1 -0
- package/dist-server/utils/lock-duration.d.ts +3 -0
- package/dist-server/utils/lock-duration.js +36 -0
- package/dist-server/utils/lock-duration.js.map +1 -0
- package/package.json +4 -4
- package/tests/account-lock-db.test.ts +216 -0
- package/tests/account-lock-rule.test.ts +141 -0
- package/tests/compiled.ts +41 -0
- package/tests/has-privilege-db.test.ts +198 -0
- package/tests/lock-recovery.test.ts +204 -0
- package/tests/password-hashing.test.ts +131 -0
- package/tests/password-persistence.test.ts +147 -0
- package/tests/permission-gate.test.ts +226 -0
- package/tests/privilege-directive.test.ts +270 -0
- package/tests/role-mutation-db.test.ts +263 -0
- package/tests/role-privileges-db.test.ts +128 -0
- package/tests/signin-policy.test.ts +200 -0
- package/tests/token-issuance.test.ts +240 -0
- package/translations/en.json +161 -1
- package/translations/ko.json +161 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unlock-user.js","sourceRoot":"","sources":["../../server/controllers/unlock-user.ts"],"names":[],"mappings":";;AAWA,kDAsBC;AAED,
|
|
1
|
+
{"version":3,"file":"unlock-user.js","sourceRoot":"","sources":["../../server/controllers/unlock-user.ts"],"names":[],"mappings":";;AAWA,kDAsBC;AAED,gCAgDC;AAnFD,6BAAyB;AAEzB,2DAAsD;AACtD,iDAAqD;AAErD,qDAA0D;AAC1D,+FAA8G;AAC9G,kFAA6E;AAC7E,mFAA0E;AAC1E,mFAA0E;AAEnE,KAAK,UAAU,mBAAmB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;IACzD,IAAI,CAAC;QACH,IAAI,KAAK,GAAG,IAAA,kDAAqB,GAAE,CAAA;QACnC,IAAI,WAAW,GAAG,MAAM,IAAA,kDAAqB,EAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,6CAAqB,CAAC,MAAM,CAAC,CAAA;QAE3F,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,UAAU,GAAG,IAAI,SAAG,CAAC,2BAA2B,KAAK,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;YACpF,MAAM,IAAA,sBAAS,EAAC;gBACd,QAAQ,EAAE,IAAI,CAAC,KAAK;gBACpB,OAAO,EAAE,wBAAwB;gBACjC,OAAO,EAAE,IAAA,gDAAsB,EAAC;oBAC9B,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,QAAQ,EAAE,UAAU;iBACrB,CAAC;aACH,CAAC,CAAA;YAEF,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,UAAU,CAAC,KAAK,EAAE,QAAQ;IAC9C;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,IAAA,qBAAa,EAAC,yCAAiB,CAAC,CAAC,OAAO,CAAC;QAClE,KAAK,EAAE;YACL,KAAK;YACL,IAAI,EAAE,6CAAqB,CAAC,MAAM;SACnC;KACF,CAAC,CAAA;IAEF;iFAC6E;IAC7E,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,YAAY,CAAA;IAE/B,IAAI,QAAQ,GAAG,MAAM,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;IAClE,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3B,IAAI,QAAQ,CAAC,MAAM,IAAI,oBAAU,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAEtD,QAAQ,CAAC,MAAM,GAAG,oBAAU,CAAC,SAAS,CAAA;IACtC,QAAQ,CAAC,QAAQ,GAAG,cAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IACxD,QAAQ,CAAC,SAAS,GAAG,CAAC,CAAA;IAEtB,MAAM,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACxC,MAAM,IAAA,qBAAa,EAAC,yCAAiB,CAAC,CAAC,MAAM,CAAC;QAC5C,MAAM;QACN,KAAK;QACL,IAAI,EAAE,6CAAqB,CAAC,MAAM;KACnC,CAAC,CAAA;IAEF,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["import { URL } from 'url'\n\nimport { sendEmail } from '@things-factory/email-base'\nimport { getRepository } from '@things-factory/shell'\n\nimport { User, UserStatus } from '../service/user/user.js'\nimport { VerificationToken, VerificationTokenType } from '../service/verification-token/verification-token.js'\nimport { getUnlockUserEmailForm } from '../templates/account-unlock-email.js'\nimport { makeVerificationToken } from './utils/make-verification-token.js'\nimport { saveVerificationToken } from './utils/save-verification-token.js'\n\nexport async function sendUnlockUserEmail({ user, context }) {\n try {\n var token = makeVerificationToken()\n var verifaction = await saveVerificationToken(user.id, token, VerificationTokenType.UNLOCK)\n\n if (verifaction) {\n var serviceUrl = new URL(`/auth/unlock-user?token=${token}`, context.header.referer)\n await sendEmail({\n receiver: user.email,\n subject: 'Your account is locked',\n content: getUnlockUserEmailForm({\n username: user.username,\n name: user.name,\n resetUrl: serviceUrl\n })\n })\n\n return true\n }\n } catch (e) {\n return false\n }\n}\n\nexport async function unlockUser(token, password) {\n /*\n * An absent condition is not a narrow condition — TypeORM drops `undefined` from `where`.\n *\n * Without this guard, a call with no token searched `{ type: UNLOCK }` alone and matched\n * **anyone's** pending unlock token, then handed that account the password the caller chose.\n * Both halves are reachable without credentials: five wrong guesses lock a victim and mint the\n * token, and `/auth/unlock-user` accepted a body with a password and no token because its own\n * guard read `!(token || password)`.\n *\n * [확인함] 2026-09-09 · locked a victim by failing signin five times, posted a password with no\n * token, and signed in as them — status activated, failCount 0, password the attacker's.\n */\n if (!token || !password) {\n return false\n }\n\n const verification = await getRepository(VerificationToken).findOne({\n where: {\n token,\n type: VerificationTokenType.UNLOCK\n }\n })\n\n /* Refuse a token that is gone, rather than destructuring null — a reused link showed the\n screen a 500 instead of a reason. `resetPassword` refuses the same way. */\n if (!verification?.userId) {\n return false\n }\n\n const { userId } = verification\n\n var userInfo = await getRepository(User).findOneBy({ id: userId })\n if (!userInfo) return false\n if (userInfo.status != UserStatus.LOCKED) return false\n\n userInfo.status = UserStatus.ACTIVATED\n userInfo.password = User.encode(password, userInfo.salt)\n userInfo.failCount = 0\n\n await getRepository(User).save(userInfo)\n await getRepository(VerificationToken).delete({\n userId,\n token,\n type: VerificationTokenType.UNLOCK\n })\n\n return true\n}\n"]}
|
|
@@ -239,16 +239,43 @@ exports.authPublicProcessRouter.post('/reset-password', async (context, next) =>
|
|
|
239
239
|
exports.authPublicProcessRouter.post('/unlock-user', async (context, next) => {
|
|
240
240
|
const { header, t } = context;
|
|
241
241
|
const { password, token } = context.request.body;
|
|
242
|
-
|
|
242
|
+
/* Both are required, the way `/auth/reset-password` requires them. This read `||`, so a body
|
|
243
|
+
carrying a password and no token passed the guard and reached the unlock with no token —
|
|
244
|
+
see the note in `unlockUser`. */
|
|
245
|
+
if (!(token && password)) {
|
|
243
246
|
context.status = 404;
|
|
244
247
|
context.body = t('error.token or password is invalid');
|
|
245
248
|
return;
|
|
246
249
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
250
|
+
const succeed = await (0, unlock_user_js_1.unlockUser)(token, password);
|
|
251
|
+
/*
|
|
252
|
+
* Say what happened. The success page used to render whether or not the unlock took, so a
|
|
253
|
+
* reused link, a token for another account, or an account that was never locked all read as
|
|
254
|
+
* "account is reactivated" — and the person then could not sign in.
|
|
255
|
+
*
|
|
256
|
+
* A refusal returns to the form with a reason, the shape `/auth/reset-password` already uses.
|
|
257
|
+
*/
|
|
258
|
+
if (!succeed) {
|
|
259
|
+
const message = t('text.invalid verification token');
|
|
260
|
+
context.status = 404;
|
|
261
|
+
context.body = message;
|
|
262
|
+
if ((0, accepts_js_1.accepts)(header.accept, ['text/html', '*/*'])) {
|
|
263
|
+
await context.render('auth-page', {
|
|
264
|
+
pageElement: 'unlock-user',
|
|
265
|
+
elementScript: '/auth/unlock-user.js',
|
|
266
|
+
data: {
|
|
267
|
+
token,
|
|
268
|
+
message,
|
|
269
|
+
disableUserSignupProcess,
|
|
270
|
+
disableUserFavoredLanguage,
|
|
271
|
+
languages
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return;
|
|
251
276
|
}
|
|
277
|
+
context.body = t('text.password reset succeed');
|
|
278
|
+
(0, access_token_cookie_js_1.clearAccessTokenCookie)(context);
|
|
252
279
|
if ((0, accepts_js_1.accepts)(header.accept, ['text/html', '*/*'])) {
|
|
253
280
|
await context.render('auth-page', {
|
|
254
281
|
pageElement: 'auth-result',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-public-process-router.js","sourceRoot":"","sources":["../../server/router/auth-public-process-router.ts"],"names":[],"mappings":";;;;AAAA,oEAA+B;AAC/B,qCAA+B;AAE/B,6CAA4C;AAC5C,iDAAsE;AAEtE,gEAAoE;AACpE,wEAAwF;AACxF,kEAA0D;AAC1D,oEAAgF;AAChF,qDAA8C;AAC9C,oDAA6C;AAC7C,4EAAwE;AAExE,MAAM,wBAAwB,GAAG,YAAM,CAAC,GAAG,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAA;AAC9E,MAAM,0BAA0B,GAAG,YAAM,CAAC,GAAG,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAA;AACvF,MAAM,SAAS,GAAG,YAAM,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAA;AACrD,MAAM,YAAY,GAAG,YAAM,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI;IAC7C,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,KAAK,EAAE,IAAI;IACX,gBAAgB,EAAE,IAAI;IACtB,WAAW,EAAE,KAAK;IAClB,eAAe,EAAE,IAAI;IACrB,eAAe,EAAE,KAAK;IACtB,oBAAoB,EAAE,CAAC;IACvB,oBAAoB,EAAE,EAAE;CACzB,CAAA;AAEY,QAAA,uBAAuB,GAAG,IAAI,oBAAM,CAAC;IAChD,MAAM,EAAE,OAAO;CAChB,CAAC,CAAA;AAEF,+BAAuB,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAC5D,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAA;IAE5C,MAAM,UAAU,GAAG,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAA;IAEtC,IAAI,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC;QAClC,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC1B,SAAS,EAAE,CAAC,SAAS,CAAC;KACvB,CAAC,CAAA;IAEF,IAAI,CAAC,IAAI,IAAI,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC;YAC9B,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,eAAK,EAAC,KAAK,CAAC,EAAE;YAC9B,SAAS,EAAE,CAAC,SAAS,CAAC;SACvB,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,QAAQ,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAA;IACpD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,QAAQ,CAAC,yBAAyB,KAAK,UAAU,KAAK,EAAE,CAAC,CAAA;IACnE,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,+BAAuB,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAC9D,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,CAAA;IAC7B,IAAA,+CAAsB,EAAC,OAAO,CAAC,CAAA;IAE/B,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,2BAA2B,CAAC,CAAA;IAE7C,IAAI,IAAA,oBAAO,EAAC,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QACjD,OAAO,CAAC,QAAQ,CAAC,IAAA,uBAAe,EAAC,OAAO,CAAC,CAAC,CAAA;IAC5C,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,+BAAuB,CAAC,GAAG,CAAC,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IACtE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAA;IAEvC,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;QAChC,WAAW,EAAE,iBAAiB;QAC9B,aAAa,EAAE,0BAA0B;QACzC,IAAI,EAAE;YACJ,KAAK;YACL,wBAAwB;YACxB,0BAA0B;YAC1B,SAAS;SACV;KACF,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,+BAAuB,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IACrE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAA;IAEvC,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;QAChC,WAAW,EAAE,gBAAgB;QAC7B,aAAa,EAAE,yBAAyB;QACxC,IAAI,EAAE;YACJ,KAAK;YACL,YAAY;YACZ,wBAAwB;YACxB,0BAA0B;YAC1B,SAAS;SACV;KACF,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,+BAAuB,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAClE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAA;IAEvC,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;QAChC,WAAW,EAAE,aAAa;QAC1B,aAAa,EAAE,sBAAsB;QACrC,IAAI,EAAE;YACJ,KAAK;YACL,wBAAwB;YACxB,0BAA0B;YAC1B,SAAS;SACV;KACF,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,+BAAuB,CAAC,GAAG,CAAC,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IACtE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,MAAM,CAAA;IAEhC,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;QAChC,WAAW,EAAE,eAAe;QAC5B,aAAa,EAAE,mBAAmB;QAClC,IAAI,EAAE;YACJ,KAAK;YACL,wBAAwB;YACxB,0BAA0B;YAC1B,SAAS;SACV;KACF,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,+BAAuB,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IACpE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,CAAA;IAC7B,IAAI,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAA;IAEhC,MAAM,IAAA,wBAAM,EAAC,KAAK,CAAC,CAAA;IAEnB,IAAI,OAAO,GAAG,CAAC,CAAC,kCAAkC,CAAC,CAAA;IAEnD,OAAO,CAAC,IAAI,GAAG,OAAO,CAAA;IAEtB,IAAI,IAAA,oBAAO,EAAC,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;YAChC,WAAW,EAAE,aAAa;YAC1B,aAAa,EAAE,iBAAiB;YAChC,IAAI,EAAE;gBACJ,OAAO;gBACP,wBAAwB;gBACxB,0BAA0B;gBAC1B,SAAS;aACV;SACF,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,+BAAuB,CAAC,IAAI,CAAC,4BAA4B,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IACjF,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,CAAA;IACrB,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAA;IAEtC,IAAI,OAAO,GAAG,MAAM,IAAA,yCAAuB,EAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAC3D,IAAI,OAAO,GAAG,CAAC,CAAC,8BAA8B,CAAC,CAAA;IAE/C,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,MAAM,GAAG,GAAG,CAAA;QACpB,OAAO,CAAC,IAAI,GAAG,OAAO,CAAA;IACxB,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,+BAAuB,CAAC,IAAI,CAAC,0BAA0B,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAC/E,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,CAAA;IACrB,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAA;IAEvD,IAAI,OAAO,GAAG,MAAM,IAAA,qCAAqB,EACvC;QACE,KAAK;QACL,SAAS;QACT,IAAI;KACL,EACD,OAAO,CACR,CAAA;IAED,IAAI,OAAO,GAAG,CAAC,CAAC,4BAA4B,CAAC,CAAA;IAE7C,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,MAAM,GAAG,GAAG,CAAA;QACpB,OAAO,CAAC,IAAI,GAAG,OAAO,CAAA;IACxB,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,+BAAuB,CAAC,IAAI,CAAC,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IACvE,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,CAAA;IACrB,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAA;IAEtC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,EAAE,CAAA;IAEzB,MAAM,QAAQ,GAAG,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAA;IACpC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC;QAClC,KAAK,EAAE;YACL,KAAK;SACN;KACF,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,MAAM,IAAA,0CAAsB,EAAC;QAC3C,IAAI;QACJ,OAAO;KACR,CAAC,CAAA;IAEF,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,MAAM,GAAG,GAAG,CAAA;QACpB,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,gCAAgC,CAAC,CAAA;IACpD,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,+BAAuB,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IACtE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,CAAA;IAC7B,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAA;IAEhD,IAAI,CAAC;QACH,IAAI,CAAC,CAAC,KAAK,IAAI,QAAQ,CAAC,EAAE,CAAC;YACzB,IAAI,OAAO,GAAG,CAAC,CAAC,oCAAoC,CAAC,CAAA;YAErD,OAAO,CAAC,MAAM,GAAG,GAAG,CAAA;YACpB,OAAO,CAAC,IAAI,GAAG;gBACb,OAAO;aACR,CAAA;YAED,IAAI,IAAA,oBAAO,EAAC,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;gBACjD,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;oBAChC,WAAW,EAAE,gBAAgB;oBAC7B,aAAa,EAAE,yBAAyB;oBACxC,IAAI,EAAE;wBACJ,KAAK;wBACL,OAAO;wBACP,YAAY;wBACZ,wBAAwB;wBACxB,0BAA0B;wBAC1B,SAAS;qBACV;iBACF,CAAC,CAAA;YACJ,CAAC;YAED,OAAM;QACR,CAAC;QAED,MAAM,IAAA,iCAAa,EAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QAE7C,IAAI,OAAO,GAAG,CAAC,CAAC,oCAAoC,CAAC,CAAA;QACrD,OAAO,CAAC,IAAI,GAAG,OAAO,CAAA;QAEtB,IAAA,+CAAsB,EAAC,OAAO,CAAC,CAAA;QAE/B,IAAI,IAAA,oBAAO,EAAC,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;YACjD,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;gBAChC,WAAW,EAAE,aAAa;gBAC1B,aAAa,EAAE,iBAAiB;gBAChC,IAAI,EAAE;oBACJ,OAAO;oBACP,wBAAwB;oBACxB,0BAA0B;oBAC1B,SAAS;iBACV;aACF,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,MAAM,GAAG,GAAG,CAAA;QACpB,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,OAAO,CAAA;QAExB,IAAI,IAAA,oBAAO,EAAC,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;YACjD,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;gBAChC,WAAW,EAAE,gBAAgB;gBAC7B,aAAa,EAAE,yBAAyB;gBACxC,IAAI,EAAE;oBACJ,KAAK;oBACL,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,YAAY;oBACZ,wBAAwB;oBACxB,0BAA0B;oBAC1B,SAAS;iBACV;aACF,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,+BAAuB,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IACnE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,CAAA;IAC7B,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAA;IAEhD,IAAI,CAAC,CAAC,KAAK,IAAI,QAAQ,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,GAAG,GAAG,CAAA;QACpB,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,oCAAoC,CAAC,CAAA;QAEtD,OAAM;IACR,CAAC;IAED,IAAI,OAAO,GAAG,MAAM,IAAA,2BAAU,EAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IAE/C,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,6BAA6B,CAAC,CAAA;QAE/C,IAAA,+CAAsB,EAAC,OAAO,CAAC,CAAA;IACjC,CAAC;IAED,IAAI,IAAA,oBAAO,EAAC,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;YAChC,WAAW,EAAE,aAAa;YAC1B,aAAa,EAAE,iBAAiB;YAChC,IAAI,EAAE;gBACJ,OAAO,EAAE,CAAC,CAAC,6BAA6B,CAAC;gBACzC,wBAAwB;gBACxB,0BAA0B;gBAC1B,SAAS;aACV;SACF,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAC,CAAA","sourcesContent":["import Router from 'koa-router'\nimport { ILike } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { getRepository, getSiteRootPath } from '@things-factory/shell'\n\nimport { resendInvitationEmail } from '../controllers/invitation.js'\nimport { resetPassword, sendPasswordResetEmail } from '../controllers/reset-password.js'\nimport { unlockUser } from '../controllers/unlock-user.js'\nimport { resendVerificationEmail, verify } from '../controllers/verification.js'\nimport { User } from '../service/user/user.js'\nimport { accepts } from '../utils/accepts.js'\nimport { clearAccessTokenCookie } from '../utils/access-token-cookie.js'\n\nconst disableUserSignupProcess = config.get('disableUserSignupProcess', false)\nconst disableUserFavoredLanguage = config.get('i18n/disableUserFavoredLanguage', false)\nconst languages = config.get('i18n/languages', false)\nconst passwordRule = config.get('password') || {\n lowerCase: true,\n upperCase: true,\n digit: true,\n specialCharacter: true,\n allowRepeat: false,\n useTightPattern: true,\n useLoosePattern: false,\n tightCharacterLength: 8,\n looseCharacterLength: 15\n}\n\nexport const authPublicProcessRouter = new Router({\n prefix: '/auth'\n})\n\nauthPublicProcessRouter.post('/join', async (context, next) => {\n const { email } = context.request.body || {}\n\n const repository = getRepository(User)\n\n var user = await repository.findOne({\n where: { username: email },\n relations: ['domains']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) {\n user = await repository.findOne({\n where: { email: ILike(email) },\n relations: ['domains']\n })\n }\n\n if (user) {\n context.redirect(`/auth/signin?username=${email}`)\n } else {\n context.redirect(`/auth/signup?username=${email}&email=${email}`)\n }\n})\n\nauthPublicProcessRouter.all('/signout', async (context, next) => {\n const { header, t } = context\n clearAccessTokenCookie(context)\n\n context.body = t('text.signout successfully')\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n context.redirect(getSiteRootPath(context))\n }\n})\n\nauthPublicProcessRouter.get('/forgot-password', async (context, next) => {\n const { email } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'forgot-password',\n elementScript: '/auth/forgot-password.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/reset-password', async (context, next) => {\n const { token } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/unlock-user', async (context, next) => {\n const { token } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'unlock-user',\n elementScript: '/auth/unlock-user.js',\n data: {\n token,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/activate/:email', async (context, next) => {\n const { email } = context.params\n\n await context.render('auth-page', {\n pageElement: 'auth-activate',\n elementScript: '/auth/activate.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/verify/:token', async (context, next) => {\n const { header, t } = context\n var token = context.params.token\n\n await verify(token)\n\n var message = t('text.user activated successfully')\n\n context.body = message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n\nauthPublicProcessRouter.post('/resend-verification-email', async (context, next) => {\n const { t } = context\n const { email } = context.request.body\n\n var succeed = await resendVerificationEmail(email, context)\n var message = t('text.verification email sent')\n\n if (succeed) {\n context.status = 200\n context.body = message\n }\n})\n\nauthPublicProcessRouter.post('/resend-invitation-email', async (context, next) => {\n const { t } = context\n const { email, reference, type } = context.request.body\n\n var succeed = await resendInvitationEmail(\n {\n email,\n reference,\n type\n },\n context\n )\n\n var message = t('text.invitation email sent')\n\n if (succeed) {\n context.status = 200\n context.body = message\n }\n})\n\nauthPublicProcessRouter.post('/forgot-password', async (context, next) => {\n const { t } = context\n const { email } = context.request.body\n\n if (!email) return next()\n\n const userRepo = getRepository(User)\n const user = await userRepo.findOne({\n where: {\n email\n }\n })\n\n const succeed = await sendPasswordResetEmail({\n user,\n context\n })\n\n if (succeed) {\n context.status = 200\n context.body = t('text.password reset email sent')\n }\n})\n\nauthPublicProcessRouter.post('/reset-password', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n try {\n if (!(token && password)) {\n let message = t('error.token or password is invalid')\n\n context.status = 404\n context.body = {\n message\n }\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n\n return\n }\n\n await resetPassword(token, password, context)\n\n var message = t('text.password changed successfully')\n context.body = message\n\n clearAccessTokenCookie(context)\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n } catch (e) {\n context.status = 404\n context.body = e.message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message: e.message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n }\n})\n\nauthPublicProcessRouter.post('/unlock-user', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n if (!(token || password)) {\n context.status = 404\n context.body = t('error.token or password is invalid')\n\n return\n }\n\n var succeed = await unlockUser(token, password)\n\n if (succeed) {\n context.body = t('text.password reset succeed')\n\n clearAccessTokenCookie(context)\n }\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message: t('text.account is reactivated'),\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n"]}
|
|
1
|
+
{"version":3,"file":"auth-public-process-router.js","sourceRoot":"","sources":["../../server/router/auth-public-process-router.ts"],"names":[],"mappings":"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","sourcesContent":["import Router from 'koa-router'\nimport { ILike } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { getRepository, getSiteRootPath } from '@things-factory/shell'\n\nimport { resendInvitationEmail } from '../controllers/invitation.js'\nimport { resetPassword, sendPasswordResetEmail } from '../controllers/reset-password.js'\nimport { unlockUser } from '../controllers/unlock-user.js'\nimport { resendVerificationEmail, verify } from '../controllers/verification.js'\nimport { User } from '../service/user/user.js'\nimport { accepts } from '../utils/accepts.js'\nimport { clearAccessTokenCookie } from '../utils/access-token-cookie.js'\n\nconst disableUserSignupProcess = config.get('disableUserSignupProcess', false)\nconst disableUserFavoredLanguage = config.get('i18n/disableUserFavoredLanguage', false)\nconst languages = config.get('i18n/languages', false)\nconst passwordRule = config.get('password') || {\n lowerCase: true,\n upperCase: true,\n digit: true,\n specialCharacter: true,\n allowRepeat: false,\n useTightPattern: true,\n useLoosePattern: false,\n tightCharacterLength: 8,\n looseCharacterLength: 15\n}\n\nexport const authPublicProcessRouter = new Router({\n prefix: '/auth'\n})\n\nauthPublicProcessRouter.post('/join', async (context, next) => {\n const { email } = context.request.body || {}\n\n const repository = getRepository(User)\n\n var user = await repository.findOne({\n where: { username: email },\n relations: ['domains']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) {\n user = await repository.findOne({\n where: { email: ILike(email) },\n relations: ['domains']\n })\n }\n\n if (user) {\n context.redirect(`/auth/signin?username=${email}`)\n } else {\n context.redirect(`/auth/signup?username=${email}&email=${email}`)\n }\n})\n\nauthPublicProcessRouter.all('/signout', async (context, next) => {\n const { header, t } = context\n clearAccessTokenCookie(context)\n\n context.body = t('text.signout successfully')\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n context.redirect(getSiteRootPath(context))\n }\n})\n\nauthPublicProcessRouter.get('/forgot-password', async (context, next) => {\n const { email } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'forgot-password',\n elementScript: '/auth/forgot-password.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/reset-password', async (context, next) => {\n const { token } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/unlock-user', async (context, next) => {\n const { token } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'unlock-user',\n elementScript: '/auth/unlock-user.js',\n data: {\n token,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/activate/:email', async (context, next) => {\n const { email } = context.params\n\n await context.render('auth-page', {\n pageElement: 'auth-activate',\n elementScript: '/auth/activate.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/verify/:token', async (context, next) => {\n const { header, t } = context\n var token = context.params.token\n\n await verify(token)\n\n var message = t('text.user activated successfully')\n\n context.body = message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n\nauthPublicProcessRouter.post('/resend-verification-email', async (context, next) => {\n const { t } = context\n const { email } = context.request.body\n\n var succeed = await resendVerificationEmail(email, context)\n var message = t('text.verification email sent')\n\n if (succeed) {\n context.status = 200\n context.body = message\n }\n})\n\nauthPublicProcessRouter.post('/resend-invitation-email', async (context, next) => {\n const { t } = context\n const { email, reference, type } = context.request.body\n\n var succeed = await resendInvitationEmail(\n {\n email,\n reference,\n type\n },\n context\n )\n\n var message = t('text.invitation email sent')\n\n if (succeed) {\n context.status = 200\n context.body = message\n }\n})\n\nauthPublicProcessRouter.post('/forgot-password', async (context, next) => {\n const { t } = context\n const { email } = context.request.body\n\n if (!email) return next()\n\n const userRepo = getRepository(User)\n const user = await userRepo.findOne({\n where: {\n email\n }\n })\n\n const succeed = await sendPasswordResetEmail({\n user,\n context\n })\n\n if (succeed) {\n context.status = 200\n context.body = t('text.password reset email sent')\n }\n})\n\nauthPublicProcessRouter.post('/reset-password', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n try {\n if (!(token && password)) {\n let message = t('error.token or password is invalid')\n\n context.status = 404\n context.body = {\n message\n }\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n\n return\n }\n\n await resetPassword(token, password, context)\n\n var message = t('text.password changed successfully')\n context.body = message\n\n clearAccessTokenCookie(context)\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n } catch (e) {\n context.status = 404\n context.body = e.message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message: e.message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n }\n})\n\nauthPublicProcessRouter.post('/unlock-user', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n /* Both are required, the way `/auth/reset-password` requires them. This read `||`, so a body\n carrying a password and no token passed the guard and reached the unlock with no token —\n see the note in `unlockUser`. */\n if (!(token && password)) {\n context.status = 404\n context.body = t('error.token or password is invalid')\n\n return\n }\n\n const succeed = await unlockUser(token, password)\n\n /*\n * Say what happened. The success page used to render whether or not the unlock took, so a\n * reused link, a token for another account, or an account that was never locked all read as\n * \"account is reactivated\" — and the person then could not sign in.\n *\n * A refusal returns to the form with a reason, the shape `/auth/reset-password` already uses.\n */\n if (!succeed) {\n const message = t('text.invalid verification token')\n\n context.status = 404\n context.body = message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'unlock-user',\n elementScript: '/auth/unlock-user.js',\n data: {\n token,\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n\n return\n }\n\n context.body = t('text.password reset succeed')\n\n clearAccessTokenCookie(context)\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message: t('text.account is reactivated'),\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n"]}
|
|
@@ -1,3 +1,49 @@
|
|
|
1
1
|
import { GraphQLSchema } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* `legacy` — **권한을 옮기는 동안 옛 이름도 받아 준다.**
|
|
4
|
+
*
|
|
5
|
+
* ── 왜 필요한가 ─────────────────────────────────────────────────────────────
|
|
6
|
+
* 리졸버의 `category` 를 바꾸는 순간 **역할이 들고 있던 기존 부여가 고아가 된다.** 새 이름을
|
|
7
|
+
* 가진 사람이 아직 없으므로, 옮긴 그날 그 기능을 쓰던 사람들이 전부 막힌다.
|
|
8
|
+
*
|
|
9
|
+
* 같은 자리가 두 곳에서 필요하다. 하나는 `board-ai` 의 비-보드 op 를 `ai-assistant` 로 옮기는
|
|
10
|
+
* 일이고(공장 질문이 보드 편집 권한을 요구하던 문제), 다른 하나는 `execute` 축을 세울 때다
|
|
11
|
+
* (`mutation` 을 들고 있던 역할이 승인 권한을 잃는다). 그래서 장치를 하나로 만든다.
|
|
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 라고 말하게** 한다.
|
|
47
|
+
*/
|
|
2
48
|
export declare const privilegeDirectiveTypeDefs: import("graphql").DocumentNode;
|
|
3
49
|
export declare const privilegeDirectiveResolver: (schema: GraphQLSchema) => GraphQLSchema;
|
|
@@ -5,16 +5,64 @@ 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");
|
|
8
9
|
const check_permission_js_1 = require("../../utils/check-permission.js");
|
|
9
10
|
const privilege_rejection_js_1 = require("./privilege-rejection.js");
|
|
10
11
|
process['PRIVILEGES'] = {};
|
|
11
12
|
const DIRECTIVE = 'privilege';
|
|
13
|
+
/**
|
|
14
|
+
* `legacy` — **권한을 옮기는 동안 옛 이름도 받아 준다.**
|
|
15
|
+
*
|
|
16
|
+
* ── 왜 필요한가 ─────────────────────────────────────────────────────────────
|
|
17
|
+
* 리졸버의 `category` 를 바꾸는 순간 **역할이 들고 있던 기존 부여가 고아가 된다.** 새 이름을
|
|
18
|
+
* 가진 사람이 아직 없으므로, 옮긴 그날 그 기능을 쓰던 사람들이 전부 막힌다.
|
|
19
|
+
*
|
|
20
|
+
* 같은 자리가 두 곳에서 필요하다. 하나는 `board-ai` 의 비-보드 op 를 `ai-assistant` 로 옮기는
|
|
21
|
+
* 일이고(공장 질문이 보드 편집 권한을 요구하던 문제), 다른 하나는 `execute` 축을 세울 때다
|
|
22
|
+
* (`mutation` 을 들고 있던 역할이 승인 권한을 잃는다). 그래서 장치를 하나로 만든다.
|
|
23
|
+
*
|
|
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 라고 말하게** 한다.
|
|
58
|
+
*/
|
|
12
59
|
exports.privilegeDirectiveTypeDefs = (0, graphql_tag_1.default) `
|
|
13
60
|
directive @privilege(
|
|
14
61
|
category: String
|
|
15
62
|
privilege: String
|
|
16
63
|
domainOwnerGranted: Boolean
|
|
17
64
|
superUserGranted: Boolean
|
|
65
|
+
legacy: String
|
|
18
66
|
) on FIELD_DEFINITION
|
|
19
67
|
`;
|
|
20
68
|
const privilegeDirectiveResolver = (schema) => (0, utils_1.mapSchema)(schema, {
|
|
@@ -25,32 +73,65 @@ const privilegeDirectiveResolver = (schema) => (0, utils_1.mapSchema)(schema, {
|
|
|
25
73
|
if (!args) {
|
|
26
74
|
throw new Error(`Unexpected Error. args should be defined in @privilege directive for field ${fieldName}.`);
|
|
27
75
|
}
|
|
28
|
-
const { domainOwnerGranted, superUserGranted, category, privilege } = privilegeDirective;
|
|
76
|
+
const { domainOwnerGranted, superUserGranted, category, privilege, legacy } = privilegeDirective;
|
|
29
77
|
if (category && privilege) {
|
|
30
78
|
process['PRIVILEGES'][`${category} ${privilege}`] = [category, privilege];
|
|
31
79
|
}
|
|
80
|
+
/*
|
|
81
|
+
* 옛 쌍은 **일부러 등록하지 않는다** — 등록하면 옛 행이 유령으로 표시되지 않아
|
|
82
|
+
* 별칭을 지울 시점을 아무도 못 본다. 위 주석 참조.
|
|
83
|
+
*/
|
|
32
84
|
// 필드의 기존 description 가져오기
|
|
33
85
|
const existingDescription = fieldConfig.description || '';
|
|
34
86
|
// 권한 정보를 포함한 새로운 description 생성
|
|
35
87
|
const privilegeDescription = `\n\n🔒 Requires privilege: ${category}:${privilege}` +
|
|
88
|
+
(legacy ? ` (or ${legacy}:${privilege} while it is being moved)` : '') +
|
|
36
89
|
(domainOwnerGranted ? ', Domain ownership' : '') +
|
|
37
90
|
(superUserGranted ? ', System ownership' : '');
|
|
38
91
|
// 기존 description과 결합
|
|
39
92
|
fieldConfig.description = `${existingDescription} ${privilegeDescription}`.trim();
|
|
40
93
|
fieldConfig.resolve = async function (source, args, context, info) {
|
|
41
94
|
const { domain, user, unsafeIP, prohibitedPrivileges } = context.state;
|
|
42
|
-
|
|
95
|
+
const permitted = await (0, check_permission_js_1.checkPermission)({
|
|
43
96
|
category,
|
|
44
97
|
privilege,
|
|
45
98
|
owner: domainOwnerGranted,
|
|
46
99
|
super: superUserGranted
|
|
47
|
-
}, user, domain, unsafeIP, prohibitedPrivileges)
|
|
100
|
+
}, user, domain, unsafeIP, prohibitedPrivileges);
|
|
101
|
+
if (permitted) {
|
|
48
102
|
return await resolve.call(this, source, args, context, info);
|
|
49
103
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
104
|
+
/*
|
|
105
|
+
* 새 이름으로 안 열렸다 — 옮기는 중이면 옛 이름으로 한 번 더 본다.
|
|
106
|
+
*
|
|
107
|
+
* 소유권 우회는 다시 묻지 않는다(`owner`·`super` 를 넘기지 않는다). 그것은 이미 위에서
|
|
108
|
+
* 판정됐고, 여기서 다시 주면 옛 이름이 우회를 한 번 더 얻는 것이 된다.
|
|
109
|
+
*
|
|
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
|
+
}
|
|
53
129
|
}
|
|
130
|
+
/*
|
|
131
|
+
* 거절은 **새 이름**으로 말한다 — 옛 이름을 말하면 관리자가 사라질 권한을 부여한다.
|
|
132
|
+
* 문장 만들기는 `privilege-rejection.ts` 가 소유한다.
|
|
133
|
+
*/
|
|
134
|
+
throw (0, privilege_rejection_js_1.privilegeRejection)(context, category, privilege);
|
|
54
135
|
};
|
|
55
136
|
return fieldConfig;
|
|
56
137
|
}
|
|
@@ -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;AAC1E,yEAAiE;AACjE,qEAA6D;AAE7D,OAAO,CAAC,YAAY,CAAC,GAAG,EAAE,CAAA;AAE1B,MAAM,SAAS,GAAG,WAAW,CAAA;
|
|
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,5 +1,6 @@
|
|
|
1
1
|
import { Role } from './role.js';
|
|
2
2
|
import { RoleQuery } from './role-query.js';
|
|
3
3
|
import { RoleMutation } from './role-mutation.js';
|
|
4
|
+
import { RolePrivilegeQuery } from './role-privilege-query.js';
|
|
4
5
|
export declare const entities: (typeof Role)[];
|
|
5
|
-
export declare const resolvers: (typeof RoleQuery | typeof RoleMutation)[];
|
|
6
|
+
export declare const resolvers: (typeof RoleQuery | typeof RoleMutation | typeof RolePrivilegeQuery)[];
|
|
@@ -4,6 +4,7 @@ exports.resolvers = exports.entities = void 0;
|
|
|
4
4
|
const role_js_1 = require("./role.js");
|
|
5
5
|
const role_query_js_1 = require("./role-query.js");
|
|
6
6
|
const role_mutation_js_1 = require("./role-mutation.js");
|
|
7
|
+
const role_privilege_query_js_1 = require("./role-privilege-query.js");
|
|
7
8
|
exports.entities = [role_js_1.Role];
|
|
8
|
-
exports.resolvers = [role_query_js_1.RoleQuery, role_mutation_js_1.RoleMutation];
|
|
9
|
+
exports.resolvers = [role_query_js_1.RoleQuery, role_mutation_js_1.RoleMutation, role_privilege_query_js_1.RolePrivilegeQuery];
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/role/index.ts"],"names":[],"mappings":";;;AAAA,uCAAgC;AAChC,mDAA2C;AAC3C,yDAAiD;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/role/index.ts"],"names":[],"mappings":";;;AAAA,uCAAgC;AAChC,mDAA2C;AAC3C,yDAAiD;AACjD,uEAA8D;AAEjD,QAAA,QAAQ,GAAG,CAAC,cAAI,CAAC,CAAA;AACjB,QAAA,SAAS,GAAG,CAAC,yBAAS,EAAE,+BAAY,EAAE,4CAAkB,CAAC,CAAA","sourcesContent":["import { Role } from './role.js'\nimport { RoleQuery } from './role-query.js'\nimport { RoleMutation } from './role-mutation.js'\nimport { RolePrivilegeQuery } from './role-privilege-query.js'\n\nexport const entities = [Role]\nexport const resolvers = [RoleQuery, RoleMutation, RolePrivilegeQuery]\n"]}
|
|
@@ -3,6 +3,22 @@ import { NewRole, RolePatch } from './role-types.js';
|
|
|
3
3
|
export declare class RoleMutation {
|
|
4
4
|
createRole(role: NewRole, context: ResolverContext): Promise<Role>;
|
|
5
5
|
updateRole(id: string, patch: RolePatch, context: ResolverContext): Promise<Role>;
|
|
6
|
+
/**
|
|
7
|
+
* 격자에서 고친 여러 줄을 한 번에 저장한다.
|
|
8
|
+
*
|
|
9
|
+
* 이 저장소의 목록 화면 관례다 — `ox-grist` 의 `dirtyRecords` 를 `cuFlag` 와 함께 보내면
|
|
10
|
+
* `+` 는 만들고 `M` 은 고친다(`updateMultipleAuthProvider`·`updateMultipleUser`·
|
|
11
|
+
* `updateMultipleDomainLink` 가 같은 모양). 역할에만 이 자리가 없어서 이름·설명을 격자에서
|
|
12
|
+
* 고칠 수 없었다.
|
|
13
|
+
*
|
|
14
|
+
* **`privileges` 는 여기서 안 건드린다.** 격자는 이름과 설명만 고치고, 권한은 전용 화면이
|
|
15
|
+
* 쓴다(`updateRole`). 안 보낸 것을 「비우라」로 읽으면 격자에서 이름만 고친 순간 그 역할이
|
|
16
|
+
* 아무것도 안 여는 역할이 된다.
|
|
17
|
+
*
|
|
18
|
+
* 지우기는 이 자리에 없다 — 격자의 삭제는 `deleteRoles` 가 받고, 그쪽은 **쓰이는 역할을
|
|
19
|
+
* 남긴다**는 규칙을 따로 갖는다.
|
|
20
|
+
*/
|
|
21
|
+
updateMultipleRole(patches: RolePatch[], context: ResolverContext): Promise<Role[]>;
|
|
6
22
|
deleteRole(id: string, context: ResolverContext): Promise<Boolean>;
|
|
7
|
-
deleteRoles(ids: string[],
|
|
23
|
+
deleteRoles(ids: string[], context: ResolverContext): Promise<boolean>;
|
|
8
24
|
}
|