Re-writing how global users are handled in server, specifically how they are retrieved, so that for relationships it can handle the global user.
This commit is contained in:
parent
1e852cabbb
commit
594a290286
|
@ -5,9 +5,9 @@ const {
|
||||||
} = require("../../db/utils")
|
} = require("../../db/utils")
|
||||||
const { InternalTables } = require("../../db/utils")
|
const { InternalTables } = require("../../db/utils")
|
||||||
const {
|
const {
|
||||||
getGlobalUsers,
|
|
||||||
addAppRoleToUser,
|
addAppRoleToUser,
|
||||||
} = require("../../utilities/workerRequests")
|
} = require("../../utilities/workerRequests")
|
||||||
|
const { getGlobalUsers, getGlobalUser } = require("../../utilities/global")
|
||||||
const { getFullUser } = require("../../utilities/users")
|
const { getFullUser } = require("../../utilities/users")
|
||||||
|
|
||||||
function removeGlobalProps(user) {
|
function removeGlobalProps(user) {
|
||||||
|
@ -20,7 +20,7 @@ function removeGlobalProps(user) {
|
||||||
|
|
||||||
exports.fetchMetadata = async function (ctx) {
|
exports.fetchMetadata = async function (ctx) {
|
||||||
const database = new CouchDB(ctx.appId)
|
const database = new CouchDB(ctx.appId)
|
||||||
const global = await getGlobalUsers(ctx, ctx.appId)
|
const global = await getGlobalUsers(ctx.appId)
|
||||||
const metadata = (
|
const metadata = (
|
||||||
await database.allDocs(
|
await database.allDocs(
|
||||||
getUserMetadataParams(null, {
|
getUserMetadataParams(null, {
|
||||||
|
|
|
@ -26,11 +26,6 @@ describe("/routing", () => {
|
||||||
|
|
||||||
describe("fetch", () => {
|
describe("fetch", () => {
|
||||||
it("returns the correct routing for basic user", async () => {
|
it("returns the correct routing for basic user", async () => {
|
||||||
workerRequests.getGlobalUsers.mockImplementationOnce((ctx, appId) => {
|
|
||||||
return {
|
|
||||||
roleId: BUILTIN_ROLE_IDS.BASIC,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/routing/client`)
|
.get(`/api/routing/client`)
|
||||||
.set(await config.roleHeaders({
|
.set(await config.roleHeaders({
|
||||||
|
@ -52,13 +47,6 @@ describe("/routing", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns the correct routing for power user", async () => {
|
it("returns the correct routing for power user", async () => {
|
||||||
workerRequests.getGlobalUsers.mockImplementationOnce((ctx, appId) => {
|
|
||||||
return {
|
|
||||||
roles: {
|
|
||||||
[appId]: BUILTIN_ROLE_IDS.POWER,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/routing/client`)
|
.get(`/api/routing/client`)
|
||||||
.set(await config.roleHeaders({
|
.set(await config.roleHeaders({
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/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 workerRequests = require("../../../utilities/workerRequests")
|
|
||||||
|
|
||||||
jest.mock("../../../utilities/workerRequests", () => ({
|
jest.mock("../../../utilities/workerRequests", () => ({
|
||||||
getGlobalUsers: jest.fn(() => {
|
getGlobalUsers: jest.fn(() => {
|
||||||
|
@ -25,30 +24,18 @@ describe("/users", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("fetch", () => {
|
describe("fetch", () => {
|
||||||
beforeEach(() => {
|
|
||||||
workerRequests.getGlobalUsers.mockImplementationOnce(() => ([
|
|
||||||
{
|
|
||||||
_id: "us_uuid1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "us_uuid2",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
))
|
|
||||||
})
|
|
||||||
|
|
||||||
it("returns a list of users from an instance db", async () => {
|
it("returns a list of users from an instance db", async () => {
|
||||||
await config.createUser("brenda@brenda.com", "brendas_password")
|
await config.createUser("uuidx")
|
||||||
await config.createUser("pam@pam.com", "pam_password")
|
await config.createUser("uuidy")
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/users/metadata`)
|
.get(`/api/users/metadata`)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.length).toBe(2)
|
expect(res.body.length).toBe(3)
|
||||||
expect(res.body.find(u => u._id === `ro_ta_users_us_uuid1`)).toBeDefined()
|
expect(res.body.find(u => u._id === `ro_ta_users_us_uuidx`)).toBeDefined()
|
||||||
expect(res.body.find(u => u._id === `ro_ta_users_us_uuid2`)).toBeDefined()
|
expect(res.body.find(u => u._id === `ro_ta_users_us_uuidy`)).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
@ -65,9 +52,6 @@ describe("/users", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
beforeEach(() => {
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to update the user", async () => {
|
it("should be able to update the user", async () => {
|
||||||
const user = await config.createUser()
|
const user = await config.createUser()
|
||||||
user.roleId = BUILTIN_ROLE_IDS.BASIC
|
user.roleId = BUILTIN_ROLE_IDS.BASIC
|
||||||
|
@ -94,14 +78,6 @@ describe("/users", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("find", () => {
|
describe("find", () => {
|
||||||
beforeEach(() => {
|
|
||||||
jest.resetAllMocks()
|
|
||||||
workerRequests.getGlobalUsers.mockImplementationOnce(() => ({
|
|
||||||
_id: "us_uuid1",
|
|
||||||
roleId: BUILTIN_ROLE_IDS.POWER,
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to find the user", async () => {
|
it("should be able to find the user", async () => {
|
||||||
const user = await config.createUser()
|
const user = await config.createUser()
|
||||||
const res = await request
|
const res = await request
|
||||||
|
@ -110,7 +86,7 @@ describe("/users", () => {
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
expect(res.body._id).toEqual(user._id)
|
expect(res.body._id).toEqual(user._id)
|
||||||
expect(res.body.roleId).toEqual(BUILTIN_ROLE_IDS.POWER)
|
expect(res.body.roleId).toEqual(BUILTIN_ROLE_IDS.ADMIN)
|
||||||
expect(res.body.tableId).toBeDefined()
|
expect(res.body.tableId).toBeDefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,7 +11,12 @@ const {
|
||||||
const { flatten } = require("lodash")
|
const { flatten } = require("lodash")
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const { FieldTypes } = require("../../constants")
|
const { FieldTypes } = require("../../constants")
|
||||||
const { getMultiIDParams } = require("../../db/utils")
|
const {
|
||||||
|
getMultiIDParams,
|
||||||
|
USER_METDATA_PREFIX,
|
||||||
|
} = require("../../db/utils")
|
||||||
|
const { partition } = require("lodash")
|
||||||
|
const { getGlobalUsers } = require("../../utilities/global")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This functionality makes sure that when rows with links are created, updated or deleted they are processed
|
* This functionality makes sure that when rows with links are created, updated or deleted they are processed
|
||||||
|
@ -57,6 +62,30 @@ async function getLinksForRows(appId, rows) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getFullLinkedDocs(appId, links) {
|
||||||
|
// create DBs
|
||||||
|
const db = new CouchDB(appId)
|
||||||
|
const linkedRowIds = links.map(link => link.id)
|
||||||
|
let linked = (await db.allDocs(getMultiIDParams(linkedRowIds))).rows.map(
|
||||||
|
row => row.doc
|
||||||
|
)
|
||||||
|
// need to handle users as specific cases
|
||||||
|
let [users, other] = partition(linked, linkRow => linkRow._id.startsWith(USER_METDATA_PREFIX))
|
||||||
|
const globalUsers = await getGlobalUsers(appId, users)
|
||||||
|
users = users.map(user => {
|
||||||
|
const globalUser = globalUsers.find(globalUser => globalUser && user._id.includes(globalUser._id))
|
||||||
|
return {
|
||||||
|
...globalUser,
|
||||||
|
// doing user second overwrites the id and rev (always metadata)
|
||||||
|
...user,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return [
|
||||||
|
...other,
|
||||||
|
...users,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update link documents for a row or table - this is to be called by the API controller when a change is occurring.
|
* Update link documents for a row or table - this is to be called by the API controller when a change is occurring.
|
||||||
* @param {string} eventType states what type of change which is occurring, means this can be expanded upon in the
|
* @param {string} eventType states what type of change which is occurring, means this can be expanded upon in the
|
||||||
|
@ -154,14 +183,13 @@ exports.attachFullLinkedDocs = async (appId, table, rows) => {
|
||||||
if (linkedTableIds.length === 0) {
|
if (linkedTableIds.length === 0) {
|
||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
|
// create DBs
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
|
// get all the links
|
||||||
const links = (await getLinksForRows(appId, rows)).filter(link =>
|
const links = (await getLinksForRows(appId, rows)).filter(link =>
|
||||||
rows.some(row => row._id === link.thisId)
|
rows.some(row => row._id === link.thisId)
|
||||||
)
|
)
|
||||||
const linkedRowIds = links.map(link => link.id)
|
let linked = await getFullLinkedDocs(appId, links)
|
||||||
const linked = (await db.allDocs(getMultiIDParams(linkedRowIds))).rows.map(
|
|
||||||
row => row.doc
|
|
||||||
)
|
|
||||||
const linkedTables = []
|
const linkedTables = []
|
||||||
for (let row of rows) {
|
for (let row of rows) {
|
||||||
for (let link of links.filter(link => link.thisId === row._id)) {
|
for (let link of links.filter(link => link.thisId === row._id)) {
|
||||||
|
|
|
@ -6,17 +6,11 @@ const {
|
||||||
APP_DEV_PREFIX,
|
APP_DEV_PREFIX,
|
||||||
APP_PREFIX,
|
APP_PREFIX,
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
|
StaticDatabases,
|
||||||
} = require("@budibase/auth/db")
|
} = require("@budibase/auth/db")
|
||||||
|
|
||||||
const UNICODE_MAX = "\ufff0"
|
const UNICODE_MAX = "\ufff0"
|
||||||
|
|
||||||
const StaticDatabases = {
|
|
||||||
BUILDER: {
|
|
||||||
name: "builder-db",
|
|
||||||
baseDoc: "builder-doc",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const AppStatus = {
|
const AppStatus = {
|
||||||
DEV: "development",
|
DEV: "development",
|
||||||
ALL: "all",
|
ALL: "all",
|
||||||
|
@ -54,9 +48,17 @@ const SearchIndexes = {
|
||||||
ROWS: "rows",
|
ROWS: "rows",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.StaticDatabases = {
|
||||||
|
BUILDER: {
|
||||||
|
name: "builder-db",
|
||||||
|
baseDoc: "builder-doc",
|
||||||
|
},
|
||||||
|
...StaticDatabases,
|
||||||
|
}
|
||||||
|
|
||||||
exports.APP_PREFIX = APP_PREFIX
|
exports.APP_PREFIX = APP_PREFIX
|
||||||
exports.APP_DEV_PREFIX = APP_DEV_PREFIX
|
exports.APP_DEV_PREFIX = APP_DEV_PREFIX
|
||||||
exports.StaticDatabases = StaticDatabases
|
exports.USER_METDATA_PREFIX = `${DocumentTypes.ROW}${SEPARATOR}${InternalTables.USER_METADATA}${SEPARATOR}`
|
||||||
exports.ViewNames = ViewNames
|
exports.ViewNames = ViewNames
|
||||||
exports.InternalTables = InternalTables
|
exports.InternalTables = InternalTables
|
||||||
exports.DocumentTypes = DocumentTypes
|
exports.DocumentTypes = DocumentTypes
|
||||||
|
|
|
@ -306,8 +306,8 @@ class TestConfiguration {
|
||||||
return await this._req(config, null, controllers.layout.save)
|
return await this._req(config, null, controllers.layout.save)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createUser() {
|
async createUser(id = null) {
|
||||||
const globalId = `us_${Math.random()}`
|
const globalId = !id ? `us_${Math.random()}` : `us_${id}`
|
||||||
const resp = await this.globalUser(globalId)
|
const resp = await this.globalUser(globalId)
|
||||||
return {
|
return {
|
||||||
...resp,
|
...resp,
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
const CouchDB = require("../db")
|
||||||
|
const {
|
||||||
|
getMultiIDParams,
|
||||||
|
getGlobalIDFromUserMetadataID,
|
||||||
|
StaticDatabases,
|
||||||
|
} = require("../db/utils")
|
||||||
|
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
|
||||||
|
const { getDeployedAppID } = require("@budibase/auth/db")
|
||||||
|
const { getGlobalUserParams } = require("@budibase/auth/db")
|
||||||
|
|
||||||
|
exports.updateAppRole = (appId, user) => {
|
||||||
|
if (!user.roles) {
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
if (user.builder && user.builder.global) {
|
||||||
|
user.roleId = BUILTIN_ROLE_IDS.ADMIN
|
||||||
|
} else {
|
||||||
|
// always use the deployed app
|
||||||
|
user.roleId = user.roles[getDeployedAppID(appId)]
|
||||||
|
if (!user.roleId) {
|
||||||
|
user.roleId = BUILTIN_ROLE_IDS.PUBLIC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete user.roles
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getGlobalUser = async (appId, userId) => {
|
||||||
|
const db = CouchDB(StaticDatabases.GLOBAL.name)
|
||||||
|
let user = await db.get(getGlobalIDFromUserMetadataID(userId))
|
||||||
|
if (user) {
|
||||||
|
delete user.password
|
||||||
|
}
|
||||||
|
return exports.updateAppRole(appId, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getGlobalUsers = async (appId = null, users = null) => {
|
||||||
|
const db = CouchDB(StaticDatabases.GLOBAL.name)
|
||||||
|
let globalUsers
|
||||||
|
if (users) {
|
||||||
|
const globalIds = users.map(user => getGlobalIDFromUserMetadataID(user._id))
|
||||||
|
globalUsers = (await db.allDocs(getMultiIDParams(globalIds))).rows.map(
|
||||||
|
row => row.doc
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
globalUsers = (await db.allDocs(getGlobalUserParams(null,{
|
||||||
|
include_docs: true,
|
||||||
|
}))).rows.map(row => row.doc)
|
||||||
|
}
|
||||||
|
globalUsers = globalUsers.filter(user => user != null).map(user => {
|
||||||
|
delete user.password
|
||||||
|
return user
|
||||||
|
})
|
||||||
|
if (!appId) {
|
||||||
|
return globalUsers
|
||||||
|
}
|
||||||
|
return globalUsers.map(user => exports.updateAppRole(appId, user))
|
||||||
|
}
|
|
@ -1,13 +1,9 @@
|
||||||
const CouchDB = require("../db")
|
const CouchDB = require("../db")
|
||||||
const { getGlobalIDFromUserMetadataID, InternalTables } = require("../db/utils")
|
const { InternalTables } = require("../db/utils")
|
||||||
const { getGlobalUsers } = require("../utilities/workerRequests")
|
const { getGlobalUser } = require("../utilities/global")
|
||||||
|
|
||||||
exports.getFullUser = async (ctx, userId) => {
|
exports.getFullUser = async (ctx, userId) => {
|
||||||
const global = await getGlobalUsers(
|
const global = await getGlobalUser(ctx.appId, userId)
|
||||||
ctx,
|
|
||||||
ctx.appId,
|
|
||||||
getGlobalIDFromUserMetadataID(userId)
|
|
||||||
)
|
|
||||||
let metadata
|
let metadata
|
||||||
try {
|
try {
|
||||||
// this will throw an error if the db doesn't exist, or there is no appId
|
// this will throw an error if the db doesn't exist, or there is no appId
|
||||||
|
|
|
@ -1,26 +1,8 @@
|
||||||
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("@budibase/auth/roles")
|
|
||||||
const { getDeployedAppID } = require("@budibase/auth/db")
|
const { getDeployedAppID } = require("@budibase/auth/db")
|
||||||
const { getGlobalIDFromUserMetadataID } = require("../db/utils")
|
const { updateAppRole, getGlobalUser } = require("./global")
|
||||||
|
|
||||||
function getAppRole(appId, user) {
|
|
||||||
if (!user.roles) {
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
if (user.builder && user.builder.global) {
|
|
||||||
user.roleId = BUILTIN_ROLE_IDS.ADMIN
|
|
||||||
} else {
|
|
||||||
// always use the deployed app
|
|
||||||
user.roleId = user.roles[getDeployedAppID(appId)]
|
|
||||||
if (!user.roleId) {
|
|
||||||
user.roleId = BUILTIN_ROLE_IDS.PUBLIC
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete user.roles
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
|
|
||||||
function request(ctx, request, noApiKey) {
|
function request(ctx, request, noApiKey) {
|
||||||
if (!request.headers) {
|
if (!request.headers) {
|
||||||
|
@ -90,27 +72,6 @@ exports.getDeployedApps = async ctx => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => {
|
|
||||||
const endpoint = globalId
|
|
||||||
? `/api/admin/users/${globalId}`
|
|
||||||
: `/api/admin/users`
|
|
||||||
const reqCfg = { method: "GET" }
|
|
||||||
const response = await fetch(
|
|
||||||
checkSlashesInUrl(env.WORKER_URL + endpoint),
|
|
||||||
request(ctx, reqCfg)
|
|
||||||
)
|
|
||||||
let users = await response.json()
|
|
||||||
if (!appId) {
|
|
||||||
return users
|
|
||||||
}
|
|
||||||
if (Array.isArray(users)) {
|
|
||||||
users = users.map(user => getAppRole(appId, user))
|
|
||||||
} else {
|
|
||||||
users = getAppRole(appId, users)
|
|
||||||
}
|
|
||||||
return users
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getGlobalSelf = async (ctx, appId = null) => {
|
exports.getGlobalSelf = async (ctx, appId = null) => {
|
||||||
const endpoint = `/api/admin/users/self`
|
const endpoint = `/api/admin/users/self`
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
|
@ -123,7 +84,7 @@ exports.getGlobalSelf = async (ctx, appId = null) => {
|
||||||
}
|
}
|
||||||
let json = await response.json()
|
let json = await response.json()
|
||||||
if (appId) {
|
if (appId) {
|
||||||
json = getAppRole(appId, json)
|
json = updateAppRole(appId, json)
|
||||||
}
|
}
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
@ -136,8 +97,7 @@ exports.addAppRoleToUser = async (ctx, appId, roleId, userId = null) => {
|
||||||
user = await exports.getGlobalSelf(ctx)
|
user = await exports.getGlobalSelf(ctx)
|
||||||
endpoint = `/api/admin/users/self`
|
endpoint = `/api/admin/users/self`
|
||||||
} else {
|
} else {
|
||||||
userId = getGlobalIDFromUserMetadataID(userId)
|
user = await getGlobalUser(appId, userId)
|
||||||
user = await exports.getGlobalUsers(ctx, appId, userId)
|
|
||||||
body._id = userId
|
body._id = userId
|
||||||
endpoint = `/api/admin/users`
|
endpoint = `/api/admin/users`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue