diff --git a/core/src/actions/group.rs b/core/src/actions/group.rs index 57af996a0..605fcf162 100644 --- a/core/src/actions/group.rs +++ b/core/src/actions/group.rs @@ -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 { - 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" )) } } diff --git a/core/src/api/permissions.rs b/core/src/api/permissions.rs index 2740c31e2..e9150b241 100644 --- a/core/src/api/permissions.rs +++ b/core/src/api/permissions.rs @@ -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, ) -> anyhow::Result { - 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::( + &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, ) -> anyhow::Result { - 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, ) -> anyhow::Result { - 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, ) -> anyhow::Result { - 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?; diff --git a/frontend/src/components/account/Resources.tsx b/frontend/src/components/account/Resources.tsx index 7d2981b99..88dbe0c7e 100644 --- a/frontend/src/components/account/Resources.tsx +++ b/frontend/src/components/account/Resources.tsx @@ -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 ( <> = (p) => { >

{item.server.name}

-
{item.server.region || "unknown region"}
+
+ {item.server.region || "unknown region"} +
{item.server.permissions?.[user_id()] || "none"}
@@ -117,6 +126,27 @@ const Resources: Component<{}> = (p) => {
+ +

groups

+ + + {(item) => ( + +

{item.name}

+
{item.permissions?.[user_id()] || "none"}
+
+ )} +
+
+
); }; diff --git a/frontend/src/components/users/User.tsx b/frontend/src/components/users/User.tsx index 011dddf35..8878952a7 100644 --- a/frontend/src/components/users/User.tsx +++ b/frontend/src/components/users/User.tsx @@ -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 ( {

servers

-
empty
+
none
@@ -182,7 +193,7 @@ const User: Component = () => {

deployments

-
empty
+
none
@@ -229,7 +240,7 @@ const User: Component = () => {

builds

-
empty
+
none
@@ -263,6 +274,44 @@ const User: Component = () => {
+ + +

groups

+ +
none
+
+
+ + + {(item) => ( + +

{item.name}

+ { + client.update_user_permissions_on_target({ + user_id: params.id, + target_type: PermissionsTarget.Group, + target_id: getId(item), + permission: permission as PermissionLevel, + }); + }} + /> +
+ )} +
+
+
); diff --git a/frontend/src/types.ts b/frontend/src/types.ts index b1a49311f..b829fed20 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -466,6 +466,7 @@ export enum PermissionsTarget { Deployment = "deployment", Build = "build", Procedure = "procedure", + Group = "group", } export enum Timelength { diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 0d5b44a29..25e8e7d93 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -194,6 +194,7 @@ pub enum PermissionsTarget { Deployment, Build, Procedure, + Group, } #[typeshare]