This commit is contained in:
Martin McKeaveney 2021-05-16 21:29:07 +01:00
commit fc7ddbe540
75 changed files with 280 additions and 175 deletions

View File

@ -1 +1,12 @@
# Budibase Authentication Library # Budibase Core backend library
This library contains core functionality, like auth and security features
which are shared between backend services.
#### Note about top level JS files
For the purposes of being able to do say `require("@budibase/auth/permissions")` we need to
specify the exports at the top-level of the module.
For these files they should be limited to a single `require` of the file that should
be exported and then a single `module.exports = ...` to export the file in
commonJS.

1
packages/auth/db.js Normal file
View File

@ -0,0 +1 @@
module.exports = require("./src/db/utils")

View File

@ -11,6 +11,7 @@
"ioredis": "^4.27.1", "ioredis": "^4.27.1",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"koa-passport": "^4.1.4", "koa-passport": "^4.1.4",
"lodash": "^4.17.21",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"passport-google-auth": "^1.0.2", "passport-google-auth": "^1.0.2",
"passport-google-oauth": "^2.0.0", "passport-google-oauth": "^2.0.0",

View File

@ -0,0 +1 @@
module.exports = require("./src/security/permissions")

4
packages/auth/redis.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
Client: require("./src/redis"),
utils: require("./src/redis/utils"),
}

1
packages/auth/roles.js Normal file
View File

@ -0,0 +1 @@
module.exports = require("./src/security/roles")

View File

