replace those helpers with Resource trait

This commit is contained in:
mbecker20
2023-07-27 03:00:36 -04:00
parent dc7690f4cf
commit 70d11c3f5d
24 changed files with 528 additions and 538 deletions

View File

@@ -3,25 +3,19 @@ use std::time::Duration;
use anyhow::{anyhow, Context};
use monitor_types::{
entities::{
alerter::Alerter,
build::Build,
builder::Builder,
deployment::{Deployment, DockerContainerState},
repo::Repo,
server::{Server, ServerStatus},
tag::CustomTag,
update::{ResourceTarget, Update},
user::User,
Operation, PermissionLevel,
Operation,
},
monitor_timestamp,
permissioned::Permissioned,
};
use mungos::{mongodb::bson::doc, AggStage::*};
use periphery_client::{requests, PeripheryClient};
use rand::{thread_rng, Rng};
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, resource::Resource, state::State};
pub mod alert;
pub mod cache;
@@ -60,8 +54,6 @@ pub fn make_update(
}
impl State {
// USER
pub async fn get_user(&self, user_id: &str) -> anyhow::Result<User> {
self.db
.users
@@ -70,22 +62,11 @@ impl State {
.context(format!("no user exists with id {user_id}"))
}
// SERVER
pub async fn get_server(&self, server_id: &str) -> anyhow::Result<Server> {
self.db
.servers
.find_one_by_id(server_id)
.await
.context("failed to get server from db")?
.context(format!("did not find any server with id {server_id}"))
}
pub async fn get_server_with_status(
&self,
server_id: &str,
) -> anyhow::Result<(Server, ServerStatus)> {
let server = self.get_server(server_id).await?;
let server: Server = self.get_resource(server_id).await?;
if !server.config.enabled {
return Ok((server, ServerStatus::Disabled));
}
@@ -100,45 +81,6 @@ impl State {
Ok((server, status))
}
pub async fn get_server_check_permissions(
&self,
server_id: &str,
user: &RequestUser,
permission_level: PermissionLevel,
) -> anyhow::Result<Server> {
let server = self.get_server(server_id).await?;
let permissions = server.get_user_permissions(&user.id);
if user.is_admin || permissions >= permission_level {
Ok(server)
} else {
Err(anyhow!(
"user does not have required permissions on this server"
))
}
}
pub async fn get_user_permission_on_server(
&self,
user_id: &str,
server_id: &str,
) -> anyhow::Result<PermissionLevel> {
let server = self.get_server(server_id).await?;
Ok(server.get_user_permissions(user_id))
}
// DEPLOYMENT
pub async fn get_deployment(&self, deployment_id: &str) -> anyhow::Result<Deployment> {
self.db
.deployments
.find_one_by_id(deployment_id)
.await
.context("failed to get deployment from db")?
.context(format!(
"did not find any deployment with id {deployment_id}"
))
}
pub async fn get_deployment_state(
&self,
deployment: &Deployment,
@@ -167,208 +109,6 @@ impl State {
Ok(state)
}
pub async fn get_deployment_check_permissions(
&self,
deployment_id: &str,
user: &RequestUser,
permission_level: PermissionLevel,
) -> anyhow::Result<Deployment> {
let deployment = self.get_deployment(deployment_id).await?;
let permissions = deployment.get_user_permissions(&user.id);
if user.is_admin || permissions >= permission_level {
Ok(deployment)
} else {
Err(anyhow!(
"user does not have required permissions on this deployment"
))
}
}
pub async fn get_user_permission_on_deployment(
&self,
user_id: &str,
deployment_id: &str,
) -> anyhow::Result<PermissionLevel> {
let deployment = self.get_deployment(deployment_id).await?;
Ok(deployment.get_user_permissions(user_id))
}
// BUILD
pub async fn get_build(&self, build_id: &str) -> anyhow::Result<Build> {
self.db
.builds
.find_one_by_id(build_id)
.await
.context("failed to get build from db")?
.context(format!("did not find any build with id {build_id}"))
}
pub async fn get_build_check_permissions(
&self,
build_id: &str,
user: &RequestUser,
permission_level: PermissionLevel,
) -> anyhow::Result<Build> {
let build = self.get_build(build_id).await?;
let permissions = build.get_user_permissions(&user.id);
if user.is_admin || permissions >= permission_level {
Ok(build)
} else {
Err(anyhow!(
"user does not have required permissions on this build"
))
}
}
pub async fn get_user_permission_on_build(
&self,
user_id: &str,
build_id: &str,
) -> anyhow::Result<PermissionLevel> {
let build = self.get_build(build_id).await?;
Ok(build.get_user_permissions(user_id))
}
pub async fn get_build_ids_for_non_admin(&self, user_id: &str) -> anyhow::Result<Vec<String>> {
self.db
.builds
.aggregate_collect(
[
Match(doc! {
format!("permissions.{}", user_id): { "$in": ["update", "execute", "read"] }
}),
Project(doc! { "_id": 1 }),
],
None,
)
.await
.context("failed to get build ids for non admin | aggregation")?
.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<_>>>()
.context("failed to get build ids for non admin | extract id from document")
}
// BUILDER
pub async fn get_builder(&self, builder_id: &str) -> anyhow::Result<Builder> {
self.db
.builders
.find_one_by_id(builder_id)
.await
.context("failed to get builder from db")?
.context(format!("did not find any builder with id {builder_id}"))
}
pub async fn get_builder_check_permissions(
&self,
builder_id: &str,
user: &RequestUser,
permission_level: PermissionLevel,
) -> anyhow::Result<Builder> {
let builder = self.get_builder(builder_id).await?;
let permissions = builder.get_user_permissions(&user.id);
if user.is_admin || permissions >= permission_level {
Ok(builder)
} else {
Err(anyhow!(
"user does not have required permissions on this builder"
))
}
}
pub async fn get_user_permission_on_builder(
&self,
user_id: &str,
builder_id: &str,
) -> anyhow::Result<PermissionLevel> {
let builder = self.get_builder(builder_id).await?;
Ok(builder.get_user_permissions(user_id))
}
// REPO
pub async fn get_repo(&self, repo_id: &str) -> anyhow::Result<Repo> {
self.db
.repos
.find_one_by_id(repo_id)
.await
.context("failed to get repo from db")?
.context(format!("did not find any repo with id {repo_id}"))
}
pub async fn get_repo_check_permissions(
&self,
repo_id: &str,
user: &RequestUser,
permission_level: PermissionLevel,
) -> anyhow::Result<Repo> {
let repo = self.get_repo(repo_id).await?;
let permissions = repo.get_user_permissions(&user.id);
if user.is_admin || permissions >= permission_level {
Ok(repo)
} else {
Err(anyhow!(
"user does not have required permissions on this repo"
))
}
}
pub async fn get_user_permission_on_repo(
&self,
user_id: &str,
repo_id: &str,
) -> anyhow::Result<PermissionLevel> {
let repo = self.get_repo(repo_id).await?;
Ok(repo.get_user_permissions(user_id))
}
// ALERTER
pub async fn get_alerter(&self, alerter_id: &str) -> anyhow::Result<Alerter> {
self.db
.alerters
.find_one_by_id(alerter_id)
.await
.context("failed to get alerter from mongo")?
.context(format!("did not find any alerter with id {alerter_id}"))
}
pub async fn get_alerter_check_permissions(
&self,
alerter_id: &str,
user: &RequestUser,
permission_level: PermissionLevel,
) -> anyhow::Result<Alerter> {
let alerter = self.get_alerter(alerter_id).await?;
let permissions = alerter.get_user_permissions(&user.id);
if user.is_admin || permissions >= permission_level {
Ok(alerter)
} else {
Err(anyhow!(
"user does not have required permissions on this alerter"
))
}
}
pub async fn get_user_permission_on_alerter(
&self,
user_id: &str,
alerter_id: &str,
) -> anyhow::Result<PermissionLevel> {
let alerter = self.get_alerter(alerter_id).await?;
Ok(alerter.get_user_permissions(user_id))
}
// TAG
pub async fn get_tag(&self, tag_id: &str) -> anyhow::Result<CustomTag> {

View File

@@ -2,7 +2,7 @@ use anyhow::{anyhow, Context};
use axum::{extract::Path, http::HeaderMap, routing::post, Router};
use hex::ToHex;
use hmac::{Hmac, Mac};
use monitor_types::requests::execute;
use monitor_types::{requests::execute, entities::{build::Build, repo::Repo}};
use resolver_api::Resolve;
use serde::Deserialize;
use sha2::Sha256;
@@ -10,7 +10,7 @@ use sha2::Sha256;
use crate::{
auth::InnerRequestUser,
helpers::random_duration,
state::{State, StateExtension},
state::{State, StateExtension}, resource::Resource,
};
type HmacSha256 = Hmac<Sha256>;
@@ -72,8 +72,8 @@ impl State {
) -> anyhow::Result<()> {
self.verify_gh_signature(headers, &body).await?;
let request_branch = extract_branch(&body)?;
let expected_branch = self.get_build(&build_id).await?.config.branch;
if request_branch != expected_branch {
let build: Build = self.get_resource(&build_id).await?;
if request_branch != build.config.branch {
return Err(anyhow!("request branch does not match expected"));
}
self.resolve(
@@ -99,8 +99,8 @@ impl State {
) -> anyhow::Result<()> {
self.verify_gh_signature(headers, &body).await?;
let request_branch = extract_branch(&body)?;
let expected_branch = self.get_repo(&repo_id).await?.config.branch;
if request_branch != expected_branch {
let repo: Repo = self.get_resource(&repo_id).await?;
if request_branch != repo.config.branch {
return Err(anyhow!("request branch does not match expected"));
}
self.resolve(
@@ -126,8 +126,8 @@ impl State {
) -> anyhow::Result<()> {
self.verify_gh_signature(headers, &body).await?;
let request_branch = extract_branch(&body)?;
let expected_branch = self.get_repo(&repo_id).await?.config.branch;
if request_branch != expected_branch {
let repo: Repo = self.get_resource(&repo_id).await?;
if request_branch != repo.config.branch {
return Err(anyhow!("request branch does not match expected"));
}
self.resolve(

View File

@@ -14,6 +14,7 @@ mod monitor;
mod requests;
mod state;
mod ws;
mod resource;
async fn app() -> anyhow::Result<()> {
let state = state::State::load().await?;

View File

@@ -7,8 +7,9 @@ use monitor_types::{
all_logs_success,
entities::{
build::{Build, BuildBuilderConfig},
builder::{AwsBuilderConfig, BuilderConfig},
builder::{AwsBuilderConfig, Builder, BuilderConfig},
deployment::DockerContainerState,
server::Server,
update::{Log, Update},
Operation, PermissionLevel,
},
@@ -27,6 +28,7 @@ use crate::{
auth::{InnerRequestUser, RequestUser},
cloud::{aws::Ec2Instance, BuildCleanupData},
helpers::make_update,
resource::Resource,
state::State,
};
@@ -41,8 +43,8 @@ impl Resolve<RunBuild, RequestUser> for State {
return Err(anyhow!("build busy"));
}
let mut build = self
.get_build_check_permissions(&build_id, &user, PermissionLevel::Execute)
let mut build: Build = self
.get_resource_check_permissions(&build_id, &user, PermissionLevel::Execute)
.await?;
build.config.version.increment();
@@ -199,7 +201,8 @@ impl Resolve<CancelBuild, RequestUser> for State {
CancelBuild { build_id }: CancelBuild,
user: RequestUser,
) -> anyhow::Result<CancelBuildResponse> {
self.get_build_check_permissions(&build_id, &user, PermissionLevel::Execute)
let _: Build = self
.get_resource_check_permissions(&build_id, &user, PermissionLevel::Execute)
.await?;
self.build_cancel.sender.lock().await.send(build_id)?;
Ok(CancelBuildResponse {})
@@ -220,7 +223,7 @@ impl State {
if server_id.is_empty() {
return Err(anyhow!("build has not configured a builder"));
}
let server = self.get_server(server_id).await?;
let server: Server = self.get_resource(server_id).await?;
let periphery = self.periphery_client(&server);
Ok((
periphery,
@@ -233,7 +236,7 @@ impl State {
if builder_id.is_empty() {
return Err(anyhow!("build has not configured a builder"));
}
let builder = self.get_builder(builder_id).await?;
let builder: Builder = self.get_resource(builder_id).await?;
match builder.config {
BuilderConfig::Aws(config) => self.get_aws_builder(build, config, update).await,
}

View File

@@ -2,7 +2,8 @@ use anyhow::anyhow;
use async_trait::async_trait;
use monitor_types::{
entities::{
deployment::DeploymentImage,
build::Build,
deployment::{Deployment, DeploymentImage},
server::ServerStatus,
update::{Log, ResourceTarget, Update, UpdateStatus},
Operation, PermissionLevel, Version,
@@ -13,7 +14,7 @@ use monitor_types::{
use periphery_client::requests;
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, resource::Resource, state::State};
#[async_trait]
impl Resolve<Deploy, RequestUser> for State {
@@ -30,8 +31,8 @@ impl Resolve<Deploy, RequestUser> for State {
return Err(anyhow!("deployment busy"));
}
let mut deployment = self
.get_deployment_check_permissions(&deployment_id, &user, PermissionLevel::Execute)
let mut deployment: Deployment = self
.get_resource_check_permissions(&deployment_id, &user, PermissionLevel::Execute)
.await?;
if deployment.config.server_id.is_empty() {
@@ -54,7 +55,7 @@ impl Resolve<Deploy, RequestUser> for State {
let version = match deployment.config.image {
DeploymentImage::Build { build_id, version } => {
let build = self.get_build(&build_id).await?;
let build: Build = self.get_resource(&build_id).await?;
let image_name = get_image_name(&build);
let version = if version.is_none() {
build.config.version
@@ -136,8 +137,8 @@ impl Resolve<StartContainer, RequestUser> for State {
return Err(anyhow!("deployment busy"));
}
let deployment = self
.get_deployment_check_permissions(&deployment_id, &user, PermissionLevel::Execute)
let deployment: Deployment = self
.get_resource_check_permissions(&deployment_id, &user, PermissionLevel::Execute)
.await?;
if deployment.config.server_id.is_empty() {
@@ -223,8 +224,8 @@ impl Resolve<StopContainer, RequestUser> for State {
return Err(anyhow!("deployment busy"));
}
let deployment = self
.get_deployment_check_permissions(&deployment_id, &user, PermissionLevel::Execute)
let deployment: Deployment = self
.get_resource_check_permissions(&deployment_id, &user, PermissionLevel::Execute)
.await?;
if deployment.config.server_id.is_empty() {
@@ -314,8 +315,8 @@ impl Resolve<RemoveContainer, RequestUser> for State {
return Err(anyhow!("deployment busy"));
}
let deployment = self
.get_deployment_check_permissions(&deployment_id, &user, PermissionLevel::Execute)
let deployment: Deployment = self
.get_resource_check_permissions(&deployment_id, &user, PermissionLevel::Execute)
.await?;
if deployment.config.server_id.is_empty() {

View File

@@ -3,7 +3,7 @@ use async_trait::async_trait;
use monitor_types::{
entities::{
update::{Log, ResourceTarget, Update, UpdateStatus},
Operation, PermissionLevel,
Operation, PermissionLevel, repo::Repo, server::Server,
},
monitor_timestamp, optional_string,
requests::execute::*,
@@ -12,7 +12,7 @@ use mungos::mongodb::bson::doc;
use periphery_client::requests;
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, state::State, resource::Resource};
#[async_trait]
impl Resolve<CloneRepo, RequestUser> for State {
@@ -21,8 +21,8 @@ impl Resolve<CloneRepo, RequestUser> for State {
CloneRepo { id }: CloneRepo,
user: RequestUser,
) -> anyhow::Result<Update> {
let repo = self
.get_repo_check_permissions(&id, &user, PermissionLevel::Execute)
let repo: Repo = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Execute)
.await?;
let inner = || async move {
@@ -32,7 +32,7 @@ impl Resolve<CloneRepo, RequestUser> for State {
return Err(anyhow!("repo has no server attached"));
}
let server = self.get_server(&repo.config.server_id).await?;
let server: Server = self.get_resource(&repo.config.server_id).await?;
let mut update = Update {
operation: Operation::CloneRepo,
@@ -112,8 +112,8 @@ impl Resolve<PullRepo, RequestUser> for State {
PullRepo { id }: PullRepo,
user: RequestUser,
) -> anyhow::Result<Update> {
let repo = self
.get_repo_check_permissions(&id, &user, PermissionLevel::Update)
let repo: Repo = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let inner = || async move {
@@ -123,7 +123,7 @@ impl Resolve<PullRepo, RequestUser> for State {
return Err(anyhow!("repo has no server attached"));
}
let server = self.get_server(&repo.config.server_id).await?;
let server: Server = self.get_resource(&repo.config.server_id).await?;
let mut update = Update {
operation: Operation::PullRepo,

View File

@@ -3,7 +3,7 @@ use async_trait::async_trait;
use monitor_types::{
entities::{
update::{Log, ResourceTarget, Update, UpdateStatus},
Operation, PermissionLevel,
Operation, PermissionLevel, server::Server,
},
monitor_timestamp,
requests::execute::*,
@@ -11,7 +11,7 @@ use monitor_types::{
use periphery_client::requests;
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, state::State, resource::Resource};
#[async_trait]
impl Resolve<PruneDockerContainers, RequestUser> for State {
@@ -24,8 +24,8 @@ impl Resolve<PruneDockerContainers, RequestUser> for State {
return Err(anyhow!("server busy"));
}
let server = self
.get_server_check_permissions(&server_id, &user, PermissionLevel::Execute)
let server: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Execute)
.await?;
let inner = || async {
@@ -95,8 +95,8 @@ impl Resolve<PruneDockerNetworks, RequestUser> for State {
}
let inner = || async {
let server = self
.get_server_check_permissions(&server_id, &user, PermissionLevel::Execute)
let server: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Execute)
.await?;
let start_ts = monitor_timestamp();
@@ -165,8 +165,8 @@ impl Resolve<PruneDockerImages, RequestUser> for State {
}
let inner = || async {
let server = self
.get_server_check_permissions(&server_id, &user, PermissionLevel::Execute)
let server: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Execute)
.await?;
let start_ts = monitor_timestamp();

View File

@@ -0,0 +1,77 @@
use anyhow::Context;
use async_trait::async_trait;
use monitor_types::{
entities::{alerter::Alerter, PermissionLevel},
requests::read::*,
};
use mungos::mongodb::bson::doc;
use resolver_api::Resolve;
use crate::{auth::RequestUser, resource::Resource, state::State};
#[async_trait]
impl Resolve<GetAlerter, RequestUser> for State {
async fn resolve(
&self,
GetAlerter { id }: GetAlerter,
user: RequestUser,
) -> anyhow::Result<Alerter> {
self.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await
}
}
#[async_trait]
impl Resolve<ListAlerters, RequestUser> for State {
async fn resolve(
&self,
ListAlerters { query }: ListAlerters,
user: RequestUser,
) -> anyhow::Result<Vec<Alerter>> {
let mut query = query.unwrap_or_default();
if !user.is_admin {
query.insert(
format!("permissions.{}", user.id),
doc! { "$in": ["read", "execute", "update"] },
);
}
let alerters = self
.db
.alerters
.get_some(query, None)
.await
.context("failed to pull alerters from mongo")?;
Ok(alerters)
}
}
#[async_trait]
impl Resolve<GetAlertersSummary, RequestUser> for State {
async fn resolve(
&self,
GetAlertersSummary {}: GetAlertersSummary,
user: RequestUser,
) -> anyhow::Result<GetAlertersSummaryResponse> {
let query = if user.is_admin {
None
} else {
let query = doc! {
format!("permissions.{}", user.id): { "$in": ["read", "execute", "update"] }
};
Some(query)
};
let total = self
.db
.alerters
.collection
.count_documents(query, None)
.await
.context("failed to count all alerter documents")?;
let res = GetAlertersSummaryResponse {
total: total as u32,
};
Ok(res)
}
}

View File

@@ -10,12 +10,12 @@ use monitor_types::{
use mungos::mongodb::bson::doc;
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, resource::Resource, state::State};
#[async_trait]
impl Resolve<GetBuild, RequestUser> for State {
async fn resolve(&self, GetBuild { id }: GetBuild, user: RequestUser) -> anyhow::Result<Build> {
self.get_build_check_permissions(&id, &user, PermissionLevel::Read)
self.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await
}
}
@@ -27,20 +27,7 @@ impl Resolve<ListBuilds, RequestUser> for State {
ListBuilds { query }: ListBuilds,
user: RequestUser,
) -> anyhow::Result<Vec<BuildListItem>> {
let mut query = query.unwrap_or_default();
if !user.is_admin {
query.insert(
format!("permissions.{}", user.id),
doc! { "$in": ["read", "execute", "update"] },
);
}
let builds = self
.db
.builds
.get_some(query, None)
.await
.context("failed to pull builds from mongo")?;
let builds: Vec<Build> = self.list_resources_for_user(&user, query).await?;
let builds = builds
.into_iter()
@@ -64,7 +51,7 @@ impl Resolve<GetBuildActionState, RequestUser> for State {
GetBuildActionState { id }: GetBuildActionState,
user: RequestUser,
) -> anyhow::Result<BuildActionState> {
self.get_build_check_permissions(&id, &user, PermissionLevel::Read)
let _: Build = self.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await?;
let action_state = self.action_states.build.get(&id).await.unwrap_or_default();
Ok(action_state)

View File

@@ -7,7 +7,7 @@ use monitor_types::{
use mungos::mongodb::bson::doc;
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, resource::Resource, state::State};
#[async_trait]
impl Resolve<GetBuilder, RequestUser> for State {
@@ -16,7 +16,7 @@ impl Resolve<GetBuilder, RequestUser> for State {
GetBuilder { id }: GetBuilder,
user: RequestUser,
) -> anyhow::Result<Builder> {
self.get_builder_check_permissions(&id, &user, PermissionLevel::Read)
self.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await
}
}
@@ -28,22 +28,7 @@ impl Resolve<ListBuilders, RequestUser> for State {
ListBuilders { query }: ListBuilders,
user: RequestUser,
) -> anyhow::Result<Vec<Builder>> {
let mut query = query.unwrap_or_default();
if !user.is_admin {
query.insert(
format!("permissions.{}", user.id),
doc! { "$in": ["read", "execute", "update"] },
);
}
let builders = self
.db
.builders
.get_some(query, None)
.await
.context("failed to pull builders from mongo")?;
Ok(builders)
self.list_resources_for_user(&user, query).await
}
}
@@ -68,7 +53,7 @@ impl Resolve<GetBuildersSummary, RequestUser> for State {
.collection
.count_documents(query, None)
.await
.context("failed to count all build documents")?;
.context("failed to count all builder documents")?;
let res = GetBuildersSummaryResponse {
total: total as u32,
};

View File

@@ -9,6 +9,7 @@ use monitor_types::{
Deployment, DeploymentActionState, DeploymentConfig, DeploymentImage,
DockerContainerState, DockerContainerStats,
},
server::Server,
update::{Log, UpdateStatus},
Operation, PermissionLevel,
},
@@ -18,7 +19,7 @@ use mungos::mongodb::{bson::doc, options::FindOneOptions};
use periphery_client::requests;
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, resource::Resource, state::State};
#[async_trait]
impl Resolve<GetDeployment, RequestUser> for State {
@@ -27,7 +28,7 @@ impl Resolve<GetDeployment, RequestUser> for State {
GetDeployment { id }: GetDeployment,
user: RequestUser,
) -> anyhow::Result<Deployment> {
self.get_deployment_check_permissions(&id, &user, PermissionLevel::Read)
self.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await
}
}
@@ -39,19 +40,7 @@ impl Resolve<ListDeployments, RequestUser> for State {
ListDeployments { query }: ListDeployments,
user: RequestUser,
) -> anyhow::Result<Vec<DeploymentListItem>> {
let mut query = query.unwrap_or_default();
if !user.is_admin {
query.insert(
format!("permissions.{}", user.id),
doc! { "$in": ["read", "execute", "update"] },
);
}
let deployments = self
.db
.deployments
.get_some(query, None)
.await
.context("failed to pull deployments from mongo")?;
let deployments: Vec<Deployment> = self.list_resources_for_user(&user, query).await?;
let deployments = deployments.into_iter().map(|deployment| async {
let status = self.deployment_status_cache.get(&deployment.id).await;
@@ -81,7 +70,8 @@ impl Resolve<GetDeploymentStatus, RequestUser> for State {
GetDeploymentStatus { id }: GetDeploymentStatus,
user: RequestUser,
) -> anyhow::Result<GetDeploymentStatusResponse> {
self.get_deployment_check_permissions(&id, &user, PermissionLevel::Read)
let _: Deployment = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await?;
let status = self
.deployment_status_cache
@@ -113,12 +103,12 @@ impl Resolve<GetLog, RequestUser> for State {
config: DeploymentConfig { server_id, .. },
..
} = self
.get_deployment_check_permissions(&deployment_id, &user, PermissionLevel::Read)
.get_resource_check_permissions(&deployment_id, &user, PermissionLevel::Read)
.await?;
if server_id.is_empty() {
return Ok(Log::default());
}
let server = self.get_server(&server_id).await?;
let server: Server = self.get_resource(&server_id).await?;
self.periphery_client(&server)
.request(requests::GetContainerLog {
name,
@@ -140,7 +130,7 @@ impl Resolve<GetDeployedVersion, RequestUser> for State {
config: DeploymentConfig { image, .. },
..
} = self
.get_deployment_check_permissions(&deployment_id, &user, PermissionLevel::Read)
.get_resource_check_permissions(&deployment_id, &user, PermissionLevel::Read)
.await?;
let version = match image {
DeploymentImage::Build { .. } => {
@@ -196,12 +186,12 @@ impl Resolve<GetDeploymentStats, RequestUser> for State {
config: DeploymentConfig { server_id, .. },
..
} = self
.get_deployment_check_permissions(&id, &user, PermissionLevel::Read)
.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await?;
if server_id.is_empty() {
return Err(anyhow!("deployment has no server attached"));
}
let server = self.get_server(&server_id).await?;
let server: Server = self.get_resource(&server_id).await?;
self.periphery_client(&server)
.request(requests::GetContainerStats { name })
.await
@@ -216,7 +206,8 @@ impl Resolve<GetDeploymentActionState, RequestUser> for State {
GetDeploymentActionState { id }: GetDeploymentActionState,
user: RequestUser,
) -> anyhow::Result<DeploymentActionState> {
self.get_deployment_check_permissions(&id, &user, PermissionLevel::Read)
let _: Deployment = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await?;
let action_state = self
.action_states

View File

@@ -25,6 +25,7 @@ mod server;
mod tag;
mod update;
mod user;
mod alerter;
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Resolver)]
@@ -67,17 +68,22 @@ enum ReadRequest {
ListBuilds(ListBuilds),
GetBuildActionState(GetBuildActionState),
// ==== BUILDER ====
GetBuildersSummary(GetBuildersSummary),
GetBuilder(GetBuilder),
ListBuilders(ListBuilders),
// ==== REPO ====
GetReposSummary(GetReposSummary),
GetRepo(GetRepo),
ListRepos(ListRepos),
GetRepoActionState(GetRepoActionState),
// ==== BUILDER ====
GetBuildersSummary(GetBuildersSummary),
GetBuilder(GetBuilder),
ListBuilders(ListBuilders),
// ==== ALERTER ====
GetAlertersSummary(GetAlertersSummary),
GetAlerter(GetAlerter),
ListAlerters(ListAlerters),
// ==== TAG ====
GetTag(GetTag),
ListTags(ListTags),

View File

@@ -10,12 +10,12 @@ use monitor_types::{
use mungos::mongodb::bson::doc;
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, resource::Resource, state::State};
#[async_trait]
impl Resolve<GetRepo, RequestUser> for State {
async fn resolve(&self, GetRepo { id }: GetRepo, user: RequestUser) -> anyhow::Result<Repo> {
self.get_repo_check_permissions(&id, &user, PermissionLevel::Read)
self.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await
}
}
@@ -27,20 +27,7 @@ impl Resolve<ListRepos, RequestUser> for State {
ListRepos { query }: ListRepos,
user: RequestUser,
) -> anyhow::Result<Vec<RepoListItem>> {
let mut query = query.unwrap_or_default();
if !user.is_admin {
query.insert(
format!("permissions.{}", user.id),
doc! { "$in": ["read", "execute", "update"] },
);
}
let repos = self
.db
.repos
.get_some(query, None)
.await
.context("failed to pull repos from mongo")?;
let repos: Vec<Repo> = self.list_resources_for_user(&user, query).await?;
let repos = repos
.into_iter()
@@ -63,7 +50,7 @@ impl Resolve<GetRepoActionState, RequestUser> for State {
GetRepoActionState { id }: GetRepoActionState,
user: RequestUser,
) -> anyhow::Result<RepoActionState> {
self.get_repo_check_permissions(&id, &user, PermissionLevel::Read)
let _: Repo = self.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await?;
let action_state = self.action_states.repo.get(&id).await.unwrap_or_default();
Ok(action_state)

View File

@@ -1,4 +1,4 @@
use anyhow::{anyhow, Context};
use anyhow::anyhow;
use async_trait::async_trait;
use futures::future::join_all;
use monitor_types::{
@@ -12,11 +12,10 @@ use monitor_types::{
},
requests::read::*,
};
use mungos::mongodb::bson::doc;
use periphery_client::requests;
use resolver_api::{Resolve, ResolveToString};
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, resource::Resource, state::State};
#[async_trait]
impl Resolve<GetPeripheryVersion, RequestUser> for State {
@@ -25,7 +24,8 @@ impl Resolve<GetPeripheryVersion, RequestUser> for State {
req: GetPeripheryVersion,
user: RequestUser,
) -> anyhow::Result<GetPeripheryVersionResponse> {
self.get_server_check_permissions(&req.server_id, &user, PermissionLevel::Read)
let _: Server = self
.get_resource_check_permissions(&req.server_id, &user, PermissionLevel::Read)
.await?;
let version = self
.server_status_cache
@@ -40,7 +40,7 @@ impl Resolve<GetPeripheryVersion, RequestUser> for State {
#[async_trait]
impl Resolve<GetServer, RequestUser> for State {
async fn resolve(&self, req: GetServer, user: RequestUser) -> anyhow::Result<Server> {
self.get_server_check_permissions(&req.id, &user, PermissionLevel::Read)
self.get_resource_check_permissions(&req.id, &user, PermissionLevel::Read)
.await
}
}
@@ -52,22 +52,9 @@ impl Resolve<ListServers, RequestUser> for State {
ListServers { query }: ListServers,
user: RequestUser,
) -> anyhow::Result<Vec<ServerListItem>> {
let mut query = query.unwrap_or_default();
if !user.is_admin {
query.insert(
format!("permissions.{}", user.id),
doc! { "$in": ["read", "execute", "update"] },
);
}
let servers = self.list_resources_for_user(&user, query).await?;
let servers = self
.db
.servers
.get_some(query, None)
.await
.context("failed to pull servers from mongo")?;
let servers = servers.into_iter().map(|server| async {
let servers = servers.into_iter().map(|server: Server| async {
let status = self.server_status_cache.get(&server.id).await;
ServerListItem {
id: server.id,
@@ -90,7 +77,8 @@ impl Resolve<GetServerStatus, RequestUser> for State {
GetServerStatus { id }: GetServerStatus,
user: RequestUser,
) -> anyhow::Result<GetServerStatusResponse> {
self.get_server_check_permissions(&id, &user, PermissionLevel::Read)
let _: Server = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await?;
let status = self
.server_status_cache
@@ -111,7 +99,8 @@ impl Resolve<GetServerActionState, RequestUser> for State {
GetServerActionState { id }: GetServerActionState,
user: RequestUser,
) -> anyhow::Result<ServerActionState> {
self.get_server_check_permissions(&id, &user, PermissionLevel::Read)
let _: Server = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
.await?;
let action_state = self.action_states.server.get(&id).await.unwrap_or_default();
Ok(action_state)
@@ -125,8 +114,8 @@ impl Resolve<GetSystemInformation, RequestUser> for State {
GetSystemInformation { server_id }: GetSystemInformation,
user: RequestUser,
) -> anyhow::Result<SystemInformation> {
let server = self
.get_server_check_permissions(&server_id, &user, PermissionLevel::Read)
let server: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Read)
.await?;
self.periphery_client(&server)
.request(requests::GetSystemInformation {})
@@ -141,7 +130,8 @@ impl ResolveToString<GetAllSystemStats, RequestUser> for State {
GetAllSystemStats { server_id }: GetAllSystemStats,
user: RequestUser,
) -> anyhow::Result<String> {
self.get_server_check_permissions(&server_id, &user, PermissionLevel::Read)
let _: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Read)
.await?;
let status = self
.server_status_cache
@@ -164,7 +154,8 @@ impl ResolveToString<GetBasicSystemStats, RequestUser> for State {
GetBasicSystemStats { server_id }: GetBasicSystemStats,
user: RequestUser,
) -> anyhow::Result<String> {
self.get_server_check_permissions(&server_id, &user, PermissionLevel::Read)
let _: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Read)
.await?;
let status = self
.server_status_cache
@@ -187,7 +178,8 @@ impl ResolveToString<GetCpuUsage, RequestUser> for State {
GetCpuUsage { server_id }: GetCpuUsage,
user: RequestUser,
) -> anyhow::Result<String> {
self.get_server_check_permissions(&server_id, &user, PermissionLevel::Read)
let _: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Read)
.await?;
let status = self
.server_status_cache
@@ -210,7 +202,8 @@ impl ResolveToString<GetDiskUsage, RequestUser> for State {
GetDiskUsage { server_id }: GetDiskUsage,
user: RequestUser,
) -> anyhow::Result<String> {
self.get_server_check_permissions(&server_id, &user, PermissionLevel::Read)
let _: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Read)
.await?;
let status = self
.server_status_cache
@@ -233,7 +226,8 @@ impl ResolveToString<GetNetworkUsage, RequestUser> for State {
GetNetworkUsage { server_id }: GetNetworkUsage,
user: RequestUser,
) -> anyhow::Result<String> {
self.get_server_check_permissions(&server_id, &user, PermissionLevel::Read)
let _: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Read)
.await?;
let status = self
.server_status_cache
@@ -256,7 +250,8 @@ impl ResolveToString<GetSystemProcesses, RequestUser> for State {
GetSystemProcesses { server_id }: GetSystemProcesses,
user: RequestUser,
) -> anyhow::Result<String> {
self.get_server_check_permissions(&server_id, &user, PermissionLevel::Read)
let _: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Read)
.await?;
let status = self
.server_status_cache
@@ -279,7 +274,8 @@ impl ResolveToString<GetSystemComponents, RequestUser> for State {
GetSystemComponents { server_id }: GetSystemComponents,
user: RequestUser,
) -> anyhow::Result<String> {
self.get_server_check_permissions(&server_id, &user, PermissionLevel::Read)
let _: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Read)
.await?;
let status = self
.server_status_cache
@@ -302,8 +298,8 @@ impl Resolve<GetDockerImages, RequestUser> for State {
GetDockerImages { server_id }: GetDockerImages,
user: RequestUser,
) -> anyhow::Result<Vec<ImageSummary>> {
let server = self
.get_server_check_permissions(&server_id, &user, PermissionLevel::Read)
let server: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Read)
.await?;
self.periphery_client(&server)
.request(requests::GetImageList {})
@@ -318,8 +314,8 @@ impl Resolve<GetDockerNetworks, RequestUser> for State {
GetDockerNetworks { server_id }: GetDockerNetworks,
user: RequestUser,
) -> anyhow::Result<Vec<DockerNetwork>> {
let server = self
.get_server_check_permissions(&server_id, &user, PermissionLevel::Read)
let server: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Read)
.await?;
self.periphery_client(&server)
.request(requests::GetNetworkList {})
@@ -334,8 +330,8 @@ impl Resolve<GetDockerContainers, RequestUser> for State {
GetDockerContainers { server_id }: GetDockerContainers,
user: RequestUser,
) -> anyhow::Result<Vec<ContainerSummary>> {
let server = self
.get_server_check_permissions(&server_id, &user, PermissionLevel::Read)
let server: Server = self
.get_resource_check_permissions(&server_id, &user, PermissionLevel::Read)
.await?;
self.periphery_client(&server)
.request(requests::GetContainerList {})
@@ -350,20 +346,7 @@ impl Resolve<GetServersSummary, RequestUser> for State {
GetServersSummary {}: GetServersSummary,
user: RequestUser,
) -> anyhow::Result<GetServersSummaryResponse> {
let query = if user.is_admin {
None
} else {
let query = doc! {
format!("permissions.{}", user.id): { "$in": ["read", "execute", "update"] }
};
Some(query)
};
let servers = self
.db
.servers
.get_some(query, None)
.await
.context("failed to get servers from db")?;
let servers: Vec<Server> = self.list_resources_for_user(&user, None).await?;
let mut res = GetServersSummaryResponse::default();
for server in servers {
res.total += 1;

View File

@@ -1,9 +1,9 @@
use async_trait::async_trait;
use monitor_types::{entities::update::Update, requests::read::ListUpdates};
use monitor_types::{entities::{update::Update, build::Build}, requests::read::ListUpdates};
use mungos::mongodb::{bson::doc, options::FindOptions};
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, state::State, resource::Resource};
#[async_trait]
impl Resolve<ListUpdates, RequestUser> for State {
@@ -23,7 +23,7 @@ impl Resolve<ListUpdates, RequestUser> for State {
.await?;
Ok(updates)
} else {
let build_ids = self.get_build_ids_for_non_admin(&user.id).await?;
let build_ids = <State as Resource<Build>>::get_resource_ids_for_non_admin(self, &user.id).await?;
todo!()
}
}

View File

@@ -8,7 +8,7 @@ use monitor_types::{
use mungos::mongodb::bson::{doc, to_bson};
use resolver_api::Resolve;
use crate::{auth::RequestUser, helpers::make_update, state::State};
use crate::{auth::RequestUser, helpers::make_update, state::State, resource::Resource};
#[async_trait]
impl Resolve<CreateAlerter, RequestUser> for State {
@@ -37,7 +37,7 @@ impl Resolve<CreateAlerter, RequestUser> for State {
.create_one(alerter)
.await
.context("failed to add alerter to db")?;
let alerter = self.get_alerter(&alerter_id).await?;
let alerter: Alerter = self.get_resource(&alerter_id).await?;
let mut update = make_update(&alerter, Operation::CreateAlerter, &user);
@@ -70,7 +70,7 @@ impl Resolve<CopyAlerter, RequestUser> for State {
description,
..
} = self
.get_alerter_check_permissions(&id, &user, PermissionLevel::Update)
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let start_ts = monitor_timestamp();
let alerter = Alerter {
@@ -91,7 +91,7 @@ impl Resolve<CopyAlerter, RequestUser> for State {
.create_one(alerter)
.await
.context("failed to add alerter to db")?;
let alerter = self.get_alerter(&alerter_id).await?;
let alerter: Alerter = self.get_resource(&alerter_id).await?;
let mut update = make_update(&alerter, Operation::CreateAlerter, &user);
@@ -120,8 +120,8 @@ impl Resolve<DeleteAlerter, RequestUser> for State {
DeleteAlerter { id }: DeleteAlerter,
user: RequestUser,
) -> anyhow::Result<Alerter> {
let alerter = self
.get_alerter_check_permissions(&id, &user, PermissionLevel::Update)
let alerter: Alerter = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let mut update = make_update(
@@ -156,8 +156,8 @@ impl Resolve<UpdateAlerter, RequestUser> for State {
UpdateAlerter { id, config }: UpdateAlerter,
user: RequestUser,
) -> anyhow::Result<Alerter> {
let alerter = self
.get_alerter_check_permissions(&id, &user, PermissionLevel::Update)
let alerter: Alerter = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let mut update = make_update(&alerter, Operation::UpdateAlerter, &user);
@@ -174,7 +174,7 @@ impl Resolve<UpdateAlerter, RequestUser> for State {
)
.await?;
let alerter = self.get_alerter(&id).await?;
let alerter: Alerter = self.get_resource(&id).await?;
update.finalize();
self.add_update(update).await?;

View File

@@ -4,7 +4,7 @@ use monitor_types::{
entities::{
build::{Build, BuildBuilderConfig},
update::{Log, UpdateStatus},
Operation, PermissionLevel,
Operation, PermissionLevel, server::Server, builder::Builder,
},
monitor_timestamp,
requests::write::*,
@@ -15,7 +15,7 @@ use resolver_api::Resolve;
use crate::{
auth::RequestUser,
helpers::{empty_or_only_spaces, make_update},
state::State,
state::State, resource::Resource,
};
#[async_trait]
@@ -28,7 +28,7 @@ impl Resolve<CreateBuild, RequestUser> for State {
if let Some(builder) = &config.builder {
match builder {
BuildBuilderConfig::Server { server_id } => {
self.get_server_check_permissions(
let _: Server = self.get_resource_check_permissions(
server_id,
&user,
PermissionLevel::Update,
@@ -37,7 +37,7 @@ impl Resolve<CreateBuild, RequestUser> for State {
.context("cannot create build on this server. user must have update permissions on the server.")?;
}
BuildBuilderConfig::Builder { builder_id } => {
self.get_builder_check_permissions(
let _: Builder = self.get_resource_check_permissions(
builder_id,
&user,
PermissionLevel::Read,
@@ -66,7 +66,7 @@ impl Resolve<CreateBuild, RequestUser> for State {
.create_one(build)
.await
.context("failed to add build to db")?;
let build = self.get_build(&build_id).await?;
let build: Build = self.get_resource(&build_id).await?;
let mut update = make_update(&build, Operation::CreateBuild, &user);
@@ -98,11 +98,11 @@ impl Resolve<CopyBuild, RequestUser> for State {
tags,
..
} = self
.get_build_check_permissions(&id, &user, PermissionLevel::Update)
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
match &config.builder {
BuildBuilderConfig::Server { server_id } => {
self.get_server_check_permissions(
let _: Server = self.get_resource_check_permissions(
server_id,
&user,
PermissionLevel::Update,
@@ -111,7 +111,7 @@ impl Resolve<CopyBuild, RequestUser> for State {
.context("cannot create build on this server. user must have update permissions on the server.")?;
}
BuildBuilderConfig::Builder { builder_id } => {
self.get_builder_check_permissions(
let _: Builder = self.get_resource_check_permissions(
builder_id,
&user,
PermissionLevel::Read,
@@ -139,7 +139,7 @@ impl Resolve<CopyBuild, RequestUser> for State {
.create_one(build)
.await
.context("failed to add build to db")?;
let build = self.get_build(&build_id).await?;
let build: Build = self.get_resource(&build_id).await?;
let mut update = make_update(&build, Operation::CreateBuild, &user);
@@ -168,8 +168,8 @@ impl Resolve<DeleteBuild, RequestUser> for State {
return Err(anyhow!("build busy"));
}
let build = self
.get_build_check_permissions(&id, &user, PermissionLevel::Update)
let build: Build = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let mut update = make_update(&build, Operation::DeleteBuild, &user);
@@ -207,15 +207,15 @@ impl Resolve<UpdateBuild, RequestUser> for State {
return Err(anyhow!("build busy"));
}
let build = self
.get_build_check_permissions(&id, &user, PermissionLevel::Update)
let build: Build = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let inner = || async move {
if let Some(builder) = &config.builder {
match builder {
BuildBuilderConfig::Server { server_id } => {
self.get_server_check_permissions(
let _: Server = self.get_resource_check_permissions(
server_id,
&user,
PermissionLevel::Update,
@@ -224,7 +224,7 @@ impl Resolve<UpdateBuild, RequestUser> for State {
.context("cannot create build on this server. user must have update permissions on the server.")?;
}
BuildBuilderConfig::Builder { builder_id } => {
self.get_builder_check_permissions(
let _: Builder = self.get_resource_check_permissions(
builder_id,
&user,
PermissionLevel::Read,
@@ -261,7 +261,7 @@ impl Resolve<UpdateBuild, RequestUser> for State {
self.add_update(update).await?;
let build = self.get_build(&build.id).await?;
let build: Build = self.get_resource(&build.id).await?;
anyhow::Ok(build)
};

View File

@@ -12,7 +12,7 @@ use monitor_types::{
use mungos::mongodb::bson::{doc, to_bson};
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, state::State, resource::Resource};
#[async_trait]
impl Resolve<CreateBuilder, RequestUser> for State {
@@ -39,7 +39,7 @@ impl Resolve<CreateBuilder, RequestUser> for State {
.create_one(builder)
.await
.context("failed to add builder to db")?;
let builder = self.get_builder(&builder_id).await?;
let builder: Builder = self.get_resource(&builder_id).await?;
let update = Update {
target: ResourceTarget::Builder(builder_id),
operation: Operation::CreateBuilder,
@@ -78,7 +78,7 @@ impl Resolve<CopyBuilder, RequestUser> for State {
description,
..
} = self
.get_builder_check_permissions(&id, &user, PermissionLevel::Update)
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let start_ts = monitor_timestamp();
let builder = Builder {
@@ -98,7 +98,7 @@ impl Resolve<CopyBuilder, RequestUser> for State {
.create_one(builder)
.await
.context("failed to add builder to db")?;
let builder = self.get_builder(&builder_id).await?;
let builder: Builder = self.get_resource(&builder_id).await?;
let update = Update {
target: ResourceTarget::Builder(builder_id),
operation: Operation::CreateBuilder,
@@ -132,8 +132,8 @@ impl Resolve<DeleteBuilder, RequestUser> for State {
DeleteBuilder { id }: DeleteBuilder,
user: RequestUser,
) -> anyhow::Result<Builder> {
let builder = self
.get_builder_check_permissions(&id, &user, PermissionLevel::Update)
let builder: Builder = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let start_ts = monitor_timestamp();
@@ -178,8 +178,8 @@ impl Resolve<UpdateBuilder, RequestUser> for State {
UpdateBuilder { id, config }: UpdateBuilder,
user: RequestUser,
) -> anyhow::Result<Builder> {
let builder = self
.get_builder_check_permissions(&id, &user, PermissionLevel::Update)
let builder: Builder = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let mut update = Update {
@@ -204,7 +204,7 @@ impl Resolve<UpdateBuilder, RequestUser> for State {
)
.await?;
let builder = self.get_builder(&id).await?;
let builder: Builder = self.get_resource(&id).await?;
update.finalize();
self.add_update(update).await?;

View File

@@ -3,7 +3,9 @@ use async_trait::async_trait;
use monitor_types::{
all_logs_success,
entities::{
build::Build,
deployment::{Deployment, DeploymentImage, DockerContainerState},
server::Server,
update::{Log, ResourceTarget, Update, UpdateStatus},
Operation, PermissionLevel,
},
@@ -15,7 +17,7 @@ use mungos::mongodb::bson::{doc, to_bson};
use periphery_client::requests;
use resolver_api::Resolve;
use crate::{auth::RequestUser, helpers::empty_or_only_spaces, state::State};
use crate::{auth::RequestUser, helpers::empty_or_only_spaces, resource::Resource, state::State};
#[async_trait]
impl Resolve<CreateDeployment, RequestUser> for State {
@@ -26,14 +28,14 @@ impl Resolve<CreateDeployment, RequestUser> for State {
) -> anyhow::Result<Deployment> {
if let Some(server_id) = &config.server_id {
if !server_id.is_empty() {
self.get_server_check_permissions(server_id, &user, PermissionLevel::Update)
let _: Server = self.get_resource_check_permissions(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.")?;
}
}
if let Some(DeploymentImage::Build { build_id, .. }) = &config.image {
if !build_id.is_empty() {
self.get_build_check_permissions(build_id, &user, PermissionLevel::Read)
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.")?;
}
@@ -56,7 +58,7 @@ impl Resolve<CreateDeployment, RequestUser> for State {
.create_one(&deployment)
.await
.context("failed to add deployment to db")?;
let deployment = self.get_deployment(&deployment_id).await?;
let deployment: Deployment = self.get_resource(&deployment_id).await?;
let update = Update {
target: ResourceTarget::Deployment(deployment_id),
operation: Operation::CreateDeployment,
@@ -96,16 +98,16 @@ impl Resolve<CopyDeployment, RequestUser> for State {
tags,
..
} = self
.get_deployment_check_permissions(&id, &user, PermissionLevel::Update)
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
if !config.server_id.is_empty() {
self.get_server_check_permissions(&config.server_id, &user, PermissionLevel::Update)
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.")?;
}
if let DeploymentImage::Build { build_id, .. } = &config.image {
if !build_id.is_empty() {
self.get_build_check_permissions(build_id, &user, PermissionLevel::Read)
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.")?;
}
@@ -128,7 +130,7 @@ impl Resolve<CopyDeployment, RequestUser> for State {
.create_one(&deployment)
.await
.context("failed to add deployment to db")?;
let deployment = self.get_deployment(&deployment_id).await?;
let deployment: Deployment = self.get_resource(&deployment_id).await?;
let update = Update {
target: ResourceTarget::Deployment(deployment_id),
operation: Operation::CreateDeployment,
@@ -166,8 +168,8 @@ impl Resolve<DeleteDeployment, RequestUser> for State {
return Err(anyhow!("deployment busy"));
}
let deployment = self
.get_deployment_check_permissions(&id, &user, PermissionLevel::Update)
let deployment: Deployment = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let inner = || async move {
@@ -195,7 +197,8 @@ impl Resolve<DeleteDeployment, RequestUser> for State {
DockerContainerState::NotDeployed | DockerContainerState::Unknown
) {
// container needs to be destroyed
let server = self.get_server(&deployment.config.server_id).await;
let server: anyhow::Result<Server> =
self.get_resource(&deployment.config.server_id).await;
if let Err(e) = server {
update.logs.push(Log::error(
"remove container",
@@ -283,20 +286,20 @@ impl Resolve<UpdateDeployment, RequestUser> for State {
return Err(anyhow!("deployment busy"));
}
let deployment = self
.get_deployment_check_permissions(&id, &user, PermissionLevel::Update)
let deployment: Deployment = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let inner = || async move {
let start_ts = monitor_timestamp();
if let Some(server_id) = &config.server_id {
self.get_server_check_permissions(server_id, &user, PermissionLevel::Update)
let _: Server = self.get_resource_check_permissions(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.")?;
}
if let Some(DeploymentImage::Build { build_id, .. }) = &config.image {
self.get_build_check_permissions(build_id, &user, PermissionLevel::Read)
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.")?;
}
@@ -346,7 +349,7 @@ impl Resolve<UpdateDeployment, RequestUser> for State {
self.add_update(update).await?;
let deployment = self.get_deployment(&id).await?;
let deployment: Deployment = self.get_resource(&id).await?;
anyhow::Ok(deployment)
};
@@ -382,8 +385,8 @@ impl Resolve<RenameDeployment, RequestUser> for State {
return Err(anyhow!("deployment busy"));
}
let deployment = self
.get_deployment_check_permissions(&id, &user, PermissionLevel::Update)
let deployment: Deployment = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let inner = || async {
@@ -401,7 +404,7 @@ impl Resolve<RenameDeployment, RequestUser> for State {
}
if container_state != DockerContainerState::NotDeployed {
let server = self.get_server(&deployment.config.server_id).await?;
let server: Server = self.get_resource(&deployment.config.server_id).await?;
match self
.periphery_client(&server)
.request(requests::RenameContainer {

View File

@@ -4,7 +4,7 @@ use monitor_types::{
entities::{
repo::Repo,
update::{Log, ResourceTarget, Update, UpdateStatus},
Operation, PermissionLevel,
Operation, PermissionLevel, server::Server,
},
monitor_timestamp,
requests::{execute, write::*},
@@ -13,7 +13,7 @@ use mungos::mongodb::bson::{doc, to_bson};
use periphery_client::requests;
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, state::State, resource::Resource};
#[async_trait]
impl Resolve<CreateRepo, RequestUser> for State {
@@ -24,7 +24,7 @@ impl Resolve<CreateRepo, RequestUser> for State {
) -> anyhow::Result<Repo> {
if let Some(server_id) = &config.server_id {
if !server_id.is_empty() {
self.get_server_check_permissions(
let _: Server = self.get_resource_check_permissions(
server_id,
&user,
PermissionLevel::Update,
@@ -53,7 +53,7 @@ impl Resolve<CreateRepo, RequestUser> for State {
.await
.context("failed to add repo to db")?;
let repo = self.get_repo(&repo_id).await?;
let repo: Repo = self.get_resource(&repo_id).await?;
let update = Update {
target: ResourceTarget::Repo(repo_id),
@@ -102,10 +102,10 @@ impl Resolve<CopyRepo, RequestUser> for State {
tags,
..
} = self
.get_repo_check_permissions(&id, &user, PermissionLevel::Update)
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
if !config.server_id.is_empty() {
self.get_server_check_permissions(
let _: Server = self.get_resource_check_permissions(
&config.server_id,
&user,
PermissionLevel::Update,
@@ -132,7 +132,7 @@ impl Resolve<CopyRepo, RequestUser> for State {
.create_one(repo)
.await
.context("failed to add repo to db")?;
let repo = self.get_repo(&repo_id).await?;
let repo: Repo = self.get_resource(&repo_id).await?;
let update = Update {
target: ResourceTarget::Repo(repo_id),
operation: Operation::CreateRepo,
@@ -163,8 +163,8 @@ impl Resolve<DeleteRepo, RequestUser> for State {
DeleteRepo { id }: DeleteRepo,
user: RequestUser,
) -> anyhow::Result<Repo> {
let repo = self
.get_repo_check_permissions(&id, &user, PermissionLevel::Update)
let repo: Repo = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let inner = || async move {
@@ -194,7 +194,7 @@ impl Resolve<DeleteRepo, RequestUser> for State {
update.logs.push(log);
if !repo.config.server_id.is_empty() {
let server = self.get_server(&repo.config.server_id).await;
let server: anyhow::Result<Server> = self.get_resource(&repo.config.server_id).await;
if let Ok(server) = server {
match self
.periphery_client(&server)
@@ -248,8 +248,8 @@ impl Resolve<UpdateRepo, RequestUser> for State {
UpdateRepo { id, config }: UpdateRepo,
user: RequestUser,
) -> anyhow::Result<Repo> {
let repo = self
.get_repo_check_permissions(&id, &user, PermissionLevel::Update)
let repo: Repo = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let inner = || async move {
@@ -257,7 +257,7 @@ impl Resolve<UpdateRepo, RequestUser> for State {
if let Some(server_id) = &config.server_id {
if !server_id.is_empty() {
self.get_server_check_permissions(
let _: Server = self.get_resource_check_permissions(
server_id,
&user,
PermissionLevel::Update,
@@ -297,7 +297,7 @@ impl Resolve<UpdateRepo, RequestUser> for State {
if new_server_id != repo.config.server_id {
if !repo.config.server_id.is_empty() {
// clean up old server
let old_server = self.get_server(&repo.config.server_id).await?;
let old_server: Server = self.get_resource(&repo.config.server_id).await?;
let res = self
.periphery_client(&old_server)
.request(requests::DeleteRepo { name: repo.name })
@@ -323,7 +323,7 @@ impl Resolve<UpdateRepo, RequestUser> for State {
}
}
let repo = self.get_repo(&repo.id).await?;
let repo: Repo = self.get_resource(&repo.id).await?;
anyhow::Ok(repo)
};

View File

@@ -12,7 +12,7 @@ use monitor_types::{
use mungos::mongodb::bson::{doc, to_bson};
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, state::State, resource::Resource};
#[async_trait]
impl Resolve<CreateServer, RequestUser> for State {
@@ -38,7 +38,7 @@ impl Resolve<CreateServer, RequestUser> for State {
.create_one(&server)
.await
.context("failed to add server to db")?;
let server = self.get_server(&server_id).await?;
let server: Server = self.get_resource(&server_id).await?;
let update = Update {
target: ResourceTarget::Server(server_id),
operation: Operation::CreateServer,
@@ -75,8 +75,8 @@ impl Resolve<DeleteServer, RequestUser> for State {
return Err(anyhow!("server busy"));
}
let server = self
.get_server_check_permissions(&id, &user, PermissionLevel::Update)
let server: Server = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
let start_ts = monitor_timestamp();
@@ -146,7 +146,7 @@ impl Resolve<UpdateServer, RequestUser> for State {
return Err(anyhow!("server busy"));
}
let start_ts = monitor_timestamp();
self.get_server_check_permissions(&id, &user, PermissionLevel::Update)
let _: Server = self.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
self.db
.servers
@@ -171,7 +171,7 @@ impl Resolve<UpdateServer, RequestUser> for State {
..Default::default()
};
let new_server = self.get_server(&id).await?;
let new_server: Server = self.get_resource(&id).await?;
self.update_cache_for_server(&new_server, 0).await;
@@ -189,8 +189,8 @@ impl Resolve<RenameServer, RequestUser> for State {
user: RequestUser,
) -> anyhow::Result<Update> {
let start_ts = monitor_timestamp();
let server = self
.get_server_check_permissions(&id, &user, PermissionLevel::Update)
let server: Server = self
.get_resource_check_permissions(&id, &user, PermissionLevel::Update)
.await?;
self.db
.updates

170
bin/core/src/resource.rs Normal file
View File

@@ -0,0 +1,170 @@
use anyhow::{anyhow, Context};
use async_trait::async_trait;
use monitor_types::{
entities::{
alerter::Alerter, build::Build, builder::Builder, deployment::Deployment, repo::Repo,
server::Server, PermissionLevel,
},
permissioned::Permissioned,
};
use mungos::{
mongodb::bson::{doc, Document},
AggStage::*,
Collection, Indexed,
};
use crate::{auth::RequestUser, state::State};
#[async_trait]
pub trait Resource<T: Indexed + Send + Unpin + Permissioned> {
fn name() -> &'static str;
fn coll(&self) -> &Collection<T>;
async fn get_resource(&self, id: &str) -> anyhow::Result<T> {
self.coll()
.find_one_by_id(id)
.await
.context(format!("failed to get {} from db", Self::name()))?
.context(format!("did not find any {} with id {id}", Self::name()))
}
async fn get_resource_check_permissions(
&self,
id: &str,
user: &RequestUser,
permission_level: PermissionLevel,
) -> anyhow::Result<T> {
let resource = self.get_resource(id).await?;
let permissions = resource.get_user_permissions(&user.id);
if user.is_admin || permissions >= permission_level {
Ok(resource)
} else {
Err(anyhow!(
"user does not have required permissions on this {}",
Self::name()
))
}
}
async fn get_user_permission_on_resource(
&self,
user_id: &str,
resource_id: &str,
) -> anyhow::Result<PermissionLevel> {
let resource = self.get_resource(resource_id).await?;
Ok(resource.get_user_permissions(user_id))
}
async fn get_resource_ids_for_non_admin(&self, user_id: &str) -> anyhow::Result<Vec<String>> {
self.coll()
.aggregate_collect(
[
Match(doc! {
format!("permissions.{}", user_id): { "$in": ["update", "execute", "read"] }
}),
Project(doc! { "_id": 1 }),
],
None,
)
.await
.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<_>>>()
.context(format!(
"failed to get {} ids for non admin | extract id from document",
Self::name()
))
}
async fn list_resources_for_user(
&self,
user: &RequestUser,
query: Option<Document>,
) -> anyhow::Result<Vec<T>> {
let mut query = query.unwrap_or_default();
if !user.is_admin {
query.insert(
format!("permissions.{}", user.id),
doc! { "$in": ["read", "execute", "update"] },
);
}
self.coll()
.get_some(query, None)
.await
.context(format!("failed to pull {}s from mongo", Self::name()))
}
}
impl Resource<Server> for State {
fn name() -> &'static str {
"server"
}
fn coll(&self) -> &Collection<Server> {
&self.db.servers
}
}
impl Resource<Deployment> for State {
fn name() -> &'static str {
"deployment"
}
fn coll(&self) -> &Collection<Deployment> {
&self.db.deployments
}
}
impl Resource<Build> for State {
fn name() -> &'static str {
"build"
}
fn coll(&self) -> &Collection<Build> {
&self.db.builds
}
}
impl Resource<Builder> for State {
fn name() -> &'static str {
"builder"
}
fn coll(&self) -> &Collection<Builder> {
&self.db.builders
}
}
impl Resource<Repo> for State {
fn name() -> &'static str {
"repo"
}
fn coll(&self) -> &Collection<Repo> {
&self.db.repos
}
}
impl Resource<Alerter> for State {
fn name() -> &'static str {
"alerter"
}
fn coll(&self) -> &Collection<Alerter> {
&self.db.alerters
}
}

View File

@@ -9,10 +9,19 @@ use axum::{
Router,
};
use futures::{SinkExt, StreamExt};
use monitor_types::entities::{
update::{ResourceTarget, ResourceTargetVariant},
user::User,
PermissionLevel,
use monitor_types::{
entities::{
alerter::Alerter,
build::Build,
builder::Builder,
deployment::Deployment,
repo::Repo,
server::Server,
update::{ResourceTarget, ResourceTargetVariant},
user::User,
PermissionLevel,
},
permissioned::Permissioned,
};
use serde_json::json;
use tokio::select;
@@ -20,6 +29,7 @@ use tokio_util::sync::CancellationToken;
use crate::{
auth::RequestUser,
resource::Resource,
state::{State, StateExtension},
};
@@ -143,36 +153,46 @@ impl State {
}
let (permissions, target) = match update_target {
ResourceTarget::Server(server_id) => {
let permissions = self
.get_user_permission_on_server(user_id, server_id)
.await?;
(permissions, ResourceTargetVariant::Server)
let resource: Server = self.get_resource(server_id).await?;
(
resource.get_user_permissions(user_id),
ResourceTargetVariant::Server,
)
}
ResourceTarget::Deployment(deployment_id) => {
let permissions = self
.get_user_permission_on_deployment(user_id, deployment_id)
.await?;
(permissions, ResourceTargetVariant::Deployment)
let resource: Deployment = self.get_resource(deployment_id).await?;
(
resource.get_user_permissions(user_id),
ResourceTargetVariant::Deployment,
)
}
ResourceTarget::Build(build_id) => {
let permissions = self.get_user_permission_on_build(user_id, build_id).await?;
(permissions, ResourceTargetVariant::Build)
let resource: Build = self.get_resource(build_id).await?;
(
resource.get_user_permissions(user_id),
ResourceTargetVariant::Build,
)
}
ResourceTarget::Builder(builder_id) => {
let permissions = self
.get_user_permission_on_builder(user_id, builder_id)
.await?;
(permissions, ResourceTargetVariant::Builder)
let resource: Builder = self.get_resource(builder_id).await?;
(
resource.get_user_permissions(user_id),
ResourceTargetVariant::Builder,
)
}
ResourceTarget::Repo(repo_id) => {
let permissions = self.get_user_permission_on_repo(user_id, repo_id).await?;
(permissions, ResourceTargetVariant::Repo)
let resource: Repo = self.get_resource(repo_id).await?;
(
resource.get_user_permissions(user_id),
ResourceTargetVariant::Repo,
)
}
ResourceTarget::Alerter(alerter_id) => {
let permissions = self
.get_user_permission_on_alerter(user_id, alerter_id)
.await?;
(permissions, ResourceTargetVariant::Alerter)
let resource: Alerter = self.get_resource(alerter_id).await?;
(
resource.get_user_permissions(user_id),
ResourceTargetVariant::Alerter,
)
}
ResourceTarget::System => {
return Err(anyhow!("user not admin, can't recieve system updates"))

View File

@@ -0,0 +1,36 @@
use resolver_api::derive::Request;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::{entities::alerter::Alerter, MongoDocument};
//
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Request)]
#[response(Alerter)]
pub struct GetAlerter {
pub id: String,
}
//
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Request)]
#[response(Vec<Alerter>)]
pub struct ListAlerters {
pub query: Option<MongoDocument>,
}
//
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Request)]
#[response(GetAlertersSummaryResponse)]
pub struct GetAlertersSummary {}
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GetAlertersSummaryResponse {
pub total: u32,
}