User fixes wip
This commit is contained in:
parent
f2bfe40c60
commit
c29f3768fa
|
@ -18,6 +18,7 @@ export enum ViewName {
|
||||||
LINK = "by_link",
|
LINK = "by_link",
|
||||||
ROUTING = "screen_routes",
|
ROUTING = "screen_routes",
|
||||||
AUTOMATION_LOGS = "automation_logs",
|
AUTOMATION_LOGS = "automation_logs",
|
||||||
|
ACCOUNT_BY_EMAIL = "account_by_email",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeprecatedViews = {
|
export const DeprecatedViews = {
|
||||||
|
@ -41,6 +42,7 @@ export enum DocumentType {
|
||||||
MIGRATIONS = "migrations",
|
MIGRATIONS = "migrations",
|
||||||
DEV_INFO = "devinfo",
|
DEV_INFO = "devinfo",
|
||||||
AUTOMATION_LOG = "log_au",
|
AUTOMATION_LOG = "log_au",
|
||||||
|
ACCOUNT = "acc",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StaticDatabases = {
|
export const StaticDatabases = {
|
||||||
|
|
|
@ -5,6 +5,8 @@ const {
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
} = require("./utils")
|
} = require("./utils")
|
||||||
const { getGlobalDB } = require("../tenancy")
|
const { getGlobalDB } = require("../tenancy")
|
||||||
|
const { StaticDatabases } = require("./constants")
|
||||||
|
const { doWithDB } = require("./");
|
||||||
|
|
||||||
const DESIGN_DB = "_design/database"
|
const DESIGN_DB = "_design/database"
|
||||||
|
|
||||||
|
@ -56,6 +58,31 @@ exports.createNewUserEmailView = async () => {
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.createAccountEmailView = async () => {
|
||||||
|
await doWithDB(StaticDatabases.PLATFORM_INFO.name, async (db) => {
|
||||||
|
let designDoc
|
||||||
|
try {
|
||||||
|
designDoc = await db.get(DESIGN_DB)
|
||||||
|
} catch (err) {
|
||||||
|
// no design doc, make one
|
||||||
|
designDoc = DesignDoc()
|
||||||
|
}
|
||||||
|
const view = {
|
||||||
|
// if using variables in a map function need to inject them before use
|
||||||
|
map: `function(doc) {
|
||||||
|
if (doc._id.startsWith("${DocumentType.ACCOUNT}${SEPARATOR}")) {
|
||||||
|
emit(doc.email.toLowerCase(), doc.tenantId)
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
}
|
||||||
|
designDoc.views = {
|
||||||
|
...designDoc.views,
|
||||||
|
[ViewName.ACCOUNT_BY_EMAIL]: view,
|
||||||
|
}
|
||||||
|
await db.put(designDoc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
exports.createUserAppView = async () => {
|
exports.createUserAppView = async () => {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
let designDoc
|
let designDoc
|
||||||
|
@ -128,23 +155,17 @@ exports.createUserBuildersView = async () => {
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.queryGlobalView = async (viewName, params, db = null) => {
|
exports.queryView = async (viewName, params, db, CreateFuncByName) => {
|
||||||
const CreateFuncByName = {
|
|
||||||
[ViewName.USER_BY_EMAIL]: exports.createNewUserEmailView,
|
|
||||||
[ViewName.BY_API_KEY]: exports.createApiKeyView,
|
|
||||||
[ViewName.USER_BY_BUILDERS]: exports.createUserBuildersView,
|
|
||||||
[ViewName.USER_BY_APP]: exports.createUserAppView,
|
|
||||||
}
|
|
||||||
// can pass DB in if working with something specific
|
|
||||||
if (!db) {
|
|
||||||
db = getGlobalDB()
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
let response = (await db.query(`database/${viewName}`, params)).rows
|
let response = (await db.query(`database/${viewName}`, params)).rows
|
||||||
response = response.map(resp =>
|
response = response.map(resp =>
|
||||||
params.include_docs ? resp.doc : resp.value
|
params.include_docs ? resp.doc : resp.value
|
||||||
)
|
)
|
||||||
|
if (params.arrayResponse) {
|
||||||
|
return response
|
||||||
|
} else {
|
||||||
return response.length <= 1 ? response[0] : response
|
return response.length <= 1 ? response[0] : response
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err != null && err.name === "not_found") {
|
if (err != null && err.name === "not_found") {
|
||||||
const createFunc = CreateFuncByName[viewName]
|
const createFunc = CreateFuncByName[viewName]
|
||||||
|
@ -156,3 +177,27 @@ exports.queryGlobalView = async (viewName, params, db = null) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.queryPlatformView = async (viewName, params) => {
|
||||||
|
const CreateFuncByName = {
|
||||||
|
[ViewName.ACCOUNT_BY_EMAIL]: exports.createAccountEmailView,
|
||||||
|
}
|
||||||
|
|
||||||
|
return doWithDB(StaticDatabases.PLATFORM_INFO.name, async (db) => {
|
||||||
|
return exports.queryView(viewName, params, db, CreateFuncByName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.queryGlobalView = async (viewName, params, db = null) => {
|
||||||
|
const CreateFuncByName = {
|
||||||
|
[ViewName.USER_BY_EMAIL]: exports.createNewUserEmailView,
|
||||||
|
[ViewName.BY_API_KEY]: exports.createApiKeyView,
|
||||||
|
[ViewName.USER_BY_BUILDERS]: exports.createUserBuildersView,
|
||||||
|
[ViewName.USER_BY_APP]: exports.createUserAppView,
|
||||||
|
}
|
||||||
|
// can pass DB in if working with something specific
|
||||||
|
if (!db) {
|
||||||
|
db = getGlobalDB()
|
||||||
|
}
|
||||||
|
return exports.queryView(viewName, params, db, CreateFuncByName)
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
export * from "./analytics"
|
export * from "./analytics"
|
||||||
|
export * from "./user"
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { User } from "../../documents"
|
||||||
|
|
||||||
|
export interface BulkCreateUsersRequest {
|
||||||
|
users: User[]
|
||||||
|
groups: any[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BulkDeleteUsersRequest {
|
||||||
|
userIds: string[]
|
||||||
|
}
|
|
@ -15,8 +15,27 @@ export interface User extends Document {
|
||||||
status?: string
|
status?: string
|
||||||
createdAt?: number // override the default createdAt behaviour - users sdk historically set this to Date.now()
|
createdAt?: number // override the default createdAt behaviour - users sdk historically set this to Date.now()
|
||||||
userGroups?: string[]
|
userGroups?: string[]
|
||||||
|
forceResetPassword?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserRoles {
|
export interface UserRoles {
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// utility types
|
||||||
|
|
||||||
|
export interface BuilderUser extends User {
|
||||||
|
builder: {
|
||||||
|
global: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdminUser extends User {
|
||||||
|
admin: {
|
||||||
|
global: boolean
|
||||||
|
},
|
||||||
|
builder: {
|
||||||
|
global: boolean
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
export * from "./info"
|
export * from "./info"
|
||||||
|
export * from "./users"
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { Document } from "../document";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* doc id is user email
|
||||||
|
*/
|
||||||
|
export interface PlatformUserByEmail extends Document {
|
||||||
|
tenantId: string
|
||||||
|
userId: string
|
||||||
|
}
|
|
@ -46,8 +46,8 @@ export const bulkCreate = async (ctx: any) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let response = await users.bulkCreate(newUsersRequested, groups)
|
const response = await users.bulkCreate(newUsersRequested, groups)
|
||||||
await groupUtils.bulkSaveGroupUsers(groupsToSave, response)
|
await groupUtils.bulkSaveGroupUsers(groupsToSave, response.successful)
|
||||||
|
|
||||||
ctx.body = response
|
ctx.body = response
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
jest.mock("nodemailer")
|
jest.mock("nodemailer")
|
||||||
const { config, request, mocks, structures } = require("../../../tests")
|
import { config, request, mocks, structures } from "../../../tests"
|
||||||
const sendMailMock = mocks.email.mock()
|
const sendMailMock = mocks.email.mock()
|
||||||
const { events } = require("@budibase/backend-core")
|
import { events } from "@budibase/backend-core"
|
||||||
|
import { User, BulkCreateUsersRequest, BulkDeleteUsersRequest } from "@budibase/types"
|
||||||
|
|
||||||
describe("/api/global/users", () => {
|
describe("/api/global/users", () => {
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
@ -12,6 +14,10 @@ describe("/api/global/users", () => {
|
||||||
await config.afterAll()
|
await config.afterAll()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
const sendUserInvite = async () => {
|
const sendUserInvite = async () => {
|
||||||
await config.saveSmtpConfig()
|
await config.saveSmtpConfig()
|
||||||
await config.saveSettingsConfig()
|
await config.saveSettingsConfig()
|
||||||
|
@ -31,6 +37,7 @@ describe("/api/global/users", () => {
|
||||||
return { code, res }
|
return { code, res }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe("invite", () => {
|
||||||
it("should be able to generate an invitation", async () => {
|
it("should be able to generate an invitation", async () => {
|
||||||
const { code, res } = await sendUserInvite()
|
const { code, res } = await sendUserInvite()
|
||||||
|
|
||||||
|
@ -58,8 +65,47 @@ describe("/api/global/users", () => {
|
||||||
expect(events.user.inviteAccepted).toBeCalledTimes(1)
|
expect(events.user.inviteAccepted).toBeCalledTimes(1)
|
||||||
expect(events.user.inviteAccepted).toBeCalledWith(user)
|
expect(events.user.inviteAccepted).toBeCalledWith(user)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const createUser = async (user) => {
|
const bulkCreateUsers = async (users: User[], groups: any[] = []) => {
|
||||||
|
const body: BulkCreateUsersRequest = { users, groups }
|
||||||
|
const res = await request
|
||||||
|
.post(`/api/global/users/bulkCreate`)
|
||||||
|
.send(body)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200)
|
||||||
|
return res.body
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("bulkCreate", () => {
|
||||||
|
|
||||||
|
it("should ignore users existing in the same tenant", async () => {
|
||||||
|
await bulkCreateUsers(toCreate)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should ignore users existing in other tenants", async () => {
|
||||||
|
await bulkCreateUsers(toCreate)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should ignore accounts using the same email", async () => {
|
||||||
|
await bulkCreateUsers(toCreate)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to bulkCreate users with different permissions", async () => {
|
||||||
|
const builder = structures.users.builderUser({ email: "bulkbasic@test.com" })
|
||||||
|
const admin = structures.users.adminUser({ email: "bulkadmin@test.com" })
|
||||||
|
const user = structures.users.user({ email: "bulkuser@test.com" })
|
||||||
|
|
||||||
|
await bulkCreateUsers([builder, admin, user])
|
||||||
|
|
||||||
|
expect(events.user.created).toBeCalledTimes(3)
|
||||||
|
expect(events.user.permissionAdminAssigned).toBeCalledTimes(1)
|
||||||
|
expect(events.user.permissionBuilderAssigned).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const createUser = async (user: User) => {
|
||||||
const existing = await config.getUser(user.email)
|
const existing = await config.getUser(user.email)
|
||||||
if (existing) {
|
if (existing) {
|
||||||
await deleteUser(existing._id)
|
await deleteUser(existing._id)
|
||||||
|
@ -67,13 +113,13 @@ describe("/api/global/users", () => {
|
||||||
return saveUser(user)
|
return saveUser(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateUser = async (user) => {
|
const updateUser = async (user: User) => {
|
||||||
const existing = await config.getUser(user.email)
|
const existing = await config.getUser(user.email)
|
||||||
user._id = existing._id
|
user._id = existing._id
|
||||||
return saveUser(user)
|
return saveUser(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveUser = async (user) => {
|
const saveUser = async (user: User) => {
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/global/users`)
|
.post(`/api/global/users`)
|
||||||
.send(user)
|
.send(user)
|
||||||
|
@ -83,30 +129,20 @@ describe("/api/global/users", () => {
|
||||||
return res.body
|
return res.body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bulkDeleteUsers = async (users: User[]) => {
|
||||||
const bulkCreateUsers = async (users) => {
|
const body: BulkDeleteUsersRequest = {
|
||||||
const res = await request
|
userIds: users.map(u => u._id!)
|
||||||
.post(`/api/global/users/bulkCreate`)
|
|
||||||
.send(users)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
return res.body
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bulkDeleteUsers = async (users) => {
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/global/users/bulkDelete`)
|
.post(`/api/global/users/bulkDelete`)
|
||||||
.send(users)
|
.send(body)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
return res.body
|
return res.body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteUser = async (email: string) => {
|
||||||
|
|
||||||
const deleteUser = async (email) => {
|
|
||||||
const user = await config.getUser(email)
|
const user = await config.getUser(email)
|
||||||
if (user) {
|
if (user) {
|
||||||
await request
|
await request
|
||||||
|
@ -119,7 +155,6 @@ describe("/api/global/users", () => {
|
||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
it("should be able to create a basic user", async () => {
|
it("should be able to create a basic user", async () => {
|
||||||
jest.clearAllMocks()
|
|
||||||
const user = structures.users.user({ email: "basic@test.com" })
|
const user = structures.users.user({ email: "basic@test.com" })
|
||||||
await createUser(user)
|
await createUser(user)
|
||||||
|
|
||||||
|
@ -129,23 +164,8 @@ describe("/api/global/users", () => {
|
||||||
expect(events.user.permissionAdminAssigned).not.toBeCalled()
|
expect(events.user.permissionAdminAssigned).not.toBeCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to bulkCreate users with different permissions", async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
const builder = structures.users.builderUser({ email: "bulkbasic@test.com" })
|
|
||||||
const admin = structures.users.adminUser({ email: "bulkadmin@test.com" })
|
|
||||||
const user = structures.users.user({ email: "bulkuser@test.com" })
|
|
||||||
|
|
||||||
let toCreate = { users: [builder, admin, user], groups: [] }
|
|
||||||
await bulkCreateUsers(toCreate)
|
|
||||||
|
|
||||||
expect(events.user.created).toBeCalledTimes(3)
|
|
||||||
expect(events.user.permissionAdminAssigned).toBeCalledTimes(1)
|
|
||||||
expect(events.user.permissionBuilderAssigned).toBeCalledTimes(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
it("should be able to create an admin user", async () => {
|
it("should be able to create an admin user", async () => {
|
||||||
jest.clearAllMocks()
|
|
||||||
const user = structures.users.adminUser({ email: "admin@test.com" })
|
const user = structures.users.adminUser({ email: "admin@test.com" })
|
||||||
await createUser(user)
|
await createUser(user)
|
||||||
|
|
||||||
|
@ -156,7 +176,6 @@ describe("/api/global/users", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to create a builder user", async () => {
|
it("should be able to create a builder user", async () => {
|
||||||
jest.clearAllMocks()
|
|
||||||
const user = structures.users.builderUser({ email: "builder@test.com" })
|
const user = structures.users.builderUser({ email: "builder@test.com" })
|
||||||
await createUser(user)
|
await createUser(user)
|
||||||
|
|
||||||
|
@ -167,7 +186,6 @@ describe("/api/global/users", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to assign app roles", async () => {
|
it("should be able to assign app roles", async () => {
|
||||||
jest.clearAllMocks()
|
|
||||||
const user = structures.users.user({ email: "assign-roles@test.com" })
|
const user = structures.users.user({ email: "assign-roles@test.com" })
|
||||||
user.roles = {
|
user.roles = {
|
||||||
"app_123": "role1",
|
"app_123": "role1",
|
||||||
|
@ -230,7 +248,7 @@ describe("/api/global/users", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to update a basic user to a builder user", async () => {
|
it("should be able to update a basic user to a builder user", async () => {
|
||||||
let user = structures.users.user({ email: "basic-update-builder@test.com" })
|
const user = structures.users.user({ email: "basic-update-builder@test.com" })
|
||||||
await createUser(user)
|
await createUser(user)
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
@ -243,7 +261,7 @@ describe("/api/global/users", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to update an admin user to a basic user", async () => {
|
it("should be able to update an admin user to a basic user", async () => {
|
||||||
let user = structures.users.adminUser({ email: "admin-update-basic@test.com" })
|
const user = structures.users.adminUser({ email: "admin-update-basic@test.com" })
|
||||||
await createUser(user)
|
await createUser(user)
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
@ -257,7 +275,7 @@ describe("/api/global/users", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to update an builder user to a basic user", async () => {
|
it("should be able to update an builder user to a basic user", async () => {
|
||||||
let user = structures.users.builderUser({ email: "builder-update-basic@test.com" })
|
const user = structures.users.builderUser({ email: "builder-update-basic@test.com" })
|
||||||
await createUser(user)
|
await createUser(user)
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
@ -334,6 +352,29 @@ describe("/api/global/users", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("bulkDelete", () => {
|
||||||
|
|
||||||
|
it("should not be able to bulkDelete account admin as admin", async () => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not be able to bulkDelete account owner as account owner", async () => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to bulk delete users with different permissions", async () => {
|
||||||
|
const builder = structures.users.builderUser({ email: "basic@test.com" })
|
||||||
|
const admin = structures.users.adminUser({ email: "admin@test.com" })
|
||||||
|
const user = structures.users.user({ email: "user@test.com" })
|
||||||
|
|
||||||
|
const createdUsers = await bulkCreateUsers([builder, admin, user])
|
||||||
|
await bulkDeleteUsers(createdUsers)
|
||||||
|
expect(events.user.deleted).toBeCalledTimes(3)
|
||||||
|
expect(events.user.permissionAdminRemoved).toBeCalledTimes(1)
|
||||||
|
expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("destroy", () => {
|
describe("destroy", () => {
|
||||||
it("should be able to destroy a basic user", async () => {
|
it("should be able to destroy a basic user", async () => {
|
||||||
let user = structures.users.user({ email: "destroy@test.com" })
|
let user = structures.users.user({ email: "destroy@test.com" })
|
||||||
|
@ -371,18 +412,11 @@ describe("/api/global/users", () => {
|
||||||
expect(events.user.permissionAdminRemoved).not.toBeCalled()
|
expect(events.user.permissionAdminRemoved).not.toBeCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to bulk delete users with different permissions", async () => {
|
it("should not be able to destroy account admin as admin", async () => {
|
||||||
jest.clearAllMocks()
|
|
||||||
const builder = structures.users.builderUser({ email: "basic@test.com" })
|
|
||||||
const admin = structures.users.adminUser({ email: "admin@test.com" })
|
|
||||||
const user = structures.users.user({ email: "user@test.com" })
|
|
||||||
|
|
||||||
let toCreate = { users: [builder, admin, user], groups: [] }
|
})
|
||||||
let createdUsers = await bulkCreateUsers(toCreate)
|
|
||||||
await bulkDeleteUsers({ userIds: [createdUsers[0]._id, createdUsers[1]._id, createdUsers[2]._id] })
|
it("should not be able to destroy account owner as account owner", async () => {
|
||||||
expect(events.user.deleted).toBeCalledTimes(3)
|
|
||||||
expect(events.user.permissionAdminRemoved).toBeCalledTimes(1)
|
|
||||||
expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1)
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -14,8 +14,10 @@ import {
|
||||||
HTTPError,
|
HTTPError,
|
||||||
accounts,
|
accounts,
|
||||||
migrations,
|
migrations,
|
||||||
|
StaticDatabases,
|
||||||
|
ViewName
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import { MigrationType, User } from "@budibase/types"
|
import { MigrationType, PlatformUserByEmail, User, Account } from "@budibase/types"
|
||||||
import { groups as groupUtils } from "@budibase/pro"
|
import { groups as groupUtils } from "@budibase/pro"
|
||||||
|
|
||||||
const PAGE_LIMIT = 8
|
const PAGE_LIMIT = 8
|
||||||
|
@ -247,6 +249,54 @@ export const addTenant = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getExistingTenantUsers = async (emails: string[]): Promise<User[]> => {
|
||||||
|
return dbUtils.queryGlobalView(ViewName.USER_BY_EMAIL, {
|
||||||
|
keys: emails,
|
||||||
|
include_docs: true,
|
||||||
|
arrayResponse: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getExistingPlatformUsers = async (emails: string[]): Promise<PlatformUserByEmail[]> => {
|
||||||
|
return dbUtils.doWithDB(StaticDatabases.PLATFORM_INFO.name, async (infoDb: any) => {
|
||||||
|
const response = await infoDb.allDocs({
|
||||||
|
keys: emails,
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
return response.rows.map((row: any) => row.doc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getExistingAccounts = async (emails: string[]): Promise<Account[]> => {
|
||||||
|
return dbUtils.queryPlatformView(ViewName.ACCOUNT_BY_EMAIL, {
|
||||||
|
keys: emails,
|
||||||
|
include_docs: true,
|
||||||
|
arrayResponse: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a system-wide search on emails:
|
||||||
|
* - in tenant
|
||||||
|
* - cross tenant
|
||||||
|
* - accounts
|
||||||
|
* return an array of emails that match the supplied emails.
|
||||||
|
*/
|
||||||
|
const searchExistingEmails = async (emails: string[]) => {
|
||||||
|
let matchedEmails: string[] = []
|
||||||
|
|
||||||
|
const existingTenantUsers = await getExistingTenantUsers(emails)
|
||||||
|
matchedEmails.push(...existingTenantUsers.map((user: User) => user.email))
|
||||||
|
|
||||||
|
const existingPlatformUsers = await getExistingPlatformUsers(emails)
|
||||||
|
matchedEmails.push(...existingPlatformUsers.map((user: PlatformUserByEmail) => user._id!))
|
||||||
|
|
||||||
|
const existingAccounts = await getExistingAccounts(emails)
|
||||||
|
matchedEmails.push(...existingAccounts.map((account: Account) => account.email))
|
||||||
|
|
||||||
|
return matchedEmails
|
||||||
|
}
|
||||||
|
|
||||||
export const bulkCreate = async (
|
export const bulkCreate = async (
|
||||||
newUsersRequested: User[],
|
newUsersRequested: User[],
|
||||||
groups: string[]
|
groups: string[]
|
||||||
|
@ -257,19 +307,16 @@ export const bulkCreate = async (
|
||||||
let usersToSave: any[] = []
|
let usersToSave: any[] = []
|
||||||
let newUsers: any[] = []
|
let newUsers: any[] = []
|
||||||
|
|
||||||
const allUsers = await db.allDocs(
|
const emails = newUsersRequested.map((user: User) => user.email)
|
||||||
dbUtils.getGlobalUserParams(null, {
|
const existingEmails = await searchExistingEmails(emails)
|
||||||
include_docs: true,
|
const unsuccessful: { email: string, reason: string }[] = []
|
||||||
})
|
|
||||||
)
|
|
||||||
let mapped = allUsers.rows.map((row: any) => row.id)
|
|
||||||
|
|
||||||
const currentUserEmails = mapped.map((x: any) => x.email) || []
|
|
||||||
for (const newUser of newUsersRequested) {
|
for (const newUser of newUsersRequested) {
|
||||||
if (
|
if (
|
||||||
newUsers.find((x: any) => x.email === newUser.email) ||
|
newUsers.find((x: any) => x.email === newUser.email) ||
|
||||||
currentUserEmails.includes(newUser.email)
|
existingEmails.includes(newUser.email)
|
||||||
) {
|
) {
|
||||||
|
unsuccessful.push({ email: newUser.email, reason: `Email address ${newUser.email} already in use.` })
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
newUser.userGroups = groups
|
newUser.userGroups = groups
|
||||||
|
@ -307,12 +354,17 @@ export const bulkCreate = async (
|
||||||
await apps.syncUserInApps(user._id)
|
await apps.syncUserInApps(user._id)
|
||||||
}
|
}
|
||||||
|
|
||||||
return usersToBulkSave.map(user => {
|
const saved = usersToBulkSave.map(user => {
|
||||||
return {
|
return {
|
||||||
_id: user._id,
|
_id: user._id,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
successful: saved,
|
||||||
|
unsuccessful
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const bulkDelete = async (userIds: any) => {
|
export const bulkDelete = async (userIds: any) => {
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
const TestConfiguration = require("./TestConfiguration")
|
|
||||||
const structures = require("./structures")
|
|
||||||
const mocks = require("./mocks")
|
|
||||||
const config = new TestConfiguration()
|
|
||||||
const request = config.getRequest()
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
structures,
|
|
||||||
mocks,
|
|
||||||
config,
|
|
||||||
request,
|
|
||||||
}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import TestConfiguration from "./TestConfiguration"
|
||||||
|
import structures from "./structures"
|
||||||
|
import mocks from "./mocks"
|
||||||
|
|
||||||
|
const config = new TestConfiguration()
|
||||||
|
const request = config.getRequest()
|
||||||
|
|
||||||
|
const pkg = {
|
||||||
|
structures,
|
||||||
|
mocks,
|
||||||
|
config,
|
||||||
|
request,
|
||||||
|
}
|
||||||
|
|
||||||
|
export = pkg
|
|
@ -1,11 +1,11 @@
|
||||||
const configs = require("./configs")
|
import configs from "./configs"
|
||||||
const users = require("./users")
|
import * as users from "./users"
|
||||||
const groups = require("./groups")
|
import * as groups from "./groups"
|
||||||
|
|
||||||
const TENANT_ID = "default"
|
const TENANT_ID = "default"
|
||||||
const CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306"
|
const CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306"
|
||||||
|
|
||||||
module.exports = {
|
export = {
|
||||||
configs,
|
configs,
|
||||||
users,
|
users,
|
||||||
TENANT_ID,
|
TENANT_ID,
|
|
@ -1,6 +1,7 @@
|
||||||
export const email = "test@test.com"
|
export const email = "test@test.com"
|
||||||
|
import { AdminUser, BuilderUser, User } from "@budibase/types"
|
||||||
|
|
||||||
export const user = (userProps: any) => {
|
export const user = (userProps: any): User => {
|
||||||
return {
|
return {
|
||||||
email: "test@test.com",
|
email: "test@test.com",
|
||||||
password: "test",
|
password: "test",
|
||||||
|
@ -9,16 +10,19 @@ export const user = (userProps: any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const adminUser = (userProps: any) => {
|
export const adminUser = (userProps: any): AdminUser => {
|
||||||
return {
|
return {
|
||||||
...user(userProps),
|
...user(userProps),
|
||||||
admin: {
|
admin: {
|
||||||
global: true,
|
global: true,
|
||||||
},
|
},
|
||||||
|
builder: {
|
||||||
|
global: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const builderUser = (userProps: any) => {
|
export const builderUser = (userProps: any): BuilderUser => {
|
||||||
return {
|
return {
|
||||||
...user(userProps),
|
...user(userProps),
|
||||||
builder: {
|
builder: {
|
||||||
|
|
Loading…
Reference in New Issue