@opengis/fastify-table 2.0.88 → 2.0.90

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 (26) hide show
  1. package/dist/server/migrations/users.sql +3 -1
  2. package/dist/server/plugins/auth/funcs/authorizeUser.d.ts.map +1 -1
  3. package/dist/server/plugins/auth/funcs/authorizeUser.js +9 -0
  4. package/dist/server/plugins/auth/index.js +1 -1
  5. package/dist/server/plugins/migration/exec.migrations.d.ts.map +1 -1
  6. package/dist/server/plugins/migration/exec.migrations.js +1 -0
  7. package/dist/server/plugins/policy/funcs/checkPolicy.d.ts.map +1 -1
  8. package/dist/server/plugins/policy/funcs/checkPolicy.js +1 -1
  9. package/dist/server/routes/auth/controllers/2factor/providers/totp.d.ts +3 -4
  10. package/dist/server/routes/auth/controllers/2factor/providers/totp.d.ts.map +1 -1
  11. package/dist/server/routes/auth/controllers/2factor/providers/totp.js +7 -11
  12. package/dist/server/routes/auth/controllers/2factor/qrcode.d.ts.map +1 -1
  13. package/dist/server/routes/auth/controllers/2factor/qrcode.js +3 -5
  14. package/dist/server/routes/auth/controllers/2factor/recovery.d.ts +1 -1
  15. package/dist/server/routes/auth/controllers/2factor/recovery.d.ts.map +1 -1
  16. package/dist/server/routes/auth/controllers/2factor/recovery.js +26 -18
  17. package/dist/server/routes/auth/controllers/2factor/reset.d.ts +3 -0
  18. package/dist/server/routes/auth/controllers/2factor/reset.d.ts.map +1 -0
  19. package/dist/server/routes/auth/controllers/2factor/reset.js +17 -0
  20. package/dist/server/routes/auth/controllers/2factor/verify.d.ts.map +1 -1
  21. package/dist/server/routes/auth/controllers/2factor/verify.js +15 -14
  22. package/dist/server/routes/auth/controllers/page/login2faTemplate.d.ts.map +1 -1
  23. package/dist/server/routes/auth/controllers/page/login2faTemplate.js +6 -8
  24. package/dist/server/routes/auth/index.d.ts.map +1 -1
  25. package/dist/server/routes/auth/index.js +4 -0
  26. package/package.json +2 -1
@@ -181,4 +181,6 @@ COMMENT ON COLUMN admin.user_cls.user_clsid IS 'ID';
181
181
  COMMENT ON COLUMN admin.user_cls.code IS 'Код';
182
182
  COMMENT ON COLUMN admin.user_cls.name IS 'Назва';
183
183
  COMMENT ON COLUMN admin.user_cls.icon IS 'Іконка';
184
- COMMENT ON COLUMN admin.user_cls.color IS 'Колір';
184
+ COMMENT ON COLUMN admin.user_cls.color IS 'Колір';
185
+
186
+ update admin.users set twofa=false where uid='1';
@@ -1 +1 @@
1
- {"version":3,"file":"authorizeUser.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/auth/funcs/authorizeUser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA4BtD,wBAA8B,aAAa,CACzC,IAAI,EAAE,YAAY,EAClB,GAAG,EAAE,GAAG,EACR,QAAQ,SAAe,EACvB,MAAM,CAAC,EAAE,MAAM,gBA8HhB"}
1
+ {"version":3,"file":"authorizeUser.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/auth/funcs/authorizeUser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAgCtD,wBAA8B,aAAa,CACzC,IAAI,EAAE,YAAY,EAClB,GAAG,EAAE,GAAG,EACR,QAAQ,SAAe,EACvB,MAAM,CAAC,EAAE,MAAM,gBAqIhB"}
@@ -1,8 +1,11 @@
1
1
  import config from "../../../../config.js";
2
2
  import logger from "../../logger/getLogger.js";
3
3
  import pgClients from "../../pg/pgClients.js";
