Updating the server to remove use of the email in the user ID.
This commit is contained in:
parent
ae792a9593
commit
ade007482c
|
@ -1,75 +1,8 @@
|
||||||
const jwt = require("jsonwebtoken")
|
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const bcrypt = require("../../utilities/bcrypt")
|
|
||||||
const env = require("../../environment")
|
|
||||||
const { getAPIKey } = require("../../utilities/usageQuota")
|
|
||||||
const { generateUserMetadataID } = require("../../db/utils")
|
|
||||||
const { setCookie } = require("../../utilities")
|
|
||||||
const { outputProcessing } = require("../../utilities/rowProcessor")
|
const { outputProcessing } = require("../../utilities/rowProcessor")
|
||||||
const { InternalTables } = require("../../db/utils")
|
const { InternalTables } = require("../../db/utils")
|
||||||
const { UserStatus } = require("@budibase/auth")
|
|
||||||
const { getFullUser } = require("../../utilities/users")
|
const { getFullUser } = require("../../utilities/users")
|
||||||
|
|
||||||
const INVALID_ERR = "Invalid Credentials"
|
|
||||||
|
|
||||||
exports.authenticate = async ctx => {
|
|
||||||
const appId = ctx.appId
|
|
||||||
if (!appId) ctx.throw(400, "No appId")
|
|
||||||
|
|
||||||
const { email, password } = ctx.request.body
|
|
||||||
|
|
||||||
if (!email) ctx.throw(400, "Email Required.")
|
|
||||||
if (!password) ctx.throw(400, "Password Required.")
|
|
||||||
|
|
||||||
// Check the user exists in the instance DB by email
|
|
||||||
const db = new CouchDB(appId)
|
|
||||||
const app = await db.get(appId)
|
|
||||||
|
|
||||||
let dbUser
|
|
||||||
try {
|
|
||||||
dbUser = await db.get(generateUserMetadataID(email))
|
|
||||||
} catch (_) {
|
|
||||||
// do not want to throw a 404 - as this could be
|
|
||||||
// used to determine valid emails
|
|
||||||
ctx.throw(401, INVALID_ERR)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that the user is currently inactive, if this is the case throw invalid
|
|
||||||
if (dbUser.status === UserStatus.INACTIVE) {
|
|
||||||
ctx.throw(401, INVALID_ERR)
|
|
||||||
}
|
|
||||||
|
|
||||||
// authenticate
|
|
||||||
if (await bcrypt.compare(password, dbUser.password)) {
|
|
||||||
const payload = {
|
|
||||||
userId: dbUser._id,
|
|
||||||
roleId: dbUser.roleId,
|
|
||||||
version: app.version,
|
|
||||||
}
|
|
||||||
// if in prod add the user api key, unless self hosted
|
|
||||||
/* istanbul ignore next */
|
|
||||||
if (env.isProd() && !env.SELF_HOSTED) {
|
|
||||||
const { apiKey } = await getAPIKey(ctx.appId)
|
|
||||||
payload.apiKey = apiKey
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = jwt.sign(payload, ctx.config.jwtSecret, {
|
|
||||||
expiresIn: "1 day",
|
|
||||||
})
|
|
||||||
|
|
||||||
setCookie(ctx, token, appId)
|
|
||||||
|
|
||||||
delete dbUser.password
|
|
||||||
ctx.body = {
|
|
||||||
token,
|
|
||||||
...dbUser,
|
|
||||||
appId,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx.throw(401, INVALID_ERR)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.fetchSelf = async ctx => {
|
exports.fetchSelf = async ctx => {
|
||||||
if (!ctx.user) {
|
if (!ctx.user) {
|
||||||
ctx.throw(403, "No user logged in")
|
ctx.throw(403, "No user logged in")
|
||||||
|
@ -82,7 +15,7 @@ exports.fetchSelf = async ctx => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await getFullUser({ ctx, userId: userId })
|
const user = await getFullUser(ctx, userId)
|
||||||
|
|
||||||
if (appId) {
|
if (appId) {
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
|
|
|
@ -42,7 +42,7 @@ async function findRow(ctx, db, tableId, rowId) {
|
||||||
// TODO remove special user case in future
|
// TODO remove special user case in future
|
||||||
if (tableId === InternalTables.USER_METADATA) {
|
if (tableId === InternalTables.USER_METADATA) {
|
||||||
ctx.params = {
|
ctx.params = {
|
||||||
userId: rowId,
|
id: rowId,
|
||||||
}
|
}
|
||||||
await userController.findMetadata(ctx)
|
await userController.findMetadata(ctx)
|
||||||
row = ctx.body
|
row = ctx.body
|
||||||
|
@ -140,13 +140,8 @@ exports.save = async function(ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inputs._rev && !inputs._id) {
|
if (!inputs._rev && !inputs._id) {
|
||||||
// TODO remove special user case in future
|
|
||||||
if (inputs.tableId === InternalTables.USER_METADATA) {
|
|
||||||
inputs._id = generateUserMetadataID(inputs.email)
|
|
||||||
} else {
|
|
||||||
inputs._id = generateRowID(inputs.tableId)
|
inputs._id = generateRowID(inputs.tableId)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// this returns the table and row incase they have been updated
|
// this returns the table and row incase they have been updated
|
||||||
const dbTable = await db.get(inputs.tableId)
|
const dbTable = await db.get(inputs.tableId)
|
||||||
|
@ -342,7 +337,7 @@ exports.destroy = async function(ctx) {
|
||||||
// TODO remove special user case in future
|
// TODO remove special user case in future
|
||||||
if (ctx.params.tableId === InternalTables.USER_METADATA) {
|
if (ctx.params.tableId === InternalTables.USER_METADATA) {
|
||||||
ctx.params = {
|
ctx.params = {
|
||||||
userId: ctx.params.rowId,
|
id: ctx.params.rowId,
|
||||||
}
|
}
|
||||||
await userController.destroyMetadata(ctx)
|
await userController.destroyMetadata(ctx)
|
||||||
} else {
|
} else {
|
||||||
|
@ -449,7 +444,7 @@ async function bulkDelete(ctx) {
|
||||||
updates = updates.concat(
|
updates = updates.concat(
|
||||||
rows.map(row => {
|
rows.map(row => {
|
||||||
ctx.params = {
|
ctx.params = {
|
||||||
userId: row._id,
|
id: row._id,
|
||||||
}
|
}
|
||||||
return userController.destroyMetadata(ctx)
|
return userController.destroyMetadata(ctx)
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,7 @@ const CouchDB = require("../../db")
|
||||||
const {
|
const {
|
||||||
generateUserMetadataID,
|
generateUserMetadataID,
|
||||||
getUserMetadataParams,
|
getUserMetadataParams,
|
||||||
getEmailFromUserMetadataID,
|
getGlobalIDFromUserMetadataID,
|
||||||
} = require("../../db/utils")
|
} = require("../../db/utils")
|
||||||
const { InternalTables } = require("../../db/utils")
|
const { InternalTables } = require("../../db/utils")
|
||||||
const { getRole } = require("../../utilities/security/roles")
|
const { getRole } = require("../../utilities/security/roles")
|
||||||
|
@ -25,15 +25,14 @@ exports.fetchMetadata = async function(ctx) {
|
||||||
).rows.map(row => row.doc)
|
).rows.map(row => row.doc)
|
||||||
const users = []
|
const users = []
|
||||||
for (let user of global) {
|
for (let user of global) {
|
||||||
const info = metadata.find(meta => meta._id.includes(user.email))
|
// find the metadata that matches up to the global ID
|
||||||
|
const info = metadata.find(meta => meta._id.includes(user._id))
|
||||||
// remove these props, not for the correct DB
|
// remove these props, not for the correct DB
|
||||||
delete user._id
|
|
||||||
delete user._rev
|
|
||||||
users.push({
|
users.push({
|
||||||
...user,
|
...user,
|
||||||
...info,
|
...info,
|
||||||
// make sure the ID is always a local ID, not a global one
|
// make sure the ID is always a local ID, not a global one
|
||||||
_id: generateUserMetadataID(user.email),
|
_id: generateUserMetadataID(user._id),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ctx.body = users
|
ctx.body = users
|
||||||
|
@ -43,17 +42,16 @@ exports.createMetadata = async function(ctx) {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
const { roleId } = ctx.request.body
|
const { roleId } = ctx.request.body
|
||||||
const email = ctx.request.body.email || ctx.user.email
|
|
||||||
|
|
||||||
// check role valid
|
// check role valid
|
||||||
const role = await getRole(appId, roleId)
|
const role = await getRole(appId, roleId)
|
||||||
if (!role) ctx.throw(400, "Invalid Role")
|
if (!role) ctx.throw(400, "Invalid Role")
|
||||||
|
|
||||||
const metadata = await saveGlobalUser(ctx, appId, email, ctx.request.body)
|
const globalUser = await saveGlobalUser(ctx, appId, ctx.request.body)
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
...metadata,
|
...globalUser,
|
||||||
_id: generateUserMetadataID(email),
|
_id: generateUserMetadataID(globalUser._id),
|
||||||
type: "user",
|
type: "user",
|
||||||
tableId: InternalTables.USER_METADATA,
|
tableId: InternalTables.USER_METADATA,
|
||||||
}
|
}
|
||||||
|
@ -64,7 +62,7 @@ exports.createMetadata = async function(ctx) {
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
_id: response.id,
|
_id: response.id,
|
||||||
_rev: response.rev,
|
_rev: response.rev,
|
||||||
email,
|
email: ctx.request.body.email,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,39 +70,34 @@ exports.updateMetadata = async function(ctx) {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
const user = ctx.request.body
|
const user = ctx.request.body
|
||||||
let email = user.email || getEmailFromUserMetadataID(user._id)
|
const globalUser = await saveGlobalUser(
|
||||||
const metadata = await saveGlobalUser(ctx, appId, email, ctx.request.body)
|
ctx,
|
||||||
if (!metadata._id) {
|
appId,
|
||||||
metadata._id = generateUserMetadataID(email)
|
getGlobalIDFromUserMetadataID(user._id),
|
||||||
|
ctx.request.body
|
||||||
|
)
|
||||||
|
const metadata = {
|
||||||
|
...globalUser,
|
||||||
|
_id: user._id || generateUserMetadataID(globalUser._id),
|
||||||
|
_rev: ctx.request.body._rev,
|
||||||
}
|
}
|
||||||
if (!metadata._rev) {
|
ctx.body = await db.put(metadata)
|
||||||
metadata._rev = ctx.request.body._rev
|
|
||||||
}
|
|
||||||
ctx.body = await db.put({
|
|
||||||
...metadata,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroyMetadata = async function(ctx) {
|
exports.destroyMetadata = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = new CouchDB(ctx.appId)
|
||||||
const email =
|
await deleteGlobalUser(ctx, getGlobalIDFromUserMetadataID(ctx.params.id))
|
||||||
ctx.params.email || getEmailFromUserMetadataID(ctx.params.userId)
|
|
||||||
await deleteGlobalUser(ctx, email)
|
|
||||||
try {
|
try {
|
||||||
const dbUser = await db.get(generateUserMetadataID(email))
|
const dbUser = await db.get(ctx.params.id)
|
||||||
await db.remove(dbUser._id, dbUser._rev)
|
await db.remove(dbUser._id, dbUser._rev)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// error just means the global user has no config in this app
|
// error just means the global user has no config in this app
|
||||||
}
|
}
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: `User ${ctx.params.email} deleted.`,
|
message: `User ${ctx.params.id} deleted.`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.findMetadata = async function(ctx) {
|
exports.findMetadata = async function(ctx) {
|
||||||
ctx.body = await getFullUser({
|
ctx.body = await getFullUser(ctx, ctx.params.id)
|
||||||
ctx,
|
|
||||||
email: ctx.params.email,
|
|
||||||
userId: ctx.params.userId,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
|
const { generateUserMetadataID } = require("../../../db/utils")
|
||||||
|
|
||||||
require("../../../utilities/workerRequests")
|
require("../../../utilities/workerRequests")
|
||||||
jest.mock("../../../utilities/workerRequests", () => ({
|
jest.mock("../../../utilities/workerRequests", () => ({
|
||||||
getGlobalUsers: jest.fn(() => {
|
getGlobalUsers: jest.fn(() => {
|
||||||
return {
|
return {
|
||||||
email: "test@test.com",
|
_id: "us_uuid1",
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
saveGlobalUser: jest.fn(() => {
|
||||||
|
return {
|
||||||
|
_id: "us_uuid1",
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
saveGlobalUser: jest.fn(),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
describe("/authenticate", () => {
|
describe("/authenticate", () => {
|
||||||
|
@ -22,14 +27,14 @@ describe("/authenticate", () => {
|
||||||
|
|
||||||
describe("fetch self", () => {
|
describe("fetch self", () => {
|
||||||
it("should be able to fetch self", async () => {
|
it("should be able to fetch self", async () => {
|
||||||
await config.createUser("test@test.com", "p4ssw0rd")
|
const user = await config.createUser("test@test.com", "p4ssw0rd")
|
||||||
const headers = await config.login("test@test.com", "p4ssw0rd")
|
const headers = await config.login("test@test.com", "p4ssw0rd", { userId: user._id })
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/self`)
|
.get(`/api/self`)
|
||||||
.set(headers)
|
.set(headers)
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
expect(res.body.email).toEqual("test@test.com")
|
expect(res.body._id).toEqual(generateUserMetadataID("us_uuid1"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
|
@ -4,11 +4,6 @@ const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
||||||
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
||||||
const workerRequests = require("../../../utilities/workerRequests")
|
const workerRequests = require("../../../utilities/workerRequests")
|
||||||
|
|
||||||
jest.mock("../../../utilities/workerRequests", () => ({
|
|
||||||
getGlobalUsers: jest.fn(),
|
|
||||||
saveGlobalUser: jest.fn(),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const route = "/test"
|
const route = "/test"
|
||||||
|
|
||||||
describe("/routing", () => {
|
describe("/routing", () => {
|
||||||
|
|
|
@ -7,7 +7,10 @@ const workerRequests = require("../../../utilities/workerRequests")
|
||||||
jest.mock("../../../utilities/workerRequests", () => ({
|
jest.mock("../../../utilities/workerRequests", () => ({
|
||||||
getGlobalUsers: jest.fn(),
|
getGlobalUsers: jest.fn(),
|
||||||
saveGlobalUser: jest.fn(() => {
|
saveGlobalUser: jest.fn(() => {
|
||||||
return {}
|
const uuid = require("uuid/v4")
|
||||||
|
return {
|
||||||
|
_id: `us_${uuid()}`
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
deleteGlobalUser: jest.fn(),
|
deleteGlobalUser: jest.fn(),
|
||||||
}))
|
}))
|
||||||
|
@ -26,10 +29,10 @@ describe("/users", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
workerRequests.getGlobalUsers.mockImplementationOnce(() => ([
|
workerRequests.getGlobalUsers.mockImplementationOnce(() => ([
|
||||||
{
|
{
|
||||||
email: "brenda@brenda.com"
|
_id: "us_uuid1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
email: "pam@pam.com"
|
_id: "us_uuid2",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
|
@ -45,8 +48,8 @@ describe("/users", () => {
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body.length).toBe(2)
|
expect(res.body.length).toBe(2)
|
||||||
expect(res.body.find(u => u.email === "brenda@brenda.com")).toBeDefined()
|
expect(res.body.find(u => u._id === `ro_ta_users_us_uuid1`)).toBeDefined()
|
||||||
expect(res.body.find(u => u.email === "pam@pam.com")).toBeDefined()
|
expect(res.body.find(u => u._id === `ro_ta_users_us_uuid2`)).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
@ -66,10 +69,10 @@ describe("/users", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
workerRequests.getGlobalUsers.mockImplementationOnce(() => ([
|
workerRequests.getGlobalUsers.mockImplementationOnce(() => ([
|
||||||
{
|
{
|
||||||
email: "bill@budibase.com"
|
_id: "us_uuid1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
email: "brandNewUser@user.com"
|
_id: "us_uuid2",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
|
@ -86,7 +89,6 @@ describe("/users", () => {
|
||||||
|
|
||||||
it("returns a success message when a user is successfully created", async () => {
|
it("returns a success message when a user is successfully created", async () => {
|
||||||
const body = basicUser(BUILTIN_ROLE_IDS.POWER)
|
const body = basicUser(BUILTIN_ROLE_IDS.POWER)
|
||||||
body.email = "bill@budibase.com"
|
|
||||||
const res = await create(body)
|
const res = await create(body)
|
||||||
|
|
||||||
expect(res.res.statusMessage).toEqual("OK")
|
expect(res.res.statusMessage).toEqual("OK")
|
||||||
|
@ -95,7 +97,6 @@ describe("/users", () => {
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
const body = basicUser(BUILTIN_ROLE_IDS.POWER)
|
const body = basicUser(BUILTIN_ROLE_IDS.POWER)
|
||||||
body.email = "brandNewUser@user.com"
|
|
||||||
await checkPermissionsEndpoint({
|
await checkPermissionsEndpoint({
|
||||||
config,
|
config,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -110,13 +111,6 @@ describe("/users", () => {
|
||||||
const user = basicUser(null)
|
const user = basicUser(null)
|
||||||
await create(user, 400)
|
await create(user, 400)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should throw error if user exists already", async () => {
|
|
||||||
await config.createUser("test@test.com")
|
|
||||||
const user = basicUser(BUILTIN_ROLE_IDS.POWER)
|
|
||||||
user.email = "test@test.com"
|
|
||||||
await create(user, 409)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
|
@ -141,10 +135,9 @@ describe("/users", () => {
|
||||||
|
|
||||||
describe("destroy", () => {
|
describe("destroy", () => {
|
||||||
it("should be able to delete the user", async () => {
|
it("should be able to delete the user", async () => {
|
||||||
const email = "test@test.com"
|
const user = await config.createUser()
|
||||||
await config.createUser(email)
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.delete(`/api/users/metadata/${email}`)
|
.delete(`/api/users/metadata/${user._id}`)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
|
@ -156,21 +149,23 @@ describe("/users", () => {
|
||||||
describe("find", () => {
|
describe("find", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetAllMocks()
|
jest.resetAllMocks()
|
||||||
|
workerRequests.saveGlobalUser.mockImplementationOnce(() => ({
|
||||||
|
_id: "us_uuid1",
|
||||||
|
}))
|
||||||
workerRequests.getGlobalUsers.mockImplementationOnce(() => ({
|
workerRequests.getGlobalUsers.mockImplementationOnce(() => ({
|
||||||
email: "test@test.com",
|
_id: "us_uuid1",
|
||||||
roleId: BUILTIN_ROLE_IDS.POWER,
|
roleId: BUILTIN_ROLE_IDS.POWER,
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to find the user", async () => {
|
it("should be able to find the user", async () => {
|
||||||
const email = "test@test.com"
|
const user = await config.createUser()
|
||||||
await config.createUser(email)
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/users/metadata/${email}`)
|
.get(`/api/users/metadata/${user._id}`)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
expect(res.body.email).toEqual(email)
|
expect(res.body._id).toEqual(user._id)
|
||||||
expect(res.body.roleId).toEqual(BUILTIN_ROLE_IDS.POWER)
|
expect(res.body.roleId).toEqual(BUILTIN_ROLE_IDS.POWER)
|
||||||
expect(res.body.tableId).toBeDefined()
|
expect(res.body.tableId).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
|
@ -63,11 +63,9 @@ exports.checkPermissionsEndpoint = async ({
|
||||||
}) => {
|
}) => {
|
||||||
const password = "PASSWORD"
|
const password = "PASSWORD"
|
||||||
await config.createUser("passUser@budibase.com", password, passRole)
|
await config.createUser("passUser@budibase.com", password, passRole)
|
||||||
const passHeader = await config.login(
|
const passHeader = await config.login("passUser@budibase.com", password, {
|
||||||
"passUser@budibase.com",
|
roleId: passRole,
|
||||||
password,
|
})
|
||||||
passRole
|
|
||||||
)
|
|
||||||
|
|
||||||
await exports
|
await exports
|
||||||
.createRequest(config.request, method, url, body)
|
.createRequest(config.request, method, url, body)
|
||||||
|
@ -75,11 +73,9 @@ exports.checkPermissionsEndpoint = async ({
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
await config.createUser("failUser@budibase.com", password, failRole)
|
await config.createUser("failUser@budibase.com", password, failRole)
|
||||||
const failHeader = await config.login(
|
const failHeader = await config.login("failUser@budibase.com", password, {
|
||||||
"failUser@budibase.com",
|
roleId: failRole,
|
||||||
password,
|
})
|
||||||
failRole
|
|
||||||
)
|
|
||||||
|
|
||||||
await exports
|
await exports
|
||||||
.createRequest(config.request, method, url, body)
|
.createRequest(config.request, method, url, body)
|
||||||
|
|
|
@ -2,6 +2,15 @@ const TestConfig = require("../../../../tests/utilities/TestConfiguration")
|
||||||
const structures = require("../../../../tests/utilities/structures")
|
const structures = require("../../../../tests/utilities/structures")
|
||||||
const env = require("../../../../environment")
|
const env = require("../../../../environment")
|
||||||
|
|
||||||
|
jest.mock("../../../../utilities/workerRequests", () => ({
|
||||||
|
getGlobalUsers: jest.fn(),
|
||||||
|
saveGlobalUser: jest.fn(() => {
|
||||||
|
return {
|
||||||
|
_id: "us_uuid1",
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
|
||||||
let request, config
|
let request, config
|
||||||
|
|
|
@ -16,7 +16,7 @@ router
|
||||||
controller.fetchMetadata
|
controller.fetchMetadata
|
||||||
)
|
)
|
||||||
.get(
|
.get(
|
||||||
"/api/users/metadata/:email",
|
"/api/users/metadata/:id",
|
||||||
authorized(PermissionTypes.USER, PermissionLevels.READ),
|
authorized(PermissionTypes.USER, PermissionLevels.READ),
|
||||||
controller.findMetadata
|
controller.findMetadata
|
||||||
)
|
)
|
||||||
|
@ -32,7 +32,7 @@ router
|
||||||
controller.createMetadata
|
controller.createMetadata
|
||||||
)
|
)
|
||||||
.delete(
|
.delete(
|
||||||
"/api/users/metadata/:email",
|
"/api/users/metadata/:id",
|
||||||
authorized(PermissionTypes.USER, PermissionLevels.WRITE),
|
authorized(PermissionTypes.USER, PermissionLevels.WRITE),
|
||||||
usage,
|
usage,
|
||||||
controller.destroyMetadata
|
controller.destroyMetadata
|
||||||
|
|
|
@ -25,6 +25,7 @@ describe("test the create user action", () => {
|
||||||
expect(res.id).toBeDefined()
|
expect(res.id).toBeDefined()
|
||||||
expect(res.revision).toBeDefined()
|
expect(res.revision).toBeDefined()
|
||||||
const userDoc = await config.getRow(InternalTables.USER_METADATA, res.id)
|
const userDoc = await config.getRow(InternalTables.USER_METADATA, res.id)
|
||||||
|
expect(userDoc).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return an error if no inputs provided", async () => {
|
it("should return an error if no inputs provided", async () => {
|
||||||
|
|
|
@ -33,6 +33,7 @@ exports.USERS_TABLE_SCHEMA = {
|
||||||
type: "table",
|
type: "table",
|
||||||
views: {},
|
views: {},
|
||||||
name: "Users",
|
name: "Users",
|
||||||
|
// TODO: ADMIN PANEL - when implemented this doesn't need to be carried out
|
||||||
schema: {
|
schema: {
|
||||||
email: {
|
email: {
|
||||||
type: exports.FieldTypes.STRING,
|
type: exports.FieldTypes.STRING,
|
||||||
|
|
|
@ -127,23 +127,23 @@ exports.generateRowID = (tableId, id = null) => {
|
||||||
/**
|
/**
|
||||||
* Gets parameters for retrieving users, this is a utility function for the getDocParams function.
|
* Gets parameters for retrieving users, this is a utility function for the getDocParams function.
|
||||||
*/
|
*/
|
||||||
exports.getUserMetadataParams = (email = "", otherProps = {}) => {
|
exports.getUserMetadataParams = (userId = null, otherProps = {}) => {
|
||||||
return exports.getRowParams(InternalTables.USER_METADATA, email, otherProps)
|
return exports.getRowParams(InternalTables.USER_METADATA, userId, otherProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new user ID based on the passed in email.
|
* Generates a new user ID based on the passed in global ID.
|
||||||
* @param {string} email The email which the ID is going to be built up of.
|
* @param {string} globalId The ID of the global user.
|
||||||
* @returns {string} The new user ID which the user doc can be stored under.
|
* @returns {string} The new user ID which the user doc can be stored under.
|
||||||
*/
|
*/
|
||||||
exports.generateUserMetadataID = email => {
|
exports.generateUserMetadataID = globalId => {
|
||||||
return exports.generateRowID(InternalTables.USER_METADATA, email)
|
return exports.generateRowID(InternalTables.USER_METADATA, globalId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Breaks up the ID to get the email address back out of it.
|
* Breaks up the ID to get the global ID.
|
||||||
*/
|
*/
|
||||||
exports.getEmailFromUserMetadataID = id => {
|
exports.getGlobalIDFromUserMetadataID = id => {
|
||||||
return id.split(
|
return id.split(
|
||||||
`${DocumentTypes.ROW}${SEPARATOR}${InternalTables.USER_METADATA}${SEPARATOR}`
|
`${DocumentTypes.ROW}${SEPARATOR}${InternalTables.USER_METADATA}${SEPARATOR}`
|
||||||
)[1]
|
)[1]
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
const { getAppId, setCookie, getCookie, Cookies } = require("@budibase/auth")
|
const { getAppId, setCookie, getCookie, Cookies } = require("@budibase/auth")
|
||||||
const { getRole } = require("../utilities/security/roles")
|
const { getRole } = require("../utilities/security/roles")
|
||||||
const { generateUserMetadataID } = require("../db/utils")
|
|
||||||
const { getGlobalUsers } = require("../utilities/workerRequests")
|
const { getGlobalUsers } = require("../utilities/workerRequests")
|
||||||
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")
|
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")
|
||||||
|
|
||||||
|
@ -40,14 +39,10 @@ module.exports = async (ctx, next) => {
|
||||||
if (appId) {
|
if (appId) {
|
||||||
ctx.appId = appId
|
ctx.appId = appId
|
||||||
if (roleId) {
|
if (roleId) {
|
||||||
const userId = ctx.user
|
|
||||||
? generateUserMetadataID(ctx.user.email)
|
|
||||||
: undefined
|
|
||||||
ctx.roleId = roleId
|
ctx.roleId = roleId
|
||||||
ctx.user = {
|
ctx.user = {
|
||||||
...ctx.user,
|
...ctx.user,
|
||||||
_id: userId,
|
_id: ctx.user ? ctx.user.userId : null,
|
||||||
userId,
|
|
||||||
role: await getRole(appId, roleId),
|
role: await getRole(appId, roleId),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ function mockWorker() {
|
||||||
jest.mock("../../utilities/workerRequests", () => ({
|
jest.mock("../../utilities/workerRequests", () => ({
|
||||||
getGlobalUsers: () => {
|
getGlobalUsers: () => {
|
||||||
return {
|
return {
|
||||||
email: "test@test.com",
|
email: "us_uuid1",
|
||||||
roles: {
|
roles: {
|
||||||
"app_test": "BASIC",
|
"app_test": "BASIC",
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ class TestConfiguration {
|
||||||
|
|
||||||
setUser() {
|
setUser() {
|
||||||
this.ctx.user = {
|
this.ctx.user = {
|
||||||
email: "test@test.com",
|
userId: "ro_ta_user_us_uuid1",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,8 +70,7 @@ class TestConfiguration {
|
||||||
|
|
||||||
defaultHeaders() {
|
defaultHeaders() {
|
||||||
const user = {
|
const user = {
|
||||||
userId: "us_test@test.com",
|
userId: "ro_ta_user_us_uuid1",
|
||||||
email: "test@test.com",
|
|
||||||
builder: {
|
builder: {
|
||||||
global: true,
|
global: true,
|
||||||
},
|
},
|
||||||
|
@ -106,12 +105,13 @@ class TestConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
async roleHeaders(email = EMAIL, roleId = BUILTIN_ROLE_IDS.ADMIN) {
|
async roleHeaders(email = EMAIL, roleId = BUILTIN_ROLE_IDS.ADMIN) {
|
||||||
|
let user
|
||||||
try {
|
try {
|
||||||
await this.createUser(email, PASSWORD, roleId)
|
user = await this.createUser(email, PASSWORD, roleId)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// allow errors here
|
// allow errors here
|
||||||
}
|
}
|
||||||
return this.login(email, PASSWORD, roleId)
|
return this.login(email, PASSWORD, { roleId, userId: user._id })
|
||||||
}
|
}
|
||||||
|
|
||||||
async createApp(appName) {
|
async createApp(appName) {
|
||||||
|
@ -293,33 +293,19 @@ class TestConfiguration {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async makeUserInactive(email) {
|
async login(email, password, { roleId, userId } = {}) {
|
||||||
const user = await this._req(
|
if (!roleId) {
|
||||||
null,
|
roleId = BUILTIN_ROLE_IDS.BUILDER
|
||||||
{
|
|
||||||
email,
|
|
||||||
},
|
|
||||||
controllers.user.findMetadata
|
|
||||||
)
|
|
||||||
return this._req(
|
|
||||||
{
|
|
||||||
...user,
|
|
||||||
status: "inactive",
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
controllers.user.updateMetadata
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(email, password, roleId = BUILTIN_ROLE_IDS.BUILDER) {
|
|
||||||
if (!this.request) {
|
if (!this.request) {
|
||||||
throw "Server has not been opened, cannot login."
|
throw "Server has not been opened, cannot login."
|
||||||
}
|
}
|
||||||
if (!email || !password) {
|
if (!email || !password) {
|
||||||
await this.createUser()
|
await this.createUser()
|
||||||
}
|
}
|
||||||
|
// have to fake this
|
||||||
const user = {
|
const user = {
|
||||||
userId: `us_${email || EMAIL}`,
|
userId: userId || `ro_ta_users_us_uuid1`,
|
||||||
email: email || EMAIL,
|
email: email || EMAIL,
|
||||||
}
|
}
|
||||||
const app = {
|
const app = {
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
const CouchDB = require("../db")
|
const CouchDB = require("../db")
|
||||||
const {
|
const { getGlobalIDFromUserMetadataID } = require("../db/utils")
|
||||||
generateUserMetadataID,
|
|
||||||
getEmailFromUserMetadataID,
|
|
||||||
} = require("../db/utils")
|
|
||||||
const { getGlobalUsers } = require("../utilities/workerRequests")
|
const { getGlobalUsers } = require("../utilities/workerRequests")
|
||||||
|
|
||||||
exports.getFullUser = async ({ ctx, email, userId }) => {
|
exports.getFullUser = async (ctx, userId) => {
|
||||||
if (!email) {
|
const global = await getGlobalUsers(
|
||||||
email = getEmailFromUserMetadataID(userId)
|
ctx,
|
||||||
}
|
ctx.appId,
|
||||||
const global = await getGlobalUsers(ctx, ctx.appId, email)
|
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
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = new CouchDB(ctx.appId)
|
||||||
metadata = await db.get(generateUserMetadataID(email))
|
metadata = await db.get(userId)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// it is fine if there is no user metadata, just remove global db info
|
// it is fine if there is no user metadata, just remove global db info
|
||||||
delete global._id
|
delete global._id
|
||||||
|
@ -24,6 +22,6 @@ exports.getFullUser = async ({ ctx, email, userId }) => {
|
||||||
...global,
|
...global,
|
||||||
...metadata,
|
...metadata,
|
||||||
// make sure the ID is always a local ID, not a global one
|
// make sure the ID is always a local ID, not a global one
|
||||||
_id: generateUserMetadataID(email),
|
_id: userId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,8 @@ exports.getDeployedApps = async ctx => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.deleteGlobalUser = async (ctx, email) => {
|
exports.deleteGlobalUser = async (ctx, globalId) => {
|
||||||
const endpoint = `/api/admin/users/${email}`
|
const endpoint = `/api/admin/users/${globalId}`
|
||||||
const reqCfg = { method: "DELETE" }
|
const reqCfg = { method: "DELETE" }
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
checkSlashesInUrl(env.WORKER_URL + endpoint),
|
checkSlashesInUrl(env.WORKER_URL + endpoint),
|
||||||
|
@ -70,8 +70,10 @@ exports.deleteGlobalUser = async (ctx, email) => {
|
||||||
return response.json()
|
return response.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getGlobalUsers = async (ctx, appId = null, email = null) => {
|
exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => {
|
||||||
const endpoint = email ? `/api/admin/users/${email}` : `/api/admin/users`
|
const endpoint = globalId
|
||||||
|
? `/api/admin/users/${globalId}`
|
||||||
|
: `/api/admin/users`
|
||||||
const reqCfg = { method: "GET" }
|
const reqCfg = { method: "GET" }
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
checkSlashesInUrl(env.WORKER_URL + endpoint),
|
checkSlashesInUrl(env.WORKER_URL + endpoint),
|
||||||
|
@ -89,8 +91,8 @@ exports.getGlobalUsers = async (ctx, appId = null, email = null) => {
|
||||||
return users
|
return users
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.saveGlobalUser = async (ctx, appId, email, body) => {
|
exports.saveGlobalUser = async (ctx, appId, body, globalId = null) => {
|
||||||
const globalUser = await exports.getGlobalUsers(ctx, appId, email)
|
const globalUser = await exports.getGlobalUsers(ctx, appId, globalId)
|
||||||
const roles = globalUser.roles || {}
|
const roles = globalUser.roles || {}
|
||||||
if (body.roleId) {
|
if (body.roleId) {
|
||||||
roles[appId] = body.roleId
|
roles[appId] = body.roleId
|
||||||
|
@ -100,9 +102,9 @@ exports.saveGlobalUser = async (ctx, appId, email, body) => {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: {
|
body: {
|
||||||
...globalUser,
|
...globalUser,
|
||||||
email,
|
|
||||||
password: body.password || undefined,
|
password: body.password || undefined,
|
||||||
status: body.status,
|
status: body.status,
|
||||||
|
email: body.email,
|
||||||
roles,
|
roles,
|
||||||
builder: {
|
builder: {
|
||||||
global: true,
|
global: true,
|
||||||
|
@ -124,5 +126,8 @@ exports.saveGlobalUser = async (ctx, appId, email, body) => {
|
||||||
delete body.status
|
delete body.status
|
||||||
delete body.roles
|
delete body.roles
|
||||||
delete body.builder
|
delete body.builder
|
||||||
return body
|
return {
|
||||||
|
...body,
|
||||||
|
_id: json._id,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue