Fixing issue with user's being logged in and trying to access other tenants public apps, this work makes sure that users from other tenants will not be 403'd immediately (too aggressive) but instead they will have all other their RBAC roles revoked.
This commit is contained in:
parent
29691637a6
commit
183b6e7b50
|
@ -1,5 +1,20 @@
|
||||||
exports.SEPARATOR = "_"
|
exports.SEPARATOR = "_"
|
||||||
|
|
||||||
|
const PRE_APP = "app"
|
||||||
|
const PRE_DEV = "dev"
|
||||||
|
|
||||||
|
exports.DocumentTypes = {
|
||||||
|
USER: "us",
|
||||||
|
WORKSPACE: "workspace",
|
||||||
|
CONFIG: "config",
|
||||||
|
TEMPLATE: "template",
|
||||||
|
APP: PRE_APP,
|
||||||
|
DEV: PRE_DEV,
|
||||||
|
APP_DEV: `${PRE_APP}${exports.SEPARATOR}${PRE_DEV}`,
|
||||||
|
APP_METADATA: `${PRE_APP}${exports.SEPARATOR}metadata`,
|
||||||
|
ROLE: "role",
|
||||||
|
}
|
||||||
|
|
||||||
exports.StaticDatabases = {
|
exports.StaticDatabases = {
|
||||||
GLOBAL: {
|
GLOBAL: {
|
||||||
name: "global-db",
|
name: "global-db",
|
||||||
|
|
|
@ -2,8 +2,8 @@ const { newid } = require("../hashing")
|
||||||
const Replication = require("./Replication")
|
const Replication = require("./Replication")
|
||||||
const { DEFAULT_TENANT_ID } = require("../constants")
|
const { DEFAULT_TENANT_ID } = require("../constants")
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
const { StaticDatabases, SEPARATOR } = require("./constants")
|
const { StaticDatabases, SEPARATOR, DocumentTypes } = require("./constants")
|
||||||
const { getTenantId } = require("../tenancy")
|
const { getTenantId, getTenantIDFromAppID } = require("../tenancy")
|
||||||
const fetch = require("node-fetch")
|
const fetch = require("node-fetch")
|
||||||
const { getCouch } = require("./index")
|
const { getCouch } = require("./index")
|
||||||
|
|
||||||
|
@ -15,25 +15,11 @@ exports.ViewNames = {
|
||||||
|
|
||||||
exports.StaticDatabases = StaticDatabases
|
exports.StaticDatabases = StaticDatabases
|
||||||
|
|
||||||
const PRE_APP = "app"
|
|
||||||
const PRE_DEV = "dev"
|
|
||||||
|
|
||||||
const DocumentTypes = {
|
|
||||||
USER: "us",
|
|
||||||
WORKSPACE: "workspace",
|
|
||||||
CONFIG: "config",
|
|
||||||
TEMPLATE: "template",
|
|
||||||
APP: PRE_APP,
|
|
||||||
DEV: PRE_DEV,
|
|
||||||
APP_DEV: `${PRE_APP}${SEPARATOR}${PRE_DEV}`,
|
|
||||||
APP_METADATA: `${PRE_APP}${SEPARATOR}metadata`,
|
|
||||||
ROLE: "role",
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.DocumentTypes = DocumentTypes
|
exports.DocumentTypes = DocumentTypes
|
||||||
exports.APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
exports.APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
||||||
exports.APP_DEV = exports.APP_DEV_PREFIX = DocumentTypes.APP_DEV + SEPARATOR
|
exports.APP_DEV = exports.APP_DEV_PREFIX = DocumentTypes.APP_DEV + SEPARATOR
|
||||||
exports.SEPARATOR = SEPARATOR
|
exports.SEPARATOR = SEPARATOR
|
||||||
|
exports.getTenantIDFromAppID = getTenantIDFromAppID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If creating DB allDocs/query params with only a single top level ID this can be used, this
|
* If creating DB allDocs/query params with only a single top level ID this can be used, this
|
||||||
|
@ -70,26 +56,6 @@ function isDevApp(app) {
|
||||||
return exports.isDevAppID(app.appId)
|
return exports.isDevAppID(app.appId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an app ID this will attempt to retrieve the tenant ID from it.
|
|
||||||
* @return {null|string} The tenant ID found within the app ID.
|
|
||||||
*/
|
|
||||||
exports.getTenantIDFromAppID = appId => {
|
|
||||||
if (!appId) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const split = appId.split(SEPARATOR)
|
|
||||||
const hasDev = split[1] === DocumentTypes.DEV
|
|
||||||
if ((hasDev && split.length === 3) || (!hasDev && split.length === 2)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (hasDev) {
|
|
||||||
return split[2]
|
|
||||||
} else {
|
|
||||||
return split[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new workspace ID.
|
* Generates a new workspace ID.
|
||||||
* @returns {string} The new workspace ID which the workspace doc can be stored under.
|
* @returns {string} The new workspace ID which the workspace doc can be stored under.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const { getDB } = require("../db")
|
const { getDB } = require("../db")
|
||||||
const { SEPARATOR, StaticDatabases } = require("../db/constants")
|
const { SEPARATOR, StaticDatabases, DocumentTypes } = require("../db/constants")
|
||||||
const { getTenantId, DEFAULT_TENANT_ID, isMultiTenant } = require("./context")
|
const { getTenantId, DEFAULT_TENANT_ID, isMultiTenant } = require("./context")
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
|
|
||||||
|
@ -117,3 +117,34 @@ exports.getTenantUser = async identifier => {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an app ID this will attempt to retrieve the tenant ID from it.
|
||||||
|
* @return {null|string} The tenant ID found within the app ID.
|
||||||
|
*/
|
||||||
|
exports.getTenantIDFromAppID = appId => {
|
||||||
|
if (!appId) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const split = appId.split(SEPARATOR)
|
||||||
|
const hasDev = split[1] === DocumentTypes.DEV
|
||||||
|
if ((hasDev && split.length === 3) || (!hasDev && split.length === 2)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (hasDev) {
|
||||||
|
return split[2]
|
||||||
|
} else {
|
||||||
|
return split[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.isUserInAppTenant = (appId, user = null) => {
|
||||||
|
let userTenantId
|
||||||
|
if (user) {
|
||||||
|
userTenantId = user.tenantId || DEFAULT_TENANT_ID
|
||||||
|
} else {
|
||||||
|
userTenantId = getTenantId()
|
||||||
|
}
|
||||||
|
const tenantId = exports.getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID
|
||||||
|
return tenantId === userTenantId
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ const CouchDB = require("../../db")
|
||||||
const { outputProcessing } = require("../../utilities/rowProcessor")
|
const { outputProcessing } = require("../../utilities/rowProcessor")
|
||||||
const { InternalTables } = require("../../db/utils")
|
const { InternalTables } = require("../../db/utils")
|
||||||
const { getFullUser } = require("../../utilities/users")
|
const { getFullUser } = require("../../utilities/users")
|
||||||
|
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
|
||||||
|
|
||||||
exports.fetchSelf = async ctx => {
|
exports.fetchSelf = async ctx => {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
|
@ -27,8 +28,15 @@ exports.fetchSelf = async ctx => {
|
||||||
...metadata,
|
...metadata,
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// user didn't exist in app, don't pretend they do
|
||||||
|
if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) {
|
||||||
|
ctx.body = {}
|
||||||
|
}
|
||||||
|
// user has a role of some sort, return them
|
||||||
|
else {
|
||||||
ctx.body = user
|
ctx.body = user
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.body = user
|
ctx.body = user
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ jest.mock("../../../utilities/workerRequests", () => ({
|
||||||
getGlobalSelf: jest.fn(() => {
|
getGlobalSelf: jest.fn(() => {
|
||||||
return {}
|
return {}
|
||||||
}),
|
}),
|
||||||
addAppRoleToUser: jest.fn(),
|
|
||||||
deleteGlobalUser: jest.fn(),
|
deleteGlobalUser: jest.fn(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,12 @@ const { Cookies } = require("@budibase/auth").constants
|
||||||
const { getRole } = require("@budibase/auth/roles")
|
const { getRole } = require("@budibase/auth/roles")
|
||||||
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
|
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
|
||||||
const { generateUserMetadataID } = require("../db/utils")
|
const { generateUserMetadataID } = require("../db/utils")
|
||||||
const { dbExists, getTenantIDFromAppID } = require("@budibase/auth/db")
|
const { dbExists } = require("@budibase/auth/db")
|
||||||
const { getTenantId } = require("@budibase/auth/tenancy")
|
const { isUserInAppTenant } = require("@budibase/auth/tenancy")
|
||||||
const { getCachedSelf } = require("../utilities/global")
|
const { getCachedSelf } = require("../utilities/global")
|
||||||
const CouchDB = require("../db")
|
const CouchDB = require("../db")
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
|
|
||||||
const DEFAULT_TENANT_ID = "default"
|
|
||||||
|
|
||||||
module.exports = async (ctx, next) => {
|
module.exports = async (ctx, next) => {
|
||||||
// try to get the appID from the request
|
// try to get the appID from the request
|
||||||
let requestAppId = getAppId(ctx)
|
let requestAppId = getAppId(ctx)
|
||||||
|
@ -55,13 +53,21 @@ module.exports = async (ctx, next) => {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If user and app tenant Ids do not match, 403
|
let noCookieSet = false
|
||||||
if (env.MULTI_TENANCY && ctx.user) {
|
// if the user not in the right tenant then make sure they have no permissions
|
||||||
const userTenantId = getTenantId()
|
// need to judge this only based on the request app ID,
|
||||||
const tenantId = getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID
|
if (
|
||||||
if (tenantId !== userTenantId) {
|
env.MULTI_TENANCY &&
|
||||||
ctx.throw(403, "Cannot access application.")
|
ctx.user &&
|
||||||
}
|
requestAppId &&
|
||||||
|
!isUserInAppTenant(requestAppId)
|
||||||
|
) {
|
||||||
|
// don't error, simply remove the users rights (they are a public user)
|
||||||
|
delete ctx.user.builder
|
||||||
|
delete ctx.user.admin
|
||||||
|
delete ctx.user.roles
|
||||||
|
roleId = BUILTIN_ROLE_IDS.PUBLIC
|
||||||
|
noCookieSet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.appId = appId
|
ctx.appId = appId
|
||||||
|
@ -78,9 +84,10 @@ module.exports = async (ctx, next) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
requestAppId !== appId ||
|
(requestAppId !== appId ||
|
||||||
appCookie == null ||
|
appCookie == null ||
|
||||||
appCookie.appId !== requestAppId
|
appCookie.appId !== requestAppId) &&
|
||||||
|
!noCookieSet
|
||||||
) {
|
) {
|
||||||
setCookie(ctx, { appId }, Cookies.CurrentApp)
|
setCookie(ctx, { appId }, Cookies.CurrentApp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,20 @@ const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
|
||||||
const { getDeployedAppID } = require("@budibase/auth/db")
|
const { getDeployedAppID } = require("@budibase/auth/db")
|
||||||
const { getGlobalUserParams } = require("@budibase/auth/db")
|
const { getGlobalUserParams } = require("@budibase/auth/db")
|
||||||
const { user: userCache } = require("@budibase/auth/cache")
|
const { user: userCache } = require("@budibase/auth/cache")
|
||||||
const { getGlobalDB } = require("@budibase/auth/tenancy")
|
const { getGlobalDB, isUserInAppTenant } = require("@budibase/auth/tenancy")
|
||||||
|
const env = require("../environment")
|
||||||
|
|
||||||
exports.updateAppRole = (appId, user) => {
|
exports.updateAppRole = (appId, user) => {
|
||||||
if (!user.roles) {
|
if (!user || !user.roles) {
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
// if in an multi-tenancy environment make sure roles are never updated
|
||||||
|
if (env.MULTI_TENANCY && !isUserInAppTenant(appId, user)) {
|
||||||
|
delete user.builder
|
||||||
|
delete user.admin
|
||||||
|
user.roleId = BUILTIN_ROLE_IDS.PUBLIC
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
// always use the deployed app
|
// always use the deployed app
|
||||||
user.roleId = user.roles[getDeployedAppID(appId)]
|
user.roleId = user.roles[getDeployedAppID(appId)]
|
||||||
// if a role wasn't found then either set as admin (builder) or public (everyone else)
|
// if a role wasn't found then either set as admin (builder) or public (everyone else)
|
||||||
|
|
|
@ -2,7 +2,7 @@ const fetch = require("node-fetch")
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
const { checkSlashesInUrl } = require("./index")
|
const { checkSlashesInUrl } = require("./index")
|
||||||
const { getDeployedAppID } = require("@budibase/auth/db")
|
const { getDeployedAppID } = require("@budibase/auth/db")
|
||||||
const { updateAppRole, getGlobalUser } = require("./global")
|
const { updateAppRole } = require("./global")
|
||||||
const { Headers } = require("@budibase/auth/constants")
|
const { Headers } = require("@budibase/auth/constants")
|
||||||
const { getTenantId, isTenantIdSet } = require("@budibase/auth/tenancy")
|
const { getTenantId, isTenantIdSet } = require("@budibase/auth/tenancy")
|
||||||
|
|
||||||
|
@ -98,38 +98,6 @@ exports.getGlobalSelf = async (ctx, appId = null) => {
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.addAppRoleToUser = async (ctx, appId, roleId, userId = null) => {
|
|
||||||
let user,
|
|
||||||
endpoint,
|
|
||||||
body = {}
|
|
||||||
if (!userId) {
|
|
||||||
user = await exports.getGlobalSelf(ctx)
|
|
||||||
endpoint = `/api/global/users/self`
|
|
||||||
} else {
|
|
||||||
user = await getGlobalUser(appId, userId)
|
|
||||||
body._id = userId
|
|
||||||
endpoint = `/api/global/users`
|
|
||||||
}
|
|
||||||
body = {
|
|
||||||
...body,
|
|
||||||
roles: {
|
|
||||||
...user.roles,
|
|
||||||
[getDeployedAppID(appId)]: roleId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const response = await fetch(
|
|
||||||
checkSlashesInUrl(env.WORKER_URL + endpoint),
|
|
||||||
request(ctx, {
|
|
||||||
method: "POST",
|
|
||||||
body,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
if (response.status !== 200) {
|
|
||||||
ctx.throw(400, "Unable to save self globally.")
|
|
||||||
}
|
|
||||||
return response.json()
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.removeAppFromUserRoles = async (ctx, appId) => {
|
exports.removeAppFromUserRoles = async (ctx, appId) => {
|
||||||
const deployedAppId = getDeployedAppID(appId)
|
const deployedAppId = getDeployedAppID(appId)
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
|
|
Loading…
Reference in New Issue