Merge pull request #10153 from Budibase/budi-6803-delete-users-when-group-removed

BUDI-6803 - Remove users that should not be there anymore when syncGlobalUsers
This commit is contained in:
Adria Navarro 2023-03-31 13:15:11 +02:00 committed by GitHub
commit cfb93063b2
5 changed files with 186 additions and 7 deletions

View File

@ -8,4 +8,5 @@ export * as plugins from "./plugins"
export * as sso from "./sso" export * as sso from "./sso"
export * as tenant from "./tenants" export * as tenant from "./tenants"
export * as users from "./users" export * as users from "./users"
export * as userGroups from "./userGroups"
export { generator } from "./generator" export { generator } from "./generator"

View File

@ -0,0 +1,10 @@
import { UserGroup } from "@budibase/types"
import { generator } from "./generator"
export function userGroup(): UserGroup {
return {
name: generator.word(),
icon: generator.word(),
color: generator.word(),
}
}

View File

@ -0,0 +1,159 @@
import { db, roles } from "@budibase/backend-core"
import { structures } from "@budibase/backend-core/tests"
import { sdk as proSdk } from "@budibase/pro"
import TestConfiguration from "../../../tests/utilities/TestConfiguration"
import { rawUserMetadata, syncGlobalUsers } from "../utils"
describe("syncGlobalUsers", () => {
const config = new TestConfiguration()
beforeEach(async () => {
await config.init()
})
afterAll(config.end)
it("the default user is synced", async () => {
await config.doInContext(config.appId, async () => {
await syncGlobalUsers()
const metadata = await rawUserMetadata()
expect(metadata).toHaveLength(1)
expect(metadata).toEqual([
expect.objectContaining({
_id: db.generateUserMetadataID(config.user._id),
}),
])
})
})
it("admin and builders users are synced", async () => {
const user1 = await config.createUser({ admin: true })
const user2 = await config.createUser({ admin: false, builder: true })
await config.doInContext(config.appId, async () => {
expect(await rawUserMetadata()).toHaveLength(1)
await syncGlobalUsers()
const metadata = await rawUserMetadata()
expect(metadata).toHaveLength(3)
expect(metadata).toContainEqual(
expect.objectContaining({
_id: db.generateUserMetadataID(user1._id),
})
)
expect(metadata).toContainEqual(
expect.objectContaining({
_id: db.generateUserMetadataID(user2._id),
})
)
})
})
it("app users are not synced if not specified", async () => {
const user = await config.createUser({ admin: false, builder: false })
await config.doInContext(config.appId, async () => {
await syncGlobalUsers()
const metadata = await rawUserMetadata()
expect(metadata).toHaveLength(1)
expect(metadata).not.toContainEqual(
expect.objectContaining({
_id: db.generateUserMetadataID(user._id),
})
)
})
})
it("app users are added when group is assigned to app", async () => {
await config.doInTenant(async () => {
const group = await proSdk.groups.save(structures.userGroups.userGroup())
const user1 = await config.createUser({ admin: false, builder: false })
const user2 = await config.createUser({ admin: false, builder: false })
await proSdk.groups.addUsers(group.id, [user1._id, user2._id])
await config.doInContext(config.appId, async () => {
await syncGlobalUsers()
expect(await rawUserMetadata()).toHaveLength(1)
await proSdk.groups.updateGroupApps(group.id, {
appsToAdd: [
{ appId: config.prodAppId!, roleId: roles.BUILTIN_ROLE_IDS.BASIC },
],
})
await syncGlobalUsers()
const metadata = await rawUserMetadata()
expect(metadata).toHaveLength(3)
expect(metadata).toContainEqual(
expect.objectContaining({
_id: db.generateUserMetadataID(user1._id),
})
)
expect(metadata).toContainEqual(
expect.objectContaining({
_id: db.generateUserMetadataID(user2._id),
})
)
})
})
})
it("app users are removed when app is removed from user group", async () => {
await config.doInTenant(async () => {
const group = await proSdk.groups.save(structures.userGroups.userGroup())
const user1 = await config.createUser({ admin: false, builder: false })
const user2 = await config.createUser({ admin: false, builder: false })
await proSdk.groups.updateGroupApps(group.id, {
appsToAdd: [
{ appId: config.prodAppId!, roleId: roles.BUILTIN_ROLE_IDS.BASIC },
],
})
await proSdk.groups.addUsers(group.id, [user1._id, user2._id])
await config.doInContext(config.appId, async () => {
await syncGlobalUsers()
expect(await rawUserMetadata()).toHaveLength(3)
await proSdk.groups.updateGroupApps(group.id, {
appsToRemove: [{ appId: config.prodAppId! }],
})
await syncGlobalUsers()
const metadata = await rawUserMetadata()
expect(metadata).toHaveLength(1)
})
})
})
it("app users are removed when app is removed from user group", async () => {
await config.doInTenant(async () => {
const group = await proSdk.groups.save(structures.userGroups.userGroup())
const user1 = await config.createUser({ admin: false, builder: false })
const user2 = await config.createUser({ admin: false, builder: false })
await proSdk.groups.updateGroupApps(group.id, {
appsToAdd: [
{ appId: config.prodAppId!, roleId: roles.BUILTIN_ROLE_IDS.BASIC },
],
})
await proSdk.groups.addUsers(group.id, [user1._id, user2._id])
await config.doInContext(config.appId, async () => {
await syncGlobalUsers()
expect(await rawUserMetadata()).toHaveLength(3)
await proSdk.groups.removeUsers(group.id, [user1._id])
await syncGlobalUsers()
const metadata = await rawUserMetadata()
expect(metadata).toHaveLength(2)
expect(metadata).not.toContainEqual(
expect.objectContaining({
_id: db.generateUserMetadataID(user1._id),
})
)
})
})
})
})

