mirror of
https://github.com/directus/directus.git
synced 2026-01-26 14:07:56 -05:00
688
api/package-lock.json
generated
688
api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -159,9 +159,9 @@ router.post('/me/tfa/enable/', asyncHandler(async (req, res) => {
|
||||
}
|
||||
|
||||
const service = new UsersService({ accountability: req.accountability });
|
||||
const url = await service.enableTFA(req.accountability.user);
|
||||
const { url, secret } = await service.enableTFA(req.accountability.user);
|
||||
|
||||
return res.json({ data: { otpauth_url: url }});
|
||||
return res.json({ data: { secret, otpauth_url: url }});
|
||||
}));
|
||||
|
||||
router.post('/me/tfa/disable', asyncHandler(async (req, res) => {
|
||||
|
||||
@@ -1239,7 +1239,9 @@ rows:
|
||||
width: half
|
||||
- collection: directus_users
|
||||
field: tfa_secret
|
||||
interface: tfa-setup
|
||||
locked: true
|
||||
special: conceal
|
||||
sort: 14
|
||||
width: half
|
||||
- collection: directus_users
|
||||
|
||||
@@ -5,7 +5,7 @@ import { sendInviteMail } from '../mail';
|
||||
import database from '../database';
|
||||
import argon2 from 'argon2';
|
||||
import { InvalidPayloadException } from '../exceptions';
|
||||
import { Accountability, Query, Item, AbstractServiceOptions } from '../types';
|
||||
import { Accountability, PrimaryKey, Item, AbstractServiceOptions } from '../types';
|
||||
import Knex from 'knex';
|
||||
import env from '../env';
|
||||
|
||||
@@ -22,6 +22,29 @@ export default class UsersService extends ItemsService {
|
||||
this.service = new ItemsService('directus_users', options);
|
||||
}
|
||||
|
||||
update(data: Partial<Item>, keys: PrimaryKey[]): Promise<PrimaryKey[]>;
|
||||
update(data: Partial<Item>, key: PrimaryKey): Promise<PrimaryKey>;
|
||||
update(data: Partial<Item>[]): Promise<PrimaryKey[]>;
|
||||
async update(
|
||||
data: Partial<Item> | Partial<Item>[],
|
||||
key?: PrimaryKey | PrimaryKey[]
|
||||
): Promise<PrimaryKey | PrimaryKey[]> {
|
||||
/**
|
||||
* @NOTE
|
||||
* This is just an extra bit of hardcoded security. We don't want anybody to be able to disable 2fa through
|
||||
* the regular /users endpoint. Period. You should only be able to manage the 2fa status through the /tfa endpoint.
|
||||
*/
|
||||
const payloads = Array.isArray(data) ? data : [data];
|
||||
|
||||
for (const payload of payloads) {
|
||||
if (payload.hasOwnProperty('tfa_secret')) {
|
||||
throw new InvalidPayloadException(`You can't change the tfa_secret manually.`);
|
||||
}
|
||||
}
|
||||
|
||||
return this.service.update(data, key as any);
|
||||
}
|
||||
|
||||
async inviteUser(email: string, role: string) {
|
||||
await this.service.create({ email, role, status: 'invited' });
|
||||
|
||||
@@ -64,7 +87,10 @@ export default class UsersService extends ItemsService {
|
||||
|
||||
await this.knex('directus_users').update({ tfa_secret: secret }).where({ id: pk });
|
||||
|
||||
return await authService.generateOTPAuthURL(pk, secret);
|
||||
return {
|
||||
secret,
|
||||
url: await authService.generateOTPAuthURL(pk, secret),
|
||||
};
|
||||
}
|
||||
|
||||
async disableTFA(pk: string) {
|
||||
|
||||
143
app/package-lock.json
generated
143
app/package-lock.json
generated
@@ -2730,6 +2730,15 @@
|
||||
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/qrcode": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.3.5.tgz",
|
||||
"integrity": "sha512-92QMnMb9m0ErBU20za5Eqtf4lzUcSkk5w/Cz30q5qod0lWHm2loztmFs2EnCY06yT51GY1+m/oFq2D8qVK2Bjg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/reach__router": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.5.tgz",
|
||||
@@ -5966,8 +5975,7 @@
|
||||
"base64-js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||
},
|
||||
"batch": {
|
||||
"version": "0.6.1",
|
||||
@@ -6371,11 +6379,29 @@
|
||||
"isarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"buffer-alloc": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
|
||||
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
|
||||
"requires": {
|
||||
"buffer-alloc-unsafe": "^1.1.0",
|
||||
"buffer-fill": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"buffer-alloc-unsafe": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
|
||||
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
|
||||
},
|
||||
"buffer-fill": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
||||
"integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
},
|
||||
"buffer-indexof": {
|
||||
"version": "1.1.1",
|
||||
@@ -6582,8 +6608,7 @@
|
||||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
|
||||
},
|
||||
"camelcase-keys": {
|
||||
"version": "6.2.2",
|
||||
@@ -7173,7 +7198,6 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
|
||||
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^3.1.0",
|
||||
"strip-ansi": "^5.2.0",
|
||||
@@ -7183,20 +7207,17 @@
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
@@ -7207,7 +7228,6 @@
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
@@ -8334,8 +8354,7 @@
|
||||
"decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
|
||||
"dev": true
|
||||
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
|
||||
},
|
||||
"decamelize-keys": {
|
||||
"version": "1.1.0",
|
||||
@@ -8746,6 +8765,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dijkstrajs": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz",
|
||||
"integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs="
|
||||
},
|
||||
"dir-glob": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz",
|
||||
@@ -9095,8 +9119,7 @@
|
||||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
|
||||
},
|
||||
"emojis-list": {
|
||||
"version": "3.0.0",
|
||||
@@ -10711,8 +10734,7 @@
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
|
||||
},
|
||||
"get-own-enumerable-property-symbols": {
|
||||
"version": "3.0.2",
|
||||
@@ -11700,8 +11722,7 @@
|
||||
"ieee754": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||
},
|
||||
"iferr": {
|
||||
"version": "0.1.5",
|
||||
@@ -14796,7 +14817,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
||||
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^3.0.0",
|
||||
"path-exists": "^3.0.0"
|
||||
@@ -16488,7 +16508,6 @@
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-try": "^2.0.0"
|
||||
}
|
||||
@@ -16497,7 +16516,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
|
||||
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.0.0"
|
||||
}
|
||||
@@ -16529,8 +16547,7 @@
|
||||
"p-try": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
|
||||
},
|
||||
"package-json": {
|
||||
"version": "6.5.0",
|
||||
@@ -16720,8 +16737,7 @@
|
||||
"path-exists": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
|
||||
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
|
||||
"dev": true
|
||||
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
@@ -16906,6 +16922,11 @@
|
||||
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
|
||||
"dev": true
|
||||
},
|
||||
"pngjs": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
|
||||
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="
|
||||
},
|
||||
"pnp-webpack-plugin": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.5.0.tgz",
|
||||
@@ -18171,6 +18192,36 @@
|
||||
"integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
|
||||
"dev": true
|
||||
},
|
||||
"qrcode": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz",
|
||||
"integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==",
|
||||
"requires": {
|
||||
"buffer": "^5.4.3",
|
||||
"buffer-alloc": "^1.2.0",
|
||||
"buffer-from": "^1.1.1",
|
||||
"dijkstrajs": "^1.0.1",
|
||||
"isarray": "^2.0.1",
|
||||
"pngjs": "^3.3.0",
|
||||
"yargs": "^13.2.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
|
||||
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
|
||||
"requires": {
|
||||
"base64-js": "^1.0.2",
|
||||
"ieee754": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
|
||||
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz",
|
||||
@@ -19250,14 +19301,12 @@
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
|
||||
"dev": true
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
|
||||
},
|
||||
"require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
@@ -19802,8 +19851,7 @@
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
|
||||
"dev": true
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
|
||||
},
|
||||
"set-value": {
|
||||
"version": "2.0.1",
|
||||
@@ -23965,8 +24013,7 @@
|
||||
"which-module": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
|
||||
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
|
||||
"dev": true
|
||||
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
|
||||
},
|
||||
"which-pm-runs": {
|
||||
"version": "1.0.0",
|
||||
@@ -24066,7 +24113,6 @@
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
|
||||
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.0",
|
||||
"string-width": "^3.0.0",
|
||||
@@ -24076,14 +24122,12 @@
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
@@ -24091,14 +24135,12 @@
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
@@ -24109,7 +24151,6 @@
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
@@ -24197,8 +24238,7 @@
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
@@ -24216,7 +24256,6 @@
|
||||
"version": "13.3.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
|
||||
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^5.0.0",
|
||||
"find-up": "^3.0.0",
|
||||
@@ -24233,14 +24272,12 @@
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
|
||||
},
|
||||
"find-up": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^3.0.0"
|
||||
}
|
||||
@@ -24248,14 +24285,12 @@
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
@@ -24266,7 +24301,6 @@
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
@@ -24277,7 +24311,6 @@
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
|
||||
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"nanoid": "^3.1.10",
|
||||
"pinia": "^0.0.7",
|
||||
"portal-vue": "^2.1.7",
|
||||
"qrcode": "^1.4.4",
|
||||
"resize-observer": "^1.0.0",
|
||||
"semver": "^7.3.2",
|
||||
"stylelint-config-prettier": "^8.0.2",
|
||||
@@ -90,6 +91,7 @@
|
||||
"@types/jest": "^26.0.5",
|
||||
"@types/marked": "^1.1.0",
|
||||
"@types/mime-types": "^2.1.0",
|
||||
"@types/qrcode": "^1.3.5",
|
||||
"@types/semver": "^7.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.34.0",
|
||||
"@typescript-eslint/parser": "^2.34.0",
|
||||
|
||||
@@ -107,7 +107,7 @@ export const htmlLabel = () => ({
|
||||
},
|
||||
template: `
|
||||
<v-checkbox v-model="checked" @change="onChange">
|
||||
<template #label>
|
||||
<template>
|
||||
Any <i>custom</i> markup in here
|
||||
</template>
|
||||
</v-checkbox>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="prepend" v-if="$scopedSlots.prepend"><slot name="prepend" /></div>
|
||||
<v-icon class="checkbox" :name="icon" @click.stop="toggleInput" />
|
||||
<span class="label type-text">
|
||||
<slot name="label" v-if="customValue === false">{{ label }}</slot>
|
||||
<slot v-if="customValue === false">{{ label }}</slot>
|
||||
<input @click.stop class="custom-input" v-else v-model="_value" />
|
||||
</span>
|
||||
<div class="append" v-if="$scopedSlots.append"><slot name="append" /></div>
|
||||
@@ -132,6 +132,7 @@ body {
|
||||
appearance: none;
|
||||
|
||||
.label:not(:empty) {
|
||||
flex-grow: 1;
|
||||
margin-left: 8px;
|
||||
transition: color var(--fast) var(--transition);
|
||||
|
||||
|
||||
@@ -138,6 +138,7 @@ export default defineComponent({
|
||||
return { _listeners, hasClick, stepUp, stepDown, input };
|
||||
|
||||
function processValue(event: KeyboardEvent) {
|
||||
if (!event.key) return;
|
||||
const key = event.key.toLowerCase();
|
||||
const systemKeys = ['meta', 'shift', 'alt', 'backspace', 'tab'];
|
||||
const value = (event.target as HTMLInputElement).value;
|
||||
|
||||
@@ -90,7 +90,7 @@ export const htmlLabel = () => ({
|
||||
},
|
||||
template: `
|
||||
<v-switch v-model="checked" @change="onChange">
|
||||
<template #label>
|
||||
<template>
|
||||
Any <i>custom</i> markup in here
|
||||
</template>
|
||||
</v-switch>
|
||||
|
||||
@@ -23,6 +23,7 @@ import InterfaceSlider from './slider/';
|
||||
import InterfaceSlug from './slug';
|
||||
import InterfaceStatus from './status';
|
||||
import InterfaceTags from './tags';
|
||||
import InterfaceTFASetup from './tfa-setup';
|
||||
import InterfaceTextarea from './textarea/';
|
||||
import InterfaceTextInput from './text-input/';
|
||||
import InterfaceToggle from './toggle/';
|
||||
@@ -56,6 +57,7 @@ export const interfaces = [
|
||||
InterfaceSlug,
|
||||
InterfaceStatus,
|
||||
InterfaceTags,
|
||||
InterfaceTFASetup,
|
||||
InterfaceTextarea,
|
||||
InterfaceTextInput,
|
||||
InterfaceToggle,
|
||||
|
||||
12
app/src/interfaces/tfa-setup/index.ts
Normal file
12
app/src/interfaces/tfa-setup/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import InterfaceTFASetup from './tfa-setup.vue';
|
||||
import { defineInterface } from '@/interfaces/define';
|
||||
|
||||
export default defineInterface({
|
||||
id: 'tfa-setup',
|
||||
name: 'tfa-setup',
|
||||
icon: 'box',
|
||||
component: InterfaceTFASetup,
|
||||
types: ['text'],
|
||||
options: [],
|
||||
system: true,
|
||||
});
|
||||
148
app/src/interfaces/tfa-setup/tfa-setup.vue
Normal file
148
app/src/interfaces/tfa-setup/tfa-setup.vue
Normal file
@@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-checkbox block :input-value="!!tfaEnabled" @click.native="toggle">
|
||||
{{ $t('enabled') }}
|
||||
<div class="spacer" />
|
||||
<template #append>
|
||||
<v-icon name="launch" class="checkbox-icon" :class="{ enabled: tfaEnabled }" />
|
||||
</template>
|
||||
</v-checkbox>
|
||||
|
||||
<v-dialog persistent v-model="enableActive">
|
||||
<v-card>
|
||||
<v-progress-circular class="loader" indeterminate v-if="loading === true" />
|
||||
<template v-show="loading === false">
|
||||
<v-card-title>
|
||||
{{ $t('tfa_scan_code') }}
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<canvas class="qr" :id="canvasID" />
|
||||
<output class="secret">{{ secret }}</output>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button @click="enableActive = false">{{ $t('done') }}</v-button>
|
||||
</v-card-actions>
|
||||
</template>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog persistent v-model="disableActive">
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
{{ $t('enter_otp_to_disable_tfa') }}
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-input type="text" :placeholder="$t('otp')" v-model="otp" />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button class="disable" :loading="loading" @click="disableTFA" :disabled="otp.length !== 6">
|
||||
{{ $t('disable_tfa') }}
|
||||
</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch } from '@vue/composition-api';
|
||||
import api from '@/api';
|
||||
import qrcode from 'qrcode';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const tfaEnabled = ref(!!props.value);
|
||||
const enableActive = ref(false);
|
||||
const disableActive = ref(false);
|
||||
const loading = ref(false);
|
||||
const canvasID = nanoid();
|
||||
const secret = ref<string>();
|
||||
const otp = ref('');
|
||||
const error = ref<any>();
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
() => {
|
||||
tfaEnabled.value = !!props.value;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return { tfaEnabled, toggle, enableActive, disableActive, loading, canvasID, secret, disableTFA, otp, error };
|
||||
|
||||
function toggle() {
|
||||
if (tfaEnabled.value === false) {
|
||||
enableActive.value = true;
|
||||
enableTFA();
|
||||
} else {
|
||||
disableActive.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
async function enableTFA() {
|
||||
loading.value = true;
|
||||
const response = await api.post('/users/me/tfa/enable');
|
||||
const url = response.data.data.otpauth_url;
|
||||
secret.value = response.data.data.secret;
|
||||
await qrcode.toCanvas(document.getElementById(canvasID), url);
|
||||
loading.value = false;
|
||||
tfaEnabled.value = true;
|
||||
}
|
||||
|
||||
async function disableTFA() {
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
await api.post('/users/me/tfa/disable', { otp: otp.value });
|
||||
|
||||
tfaEnabled.value = false;
|
||||
disableActive.value = false;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.checkbox-icon {
|
||||
--v-icon-color: var(--foreground-subdued);
|
||||
|
||||
&.enabled {
|
||||
--v-icon-color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
.loader {
|
||||
margin: 100px auto;
|
||||
}
|
||||
|
||||
.qr {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.secret {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
color: var(--foreground-subdued);
|
||||
font-family: var(--family-monospace);
|
||||
letter-spacing: 2.6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.disable {
|
||||
--v-button-background-color: var(--warning);
|
||||
--v-button-background-color-hover: var(--warning-125);
|
||||
}
|
||||
</style>
|
||||
@@ -12,6 +12,7 @@ export type InterfaceConfig = {
|
||||
relationship?: null | 'm2o' | 'o2m' | 'm2m';
|
||||
hideLabel?: boolean;
|
||||
hideLoader?: boolean;
|
||||
system?: boolean;
|
||||
};
|
||||
|
||||
export type InterfaceContext = { i18n: VueI18n };
|
||||
|
||||
@@ -422,6 +422,11 @@
|
||||
|
||||
"enabled": "Enabled",
|
||||
|
||||
"enable_tfa": "Enable 2FA",
|
||||
"disable_tfa": "Disable 2FA",
|
||||
"tfa_scan_code": "Scan the code in your authenticator app to finish setting up 2FA",
|
||||
"enter_otp_to_disable_tfa": "Enter the OTP to disable 2FA",
|
||||
|
||||
"use_bold_style": "Use bold style",
|
||||
|
||||
"formatted_value": "Formatted Value",
|
||||
|
||||
@@ -43,6 +43,9 @@ export default defineComponent({
|
||||
setup(props) {
|
||||
const availableInterfaces = computed(() =>
|
||||
interfaces.filter((inter) => {
|
||||
// Filter out all system interfaces
|
||||
if (inter.system !== undefined && inter.system === true) return false;
|
||||
|
||||
const matchesType = inter.types.includes(state.fieldData?.type || 'alias');
|
||||
let matchesRelation = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user