feat(backend): implemented centralized RBAC checks
This commit is contained in:
parent
080986294b
commit
19a81a0aed
10 changed files with 77 additions and 39 deletions
|
@ -105,3 +105,22 @@ model Set {
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model CuratedSet {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
img String?
|
||||||
|
nameDe String @default("")
|
||||||
|
nameEn String @default("")
|
||||||
|
nameEs String @default("")
|
||||||
|
nameFr String @default("")
|
||||||
|
nameNl String @default("")
|
||||||
|
notesDe String @default("")
|
||||||
|
notesEn String @default("")
|
||||||
|
notesEs String @default("")
|
||||||
|
notesFr String @default("")
|
||||||
|
notesNl String @default("")
|
||||||
|
measies String @default("{}")
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ const baseConfig = {
|
||||||
// Config
|
// Config
|
||||||
api,
|
api,
|
||||||
apikeys: {
|
apikeys: {
|
||||||
levels: [0, 1, 2, 3, 4, 5, 6, 7, 8],
|
levels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||||
expiryMaxSeconds: 365 * 24 * 3600,
|
expiryMaxSeconds: 365 * 24 * 3600,
|
||||||
},
|
},
|
||||||
avatars: {
|
avatars: {
|
||||||
|
@ -73,10 +73,15 @@ const baseConfig = {
|
||||||
port,
|
port,
|
||||||
roles: {
|
roles: {
|
||||||
levels: {
|
levels: {
|
||||||
|
readNone: 0,
|
||||||
|
readSome: 1,
|
||||||
|
readOnly: 2,
|
||||||
|
writeSome: 3,
|
||||||
user: 4,
|
user: 4,
|
||||||
bughunter: 5,
|
curator: 5,
|
||||||
support: 7,
|
bughunter: 6,
|
||||||
admin: 8,
|
support: 8,
|
||||||
|
admin: 9,
|
||||||
},
|
},
|
||||||
base: 'user',
|
base: 'user',
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,6 +15,8 @@ import { loadExpressMiddleware, loadPassportMiddleware } from './middleware.mjs'
|
||||||
import { encryption } from './utils/crypto.mjs'
|
import { encryption } from './utils/crypto.mjs'
|
||||||
// Multi-Factor Authentication (MFA)
|
// Multi-Factor Authentication (MFA)
|
||||||
import { mfa } from './utils/mfa.mjs'
|
import { mfa } from './utils/mfa.mjs'
|
||||||
|
// Role-Based Access Control (RBAC)
|
||||||
|
import { rbac } from './utils/rbac.mjs'
|
||||||
// Email
|
// Email
|
||||||
import { mailer } from './utils/email.mjs'
|
import { mailer } from './utils/email.mjs'
|
||||||
// Swagger
|
// Swagger
|
||||||
|
@ -36,6 +38,7 @@ const tools = {
|
||||||
...encryption(config.encryption.key),
|
...encryption(config.encryption.key),
|
||||||
...mfa(config.mfa),
|
...mfa(config.mfa),
|
||||||
...mailer(config),
|
...mailer(config),
|
||||||
|
...rbac(config.roles),
|
||||||
config,
|
config,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,6 @@ import http from 'passport-http'
|
||||||
import jwt from 'passport-jwt'
|
import jwt from 'passport-jwt'
|
||||||
import { ApikeyModel } from './models/apikey.mjs'
|
import { ApikeyModel } from './models/apikey.mjs'
|
||||||
|
|
||||||
const levelFromRole = (role) => {
|
|
||||||
if (role === 'user') return 4
|
|
||||||
if (role === 'bughunter') return 5
|
|
||||||
if (role === 'support') return 6
|
|
||||||
if (role === 'admin') return 8
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadExpressMiddleware(app) {
|
function loadExpressMiddleware(app) {
|
||||||
app.use(cors())
|
app.use(cors())
|
||||||
}
|
}
|
||||||
|
@ -36,7 +27,7 @@ function loadPassportMiddleware(passport, tools) {
|
||||||
return done(null, {
|
return done(null, {
|
||||||
...jwt_payload,
|
...jwt_payload,
|
||||||
uid: jwt_payload._id,
|
uid: jwt_payload._id,
|
||||||
level: levelFromRole(jwt_payload.role),
|
level: tools.config.roles.levels[jwt_payload.role] || 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { UserModel } from './user.mjs'
|
||||||
export function ApikeyModel(tools) {
|
export function ApikeyModel(tools) {
|
||||||
this.config = tools.config
|
this.config = tools.config
|
||||||
this.prisma = tools.prisma
|
this.prisma = tools.prisma
|
||||||
|
this.rbac = tools.rbac
|
||||||
this.User = new UserModel(tools)
|
this.User = new UserModel(tools)
|
||||||
|
|
||||||
return this
|
return this
|
||||||
|
@ -48,7 +49,7 @@ ApikeyModel.prototype.verify = async function (key, secret) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ApikeyModel.prototype.guardedRead = async function ({ params, user }) {
|
ApikeyModel.prototype.guardedRead = async function ({ params, user }) {
|
||||||
if (user.level < 1) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.readSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
|
|
||||||
await this.unguardedRead({ id: params.id })
|
await this.unguardedRead({ id: params.id })
|
||||||
|
@ -56,7 +57,7 @@ ApikeyModel.prototype.guardedRead = async function ({ params, user }) {
|
||||||
|
|
||||||
if (this.record.userId !== user.uid) {
|
if (this.record.userId !== user.uid) {
|
||||||
// Not own key - only admin can do that
|
// Not own key - only admin can do that
|
||||||
if (user.level < 8) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.admin(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.setResponse(200, 'success', {
|
return this.setResponse(200, 'success', {
|
||||||
|
@ -72,7 +73,7 @@ ApikeyModel.prototype.guardedRead = async function ({ params, user }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ApikeyModel.prototype.guardedDelete = async function ({ params, user }) {
|
ApikeyModel.prototype.guardedDelete = async function ({ params, user }) {
|
||||||
if (user.level < 4) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.user(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
|
|
||||||
await this.unguardedRead({ id: params.id })
|
await this.unguardedRead({ id: params.id })
|
||||||
|
@ -80,7 +81,7 @@ ApikeyModel.prototype.guardedDelete = async function ({ params, user }) {
|
||||||
|
|
||||||
if (this.record.userId !== user.uid) {
|
if (this.record.userId !== user.uid) {
|
||||||
// Not own key - only admin can do that
|
// Not own key - only admin can do that
|
||||||
if (user.level < 8) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.admin(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.unguardedDelete()
|
await this.unguardedDelete()
|
||||||
|
|
|
@ -6,6 +6,7 @@ export function PatternModel(tools) {
|
||||||
this.prisma = tools.prisma
|
this.prisma = tools.prisma
|
||||||
this.decrypt = tools.decrypt
|
this.decrypt = tools.decrypt
|
||||||
this.encrypt = tools.encrypt
|
this.encrypt = tools.encrypt
|
||||||
|
this.rbac = tools.rbac
|
||||||
this.encryptedFields = ['data', 'img', 'name', 'notes', 'settings']
|
this.encryptedFields = ['data', 'img', 'name', 'notes', 'settings']
|
||||||
this.clear = {} // For holding decrypted data
|
this.clear = {} // For holding decrypted data
|
||||||
|
|
||||||
|
@ -13,7 +14,7 @@ export function PatternModel(tools) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PatternModel.prototype.guardedCreate = async function ({ body, user }) {
|
PatternModel.prototype.guardedCreate = async function ({ body, user }) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.user(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (Object.keys(body).length < 2) return this.setResponse(400, 'postBodyMissing')
|
if (Object.keys(body).length < 2) return this.setResponse(400, 'postBodyMissing')
|
||||||
if (!body.set) return this.setResponse(400, 'setMissing')
|
if (!body.set) return this.setResponse(400, 'setMissing')
|
||||||
if (typeof body.set !== 'number') return this.setResponse(400, 'setNotNumeric')
|
if (typeof body.set !== 'number') return this.setResponse(400, 'setNotNumeric')
|
||||||
|
@ -95,11 +96,11 @@ PatternModel.prototype.read = async function (where) {
|
||||||
* Stores result in this.record
|
* Stores result in this.record
|
||||||
*/
|
*/
|
||||||
PatternModel.prototype.guardedRead = async function ({ params, user }) {
|
PatternModel.prototype.guardedRead = async function ({ params, user }) {
|
||||||
if (user.level < 1) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.readSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
|
|
||||||
await this.read({ id: parseInt(params.id) })
|
await this.read({ id: parseInt(params.id) })
|
||||||
if (this.record.userId !== user.uid && user.level < 5) {
|
if (this.record.userId !== user.uid && !this.rbac.bughunter(user)) {
|
||||||
return this.setResponse(403, 'insufficientAccessLevel')
|
return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,11 +117,11 @@ PatternModel.prototype.guardedRead = async function ({ params, user }) {
|
||||||
* Stores result in this.record
|
* Stores result in this.record
|
||||||
*/
|
*/
|
||||||
PatternModel.prototype.guardedClone = async function ({ params, user }) {
|
PatternModel.prototype.guardedClone = async function ({ params, user }) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
|
|
||||||
await this.read({ id: parseInt(params.id) })
|
await this.read({ id: parseInt(params.id) })
|
||||||
if (this.record.userId !== user.uid && !this.record.public && user.level < 5) {
|
if (this.record.userId !== user.uid && !this.record.public && !this.rbac.support(support)) {
|
||||||
return this.setResponse(403, 'insufficientAccessLevel')
|
return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,10 +205,10 @@ PatternModel.prototype.unguardedUpdate = async function (data) {
|
||||||
* so we can't be certain it's safe
|
* so we can't be certain it's safe
|
||||||
*/
|
*/
|
||||||
PatternModel.prototype.guardedUpdate = async function ({ params, body, user }) {
|
PatternModel.prototype.guardedUpdate = async function ({ params, body, user }) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
await this.read({ id: parseInt(params.id) })
|
await this.read({ id: parseInt(params.id) })
|
||||||
if (this.record.userId !== user.uid && user.level < 8) {
|
if (this.record.userId !== user.uid && !this.rbac.admin(user)) {
|
||||||
return this.setResponse(403, 'insufficientAccessLevel')
|
return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
}
|
}
|
||||||
const data = {}
|
const data = {}
|
||||||
|
@ -248,11 +249,11 @@ PatternModel.prototype.unguardedDelete = async function () {
|
||||||
* Removes the pattern - Checks permissions
|
* Removes the pattern - Checks permissions
|
||||||
*/
|
*/
|
||||||
PatternModel.prototype.guardedDelete = async function ({ params, user }) {
|
PatternModel.prototype.guardedDelete = async function ({ params, user }) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
if (this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
|
|
||||||
await this.read({ id: parseInt(params.id) })
|
await this.read({ id: parseInt(params.id) })
|
||||||
if (this.record.userId !== user.uid && user.level < 8) {
|
if (this.record.userId !== user.uid && !this.rbac.admin(user)) {
|
||||||
return this.setResponse(403, 'insufficientAccessLevel')
|
return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ export function SetModel(tools) {
|
||||||
this.prisma = tools.prisma
|
this.prisma = tools.prisma
|
||||||
this.decrypt = tools.decrypt
|
this.decrypt = tools.decrypt
|
||||||
this.encrypt = tools.encrypt
|
this.encrypt = tools.encrypt
|
||||||
|
this.rbac = tools.rbac
|
||||||
this.encryptedFields = ['measies', 'img', 'name', 'notes']
|
this.encryptedFields = ['measies', 'img', 'name', 'notes']
|
||||||
this.clear = {} // For holding decrypted data
|
this.clear = {} // For holding decrypted data
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ export function SetModel(tools) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SetModel.prototype.guardedCreate = async function ({ body, user }) {
|
SetModel.prototype.guardedCreate = async function ({ body, user }) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (Object.keys(body).length < 1) return this.setResponse(400, 'postBodyMissing')
|
if (Object.keys(body).length < 1) return this.setResponse(400, 'postBodyMissing')
|
||||||
if (!body.name || typeof body.name !== 'string') return this.setResponse(400, 'nameMissing')
|
if (!body.name || typeof body.name !== 'string') return this.setResponse(400, 'nameMissing')
|
||||||
|
|
||||||
|
@ -86,11 +87,11 @@ SetModel.prototype.read = async function (where) {
|
||||||
* Stores result in this.record
|
* Stores result in this.record
|
||||||
*/
|
*/
|
||||||
SetModel.prototype.guardedRead = async function ({ params, user }) {
|
SetModel.prototype.guardedRead = async function ({ params, user }) {
|
||||||
if (user.level < 1) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.readSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
|
|
||||||
await this.read({ id: parseInt(params.id) })
|
await this.read({ id: parseInt(params.id) })
|
||||||
if (this.record.userId !== user.uid && user.level < 5) {
|
if (this.record.userId !== user.uid && !this.rbac.bughunter(user)) {
|
||||||
return this.setResponse(403, 'insufficientAccessLevel')
|
return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,11 +124,11 @@ SetModel.prototype.publicRead = async function ({ params }) {
|
||||||
* Stores result in this.record
|
* Stores result in this.record
|
||||||
*/
|
*/
|
||||||
SetModel.prototype.guardedClone = async function ({ params, user }) {
|
SetModel.prototype.guardedClone = async function ({ params, user }) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
|
|
||||||
await this.read({ id: parseInt(params.id) })
|
await this.read({ id: parseInt(params.id) })
|
||||||
if (this.record.userId !== user.uid && !this.record.public && user.level < 5) {
|
if (this.record.userId !== user.uid && !this.record.public && !this.rbac.support(user)) {
|
||||||
return this.setResponse(403, 'insufficientAccessLevel')
|
return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,10 +232,10 @@ SetModel.prototype.unguardedUpdate = async function (data) {
|
||||||
* so we can't be certain it's safe
|
* so we can't be certain it's safe
|
||||||
*/
|
*/
|
||||||
SetModel.prototype.guardedUpdate = async function ({ params, body, user }) {
|
SetModel.prototype.guardedUpdate = async function ({ params, body, user }) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
await this.read({ id: parseInt(params.id) })
|
await this.read({ id: parseInt(params.id) })
|
||||||
if (this.record.userId !== user.uid && user.level < 8) {
|
if (this.record.userId !== user.uid && !this.rbac.admin(user)) {
|
||||||
return this.setResponse(403, 'insufficientAccessLevel')
|
return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
}
|
}
|
||||||
const data = {}
|
const data = {}
|
||||||
|
@ -287,11 +288,11 @@ SetModel.prototype.unguardedDelete = async function () {
|
||||||
* Removes the set - Checks permissions
|
* Removes the set - Checks permissions
|
||||||
*/
|
*/
|
||||||
SetModel.prototype.guardedDelete = async function ({ params, user }) {
|
SetModel.prototype.guardedDelete = async function ({ params, user }) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
|
|
||||||
await this.read({ id: parseInt(params.id) })
|
await this.read({ id: parseInt(params.id) })
|
||||||
if (this.record.userId !== user.uid && user.level < 8) {
|
if (this.record.userId !== user.uid && !this.rbac.admin(user)) {
|
||||||
return this.setResponse(403, 'insufficientAccessLevel')
|
return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ export function UserModel(tools) {
|
||||||
this.decrypt = tools.decrypt
|
this.decrypt = tools.decrypt
|
||||||
this.encrypt = tools.encrypt
|
this.encrypt = tools.encrypt
|
||||||
this.mfa = tools.mfa
|
this.mfa = tools.mfa
|
||||||
|
this.rbac = tools.rbac
|
||||||
this.mailer = tools.email
|
this.mailer = tools.email
|
||||||
this.Confirmation = new ConfirmationModel(tools)
|
this.Confirmation = new ConfirmationModel(tools)
|
||||||
this.encryptedFields = ['bio', 'github', 'email', 'initial', 'img', 'mfaSecret']
|
this.encryptedFields = ['bio', 'github', 'email', 'initial', 'img', 'mfaSecret']
|
||||||
|
@ -69,7 +70,7 @@ UserModel.prototype.cloak = function (data) {
|
||||||
* Stores result in this.record
|
* Stores result in this.record
|
||||||
*/
|
*/
|
||||||
UserModel.prototype.guardedRead = async function (where, { user }) {
|
UserModel.prototype.guardedRead = async function (where, { user }) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.readSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
await this.read(where)
|
await this.read(where)
|
||||||
|
|
||||||
|
@ -481,7 +482,7 @@ UserModel.prototype.unguardedUpdate = async function (data) {
|
||||||
* so we can't be certain it's safe
|
* so we can't be certain it's safe
|
||||||
*/
|
*/
|
||||||
UserModel.prototype.guardedUpdate = async function ({ body, user }) {
|
UserModel.prototype.guardedUpdate = async function ({ body, user }) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
const data = {}
|
const data = {}
|
||||||
// Bio
|
// Bio
|
||||||
|
@ -600,7 +601,7 @@ UserModel.prototype.guardedUpdate = async function ({ body, user }) {
|
||||||
* user-provided data so we can't be certain it's safe
|
* user-provided data so we can't be certain it's safe
|
||||||
*/
|
*/
|
||||||
UserModel.prototype.guardedMfaUpdate = async function ({ body, user, ip }) {
|
UserModel.prototype.guardedMfaUpdate = async function ({ body, user, ip }) {
|
||||||
if (user.level < 4) return this.setResponse(403, 'insufficientAccessLevel')
|
if (!this.rbac.user(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
if (body.mfa === true && this.record.mfaEnabled === true)
|
if (body.mfa === true && this.record.mfaEnabled === true)
|
||||||
return this.setResponse(400, 'mfaActive')
|
return this.setResponse(400, 'mfaActive')
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { usersRoutes } from './users.mjs'
|
||||||
import { setsRoutes } from './sets.mjs'
|
import { setsRoutes } from './sets.mjs'
|
||||||
import { patternsRoutes } from './patterns.mjs'
|
import { patternsRoutes } from './patterns.mjs'
|
||||||
import { confirmationsRoutes } from './confirmations.mjs'
|
import { confirmationsRoutes } from './confirmations.mjs'
|
||||||
|
//import { curatedSetsRoutes } from './curated-sets.mjs'
|
||||||
|
|
||||||
export const routes = {
|
export const routes = {
|
||||||
apikeysRoutes,
|
apikeysRoutes,
|
||||||
|
@ -10,4 +11,5 @@ export const routes = {
|
||||||
setsRoutes,
|
setsRoutes,
|
||||||
patternsRoutes,
|
patternsRoutes,
|
||||||
confirmationsRoutes,
|
confirmationsRoutes,
|
||||||
|
//curatedSetsRoutes,
|
||||||
}
|
}
|
||||||
|
|
14
sites/backend/src/utils/rbac.mjs
Normal file
14
sites/backend/src/utils/rbac.mjs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Exporting this closure that makes sure we have access to the
|
||||||
|
* instantiated config
|
||||||
|
*/
|
||||||
|
export const rbac = ({ levels }) => {
|
||||||
|
const rbacMethods = {}
|
||||||
|
for (const [name, level] of Object.entries(levels)) {
|
||||||
|
rbacMethods[name] = (user) => user.level >= level
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
rbac: rbacMethods,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue