support manage user permissions on groups

This commit is contained in:
mbecker20
2023-04-09 19:24:07 +00:00
parent 6a8f66f272
commit b13e624a66
6 changed files with 135 additions and 27 deletions

View File

@@ -11,17 +11,17 @@ use crate::{auth::RequestUser, state::State};
impl State {
pub async fn get_group_check_permissions(
&self,
deployment_id: &str,
group_id: &str,
user: &RequestUser,
permission_level: PermissionLevel,
) -> anyhow::Result<Group> {
let group = self.db.get_group(deployment_id).await?;
let group = self.db.get_group(group_id).await?;
let permissions = group.get_user_permissions(&user.id);
if user.is_admin || permissions >= permission_level {
Ok(group)
} else {
Err(anyhow!(
"user does not have required permissions on this deployment"
"user does not have required permissions on this group"
))
}
}

View File

@@ -3,8 +3,8 @@ use axum::{routing::post, Extension, Json, Router};
use helpers::handle_anyhow_error;
use mungos::{doc, Deserialize, Document, Serialize};
use types::{
monitor_timestamp, Build, Deployment, Log, Operation, PermissionLevel, PermissionsTarget,
Procedure, Server, Update, UpdateStatus, UpdateTarget,
monitor_timestamp, Build, Deployment, Group, Log, Operation, PermissionLevel,
PermissionsTarget, Procedure, Server, Update, UpdateStatus, UpdateTarget,
};
use typeshare::typeshare;
@@ -82,10 +82,10 @@ pub fn router() -> Router {
async fn update_permissions(
Extension(state): StateExtension,
Extension(user): RequestUserExtension,
Extension(req_user): RequestUserExtension,
Json(permission_update): Json<PermissionsUpdateBody>,
) -> anyhow::Result<Update> {
if !user.is_admin {
if !req_user.is_admin {
return Err(anyhow!(
"user not authorized for this action (is not admin)"
));
@@ -107,7 +107,7 @@ async fn update_permissions(
operation: Operation::ModifyUserPermissions,
start_ts: monitor_timestamp(),
success: true,
operator: user.id.clone(),
operator: req_user.id.clone(),
status: UpdateStatus::Complete,
..Default::default()
};
@@ -199,9 +199,9 @@ async fn update_permissions(
.procedures
.find_one_by_id(&permission_update.target_id)
.await
.context("failed at find build query")?
.context("failed at find procedure query")?
.ok_or(anyhow!(
"failed to find a build with id {}",
"failed to find a procedure with id {}",
permission_update.target_id
))?;
state
@@ -220,6 +220,33 @@ async fn update_permissions(
target_user.username, permission_update.permission, procedure.name
)
}
PermissionsTarget::Group => {
let group = state
.db
.groups
.find_one_by_id(&permission_update.target_id)
.await
.context("failed at find group query")?
.ok_or(anyhow!(
"failed to find a group with id {}",
permission_update.target_id
))?;
state
.db
.groups
.update_one::<Group>(
&permission_update.target_id,
mungos::Update::Set(doc! {
format!("permissions.{}", permission_update.user_id): permission_update.permission.to_string()
}),
)
.await?;
update.target = UpdateTarget::Group(group.id);
format!(
"user {} given {} permissions on group {}",
target_user.username, permission_update.permission, group.name
)
}
};
update
.logs
@@ -231,10 +258,10 @@ async fn update_permissions(
async fn modify_user_enabled(
Extension(state): StateExtension,
Extension(user): RequestUserExtension,
Extension(req_user): RequestUserExtension,
Json(ModifyUserEnabledBody { user_id, enabled }): Json<ModifyUserEnabledBody>,
) -> anyhow::Result<Update> {
if !user.is_admin {
if !req_user.is_admin {
return Err(anyhow!(
"user does not have permissions for this action (not admin)"
));
@@ -264,7 +291,7 @@ async fn modify_user_enabled(
end_ts: Some(ts),
status: UpdateStatus::Complete,
success: true,
operator: user.id.clone(),
operator: req_user.id.clone(),
..Default::default()
};
update.id = state.add_update(update.clone()).await?;
@@ -273,13 +300,13 @@ async fn modify_user_enabled(
async fn modify_user_create_server_permissions(
Extension(state): StateExtension,
Extension(user): RequestUserExtension,
Extension(req_user): RequestUserExtension,
Json(ModifyUserCreateServerBody {
user_id,
create_server_permissions,
}): Json<ModifyUserCreateServerBody>,
) -> anyhow::Result<Update> {
if !user.is_admin {
if !req_user.is_admin {
return Err(anyhow!(
"user does not have permissions for this action (not admin)"
));
@@ -319,7 +346,7 @@ async fn modify_user_create_server_permissions(
end_ts: Some(ts),
status: UpdateStatus::Complete,
success: true,
operator: user.id.clone(),
operator: req_user.id.clone(),
..Default::default()
};
update.id = state.add_update(update.clone()).await?;
@@ -328,13 +355,13 @@ async fn modify_user_create_server_permissions(
async fn modify_user_create_build_permissions(
Extension(state): StateExtension,
Extension(user): RequestUserExtension,
Extension(req_user): RequestUserExtension,
Json(ModifyUserCreateBuildBody {
user_id,
create_build_permissions,
}): Json<ModifyUserCreateBuildBody>,
) -> anyhow::Result<Update> {
if !user.is_admin {
if !req_user.is_admin {
return Err(anyhow!(
"user does not have permissions for this action (not admin)"
));
@@ -374,7 +401,7 @@ async fn modify_user_create_build_permissions(
end_ts: Some(ts),
status: UpdateStatus::Complete,
success: true,
operator: user.id.clone(),
operator: req_user.id.clone(),
..Default::default()
};
update.id = state.add_update(update.clone()).await?;

View File

@@ -5,13 +5,13 @@ import { useAppState } from "../../state/StateProvider";
import { useUser } from "../../state/UserProvider";
import { PermissionLevel } from "../../types";
import { getId } from "../../util/helpers";
import Flex from "../shared/layout/Flex";
import Grid from "../shared/layout/Grid";
import Flex from "../shared/layout/Flex";
const Resources: Component<{}> = (p) => {
const { user, user_id } = useUser();
const { user_id } = useUser();
const { isMobile } = useAppDimensions();
const { builds, deployments, servers } = useAppState();
const { builds, deployments, servers, groups } = useAppState();
const [search, setSearch] = createSignal("");
const _servers = createMemo(() => {
return servers.filterArray((s) => {
@@ -34,6 +34,13 @@ const Resources: Component<{}> = (p) => {
return p ? p !== PermissionLevel.None : false;
});
});
const _groups = createMemo(() => {
return groups.filterArray((b) => {
if (!b.name.includes(search())) return false;
const p = b.permissions?.[user_id()];
return p ? p !== PermissionLevel.None : false;
});
});
return (
<>
<Grid
@@ -55,7 +62,9 @@ const Resources: Component<{}> = (p) => {
>
<Grid gap="0.25rem">
<h2>{item.server.name}</h2>
<div class="dimmed">{item.server.region || "unknown region"}</div>
<div class="dimmed">
{item.server.region || "unknown region"}
</div>
</Grid>
<div>{item.server.permissions?.[user_id()] || "none"}</div>
</A>
@@ -117,6 +126,27 @@ const Resources: Component<{}> = (p) => {
</For>
</Grid>
</Grid>
<Grid
class="card shadow"
style={{ width: "100%", "box-sizing": "border-box" }}
>
<h1>groups</h1>
<Grid gridTemplateColumns={isMobile() ? undefined : "1fr 1fr"}>
<For each={_groups()}>
{(item) => (
<Flex
class="card light shadow hover full-width"
style={{
"justify-content": "space-between",
}}
>
<h2>{item.name}</h2>
<div>{item.permissions?.[user_id()] || "none"}</div>
</Flex>
)}
</For>
</Grid>
</Grid>
</>
);
};

View File

@@ -33,7 +33,7 @@ import Selector from "../shared/menu/Selector";
const User: Component = () => {
const { isMobile } = useAppDimensions();
const { builds, deployments, servers, ws } = useAppState();
const { builds, deployments, servers, groups, ws } = useAppState();
const params = useParams<{ id: string }>();
const [user, { refetch }] = createResource(() =>
client.get_user_by_id(params.id)
@@ -86,6 +86,17 @@ const User: Component = () => {
});
}
});
const _groups = createMemo(() => {
if (showAll()) {
return groups.filterArray((b) => b.name.includes(search()));
} else {
return groups.filterArray((b) => {
if (!b.name.includes(search())) return false;
const p = b.permissions?.[params.id];
return p ? p !== PermissionLevel.None : false;
});
}
});
return (
<Grid
class="card shadow"
@@ -138,7 +149,7 @@ const User: Component = () => {
<Flex alignItems="center">
<h1>servers</h1>
<Show when={_servers()?.length === 0}>
<div>empty</div>
<div>none</div>
</Show>
</Flex>
<Grid gridTemplateColumns={isMobile() ? undefined : "1fr 1fr"}>
@@ -182,7 +193,7 @@ const User: Component = () => {
<Flex alignItems="center">
<h1>deployments</h1>
<Show when={_deployments()?.length === 0}>
<div>empty</div>
<div>none</div>
</Show>
</Flex>
<Grid gridTemplateColumns={isMobile() ? undefined : "1fr 1fr"}>
@@ -229,7 +240,7 @@ const User: Component = () => {
<Flex alignItems="center">
<h1>builds</h1>
<Show when={_builds()?.length === 0}>
<div>empty</div>
<div>none</div>
</Show>
</Flex>
<Grid gridTemplateColumns={isMobile() ? undefined : "1fr 1fr"}>
@@ -263,6 +274,44 @@ const User: Component = () => {
</For>
</Grid>
</Grid>
<Grid class="card light shadow">
<Flex alignItems="center">
<h1>groups</h1>
<Show when={_builds()?.length === 0}>
<div>none</div>
</Show>
</Flex>
<Grid gridTemplateColumns={isMobile() ? undefined : "1fr 1fr"}>
<For each={_groups()}>
{(item) => (
<Flex
class="card shadow"
alignItems="center"
justifyContent="space-between"
>
<h2>{item.name}</h2>
<Selector
targetClass={
(item.permissions?.[params.id] || "none") !== "none"
? "blue"
: "red"
}
selected={item.permissions?.[params.id] || "none"}
items={["none", "read", "execute", "update"]}
onSelect={(permission) => {
client.update_user_permissions_on_target({
user_id: params.id,
target_type: PermissionsTarget.Group,
target_id: getId(item),
permission: permission as PermissionLevel,
});
}}
/>
</Flex>
)}
</For>
</Grid>
</Grid>
</Show>
</Grid>
);

View File

@@ -466,6 +466,7 @@ export enum PermissionsTarget {
Deployment = "deployment",
Build = "build",
Procedure = "procedure",
Group = "group",
}
export enum Timelength {

View File

@@ -194,6 +194,7 @@ pub enum PermissionsTarget {
Deployment,
Build,
Procedure,
Group,
}
#[typeshare]