Changing the role system to have permissions integrated rather than the permissions being per user.
This commit is contained in:
parent
fa8d8fcfea
commit
65302e1dd9
|
@ -34,7 +34,6 @@ exports.authenticate = async ctx => {
|
||||||
userId: dbUser._id,
|
userId: dbUser._id,
|
||||||
roleId: dbUser.roleId,
|
roleId: dbUser.roleId,
|
||||||
version: app.version,
|
version: app.version,
|
||||||
permissions: dbUser.permissions || [],
|
|
||||||
}
|
}
|
||||||
// if in cloud add the user api key
|
// if in cloud add the user api key
|
||||||
if (env.CLOUD) {
|
if (env.CLOUD) {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
const { BUILTIN_PERMISSIONS } = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
|
exports.fetch = async function(ctx) {
|
||||||
|
// TODO: need to build out custom permissions
|
||||||
|
ctx.body = Object.values(BUILTIN_PERMISSIONS)
|
||||||
|
}
|
|
@ -25,9 +25,13 @@ exports.find = async function(ctx) {
|
||||||
|
|
||||||
exports.save = async function(ctx) {
|
exports.save = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
|
let { _id, name, inherits, permissionId } = ctx.request.body
|
||||||
let id = ctx.request.body._id || generateRoleID()
|
if (!_id) {
|
||||||
const role = new Role(id, ctx.request.body.name, ctx.request.body.inherits)
|
_id = generateRoleID()
|
||||||
|
}
|
||||||
|
const role = new Role(_id, name)
|
||||||
|
.addPermission(permissionId)
|
||||||
|
.addInheritance(inherits)
|
||||||
if (ctx.request.body._rev) {
|
if (ctx.request.body._rev) {
|
||||||
role._rev = ctx.request.body._rev
|
role._rev = ctx.request.body._rev
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const bcrypt = require("../../utilities/bcrypt")
|
const bcrypt = require("../../utilities/bcrypt")
|
||||||
const { generateUserID, getUserParams, ViewNames } = require("../../db/utils")
|
const { generateUserID, getUserParams, ViewNames } = require("../../db/utils")
|
||||||
const { BUILTIN_ROLE_ID_ARRAY } = require("../../utilities/security/roles")
|
const { getRole } = require("../../utilities/security/roles")
|
||||||
const {
|
|
||||||
BUILTIN_PERMISSION_NAMES,
|
|
||||||
} = require("../../utilities/security/permissions")
|
|
||||||
|
|
||||||
exports.fetch = async function(ctx) {
|
exports.fetch = async function(ctx) {
|
||||||
const database = new CouchDB(ctx.user.appId)
|
const database = new CouchDB(ctx.user.appId)
|
||||||
|
@ -18,13 +15,13 @@ exports.fetch = async function(ctx) {
|
||||||
|
|
||||||
exports.create = async function(ctx) {
|
exports.create = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
const { username, password, name, roleId, permissions } = ctx.request.body
|
const { username, password, name, roleId } = ctx.request.body
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
ctx.throw(400, "Username and Password Required.")
|
ctx.throw(400, "Username and Password Required.")
|
||||||
}
|
}
|
||||||
|
|
||||||
const role = await checkRole(db, roleId)
|
const role = await getRole(ctx.user.appId, roleId)
|
||||||
|
|
||||||
if (!role) ctx.throw(400, "Invalid Role")
|
if (!role) ctx.throw(400, "Invalid Role")
|
||||||
|
|
||||||
|
@ -35,7 +32,6 @@ exports.create = async function(ctx) {
|
||||||
name: name || username,
|
name: name || username,
|
||||||
type: "user",
|
type: "user",
|
||||||
roleId,
|
roleId,
|
||||||
permissions: permissions || [BUILTIN_PERMISSION_NAMES.POWER],
|
|
||||||
tableId: ViewNames.USERS,
|
tableId: ViewNames.USERS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,15 +84,3 @@ exports.find = async function(ctx) {
|
||||||
_rev: user._rev,
|
_rev: user._rev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkRole = async (db, roleId) => {
|
|
||||||
if (!roleId) return
|
|
||||||
if (BUILTIN_ROLE_ID_ARRAY.indexOf(roleId) !== -1) {
|
|
||||||
return {
|
|
||||||
_id: roleId,
|
|
||||||
name: roleId,
|
|
||||||
permissions: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return await db.get(roleId)
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ const apiKeysRoutes = require("./apikeys")
|
||||||
const templatesRoutes = require("./templates")
|
const templatesRoutes = require("./templates")
|
||||||
const analyticsRoutes = require("./analytics")
|
const analyticsRoutes = require("./analytics")
|
||||||
const routingRoutes = require("./routing")
|
const routingRoutes = require("./routing")
|
||||||
|
const permissionRoutes = require("./permission")
|
||||||
|
|
||||||
exports.mainRoutes = [
|
exports.mainRoutes = [
|
||||||
deployRoutes,
|
deployRoutes,
|
||||||
|
@ -32,6 +33,7 @@ exports.mainRoutes = [
|
||||||
analyticsRoutes,
|
analyticsRoutes,
|
||||||
webhookRoutes,
|
webhookRoutes,
|
||||||
routingRoutes,
|
routingRoutes,
|
||||||
|
permissionRoutes,
|
||||||
// these need to be handled last as they still use /api/:tableId
|
// these need to be handled last as they still use /api/:tableId
|
||||||
// this could be breaking as koa may recognise other routes as this
|
// this could be breaking as koa may recognise other routes as this
|
||||||
tableRoutes,
|
tableRoutes,
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
const Router = require("@koa/router")
|
||||||
|
const controller = require("../controllers/permission")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
|
const router = Router()
|
||||||
|
|
||||||
|
router.get("/api/permissions", authorized(BUILDER), controller.fetch)
|
||||||
|
|
||||||
|
module.exports = router
|
|
@ -2,11 +2,27 @@ const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/role")
|
const controller = require("../controllers/role")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER } = require("../../utilities/security/permissions")
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
const Joi = require("joi")
|
||||||
|
const joiValidator = require("../../middleware/joi-validator")
|
||||||
|
const {
|
||||||
|
BUILTIN_PERMISSION_IDS,
|
||||||
|
} = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
|
function generateValidator() {
|
||||||
|
// prettier-ignore
|
||||||
|
return joiValidator.body(Joi.object({
|
||||||
|
_id: Joi.string().optional(),
|
||||||
|
_rev: Joi.string().optional(),
|
||||||
|
name: Joi.string().required(),
|
||||||
|
permissionId: Joi.string().valid(...Object.values(BUILTIN_PERMISSION_IDS)).required(),
|
||||||
|
inherits: Joi.string().optional(),
|
||||||
|
}).unknown(true))
|
||||||
|
}
|
||||||
|
|
||||||
router
|
router
|
||||||
.post("/api/roles", authorized(BUILDER), controller.save)
|
.post("/api/roles", authorized(BUILDER), generateValidator(), controller.save)
|
||||||
.get("/api/roles", authorized(BUILDER), controller.fetch)
|
.get("/api/roles", authorized(BUILDER), controller.fetch)
|
||||||
.get("/api/roles/:roleId", authorized(BUILDER), controller.find)
|
.get("/api/roles/:roleId", authorized(BUILDER), controller.find)
|
||||||
.delete("/api/roles/:roleId/:rev", authorized(BUILDER), controller.destroy)
|
.delete("/api/roles/:roleId/:rev", authorized(BUILDER), controller.destroy)
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
const CouchDB = require("../../../db")
|
const CouchDB = require("../../../db")
|
||||||
const supertest = require("supertest")
|
const supertest = require("supertest")
|
||||||
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
||||||
const {
|
|
||||||
BUILTIN_PERMISSION_NAMES,
|
|
||||||
} = require("../../../utilities/security/permissions")
|
|
||||||
const packageJson = require("../../../../package")
|
const packageJson = require("../../../../package")
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
const env = require("../../../environment")
|
const env = require("../../../environment")
|
||||||
|
@ -131,49 +128,7 @@ exports.createUser = async (
|
||||||
return res.body
|
return res.body
|
||||||
}
|
}
|
||||||
|
|
||||||
const createUserWithOnePermission = async (request, appId, permName) => {
|
const createUserWithRole = async (request, appId, roleId, username) => {
|
||||||
let permissions = [permName]
|
|
||||||
|
|
||||||
return await createUserWithPermissions(
|
|
||||||
request,
|
|
||||||
appId,
|
|
||||||
permissions,
|
|
||||||
"onePermOnlyUser"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const createUserWithAdminPermissions = async (request, appId) => {
|
|
||||||
let permissions = [BUILTIN_PERMISSION_NAMES.ADMIN]
|
|
||||||
|
|
||||||
return await createUserWithPermissions(
|
|
||||||
request,
|
|
||||||
appId,
|
|
||||||
permissions,
|
|
||||||
"adminUser"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const createUserWithAllPermissionExceptOne = async (
|
|
||||||
request,
|
|
||||||
appId,
|
|
||||||
permName
|
|
||||||
) => {
|
|
||||||
let permissions = [permName]
|
|
||||||
|
|
||||||
return await createUserWithPermissions(
|
|
||||||
request,
|
|
||||||
appId,
|
|
||||||
permissions,
|
|
||||||
"allPermsExceptOneUser"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const createUserWithPermissions = async (
|
|
||||||
request,
|
|
||||||
appId,
|
|
||||||
permissions,
|
|
||||||
username
|
|
||||||
) => {
|
|
||||||
const password = `password_${username}`
|
const password = `password_${username}`
|
||||||
await request
|
await request
|
||||||
.post(`/api/users`)
|
.post(`/api/users`)
|
||||||
|
@ -182,8 +137,7 @@ const createUserWithPermissions = async (
|
||||||
name: username,
|
name: username,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
roleId: BUILTIN_ROLE_IDS.POWER,
|
roleId,
|
||||||
permissions,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const anonUser = {
|
const anonUser = {
|
||||||
|
@ -216,23 +170,29 @@ exports.testPermissionsForEndpoint = async ({
|
||||||
url,
|
url,
|
||||||
body,
|
body,
|
||||||
appId,
|
appId,
|
||||||
permName1,
|
passRole,
|
||||||
permName2,
|
failRole,
|
||||||
}) => {
|
}) => {
|
||||||
const headers = await createUserWithOnePermission(request, appId, permName1)
|
const passHeader = await createUserWithRole(
|
||||||
|
|
||||||
await createRequest(request, method, url, body)
|
|
||||||
.set(headers)
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
const noPermsHeaders = await createUserWithAllPermissionExceptOne(
|
|
||||||
request,
|
request,
|
||||||
appId,
|
appId,
|
||||||
permName2
|
passRole,
|
||||||
|
"passUser"
|
||||||
)
|
)
|
||||||
|
|
||||||
await createRequest(request, method, url, body)
|
await createRequest(request, method, url, body)
|
||||||
.set(noPermsHeaders)
|
.set(passHeader)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
const failHeader = await createUserWithRole(
|
||||||
|
request,
|
||||||
|
appId,
|
||||||
|
failRole,
|
||||||
|
"failUser"
|
||||||
|
)
|
||||||
|
|
||||||
|
await createRequest(request, method, url, body)
|
||||||
|
.set(failHeader)
|
||||||
.expect(403)
|
.expect(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +203,12 @@ exports.builderEndpointShouldBlockNormalUsers = async ({
|
||||||
body,
|
body,
|
||||||
appId,
|
appId,
|
||||||
}) => {
|
}) => {
|
||||||
const headers = await createUserWithAdminPermissions(request, appId)
|
const headers = await createUserWithRole(
|
||||||
|
request,
|
||||||
|
appId,
|
||||||
|
BUILTIN_ROLE_IDS.BASIC,
|
||||||
|
"basicUser"
|
||||||
|
)
|
||||||
|
|
||||||
await createRequest(request, method, url, body)
|
await createRequest(request, method, url, body)
|
||||||
.set(headers)
|
.set(headers)
|
||||||
|
|
|
@ -8,8 +8,9 @@ const {
|
||||||
const {
|
const {
|
||||||
BUILTIN_ROLE_IDS,
|
BUILTIN_ROLE_IDS,
|
||||||
} = require("../../../utilities/security/roles")
|
} = require("../../../utilities/security/roles")
|
||||||
|
const { BUILTIN_PERMISSION_IDS } = require("../../../utilities/security/permissions")
|
||||||
|
|
||||||
const roleBody = { name: "user", inherits: BUILTIN_ROLE_IDS.BASIC }
|
const roleBody = { name: "NewRole", inherits: BUILTIN_ROLE_IDS.BASIC, permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY }
|
||||||
|
|
||||||
describe("/roles", () => {
|
describe("/roles", () => {
|
||||||
let server
|
let server
|
||||||
|
@ -43,7 +44,7 @@ describe("/roles", () => {
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.res.statusMessage).toEqual("Role 'user' created successfully.")
|
expect(res.res.statusMessage).toEqual("Role 'NewRole' created successfully.")
|
||||||
expect(res.body._id).toBeDefined()
|
expect(res.body._id).toBeDefined()
|
||||||
expect(res.body._rev).toBeDefined()
|
expect(res.body._rev).toBeDefined()
|
||||||
})
|
})
|
||||||
|
@ -71,16 +72,19 @@ describe("/roles", () => {
|
||||||
expect(res.body.length).toBe(3)
|
expect(res.body.length).toBe(3)
|
||||||
|
|
||||||
const adminRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.ADMIN)
|
const adminRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.ADMIN)
|
||||||
expect(adminRole.inherits).toEqual(BUILTIN_ROLE_IDS.POWER)
|
|
||||||
expect(adminRole).toBeDefined()
|
expect(adminRole).toBeDefined()
|
||||||
|
expect(adminRole.inherits).toEqual(BUILTIN_ROLE_IDS.POWER)
|
||||||
|
expect(adminRole.permissionId).toEqual(BUILTIN_PERMISSION_IDS.ADMIN)
|
||||||
|
|
||||||
const powerUserRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.POWER)
|
const powerUserRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.POWER)
|
||||||
expect(powerUserRole.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC)
|
|
||||||
expect(powerUserRole).toBeDefined()
|
expect(powerUserRole).toBeDefined()
|
||||||
|
expect(powerUserRole.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC)
|
||||||
|
expect(powerUserRole.permissionId).toEqual(BUILTIN_PERMISSION_IDS.POWER)
|
||||||
|
|
||||||
const customRoleFetched = res.body.find(r => r._id === customRole._id)
|
const customRoleFetched = res.body.find(r => r._id === customRole._id)
|
||||||
expect(customRoleFetched.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC)
|
|
||||||
expect(customRoleFetched).toBeDefined()
|
expect(customRoleFetched).toBeDefined()
|
||||||
|
expect(customRoleFetched.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC)
|
||||||
|
expect(customRoleFetched.permissionId).toEqual(BUILTIN_PERMISSION_IDS.READ_ONLY)
|
||||||
})
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -89,7 +93,7 @@ describe("/roles", () => {
|
||||||
it("should delete custom roles", async () => {
|
it("should delete custom roles", async () => {
|
||||||
const createRes = await request
|
const createRes = await request
|
||||||
.post(`/api/roles`)
|
.post(`/api/roles`)
|
||||||
.send({ name: "user" })
|
.send({ name: "user", permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY })
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
|
@ -5,12 +5,16 @@ const {
|
||||||
createUser,
|
createUser,
|
||||||
testPermissionsForEndpoint,
|
testPermissionsForEndpoint,
|
||||||
} = require("./couchTestUtils")
|
} = require("./couchTestUtils")
|
||||||
const {
|
|
||||||
BUILTIN_PERMISSION_NAMES,
|
|
||||||
} = require("../../../utilities/security/permissions")
|
|
||||||
const {
|
const {
|
||||||
BUILTIN_ROLE_IDS,
|
BUILTIN_ROLE_IDS,
|
||||||
} = require("../../../utilities/security/roles")
|
} = require("../../../utilities/security/roles")
|
||||||
|
const { cloneDeep } = require("lodash/fp")
|
||||||
|
|
||||||
|
const baseBody = {
|
||||||
|
name: "brandNewUser",
|
||||||
|
password: "yeeooo",
|
||||||
|
roleId: BUILTIN_ROLE_IDS.POWER
|
||||||
|
}
|
||||||
|
|
||||||
describe("/users", () => {
|
describe("/users", () => {
|
||||||
let request
|
let request
|
||||||
|
@ -20,12 +24,12 @@ describe("/users", () => {
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
({ request, server } = await supertest(server))
|
({ request, server } = await supertest(server))
|
||||||
});
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
app = await createApplication(request)
|
app = await createApplication(request)
|
||||||
appId = app.instance._id
|
appId = app.instance._id
|
||||||
});
|
})
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
server.close()
|
server.close()
|
||||||
|
@ -54,38 +58,40 @@ describe("/users", () => {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/api/users`,
|
url: `/api/users`,
|
||||||
appId: appId,
|
appId: appId,
|
||||||
permName1: BUILTIN_PERMISSION_NAMES.POWER,
|
passRole: BUILTIN_ROLE_IDS.ADMIN,
|
||||||
permName2: BUILTIN_PERMISSION_NAMES.WRITE,
|
failRole: BUILTIN_ROLE_IDS.PUBLIC,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
|
|
||||||
it("returns a success message when a user is successfully created", async () => {
|
it("returns a success message when a user is successfully created", async () => {
|
||||||
|
const body = cloneDeep(baseBody)
|
||||||
|
body.username = "bill"
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/users`)
|
.post(`/api/users`)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.send({ name: "Bill", username: "bill", password: "bills_password", roleId: BUILTIN_ROLE_IDS.POWER })
|
.send(body)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
|
||||||
expect(res.res.statusMessage).toEqual("User created successfully.");
|
expect(res.res.statusMessage).toEqual("User created successfully.")
|
||||||
expect(res.body._id).toBeUndefined()
|
expect(res.body._id).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
const body = cloneDeep(baseBody)
|
||||||
|
body.username = "brandNewUser"
|
||||||
await testPermissionsForEndpoint({
|
await testPermissionsForEndpoint({
|
||||||
request,
|
request,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: { name: "brandNewUser", username: "brandNewUser", password: "yeeooo", roleId: BUILTIN_ROLE_IDS.POWER },
|
body,
|
||||||
url: `/api/users`,
|
url: `/api/users`,
|
||||||
appId: appId,
|
appId: appId,
|
||||||
permName1: BUILTIN_PERMISSION_NAMES.ADMIN,
|
passRole: BUILTIN_ROLE_IDS.ADMIN,
|
||||||
permName2: BUILTIN_PERMISSION_NAMES.POWER,
|
failRole: BUILTIN_ROLE_IDS.PUBLIC,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
});
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")
|
const {
|
||||||
|
BUILTIN_ROLE_IDS,
|
||||||
|
getUserPermissionIds,
|
||||||
|
} = require("../utilities/security/roles")
|
||||||
const {
|
const {
|
||||||
PermissionTypes,
|
PermissionTypes,
|
||||||
doesHavePermission,
|
doesHavePermission,
|
||||||
|
@ -48,7 +51,7 @@ module.exports = (permType, permLevel = null) => async (ctx, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const role = ctx.user.role
|
const role = ctx.user.role
|
||||||
const permissions = ctx.user.permissions
|
const permissions = await getUserPermissionIds(ctx.appId, role._id)
|
||||||
if (ADMIN_ROLES.indexOf(role._id) !== -1) {
|
if (ADMIN_ROLES.indexOf(role._id) !== -1) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
const { BUILTIN_ROLE_IDS } = require("../security/roles")
|
const { BUILTIN_ROLE_IDS } = require("../security/roles")
|
||||||
const { BUILTIN_PERMISSION_NAMES } = require("../security/permissions")
|
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
|
@ -11,7 +10,6 @@ module.exports = async (ctx, appId, version) => {
|
||||||
const builderUser = {
|
const builderUser = {
|
||||||
userId: "BUILDER",
|
userId: "BUILDER",
|
||||||
roleId: BUILTIN_ROLE_IDS.BUILDER,
|
roleId: BUILTIN_ROLE_IDS.BUILDER,
|
||||||
permissions: [BUILTIN_PERMISSION_NAMES.ADMIN],
|
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
if (env.BUDIBASE_API_KEY) {
|
if (env.BUDIBASE_API_KEY) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ function getAllowedLevels(userPermLevel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.BUILTIN_PERMISSION_NAMES = {
|
exports.BUILTIN_PERMISSION_IDS = {
|
||||||
READ_ONLY: "read_only",
|
READ_ONLY: "read_only",
|
||||||
WRITE: "write",
|
WRITE: "write",
|
||||||
ADMIN: "admin",
|
ADMIN: "admin",
|
||||||
|
@ -54,21 +54,24 @@ exports.BUILTIN_PERMISSION_NAMES = {
|
||||||
|
|
||||||
exports.BUILTIN_PERMISSIONS = {
|
exports.BUILTIN_PERMISSIONS = {
|
||||||
READ_ONLY: {
|
READ_ONLY: {
|
||||||
name: exports.BUILTIN_PERMISSION_NAMES.READ_ONLY,
|
_id: exports.BUILTIN_PERMISSION_IDS.READ_ONLY,
|
||||||
|
name: "Read only",
|
||||||
permissions: [
|
permissions: [
|
||||||
new Permission(PermissionTypes.TABLE, PermissionLevels.READ),
|
new Permission(PermissionTypes.TABLE, PermissionLevels.READ),
|
||||||
new Permission(PermissionTypes.VIEW, PermissionLevels.READ),
|
new Permission(PermissionTypes.VIEW, PermissionLevels.READ),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
WRITE: {
|
WRITE: {
|
||||||
name: exports.BUILTIN_PERMISSION_NAMES.WRITE,
|
_id: exports.BUILTIN_PERMISSION_IDS.WRITE,
|
||||||
|
name: "Read/Write",
|
||||||
permissions: [
|
permissions: [
|
||||||
new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||||
new Permission(PermissionTypes.VIEW, PermissionLevels.READ),
|
new Permission(PermissionTypes.VIEW, PermissionLevels.READ),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
POWER: {
|
POWER: {
|
||||||
name: exports.BUILTIN_PERMISSION_NAMES.POWER,
|
_id: exports.BUILTIN_PERMISSION_IDS.POWER,
|
||||||
|
name: "Power",
|
||||||
permissions: [
|
permissions: [
|
||||||
new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||||
new Permission(PermissionTypes.USER, PermissionLevels.READ),
|
new Permission(PermissionTypes.USER, PermissionLevels.READ),
|
||||||
|
@ -78,7 +81,8 @@ exports.BUILTIN_PERMISSIONS = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
ADMIN: {
|
ADMIN: {
|
||||||
name: exports.BUILTIN_PERMISSION_NAMES.ADMIN,
|
_id: exports.BUILTIN_PERMISSION_IDS.ADMIN,
|
||||||
|
name: "Admin",
|
||||||
permissions: [
|
permissions: [
|
||||||
new Permission(PermissionTypes.TABLE, PermissionLevels.ADMIN),
|
new Permission(PermissionTypes.TABLE, PermissionLevels.ADMIN),
|
||||||
new Permission(PermissionTypes.USER, PermissionLevels.ADMIN),
|
new Permission(PermissionTypes.USER, PermissionLevels.ADMIN),
|
||||||
|
@ -89,11 +93,11 @@ exports.BUILTIN_PERMISSIONS = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.doesHavePermission = (permType, permLevel, userPermissionNames) => {
|
exports.doesHavePermission = (permType, permLevel, permissionIds) => {
|
||||||
const builtins = Object.values(exports.BUILTIN_PERMISSIONS)
|
const builtins = Object.values(exports.BUILTIN_PERMISSIONS)
|
||||||
let permissions = flatten(
|
let permissions = flatten(
|
||||||
builtins
|
builtins
|
||||||
.filter(builtin => userPermissionNames.indexOf(builtin.name) !== -1)
|
.filter(builtin => permissionIds.indexOf(builtin._id) !== -1)
|
||||||
.map(builtin => builtin.permissions)
|
.map(builtin => builtin.permissions)
|
||||||
)
|
)
|
||||||
for (let permission of permissions) {
|
for (let permission of permissions) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
|
const { BUILTIN_PERMISSION_IDS } = require("./permissions")
|
||||||
|
|
||||||
const BUILTIN_IDS = {
|
const BUILTIN_IDS = {
|
||||||
ADMIN: "ADMIN",
|
ADMIN: "ADMIN",
|
||||||
|
@ -9,20 +10,37 @@ const BUILTIN_IDS = {
|
||||||
BUILDER: "BUILDER",
|
BUILDER: "BUILDER",
|
||||||
}
|
}
|
||||||
|
|
||||||
function Role(id, name, inherits) {
|
function Role(id, name) {
|
||||||
this._id = id
|
this._id = id
|
||||||
this.name = name
|
this.name = name
|
||||||
if (inherits) {
|
}
|
||||||
this.inherits = inherits
|
|
||||||
}
|
Role.prototype.addPermission = function(permissionId) {
|
||||||
|
this.permissionId = permissionId
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
Role.prototype.addInheritance = function(inherits) {
|
||||||
|
this.inherits = inherits
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.BUILTIN_ROLES = {
|
exports.BUILTIN_ROLES = {
|
||||||
ADMIN: new Role(BUILTIN_IDS.ADMIN, "Admin", BUILTIN_IDS.POWER),
|
ADMIN: new Role(BUILTIN_IDS.ADMIN, "Admin")
|
||||||
POWER: new Role(BUILTIN_IDS.POWER, "Power", BUILTIN_IDS.BASIC),
|
.addPermission(BUILTIN_PERMISSION_IDS.ADMIN)
|
||||||
BASIC: new Role(BUILTIN_IDS.BASIC, "Basic", BUILTIN_IDS.PUBLIC),
|
.addInheritance(BUILTIN_IDS.POWER),
|
||||||
PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public"),
|
POWER: new Role(BUILTIN_IDS.POWER, "Power")
|
||||||
BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder"),
|
.addPermission(BUILTIN_PERMISSION_IDS.POWER)
|
||||||
|
.addInheritance(BUILTIN_IDS.BASIC),
|
||||||
|
BASIC: new Role(BUILTIN_IDS.BASIC, "Basic")
|
||||||
|
.addPermission(BUILTIN_PERMISSION_IDS.WRITE)
|
||||||
|
.addInheritance(BUILTIN_IDS.PUBLIC),
|
||||||
|
PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public").addPermission(
|
||||||
|
BUILTIN_PERMISSION_IDS.READ_ONLY
|
||||||
|
),
|
||||||
|
BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder").addPermission(
|
||||||
|
BUILTIN_PERMISSION_IDS.ADMIN
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.BUILTIN_ROLE_ID_ARRAY = Object.values(exports.BUILTIN_ROLES).map(
|
exports.BUILTIN_ROLE_ID_ARRAY = Object.values(exports.BUILTIN_ROLES).map(
|
||||||
|
@ -60,6 +78,29 @@ exports.getRole = async (appId, roleId) => {
|
||||||
return role
|
return role
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple function to get all the roles based on the top level user role ID.
|
||||||
|
*/
|
||||||
|
async function getAllUserRoles(appId, userRoleId) {
|
||||||
|
if (!userRoleId) {
|
||||||
|
return [BUILTIN_IDS.PUBLIC]
|
||||||
|
}
|
||||||
|
let currentRole = await exports.getRole(appId, userRoleId)
|
||||||
|
let roles = currentRole ? [currentRole] : []
|
||||||
|
let roleIds = [userRoleId]
|
||||||
|
// get all the inherited roles
|
||||||
|
while (
|
||||||
|
currentRole &&
|
||||||
|
currentRole.inherits &&
|
||||||
|
roleIds.indexOf(currentRole.inherits) === -1
|
||||||
|
) {
|
||||||
|
roleIds.push(currentRole.inherits)
|
||||||
|
currentRole = await exports.getRole(appId, currentRole.inherits)
|
||||||
|
roles.push(currentRole)
|
||||||
|
}
|
||||||
|
return roles
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an ordered array of the user's inherited role IDs, this can be used
|
* Returns an ordered array of the user's inherited role IDs, this can be used
|
||||||
* to determine if a user can access something that requires a specific role.
|
* to determine if a user can access something that requires a specific role.
|
||||||
|
@ -70,22 +111,21 @@ exports.getRole = async (appId, roleId) => {
|
||||||
*/
|
*/
|
||||||
exports.getUserRoleHierarchy = async (appId, userRoleId) => {
|
exports.getUserRoleHierarchy = async (appId, userRoleId) => {
|
||||||
// special case, if they don't have a role then they are a public user
|
// special case, if they don't have a role then they are a public user
|
||||||
if (!userRoleId) {
|
return (await getAllUserRoles(appId, userRoleId)).map(role => role._id)
|
||||||
return [BUILTIN_IDS.PUBLIC]
|
}
|
||||||
}
|
|
||||||
let roleIds = [userRoleId]
|
/**
|
||||||
let userRole = await exports.getRole(appId, userRoleId)
|
* Get all of the user permissions which could be found across the role hierarchy
|
||||||
// check if inherited makes it possible
|
* @param appId The ID of the application from which roles should be obtained.
|
||||||
while (
|
* @param userRoleId The user's role ID, this can be found in their access token.
|
||||||
userRole &&
|
* @returns {Promise<string[]>} A list of permission IDs these should all be unique.
|
||||||
userRole.inherits &&
|
*/
|
||||||
roleIds.indexOf(userRole.inherits) === -1
|
exports.getUserPermissionIds = async (appId, userRoleId) => {
|
||||||
) {
|
return [
|
||||||
roleIds.push(userRole.inherits)
|
...new Set(
|
||||||
// go to get the inherited incase it inherits anything
|
(await getAllUserRoles(appId, userRoleId)).map(role => role.permissionId)
|
||||||
userRole = await exports.getRole(appId, userRole.inherits)
|
),
|
||||||
}
|
]
|
||||||
return roleIds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AccessController {
|
class AccessController {
|
||||||
|
|
Loading…
Reference in New Issue