mirror of
https://github.com/moghtech/komodo.git
synced 2026-09-07 08:00:54 +00:00
api check UserGroup for access
This commit is contained in:
@@ -19,7 +19,7 @@ use crate::{
|
||||
jwt::jwt_client,
|
||||
},
|
||||
config::core_config,
|
||||
helpers::get_user,
|
||||
helpers::query::get_user,
|
||||
state::State,
|
||||
};
|
||||
|
||||
|
||||
@@ -43,9 +43,10 @@ use crate::{
|
||||
config::core_config,
|
||||
db::db_client,
|
||||
helpers::{
|
||||
add_update, channel::build_cancel_channel, get_deployment_state,
|
||||
make_update, periphery_client, resource::StateResource,
|
||||
update_update,
|
||||
channel::build_cancel_channel,
|
||||
query::get_deployment_state, periphery_client,
|
||||
resource::StateResource,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::{action_states, State},
|
||||
};
|
||||
|
||||
@@ -22,8 +22,9 @@ use serror::serialize_error_pretty;
|
||||
use crate::{
|
||||
db::db_client,
|
||||
helpers::{
|
||||
add_update, get_server_with_status, make_update,
|
||||
periphery_client, resource::StateResource, update_update,
|
||||
query::get_server_with_status, periphery_client,
|
||||
resource::StateResource,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
monitor::update_cache_for_server,
|
||||
state::{action_states, State},
|
||||
@@ -352,7 +353,7 @@ impl Resolve<StopAllContainers, User> for State {
|
||||
)
|
||||
.await
|
||||
.context("failed to find deployments on server")?;
|
||||
|
||||
|
||||
let server_id = server.id.clone();
|
||||
|
||||
let inner = || async move {
|
||||
|
||||
@@ -12,8 +12,9 @@ use tokio::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
add_update, make_update, procedure::execute_procedure,
|
||||
resource::StateResource, update_update,
|
||||
procedure::execute_procedure,
|
||||
resource::StateResource,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::State,
|
||||
};
|
||||
|
||||
@@ -22,8 +22,9 @@ use serror::serialize_error_pretty;
|
||||
use crate::{
|
||||
db::db_client,
|
||||
helpers::{
|
||||
add_update, periphery_client, resource::StateResource,
|
||||
update_update,
|
||||
periphery_client,
|
||||
resource::StateResource,
|
||||
update::{add_update, update_update},
|
||||
},
|
||||
state::{action_states, State},
|
||||
};
|
||||
|
||||
@@ -17,8 +17,9 @@ use serror::serialize_error_pretty;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
add_update, make_update, periphery_client,
|
||||
resource::StateResource, update_update,
|
||||
periphery_client,
|
||||
resource::StateResource,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::{action_states, State},
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ use monitor_client::{
|
||||
use mungos::find::find_collect;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{db::db_client, helpers::get_tag, state::State};
|
||||
use crate::{db::db_client, helpers::query::get_tag, state::State};
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<GetTag, User> for State {
|
||||
|
||||
@@ -23,7 +23,7 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
db::db_client,
|
||||
helpers::{
|
||||
add_update, create_permission, make_update,
|
||||
create_permission, update::{add_update, make_update},
|
||||
remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@ use mungos::{by_id::find_one_by_id, mongodb::bson::doc};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
auth::random_string, db::db_client, helpers::get_user, state::State,
|
||||
auth::random_string, db::db_client, helpers::query::get_user, state::State,
|
||||
};
|
||||
|
||||
const SECRET_LENGTH: usize = 40;
|
||||
|
||||
@@ -24,10 +24,10 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
db::db_client,
|
||||
helpers::{
|
||||
add_update, create_permission, empty_or_only_spaces, make_update,
|
||||
create_permission, empty_or_only_spaces,
|
||||
remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update_update,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::{action_states, State},
|
||||
};
|
||||
|
||||
@@ -25,9 +25,9 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
db::db_client,
|
||||
helpers::{
|
||||
add_update, create_permission, make_update,
|
||||
remove_from_recently_viewed,
|
||||
create_permission, remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
state::State,
|
||||
};
|
||||
|
||||
@@ -30,11 +30,11 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
db::db_client,
|
||||
helpers::{
|
||||
add_update, create_permission, empty_or_only_spaces,
|
||||
get_deployment_state, make_update, periphery_client,
|
||||
create_permission, empty_or_only_spaces, periphery_client,
|
||||
query::get_deployment_state,
|
||||
remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update_update,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::{action_states, State},
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
cloud::aws::launch_ec2_instance,
|
||||
helpers::{add_update, make_update, update_update},
|
||||
helpers::update::{add_update, make_update, update_update},
|
||||
state::State,
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,10 @@ use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
db::db_client,
|
||||
helpers::{add_update, get_user, make_update},
|
||||
helpers::{
|
||||
query::get_user,
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
state::State,
|
||||
};
|
||||
|
||||
@@ -87,7 +90,10 @@ impl Resolve<UpdateUserPermissions, User> for State {
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<UpdateUserPermissionsOnTarget, User> for State {
|
||||
#[instrument(name = "UpdateUserPermissionsOnTarget", skip(self, admin))]
|
||||
#[instrument(
|
||||
name = "UpdateUserPermissionsOnTarget",
|
||||
skip(self, admin)
|
||||
)]
|
||||
async fn resolve(
|
||||
&self,
|
||||
UpdateUserPermissionsOnTarget {
|
||||
|
||||
@@ -26,10 +26,9 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
db::db_client,
|
||||
helpers::{
|
||||
add_update, create_permission, make_update,
|
||||
remove_from_recently_viewed,
|
||||
create_permission, remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update_update,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::{action_states, State},
|
||||
};
|
||||
|
||||
@@ -26,10 +26,9 @@ use serror::serialize_error_pretty;
|
||||
use crate::{
|
||||
db::db_client,
|
||||
helpers::{
|
||||
add_update, create_permission, make_update, periphery_client,
|
||||
remove_from_recently_viewed,
|
||||
create_permission, periphery_client, remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update_update,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::{action_states, State},
|
||||
};
|
||||
|
||||
@@ -25,12 +25,10 @@ use serror::serialize_error_pretty;
|
||||
use crate::{
|
||||
db::db_client,
|
||||
helpers::{
|
||||
add_update,
|
||||
cache::server_status_cache,
|
||||
create_permission, make_update, periphery_client,
|
||||
remove_from_recently_viewed,
|
||||
create_permission, periphery_client, remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update_update,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
monitor::update_cache_for_server,
|
||||
state::{action_states, State},
|
||||
|
||||
@@ -22,7 +22,7 @@ use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
db::db_client,
|
||||
helpers::{get_tag, get_tag_check_owner, resource::StateResource},
|
||||
helpers::{query::{get_tag, get_tag_check_owner}, resource::StateResource},
|
||||
state::State,
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ use mungos::{
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{db::db_client, helpers::get_user, state::State};
|
||||
use crate::{db::db_client, helpers::query::get_user, state::State};
|
||||
|
||||
const RECENTLY_VIEWED_MAX: usize = 10;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use serde::Deserialize;
|
||||
use serror::AuthError;
|
||||
|
||||
use crate::{db::db_client, helpers::get_user};
|
||||
use crate::{db::db_client, helpers::query::get_user};
|
||||
|
||||
use self::jwt::{jwt_client, JwtClaims};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ use monitor_client::entities::{
|
||||
tag::Tag,
|
||||
update::Update,
|
||||
user::User,
|
||||
user_group::UserGroup,
|
||||
};
|
||||
use mungos::{
|
||||
init::MongoBuilder,
|
||||
@@ -35,6 +36,7 @@ pub async fn db_client() -> &'static DbClient {
|
||||
|
||||
pub struct DbClient {
|
||||
pub users: Collection<User>,
|
||||
pub user_groups: Collection<UserGroup>,
|
||||
pub permissions: Collection<Permission>,
|
||||
pub api_keys: Collection<ApiKey>,
|
||||
pub tags: Collection<Tag>,
|
||||
@@ -90,6 +92,7 @@ impl DbClient {
|
||||
|
||||
let client = DbClient {
|
||||
users: User::collection(&db, true).await?,
|
||||
user_groups: UserGroup::collection(&db, true).await?,
|
||||
permissions: Permission::collection(&db, true).await?,
|
||||
api_keys: ApiKey::collection(&db, true).await?,
|
||||
tags: Tag::collection(&db, true).await?,
|
||||
|
||||
+11
-182
@@ -1,32 +1,25 @@
|
||||
use std::{str::FromStr, time::Duration};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use monitor_client::entities::{
|
||||
deployment::{Deployment, DockerContainerState},
|
||||
monitor_timestamp,
|
||||
permission::{Permission, PermissionLevel},
|
||||
server::{Server, ServerStatus},
|
||||
tag::Tag,
|
||||
update::{ResourceTarget, Update, UpdateListItem},
|
||||
user::{admin_service_user, User},
|
||||
Operation,
|
||||
permission::{Permission, PermissionLevel, UserTarget},
|
||||
server::Server,
|
||||
update::ResourceTarget,
|
||||
user::User,
|
||||
};
|
||||
use mungos::{
|
||||
by_id::{find_one_by_id, update_one_by_id},
|
||||
mongodb::bson::{doc, oid::ObjectId, to_document},
|
||||
};
|
||||
use periphery_client::{api, PeripheryClient};
|
||||
use mungos::mongodb::bson::doc;
|
||||
use periphery_client::PeripheryClient;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::{config::core_config, db::db_client};
|
||||
|
||||
use self::{channel::update_channel, resource::StateResource};
|
||||
|
||||
pub mod alert;
|
||||
pub mod cache;
|
||||
pub mod channel;
|
||||
pub mod procedure;
|
||||
pub mod query;
|
||||
pub mod resource;
|
||||
pub mod update;
|
||||
|
||||
pub fn empty_or_only_spaces(word: &str) -> bool {
|
||||
if word.is_empty() {
|
||||
@@ -44,170 +37,6 @@ pub fn random_duration(min_ms: u64, max_ms: u64) -> Duration {
|
||||
Duration::from_millis(thread_rng().gen_range(min_ms..max_ms))
|
||||
}
|
||||
|
||||
pub fn make_update(
|
||||
target: impl Into<ResourceTarget>,
|
||||
operation: Operation,
|
||||
user: &User,
|
||||
) -> Update {
|
||||
Update {
|
||||
start_ts: monitor_timestamp(),
|
||||
target: target.into(),
|
||||
operation,
|
||||
operator: user.id.clone(),
|
||||
success: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_user(user_id: &str) -> anyhow::Result<User> {
|
||||
if let Some(user) = admin_service_user(user_id) {
|
||||
return Ok(user);
|
||||
}
|
||||
find_one_by_id(&db_client().await.users, user_id)
|
||||
.await
|
||||
.context("failed to query mongo for user")?
|
||||
.with_context(|| format!("no user found with id {user_id}"))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_server_with_status(
|
||||
server_id_or_name: &str,
|
||||
) -> anyhow::Result<(Server, ServerStatus)> {
|
||||
let server = Server::get_resource(server_id_or_name).await?;
|
||||
if !server.config.enabled {
|
||||
return Ok((server, ServerStatus::Disabled));
|
||||
}
|
||||
let status =
|
||||
match periphery_client(&server)?.request(api::GetHealth {}).await
|
||||
{
|
||||
Ok(_) => ServerStatus::Ok,
|
||||
Err(_) => ServerStatus::NotOk,
|
||||
};
|
||||
Ok((server, status))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_deployment_state(
|
||||
deployment: &Deployment,
|
||||
) -> anyhow::Result<DockerContainerState> {
|
||||
if deployment.config.server_id.is_empty() {
|
||||
return Ok(DockerContainerState::NotDeployed);
|
||||
}
|
||||
let (server, status) =
|
||||
get_server_with_status(&deployment.config.server_id).await?;
|
||||
if status != ServerStatus::Ok {
|
||||
return Ok(DockerContainerState::Unknown);
|
||||
}
|
||||
let container = periphery_client(&server)?
|
||||
.request(api::container::GetContainerList {})
|
||||
.await?
|
||||
.into_iter()
|
||||
.find(|container| container.name == deployment.name);
|
||||
|
||||
let state = match container {
|
||||
Some(container) => container.state,
|
||||
None => DockerContainerState::NotDeployed,
|
||||
};
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
// TAG
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_tag(id_or_name: &str) -> anyhow::Result<Tag> {
|
||||
let query = match ObjectId::from_str(id_or_name) {
|
||||
Ok(id) => doc! { "_id": id },
|
||||
Err(_) => doc! { "name": id_or_name },
|
||||
};
|
||||
db_client()
|
||||
.await
|
||||
.tags
|
||||
.find_one(query, None)
|
||||
.await
|
||||
.context("failed to query mongo for tag")?
|
||||
.with_context(|| format!("no tag found matching {id_or_name}"))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_tag_check_owner(
|
||||
id_or_name: &str,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Tag> {
|
||||
let tag = get_tag(id_or_name).await?;
|
||||
if user.admin || tag.owner == user.id {
|
||||
return Ok(tag);
|
||||
}
|
||||
Err(anyhow!("user must be tag owner or admin"))
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
#[instrument(level = "debug")]
|
||||
async fn update_list_item(
|
||||
update: Update,
|
||||
) -> anyhow::Result<UpdateListItem> {
|
||||
let username = if User::is_service_user(&update.operator) {
|
||||
update.operator.clone()
|
||||
} else {
|
||||
find_one_by_id(&db_client().await.users, &update.operator)
|
||||
.await
|
||||
.context("failed to query mongo for user")?
|
||||
.with_context(|| {
|
||||
format!("no user found with id {}", update.operator)
|
||||
})?
|
||||
.username
|
||||
};
|
||||
let update = UpdateListItem {
|
||||
id: update.id,
|
||||
operation: update.operation,
|
||||
start_ts: update.start_ts,
|
||||
success: update.success,
|
||||
operator: update.operator,
|
||||
target: update.target,
|
||||
status: update.status,
|
||||
version: update.version,
|
||||
username,
|
||||
};
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
async fn send_update(update: UpdateListItem) -> anyhow::Result<()> {
|
||||
update_channel().sender.lock().await.send(update)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn add_update(
|
||||
mut update: Update,
|
||||
) -> anyhow::Result<String> {
|
||||
update.id = db_client()
|
||||
.await
|
||||
.updates
|
||||
.insert_one(&update, None)
|
||||
.await
|
||||
.context("failed to insert update into db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not object id")?
|
||||
.to_string();
|
||||
let id = update.id.clone();
|
||||
let update = update_list_item(update).await?;
|
||||
let _ = send_update(update).await;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn update_update(update: Update) -> anyhow::Result<()> {
|
||||
update_one_by_id(&db_client().await.updates, &update.id, mungos::update::Update::Set(to_document(&update)?), None)
|
||||
.await
|
||||
.context("failed to update the update on db. the update build process was deleted")?;
|
||||
let update = update_list_item(update).await?;
|
||||
let _ = send_update(update).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub async fn remove_from_recently_viewed<T>(
|
||||
resource: T,
|
||||
@@ -275,8 +104,8 @@ pub async fn create_permission<T>(
|
||||
.insert_one(
|
||||
Permission {
|
||||
id: Default::default(),
|
||||
user_id: user.id.clone(),
|
||||
target: target.clone(),
|
||||
user_target: UserTarget::User(user.id.clone()),
|
||||
resource_target: target.clone(),
|
||||
level,
|
||||
},
|
||||
None,
|
||||
|
||||
@@ -16,7 +16,7 @@ use tokio::sync::Mutex;
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
use super::update_update;
|
||||
use super::update::update_update;
|
||||
|
||||
#[instrument]
|
||||
pub async fn execute_procedure(
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use monitor_client::entities::{
|
||||
deployment::{Deployment, DockerContainerState},
|
||||
server::{Server, ServerStatus},
|
||||
tag::Tag,
|
||||
user::{admin_service_user, User},
|
||||
};
|
||||
use mungos::{
|
||||
by_id::find_one_by_id,
|
||||
find::find_collect,
|
||||
mongodb::bson::{doc, oid::ObjectId},
|
||||
};
|
||||
|
||||
use crate::db::db_client;
|
||||
|
||||
use super::resource::StateResource;
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_user(user_id: &str) -> anyhow::Result<User> {
|
||||
if let Some(user) = admin_service_user(user_id) {
|
||||
return Ok(user);
|
||||
}
|
||||
find_one_by_id(&db_client().await.users, user_id)
|
||||
.await
|
||||
.context("failed to query mongo for user")?
|
||||
.with_context(|| format!("no user found with id {user_id}"))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_server_with_status(
|
||||
server_id_or_name: &str,
|
||||
) -> anyhow::Result<(Server, ServerStatus)> {
|
||||
let server = Server::get_resource(server_id_or_name).await?;
|
||||
if !server.config.enabled {
|
||||
return Ok((server, ServerStatus::Disabled));
|
||||
}
|
||||
let status = match super::periphery_client(&server)?
|
||||
.request(periphery_client::api::GetHealth {})
|
||||
.await
|
||||
{
|
||||
Ok(_) => ServerStatus::Ok,
|
||||
Err(_) => ServerStatus::NotOk,
|
||||
};
|
||||
Ok((server, status))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_deployment_state(
|
||||
deployment: &Deployment,
|
||||
) -> anyhow::Result<DockerContainerState> {
|
||||
if deployment.config.server_id.is_empty() {
|
||||
return Ok(DockerContainerState::NotDeployed);
|
||||
}
|
||||
let (server, status) =
|
||||
get_server_with_status(&deployment.config.server_id).await?;
|
||||
if status != ServerStatus::Ok {
|
||||
return Ok(DockerContainerState::Unknown);
|
||||
}
|
||||
let container = super::periphery_client(&server)?
|
||||
.request(periphery_client::api::container::GetContainerList {})
|
||||
.await?
|
||||
.into_iter()
|
||||
.find(|container| container.name == deployment.name);
|
||||
|
||||
let state = match container {
|
||||
Some(container) => container.state,
|
||||
None => DockerContainerState::NotDeployed,
|
||||
};
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
// TAG
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_tag(id_or_name: &str) -> anyhow::Result<Tag> {
|
||||
let query = match ObjectId::from_str(id_or_name) {
|
||||
Ok(id) => doc! { "_id": id },
|
||||
Err(_) => doc! { "name": id_or_name },
|
||||
};
|
||||
db_client()
|
||||
.await
|
||||
.tags
|
||||
.find_one(query, None)
|
||||
.await
|
||||
.context("failed to query mongo for tag")?
|
||||
.with_context(|| format!("no tag found matching {id_or_name}"))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_tag_check_owner(
|
||||
id_or_name: &str,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Tag> {
|
||||
let tag = get_tag(id_or_name).await?;
|
||||
if user.admin || tag.owner == user.id {
|
||||
return Ok(tag);
|
||||
}
|
||||
Err(anyhow!("user must be tag owner or admin"))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_user_user_group_ids(
|
||||
user_id: &str,
|
||||
) -> anyhow::Result<Vec<String>> {
|
||||
let res = find_collect(
|
||||
&db_client().await.user_groups,
|
||||
doc! {
|
||||
"users": user_id
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to query db for user groups")?
|
||||
.into_iter()
|
||||
.map(|ug| ug.id)
|
||||
.collect();
|
||||
Ok(res)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::str::FromStr;
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use futures::future::join_all;
|
||||
@@ -54,7 +54,7 @@ use crate::{db::db_client, state::State};
|
||||
|
||||
use super::{
|
||||
cache::{deployment_status_cache, server_status_cache},
|
||||
get_tag,
|
||||
query::{get_tag, get_user_user_group_ids},
|
||||
};
|
||||
|
||||
pub trait StateResource {
|
||||
@@ -582,21 +582,26 @@ pub async fn get_user_permission_on_resource(
|
||||
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();
|
||||
let permission = find_collect(
|
||||
&db_client().await.permissions,
|
||||
doc! {
|
||||
"$or": user_target_query(user_id).await?,
|
||||
"target.type": resource_variant.as_ref(),
|
||||
"target.id": resource_id
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to query db for permissions")?
|
||||
.into_iter()
|
||||
// get the max permission user has between personal / any user groups
|
||||
.fold(PermissionLevel::None, |level, permission| {
|
||||
if permission.level > level {
|
||||
permission.level
|
||||
} else {
|
||||
level
|
||||
}
|
||||
});
|
||||
Ok(permission)
|
||||
}
|
||||
|
||||
@@ -628,8 +633,8 @@ pub async fn get_resource_ids_for_non_admin(
|
||||
let permissions = find_collect(
|
||||
&db_client().await.permissions,
|
||||
doc! {
|
||||
"user_id": user_id,
|
||||
"target.type": resource_type.as_ref(),
|
||||
"$or": user_target_query(user_id).await?,
|
||||
"resource_target.type": resource_type.as_ref(),
|
||||
"level": { "$in": ["Read", "Execute", "Update"] }
|
||||
},
|
||||
None,
|
||||
@@ -637,9 +642,29 @@ pub async fn get_resource_ids_for_non_admin(
|
||||
.await
|
||||
.context("failed to query permissions on db")?
|
||||
.into_iter()
|
||||
.map(|p| p.target.extract_variant_id().1.to_string())
|
||||
.collect();
|
||||
Ok(permissions)
|
||||
.map(|p| p.resource_target.extract_variant_id().1.to_string())
|
||||
// collect into hashset first to remove any duplicates
|
||||
.collect::<HashSet<_>>();
|
||||
Ok(permissions.into_iter().collect())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
async fn user_target_query(
|
||||
user_id: &str,
|
||||
) -> anyhow::Result<Vec<Document>> {
|
||||
let mut user_target_query = vec![
|
||||
doc! { "user_target.type": "User", "user_target.id": user_id },
|
||||
];
|
||||
let user_groups = get_user_user_group_ids(user_id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|ug_id| {
|
||||
doc! {
|
||||
"user_target.type": "UserGroup", "user_target.id": ug_id,
|
||||
}
|
||||
});
|
||||
user_target_query.extend(user_groups);
|
||||
Ok(user_target_query)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
use anyhow::Context;
|
||||
use monitor_client::entities::{
|
||||
monitor_timestamp,
|
||||
update::{ResourceTarget, Update, UpdateListItem},
|
||||
user::User,
|
||||
Operation,
|
||||
};
|
||||
use mungos::{
|
||||
by_id::{find_one_by_id, update_one_by_id},
|
||||
mongodb::bson::to_document,
|
||||
};
|
||||
|
||||
use crate::db::db_client;
|
||||
|
||||
use super::channel::update_channel;
|
||||
|
||||
pub fn make_update(
|
||||
target: impl Into<ResourceTarget>,
|
||||
operation: Operation,
|
||||
user: &User,
|
||||
) -> Update {
|
||||
Update {
|
||||
start_ts: monitor_timestamp(),
|
||||
target: target.into(),
|
||||
operation,
|
||||
operator: user.id.clone(),
|
||||
success: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
async fn update_list_item(
|
||||
update: Update,
|
||||
) -> anyhow::Result<UpdateListItem> {
|
||||
let username = if User::is_service_user(&update.operator) {
|
||||
update.operator.clone()
|
||||
} else {
|
||||
find_one_by_id(&db_client().await.users, &update.operator)
|
||||
.await
|
||||
.context("failed to query mongo for user")?
|
||||
.with_context(|| {
|
||||
format!("no user found with id {}", update.operator)
|
||||
})?
|
||||
.username
|
||||
};
|
||||
let update = UpdateListItem {
|
||||
id: update.id,
|
||||
operation: update.operation,
|
||||
start_ts: update.start_ts,
|
||||
success: update.success,
|
||||
operator: update.operator,
|
||||
target: update.target,
|
||||
status: update.status,
|
||||
version: update.version,
|
||||
username,
|
||||
};
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
async fn send_update(update: UpdateListItem) -> anyhow::Result<()> {
|
||||
update_channel().sender.lock().await.send(update)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn add_update(
|
||||
mut update: Update,
|
||||
) -> anyhow::Result<String> {
|
||||
update.id = db_client()
|
||||
.await
|
||||
.updates
|
||||
.insert_one(&update, None)
|
||||
.await
|
||||
.context("failed to insert update into db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not object id")?
|
||||
.to_string();
|
||||
let id = update.id.clone();
|
||||
let update = update_list_item(update).await?;
|
||||
let _ = send_update(update).await;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn update_update(update: Update) -> anyhow::Result<()> {
|
||||
update_one_by_id(&db_client().await.updates, &update.id, mungos::update::Update::Set(to_document(&update)?), None)
|
||||
.await
|
||||
.context("failed to update the update on db. the update build process was deleted")?;
|
||||
let update = update_list_item(update).await?;
|
||||
let _ = send_update(update).await;
|
||||
Ok(())
|
||||
}
|
||||
@@ -19,6 +19,7 @@ pub mod server;
|
||||
pub mod tag;
|
||||
pub mod update;
|
||||
pub mod user;
|
||||
pub mod user_group;
|
||||
|
||||
#[typeshare(serialized_as = "number")]
|
||||
pub type I64 = i64;
|
||||
|
||||
@@ -8,8 +8,7 @@ use typeshare::typeshare;
|
||||
|
||||
use super::{update::ResourceTarget, MongoId};
|
||||
|
||||
|
||||
/// Representation of a User or UserGroups permission on a resource.
|
||||
/// Representation of a User or UserGroups permission on a resource.
|
||||
#[typeshare]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, MongoIndexed)]
|
||||
// To query for all permissions on a target
|
||||
@@ -27,14 +26,24 @@ pub struct Permission {
|
||||
pub id: MongoId,
|
||||
/// Attached user
|
||||
#[index]
|
||||
pub user_id: String,
|
||||
pub user_target: UserTarget,
|
||||
/// The target resource
|
||||
pub target: ResourceTarget,
|
||||
pub resource_target: ResourceTarget,
|
||||
/// The permission level
|
||||
#[serde(default)]
|
||||
pub level: PermissionLevel,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", content = "id")]
|
||||
pub enum UserTarget {
|
||||
/// User Id
|
||||
User(String),
|
||||
/// UserGroup Id
|
||||
UserGroup(String),
|
||||
}
|
||||
|
||||
/// The levels of permission that a User or UserGroup can have on a resource.
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
|
||||
@@ -7,9 +7,7 @@ use mungos::mongodb::bson::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::entities::{MongoId, I64};
|
||||
|
||||
use super::update::ResourceTarget;
|
||||
use crate::entities::{update::ResourceTarget, MongoId, I64};
|
||||
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
@@ -20,7 +18,7 @@ use super::update::ResourceTarget;
|
||||
#[sparse_doc_index(doc! { "config.data.github_id": 1 })]
|
||||
pub struct User {
|
||||
/// The Mongo ID of the User.
|
||||
/// This field is de/serialized from/to JSON as
|
||||
/// This field is de/serialized from/to JSON as
|
||||
/// `{ "_id": { "$oid": "..." }, ...(rest of serialized User) }`
|
||||
#[serde(
|
||||
default,
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
use mongo_indexed::derive::MongoIndexed;
|
||||
use mungos::mongodb::bson::{
|
||||
serde_helpers::hex_string_as_object_id, Document,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use super::{MongoId, I64};
|
||||
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Default, MongoIndexed,
|
||||
)]
|
||||
pub struct UserGroup {
|
||||
/// The Mongo ID of the UserGroup.
|
||||
/// This field is de/serialized from/to JSON as
|
||||
/// `{ "_id": { "$oid": "..." }, ...(rest of serialized User) }`
|
||||
#[serde(
|
||||
default,
|
||||
rename = "_id",
|
||||
skip_serializing_if = "String::is_empty",
|
||||
with = "hex_string_as_object_id"
|
||||
)]
|
||||
pub id: MongoId,
|
||||
|
||||
#[unique_index]
|
||||
pub name: String,
|
||||
|
||||
/// User ids
|
||||
#[index]
|
||||
pub users: Vec<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub updated_at: I64,
|
||||
}
|
||||
Reference in New Issue
Block a user