4
+ import getRedis from "../../redis/funcs/getRedis.js";
4
5
  import applyHook from "../../hook/applyHook.js";
5
6
  import logAuth from "./logAuth.js";
7
+ const rclient = getRedis();
8
+ const rclient2 = getRedis({ db: 2 });
6
9
  /*
7
10
  session duration by default
8
11
  * 10 hours = 600 minutes w/o keep (remember me checkbox)
@@ -59,6 +62,12 @@ export default async function authorizeUser(user, req, authType = "creds-user",
59
62
  const redirectUrl = req.headers?.referer?.match?.(/[?&]redirect=([^&]+)/)?.[1] || "/";
60
63
  const twofaEnabled = user?.twofa && user.uid && pg;
61
64
  const registered = false; // ? check by created/updated date?
65
+ if (config.auth?.oneUser || true) {
66
+ const userSessionKey = `${config.pg?.database}:user:${user.uid}`;
67
+ const sessionID = await rclient.get(userSessionKey);
68
+ await rclient2.del(`session_auth:${config.pg?.database}:${sessionID}`);
69
+ await rclient?.set?.(userSessionKey, req.session.sessionId);
70
+ }
62
71
  if (req.method === "POST" &&
63
72
  (!twofaEnabled || req.session?.secondFactorPassed)) {
64
73
  return {
@@ -78,7 +78,7 @@ export async function onRequest(req, reply) {
78
78
  // if 2factor is enabled globally + for user and secondFactorPassed not true => redirect to 2factor login page
79
79
  if (req.user?.uid &&
80
80
  req.user?.twofa &&
81
- config.auth?.["2factor"] &&
81
+ // config.auth?.["2factor"] &&
82
82
  !isPublic &&
83
83
  (routeOptions?.method || "GET") === "GET" &&
84
84
  !req.session?.secondFactorPassed &&
@@ -1 +1 @@
1
- {"version":3,"file":"exec.migrations.d.ts","sourceRoot":"","sources":["../../../../server/plugins/migration/exec.migrations.ts"],"names":[],"mappings":"AAWA,wBAA8B,cAAc,CAC1C,OAAO,EAAE,GAAG,EACZ,EAAE,MAAmB,EACrB,MAAM,UAAQ,mBA+Df"}
1
+ {"version":3,"file":"exec.migrations.d.ts","sourceRoot":"","sources":["../../../../server/plugins/migration/exec.migrations.ts"],"names":[],"mappings":"AAWA,wBAA8B,cAAc,CAC1C,OAAO,EAAE,GAAG,EACZ,EAAE,MAAmB,EACrB,MAAM,UAAQ,mBAgEf"}
@@ -26,6 +26,7 @@ export default async function execMigrations(dirPath, pg = pgClients.client, isc
26
26
  return txt;
27
27
  }
28
28
  if (process.env.NODE_ENV !== "production" &&
29
+ !process.env.MIGRATE &&
29
30
  !(iscore ? config.migrationsCore : config.migrations)) {
30
31
  const txt = `migrations skip: not a production environment - ${iscore ? "core" : "path"} : ${dirPath}`;
31
32
  if (debug)
@@ -1 +1 @@
1
- {"version":3,"file":"checkPolicy.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/policy/funcs/checkPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,KAAK,EAEV,eAAe,EAEhB,MAAM,wBAAwB,CAAC;AAKhC,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,oSAiL5E"}
1
+ {"version":3,"file":"checkPolicy.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/policy/funcs/checkPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,KAAK,EAEV,eAAe,EAEhB,MAAM,wBAAwB,CAAC;AAKhC,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,oSA+K5E"}
@@ -10,7 +10,7 @@ export default function checkPolicy(req, reply) {
10
10
  return null;
11
11
  }
12
12
  // ! skip non-API Requests
13
- const isApi = ["/files/", "/api/", "/api-user/", "/logger", "/file/"].filter((el) => path.includes(el)).length;
13
+ const isApi = routeOptions.method && routeOptions.url && routeOptions.handler;
14
14
  if (!isApi) {
15
15
  return null;
16
16
  }
@@ -1,7 +1,6 @@
1
1
  import { ExtendedPG } from "../../../../../types/core.js";
2
2
  interface ISecret {
3
3
  uid: string;
4
- TYPE?: string;
5
4
  pg: ExtendedPG;
6
5
  }
7
6
  interface ICode {
@@ -10,9 +9,9 @@ interface ICode {
10
9
  pg: ExtendedPG;
11
10
  enable?: boolean;
12
11
  }
13
- declare const enableSecret: ({ uid, TYPE, pg }: ISecret) => Promise<void>;
14
- declare const deleteSecret: ({ uid, TYPE, pg }: ISecret) => Promise<void>;
15
- declare const getSecret: ({ uid, TYPE, pg }: ISecret) => Promise<{
12
+ declare const enableSecret: ({ uid, pg }: ISecret) => Promise<void>;
13
+ declare const deleteSecret: ({ uid, pg }: ISecret) => Promise<void>;
14
+ declare const getSecret: ({ uid, pg }: ISecret) => Promise<{
16
15
  secret: any;
17
16
  enabled: any;
18
17
  recoveryCodes: any;
@@ -1 +1 @@
1
- {"version":3,"file":"totp.d.ts","sourceRoot":"","sources":["../../../../../../../server/routes/auth/controllers/2factor/providers/totp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAQ1D,UAAU,OAAO;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,UAAU,CAAC;CAChB;AAED,UAAU,KAAK;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,UAAU,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAaD,QAAA,MAAM,YAAY,GAAU,mBAAmB,OAAO,kBAKrD,CAAC;AAEF,QAAA,MAAM,YAAY,GAAU,mBAAmB,OAAO,kBAKrD,CAAC;AAEF,QAAA,MAAM,SAAS,GAAU,mBAAmB,OAAO;;;;EAclD,CAAC;AAqCF,QAAA,MAAM,QAAQ,GAAU,aAAa,OAAO;;;;;;;;;;;;EA4C3C,CAAC;AAEF,QAAA,MAAM,MAAM,GAAU,0BAA0B,KAAK;;;EAiBpD,CAAC;AAKF,QAAA,MAAM,MAAM,GAAU,2BAA2B,KAAK,iBAuBrD,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;;AAE3E,wBAAoB"}
1
+ {"version":3,"file":"totp.d.ts","sourceRoot":"","sources":["../../../../../../../server/routes/auth/controllers/2factor/providers/totp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAQ1D,UAAU,OAAO;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,UAAU,CAAC;CAChB;AAED,UAAU,KAAK;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,UAAU,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAYD,QAAA,MAAM,YAAY,GAAU,aAAa,OAAO,kBAK/C,CAAC;AAEF,QAAA,MAAM,YAAY,GAAU,aAAa,OAAO,kBAK/C,CAAC;AAEF,QAAA,MAAM,SAAS,GAAU,aAAa,OAAO;;;;EAc5C,CAAC;AAuBF,QAAA,MAAM,QAAQ,GAAU,aAAa,OAAO;;;;;;;;;;;;EA0C3C,CAAC;AAEF,QAAA,MAAM,MAAM,GAAU,0BAA0B,KAAK;;;EAiBpD,CAAC;AAKF,QAAA,MAAM,MAAM,GAAU,2BAA2B,KAAK,iBAqBrD,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;;AAE3E,wBAAoB"}
@@ -2,13 +2,13 @@ import crypto from "node:crypto";
2
2
  import qrcode from "qrcode";
3
3
  import { authenticator } from "otplib";
4
4
  const TYPE = "TOTP";
5
- const enableSecret = async ({ uid, TYPE, pg }) => {
5
+ const enableSecret = async ({ uid, pg }) => {
6
6
  await pg.query("update admin.users_social_auth set enabled=true where uid = $1 and social_auth_type = $2", [uid, TYPE]);
7
7
  };
8
- const deleteSecret = async ({ uid, TYPE, pg }) => {
8
+ const deleteSecret = async ({ uid, pg }) => {
9
9
  await pg.query("delete from admin.users_social_auth where uid=$1 and social_auth_type = $2", [uid, TYPE]);
10
10
  };
11
- const getSecret = async ({ uid, TYPE, pg }) => {
11
+ const getSecret = async ({ uid, pg }) => {
12
12
  const { social_auth_code: secret, enabled, recoveryCodes, } = await pg
13
13
  .query(`select social_auth_code, enabled, social_auth_obj->'codesArray' as "recoveryCodes"
14
14
  from admin.users_social_auth
@@ -16,11 +16,11 @@ const getSecret = async ({ uid, TYPE, pg }) => {
16
16
  .then((el) => el.rows?.[0] || {});
17
17
  return { secret, enabled, recoveryCodes };
18
18
  };
19
- const addSecret = async ({ uid, secret, TYPE, pg, recoveryCodes, otp, }) => {
19
+ const addSecret = async ({ uid, secret, pg, recoveryCodes, otp }) => {
20
20
  await pg.query(`insert into admin.users_social_auth(uid, social_auth_code, social_auth_type, social_auth_obj, social_auth_url, enabled)
21
21
  values($1, $2, $3, $4::json, $5, false)`, [uid, secret, TYPE, { codesArray: recoveryCodes }, otp]);
22
22
  };
23
- const updateSecret = async ({ uid, TYPE, pg, secret, recoveryCodes, otp, }) => {
23
+ const updateSecret = async ({ uid, pg, secret, recoveryCodes, otp }) => {
24
24
  const result = await pg
25
25
  .query(`update admin.users_social_auth
26
26
  set social_auth_code=$3, social_auth_obj=$4::json, social_auth_url=$5
@@ -30,7 +30,7 @@ const updateSecret = async ({ uid, TYPE, pg, secret, recoveryCodes, otp, }) => {
30
30
  };
31
31
  // return a new secret until it's enabled
32
32
  const generate = async ({ uid, pg }) => {
33
- const { enabled } = await getSecret({ uid, TYPE, pg });
33
+ const { enabled } = await getSecret({ uid, pg });
34
34
  if (enabled)
35
35
  return { enabled };
36
36
  const secret = authenticator.generateSecret();
@@ -48,7 +48,6 @@ const generate = async ({ uid, pg }) => {
48
48
  await addSecret({
49
49
  uid,
50
50
  secret,
51
- TYPE,
52
51
  pg,
53
52
  recoveryCodes,
54
53
  otp,
@@ -58,7 +57,6 @@ const generate = async ({ uid, pg }) => {
58
57
  await updateSecret({
59
58
  uid,
60
59
  secret,
61
- TYPE,
62
60
  pg,
63
61
  recoveryCodes,
64
62
  otp,
@@ -72,7 +70,7 @@ const generate = async ({ uid, pg }) => {
72
70
  };
73
71
  };
74
72
  const verify = async ({ uid, code: token, pg }) => {
75
- const { secret, enabled, recoveryCodes } = await getSecret({ uid, TYPE, pg });
73
+ const { secret, enabled, recoveryCodes } = await getSecret({ uid, pg });
76
74
  // console.debug('secret', secret, 'enabled', enabled, 'verification', 'token', authenticator.generate(secret), authenticator.verify({ token: authenticator.generate(secret), secret }));
77
75
  if (!secret) {
78
76
  throw new Error("Включіть двофакторну аутентифікацію");
@@ -99,14 +97,12 @@ const toggle = async ({ uid, code, pg, enable }) => {
99
97
  if (enable) {
100
98
  await enableSecret({
101
99
  uid,
102
- TYPE,
103
100
  pg,
104
101
  });
105
102
  return recoveryCodes;
106
103
  }
107
104
  await deleteSecret({
108
105
  uid,
109
- TYPE,
110
106
  pg,
111
107
  });
112
108
  return "Відключено";
@@ -1 +1 @@
1
- {"version":3,"file":"qrcode.d.ts","sourceRoot":"","sources":["../../../../../../server/routes/auth/controllers/2factor/qrcode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAa5D,wBAA8B,MAAM,CAClC,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,YAAY,kBAsDpB"}
1
+ {"version":3,"file":"qrcode.d.ts","sourceRoot":"","sources":["../../../../../../server/routes/auth/controllers/2factor/qrcode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAa5D,wBAA8B,MAAM,CAClC,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,YAAY,kBAoDpB"}
@@ -7,7 +7,7 @@ const headers = {
7
7
  };
8
8
  export default async function qrCode(req, reply) {
9
9
  const { pg = pgClients.client } = req;
10
- const { uid } = req.session?.passport?.user || {};
10
+ const { uid } = req.user || {};
11
11
  if (!uid) {
12
12
  return reply.status(401).send({ error: "unauthorized", code: 401 });
13
13
  }
@@ -20,12 +20,10 @@ export default async function qrCode(req, reply) {
20
20
  const userExists = await pg
21
21
  .query(`select uid from admin.users where uid=$1`, [uid])
22
22
  .then((el) => el.rows?.[0]?.uid);
23
- if (!userExists && config.pg) {
23
+ if (!userExists) {
24
24
  return reply.status(404).send({ error: "invalid user", code: 404 });
25
25
  }
26
- const { enabled, secret } = pg
27
- ? await getSecret({ pg, TYPE: "TOTP", uid })
28
- : {};
26
+ const { enabled, secret } = await getSecret({ pg, uid });
29
27
  const otp = secret
30
28
  ? await pg
31
29
  .query(`select social_auth_url
@@ -16,5 +16,5 @@ import { FastifyReply } from "fastify";
16
16
  * @returns {String|Object} message Повідомлення про успішне виконання або об'єкт з параметрами
17
17
  * @returns {String} redirect Шлях до переадресації
18
18
  */
