group refactoring

This commit is contained in:
Ruben Fiszel
2022-12-16 19:55:46 +01:00
parent 7a9ab35dc4
commit 8d7dd7359c
5 changed files with 191 additions and 97 deletions
-14
View File
@@ -3312,20 +3312,6 @@
},
"query": "SELECT * FROM group_ WHERE workspace_id = $1 ORDER BY name desc LIMIT $2 OFFSET $3"
},
"add01e9e31d64e88b84c9505fe3de553031e581b1bb173413a9a3e3eb0817b43": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar"
]
}
},
"query": "INSERT INTO usr_to_group (workspace_id, usr, group_) VALUES ($1, $2, $3)"
},
"aeaae7641539d51f4535b04e9a7adb7545bd37d0d706edb54b8f5507403105ee": {
"describe": {
"columns": [
+12 -1
View File
@@ -24,6 +24,7 @@ use windmill_common::{
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Postgres, Transaction};
use windmill_queue::CLOUD_HOSTED;
pub fn workspaced_service() -> Router {
Router::new()
@@ -159,6 +160,16 @@ async fn get_group(
Extension(user_db): Extension<UserDB>,
Path((w_id, name)): Path<(String, String)>,
) -> JsonResult<GroupInfo> {
if *CLOUD_HOSTED && w_id == "demo" && name == "all" && !authed.is_admin {
return Ok(Json(GroupInfo {
workspace_id: w_id,
name: name,
summary: Some("The group that contains all users".to_string()),
members: vec!["redacted_in_demo_workspace".to_string()],
extra_perms: serde_json::json!({}),
}));
}
let mut tx = user_db.begin(&authed).await?;
let group = not_found_if_none(get_group_opt(&mut tx, &w_id, &name).await?, "Group", &name)?;
@@ -266,7 +277,7 @@ async fn add_user(
sqlx::query_as!(
Group,
"INSERT INTO usr_to_group (workspace_id, usr, group_) VALUES ($1, $2, $3)",
"INSERT INTO usr_to_group (workspace_id, usr, group_) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING",
&w_id,
user_username,
name,
+178 -63
View File
@@ -11,11 +11,13 @@
export let name: string
let can_write = false
type Role = 'viewer' | 'member' | 'manager' | 'member_manager'
type Role = 'member' | 'manager' | 'member_manager'
let group: Group | undefined
let members: { name: string; role: Role }[] | undefined = undefined
let members: { member_name: string; role: Role }[] | undefined = undefined
let managing_groups: string[] = []
let usernames: string[] | undefined = []
let username: string = ''
let groups: string[] = []
async function loadUsernames(): Promise<void> {
usernames = await UserService.listUsernames({ workspace: $workspaceStore! })
@@ -28,8 +30,13 @@
}
async function load() {
loadGroups()
await loadGroup()
await loadUsernames()
loadUsernames()
}
async function loadGroups(): Promise<void> {
groups = (await GroupService.listGroups({ workspace: $workspaceStore! })).map((x) => x.name)
}
async function addToGroup() {
@@ -41,41 +48,64 @@
loadGroup()
}
async function addToManagingGroup() {
await GranularAclService.addGranularAcls({
workspace: $workspaceStore ?? '',
path: name,
kind: 'group_',
requestBody: {
owner: 'g/' + new_managing_group,
write: true
}
})
loadGroup()
}
async function loadGroup(): Promise<void> {
group = await GroupService.getGroup({ workspace: $workspaceStore!, name })
can_write = canWrite(group.name!, group.extra_perms ?? {}, $userStore)
members = Object.keys(group?.extra_perms ?? {})
.concat(group?.members ?? [])
.map((x) => {
return {
name: x,
role: getRole(x)
}
})
can_write = canWrite(name!, group.extra_perms ?? {}, $userStore)
members = Array.from(
new Set(
Object.entries(group?.extra_perms ?? {})
.filter(([k, v]) => k.startsWith('u/') && v)
.map(([k, _]) => k.split('/')[1])
.concat(group?.members ?? [])
)
).map((x) => {
return {
member_name: x,
role: getRole(x)
}
})
managing_groups = Object.entries(group?.extra_perms ?? {})
.filter(([k, v]) => k.startsWith('g/') && v)
.map(([k, v]) => k)
}
function getRole(x: string): Role {
const writer = x in (group?.extra_perms ?? {}) && (group?.extra_perms ?? {})[name]
const writer = 'u/' + x in (group?.extra_perms ?? {}) && (group?.extra_perms ?? {})['u/' + x]
const member = group?.members?.includes(x)
console.log(writer, member, x, group?.members, group?.extra_perms)
if (writer && member) {
return 'member_manager'
} else if (writer) {
return 'manager'
} else if (member) {
} else {
return 'member'
}
return 'viewer'
}
let new_managing_group = ''
</script>
<div class="flex flex-col gap-6">
<h2>Summary</h2>
<h1>{name}</h1>
{#if group}
<p>{group?.summary ?? 'No summary'}</p>
{:else}
<Skeleton layout={[[4]]} />
{/if}
<h2>Members</h2>
<h2>Members & Managers</h2>
{#if can_write}
<div class="flex items-start">
<AutoComplete items={usernames} bind:selectedItem={username} />
@@ -92,51 +122,91 @@
<th />
</tr>
<tbody slot="body">
{#each members as { name, role }}<tr>
<td>{name}</td>
{#each members as { member_name, role }}<tr>
<td>{member_name}</td>
<td>
<ToggleButtonGroup
selected={role}
on:selected={async (e) => {
const group = e.detail
// const wasInGroup = (group?.members ?? []).includes(group)
// const inAcl = (
// group?.extra_perms ? Object.keys(group?.extra_perms) : []
// ).includes(group)
if (group == 'member') {
GroupService.addUserToGroup(group)
// GranularAclService.removeGranularAcls({
// workspace: $workspaceStore ?? '',
// path: name,
// kind: 'group_',
// requestBody: $userStore.
// })
}
loadGroup()
}}
>
<ToggleButton position="left" value="member" size="xs"
>Viewer <Tooltip
>A viewer can see who are the members and managers of a group</Tooltip
></ToggleButton
>
<ToggleButton position="left" value="member" size="xs"
>Member <Tooltip
>A Member of a group can see everything the group can see, write to everything
the group can write, and generally act on behalf of the group</Tooltip
></ToggleButton
>
<ToggleButton position="center" value="manager" size="xs"
>Manager <Tooltip
>A manager of a group can manage the group, adding and removing users and change
their roles. He is not necessarily a member of the group.</Tooltip
></ToggleButton
>
<ToggleButton position="right" value="member_manager" size="xs"
>Member & Manager</ToggleButton
>
</ToggleButtonGroup></td
{#if can_write}
<div>
<ToggleButtonGroup
selected={role}
on:selected={async (e) => {
const role = e.detail
// const wasInGroup = (group?.members ?? []).includes(group)
// const inAcl = (
// group?.extra_perms ? Object.keys(group?.extra_perms) : []
// ).includes(group)
if (role == 'member') {
await GroupService.addUserToGroup({
workspace: $workspaceStore ?? '',
name,
requestBody: {
username: member_name
}
})
await GranularAclService.removeGranularAcls({
workspace: $workspaceStore ?? '',
path: name,
kind: 'group_',
requestBody: {
owner: 'u/' + member_name
}
})
} else if (role == 'manager') {
await GroupService.removeUserToGroup({
workspace: $workspaceStore ?? '',
name,
requestBody: {
username: member_name
}
})
await GranularAclService.addGranularAcls({
workspace: $workspaceStore ?? '',
path: name,
kind: 'group_',
requestBody: {
owner: 'u/' + member_name,
write: true
}
})
} else if (role == 'member_manager') {
await GroupService.addUserToGroup({
workspace: $workspaceStore ?? '',
name,
requestBody: {
username: member_name
}
})
await GranularAclService.addGranularAcls({
workspace: $workspaceStore ?? '',
path: name,
kind: 'group_',
requestBody: {
owner: 'u/' + member_name,
write: true
}
})
}
loadGroup()
}}
>
<ToggleButton position="left" value="member" size="xs"
>Member <Tooltip
>A Member of a group can see everything the group can see, write to
everything the group can write, and generally act on behalf of the group</Tooltip
></ToggleButton
>
<ToggleButton position="center" value="manager" size="xs"
>Manager <Tooltip
>A manager of a group can manage the group, adding and removing users and
change their roles. Being a manager does not make you a member.</Tooltip
></ToggleButton
>
<ToggleButton position="right" value="member_manager" size="xs"
>Member & Manager</ToggleButton
>
</ToggleButtonGroup>
</div>
{/if}</td
>
<td>
{#if can_write}
@@ -145,8 +215,53 @@
on:click={async () => {
await GroupService.removeUserToGroup({
workspace: $workspaceStore ?? '',
name: group?.name ?? '',
requestBody: { username: name }
name,
requestBody: { username: member_name }
})
loadGroup()
}}>remove</button
>
{/if}</td
>
</tr>{/each}
</tbody>
</TableCustom>
<h2 class="mt-10"
>Groups managing this group <Tooltip>Any member of those groups can manage this group</Tooltip
></h2
>
<div class="flex items-start">
<AutoComplete items={groups} bind:selectedItem={new_managing_group} />
<Button
variant="contained"
color="blue"
size="sm"
btnClasses="!ml-4"
on:click={addToManagingGroup}
>
Add group managing this group
</Button>
</div>
<TableCustom>
<tr slot="header-row">
<th>group</th>
<th />
</tr>
<tbody slot="body">
{#each managing_groups as managing_group}<tr>
<td>{managing_group.split('/')[1]}</td>
<td>
{#if can_write}
<button
class="ml-2 text-red-500"
on:click={async () => {
await GranularAclService.removeGranularAcls({
workspace: $workspaceStore ?? '',
path: name,
kind: 'group_',
requestBody: {
owner: managing_group
}
})
loadGroup()
}}>remove</button
-1
View File
@@ -18,7 +18,6 @@
import CenteredPage from '$lib/components/CenteredPage.svelte'
import Icon from 'svelte-awesome'
import { faCross, faEdit, faPlay, faPlus, faQuestion } from '@fortawesome/free-solid-svg-icons'
import { Alert } from '$lib/components/common'
let logs: AuditLog[]
let usernames: string[]
+1 -18
View File
@@ -26,7 +26,6 @@
let newGroupName: string = ''
let groups: GroupW[] = []
let shareModal: ShareModal
let groupDrawer: Drawer
async function loadGroups(): Promise<void> {
@@ -59,14 +58,6 @@
let editGroupName: string = ''
</script>
<ShareModal
bind:this={shareModal}
kind="group_"
on:change={() => {
loadGroups()
}}
/>
<Drawer bind:this={groupDrawer}>
<DrawerContent title="Group {editGroupName}" on:close={groupDrawer.closeDrawer}>
<GroupEditor name={editGroupName} />
@@ -120,7 +111,7 @@
placement="bottom-end"
dropdownItems={[
{
displayName: 'Manage members',
displayName: 'Manage group',
icon: faEdit,
disabled: !canWrite,
action: () => {
@@ -128,14 +119,6 @@
groupDrawer.openDrawer()
}
},
{
displayName: 'Manage ACL of the group',
icon: faShare,
disabled: !canWrite,
action: () => {
shareModal.openDrawer(name)
}
},
{
displayName: 'Delete',