update handling of group roles

This commit is contained in:
Peter Clement 2022-07-19 14:20:57 +01:00
parent c5b9be60c7
commit 3f5fea9adc
11 changed files with 14582 additions and 28 deletions

View File

@ -8,7 +8,7 @@
"editor.defaultFormatter": "vscode.json-language-features" "editor.defaultFormatter": "vscode.json-language-features"
}, },
"[javascript]": { "[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "vscode.typescript-language-features"
}, },
"debug.javascript.terminalOptions": { "debug.javascript.terminalOptions": {
"skipFiles": [ "skipFiles": [
@ -16,4 +16,7 @@
"<node_internals>/**" "<node_internals>/**"
] ]
}, },
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
},
} }

View File

@ -13,7 +13,7 @@
notifications, notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import { onMount } from "svelte" import { onMount } from "svelte"
import { apps, organisation, auth } from "stores/portal" import { apps, organisation, auth, groups } from "stores/portal"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { AppStatus } from "constants" import { AppStatus } from "constants"
import { gradient } from "actions" import { gradient } from "actions"
@ -30,20 +30,37 @@
try { try {
await organisation.init() await organisation.init()
await apps.load() await apps.load()
await groups.actions.init()
} catch (error) { } catch (error) {
notifications.error("Error loading apps") notifications.error("Error loading apps")
} }
loaded = true loaded = true
}) })
const publishedAppsOnly = app => app.status === AppStatus.DEPLOYED const publishedAppsOnly = app => app.status === AppStatus.DEPLOYED
$: userGroups = $groups.filter(group =>
group.users.find(user => user._id === $auth.user?._id)
)
let userApps = []
$: publishedApps = $apps.filter(publishedAppsOnly) $: publishedApps = $apps.filter(publishedAppsOnly)
$: userApps = $auth.user?.builder?.global
$: {
if (!Object.keys($auth.user?.roles).length) {
userApps = $auth.user?.builder?.global
? publishedApps
: publishedApps.filter(app => {
return userGroups.find(group => {
return Object.keys(group.roles).includes(app.prodId)
})
})
} else {
userApps = $auth.user?.builder?.global
? publishedApps ? publishedApps
: publishedApps.filter(app => : publishedApps.filter(app =>
Object.keys($auth.user?.roles).includes(app.prodId) Object.keys($auth.user?.roles).includes(app.prodId)
) )
}
}
function getUrl(app) { function getUrl(app) {
if (app.url) { if (app.url) {

View File

@ -31,7 +31,7 @@
$: page = $pageInfo.page $: page = $pageInfo.page
$: fetchUsers(page, search) $: fetchUsers(page, search)
$: group = $groups.find(x => x._id === groupId) $: group = $groups.find(x => x._id === groupId)
$: console.log(group.users)
async function addAll() { async function addAll() {
group.users = selectedUsers group.users = selectedUsers
await groups.actions.save(group) await groups.actions.save(group)
@ -175,9 +175,11 @@
iconBackground={app?.icon?.color || ""} iconBackground={app?.icon?.color || ""}
> >
<div class="title "> <div class="title ">
<StatusLight color={RoleUtils.getRoleColour(group.role)} /> <StatusLight
color={RoleUtils.getRoleColour(group.roles[app.prodId])}
/>
<div style="margin-left: var(--spacing-s);"> <div style="margin-left: var(--spacing-s);">
<Body size="XS">{group.role}</Body> <Body size="XS">{group.roles[app.prodId]}</Body>
</div> </div>
</div> </div>
</ListItem> </ListItem>

View File

@ -40,6 +40,7 @@
let selectedGroups = [] let selectedGroups = []
let allAppList = [] let allAppList = []
$: user = users.get(userId)
$: isProPlan = $auth.user?.license.plan.type !== Constants.PlanType.FREE $: isProPlan = $auth.user?.license.plan.type !== Constants.PlanType.FREE
$: allAppList = $apps $: allAppList = $apps
@ -69,7 +70,7 @@
selectedGroups && selectedGroups &&
group?.name?.toLowerCase().includes(searchTerm.toLowerCase()) group?.name?.toLowerCase().includes(searchTerm.toLowerCase())
) )
$: console.log($groups)
$: userGroups = $groups.filter(x => { $: userGroups = $groups.filter(x => {
return x.users?.find(y => { return x.users?.find(y => {
return y._id === userId return y._id === userId
@ -133,16 +134,15 @@
async function addGroup(groupId) { async function addGroup(groupId) {
let selectedGroup = selectedGroups.includes(groupId) let selectedGroup = selectedGroups.includes(groupId)
let newUser = $users.find(user => user._id === userId)
let group = $groups.find(group => group._id === groupId) let group = $groups.find(group => group._id === groupId)
if (selectedGroup) { if (selectedGroup) {
selectedGroups = selectedGroups.filter(id => id === selectedGroup) selectedGroups = selectedGroups.filter(id => id === selectedGroup)
let newUsers = group.users.filter(user => user._id !== newUser._id) let newUsers = group.users.filter(groupUser => user._id !== groupUser._id)
group.users = newUsers group.users = newUsers
} else { } else {
selectedGroups = [...selectedGroups, groupId] selectedGroups = [...selectedGroups, groupId]
group.users.push(newUser) group.users.push(user)
} }
await groups.actions.save(group) await groups.actions.save(group)

View File

@ -9,6 +9,7 @@
Modal, Modal,
notifications, notifications,
Pagination, Pagination,
Icon,
} from "@budibase/bbui" } from "@budibase/bbui"
import { onMount } from "svelte" import { onMount } from "svelte"
@ -31,7 +32,7 @@
$: fetchUsers(page, search) $: fetchUsers(page, search)
$: isProPlan = $auth.user?.license.plan.type !== Constants.PlanType.FREE $: isProPlan = $auth.user?.license.plan.type !== Constants.PlanType.FREE
$: console.log($users.data)
$: appUsers = $: appUsers =
$users.data?.filter(x => { $users.data?.filter(x => {
return Object.keys(x.roles).find(y => { return Object.keys(x.roles).find(y => {
@ -48,7 +49,6 @@
async function addData(appData) { async function addData(appData) {
let gr_prefix = "gr" let gr_prefix = "gr"
let us_prefix = "us" let us_prefix = "us"
console.log(appData)
appData.forEach(async data => { appData.forEach(async data => {
if (data.id.startsWith(gr_prefix)) { if (data.id.startsWith(gr_prefix)) {
let matchedGroup = $groups.find(group => { let matchedGroup = $groups.find(group => {
@ -75,6 +75,35 @@
await users.search({ page, appId: app.prodId }) await users.search({ page, appId: app.prodId })
} }
async function removeUser(user) {
// Remove the user role
const filteredRoles = { ...user.roles }
delete filteredRoles[app?.prodId]
await users.save({
...user,
roles: {
...filteredRoles,
},
})
await users.search({ page, appId: app.prodId })
}
async function removeGroup(group) {
// Remove the user role
let filteredApps = group.apps.filter(x => x.appId !== app.appId)
const filteredRoles = { ...group.roles }
delete filteredRoles[app?.prodId]
await groups.actions.save({
...group,
apps: filteredApps,
roles: { ...filteredRoles },
})
await users.search({ page, appId: app.prodId })
}
async function updateUserRole(role, user) { async function updateUserRole(role, user) {
user.roles[app.prodId] = role user.roles[app.prodId] = role
users.save(user) users.save(user)
@ -145,6 +174,12 @@
Object.keys(group.roles).find(x => x === app.prodId) Object.keys(group.roles).find(x => x === app.prodId)
]} ]}
/> />
<Icon
on:click={() => removeGroup(group)}
hoverable
size="S"
name="Close"
/>
</ListItem> </ListItem>
{/each} {/each}
</List> </List>
@ -161,6 +196,12 @@
Object.keys(user.roles).find(x => x === app.prodId) Object.keys(user.roles).find(x => x === app.prodId)
]} ]}
/> />
<Icon
on:click={() => removeUser(user)}
hoverable
size="S"
name="Close"
/>
</ListItem> </ListItem>
{/each} {/each}
</List> </List>

View File

@ -17,7 +17,8 @@ export function createUsersStore() {
async function get(userId) { async function get(userId) {
try { try {
return await API.getUser(userId) let user = await API.getUser(userId)
return user
} catch (err) { } catch (err) {
return null return null
} }

View File

@ -31,9 +31,9 @@ exports.updateAppRole = async (user, { appId } = {}) => {
// if a role wasn't found then either set as admin (builder) or public (everyone else) // if a role wasn't found then either set as admin (builder) or public (everyone else)
if (!user.roleId && user.builder && user.builder.global) { if (!user.roleId && user.builder && user.builder.global) {
user.roleId = BUILTIN_ROLE_IDS.ADMIN user.roleId = BUILTIN_ROLE_IDS.ADMIN
} else if (!user.roleId) { } else if (!user.roleId && !user?.userGroups?.length) {
user.roleId = BUILTIN_ROLE_IDS.PUBLIC user.roleId = BUILTIN_ROLE_IDS.PUBLIC
} else if (user?.userGroups?.length) { } else if (!user.roleId && user?.userGroups?.length) {
let roleId = await groups.getGroupRoleId(user, appId) let roleId = await groups.getGroupRoleId(user, appId)
user.roleId = roleId user.roleId = roleId
} }

14447
packages/server/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,8 @@ export interface User extends Document {
password?: string password?: string
status?: string status?: string
createdAt?: number // override the default createdAt behaviour - users sdk historically set this to Date.now() createdAt?: number
userGroups?: string[] // override the default createdAt behaviour - users sdk historically set this to Date.now()
} }
export interface UserRoles { export interface UserRoles {

View File

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

View File

@ -47,13 +47,15 @@ export const bulkSave = async (ctx: any) => {
} }
try { try {
let response = [] let response: any[] = []
for (const user of newUsers) { for (const user of newUsers) {
response = await users.save(user, { response.push(
await users.save(user, {
hashPassword: true, hashPassword: true,
requirePassword: user.requirePassword, requirePassword: user.requirePassword,
bulkCreate: false, bulkCreate: false,
}) })
)
} }
// delete passwords and add to group // delete passwords and add to group
@ -63,7 +65,7 @@ export const bulkSave = async (ctx: any) => {
if (groupsToSave.length) { if (groupsToSave.length) {
groupsToSave.forEach(async (userGroup: UserGroup) => { groupsToSave.forEach(async (userGroup: UserGroup) => {
userGroup.users = [...userGroup.users, ...newUsers] userGroup.users = [...userGroup.users, ...response]
await db.put(userGroup) await db.put(userGroup)
events.group.usersAdded( events.group.usersAdded(
newUsers.map(u => u.email), newUsers.map(u => u.email),
@ -141,7 +143,24 @@ export const adminUser = async (ctx: any) => {
export const destroy = async (ctx: any) => { export const destroy = async (ctx: any) => {
const id = ctx.params.id 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) await users.destroy(id, ctx.user)
// Remove asssosicated groups
if (groups) {
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
await db.put(group)
}
}
ctx.body = { ctx.body = {
message: `User ${id} deleted.`, message: `User ${id} deleted.`,
} }
@ -149,10 +168,27 @@ export const destroy = async (ctx: any) => {
export const bulkDelete = async (ctx: any) => { export const bulkDelete = async (ctx: any) => {
const { userIds } = ctx.request.body const { userIds } = ctx.request.body
const db = tenancy.getGlobalDB()
let deleted = 0 let deleted = 0
for (const id of userIds) { for (const id of userIds) {
let user: User = await db.get(id)
let groups = user.userGroups
await users.destroy(id, ctx.user) await users.destroy(id, ctx.user)
if (groups) {
for (const groupId of groups) {
let group = await db.get(groupId)
let updatedUsersGroup = group.users.filter(
(groupUser: any) => groupUser.email !== user.email
)
console.log(updatedUsersGroup)
group.users = updatedUsersGroup
await db.put(group)
}
}
deleted++ deleted++
} }