19
- export default function recoveryFunction(req: any, reply: FastifyReply): Promise<never>;
19
+ export default function recovery(req: any, reply: FastifyReply): Promise<never>;
20
20
  //# sourceMappingURL=recovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../../../../../server/routes/auth/controllers/2factor/recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAiBvC;;;;;;;;;;;;;;;;GAgBG;AAEH,wBAA8B,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,kBAsE3E"}
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../../../../../server/routes/auth/controllers/2factor/recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAiBvC;;;;;;;;;;;;;;;;GAgBG;AAEH,wBAA8B,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,kBAqFnE"}
@@ -26,19 +26,24 @@ const dirname = path.dirname(fileURLToPath(import.meta.url));
26
26
  * @returns {String|Object} message Повідомлення про успішне виконання або об'єкт з параметрами
27
27
  * @returns {String} redirect Шлях до переадресації
28
28
  */
29
- export default async function recoveryFunction(req, reply) {
30
- const { pg = pgClients.client, session = {}, body = {} } = req;
31
- if (!config?.auth?.["2factor"]) {
32
- return reply.status(400).send("2fa not enabled");
29
+ export default async function recovery(req, reply) {
30
+ const { pg = pgClients.client, user, body = {}, method } = req;
31
+ if (!user?.uid) {
32
+ return reply.status(401).send({ error: "unauthorized", code: 401 });
33
+ }
34
+ if (!user.twofa) {
35
+ return reply.status(400).send({ error: "2fa not enabled", code: 400 });
33
36
  }
34
37
  if (!config.pg) {
35
- return reply.status(400).send("empty pg");
38
+ return reply.status(400).send({ error: "empty pg", code: 400 });
36
39
  }
37
40
  const { code } = body;
38
- const { uid, email } = session.passport?.user || {};
41
+ const { uid, email } = user || {};
39
42
  if (!code) {
40
43
  if (!email) {
41
- return reply.status(404).send("user recovery email not set");
44
+ return reply
45
+ .status(404)
46
+ .send({ error: "user recovery email not set", code: 404 });
42
47
  }
43
48
  // return reply.status(400).send('not enough params');
44
49
  const customPt = await getTemplate("pt", template);
@@ -49,8 +54,9 @@ export default async function recoveryFunction(req, reply) {
49
54
  where uid = $1 and social_auth_type = $2`, [uid, "TOTP"])
50
55
  .then((el) => el.rows?.[0]?.recoveryCodes || []);
51
56
  if (!recoveryCodes?.length) {
52
- return reply.status(404).send("user recovery code not found");
53
- // return { message: 'user recovery code not found', status: 404 };
57
+ return reply
58
+ .status(404)
59
+ .send({ error: "user recovery code not found", code: 404 });
54
60
  }
55
61
  const html = await handlebars.compile(pt)({
56
62
  recoveryCodes: [recoveryCodes[0]],
@@ -61,23 +67,25 @@ export default async function recoveryFunction(req, reply) {
61
67
  to: email,
62
68
  template: html,
63
69
  title: `Recovery code for ${req.hostname} 2-factor authentication`,
64
- nocache: config.local || config.debug,
70
+ nocache: config.local || config.debug || user?.user_type?.includes?.("admin"),
65
71
  });
72
+ if (method === "POST") {
73
+ return reply.status(200).send({ redirectUrl: "/2factor?recovery=1" });
74
+ }
66
75
  return reply.redirect("/2factor?recovery=1");
67
- // return reply.status(200).send('recovery code sent to user email');
68
76
  }
69
77
  try {
70
78
  // validate recovery code
71
79
  await verify({ uid, code, pg });
72
80
  // delete old secret
73
- await deleteSecret({
74
- pg,
75
- TYPE: "TOTP",
76
- uid,
77
- });
78
- return reply.redirect("/2factor");
81
+ await deleteSecret({ pg, uid });
82
+ const redirectUrl = config.auth?.link?.["2fa"]?.login || "/2factor";
83
+ if (method === "POST") {
84
+ return reply.status(200).send({ redirectUrl });
85
+ }
86
+ return reply.redirect(redirectUrl);
79
87
  }
80
88
  catch (err) {
81
- return reply.status(500).send(err.toString());
89
+ return reply.status(500).send({ error: err.toString(), code: 500 });
82
90
  }
83
91
  }
@@ -0,0 +1,3 @@
1
+ import { FastifyReply } from "fastify";
2
+ export default function reset(req: any, reply: FastifyReply): Promise<never>;
3
+ //# sourceMappingURL=reset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset.d.ts","sourceRoot":"","sources":["../../../../../../server/routes/auth/controllers/2factor/reset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAOvC,wBAA8B,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,kBAiBhE"}
@@ -0,0 +1,17 @@
1
+ import config from "../../../../../config.js";
2
+ import pgClients from "../../../../plugins/pg/pgClients.js";
3
+ import { deleteSecret } from "./providers/totp.js";
4
+ export default async function reset(req, reply) {
5
+ const { pg = pgClients.client, query } = req;
6
+ if (!query?.uid) {
7
+ return reply.status(400).send({
8
+ error: "not enough query params: uid",
9
+ code: 400,
10
+ });
11
+ }
12
+ if (!config.pg) {
13
+ return reply.status(400).send({ error: "empty pg", code: 400 });
14
+ }
15
+ await deleteSecret({ pg, uid: query.uid });
16
+ return reply.status(200).send({ ok: true });
17
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../../../../server/routes/auth/controllers/2factor/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AA0BvC;;;;;;;;;;;;;;;;GAgBG;AAEH,wBAA8B,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,kBAmEzE"}
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../../../../server/routes/auth/controllers/2factor/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AA0BvC;;;;;;;;;;;;;;;;GAgBG;AAEH,wBAA8B,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,kBAwEzE"}
@@ -30,26 +30,23 @@ const defaultPt = existsSync(path.join(dirname, `../../../../templates/pt/${temp
30
30
  * @returns {String} redirect Шлях до переадресації
31
31
  */
32
32
  export default async function verifyFunction(req, reply) {
33
- const { pg = pgClients.client, session = {}, body = {} } = req;
34
- const { uid, twofa, email } = session?.passport?.user || {};
35
- // const { nocache = config.local } = query;
33
+ const { pg = pgClients.client, user = {}, body = {} } = req;
34
+ const { uid, twofa, email } = user || {};
36
35
  const { code } = body;
37
36
  if (!twofa) {
38
- return reply.status(400).send("2fa not enabled");
37
+ return reply.status(400).send({ error: "2fa not enabled", code: 400 });
39
38
  }
40
39
  if (!config.pg) {
41
- return reply.status(400).send("empty pg");
40
+ return reply.status(400).send({ error: "empty pg", code: 400 });
42
41
  }
43
42
  if (!code) {
44
- return reply.status(400).send("not enough params");
43
+ return reply
44
+ .status(400)
45
+ .send({ error: "not enough body params: code", code: 400 });
45
46
  }
46
47
  try {
47
48
  const { enabled } = await verify({ uid, code, pg });
48
- await enableSecret({
49
- pg,
50
- TYPE: "TOTP",
51
- uid,
52
- });
49
+ await enableSecret({ pg, uid });
53
50
  req.session.secondFactorPassed = true;
54
51
  if (!enabled && email) {
55
52
  const { recoveryCodes } = await pg
@@ -68,7 +65,7 @@ export default async function verifyFunction(req, reply) {
68
65
  to: email,
69
66
  template: html,
70
67
  title: `Recovery codes for ${req.hostname} 2-factor authentication`,
71
- nocache: config.local || config.debug,
68
+ nocache: config.local || config.debug || user?.user_type?.includes?.("admin"),
72
69
  });
73
70
  }
74
71
  const redirectUrl = req.headers?.referer?.match?.(/[?&]redirect=([^&]+)/)?.[1] || "/";
@@ -78,8 +75,12 @@ export default async function verifyFunction(req, reply) {
78
75
  .send({ redirectUrl: redirectUrl.startsWith("/") ? redirectUrl : "/" });
79
76
  }
80
77
  catch (err) {
81
- if (err.message === "Невірний код") {
82
- return reply.status(401).send({ error: err.message, code: 401 });
78
+ if ([
79
+ "Невірний код",
80
+ "Вже знаходиться у даному стані",
81
+ "Включіть двофакторну аутентифікацію",
82
+ ].includes(err.message)) {
83
+ return reply.status(400).send({ error: err.message, code: 400 });
83
84
  }
84
85
  return reply.status(500).send(err.toString());
85
86
  }
@@ -1 +1 @@
1
- {"version":3,"file":"login2faTemplate.d.ts","sourceRoot":"","sources":["../../../../../../server/routes/auth/controllers/page/login2faTemplate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAwB5D,wBAA8B,aAAa,CACzC,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,YAAY,kBA0EpB"}
1
+ {"version":3,"file":"login2faTemplate.d.ts","sourceRoot":"","sources":["../../../../../../server/routes/auth/controllers/page/login2faTemplate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAiC5D,wBAA8B,aAAa,CACzC,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,YAAY,kBAgEpB"}
@@ -10,6 +10,8 @@ import { getSecret, generate } from "../2factor/providers/totp.js";
10
10
  // relative default template filepath
11
11
  const filename = fileURLToPath(import.meta.url);
12
12
  const dirname = path.dirname(filename);
13
+ const twoFactorPagePath = path.join(dirname, "../../../../../server/templates/page/2factor.html");
14
+ const defaultPagePath = path.join(dirname, "../../../../../server/templates/page/2factor-recovery.html");
13
15
  const headers = {
14
16
  "Content-Type": "text/html; charset=UTF-8",
15
17
  "Cache-Control": "no-cache",
@@ -17,9 +19,9 @@ const headers = {
17
19
  };
18
20
  export default async function loginTemplate(req, reply) {
19
21
  const { pg = pgClients.client } = req;
20
- const { uid } = req.session?.passport?.user || {};
22
+ const { uid } = req.user || {};
21
23
  if (!uid) {
22
- return reply.redirect("/login");
24
+ return reply.status(401).send({ error: "unauthorized", code: 401 });
23
25
  }
24
26
  const userExists = pg?.pk?.["admin.users"]
25
27
  ? await pg
@@ -27,14 +29,11 @@ export default async function loginTemplate(req, reply) {
27
29
  .then((el) => el.rows?.[0]?.uid)
28
30
  : false;
29
31
  if (!userExists && config.pg) {
30
- return reply.status(400).send("user not found in db");
32
+ return reply.status(404).send({ error: "user not found in db", code: 400 });
31
33
  }
32
- const twoFactorPagePath = path.join(dirname, "../../../../../server/templates/page/2factor.html");
33
34
  const customBody = await getTemplate("page", "2factor");
34
35
  const body = customBody || (await readFile(twoFactorPagePath, "utf8"));
35
- const { enabled, secret } = config.pg
36
- ? await getSecret({ pg, TYPE: "TOTP", uid })
37
- : {};
36
+ const { enabled, secret } = config.pg ? await getSecret({ pg, uid }) : {};
38
37
  const { otp, recoveryCodes, key } = secret && pg?.pk?.["admin.users_social_auth"]
39
38
  ? await pg
40
39
  .query(`select social_auth_obj->'codesArray' as "recoveryCodes", enabled, social_auth_url as otp
@@ -44,7 +43,6 @@ export default async function loginTemplate(req, reply) {
44
43
  /* -- access recovery start */
45
44
  // user already authorized via euSign / social / login
46
45
  if (uid && !req.session?.secondFactorPassed && req.query?.recovery) {
47
- const defaultPagePath = path.join(dirname, "../../../../../server/templates/page/2factor-recovery.html");
48
46
  const customBodyRecovery = await getTemplate("page", "2factor-recovery");
49
47
  const bodyRecovery = customBodyRecovery || (await readFile(defaultPagePath, "utf8"));
50
48
  const html = await handlebars.compile(bodyRecovery)({
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/routes/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAkC1C,iBAAS,MAAM,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,GAAE,GAAQ,QA8ElD;AAED,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/routes/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAmC1C,iBAAS,MAAM,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,GAAE,GAAQ,QAiFlD;AAED,eAAe,MAAM,CAAC"}
@@ -12,6 +12,7 @@ import updateUserInfo from "./controllers/core/updateUserInfo.js";
12
12
  // 2factor / totp
13
13
  import verify from "./controllers/2factor/verify.js";
14
14
  import recovery from "./controllers/2factor/recovery.js";
15
+ import reset from "./controllers/2factor/reset.js";
15
16
  // pages
16
17
  import loginTemplate from "./controllers/page/loginTemplate.js";
17
18
  import login2faTemplate from "./controllers/page/login2faTemplate.js";
@@ -49,6 +50,9 @@ function plugin(app, opt = {}) {
49
50
  if (!app.hasRoute({ method: "GET", url: "/2factor/recovery" })) {
50
51
  app.get("/2factor/recovery", params, recovery);
51
52
  }
53
+ if (!app.hasRoute({ method: "GET", url: "/2factor/reset" })) {
54
+ app.get("/2factor/reset", { config: { role: "admin" } }, reset);
55
+ }
52
56
  // get/edit user info
53
57
  if (!app.hasRoute({ method: "GET", url: "/user" })) {
54
58
  app.get("/user", params, getUserInfo);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "2.0.88",
3
+ "version": "2.0.90",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "keywords": [
@@ -25,6 +25,7 @@
25
25
  "clean": "tsc -b --clean",
26
26
  "build": "tsc -b --clean && tsc && copyfiles server/plugins/grpc/utils/*.proto dist && copyfiles server/migrations/*.sql dist && copyfiles server/templates/**/*.html dist && copyfiles server/templates/**/*.hbs dist",
27
27
  "prod": "NODE_ENV=production bun dist/server",
28
+ "migrate": "MIGRATE=true bun script/migrate",
28
29
  "patch": "npm version patch && git push && npm publish",
29
30
  "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
30
31
  "test": "bun test",