permissions table

This commit is contained in:
mbecker20
2024-03-25 23:19:08 -07:00
parent 8d1410b181
commit 65aba12dd0
15 changed files with 227 additions and 147 deletions

View File

@@ -1,3 +1,5 @@
use std::str::FromStr;
use anyhow::Context;
use async_trait::async_trait;
use monitor_client::{
@@ -6,14 +8,19 @@ use monitor_client::{
alerter::{Alerter, AlerterListItem},
permission::PermissionLevel,
resource::AddFilters,
update::ResourceTargetVariant,
user::User,
},
};
use mungos::mongodb::bson::{doc, Document};
use mungos::mongodb::bson::{doc, oid::ObjectId, Document};
use resolver_api::Resolve;
use crate::{
db::db_client, helpers::resource::StateResource, state::State,
db::db_client,
helpers::resource::{
get_resource_ids_for_non_admin, StateResource,
},
state::State,
};
#[async_trait]
@@ -59,8 +66,16 @@ impl Resolve<GetAlertersSummary, User> for State {
let query = if user.admin {
None
} else {
let ids = get_resource_ids_for_non_admin(
&user.id,
ResourceTargetVariant::Alerter,
)
.await?
.into_iter()
.flat_map(|id| ObjectId::from_str(&id))
.collect::<Vec<_>>();
let query = doc! {
format!("permissions.{}", user.id): { "$in": ["read", "execute", "update"] }
"_id": { "$in": ids }
};
Some(query)
};

View File

@@ -1,4 +1,4 @@
use std::{collections::HashMap, sync::OnceLock};
use std::{collections::HashMap, str::FromStr, sync::OnceLock};
use anyhow::Context;
use async_timing_util::unix_timestamp_ms;
@@ -10,7 +10,7 @@ use monitor_client::{
build::{Build, BuildActionState, BuildListItem},
permission::PermissionLevel,
resource::AddFilters,
update::UpdateStatus,
update::{ResourceTargetVariant, UpdateStatus},
user::User,
Operation,
},
@@ -18,7 +18,7 @@ use monitor_client::{
use mungos::{
find::find_collect,
mongodb::{
bson::{doc, Document},
bson::{doc, oid::ObjectId, Document},
options::FindOptions,
},
};
@@ -27,7 +27,9 @@ use resolver_api::{Resolve, ResolveToString};
use crate::{
config::core_config,
db::db_client,
helpers::resource::StateResource,
helpers::resource::{
get_resource_ids_for_non_admin, StateResource,
},
state::{action_states, State},
};
@@ -94,8 +96,16 @@ impl Resolve<GetBuildsSummary, User> for State {
let query = if user.admin {
None
} else {
let ids = get_resource_ids_for_non_admin(
&user.id,
ResourceTargetVariant::Build,
)
.await?
.into_iter()
.flat_map(|id| ObjectId::from_str(&id))
.collect::<Vec<_>>();
let query = doc! {
format!("permissions.{}", user.id): { "$in": ["read", "execute", "update"] }
"_id": { "$in": ids }
};
Some(query)
};

View File

@@ -1,3 +1,5 @@
use std::str::FromStr;
use anyhow::Context;
use async_trait::async_trait;
use monitor_client::{
@@ -6,14 +8,19 @@ use monitor_client::{
builder::{Builder, BuilderConfig, BuilderListItem},
permission::PermissionLevel,
resource::AddFilters,
update::ResourceTargetVariant,
user::User,
},
};
use mungos::mongodb::bson::{doc, Document};
use mungos::mongodb::bson::{doc, oid::ObjectId, Document};
use resolver_api::Resolve;
use crate::{
db::db_client, helpers::resource::StateResource, state::State,
db::db_client,
helpers::resource::{
get_resource_ids_for_non_admin, StateResource,
},
state::State,
};
#[async_trait]
@@ -59,8 +66,16 @@ impl Resolve<GetBuildersSummary, User> for State {
let query = if user.admin {
None
} else {
let ids = get_resource_ids_for_non_admin(
&user.id,
ResourceTargetVariant::Builder,
)
.await?
.into_iter()
.flat_map(|id| ObjectId::from_str(&id))
.collect::<Vec<_>>();
let query = doc! {
format!("permissions.{}", user.id): { "$in": ["read", "execute", "update"] }
"_id": { "$in": ids }
};
Some(query)
};

View File

@@ -1,4 +1,4 @@
use std::cmp;
use std::{cmp, str::FromStr};
use anyhow::{anyhow, Context};
use async_trait::async_trait;
@@ -13,7 +13,7 @@ use monitor_client::{
permission::PermissionLevel,
resource::AddFilters,
server::Server,
update::{Log, UpdateStatus},
update::{Log, ResourceTargetVariant, UpdateStatus},
user::User,
Operation,
},
@@ -21,7 +21,7 @@ use monitor_client::{
use mungos::{
find::find_collect,
mongodb::{
bson::{doc, Document},
bson::{doc, oid::ObjectId, Document},
options::FindOneOptions,
},
};
@@ -31,8 +31,9 @@ use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{
cache::deployment_status_cache, periphery_client,
resource::StateResource,
cache::deployment_status_cache,
periphery_client,
resource::{get_resource_ids_for_non_admin, StateResource},
},
state::{action_states, State},
};
@@ -260,8 +261,16 @@ impl Resolve<GetDeploymentsSummary, User> for State {
let query = if user.admin {
None
} else {
let ids = get_resource_ids_for_non_admin(
&user.id,
ResourceTargetVariant::Deployment,
)
.await?
.into_iter()
.flat_map(|id| ObjectId::from_str(&id))
.collect::<Vec<_>>();
let query = doc! {
format!("permissions.{}", user.id): { "$in": ["read", "execute", "update"] }
"_id": { "$in": ids }
};
Some(query)
};

View File

@@ -1,3 +1,5 @@
use std::str::FromStr;
use anyhow::Context;
use async_trait::async_trait;
use monitor_client::{
@@ -10,15 +12,17 @@ use monitor_client::{
},
entities::{
permission::PermissionLevel, procedure::Procedure,
resource::AddFilters, user::User,
resource::AddFilters, update::ResourceTargetVariant, user::User,
},
};
use mungos::mongodb::bson::{doc, Document};
use mungos::mongodb::bson::{doc, oid::ObjectId, Document};
use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::resource::StateResource,
helpers::resource::{
get_resource_ids_for_non_admin, StateResource,
},
state::{action_states, State},
};
@@ -81,8 +85,16 @@ impl Resolve<GetProceduresSummary, User> for State {
let query = if user.admin {
None
} else {
let ids = get_resource_ids_for_non_admin(
&user.id,
ResourceTargetVariant::Procedure,
)
.await?
.into_iter()
.flat_map(|id| ObjectId::from_str(&id))
.collect::<Vec<_>>();
let query = doc! {
format!("permissions.{}", user.id): { "$in": ["read", "execute", "update"] }
"_id": { "$in": ids }
};
Some(query)
};

View File

@@ -1,3 +1,5 @@
use std::str::FromStr;
use anyhow::Context;
use async_trait::async_trait;
use monitor_client::{
@@ -6,15 +8,18 @@ use monitor_client::{
permission::PermissionLevel,
repo::{Repo, RepoActionState, RepoListItem},
resource::AddFilters,
update::ResourceTargetVariant,
user::User,
},
};
use mungos::mongodb::bson::{doc, Document};
use mungos::mongodb::bson::{doc, oid::ObjectId, Document};
use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::resource::StateResource,
helpers::resource::{
get_resource_ids_for_non_admin, StateResource,
},
state::{action_states, State},
};
@@ -81,8 +86,16 @@ impl Resolve<GetReposSummary, User> for State {
let query = if user.admin {
None
} else {
let ids = get_resource_ids_for_non_admin(
&user.id,
ResourceTargetVariant::Alerter,
)
.await?
.into_iter()
.flat_map(|id| ObjectId::from_str(&id))
.collect::<Vec<_>>();
let query = doc! {
format!("permissions.{}", user.id): { "$in": ["read", "execute", "update"] }
"_id": { "$in": ids }
};
Some(query)
};

View File

@@ -22,7 +22,8 @@ use crate::{
db::db_client,
helpers::{
add_update, create_permission, make_update,
remove_from_recently_viewed, resource::StateResource,
remove_from_recently_viewed,
resource::{delete_all_permissions_on_resource, StateResource},
},
state::State,
};
@@ -171,6 +172,8 @@ impl Resolve<DeleteAlerter, User> for State {
.await
.context("failed to delete alerter from database")?;
delete_all_permissions_on_resource(&alerter).await;
update.push_simple_log(
"delete alerter",
format!("deleted alerter {}", alerter.name),

View File

@@ -25,7 +25,8 @@ use crate::{
db::db_client,
helpers::{
add_update, create_permission, empty_or_only_spaces, make_update,
remove_from_recently_viewed, resource::StateResource,
remove_from_recently_viewed,
resource::{delete_all_permissions_on_resource, StateResource},
update_update,
},
state::{action_states, State},
@@ -189,6 +190,8 @@ impl Resolve<DeleteBuild, User> for State {
.await
.context("failed to delete build from database");
delete_all_permissions_on_resource(&build).await;
let log = match res {
Ok(_) => Log::simple(
"delete build",

View File

@@ -20,8 +20,9 @@ use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{
add_update, create_permission, remove_from_recently_viewed,
resource::StateResource,
add_update, create_permission, make_update,
remove_from_recently_viewed,
resource::{delete_all_permissions_on_resource, StateResource},
},
state::State,
};
@@ -162,8 +163,6 @@ impl Resolve<DeleteBuilder, User> for State {
)
.await?;
let start_ts = monitor_timestamp();
db_client()
.await
.builds
@@ -180,17 +179,15 @@ impl Resolve<DeleteBuilder, User> for State {
.await
.context("failed to delete builder from database")?;
let mut update = Update {
target: (&builder).into(),
operation: Operation::DeleteBuilder,
start_ts,
operator: user.id.clone(),
logs: vec![Log::simple(
"delete builder",
format!("deleted builder {}", builder.name),
)],
..Default::default()
};
delete_all_permissions_on_resource(&builder).await;
let mut update =
make_update(&builder, Operation::DeleteBuilder, &user);
update.push_simple_log(
"delete builder",
format!("deleted builder {}", builder.name),
);
update.finalize();
add_update(update).await?;

View File

@@ -27,7 +27,8 @@ use crate::{
helpers::{
add_update, create_permission, empty_or_only_spaces,
get_deployment_state, make_update, periphery_client,
remove_from_recently_viewed, resource::StateResource,
remove_from_recently_viewed,
resource::{delete_all_permissions_on_resource, StateResource},
update_update,
},
state::{action_states, State},
@@ -198,21 +199,13 @@ impl Resolve<DeleteDeployment, User> for State {
.await?;
let inner = || async move {
let start_ts = monitor_timestamp();
let state = get_deployment_state(&deployment)
.await
.context("failed to get container state")?;
let mut update = Update {
target: ResourceTarget::Deployment(deployment.id.clone()),
operation: Operation::DeleteDeployment,
start_ts,
operator: user.id.clone(),
success: true,
status: UpdateStatus::InProgress,
..Default::default()
};
let mut update =
make_update(&deployment, Operation::DeleteDeployment, &user);
update.in_progress();
update.id = add_update(update.clone()).await?;
@@ -279,6 +272,8 @@ impl Resolve<DeleteDeployment, User> for State {
),
};
delete_all_permissions_on_resource(&deployment).await;
update.logs.push(log);
update.end_ts = Some(monitor_timestamp());
update.status = UpdateStatus::Complete;

View File

@@ -5,21 +5,23 @@ use monitor_client::{
UpdateUserPermissions, UpdateUserPermissionsOnTarget,
},
entities::{
monitor_timestamp,
update::{Log, ResourceTarget, Update, UpdateStatus},
update::{ResourceTarget, Update},
user::User,
Operation,
},
};
use mungos::{
by_id::{find_one_by_id, update_one_by_id},
mongodb::bson::{doc, Document},
mongodb::{
bson::{doc, Document},
options::UpdateOptions,
},
};
use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{add_update, get_user},
helpers::{add_update, get_user, make_update},
state::State,
};
@@ -35,7 +37,6 @@ impl Resolve<UpdateUserPermissions, User> for State {
}: UpdateUserPermissions,
admin: User,
) -> anyhow::Result<Update> {
let start_ts = monitor_timestamp();
if !admin.admin {
return Err(anyhow!("this method is admin only"));
}
@@ -66,25 +67,18 @@ impl Resolve<UpdateUserPermissions, User> for State {
None,
)
.await?;
let end_ts = monitor_timestamp();
let mut update = Update {
target: ResourceTarget::System("system".to_string()),
operation: Operation::UpdateUserPermissions,
logs: vec![Log::simple(
"modify user enabled",
format!(
"update permissions for {} ({})\nenabled: {enabled:?}\ncreate servers: {create_servers:?}\ncreate builds: {create_builds:?}",
user.username,
user.id,
),
)],
start_ts,
end_ts: end_ts.into(),
status: UpdateStatus::Complete,
success: true,
operator: admin.id.clone(),
..Default::default()
};
let mut update = make_update(
ResourceTarget::System("system".to_string()),
Operation::UpdateUserPermissions,
&admin,
);
update.push_simple_log("modify user enabled", format!(
"update permissions for {} ({})\nenabled: {enabled:?}\ncreate servers: {create_servers:?}\ncreate builds: {create_builds:?}",
user.username,
user.id,
));
update.finalize();
update.id = add_update(update.clone()).await?;
Ok(update)
}
@@ -101,7 +95,6 @@ impl Resolve<UpdateUserPermissionsOnTarget, User> for State {
}: UpdateUserPermissionsOnTarget,
admin: User,
) -> anyhow::Result<Update> {
let start_ts = monitor_timestamp();
if !admin.admin {
return Err(anyhow!("this method is admin only"));
}
@@ -125,23 +118,19 @@ impl Resolve<UpdateUserPermissionsOnTarget, User> for State {
"level": permission.as_ref(),
}
},
None
UpdateOptions::builder().upsert(true).build()
).await?;
let log_text = format!(
"user {} given {} permissions on {target:?}",
user.username, permission,
);
let mut update = Update {
operation: Operation::UpdateUserPermissionsOnTarget,
start_ts,
success: true,
operator: admin.id.clone(),
status: UpdateStatus::Complete,
target: target.clone(),
logs: vec![Log::simple("modify permissions", log_text)],
end_ts: monitor_timestamp().into(),
..Default::default()
};
let mut update = make_update(
target,
Operation::UpdateUserPermissionsOnTarget,
&admin,
);
update.push_simple_log("modify permissions", log_text);
update.finalize();
update.id = add_update(update.clone()).await?;
Ok(update)
}

View File

@@ -18,7 +18,8 @@ use crate::{
db::db_client,
helpers::{
add_update, create_permission, make_update,
remove_from_recently_viewed, resource::StateResource,
remove_from_recently_viewed,
resource::{delete_all_permissions_on_resource, StateResource},
update_update,
},
state::{action_states, State},
@@ -224,6 +225,8 @@ impl Resolve<DeleteProcedure, User> for State {
.await
.context("failed to delete build from database");
delete_all_permissions_on_resource(&procedure).await;
let log = match res {
Ok(_) => Log::simple(
"delete procedure",

View File

@@ -8,7 +8,7 @@ use monitor_client::{
repo::Repo,
server::Server,
to_monitor_name,
update::{Log, ResourceTarget, Update, UpdateStatus},
update::{Log, ResourceTarget, Update},
user::User,
Operation,
},
@@ -24,7 +24,8 @@ use crate::{
db::db_client,
helpers::{
add_update, create_permission, make_update, periphery_client,
remove_from_recently_viewed, resource::StateResource,
remove_from_recently_viewed,
resource::{delete_all_permissions_on_resource, StateResource},
update_update,
},
state::{action_states, State},
@@ -163,25 +164,13 @@ impl Resolve<CopyRepo, User> for State {
.to_string();
let repo: Repo = self.get_resource(&repo_id).await?;
create_permission(&user, &repo, PermissionLevel::Update).await;
let update = Update {
target: ResourceTarget::Repo(repo_id),
operation: Operation::CreateRepo,
start_ts,
end_ts: Some(monitor_timestamp()),
operator: user.id.clone(),
success: true,
logs: vec![
Log::simple(
"create repo",
format!(
"created repo\nid: {}\nname: {}",
repo.id, repo.name
),
),
Log::simple("config", format!("{:#?}", repo.config)),
],
..Default::default()
};
let mut update = make_update(&repo, Operation::CreateRepo, &user);
update.push_simple_log(
"create repo",
format!("created repo\nid: {}\nname: {}", repo.id, repo.name),
);
update.push_simple_log("config", format!("{:#?}", repo.config));
update.finalize();
add_update(update).await?;
@@ -214,15 +203,9 @@ impl Resolve<DeleteRepo, User> for State {
};
let inner = || async move {
let mut update = Update {
operation: Operation::DeleteRepo,
target: ResourceTarget::Repo(repo.id.clone()),
start_ts: monitor_timestamp(),
status: UpdateStatus::InProgress,
operator: user.id.clone(),
success: true,
..Default::default()
};
let mut update =
make_update(&repo, Operation::DeleteRepo, &user);
update.in_progress();
update.id = add_update(update.clone()).await?;
let res =
@@ -230,6 +213,8 @@ impl Resolve<DeleteRepo, User> for State {
.await
.context("failed to delete repo from database");
delete_all_permissions_on_resource(&repo).await;
let log = match res {
Ok(_) => Log::simple(
"delete repo",

View File

@@ -21,9 +21,12 @@ use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{
add_update, cache::server_status_cache, create_permission,
make_update, periphery_client, remove_from_recently_viewed,
resource::StateResource, update_update,
add_update,
cache::server_status_cache,
create_permission, make_update, periphery_client,
remove_from_recently_viewed,
resource::{delete_all_permissions_on_resource, StateResource},
update_update,
},
monitor::update_cache_for_server,
state::{action_states, State},
@@ -110,8 +113,6 @@ impl Resolve<DeleteServer, User> for State {
)
.await?;
let start_ts = monitor_timestamp();
db_client()
.await
.builds
@@ -149,17 +150,14 @@ impl Resolve<DeleteServer, User> for State {
.await
.context("failed to delete server from mongo")?;
let mut update = Update {
target: ResourceTarget::Server(id.clone()),
operation: Operation::DeleteServer,
start_ts,
operator: user.id.clone(),
logs: vec![Log::simple(
"delete server",
format!("deleted server {}", server.name),
)],
..Default::default()
};
delete_all_permissions_on_resource(&server).await;
let mut update =
make_update(&server, Operation::DeleteServer, &user);
update.push_simple_log(
"delete server",
format!("deleted server {}", server.name),
);
update.finalize();
add_update(update).await?;

View File

@@ -19,7 +19,7 @@ use monitor_client::entities::{
procedure::{Procedure, ProcedureListItem, ProcedureListItemInfo},
repo::{Repo, RepoInfo, RepoListItem},
server::{Server, ServerListItem, ServerListItemInfo},
update::ResourceTargetVariant,
update::{ResourceTarget, ResourceTargetVariant},
user::User,
};
use mungos::{
@@ -96,17 +96,11 @@ pub trait StateResource<
&self,
user_id: &str,
) -> anyhow::Result<Vec<String>> {
let permissions = find_collect(
&db_client().await.permissions,
doc! { "user_id": user_id, "target.type": self.resource_target_variant().as_ref() },
None,
get_resource_ids_for_non_admin(
user_id,
self.resource_target_variant(),
)
.await
.context("failed to query permissions on db")?
.into_iter()
.map(|p| p.target.extract_variant_id().1.to_string())
.collect();
Ok(permissions)
}
async fn list_resources_for_user(
@@ -515,3 +509,42 @@ pub async fn get_user_permission_on_resource(
.unwrap_or_default();
Ok(permission)
}
pub async fn delete_all_permissions_on_resource(
target: impl Into<ResourceTarget>,
) {
let target: ResourceTarget = target.into();
let (variant, id) = target.extract_variant_id();
if let Err(e) = db_client()
.await
.permissions
.delete_many(
doc! { "target.type": variant.as_ref(), "target.id": &id },
None,
)
.await
{
warn!("failed to delete_many permissions matching target {target:?} | {e:#}");
}
}
pub async fn get_resource_ids_for_non_admin(
user_id: &str,
resource_type: ResourceTargetVariant,
) -> anyhow::Result<Vec<String>> {
let permissions = find_collect(
&db_client().await.permissions,
doc! {
"user_id": user_id,
"target.type": resource_type.as_ref(),
"level": { "$in": ["Read", "Execute", "Update"] }
},
None,
)
.await
.context("failed to query permissions on db")?
.into_iter()
.map(|p| p.target.extract_variant_id().1.to_string())
.collect();
Ok(permissions)
}