WIP - this is working towards the permissions system but stopping here for the night, this is currently not functional.

This commit is contained in:
Michael Drury 2020-11-11 17:34:15 +00:00
parent 5b26fce1ea
commit 1f4e27eb13
29 changed files with 263 additions and 150 deletions

View File

@ -2,9 +2,8 @@ const CouchDB = require("../../db")
const { const {
generateAdminPermissions, generateAdminPermissions,
generatePowerUserPermissions, generatePowerUserPermissions,
POWERUSER_LEVEL_ID, BUILTIN_LEVELS,
ADMIN_LEVEL_ID, } = require("../../utilities/security/accessLevels")
} = require("../../utilities/accessLevels")
const { const {
generateAccessLevelID, generateAccessLevelID,
getAccessLevelParams, getAccessLevelParams,
@ -21,13 +20,11 @@ exports.fetch = async function(ctx) {
const staticAccessLevels = [ const staticAccessLevels = [
{ {
_id: ADMIN_LEVEL_ID, ...BUILTIN_LEVELS.admin,
name: "Admin",
permissions: await generateAdminPermissions(ctx.user.appId), permissions: await generateAdminPermissions(ctx.user.appId),
}, },
{ {
_id: POWERUSER_LEVEL_ID, ...BUILTIN_LEVELS.power,
name: "Power User",
permissions: await generatePowerUserPermissions(ctx.user.appId), permissions: await generatePowerUserPermissions(ctx.user.appId),
}, },
] ]

View File

@ -2,9 +2,8 @@ const CouchDB = require("../../db")
const bcrypt = require("../../utilities/bcrypt") const bcrypt = require("../../utilities/bcrypt")
const { generateUserID, getUserParams } = require("../../db/utils") const { generateUserID, getUserParams } = require("../../db/utils")
const { const {
POWERUSER_LEVEL_ID, BUILTIN_LEVELS_IDS,
ADMIN_LEVEL_ID, } = require("../../utilities/security/accessLevels")
} = require("../../utilities/accessLevels")
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {
const database = new CouchDB(ctx.user.appId) const database = new CouchDB(ctx.user.appId)
@ -89,10 +88,7 @@ exports.find = async function(ctx) {
const checkAccessLevel = async (db, accessLevelId) => { const checkAccessLevel = async (db, accessLevelId) => {
if (!accessLevelId) return if (!accessLevelId) return
if ( if (BUILTIN_LEVELS_IDS.indexOf(accessLevelId) !== -1) {
accessLevelId === POWERUSER_LEVEL_ID ||
accessLevelId === ADMIN_LEVEL_ID
) {
return { return {
_id: accessLevelId, _id: accessLevelId,
name: accessLevelId, name: accessLevelId,

View File

@ -1,14 +1,20 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../controllers/accesslevel") const controller = require("../controllers/accesslevel")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/security/permissions")
const router = Router() const router = Router()
router router
.post("/api/accesslevels", controller.create) .post("/api/accesslevels", authorized(BUILDER), controller.create)
.put("/api/accesslevels", controller.update) .put("/api/accesslevels", authorized(BUILDER), controller.update)
.get("/api/accesslevels", controller.fetch) .get("/api/accesslevels", authorized(BUILDER), controller.fetch)
.get("/api/accesslevels/:levelId", controller.find) .get("/api/accesslevels/:levelId", authorized(BUILDER), controller.find)
.delete("/api/accesslevels/:levelId/:rev", controller.destroy) .delete(
.patch("/api/accesslevels/:levelId", controller.patch) "/api/accesslevels/:levelId/:rev",
authorized(BUILDER),
controller.destroy
)
.patch("/api/accesslevels/:levelId", authorized(BUILDER), controller.patch)
module.exports = router module.exports = router

View File

@ -1,7 +1,7 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels")
const controller = require("../controllers/analytics") const controller = require("../controllers/analytics")
const { BUILDER } = require("../../utilities/security/permissions")
const router = Router() const router = Router()

View File

@ -1,7 +1,7 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../controllers/apikeys") const controller = require("../controllers/apikeys")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/security/permissions")
const router = Router() const router = Router()

View File

@ -1,7 +1,7 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../controllers/application") const controller = require("../controllers/application")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/security/permissions")
const router = Router() const router = Router()

View File

@ -2,7 +2,11 @@ const Router = require("@koa/router")
const controller = require("../controllers/automation") const controller = require("../controllers/automation")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const joiValidator = require("../../middleware/joi-validator") const joiValidator = require("../../middleware/joi-validator")
const { BUILDER, EXECUTE_AUTOMATION } = require("../../utilities/accessLevels") const {
BUILDER,
PermissionLevels,
PermissionTypes,
} = require("../../utilities/security/permissions")
const Joi = require("joi") const Joi = require("joi")
const router = Router() const router = Router()
@ -75,7 +79,7 @@ router
) )
.post( .post(
"/api/automations/:id/trigger", "/api/automations/:id/trigger",
authorized(EXECUTE_AUTOMATION), authorized(PermissionTypes.AUTOMATION, PermissionLevels.EXECUTE),
controller.trigger controller.trigger
) )
.delete("/api/automations/:id/:rev", authorized(BUILDER), controller.destroy) .delete("/api/automations/:id/:rev", authorized(BUILDER), controller.destroy)

View File

@ -1,7 +1,7 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../controllers/component") const controller = require("../controllers/component")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/security/permissions")
const router = Router() const router = Router()

View File

@ -1,7 +1,7 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../controllers/deploy") const controller = require("../controllers/deploy")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/security/permissions")
const router = Router() const router = Router()

View File

@ -1,6 +1,6 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/security/permissions")
const controller = require("../controllers/page") const controller = require("../controllers/page")
const router = Router() const router = Router()

View File

@ -2,46 +2,49 @@ const Router = require("@koa/router")
const rowController = require("../controllers/row") const rowController = require("../controllers/row")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const usage = require("../../middleware/usageQuota") const usage = require("../../middleware/usageQuota")
const { READ_TABLE, WRITE_TABLE } = require("../../utilities/accessLevels") const {
PermissionLevels,
PermissionTypes,
} = require("../../utilities/security/permissions")
const router = Router() const router = Router()
router router
.get( .get(
"/api/:tableId/:rowId/enrich", "/api/:tableId/:rowId/enrich",
authorized(READ_TABLE, ctx => ctx.params.tableId), authorized(PermissionTypes.TABLE, PermissionLevels.READ),
rowController.fetchEnrichedRow rowController.fetchEnrichedRow
) )
.get( .get(
"/api/:tableId/rows", "/api/:tableId/rows",
authorized(READ_TABLE, ctx => ctx.params.tableId), authorized(PermissionTypes.TABLE, PermissionLevels.READ),
rowController.fetchTableRows rowController.fetchTableRows
) )
.get( .get(
"/api/:tableId/rows/:rowId", "/api/:tableId/rows/:rowId",
authorized(READ_TABLE, ctx => ctx.params.tableId), authorized(PermissionTypes.TABLE, PermissionLevels.READ),
rowController.find rowController.find
) )
.post("/api/rows/search", rowController.search) .post("/api/rows/search", rowController.search)
.post( .post(
"/api/:tableId/rows", "/api/:tableId/rows",
authorized(WRITE_TABLE, ctx => ctx.params.tableId), authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
usage, usage,
rowController.save rowController.save
) )
.patch( .patch(
"/api/:tableId/rows/:id", "/api/:tableId/rows/:id",
authorized(WRITE_TABLE, ctx => ctx.params.tableId), authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
rowController.patch rowController.patch
) )
.post( .post(
"/api/:tableId/rows/validate", "/api/:tableId/rows/validate",
authorized(WRITE_TABLE, ctx => ctx.params.tableId), authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
rowController.validate rowController.validate
) )
.delete( .delete(
"/api/:tableId/rows/:rowId/:revId", "/api/:tableId/rows/:rowId/:revId",
authorized(WRITE_TABLE, ctx => ctx.params.tableId), authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
usage, usage,
rowController.destroy rowController.destroy
) )

View File

@ -1,7 +1,7 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../controllers/screen") const controller = require("../controllers/screen")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/security/permissions")
const joiValidator = require("../../middleware/joi-validator") const joiValidator = require("../../middleware/joi-validator")
const Joi = require("joi") const Joi = require("joi")

View File

@ -3,7 +3,7 @@ const controller = require("../controllers/static")
const { budibaseTempDir } = require("../../utilities/budibaseDir") const { budibaseTempDir } = require("../../utilities/budibaseDir")
const env = require("../../environment") const env = require("../../environment")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/security/permissions")
const usage = require("../../middleware/usageQuota") const usage = require("../../middleware/usageQuota")
const router = Router() const router = Router()

View File

@ -1,7 +1,11 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const tableController = require("../controllers/table") const tableController = require("../controllers/table")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER, READ_TABLE } = require("../../utilities/accessLevels") const {
BUILDER,
PermissionLevels,
PermissionTypes,
} = require("../../utilities/security/permissions")
const router = Router() const router = Router()
@ -9,7 +13,7 @@ router
.get("/api/tables", authorized(BUILDER), tableController.fetch) .get("/api/tables", authorized(BUILDER), tableController.fetch)
.get( .get(
"/api/tables/:id", "/api/tables/:id",
authorized(READ_TABLE, ctx => ctx.params.id), authorized(PermissionTypes.TABLE, PermissionLevels.READ),
tableController.find tableController.find
) )
.post("/api/tables", authorized(BUILDER), tableController.save) .post("/api/tables", authorized(BUILDER), tableController.save)

View File

@ -1,7 +1,7 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../controllers/templates") const controller = require("../controllers/templates")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/security/permissions")
const router = Router() const router = Router()

View File

@ -8,11 +8,11 @@ const {
const { const {
generateAdminPermissions, generateAdminPermissions,
generatePowerUserPermissions, generatePowerUserPermissions,
POWERUSER_LEVEL_ID, BUILTIN_LEVELS,
ADMIN_LEVEL_ID,
READ_TABLE, READ_TABLE,
WRITE_TABLE, WRITE_TABLE,
} = require("../../../utilities/accessLevels") } = require("../../../utilities/security/accessLevels")
const { BUILTIN_PERMISSION_NAMES } = require("../../../utilities/security/permissions")
describe("/accesslevels", () => { describe("/accesslevels", () => {
let server let server
@ -59,7 +59,7 @@ describe("/accesslevels", () => {
it("should list custom levels, plus 2 default levels", async () => { it("should list custom levels, plus 2 default levels", async () => {
const createRes = await request const createRes = await request
.post(`/api/accesslevels`) .post(`/api/accesslevels`)
.send({ name: "user", permissions: [ { itemId: table._id, name: READ_TABLE }] }) .send({ name: "user", permissions: [BUILTIN_PERMISSION_NAMES.READ_ONLY] })
.set(defaultHeaders(appId)) .set(defaultHeaders(appId))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -74,11 +74,11 @@ describe("/accesslevels", () => {
expect(res.body.length).toBe(3) expect(res.body.length).toBe(3)
const adminLevel = res.body.find(r => r._id === ADMIN_LEVEL_ID) const adminLevel = res.body.find(r => r._id === BUILTIN_LEVELS.admin._id)
expect(adminLevel).toBeDefined() expect(adminLevel).toBeDefined()
expect(adminLevel.permissions).toEqual(await generateAdminPermissions(appId)) expect(adminLevel.permissions).toEqual(await generateAdminPermissions(appId))
const powerUserLevel = res.body.find(r => r._id === POWERUSER_LEVEL_ID) const powerUserLevel = res.body.find(r => r._id === BUILTIN_LEVELS.power._id)
expect(powerUserLevel).toBeDefined() expect(powerUserLevel).toBeDefined()
expect(powerUserLevel.permissions).toEqual(await generatePowerUserPermissions(appId)) expect(powerUserLevel.permissions).toEqual(await generatePowerUserPermissions(appId))
@ -92,7 +92,7 @@ describe("/accesslevels", () => {
it("should delete custom access level", async () => { it("should delete custom access level", async () => {
const createRes = await request const createRes = await request
.post(`/api/accesslevels`) .post(`/api/accesslevels`)
.send({ name: "user", permissions: [ { itemId: table._id, name: READ_TABLE } ] }) .send({ name: "user", permissions: [BUILTIN_PERMISSION_NAMES.READ_ONLY] })
.set(defaultHeaders(appId)) .set(defaultHeaders(appId))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -115,7 +115,7 @@ describe("/accesslevels", () => {
it("should add given permissions", async () => { it("should add given permissions", async () => {
const createRes = await request const createRes = await request
.post(`/api/accesslevels`) .post(`/api/accesslevels`)
.send({ name: "user", permissions: [ { itemId: table._id, name: READ_TABLE }] }) .send({ name: "user", permissions: [BUILTIN_PERMISSION_NAMES.READ_ONLY] })
.set(defaultHeaders(appId)) .set(defaultHeaders(appId))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)

View File

@ -5,7 +5,7 @@ const {
ANON_LEVEL_ID, ANON_LEVEL_ID,
BUILDER_LEVEL_ID, BUILDER_LEVEL_ID,
generateAdminPermissions, generateAdminPermissions,
} = require("../../../utilities/accessLevels") } = require("../../../utilities/security/accessLevels")
const packageJson = require("../../../../package") const packageJson = require("../../../../package")
const jwt = require("jsonwebtoken") const jwt = require("jsonwebtoken")
const env = require("../../../environment") const env = require("../../../environment")

View File

@ -9,7 +9,7 @@ const {
POWERUSER_LEVEL_ID, POWERUSER_LEVEL_ID,
LIST_USERS, LIST_USERS,
USER_MANAGEMENT USER_MANAGEMENT
} = require("../../../utilities/accessLevels") } = require("../../../utilities/security/accessLevels")
describe("/users", () => { describe("/users", () => {
let request let request

View File

@ -1,19 +1,39 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../controllers/user") const controller = require("../controllers/user")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { USER_MANAGEMENT, LIST_USERS } = require("../../utilities/accessLevels") const {
PermissionLevels,
PermissionTypes,
} = require("../../utilities/security/permissions")
const usage = require("../../middleware/usageQuota") const usage = require("../../middleware/usageQuota")
const router = Router() const router = Router()
router router
.get("/api/users", authorized(LIST_USERS), controller.fetch) .get(
.get("/api/users/:username", authorized(USER_MANAGEMENT), controller.find) "/api/users",
.put("/api/users/", authorized(USER_MANAGEMENT), controller.update) authorized(PermissionTypes.USER, PermissionLevels.READ),
.post("/api/users", authorized(USER_MANAGEMENT), usage, controller.create) controller.fetch
)
.get(
"/api/users/:username",
authorized(PermissionTypes.USER, PermissionLevels.READ),
controller.find
)
.put(
"/api/users/",
authorized(PermissionTypes.USER, PermissionLevels.WRITE),
controller.update
)
.post(
"/api/users",
authorized(PermissionTypes.USER, PermissionLevels.WRITE),
usage,
controller.create
)
.delete( .delete(
"/api/users/:username", "/api/users/:username",
authorized(USER_MANAGEMENT), authorized(PermissionTypes.USER, PermissionLevels.WRITE),
usage, usage,
controller.destroy controller.destroy
) )

View File

@ -2,7 +2,11 @@ const Router = require("@koa/router")
const viewController = require("../controllers/view") const viewController = require("../controllers/view")
const rowController = require("../controllers/row") const rowController = require("../controllers/row")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER, READ_VIEW } = require("../../utilities/accessLevels") const {
BUILDER,
PermissionTypes,
PermissionLevels,
} = require("../../utilities/security/permissions")
const usage = require("../../middleware/usageQuota") const usage = require("../../middleware/usageQuota")
const router = Router() const router = Router()
@ -10,7 +14,7 @@ const router = Router()
router router
.get( .get(
"/api/views/:viewName", "/api/views/:viewName",
authorized(READ_VIEW, ctx => ctx.params.viewName), authorized(PermissionTypes.VIEW, PermissionLevels.READ),
rowController.fetchView rowController.fetchView
) )
.get("/api/views", authorized(BUILDER), viewController.fetch) .get("/api/views", authorized(BUILDER), viewController.fetch)

View File

@ -2,7 +2,11 @@ const Router = require("@koa/router")
const controller = require("../controllers/webhook") const controller = require("../controllers/webhook")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const joiValidator = require("../../middleware/joi-validator") const joiValidator = require("../../middleware/joi-validator")
const { BUILDER, EXECUTE_WEBHOOK } = require("../../utilities/accessLevels") const {
BUILDER,
PermissionTypes,
PermissionLevels,
} = require("../../utilities/security/permissions")
const Joi = require("joi") const Joi = require("joi")
const router = Router() const router = Router()
@ -38,7 +42,7 @@ router
) )
.post( .post(
"/api/webhooks/trigger/:instance/:id", "/api/webhooks/trigger/:instance/:id",
authorized(EXECUTE_WEBHOOK), authorized(PermissionTypes.WEBHOOK, PermissionLevels.EXECUTE),
controller.trigger controller.trigger
) )

View File

@ -1,4 +1,4 @@
const accessLevels = require("../../utilities/accessLevels") const accessLevels = require("../../utilities/security/accessLevels")
const userController = require("../../api/controllers/user") const userController = require("../../api/controllers/user")
const env = require("../../environment") const env = require("../../environment")
const usage = require("../../utilities/usageQuota") const usage = require("../../utilities/usageQuota")
@ -28,7 +28,7 @@ module.exports.definition = {
accessLevelId: { accessLevelId: {
type: "string", type: "string",
title: "Access Level", title: "Access Level",
enum: accessLevels.ACCESS_LEVELS, enum: accessLevels.BUILTIN_LEVELS,
pretty: Object.values(accessLevels.PRETTY_ACCESS_LEVELS), pretty: Object.values(accessLevels.PRETTY_ACCESS_LEVELS),
}, },
}, },

View File

@ -2,11 +2,8 @@ const jwt = require("jsonwebtoken")
const STATUS_CODES = require("../utilities/statusCodes") const STATUS_CODES = require("../utilities/statusCodes")
const accessLevelController = require("../api/controllers/accesslevel") const accessLevelController = require("../api/controllers/accesslevel")
const { const {
ADMIN_LEVEL_ID, BUILTIN_LEVEL_IDS,
POWERUSER_LEVEL_ID, } = require("../utilities/security/accessLevels")
BUILDER_LEVEL_ID,
ANON_LEVEL_ID,
} = require("../utilities/accessLevels")
const env = require("../environment") const env = require("../environment")
const { AuthTypes } = require("../constants") const { AuthTypes } = require("../constants")
const { getAppId, getCookieName, setCookie } = require("../utilities") const { getAppId, getCookieName, setCookie } = require("../utilities")
@ -74,12 +71,7 @@ module.exports = async (ctx, next) => {
* @param {*} accessLevelId - the id of the users access level * @param {*} accessLevelId - the id of the users access level
*/ */
const getAccessLevel = async (appId, accessLevelId) => { const getAccessLevel = async (appId, accessLevelId) => {
if ( if (BUILTIN_LEVEL_IDS.indexOf(accessLevelId) !== -1) {
accessLevelId === POWERUSER_LEVEL_ID ||
accessLevelId === ADMIN_LEVEL_ID ||
accessLevelId === BUILDER_LEVEL_ID ||
accessLevelId === ANON_LEVEL_ID
) {
return { return {
_id: accessLevelId, _id: accessLevelId,
name: accessLevelId, name: accessLevelId,

View File

@ -1,17 +1,14 @@
const { const { BUILTIN_LEVELS } = require("../utilities/security/accessLevels")
adminPermissions, const { PermissionTypes } = require("../utilities/security/permissions")
ADMIN_LEVEL_ID,
POWERUSER_LEVEL_ID,
BUILDER_LEVEL_ID,
BUILDER,
} = require("../utilities/accessLevels")
const env = require("../environment") const env = require("../environment")
const { apiKeyTable } = require("../db/dynamoClient") const { apiKeyTable } = require("../db/dynamoClient")
const { AuthTypes } = require("../constants") const { AuthTypes } = require("../constants")
const ADMIN_PERMS = [BUILTIN_LEVELS.admin._id, BUILTIN_LEVELS.builder._id]
const LOCAL_PASS = new RegExp(["webhooks/trigger", "webhooks/schema"].join("|")) const LOCAL_PASS = new RegExp(["webhooks/trigger", "webhooks/schema"].join("|"))
module.exports = (permName, getItemId) => async (ctx, next) => { module.exports = (permType, permLevel = null) => async (ctx, next) => {
// webhooks can pass locally // webhooks can pass locally
if (!env.CLOUD && LOCAL_PASS.test(ctx.request.url)) { if (!env.CLOUD && LOCAL_PASS.test(ctx.request.url)) {
return next() return next()
@ -37,7 +34,7 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
} }
// don't expose builder endpoints in the cloud // don't expose builder endpoints in the cloud
if (env.CLOUD && permName === BUILDER) return if (env.CLOUD && permType === PermissionTypes.BUILDER) return
if (!ctx.auth.authenticated) { if (!ctx.auth.authenticated) {
ctx.throw(403, "Session not authenticated") ctx.throw(403, "Session not authenticated")
@ -47,41 +44,18 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
ctx.throw(403, "User not found") ctx.throw(403, "User not found")
} }
if (ctx.user.accessLevel._id === ADMIN_LEVEL_ID) { if (ADMIN_PERMS.indexOf(ctx.user.accessLevel._id) !== -1) {
return next() return next()
} }
if (ctx.user.accessLevel._id === BUILDER_LEVEL_ID) { if (permType === PermissionTypes.BUILDER) {
return next()
}
if (permName === BUILDER) {
ctx.throw(403, "Not Authorized") ctx.throw(403, "Not Authorized")
return return
} }
const permissionId = ({ name, itemId }) => name + (itemId ? `-${itemId}` : "") // TODO: Replace the old permissions system here, check whether
// user has permission to use endpoint they are trying to access
const thisPermissionId = permissionId({
name: permName,
itemId: getItemId && getItemId(ctx),
})
// power user has everything, except the admin specific perms
if (
ctx.user.accessLevel._id === POWERUSER_LEVEL_ID &&
!adminPermissions.map(permissionId).includes(thisPermissionId)
) {
return next() return next()
}
if ( //ctx.throw(403, "Not Authorized")
ctx.user.accessLevel.permissions
.map(permissionId)
.includes(thisPermissionId)
) {
return next()
}
ctx.throw(403, "Not Authorized")
} }

View File

@ -1,36 +0,0 @@
// Permissions
module.exports.READ_TABLE = "read-table"
module.exports.WRITE_TABLE = "write-table"
module.exports.READ_VIEW = "read-view"
module.exports.EXECUTE_AUTOMATION = "execute-automation"
module.exports.EXECUTE_WEBHOOK = "execute-webhook"
module.exports.USER_MANAGEMENT = "user-management"
module.exports.BUILDER = "builder"
module.exports.LIST_USERS = "list-users"
// Access Level IDs
module.exports.ADMIN_LEVEL_ID = "ADMIN"
module.exports.POWERUSER_LEVEL_ID = "POWER_USER"
module.exports.BUILDER_LEVEL_ID = "BUILDER"
module.exports.ANON_LEVEL_ID = "ANON"
module.exports.ACCESS_LEVELS = [
module.exports.ADMIN_LEVEL_ID,
module.exports.POWERUSER_LEVEL_ID,
module.exports.BUILDER_LEVEL_ID,
module.exports.ANON_LEVEL_ID,
]
module.exports.PRETTY_ACCESS_LEVELS = {
[module.exports.ADMIN_LEVEL_ID]: "Admin",
[module.exports.POWERUSER_LEVEL_ID]: "Power user",
[module.exports.BUILDER_LEVEL_ID]: "Builder",
}
module.exports.adminPermissions = [
{
name: module.exports.USER_MANAGEMENT,
},
]
// to avoid circular dependencies this is included later, after exporting all enums
const permissions = require("./permissions")
module.exports.generateAdminPermissions = permissions.generateAdminPermissions
module.exports.generatePowerUserPermissions =
permissions.generatePowerUserPermissions

View File

@ -1,4 +1,4 @@
const { BUILDER_LEVEL_ID } = require("../accessLevels") const { BUILDER_LEVEL_ID } = require("../security/accessLevels")
const env = require("../../environment") const env = require("../../environment")
const CouchDB = require("../../db") const CouchDB = require("../../db")
const jwt = require("jsonwebtoken") const jwt = require("jsonwebtoken")

View File

@ -1,7 +1,7 @@
const viewController = require("../api/controllers/view") const viewController = require("../api/controllers/view")
const tableController = require("../api/controllers/table") const tableController = require("../api/controllers/table")
const automationController = require("../api/controllers/automation") const automationController = require("../api/controllers/automation")
const accessLevels = require("./accessLevels") const accessLevels = require("./security/accessLevels")
// this has been broken out to reduce risk of circular dependency from utilities, no enums defined here // this has been broken out to reduce risk of circular dependency from utilities, no enums defined here
const generateAdminPermissions = async appId => [ const generateAdminPermissions = async appId => [

View File

@ -0,0 +1,44 @@
const { DocumentTypes, SEPARATOR } = require("../../db/utils")
function makeAccessLevelId(baseId) {
return `${DocumentTypes.ACCESS_LEVEL}${SEPARATOR}${baseId}`
}
// Permissions
exports.READ_TABLE = "read-table"
exports.WRITE_TABLE = "write-table"
exports.READ_VIEW = "read-view"
exports.EXECUTE_AUTOMATION = "execute-automation"
exports.EXECUTE_WEBHOOK = "execute-webhook"
exports.USER_MANAGEMENT = "user-management"
exports.BUILDER = "builder"
exports.LIST_USERS = "list-users"
// Access Level IDs
exports.ADMIN_LEVEL_ID = "ADMIN"
exports.POWERUSER_LEVEL_ID = "POWER_USER"
exports.BUILDER_LEVEL_ID = "BUILDER"
exports.ANON_LEVEL_ID = "ANON"
exports.BUILTIN_LEVELS = {
admin: { _id: makeAccessLevelId("ADMIN"), name: "Admin" },
power: { _id: makeAccessLevelId("POWER_USER"), name: "Power user" },
builder: { _id: makeAccessLevelId("BUILDER"), name: "Builder" },
anon: { _id: makeAccessLevelId("ANON"), name: "Anonymous" },
}
exports.BUILTIN_LEVEL_IDS = Object.values(exports.BUILTIN_LEVELS).map(
level => level._id
)
exports.PRETTY_ACCESS_LEVELS = {
[exports.ADMIN_LEVEL_ID]: "Admin",
[exports.POWERUSER_LEVEL_ID]: "Power user",
[exports.BUILDER_LEVEL_ID]: "Builder",
}
exports.adminPermissions = [
{
name: exports.USER_MANAGEMENT,
},
]
// to avoid circular dependencies this is included later, after exporting all enums
const permissions = require("../permissions")
exports.generateAdminPermissions = permissions.generateAdminPermissions
exports.generatePowerUserPermissions = permissions.generatePowerUserPermissions

View File

@ -0,0 +1,101 @@
const { flatten } = require("lodash")
exports.READ_TABLE = "read-table"
exports.WRITE_TABLE = "write-table"
exports.READ_VIEW = "read-view"
exports.EXECUTE_AUTOMATION = "execute-automation"
exports.EXECUTE_WEBHOOK = "execute-webhook"
exports.USER_MANAGEMENT = "user-management"
exports.BUILDER = "builder"
exports.LIST_USERS = "list-users"
const PermissionLevels = {
READ: "read",
WRITE: "write",
EXECUTE: "execute",
ADMIN: "admin",
}
const PermissionTypes = {
TABLE: "table",
USER: "user",
AUTOMATION: "automation",
WEBHOOK: "webhook",
BUILDER: "builder",
VIEW: "view",
}
function Permission(type, level) {
this.level = level
this.type = type
}
/**
* Given the specified permission level for the user return the levels they are allowed to carry out.
* @param {string} userPermLevel The permission level of the user.
* @return {string[]} All the permission levels this user is allowed to carry out.
*/
function getAllowedLevels(userPermLevel) {
switch (userPermLevel) {
case PermissionLevels.READ:
return [PermissionLevels.READ]
case PermissionLevels.WRITE:
return [PermissionLevels.READ, PermissionLevels.WRITE]
case PermissionLevels.EXECUTE:
return [PermissionLevels.EXECUTE]
case PermissionLevels.ADMIN:
return [
PermissionLevels.READ,
PermissionLevels.WRITE,
PermissionLevels.EXECUTE,
]
default:
return []
}
}
// TODO: need to expand on this
exports.BUILTIN_PERMISSION_NAMES = {
READ_ONLY: "read_only",
WRITE: "write",
}
exports.BUILTIN_PERMISSIONS = {
READ_ONLY: {
name: exports.BUILTIN_PERMISSION_NAMES.READ_ONLY,
permissions: [
new Permission(PermissionTypes.TABLE, PermissionLevels.READ),
new Permission(PermissionTypes.VIEW, PermissionLevels.READ),
],
},
WRITE: {
name: exports.BUILTIN_PERMISSION_NAMES.WRITE,
permissions: [
new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE),
new Permission(PermissionTypes.VIEW, PermissionLevels.READ),
],
},
}
exports.doesHavePermission = (permType, permLevel, userPermissionNames) => {
const builtins = Object.values(exports.BUILTIN_PERMISSIONS)
let permissions = flatten(
builtins
.filter(builtin => userPermissionNames.indexOf(builtin.name) !== -1)
.map(builtin => builtin.permissions)
)
for (let permission of permissions) {
if (
permission.type === permType &&
getAllowedLevels(permission.level).indexOf(permLevel) !== -1
) {
return true
}
}
return false
}
// utility as a lot of things need simply the builder permission
exports.BUILDER = PermissionTypes.BUILDER
exports.PermissionTypes = PermissionTypes
exports.PermissionLevels = PermissionLevels