mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 00:01:34 +00:00
group refactoring
This commit is contained in:
@@ -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": [
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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[]
|
||||
|
||||
@@ -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',
|
||||
|
||||
|
||||
Reference in New Issue
Block a user