@ -7,3 +7,7 @@ module.exports.setDB = pouch => {
module.exports.getDB = dbName => { module.exports.getDB = dbName => {
return new Pouch(dbName) return new Pouch(dbName)
} }
module.exports.getCouch = () => {
return Pouch
}

View File

@ -1,5 +1,6 @@
const { newid } = require("../hashing") const { newid } = require("../hashing")
const Replication = require("./Replication") const Replication = require("./Replication")
const { getCouch } = require("./index")
const UNICODE_MAX = "\ufff0" const UNICODE_MAX = "\ufff0"
const SEPARATOR = "_" const SEPARATOR = "_"
@ -25,6 +26,7 @@ const DocumentTypes = {
APP: "app", APP: "app",
APP_DEV: "app_dev", APP_DEV: "app_dev",
APP_METADATA: "app_metadata", APP_METADATA: "app_metadata",
ROLE: "role",
} }
exports.DocumentTypes = DocumentTypes exports.DocumentTypes = DocumentTypes
@ -32,6 +34,29 @@ exports.APP_PREFIX = DocumentTypes.APP + SEPARATOR
exports.APP_DEV_PREFIX = DocumentTypes.APP_DEV + SEPARATOR exports.APP_DEV_PREFIX = DocumentTypes.APP_DEV + SEPARATOR
exports.SEPARATOR = SEPARATOR exports.SEPARATOR = SEPARATOR
/**
* If creating DB allDocs/query params with only a single top level ID this can be used, this
* is usually the case as most of our docs are top level e.g. tables, automations, users and so on.
* More complex cases such as link docs and rows which have multiple levels of IDs that their
* ID consists of need their own functions to build the allDocs parameters.
* @param {string} docType The type of document which input params are being built for, e.g. user,
* link, app, table and so on.
* @param {string|null} docId The ID of the document minus its type - this is only needed if looking
* for a singular document.
* @param {object} otherProps Add any other properties onto the request, e.g. include_docs.
* @returns {object} Parameters which can then be used with an allDocs request.
*/
function getDocParams(docType, docId = null, otherProps = {}) {
if (docId == null) {
docId = ""
}
return {
...otherProps,
startkey: `${docType}${SEPARATOR}${docId}`,
endkey: `${docType}${SEPARATOR}${docId}${UNICODE_MAX}`,
}
}
/** /**
* Generates a new group ID. * Generates a new group ID.
* @returns {string} The new group ID which the group doc can be stored under. * @returns {string} The new group ID which the group doc can be stored under.
@ -101,6 +126,51 @@ exports.getTemplateParams = (ownerId, templateId, otherProps = {}) => {
} }
} }
/**
* Generates a new role ID.
* @returns {string} The new role ID which the role doc can be stored under.
*/
exports.generateRoleID = id => {
return `${DocumentTypes.ROLE}${SEPARATOR}${id || newid()}`
}
/**
* Gets parameters for retrieving a role, this is a utility function for the getDocParams function.
*/
exports.getRoleParams = (roleId = null, otherProps = {}) => {
return getDocParams(DocumentTypes.ROLE, roleId, otherProps)
}
/**
* Lots of different points in the system need to find the full list of apps, this will
* enumerate the entire CouchDB cluster and get the list of databases (every app).
* NOTE: this operation is fine in self hosting, but cannot be used when hosting many
* different users/companies apps as there is no security around it - all apps are returned.
* @return {Promise<object[]>} returns the app information document stored in each app database.
*/
exports.getAllApps = async (devApps = false) => {
const CouchDB = getCouch()
let allDbs = await CouchDB.allDbs()
const appDbNames = allDbs.filter(dbName =>
dbName.startsWith(exports.APP_PREFIX)
)
const appPromises = appDbNames.map(db => new CouchDB(db).get(DocumentTypes.APP_METADATA))
if (appPromises.length === 0) {
return []
} else {
const response = await Promise.allSettled(appPromises)
const apps = response
.filter(result => result.status === "fulfilled")
.map(({ value }) => value)
return apps.filter(app => {
if (devApps) {
return app.appId.startsWith(exports.APP_DEV_PREFIX)
}
return !app.appId.startsWith(exports.APP_DEV_PREFIX)
})
}
}
/** /**
* Generates a new configuration ID. * Generates a new configuration ID.
* @returns {string} The new configuration ID which the config doc can be stored under. * @returns {string} The new configuration ID which the config doc can be stored under.

View File

@ -126,7 +126,13 @@ exports.makeSureBucketExists = async (client, bucketName) => {
* Uploads the contents of a file given the required parameters, useful when * Uploads the contents of a file given the required parameters, useful when
* temp files in use (for example file uploaded as an attachment). * temp files in use (for example file uploaded as an attachment).
*/ */
exports.upload = async ({ bucket: bucketName, filename, path, type, metadata }) => { exports.upload = async ({
bucket: bucketName,
filename,
path,
type,
metadata,
}) => {
const extension = [...filename.split(".")].pop() const extension = [...filename.split(".")].pop()
const fileBytes = fs.readFileSync(path) const fileBytes = fs.readFileSync(path)

View File

@ -1,7 +1,12 @@
const CouchDB = require("../../db") const { getDB } = require("../db")
const { cloneDeep } = require("lodash/fp") const { cloneDeep } = require("lodash/fp")
const { BUILTIN_PERMISSION_IDS, higherPermission } = require("./permissions") const { BUILTIN_PERMISSION_IDS, higherPermission } = require("./permissions")
const { generateRoleID, DocumentTypes, SEPARATOR } = require("../../db/utils") const {
generateRoleID,
getRoleParams,
DocumentTypes,
SEPARATOR,
} = require("../db/utils")
const BUILTIN_IDS = { const BUILTIN_IDS = {
ADMIN: "ADMIN", ADMIN: "ADMIN",
@ -11,6 +16,14 @@ const BUILTIN_IDS = {
BUILDER: "BUILDER", BUILDER: "BUILDER",
} }
// exclude internal roles like builder
const EXTERNAL_BUILTIN_ROLE_IDS = [
BUILTIN_IDS.ADMIN,
BUILTIN_IDS.POWER,
BUILTIN_IDS.BASIC,
BUILTIN_IDS.PUBLIC,
]
function Role(id, name) { function Role(id, name) {
this._id = id this._id = id
this.name = name this.name = name
@ -116,7 +129,7 @@ exports.getRole = async (appId, roleId) => {
) )
} }
try { try {
const db = new CouchDB(appId) const db = getDB(appId)
const dbRole = await db.get(exports.getDBRoleID(roleId)) const dbRole = await db.get(exports.getDBRoleID(roleId))
role = Object.assign(role, dbRole) role = Object.assign(role, dbRole)
// finalise the ID // finalise the ID
@ -192,6 +205,39 @@ exports.getUserPermissions = async (appId, userRoleId) => {
} }
} }
/**
* Given an app ID this will retrieve all of the roles that are currently within that app.
* @param {string} appId The ID of the app to retrieve the roles from.
* @return {Promise<object[]>} An array of the role objects that were found.
*/
exports.getAllRoles = async appId => {
const db = getDB(appId)
const body = await db.allDocs(
getRoleParams(null, {
include_docs: true,
})
)
let roles = body.rows.map(row => row.doc)
const builtinRoles = exports.getBuiltinRoles()
// need to combine builtin with any DB record of them (for sake of permissions)
for (let builtinRoleId of EXTERNAL_BUILTIN_ROLE_IDS) {
const builtinRole = builtinRoles[builtinRoleId]
const dbBuiltin = roles.filter(
dbRole => exports.getExternalRoleID(dbRole._id) === builtinRoleId
)[0]
if (dbBuiltin == null) {
roles.push(builtinRole)
} else {
// remove role and all back after combining with the builtin
roles = roles.filter(role => role._id !== dbBuiltin._id)
dbBuiltin._id = exports.getExternalRoleID(dbBuiltin._id)
roles.push(Object.assign(builtinRole, dbBuiltin))
}
}
return roles
}
class AccessController { class AccessController {
constructor(appId) { constructor(appId) {
this.appId = appId this.appId = appId

View File

@ -11,7 +11,7 @@ export const FrontendTypes = {
export const AppStatus = { export const AppStatus = {
DEV: "dev", DEV: "dev",
PUBLISHED: "published" PUBLISHED: "published",
} }
// fields on the user table that cannot be edited // fields on the user table that cannot be edited

View File

@ -20,10 +20,7 @@ const {
DocumentTypes, DocumentTypes,
AppStatus, AppStatus,
} = require("../../db/utils") } = require("../../db/utils")
const { const { BUILTIN_ROLE_IDS, AccessController } = require("@budibase/auth/roles")
BUILTIN_ROLE_IDS,
AccessController,
} = require("../../utilities/security/roles")
const { BASE_LAYOUTS } = require("../../constants/layouts") const { BASE_LAYOUTS } = require("../../constants/layouts")
const { const {
createHomeScreen, createHomeScreen,
@ -120,15 +117,8 @@ async function createInstance(template) {
} }
exports.fetch = async function (ctx) { exports.fetch = async function (ctx) {
let apps = await getAllApps() const isDev = ctx.query && ctx.query.status === AppStatus.DEV
const apps = await getAllApps(isDev)
const isDev = ctx.query.status === AppStatus.DEV
apps = apps.filter(app => {
if (isDev) {
return app.appId.startsWith(DocumentTypes.APP_DEV)
}
return !app.appId.startsWith(DocumentTypes.APP_DEV)
})
// get the locks for all the dev apps // get the locks for all the dev apps
if (isDev) { if (isDev) {

View File

@ -1,6 +1,6 @@
const PouchDB = require("../../../db") const PouchDB = require("../../../db")
const Deployment = require("./Deployment") const Deployment = require("./Deployment")
const { Replication, StaticDatabases } = require("@budibase/auth").db const { Replication, StaticDatabases } = require("@budibase/auth/db")
const { DocumentTypes } = require("../../../db/utils") const { DocumentTypes } = require("../../../db/utils")
// the max time we can wait for an invalidation to complete before considering it failed // the max time we can wait for an invalidation to complete before considering it failed

View File

@ -1,6 +1,6 @@
const CouchDB = require("../../db") const CouchDB = require("../../db")
const { getDeployedApps } = require("../../utilities/workerRequests") const { getDeployedApps } = require("../../utilities/workerRequests")
const { getScopedConfig } = require("@budibase/auth").db const { getScopedConfig } = require("@budibase/auth/db")
const { Configs } = require("@budibase/auth").constants const { Configs } = require("@budibase/auth").constants
const { checkSlashesInUrl } = require("../../utilities") const { checkSlashesInUrl } = require("../../utilities")

View File

@ -3,19 +3,19 @@ const {
PermissionLevels, PermissionLevels,
isPermissionLevelHigherThanRead, isPermissionLevelHigherThanRead,
higherPermission, higherPermission,
} = require("../../utilities/security/permissions") } = require("@budibase/auth/permissions")
const { const {
isBuiltin, isBuiltin,
getDBRoleID, getDBRoleID,
getExternalRoleID, getExternalRoleID,
getBuiltinRoles, getBuiltinRoles,
} = require("../../utilities/security/roles") } = require("@budibase/auth/roles")
const { getRoleParams } = require("../../db/utils") const { getRoleParams } = require("../../db/utils")
const CouchDB = require("../../db") const CouchDB = require("../../db")
const { const {
CURRENTLY_SUPPORTED_LEVELS, CURRENTLY_SUPPORTED_LEVELS,
getBasePermissions, getBasePermissions,
} = require("../../utilities/security/utilities") } = require("../../utilities/security")
const PermissionUpdateType = { const PermissionUpdateType = {
REMOVE: "remove", REMOVE: "remove",

View File

@ -1,12 +1,12 @@
const CouchDB = require("../../db") const CouchDB = require("../../db")
const { const {
getBuiltinRoles, getBuiltinRoles,
BUILTIN_ROLE_IDS,
Role, Role,
getRole, getRole,
isBuiltin, isBuiltin,
getExternalRoleID, getExternalRoleID,
} = require("../../utilities/security/roles") getAllRoles,
} = require("@budibase/auth/roles")
const { const {
generateRoleID, generateRoleID,
getRoleParams, getRoleParams,
@ -19,14 +19,6 @@ const UpdateRolesOptions = {
REMOVED: "removed", REMOVED: "removed",
} }
// exclude internal roles like builder
const EXTERNAL_BUILTIN_ROLE_IDS = [
BUILTIN_ROLE_IDS.ADMIN,
BUILTIN_ROLE_IDS.POWER,
BUILTIN_ROLE_IDS.BASIC,
BUILTIN_ROLE_IDS.PUBLIC,
]
async function updateRolesOnUserTable(db, roleId, updateOption) { async function updateRolesOnUserTable(db, roleId, updateOption) {
const table = await db.get(InternalTables.USER_METADATA) const table = await db.get(InternalTables.USER_METADATA)
const schema = table.schema const schema = table.schema
@ -51,31 +43,7 @@ async function updateRolesOnUserTable(db, roleId, updateOption) {
} }
exports.fetch = async function (ctx) { exports.fetch = async function (ctx) {
const db = new CouchDB(ctx.appId) ctx.body = await getAllRoles(ctx.appId)
const body = await db.allDocs(
getRoleParams(null, {
include_docs: true,
})
)
let roles = body.rows.map(row => row.doc)
const builtinRoles = getBuiltinRoles()
// need to combine builtin with any DB record of them (for sake of permissions)
for (let builtinRoleId of EXTERNAL_BUILTIN_ROLE_IDS) {
const builtinRole = builtinRoles[builtinRoleId]
const dbBuiltin = roles.filter(
dbRole => getExternalRoleID(dbRole._id) === builtinRoleId
)[0]
if (dbBuiltin == null) {
roles.push(builtinRole)
} else {
// remove role and all back after combining with the builtin
roles = roles.filter(role => role._id !== dbBuiltin._id)
dbBuiltin._id = getExternalRoleID(dbBuiltin._id)
roles.push(Object.assign(builtinRole, dbBuiltin))
}
}
ctx.body = roles
} }
exports.find = async function (ctx) { exports.find = async function (ctx) {

View File

@ -2,7 +2,7 @@ const { getRoutingInfo } = require("../../utilities/routing")
const { const {
getUserRoleHierarchy, getUserRoleHierarchy,
BUILTIN_ROLE_IDS, BUILTIN_ROLE_IDS,
} = require("../../utilities/security/roles") } = require("@budibase/auth/roles")
const URL_SEPARATOR = "/" const URL_SEPARATOR = "/"

View File

@ -1,6 +1,6 @@
const CouchDB = require("../../db") const CouchDB = require("../../db")
const { getScreenParams, generateScreenID } = require("../../db/utils") const { getScreenParams, generateScreenID } = require("../../db/utils")
const { AccessController } = require("../../utilities/security/roles") const { AccessController } = require("@budibase/auth/roles")
exports.fetch = async ctx => { exports.fetch = async ctx => {
const appId = ctx.appId const appId = ctx.appId

View File

@ -5,7 +5,7 @@ const {
getGlobalIDFromUserMetadataID, getGlobalIDFromUserMetadataID,
} = require("../../db/utils") } = require("../../db/utils")
const { InternalTables } = require("../../db/utils") const { InternalTables } = require("../../db/utils")
const { getRole, BUILTIN_ROLE_IDS } = require("../../utilities/security/roles") const { getRole, BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { const {
getGlobalUsers, getGlobalUsers,
saveGlobalUser, saveGlobalUser,

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 controller = require("../controllers/analytics") const controller = require("../controllers/analytics")
const { BUILDER } = require("../../utilities/security/permissions") const { BUILDER } = require("@budibase/auth/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/security/permissions") const { BUILDER } = require("@budibase/auth/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/security/permissions") const { BUILDER } = require("@budibase/auth/permissions")
const router = Router() const router = Router()

View File

@ -6,7 +6,7 @@ const {
BUILDER, BUILDER,
PermissionLevels, PermissionLevels,
PermissionTypes, PermissionTypes,
} = require("../../utilities/security/permissions") } = require("@budibase/auth/permissions")
const Joi = require("joi") const Joi = require("joi")
const { bodyResource, paramResource } = require("../../middleware/resourceId") const { bodyResource, paramResource } = require("../../middleware/resourceId")

View File

@ -1,7 +1,7 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../controllers/backup") const controller = require("../controllers/backup")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/security/permissions") const { BUILDER } = require("@budibase/auth/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/component") const controller = require("../controllers/component")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/security/permissions") const { BUILDER } = require("@budibase/auth/permissions")
const router = Router() const router = Router()

View File

@ -5,7 +5,7 @@ const {
BUILDER, BUILDER,
PermissionLevels, PermissionLevels,
PermissionTypes, PermissionTypes,
} = require("../../utilities/security/permissions") } = require("@budibase/auth/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/security/permissions") const { BUILDER } = require("@budibase/auth/permissions")
const router = Router() const router = Router()

View File

@ -2,7 +2,7 @@ const Router = require("@koa/router")
const controller = require("../controllers/dev") const controller = require("../controllers/dev")
const env = require("../../environment") const env = require("../../environment")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/security/permissions") const { BUILDER } = require("@budibase/auth/permissions")
const router = Router() const router = Router()

View File

@ -2,7 +2,7 @@ const Router = require("@koa/router")
const controller = require("../controllers/hosting") const controller = require("../controllers/hosting")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const selfhost = require("../../middleware/selfhost") const selfhost = require("../../middleware/selfhost")
const { BUILDER } = require("../../utilities/security/permissions") const { BUILDER } = require("@budibase/auth/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/integration") const controller = require("../controllers/integration")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/security/permissions") const { BUILDER } = require("@budibase/auth/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/security/permissions") const { BUILDER } = require("@budibase/auth/permissions")
const controller = require("../controllers/layout") const controller = require("../controllers/layout")
const router = Router() const router = Router()

View File

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

View File

@ -1,12 +1,12 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const queryController = require("../controllers/query") const queryController = require("../controllers/query")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/security/permissions")
const Joi = require("joi") const Joi = require("joi")
const { const {
PermissionLevels, PermissionLevels,
PermissionTypes, PermissionTypes,
} = require("../../utilities/security/permissions") BUILDER,
} = require("@budibase/auth/permissions")
const joiValidator = require("../../middleware/joi-validator") const joiValidator = require("../../middleware/joi-validator")
const { const {
bodyResource, bodyResource,

View File

@ -1,15 +1,13 @@
const Router = require("@koa/router") 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,
PermissionLevels,
} = require("../../utilities/security/permissions")
const Joi = require("joi") const Joi = require("joi")
const joiValidator = require("../../middleware/joi-validator") const joiValidator = require("../../middleware/joi-validator")
const { const {
BUILTIN_PERMISSION_IDS, BUILTIN_PERMISSION_IDS,
} = require("../../utilities/security/permissions") BUILDER,
PermissionLevels,
} = require("@budibase/auth/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/security/permissions") const { BUILDER } = require("@budibase/auth/permissions")
const controller = require("../controllers/routing") const controller = require("../controllers/routing")
const router = Router() const router = Router()

View File

@ -9,7 +9,7 @@ const {
const { const {
PermissionLevels, PermissionLevels,
PermissionTypes, PermissionTypes,
} = require("../../utilities/security/permissions") } = require("@budibase/auth/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/screen") const controller = require("../controllers/screen")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/security/permissions") const { BUILDER } = require("@budibase/auth/permissions")
const joiValidator = require("../../middleware/joi-validator") const joiValidator = require("../../middleware/joi-validator")
const Joi = require("joi") const Joi = require("joi")

View File

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

View File

@ -3,7 +3,7 @@ const controller = require("../controllers/search")
const { const {
PermissionTypes, PermissionTypes,
PermissionLevels, PermissionLevels,
} = require("../../utilities/security/permissions") } = require("@budibase/auth/permissions")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { paramResource } = require("../../middleware/resourceId") const { paramResource } = require("../../middleware/resourceId")

View File

@ -6,7 +6,7 @@ const {
BUILDER, BUILDER,
PermissionTypes, PermissionTypes,
PermissionLevels, PermissionLevels,
} = require("../../utilities/security/permissions") } = require("@budibase/auth/permissions")
const usage = require("../../middleware/usageQuota") const usage = require("../../middleware/usageQuota")
const env = require("../../environment") const env = require("../../environment")

View File

@ -6,7 +6,7 @@ const {
BUILDER, BUILDER,
PermissionLevels, PermissionLevels,
PermissionTypes, PermissionTypes,
} = require("../../utilities/security/permissions") } = require("@budibase/auth/permissions")
const joiValidator = require("../../middleware/joi-validator") const joiValidator = require("../../middleware/joi-validator")
const Joi = require("joi") const Joi = require("joi")

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/security/permissions") const { BUILDER } = require("@budibase/auth/permissions")
const router = Router() const router = Router()

View File

@ -1,6 +1,14 @@
const { clearAllApps, checkBuilderEndpoint } = require("./utilities/TestFunctions") const { clearAllApps, checkBuilderEndpoint } = require("./utilities/TestFunctions")
const setup = require("./utilities") const setup = require("./utilities")
jest.mock("../../../utilities/redis", () => ({
init: jest.fn(),
getAllLocks: () => {
return []
},
updateLock: jest.fn(),
}))
describe("/applications", () => { describe("/applications", () => {
let request = setup.getRequest() let request = setup.getRequest()
let config = setup.getConfig() let config = setup.getConfig()
@ -40,7 +48,7 @@ describe("/applications", () => {
await config.createApp(request, "app2") await config.createApp(request, "app2")
const res = await request const res = await request
.get("/api/applications") .get("/api/applications?status=dev")
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)

View File

@ -1,4 +1,4 @@
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const setup = require("./utilities") const setup = require("./utilities")
const { basicRow } = setup.structures const { basicRow } = setup.structures

View File

@ -1,7 +1,7 @@
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { const {
BUILTIN_PERMISSION_IDS, BUILTIN_PERMISSION_IDS,
} = require("../../../utilities/security/permissions") } = require("@budibase/auth/permissions")
const setup = require("./utilities") const setup = require("./utilities")
const { basicRole } = setup.structures const { basicRole } = setup.structures

View File

@ -1,7 +1,7 @@
const setup = require("./utilities") const setup = require("./utilities")
const { basicScreen } = setup.structures const { basicScreen } = setup.structures
const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const workerRequests = require("../../../utilities/workerRequests") const workerRequests = require("../../../utilities/workerRequests")
const route = "/test" const route = "/test"

View File

@ -1,4 +1,4 @@
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { checkPermissionsEndpoint } = require("./utilities/TestFunctions") const { checkPermissionsEndpoint } = require("./utilities/TestFunctions")
const setup = require("./utilities") const setup = require("./utilities")
const { basicUser } = setup.structures const { basicUser } = setup.structures

View File

@ -14,7 +14,7 @@ exports.getAllTableRows = async config => {
} }
exports.clearAllApps = async () => { exports.clearAllApps = async () => {
const req = {} const req = { query: { status: "dev" } }
await appController.fetch(req) await appController.fetch(req)
const apps = req.body const apps = req.body
if (!apps || apps.length <= 0) { if (!apps || apps.length <= 0) {

View File

@ -4,7 +4,7 @@ const authorized = require("../../middleware/authorized")
const { const {
PermissionLevels, PermissionLevels,
PermissionTypes, PermissionTypes,
} = require("../../utilities/security/permissions") } = require("@budibase/auth/permissions")
const usage = require("../../middleware/usageQuota") const usage = require("../../middleware/usageQuota")
const router = Router() const router = Router()

View File

@ -7,7 +7,7 @@ const {
BUILDER, BUILDER,
PermissionTypes, PermissionTypes,
PermissionLevels, PermissionLevels,
} = require("../../utilities/security/permissions") } = require("@budibase/auth/permissions")
const usage = require("../../middleware/usageQuota") const usage = require("../../middleware/usageQuota")
const router = Router() const router = Router()

View File

@ -2,7 +2,7 @@ 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 } = require("../../utilities/security/permissions") const { BUILDER } = require("@budibase/auth/permissions")
const Joi = require("joi") const Joi = require("joi")
const router = Router() const router = Router()

View File

@ -1,4 +1,4 @@
const roles = require("../../utilities/security/roles") const roles = require("@budibase/auth/roles")
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")

View File

@ -1,6 +1,6 @@
const usageQuota = require("../../utilities/usageQuota") const usageQuota = require("../../utilities/usageQuota")
const setup = require("./utilities") const setup = require("./utilities")
const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { InternalTables } = require("../../db/utils") const { InternalTables } = require("../../db/utils")
jest.mock("../../utilities/usageQuota") jest.mock("../../utilities/usageQuota")

View File

@ -6,7 +6,7 @@ const Queue = env.isTest()
: require("bull") : require("bull")
const { getAutomationParams } = require("../db/utils") const { getAutomationParams } = require("../db/utils")
const { coerce } = require("../utilities/rowProcessor") const { coerce } = require("../utilities/rowProcessor")
const { utils } = require("@budibase/auth").redis const { utils } = require("@budibase/auth/redis")
const { opts } = utils.getRedisOptions() const { opts } = utils.getRedisOptions()
let automationQueue = new Queue("automationQueue", { redis: opts }) let automationQueue = new Queue("automationQueue", { redis: opts })

View File

@ -1,4 +1,4 @@
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { UserStatus } = require("@budibase/auth").constants const { UserStatus } = require("@budibase/auth").constants
const { ObjectStoreBuckets } = require("@budibase/auth").objectStore const { ObjectStoreBuckets } = require("@budibase/auth").objectStore

View File

@ -1,4 +1,4 @@
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { BASE_LAYOUT_PROP_IDS } = require("./layouts") const { BASE_LAYOUT_PROP_IDS } = require("./layouts")
const { LOGO_URL } = require("../constants") const { LOGO_URL } = require("../constants")

View File

@ -1,10 +1,12 @@
const newid = require("./newid") const newid = require("./newid")
const { const {
DocumentTypes: CoreDocTypes, DocumentTypes: CoreDocTypes,
getRoleParams,
generateRoleID,
APP_DEV_PREFIX, APP_DEV_PREFIX,
APP_PREFIX, APP_PREFIX,
SEPARATOR, SEPARATOR,
} = require("@budibase/auth").db } = require("@budibase/auth/db")
const UNICODE_MAX = "\ufff0" const UNICODE_MAX = "\ufff0"
@ -24,12 +26,12 @@ const DocumentTypes = {
APP: CoreDocTypes.APP, APP: CoreDocTypes.APP,
APP_DEV: CoreDocTypes.APP_DEV, APP_DEV: CoreDocTypes.APP_DEV,
APP_METADATA: CoreDocTypes.APP_METADATA, APP_METADATA: CoreDocTypes.APP_METADATA,
ROLE: CoreDocTypes.ROLE,
TABLE: "ta", TABLE: "ta",
ROW: "ro", ROW: "ro",
USER: "us", USER: "us",
AUTOMATION: "au", AUTOMATION: "au",
LINK: "li", LINK: "li",
ROLE: "role",
WEBHOOK: "wh", WEBHOOK: "wh",
INSTANCE: "inst", INSTANCE: "inst",
LAYOUT: "layout", LAYOUT: "layout",
@ -62,6 +64,9 @@ exports.UNICODE_MAX = UNICODE_MAX
exports.SearchIndexes = SearchIndexes exports.SearchIndexes = SearchIndexes
exports.AppStatus = AppStatus exports.AppStatus = AppStatus
exports.generateRoleID = generateRoleID
exports.getRoleParams = getRoleParams
exports.getQueryIndex = viewName => { exports.getQueryIndex = viewName => {
return `database/${viewName}` return `database/${viewName}`
} }
@ -225,21 +230,6 @@ exports.generateDevAppID = appId => {
return `${DocumentTypes.APP_DEV}${SEPARATOR}${uuid}` return `${DocumentTypes.APP_DEV}${SEPARATOR}${uuid}`
} }
/**
* Generates a new role ID.
* @returns {string} The new role ID which the role doc can be stored under.
*/
exports.generateRoleID = id => {
return `${DocumentTypes.ROLE}${SEPARATOR}${id || newid()}`
}
/**
* Gets parameters for retrieving a role, this is a utility function for the getDocParams function.
*/
exports.getRoleParams = (roleId = null, otherProps = {}) => {
return getDocParams(DocumentTypes.ROLE, roleId, otherProps)
}
/** /**
* Generates a new layout ID. * Generates a new layout ID.
* @returns {string} The new layout ID which the layout doc can be stored under. * @returns {string} The new layout ID which the layout doc can be stored under.

View File

@ -1,9 +1,9 @@
const { getUserPermissions } = require("../utilities/security/roles") const { getUserPermissions } = require("@budibase/auth/roles")
const { const {
PermissionTypes, PermissionTypes,
doesHaveResourcePermission, doesHaveResourcePermission,
doesHaveBasePermission, doesHaveBasePermission,
} = require("../utilities/security/permissions") } = require("@budibase/auth/permissions")
const { APP_DEV_PREFIX } = require("../db/utils") const { APP_DEV_PREFIX } = require("../db/utils")
const { doesUserHaveLock, updateLock } = require("../utilities/redis") const { doesUserHaveLock, updateLock } = require("../utilities/redis")

View File

@ -1,8 +1,8 @@
const { getAppId, setCookie, getCookie } = require("@budibase/auth").utils const { getAppId, setCookie, getCookie } = require("@budibase/auth").utils
const { Cookies } = require("@budibase/auth").constants const { Cookies } = require("@budibase/auth").constants
const { getRole } = require("../utilities/security/roles") const { getRole } = require("@budibase/auth/roles")
const { getGlobalUsers } = require("../utilities/workerRequests") const { getGlobalUsers } = require("../utilities/workerRequests")
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { generateUserMetadataID } = require("../db/utils") const { generateUserMetadataID } = require("../db/utils")
module.exports = async (ctx, next) => { module.exports = async (ctx, next) => {

View File

@ -1,6 +1,6 @@
const authorizedMiddleware = require("../authorized") const authorizedMiddleware = require("../authorized")
const env = require("../../environment") const env = require("../../environment")
const { PermissionTypes, PermissionLevels } = require("../../utilities/security/permissions") const { PermissionTypes, PermissionLevels } = require("@budibase/auth/permissions")
jest.mock("../../environment", () => ({ jest.mock("../../environment", () => ({
prod: false, prod: false,
isTest: () => true, isTest: () => true,

View File

@ -8,7 +8,8 @@ function mockWorker() {
_id: "us_uuid1", _id: "us_uuid1",
roles: { roles: {
"app_test": "BASIC", "app_test": "BASIC",
} },
roleId: "BASIC",
} }
} }
})) }))

View File

@ -1,7 +1,6 @@
const selfHostMiddleware = require("../selfhost") const selfHostMiddleware = require("../selfhost")
const env = require("../../environment") const env = require("../../environment")
jest.mock("../../environment") jest.mock("../../environment")
jest.mock("../../utilities/builder/hosting")
class TestConfiguration { class TestConfiguration {
constructor() { constructor() {

View File

@ -1,4 +1,4 @@
const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const env = require("../../environment") const env = require("../../environment")
const { const {
basicTable, basicTable,
@ -16,7 +16,7 @@ const supertest = require("supertest")
const { cleanup } = require("../../utilities/fileSystem") const { cleanup } = require("../../utilities/fileSystem")
const { Cookies } = require("@budibase/auth").constants const { Cookies } = require("@budibase/auth").constants
const { jwt } = require("@budibase/auth").auth const { jwt } = require("@budibase/auth").auth
const { StaticDatabases } = require("@budibase/auth").db const { StaticDatabases } = require("@budibase/auth/db")
const CouchDB = require("../../db") const CouchDB = require("../../db")
const GLOBAL_USER_ID = "us_uuid1" const GLOBAL_USER_ID = "us_uuid1"

View File

@ -1,7 +1,5 @@
const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { const { BUILTIN_PERMISSION_IDS } = require("@budibase/auth/permissions")
BUILTIN_PERMISSION_IDS,
} = require("../../utilities/security/permissions")
const { createHomeScreen } = require("../../constants/screens") const { createHomeScreen } = require("../../constants/screens")
const { EMPTY_LAYOUT } = require("../../constants/layouts") const { EMPTY_LAYOUT } = require("../../constants/layouts")
const { cloneDeep } = require("lodash/fp") const { cloneDeep } = require("lodash/fp")

View File

@ -1,36 +1,13 @@
const env = require("../environment") const env = require("../environment")
const { APP_PREFIX, DocumentTypes } = require("../db/utils")
const CouchDB = require("../db")
const { OBJ_STORE_DIRECTORY, ObjectStoreBuckets } = require("../constants") const { OBJ_STORE_DIRECTORY, ObjectStoreBuckets } = require("../constants")
const { getAllApps } = require("@budibase/auth/db")
const BB_CDN = "https://cdn.app.budi.live/assets" const BB_CDN = "https://cdn.app.budi.live/assets"
exports.wait = ms => new Promise(resolve => setTimeout(resolve, ms)) exports.wait = ms => new Promise(resolve => setTimeout(resolve, ms))
exports.isDev = env.isDev exports.isDev = env.isDev
exports.getAllApps = getAllApps
/**
* Lots of different points in the app need to find the full list of apps, this will
* enumerate the entire CouchDB cluster and get the list of databases (every app).
* NOTE: this operation is fine in self hosting, but cannot be used when hosting many
* different users/companies apps as there is no security around it - all apps are returned.
* @return {Promise<object[]>} returns the app information document stored in each app database.
*/
exports.getAllApps = async () => {
let allDbs = await CouchDB.allDbs()
const appDbNames = allDbs.filter(dbName => dbName.startsWith(APP_PREFIX))
const appPromises = appDbNames.map(db =>
new CouchDB(db).get(DocumentTypes.APP_METADATA)
)
if (appPromises.length === 0) {
return []
} else {
const response = await Promise.allSettled(appPromises)
return response
.filter(result => result.status === "fulfilled")
.map(({ value }) => value)
}
}
/** /**
* Makes sure that a URL has the correct number of slashes, while maintaining the * Makes sure that a URL has the correct number of slashes, while maintaining the

View File

@ -1,4 +1,4 @@
const { Client, utils } = require("@budibase/auth").redis const { Client, utils } = require("@budibase/auth/redis")
const { getGlobalIDFromUserMetadataID } = require("../db/utils") const { getGlobalIDFromUserMetadataID } = require("../db/utils")
const APP_DEV_LOCK_SECONDS = 600 const APP_DEV_LOCK_SECONDS = 600

View File

@ -3,12 +3,9 @@ const {
PermissionTypes, PermissionTypes,
getBuiltinPermissionByID, getBuiltinPermissionByID,
isPermissionLevelHigherThanRead, isPermissionLevelHigherThanRead,
} = require("../../utilities/security/permissions") } = require("@budibase/auth/permissions")
const { const { lowerBuiltinRoleID, getBuiltinRoles } = require("@budibase/auth/roles")
lowerBuiltinRoleID, const { DocumentTypes } = require("../db/utils")
getBuiltinRoles,
} = require("../../utilities/security/roles")
const { DocumentTypes } = require("../../db/utils")
const CURRENTLY_SUPPORTED_LEVELS = [ const CURRENTLY_SUPPORTED_LEVELS = [
PermissionLevels.WRITE, PermissionLevels.WRITE,

View File

@ -1,7 +1,7 @@
const fetch = require("node-fetch") const fetch = require("node-fetch")
const env = require("../environment") const env = require("../environment")
const { checkSlashesInUrl } = require("./index") const { checkSlashesInUrl } = require("./index")
const { BUILTIN_ROLE_IDS } = require("./security/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
function getAppRole(appId, user) { function getAppRole(appId, user) {
if (!user.roles) { if (!user.roles) {

View File

@ -0,0 +1,24 @@
const { getAllRoles } = require("@budibase/auth/roles")
const { getAllApps } = require("@budibase/auth/db")
exports.fetch = async ctx => {
// always use the dev apps as they'll be most up to date (true)
const apps = await getAllApps(true)
const promises = []
for (let app of apps) {
promises.push(getAllRoles(app._id))
}
const roles = await Promise.all(promises)
const response = {}
for (let app of apps) {
response[app._id] = roles.shift()
}
ctx.body = response
}
exports.find = async ctx => {
const appId = ctx.params.appId
ctx.body = {
roles: await getAllRoles(appId),
}
}

View File

@ -0,0 +1,10 @@
const Router = require("@koa/router")
const controller = require("../../controllers/admin/roles")
const router = Router()
router
.get("/api/admin/roles", controller.fetch)
.get("/api/admin/roles/:appId", controller.find)
module.exports = router

View File

@ -45,6 +45,7 @@ router
.post("/api/admin/users/init", controller.adminUser) .post("/api/admin/users/init", controller.adminUser)
.delete("/api/admin/users/:id", controller.destroy) .delete("/api/admin/users/:id", controller.destroy)
.get("/api/admin/users/:id", controller.find) .get("/api/admin/users/:id", controller.find)
.get("/api/admin/roles/:appId")
.post("/api/admin/users/invite", buildInviteValidation(), controller.invite) .post("/api/admin/users/invite", buildInviteValidation(), controller.invite)
.post( .post(
"/api/admin/users/invite/accept", "/api/admin/users/invite/accept",

View File

@ -4,6 +4,7 @@ const groupRoutes = require("./admin/groups")
const templateRoutes = require("./admin/templates") const templateRoutes = require("./admin/templates")
const emailRoutes = require("./admin/email") const emailRoutes = require("./admin/email")
const authRoutes = require("./admin/auth") const authRoutes = require("./admin/auth")
const roleRoutes = require("./admin/roles")
const appRoutes = require("./app") const appRoutes = require("./app")
exports.routes = [ exports.routes = [
@ -14,4 +15,5 @@ exports.routes = [
appRoutes, appRoutes,
templateRoutes, templateRoutes,
emailRoutes, emailRoutes,
roleRoutes,
] ]

View File

@ -1,4 +1,4 @@
const { Client, utils } = require("@budibase/auth").redis const { Client, utils } = require("@budibase/auth/redis")
const { newid } = require("@budibase/auth").utils const { newid } = require("@budibase/auth").utils
function getExpirySecondsForDB(db) { function getExpirySecondsForDB(db) {