1
0
Fork 0
mirror of synced 2024-06-14 00:14:39 +12:00

refactor groups into pro and some other pr comments

This commit is contained in:
Peter Clement 2022-07-26 20:04:29 +01:00
parent 3914501084
commit 0c831f369d
11 changed files with 77 additions and 106 deletions

View file

@ -28,9 +28,9 @@ class UsageLimitError extends HTTPError {
}
class FeatureDisabledError extends HTTPError {
constructor(message, limitName) {
constructor(message, featureName) {
super(message, 400, codes.FEATURE_DISABLED, type)
this.limitName = limitName
this.featureName = featureName
}
}

View file

@ -7,7 +7,7 @@ import {
GroupUpdatedEvent,
GroupUsersAddedEvent,
GroupUsersDeletedEvent,
GroupsAddedOnboarding,
GroupAddedOnboardingEvent,
UserGroupRoles,
} from "@budibase/types"
@ -34,24 +34,22 @@ export async function deleted(group: UserGroup) {
export async function usersAdded(emails: string[], group: UserGroup) {
const properties: GroupUsersAddedEvent = {
emails,
count: emails.length,
groupId: group._id as string,
}
await publishEvent(Event.USER_GROUP_USER_ADDED, properties)
await publishEvent(Event.USER_GROUP_USERS_ADDED, properties)
}
export async function usersDeleted(emails: string[], group: UserGroup) {
const properties: GroupUsersDeletedEvent = {
emails,
count: emails.length,
groupId: group._id as string,
}
await publishEvent(Event.USER_GROUP_USER_REMOVED, properties)
await publishEvent(Event.USER_GROUP_USERS_REMOVED, properties)
}
export async function createdOnboarding(groupId: string) {
const properties: GroupsAddedOnboarding = {
const properties: GroupAddedOnboardingEvent = {
groupId: groupId,
onboarding: true,
}

View file

@ -24,6 +24,21 @@ describe("/users", () => {
describe("fetch", () => {
it("returns a list of users from an instance db", async () => {
await config.createUser("uuidx")
await config.createUser("uuidy")
const res = await request
.get(`/api/users/metadata`)
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
expect(res.body.length).toBe(3)
expect(res.body.find(u => u._id === `ro_ta_users_us_uuidx`)).toBeDefined()
expect(res.body.find(u => u._id === `ro_ta_users_us_uuidy`)).toBeDefined()
})
it("should apply authorization to endpoint", async () => {
await config.createUser()
await checkPermissionsEndpoint({

View file

@ -14,8 +14,9 @@ const env = require("../environment")
const { getAppId } = require("@budibase/backend-core/context")
const { groups } = require("@budibase/pro")
exports.updateAppRole = async (user, { appId } = {}) => {
exports.updateAppRole = (user, { appId } = {}) => {
appId = appId || getAppId()
if (!user || !user.roles) {
return user
}
@ -31,21 +32,33 @@ exports.updateAppRole = async (user, { appId } = {}) => {
// if a role wasn't found then either set as admin (builder) or public (everyone else)
if (!user.roleId && user.builder && user.builder.global) {
user.roleId = BUILTIN_ROLE_IDS.ADMIN
} else if (!user.roleId && !user?.userGroups?.length) {
} else if (!user.roleId) {
user.roleId = BUILTIN_ROLE_IDS.PUBLIC
} else if (!user.roleId && user?.userGroups?.length) {
let roleId = await groups.getGroupRoleId(user, appId)
user.roleId = roleId
}
delete user.roles
return user
}
function processUser(user, { appId } = {}) {
async function checkGroupRoles(user, { appId } = {}) {
if (!user.roleId) {
let roleId = await groups.getGroupRoleId(user, appId)
user.roleId = roleId
}
return user
}
async function processUser(user, { appId } = {}) {
if (user) {
delete user.password
}
return exports.updateAppRole(user, { appId })
user = await exports.updateAppRole(user, { appId })
if (user?.userGroups?.length) {
user = await checkGroupRoles(user, { appId })
}
return user
}
exports.getCachedSelf = async (ctx, appId) => {
@ -93,6 +106,8 @@ exports.getGlobalUsers = async (users = null) => {
if (!appId) {
return globalUsers
}
console.log("maybe??")
return globalUsers.map(user => exports.updateAppRole(user))
}

View file

@ -14,8 +14,8 @@ export interface User extends Document {
password?: string
status?: string
createdAt?: number
userGroups?: string[] // 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[]
}
export interface UserRoles {

View file

@ -5,7 +5,7 @@ export interface UserGroup extends Document {
icon: string
color: string
users: User[]
apps: any
apps: any[]
roles: UserGroupRoles
createdAt?: number
}

View file

@ -155,10 +155,10 @@ export enum Event {
USER_GROUP_CREATED = "user_group:created",
USER_GROUP_UPDATED = "user_group:updated",
USER_GROUP_DELETED = "user_group:deleted",
USER_GROUP_USER_ADDED = "user_group_user:added",
USER_GROUP_USER_REMOVED = "user_group_user:deleted",
USER_GROUP_PERMISSIONS_EDITED = "user_group_permissions:edited",
USER_GROUP_ONBOARDING = "user_group_onboarding:added",
USER_GROUP_USERS_ADDED = "user_group:user_added",
USER_GROUP_USERS_REMOVED = "user_group_:users_deleted",
USER_GROUP_PERMISSIONS_EDITED = "user_group_:permissions_edited",
USER_GROUP_ONBOARDING = "user_group_:onboarding_added",
}
// properties added at the final stage of the event pipeline

View file

@ -13,18 +13,16 @@ export interface GroupDeletedEvent extends BaseEvent {
}
export interface GroupUsersAddedEvent extends BaseEvent {
emails: string[]
count: number
groupId: string
}
export interface GroupUsersDeletedEvent extends BaseEvent {
emails: string[]
count: number
groupId: string
}
export interface GroupsAddedOnboarding extends BaseEvent {
export interface GroupAddedOnboardingEvent extends BaseEvent {
groupId: string
onboarding: boolean
}

View file

@ -13,7 +13,7 @@ import {
cache,
} from "@budibase/backend-core"
import { checkAnyUserExists } from "../../../utilities/users"
import { groups as groupUtils } from "@budibase/pro"
const MAX_USERS_UPLOAD_LIMIT = 1000
export const save = async (ctx: any) => {
@ -46,34 +46,8 @@ export const bulkCreate = async (ctx: any) => {
try {
let response = await users.bulkCreate(newUsersRequested, groups)
await groupUtils.bulkSaveGroupUsers(groupsToSave, response)
if (groupsToSave.length) {
let groupsPromises: any = []
groupsToSave.forEach(async (userGroup: UserGroup) => {
userGroup.users = [...userGroup.users, ...response]
groupsPromises.push(db.put(userGroup))
})
const groupResults = await Promise.all(groupsPromises)
await db.bulkDocs(groupResults)
let eventFns = []
for (const group of groupResults) {
eventFns.push(() => {
events.group.usersAdded(
response.map(u => u.email),
group
)
})
eventFns.push(() => {
events.group.createdOnboarding(group._id as string)
})
}
for (const fn of eventFns) {
await fn()
}
}
ctx.body = response
} catch (err: any) {
ctx.throw(err.status || 400, err)
@ -142,27 +116,9 @@ export const adminUser = async (ctx: any) => {
export const destroy = async (ctx: any) => {
const id = ctx.params.id
const db = tenancy.getGlobalDB()
let user: User = await db.get(id)
let groups = user.userGroups
await users.destroy(id, ctx.user)
// Remove asssosicated groups
if (groups) {
let groupsPromises = []
for (const groupId of groups) {
let group = await db.get(groupId)
let updatedUsersGroup = group.users.filter(
(groupUser: any) => groupUser.email !== user.email
)
group.users = updatedUsersGroup
groupsPromises.push(db.put(group))
}
await db.bulkDocs(groupsPromises)
}
ctx.body = {
message: `User ${id} deleted.`,
}
@ -170,30 +126,9 @@ export const destroy = async (ctx: any) => {
export const bulkDelete = async (ctx: any) => {
const { userIds } = ctx.request.body
const db = tenancy.getGlobalDB()
try {
let { groupsToModify, usersResponse } = await users.bulkDelete(userIds)
// if there are groups to delete, do it here
if (Object.keys(groupsToModify).length) {
let groups = (
await db.allDocs({
include_docs: true,
keys: Object.keys(groupsToModify),
})
).rows.map((group: any) => group.doc)
let groupsPromises = []
for (const group of groups) {
let updatedUsersGroup = group.users.filter(
(groupUser: any) => !groupsToModify[group._id].includes(groupUser._id)
)
group.users = updatedUsersGroup
groupsPromises.push(db.put(group))
}
await db.bulkDocs(groupsPromises)
}
await groupUtils.bulkDeleteGroupUsers(groupsToModify)
ctx.body = {
message: `${usersResponse.length} user(s) deleted`,
@ -314,7 +249,6 @@ export const inviteAccept = async (ctx: any) => {
return saved
})
} catch (err: any) {
console.log(err)
if (err.code === errors.codes.USAGE_LIMIT_EXCEEDED) {
// explicitly re-throw limit exceeded errors
ctx.throw(400, err)

View file

@ -1,2 +1 @@
export * from "./users"
export * from "./events"

View file

@ -3,7 +3,6 @@ import { quotas } from "@budibase/pro"
import * as apps from "../../utilities/appService"
import * as eventHelpers from "./events"
import {
events,
tenancy,
utils,
db as dbUtils,
@ -16,8 +15,8 @@ import {
accounts,
migrations,
} from "@budibase/backend-core"
import { MigrationType, UserGroup } from "@budibase/types"
import { build } from "joi"
import { MigrationType, User } from "@budibase/types"
import { groups as groupUtils } from "@budibase/pro/"
const PAGE_LIMIT = 8
@ -107,10 +106,11 @@ export const buildUser = async (
) => {
let { password, _id } = user
// get the password, make sure one is defined
let hashedPassword
if (password) {
hashedPassword = opts.hashPassword ? await utils.hash(password) : password
} else if (dbUser) {
hashedPassword = dbUser.password
} else if (opts.requirePassword) {
throw "Password must be specified."
}
@ -125,7 +125,6 @@ export const buildUser = async (
password: hashedPassword,
tenantId,
}
// make sure the roles object is always present
if (!user.roles) {
user.roles = {}
@ -202,6 +201,7 @@ export const save = async (
const putUserFn = () => {
return db.put(builtUser)
}
console.log(builtUser)
if (eventHelpers.isAddingBuilder(builtUser, dbUser)) {
response = await quotas.addDeveloper(putUserFn)
} else {
@ -244,7 +244,10 @@ export const addTenant = async (
}
}
export const bulkCreate = async (newUsersRequested: any[], groups: any) => {
export const bulkCreate = async (
newUsersRequested: User[],
groups: string[]
) => {
const db = tenancy.getGlobalDB()
const tenantId = tenancy.getTenantId()
@ -291,16 +294,18 @@ export const bulkCreate = async (newUsersRequested: any[], groups: any) => {
})
const usersToBulkSave = await Promise.all(usersToSave)
await quotas.addDevelopers(() => db.bulkDocs(usersToBulkSave), builderCount)
const response = await quotas.addDevelopers(
() => db.bulkDocs(usersToBulkSave),
builderCount
)
// Post processing of bulk added users, i.e events and cache operations
for (const user of usersToBulkSave) {
delete user.password
await eventHelpers.handleSaveEvents(user, null)
await apps.syncUserInApps(user._id)
//await eventHelpers.handleSaveEvents(user, null)
//await apps.syncUserInApps(user._id)
}
return usersToBulkSave
return response
}
export const bulkDelete = async (userIds: any) => {
@ -356,6 +361,7 @@ export const bulkDelete = async (userIds: any) => {
export const destroy = async (id: string, currentUser: any) => {
const db = tenancy.getGlobalDB()
const dbUser = await db.get(id)
let groups = dbUser.userGroups
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
// root account holder can't be deleted from inside budibase
@ -371,7 +377,13 @@ export const destroy = async (id: string, currentUser: any) => {
}
await deprovisioning.removeUserFromInfoDB(dbUser)
await db.remove(dbUser._id, dbUser._rev)
if (groups) {
await groupUtils.deleteGroupUsers(groups, dbUser)
}
await eventHelpers.handleDeleteEvents(dbUser)
await quotas.removeUser(dbUser)
await cache.user.invalidateUser(dbUser._id)