more resource permissions to own table

This commit is contained in:
mbecker20
2024-03-25 20:03:10 -07:00
parent d9fbccb644
commit 8d1410b181
37 changed files with 381 additions and 485 deletions
+2 -1
View File
@@ -13,10 +13,11 @@ use monitor_client::{
builder::{AwsBuilderConfig, Builder, BuilderConfig},
deployment::DockerContainerState,
monitor_timestamp,
permission::PermissionLevel,
server::Server,
update::{Log, Update},
user::User,
Operation, PermissionLevel,
Operation,
},
};
use mungos::{
+2 -1
View File
@@ -7,10 +7,11 @@ use monitor_client::{
build::Build,
deployment::{Deployment, DeploymentImage},
get_image_name, monitor_timestamp,
permission::PermissionLevel,
server::ServerStatus,
update::{Log, ResourceTarget, Update, UpdateStatus},
user::User,
Operation, PermissionLevel, Version,
Operation, Version,
},
};
use mungos::{find::find_collect, mongodb::bson::doc};
+2 -2
View File
@@ -2,8 +2,8 @@ use async_trait::async_trait;
use monitor_client::{
api::execute::RunProcedure,
entities::{
procedure::Procedure, update::Update, user::User, Operation,
PermissionLevel,
permission::PermissionLevel, procedure::Procedure,
update::Update, user::User, Operation,
},
};
use resolver_api::Resolve;
+2 -1
View File
@@ -6,11 +6,12 @@ use monitor_client::{
api::execute::*,
entities::{
monitor_timestamp, optional_string,
permission::PermissionLevel,
repo::Repo,
server::Server,
update::{Log, ResourceTarget, Update, UpdateStatus},
user::User,
Operation, PermissionLevel,
Operation,
},
};
use mungos::mongodb::bson::{doc, oid::ObjectId};
+2 -1
View File
@@ -4,10 +4,11 @@ use monitor_client::{
api::execute::*,
entities::{
monitor_timestamp,
permission::PermissionLevel,
server::Server,
update::{Log, ResourceTarget, Update, UpdateStatus},
user::User,
Operation, PermissionLevel,
Operation,
},
};
use periphery_client::api;
+1 -1
View File
@@ -4,9 +4,9 @@ use monitor_client::{
api::read::*,
entities::{
alerter::{Alerter, AlerterListItem},
permission::PermissionLevel,
resource::AddFilters,
user::User,
PermissionLevel,
},
};
use mungos::mongodb::bson::{doc, Document};
+2 -1
View File
@@ -8,10 +8,11 @@ use monitor_client::{
api::read::*,
entities::{
build::{Build, BuildActionState, BuildListItem},
permission::PermissionLevel,
resource::AddFilters,
update::UpdateStatus,
user::User,
Operation, PermissionLevel,
Operation,
},
};
use mungos::{
+1 -1
View File
@@ -4,9 +4,9 @@ use monitor_client::{
api::read::{self, *},
entities::{
builder::{Builder, BuilderConfig, BuilderListItem},
permission::PermissionLevel,
resource::AddFilters,
user::User,
PermissionLevel,
},
};
use mungos::mongodb::bson::{doc, Document};
+2 -1
View File
@@ -10,11 +10,12 @@ use monitor_client::{
DeploymentImage, DeploymentListItem, DockerContainerState,
DockerContainerStats,
},
permission::PermissionLevel,
resource::AddFilters,
server::Server,
update::{Log, UpdateStatus},
user::User,
Operation, PermissionLevel,
Operation,
},
};
use mungos::{
+2 -2
View File
@@ -9,8 +9,8 @@ use monitor_client::{
ListProceduresResponse,
},
entities::{
procedure::Procedure, resource::AddFilters, user::User,
PermissionLevel,
permission::PermissionLevel, procedure::Procedure,
resource::AddFilters, user::User,
},
};
use mungos::mongodb::bson::{doc, Document};
+1 -1
View File
@@ -3,10 +3,10 @@ use async_trait::async_trait;
use monitor_client::{
api::read::*,
entities::{
permission::PermissionLevel,
repo::{Repo, RepoActionState, RepoListItem},
resource::AddFilters,
user::User,
PermissionLevel,
},
};
use mungos::mongodb::bson::{doc, Document};
+1 -1
View File
@@ -5,6 +5,7 @@ use monitor_client::{
api::read::*,
entities::{
deployment::ContainerSummary,
permission::PermissionLevel,
resource::AddFilters,
server::{
docker_image::ImageSummary, docker_network::DockerNetwork,
@@ -12,7 +13,6 @@ use monitor_client::{
ServerListItem, ServerStatus,
},
user::User,
PermissionLevel,
},
};
use mungos::{
+1 -1
View File
@@ -9,12 +9,12 @@ use monitor_client::{
build::Build,
builder::Builder,
deployment::Deployment,
permission::PermissionLevel,
procedure::Procedure,
repo::Repo,
server::Server,
update::{ResourceTarget, Update, UpdateListItem},
user::User,
PermissionLevel,
},
};
use mungos::{
+8 -9
View File
@@ -7,8 +7,9 @@ use monitor_client::{
entities::{
alerter::{Alerter, AlerterInfo},
monitor_timestamp,
permission::PermissionLevel,
user::User,
Operation, PermissionLevel,
Operation,
},
};
use mungos::{
@@ -20,8 +21,8 @@ use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{
add_update, make_update, remove_from_recently_viewed,
resource::StateResource,
add_update, create_permission, make_update,
remove_from_recently_viewed, resource::StateResource,
},
state::State,
};
@@ -44,9 +45,6 @@ impl Resolve<CreateAlerter, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description: Default::default(),
tags: Default::default(),
config: config.into(),
@@ -64,6 +62,8 @@ impl Resolve<CreateAlerter, User> for State {
.to_string();
let alerter: Alerter = self.get_resource(&alerter_id).await?;
create_permission(&user, &alerter, PermissionLevel::Update).await;
let mut update =
make_update(&alerter, Operation::CreateAlerter, &user);
@@ -108,9 +108,6 @@ impl Resolve<CopyAlerter, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description,
config,
tags: Default::default(),
@@ -128,6 +125,8 @@ impl Resolve<CopyAlerter, User> for State {
.to_string();
let alerter: Alerter = self.get_resource(&alerter_id).await?;
create_permission(&user, &alerter, PermissionLevel::Update).await;
let mut update =
make_update(&alerter, Operation::CreateAlerter, &user);
+9 -9
View File
@@ -7,10 +7,12 @@ use monitor_client::{
entities::{
build::Build,
builder::Builder,
monitor_timestamp, to_monitor_name,
monitor_timestamp,
permission::PermissionLevel,
to_monitor_name,
update::{Log, UpdateStatus},
user::User,
Operation, PermissionLevel,
Operation,
},
};
use mungos::{
@@ -22,7 +24,7 @@ use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{
add_update, empty_or_only_spaces, make_update,
add_update, create_permission, empty_or_only_spaces, make_update,
remove_from_recently_viewed, resource::StateResource,
update_update,
},
@@ -48,9 +50,6 @@ impl Resolve<CreateBuild, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description: Default::default(),
tags: Default::default(),
config: config.into(),
@@ -68,6 +67,8 @@ impl Resolve<CreateBuild, User> for State {
.to_string();
let build: Build = self.get_resource(&build_id).await?;
create_permission(&user, &build, PermissionLevel::Update).await;
let mut update =
make_update(&build, Operation::CreateBuild, &user);
@@ -115,9 +116,6 @@ impl Resolve<CopyBuild, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description,
tags,
config,
@@ -135,6 +133,8 @@ impl Resolve<CopyBuild, User> for State {
.to_string();
let build: Build = self.get_resource(&build_id).await?;
create_permission(&user, &build, PermissionLevel::Update).await;
let mut update =
make_update(&build, Operation::CreateBuild, &user);
+6 -8
View File
@@ -5,9 +5,10 @@ use monitor_client::{
entities::{
builder::Builder,
monitor_timestamp,
permission::PermissionLevel,
update::{Log, ResourceTarget, Update},
user::User,
Operation, PermissionLevel,
Operation,
},
};
use mungos::{
@@ -19,7 +20,8 @@ use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{
add_update, remove_from_recently_viewed, resource::StateResource,
add_update, create_permission, remove_from_recently_viewed,
resource::StateResource,
},
state::State,
};
@@ -36,9 +38,6 @@ impl Resolve<CreateBuilder, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description: Default::default(),
tags: Default::default(),
config: config.into(),
@@ -55,6 +54,7 @@ impl Resolve<CreateBuilder, User> for State {
.context("inserted_id is not ObjectId")?
.to_string();
let builder: Builder = self.get_resource(&builder_id).await?;
create_permission(&user, &builder, PermissionLevel::Update).await;
let update = Update {
target: ResourceTarget::Builder(builder_id),
operation: Operation::CreateBuilder,
@@ -104,9 +104,6 @@ impl Resolve<CopyBuilder, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description,
tags: Default::default(),
config,
@@ -123,6 +120,7 @@ impl Resolve<CopyBuilder, User> for State {
.context("inserted_id is not ObjectId")?
.to_string();
let builder: Builder = self.get_resource(&builder_id).await?;
create_permission(&user, &builder, PermissionLevel::Update).await;
let update = Update {
target: ResourceTarget::Builder(builder_id),
operation: Operation::CreateBuilder,
+41 -52
View File
@@ -7,11 +7,12 @@ use monitor_client::{
build::Build,
deployment::{Deployment, DeploymentImage, DockerContainerState},
monitor_timestamp,
permission::PermissionLevel,
server::Server,
to_monitor_name,
update::{Log, ResourceTarget, Update, UpdateStatus},
user::User,
Operation, PermissionLevel,
Operation,
},
};
use mungos::{
@@ -24,9 +25,10 @@ use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{
add_update, empty_or_only_spaces, get_deployment_state,
make_update, periphery_client, remove_from_recently_viewed,
resource::StateResource, update_update,
add_update, create_permission, empty_or_only_spaces,
get_deployment_state, make_update, periphery_client,
remove_from_recently_viewed, resource::StateResource,
update_update,
},
state::{action_states, State},
};
@@ -60,9 +62,6 @@ impl Resolve<CreateDeployment, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description: Default::default(),
tags: Default::default(),
config: config.into(),
@@ -80,25 +79,21 @@ impl Resolve<CreateDeployment, User> for State {
.to_string();
let deployment: Deployment =
self.get_resource(&deployment_id).await?;
let update = Update {
target: ResourceTarget::Deployment(deployment_id),
operation: Operation::CreateDeployment,
start_ts,
end_ts: Some(monitor_timestamp()),
operator: user.id.clone(),
success: true,
logs: vec![
Log::simple(
"create deployment",
format!(
"created deployment\nid: {}\nname: {}",
deployment.id, deployment.name
),
),
Log::simple("config", format!("{:#?}", deployment.config)),
],
..Default::default()
};
create_permission(&user, &deployment, PermissionLevel::Update)
.await;
let mut update =
make_update(&deployment, Operation::CreateDeployment, &user);
update.push_simple_log(
"create deployment",
format!(
"created deployment\nid: {}\nname: {}",
deployment.id, deployment.name
),
);
update
.push_simple_log("config", format!("{:#?}", deployment.config));
update.finalize();
add_update(update).await?;
@@ -128,14 +123,14 @@ impl Resolve<CopyDeployment, User> for State {
.await?;
if !config.server_id.is_empty() {
let _: Server = self.get_resource_check_permissions(&config.server_id, &user, PermissionLevel::Update)
.await
.context("cannot create deployment on this server. user must have update permissions on the server to perform this action.")?;
.await
.context("cannot create deployment on this server. user must have update permissions on the server to perform this action.")?;
}
if let DeploymentImage::Build { build_id, .. } = &config.image {
if !build_id.is_empty() {
let _: Build = self.get_resource_check_permissions(build_id, &user, PermissionLevel::Read)
.await
.context("cannot create deployment with this build attached. user must have at least read permissions on the build to perform this action.")?;
.await
.context("cannot create deployment with this build attached. user must have at least read permissions on the build to perform this action.")?;
}
}
let start_ts = monitor_timestamp();
@@ -143,9 +138,6 @@ impl Resolve<CopyDeployment, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description,
tags,
config,
@@ -163,25 +155,22 @@ impl Resolve<CopyDeployment, User> for State {
.to_string();
let deployment: Deployment =
self.get_resource(&deployment_id).await?;
let update = Update {
target: ResourceTarget::Deployment(deployment_id),
operation: Operation::CreateDeployment,
start_ts,
end_ts: Some(monitor_timestamp()),
operator: user.id.clone(),
success: true,
logs: vec![
Log::simple(
"create deployment",
format!(
"created deployment\nid: {}\nname: {}",
deployment.id, deployment.name
),
),
Log::simple("config", format!("{:#?}", deployment.config)),
],
..Default::default()
};
create_permission(&user, &deployment, PermissionLevel::Update)
.await;
let mut update =
make_update(&deployment, Operation::CreateDeployment, &user);
update.push_simple_log(
"create deployment",
format!(
"created deployment\nid: {}\nname: {}",
deployment.id, deployment.name
),
);
update
.push_simple_log("config", format!("{:#?}", deployment.config));
update.finalize();
add_update(update).await?;
+18 -145
View File
@@ -5,11 +5,7 @@ use monitor_client::{
UpdateUserPermissions, UpdateUserPermissionsOnTarget,
},
entities::{
alerter::Alerter,
monitor_timestamp,
procedure::Procedure,
repo::Repo,
server::Server,
update::{Log, ResourceTarget, Update, UpdateStatus},
user::User,
Operation,
@@ -23,7 +19,7 @@ use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{add_update, get_user, resource::StateResource},
helpers::{add_update, get_user},
state::State,
};
@@ -43,7 +39,6 @@ impl Resolve<UpdateUserPermissions, User> for State {
if !admin.admin {
return Err(anyhow!("this method is admin only"));
}
let user = find_one_by_id(&db_client().await.users, &user_id)
.await
.context("failed to query mongo for user")?
@@ -81,7 +76,6 @@ impl Resolve<UpdateUserPermissions, User> for State {
"update permissions for {} ({})\nenabled: {enabled:?}\ncreate servers: {create_servers:?}\ncreate builds: {create_builds:?}",
user.username,
user.id,
),
)],
start_ts,
@@ -111,7 +105,6 @@ impl Resolve<UpdateUserPermissionsOnTarget, User> for State {
if !admin.admin {
return Err(anyhow!("this method is admin only"));
}
let user = get_user(&user_id).await?;
if user.admin {
return Err(anyhow!(
@@ -121,143 +114,23 @@ impl Resolve<UpdateUserPermissionsOnTarget, User> for State {
if !user.enabled {
return Err(anyhow!("user not enabled"));
}
let log_text = match &target {
ResourceTarget::System(_) => {
return Err(anyhow!("target can not be system"))
}
ResourceTarget::Build(id) => {
let build = find_one_by_id(&db_client().await.builds, id)
.await
.context("failed at find build query")?
.ok_or(anyhow!("failed to find a build with id {id}"))?;
update_one_by_id(
&db_client().await.builds,
id,
mungos::update::Update::Set(doc! {
format!("permissions.{}", user_id): permission.to_string()
}),
None,
)
.await?;
format!(
"user {} given {} permissions on build {}",
user.username, permission, build.name
)
}
ResourceTarget::Builder(id) => {
let builder = find_one_by_id(&db_client().await.builders, id)
.await
.context("failed at find builder query")?
.with_context(|| {
format!("failed to find a builder with id {id}")
})?;
update_one_by_id(
&db_client().await.builders,
id,
mungos::update::Update::Set(doc! {
format!("permissions.{}", user_id): permission.to_string()
}),
None,
)
.await?;
format!(
"user {} given {} permissions on builder {}",
user.username, permission, builder.name
)
}
ResourceTarget::Deployment(id) => {
let deployment =
find_one_by_id(&db_client().await.deployments, id)
.await
.context("failed at find deployment query")?
.with_context(|| {
format!("failed to find a deployment with id {id}")
})?;
update_one_by_id(
&db_client().await.deployments,
id,
mungos::update::Update::Set(doc! {
format!("permissions.{}", user_id): permission.to_string()
}),
None,
)
.await?;
format!(
"user {} given {} permissions on deployment {}",
user.username, permission, deployment.name
)
}
ResourceTarget::Server(id) => {
// find_one_by_id(&db_client().await.servers, id)
let server: Server = self.get_resource(id).await?;
update_one_by_id(
&db_client().await.servers,
id,
mungos::update::Update::Set(doc! {
format!("permissions.{}", user_id): permission.to_string()
}),
None,
)
.await?;
format!(
"user {} given {} permissions on server {}",
user.username, permission, server.name
)
}
ResourceTarget::Repo(id) => {
let repo: Repo = self.get_resource(id).await?;
update_one_by_id(
&db_client().await.repos,
id,
mungos::update::Update::Set(doc! {
format!("permissions.{}", user_id): permission.to_string()
}),
None,
)
.await?;
format!(
"user {} given {} permissions on repo {}",
user.username, permission, repo.name
)
}
ResourceTarget::Alerter(id) => {
let alerter: Alerter = self.get_resource(id).await?;
update_one_by_id(
&db_client().await.alerters,
id,
mungos::update::Update::Set(doc! {
format!("permissions.{}", user_id): permission.to_string()
}),
None,
)
.await?;
format!(
"user {} given {} permissions on alerter {}",
user.username, permission, alerter.name
)
}
ResourceTarget::Procedure(id) => {
let procedure: Procedure = self.get_resource(id).await?;
update_one_by_id(
&db_client().await.procedures,
id,
mungos::update::Update::Set(doc! {
format!("permissions.{}", user_id): permission.to_string()
}),
None,
)
.await?;
format!(
"user {} given {} permissions on procedure {}",
user.username, permission, procedure.id
)
}
};
let (variant, id) = target.extract_variant_id();
db_client().await.permissions.update_one(
doc! { "user_id": &user.id, "target.type": variant.as_ref(), "target.id": id },
doc! {
"$set": {
"user_id": &user.id,
"target.type": variant.as_ref(),
"target.id": id,
"level": permission.as_ref(),
}
},
None
).await?;
let log_text = format!(
"user {} given {} permissions on {target:?}",
user.username, permission,
);
let mut update = Update {
operation: Operation::UpdateUserPermissionsOnTarget,
start_ts,
+12 -10
View File
@@ -3,8 +3,9 @@ use async_trait::async_trait;
use monitor_client::{
api::write::*,
entities::{
monitor_timestamp, procedure::Procedure, to_monitor_name,
update::Log, user::User, Operation, PermissionLevel,
monitor_timestamp, permission::PermissionLevel,
procedure::Procedure, to_monitor_name, update::Log, user::User,
Operation,
},
};
use mungos::{
@@ -16,8 +17,9 @@ use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{
add_update, make_update, remove_from_recently_viewed,
resource::StateResource, update_update,
add_update, create_permission, make_update,
remove_from_recently_viewed, resource::StateResource,
update_update,
},
state::{action_states, State},
};
@@ -35,9 +37,6 @@ impl Resolve<CreateProcedure, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description: Default::default(),
tags: Default::default(),
info: Default::default(),
@@ -56,6 +55,9 @@ impl Resolve<CreateProcedure, User> for State {
let procedure: Procedure =
self.get_resource(&procedure_id).await?;
create_permission(&user, &procedure, PermissionLevel::Update)
.await;
let mut update =
make_update(&procedure, Operation::CreateProcedure, &user);
@@ -103,9 +105,6 @@ impl Resolve<CopyProcedure, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description,
tags,
config,
@@ -124,6 +123,9 @@ impl Resolve<CopyProcedure, User> for State {
let procedure: Procedure =
self.get_resource(&procedure_id).await?;
create_permission(&user, &procedure, PermissionLevel::Update)
.await;
let mut update =
make_update(&procedure, Operation::CreateProcedure, &user);
+18 -20
View File
@@ -4,12 +4,13 @@ use monitor_client::{
api::{execute, write::*},
entities::{
monitor_timestamp,
permission::PermissionLevel,
repo::Repo,
server::Server,
to_monitor_name,
update::{Log, ResourceTarget, Update, UpdateStatus},
user::User,
Operation, PermissionLevel,
Operation,
},
};
use mungos::{
@@ -22,7 +23,7 @@ use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{
add_update, make_update, periphery_client,
add_update, create_permission, make_update, periphery_client,
remove_from_recently_viewed, resource::StateResource,
update_update,
},
@@ -40,12 +41,12 @@ impl Resolve<CreateRepo, User> for State {
if let Some(server_id) = &config.server_id {
if !server_id.is_empty() {
let _: Server = self.get_resource_check_permissions(
server_id,
&user,
PermissionLevel::Update,
)
.await
.context("cannot create repo on this server. user must have update permissions on the server.")?;
server_id,
&user,
PermissionLevel::Update,
)
.await
.context("cannot create repo on this server. user must have update permissions on the server.")?;
}
}
let start_ts = monitor_timestamp();
@@ -53,9 +54,6 @@ impl Resolve<CreateRepo, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description: Default::default(),
tags: Default::default(),
config: config.into(),
@@ -74,6 +72,8 @@ impl Resolve<CreateRepo, User> for State {
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,
@@ -134,21 +134,18 @@ impl Resolve<CopyRepo, User> for State {
.await?;
if !config.server_id.is_empty() {
let _: Server = self.get_resource_check_permissions(
&config.server_id,
&user,
PermissionLevel::Update,
)
.await
.context("cannot create repo on this server. user must have update permissions on the server.")?;
&config.server_id,
&user,
PermissionLevel::Update,
)
.await
.context("cannot create repo on this server. user must have update permissions on the server.")?;
}
let start_ts = monitor_timestamp();
let repo = Repo {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description,
tags,
config,
@@ -165,6 +162,7 @@ impl Resolve<CopyRepo, User> for State {
.context("inserted_id is not ObjectId")?
.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,
+5 -6
View File
@@ -4,10 +4,11 @@ use monitor_client::{
api::write::*,
entities::{
monitor_timestamp,
permission::PermissionLevel,
server::Server,
update::{Log, ResourceTarget, Update, UpdateStatus},
user::User,
Operation, PermissionLevel,
Operation,
},
};
use mungos::{
@@ -20,8 +21,8 @@ use resolver_api::Resolve;
use crate::{
db::db_client,
helpers::{
add_update, cache::server_status_cache, make_update,
periphery_client, remove_from_recently_viewed,
add_update, cache::server_status_cache, create_permission,
make_update, periphery_client, remove_from_recently_viewed,
resource::StateResource, update_update,
},
monitor::update_cache_for_server,
@@ -45,9 +46,6 @@ impl Resolve<CreateServer, User> for State {
id: Default::default(),
name,
updated_at: start_ts,
permissions: [(user.id.clone(), PermissionLevel::Update)]
.into_iter()
.collect(),
description: Default::default(),
tags: Default::default(),
config: config.into(),
@@ -64,6 +62,7 @@ impl Resolve<CreateServer, User> for State {
.context("inserted_id is not ObjectId")?
.to_string();
let server: Server = self.get_resource(&server_id).await?;
create_permission(&user, &server, PermissionLevel::Update).await;
let update = Update {
target: ResourceTarget::Server(server_id),
operation: Operation::CreateServer,
+3 -3
View File
@@ -7,9 +7,9 @@ use monitor_client::{
},
entities::{
alerter::Alerter, build::Build, builder::Builder,
deployment::Deployment, procedure::Procedure, repo::Repo,
server::Server, tag::CustomTag, update::ResourceTarget,
user::User, PermissionLevel,
deployment::Deployment, permission::PermissionLevel,
procedure::Procedure, repo::Repo, server::Server, tag::CustomTag,
update::ResourceTarget, user::User,
},
};
use mungos::{
+3
View File
@@ -6,6 +6,7 @@ use monitor_client::entities::{
build::Build,
builder::Builder,
deployment::Deployment,
permission::Permission,
procedure::Procedure,
repo::Repo,
server::{stats::SystemStatsRecord, Server},
@@ -34,6 +35,7 @@ pub async fn db_client() -> &'static DbClient {
pub struct DbClient {
pub users: Collection<User>,
pub permissions: Collection<Permission>,
pub api_keys: Collection<ApiKey>,
pub tags: Collection<CustomTag>,
pub updates: Collection<Update>,
@@ -88,6 +90,7 @@ impl DbClient {
let client = DbClient {
users: User::collection(&db, true).await?,
permissions: Permission::collection(&db, true).await?,
api_keys: ApiKey::collection(&db, true).await?,
tags: CustomTag::collection(&db, true).await?,
updates: Update::collection(&db, true).await?,
+29
View File
@@ -4,6 +4,7 @@ use anyhow::{anyhow, Context};
use monitor_client::entities::{
deployment::{Deployment, DockerContainerState},
monitor_timestamp,
permission::{Permission, PermissionLevel},
server::{Server, ServerStatus},
tag::CustomTag,
update::{ResourceTarget, Update, UpdateListItem},
@@ -223,3 +224,31 @@ pub fn periphery_client(
Ok(client)
}
pub async fn create_permission(
user: &User,
target: impl Into<ResourceTarget>,
level: PermissionLevel,
) {
// No need to actually create permissions for admins
if user.admin {
return;
}
let target: ResourceTarget = target.into();
if let Err(e) = db_client()
.await
.permissions
.insert_one(
Permission {
id: Default::default(),
user_id: user.id.clone(),
target: target.clone(),
level,
},
None,
)
.await
{
warn!("failed to create permission for {target:?} | {e:#}");
};
}
+96 -57
View File
@@ -3,32 +3,26 @@ use std::str::FromStr;
use anyhow::{anyhow, Context};
use async_trait::async_trait;
use futures::future::join_all;
use monitor_client::{
entities::{
alerter::{
Alerter, AlerterConfig, AlerterListItem, AlerterListItemInfo,
},
build::{Build, BuildListItem, BuildListItemInfo},
builder::{
Builder, BuilderConfig, BuilderListItem, BuilderListItemInfo,
},
deployment::{
Deployment, DeploymentImage, DeploymentListItem,
DeploymentListItemInfo,
},
procedure::{
Procedure, ProcedureListItem, ProcedureListItemInfo,
},
repo::{Repo, RepoInfo, RepoListItem},
server::{Server, ServerListItem, ServerListItemInfo},
update::ResourceTargetVariant,
user::User,
PermissionLevel,
use monitor_client::entities::{
alerter::{
Alerter, AlerterConfig, AlerterListItem, AlerterListItemInfo,
},
permissioned::Permissioned,
build::{Build, BuildListItem, BuildListItemInfo},
builder::{
Builder, BuilderConfig, BuilderListItem, BuilderListItemInfo,
},
deployment::{
Deployment, DeploymentImage, DeploymentListItem,
DeploymentListItemInfo,
},
permission::PermissionLevel,
procedure::{Procedure, ProcedureListItem, ProcedureListItemInfo},
repo::{Repo, RepoInfo, RepoListItem},
server::{Server, ServerListItem, ServerListItemInfo},
update::ResourceTargetVariant,
user::User,
};
use mungos::{
aggregate::aggregate_collect,
by_id::{find_one_by_id, update_one_by_id},
find::find_collect,
mongodb::{
@@ -44,12 +38,13 @@ use super::cache::{deployment_status_cache, server_status_cache};
#[async_trait]
pub trait StateResource<
T: Send + Sync + Unpin + Serialize + DeserializeOwned + Permissioned,
T: Send + Sync + Unpin + Serialize + DeserializeOwned,
>
{
type ListItem: Serialize + Send;
fn name() -> &'static str;
fn resource_target_variant(&self) -> ResourceTargetVariant;
async fn coll(&self) -> &Collection<T>;
async fn to_list_item(
&self,
@@ -72,7 +67,8 @@ pub trait StateResource<
permission_level: PermissionLevel,
) -> anyhow::Result<T> {
let resource = self.get_resource(id).await?;
let permissions = resource.get_user_permissions(&user.id);
let permissions =
self.get_user_permission_on_resource(&user.id, id).await?;
if user.admin || permissions >= permission_level {
Ok(resource)
} else {
@@ -88,40 +84,29 @@ pub trait StateResource<
user_id: &str,
resource_id: &str,
) -> anyhow::Result<PermissionLevel> {
let resource = self.get_resource(resource_id).await?;
Ok(resource.get_user_permissions(user_id))
get_user_permission_on_resource(
user_id,
self.resource_target_variant(),
resource_id,
)
.await
}
async fn get_resource_ids_for_non_admin(
&self,
user_id: &str,
) -> anyhow::Result<Vec<String>> {
use mungos::aggregate::AggStage::*;
aggregate_collect(
self.coll().await,
[
Match(doc! {
format!("permissions.{}", user_id): { "$in": ["update", "execute", "read"] }
}),
Project(doc! { "_id": 1 }),
], None)
.await
.with_context(|| format!("failed to get {} ids for non admin | aggregation", Self::name()))?
.into_iter()
.map(|d| {
let id = d
.get("_id")
.context("no _id field")?
.as_object_id()
.context("_id not ObjectId")?
.to_string();
anyhow::Ok(id)
})
.collect::<anyhow::Result<Vec<_>>>()
.with_context(|| format!(
"failed to get {} ids for non admin | extract id from document",
Self::name()
))
let permissions = find_collect(
&db_client().await.permissions,
doc! { "user_id": user_id, "target.type": self.resource_target_variant().as_ref() },
None,
)
.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(
@@ -130,10 +115,13 @@ pub trait StateResource<
user: &User,
) -> anyhow::Result<Vec<Self::ListItem>> {
if !user.admin {
filters.insert(
format!("permissions.{}", user.id),
doc! { "$in": ["read", "execute", "update"] },
);
let ids = self
.get_resource_ids_for_non_admin(&user.id)
.await?
.into_iter()
.flat_map(|id| ObjectId::from_str(&id))
.collect::<Vec<_>>();
filters.insert("_id", doc! { "$in": ids });
}
let list = find_collect(self.coll().await, filters, None)
.await
@@ -221,6 +209,10 @@ impl StateResource<Server> for State {
"server"
}
fn resource_target_variant(&self) -> ResourceTargetVariant {
ResourceTargetVariant::Server
}
async fn coll(&self) -> &Collection<Server> {
&db_client().await.servers
}
@@ -261,6 +253,10 @@ impl StateResource<Deployment> for State {
"deployment"
}
fn resource_target_variant(&self) -> ResourceTargetVariant {
ResourceTargetVariant::Deployment
}
async fn coll(&self) -> &Collection<Deployment> {
&db_client().await.deployments
}
@@ -314,6 +310,10 @@ impl StateResource<Build> for State {
"build"
}
fn resource_target_variant(&self) -> ResourceTargetVariant {
ResourceTargetVariant::Build
}
async fn coll(&self) -> &Collection<Build> {
&db_client().await.builds
}
@@ -346,6 +346,10 @@ impl StateResource<Repo> for State {
"repo"
}
fn resource_target_variant(&self) -> ResourceTargetVariant {
ResourceTargetVariant::Repo
}
async fn coll(&self) -> &Collection<Repo> {
&db_client().await.repos
}
@@ -377,6 +381,10 @@ impl StateResource<Builder> for State {
"builder"
}
fn resource_target_variant(&self) -> ResourceTargetVariant {
ResourceTargetVariant::Builder
}
async fn coll(&self) -> &Collection<Builder> {
&db_client().await.builders
}
@@ -418,6 +426,10 @@ impl StateResource<Alerter> for State {
"alerter"
}
fn resource_target_variant(&self) -> ResourceTargetVariant {
ResourceTargetVariant::Alerter
}
async fn coll(&self) -> &Collection<Alerter> {
&db_client().await.alerters
}
@@ -454,6 +466,10 @@ impl StateResource<Procedure> for State {
"procedure"
}
fn resource_target_variant(&self) -> ResourceTargetVariant {
ResourceTargetVariant::Procedure
}
async fn coll(&self) -> &Collection<Procedure> {
&db_client().await.procedures
}
@@ -476,3 +492,26 @@ impl StateResource<Procedure> for State {
})
}
}
pub async fn get_user_permission_on_resource(
user_id: &str,
resource_variant: ResourceTargetVariant,
resource_id: &str,
) -> anyhow::Result<PermissionLevel> {
let permission = db_client()
.await
.permissions
.find_one(
doc! {
"user_id": user_id,
"target.type": resource_variant.as_ref(),
"target.id": resource_id
},
None,
)
.await
.context("failed to query permissions table")?
.map(|permission| permission.level)
.unwrap_or_default();
Ok(permission)
}
+10 -73
View File
@@ -11,18 +11,8 @@ use axum::{
use futures::{SinkExt, StreamExt};
use monitor_client::{
entities::{
alerter::Alerter,
build::Build,
builder::Builder,
deployment::Deployment,
procedure::Procedure,
repo::Repo,
server::Server,
update::{ResourceTarget, ResourceTargetVariant},
user::User,
PermissionLevel,
permission::PermissionLevel, update::ResourceTarget, user::User,
},
permissioned::Permissioned,
ws::WsLoginMessage,
};
use mungos::by_id::find_one_by_id;
@@ -33,8 +23,10 @@ use tokio_util::sync::CancellationToken;
use crate::{
auth::{auth_api_key_check_enabled, auth_jwt_check_enabled},
db::db_client,
helpers::{channel::update_channel, resource::StateResource},
state::State,
helpers::{
channel::update_channel,
resource::get_user_permission_on_resource,
},
};
pub fn router() -> Router {
@@ -207,67 +199,12 @@ async fn user_can_see_update(
if user.admin {
return Ok(());
}
let (permissions, target) = match update_target {
ResourceTarget::Server(server_id) => {
let resource: Server = State.get_resource(server_id).await?;
(
resource.get_user_permissions(&user.id),
ResourceTargetVariant::Server,
)
}
ResourceTarget::Deployment(deployment_id) => {
let resource: Deployment =
State.get_resource(deployment_id).await?;
(
resource.get_user_permissions(&user.id),
ResourceTargetVariant::Deployment,
)
}
ResourceTarget::Build(build_id) => {
let resource: Build = State.get_resource(build_id).await?;
(
resource.get_user_permissions(&user.id),
ResourceTargetVariant::Build,
)
}
ResourceTarget::Builder(builder_id) => {
let resource: Builder = State.get_resource(builder_id).await?;
(
resource.get_user_permissions(&user.id),
ResourceTargetVariant::Builder,
)
}
ResourceTarget::Repo(repo_id) => {
let resource: Repo = State.get_resource(repo_id).await?;
(
resource.get_user_permissions(&user.id),
ResourceTargetVariant::Repo,
)
}
ResourceTarget::Alerter(alerter_id) => {
let resource: Alerter = State.get_resource(alerter_id).await?;
(
resource.get_user_permissions(&user.id),
ResourceTargetVariant::Alerter,
)
}
ResourceTarget::Procedure(prodecure_id) => {
let resource: Procedure =
State.get_resource(prodecure_id).await?;
(
resource.get_user_permissions(&user.id),
ResourceTargetVariant::Procedure,
)
}
ResourceTarget::System(_) => {
return Err(anyhow!(
"user not admin, can't recieve system updates"
))
}
};
if permissions != PermissionLevel::None {
let (variant, id) = update_target.extract_variant_id();
let permissions =
get_user_permission_on_resource(&user.id, variant, id).await?;
if permissions > PermissionLevel::None {
Ok(())
} else {
Err(anyhow!("user does not have permissions on {target}"))
Err(anyhow!("user does not have permissions on {variant} {id}"))
}
}
+5 -5
View File
@@ -193,11 +193,11 @@ impl TryFrom<Build> for monitor_client::entities::build::Build {
id: value.id,
name: value.name,
description: value.description,
permissions: value
.permissions
.into_iter()
.map(|(id, p)| (id, p.into()))
.collect(),
// permissions: value
// .permissions
// .into_iter()
// .map(|(id, p)| (id, p.into()))
// .collect(),
updated_at: unix_from_monitor_ts(&value.updated_at)?,
tags: Vec::new(),
info: BuildInfo {
+5 -5
View File
@@ -327,11 +327,11 @@ impl TryFrom<Deployment>
id: value.id,
name: value.name,
description: value.description,
permissions: value
.permissions
.into_iter()
.map(|(id, p)| (id, p.into()))
.collect(),
// permissions: value
// .permissions
// .into_iter()
// .map(|(id, p)| (id, p.into()))
// .collect(),
updated_at: unix_from_monitor_ts(&value.updated_at)?,
tags: Vec::new(),
info: (),
+2 -2
View File
@@ -238,10 +238,10 @@ impl Default for &PermissionLevel {
}
impl From<PermissionLevel>
for monitor_client::entities::PermissionLevel
for monitor_client::entities::permission::PermissionLevel
{
fn from(value: PermissionLevel) -> Self {
use monitor_client::entities::PermissionLevel::*;
use monitor_client::entities::permission::PermissionLevel::*;
match value {
PermissionLevel::None => None,
PermissionLevel::Read => Read,
+5 -5
View File
@@ -293,11 +293,11 @@ impl TryFrom<Server> for monitor_client::entities::server::Server {
id: value.id,
name: value.name,
description: value.description,
permissions: value
.permissions
.into_iter()
.map(|(id, p)| (id, p.into()))
.collect(),
// permissions: value
// .permissions
// .into_iter()
// .map(|(id, p)| (id, p.into()))
// .collect(),
updated_at: unix_from_monitor_ts(&value.updated_at)?,
tags: Vec::new(),
info: (),
+1 -1
View File
@@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::entities::{
permission::PermissionLevel,
update::{ResourceTarget, Update},
PermissionLevel,
};
use super::MonitorWriteRequest;
+1 -36
View File
@@ -1,5 +1,3 @@
use std::collections::HashMap;
use anyhow::{anyhow, Context};
use async_timing_util::unix_timestamp_ms;
use serde::{Deserialize, Serialize};
@@ -12,6 +10,7 @@ pub mod api_key;
pub mod build;
pub mod builder;
pub mod deployment;
pub mod permission;
pub mod procedure;
pub mod repo;
pub mod resource;
@@ -28,8 +27,6 @@ pub type U64 = u64;
pub type MongoDocument = mungos::mongodb::bson::Document;
#[typeshare(serialized_as = "MongoIdObj")]
pub type MongoId = String;
#[typeshare]
pub type PermissionsMap = HashMap<String, PermissionLevel>;
pub fn all_logs_success(logs: &[update::Log]) -> bool {
for log in logs {
@@ -296,38 +293,6 @@ pub enum Timelength {
ThirtyDays,
}
#[typeshare]
#[derive(
Serialize,
Deserialize,
Debug,
Display,
EnumString,
Hash,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PermissionLevel {
#[default]
None,
Read,
Execute,
Update,
}
impl Default for &PermissionLevel {
fn default() -> Self {
&PermissionLevel::None
}
}
#[typeshare]
#[derive(
Serialize,
+61
View File
@@ -0,0 +1,61 @@
use mongo_indexed::derive::MongoIndexed;
use mungos::mongodb::bson::{
doc, serde_helpers::hex_string_as_object_id, Document,
};
use serde::{Deserialize, Serialize};
use strum::{AsRefStr, Display, EnumString};
use typeshare::typeshare;
use super::{update::ResourceTarget, MongoId};
#[typeshare]
#[derive(Debug, Clone, Serialize, Deserialize, MongoIndexed)]
// To query for all permissions on a target
#[doc_index(doc! { "target.type": 1, "target.id": 1 })]
// Only one permission allowed per user / target
#[unique_doc_index(doc! { "user_id": 1, "target.type": 1, "target.id": 1 })]
pub struct Permission {
#[serde(
default,
rename = "_id",
skip_serializing_if = "String::is_empty",
with = "hex_string_as_object_id"
)]
pub id: MongoId,
#[index]
pub user_id: String,
pub target: ResourceTarget,
#[serde(default)]
pub level: PermissionLevel,
}
#[typeshare]
#[derive(
Serialize,
Deserialize,
Debug,
Display,
EnumString,
AsRefStr,
Hash,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
)]
pub enum PermissionLevel {
#[default]
None,
Read,
Execute,
Update,
}
impl Default for &PermissionLevel {
fn default() -> Self {
&PermissionLevel::None
}
}
+1 -5
View File
@@ -8,7 +8,7 @@ use typeshare::typeshare;
use crate::entities::{MongoId, I64};
use super::{update::ResourceTargetVariant, PermissionsMap};
use super::update::ResourceTargetVariant;
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Builder)]
@@ -28,10 +28,6 @@ pub struct Resource<Config, Info: Default = ()> {
#[builder(default)]
pub description: String,
#[serde(default)]
#[builder(setter(skip))]
pub permissions: PermissionsMap,
#[serde(default)]
#[builder(setter(skip))]
pub updated_at: I64,
+21 -1
View File
@@ -5,7 +5,7 @@ use mungos::mongodb::bson::{
doc, serde_helpers::hex_string_as_object_id, Document,
};
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};
use strum::{AsRefStr, Display, EnumString};
use typeshare::typeshare;
use crate::entities::{
@@ -151,6 +151,7 @@ impl Log {
Copy,
Display,
EnumString,
AsRefStr,
PartialEq,
Eq
)]
@@ -166,6 +167,25 @@ pub enum ResourceTarget {
Procedure(String),
}
impl ResourceTarget {
pub fn extract_variant_id(
&self,
) -> (ResourceTargetVariant, &String) {
let variant: ResourceTargetVariant = self.into();
let id = match &self {
ResourceTarget::System(id) => id,
ResourceTarget::Build(id) => id,
ResourceTarget::Builder(id) => id,
ResourceTarget::Deployment(id) => id,
ResourceTarget::Server(id) => id,
ResourceTarget::Repo(id) => id,
ResourceTarget::Alerter(id) => id,
ResourceTarget::Procedure(id) => id,
};
(variant, id)
}
}
impl Default for ResourceTarget {
fn default() -> Self {
Self::System("system".to_string())
-1
View File
@@ -5,7 +5,6 @@ use serde::Deserialize;
pub mod api;
pub mod busy;
pub mod entities;
pub mod permissioned;
pub mod ws;
mod request;
-17
View File
@@ -1,17 +0,0 @@
use crate::entities::{
resource::Resource, PermissionLevel, PermissionsMap,
};
pub trait Permissioned {
fn permissions_map(&self) -> &PermissionsMap;
fn get_user_permissions(&self, user_id: &str) -> PermissionLevel {
*self.permissions_map().get(user_id).unwrap_or_default()
}
}
impl<C, I: Default> Permissioned for Resource<C, I> {
fn permissions_map(&self) -> &PermissionsMap {
&self.permissions
}
}