View File

@ -6,25 +6,33 @@ import {
InternalTables, InternalTables,
} from "../../db/utils" } from "../../db/utils"
import { isEqual } from "lodash" import { isEqual } from "lodash"
import { ContextUser, UserMetadata } from "@budibase/types"
export function combineMetadataAndUser(user: any, metadata: any) { export function combineMetadataAndUser(
user: ContextUser,
metadata: UserMetadata | UserMetadata[]
) {
const metadataId = generateUserMetadataID(user._id!)
const found = Array.isArray(metadata)
? metadata.find(doc => doc._id === metadataId)
: metadata
// skip users with no access // skip users with no access
if ( if (
user.roleId == null || user.roleId == null ||
user.roleId === rolesCore.BUILTIN_ROLE_IDS.PUBLIC user.roleId === rolesCore.BUILTIN_ROLE_IDS.PUBLIC
) { ) {
// If it exists and it should not, we must remove it
if (found?._id) {
return { ...found, _deleted: true }
}
return null return null
} }
delete user._rev delete user._rev
const metadataId = generateUserMetadataID(user._id)
const newDoc = { const newDoc = {
...user, ...user,
_id: metadataId, _id: metadataId,
tableId: InternalTables.USER_METADATA, tableId: InternalTables.USER_METADATA,
} }
const found = Array.isArray(metadata)
? metadata.find(doc => doc._id === metadataId)
: metadata
// copy rev over for the purposes of equality check // copy rev over for the purposes of equality check
if (found) { if (found) {
newDoc._rev = found._rev newDoc._rev = found._rev
@ -58,7 +66,7 @@ export async function syncGlobalUsers() {
]) ])
const toWrite = [] const toWrite = []
for (let user of users) { for (let user of users) {
const combined = await combineMetadataAndUser(user, metadata) const combined = combineMetadataAndUser(user, metadata)
if (combined) { if (combined) {
toWrite.push(combined) toWrite.push(combined)
} }

View File

@ -47,6 +47,7 @@ import {
SourceName, SourceName,
Table, Table,
SearchFilters, SearchFilters,
UserRoles,
} from "@budibase/types" } from "@budibase/types"
type DefaultUserValues = { type DefaultUserValues = {
@ -277,7 +278,7 @@ class TestConfiguration {
email?: string email?: string
builder?: boolean builder?: boolean
admin?: boolean admin?: boolean
roles?: any roles?: UserRoles
} = {} } = {}
) { ) {
let { id, firstName, lastName, email, builder, admin, roles } = user let { id, firstName, lastName, email, builder, admin, roles } = user