Skip to content

Commit

Permalink
Delete account grafana (#222)
Browse files Browse the repository at this point in the history
* delete_Account_grafana

* delete_Account_grafana

* delete_Account_grafana

* create/delete user in grafana

* add_member_to grafana team
  • Loading branch information
dzlk17 authored Nov 22, 2024
1 parent 21eff50 commit fd2a875
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 58 deletions.
28 changes: 26 additions & 2 deletions database/src/tables/domain_verifications/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ impl Db {
domain_name: &String,
app_id: &String,
) -> Result<(), DbError> {

let query_body = format!(
"UPDATE {DOMAIN_VERIFICATIONS_TABLE_NAME} SET deleted_at = $1 WHERE domain_name = $2 AND app_id = $3 AND deleted_at IS NULL AND finished_at IS NOT NULL AND cancelled_at IS NULL"
);
Expand All @@ -104,7 +103,6 @@ impl Db {
tx: &mut sqlx::Transaction<'_, sqlx::Postgres>,
app_id: &String,
) -> Result<(), DbError> {

let query_body = format!(
"UPDATE {DOMAIN_VERIFICATIONS_TABLE_NAME} SET deleted_at = $1 WHERE app_id = $2 AND deleted_at IS NULL"
);
Expand All @@ -120,7 +118,33 @@ impl Db {
Err(e) => Err(e).map_err(|e| e.into()),
}
}
pub async fn delete_domain_verifications_for_inactive_apps(
&self,
tx: &mut sqlx::Transaction<'_, sqlx::Postgres>,
app_ids: &Vec<String>,
) -> Result<(), DbError> {
if app_ids.is_empty() {
return Ok(());
}

let query_body = format!(
"UPDATE {DOMAIN_VERIFICATIONS_TABLE_NAME}
SET deleted_at = $1
WHERE app_id = ANY($2)
AND deleted_at IS NULL"
);

let query_result = sqlx::query(&query_body)
.bind(&get_current_datetime())
.bind(&app_ids)
.execute(&mut **tx)
.await;

match query_result {
Ok(_) => Ok(()),
Err(e) => Err(e).map_err(|e| e.into()),
}
}
}

#[cfg(feature = "cloud_integration_tests")]
Expand Down
93 changes: 84 additions & 9 deletions server/src/http/cloud/delete_account_finish.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{
env::is_env_production,
http::cloud::utils::{check_auth_code, validate_request},
middlewares::auth_middleware::UserId,
structs::{
Expand All @@ -10,10 +11,13 @@ use axum::{extract::State, http::StatusCode, Extension, Json};
use database::db::Db;
use garde::Validate;
use log::error;
use openapi::apis::configuration::Configuration;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use ts_rs::TS;

use super::grafana_utils::delete_user_account::handle_grafana_delete_user_account;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TS, Validate)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
Expand All @@ -25,6 +29,7 @@ pub struct HttpDeleteAccountFinishRequest {
pub async fn delete_account_finish(
State(db): State<Arc<Db>>,
State(sessions_cache): State<Arc<ApiSessionsCache>>,
State(grafana_conf): State<Arc<Configuration>>,
Extension(user_id): Extension<UserId>,
Json(request): Json<HttpDeleteAccountFinishRequest>,
) -> Result<Json<()>, (StatusCode, String)> {
Expand Down Expand Up @@ -84,19 +89,89 @@ pub async fn delete_account_finish(
CloudApiErrors::DatabaseError.to_string(),
)
})?;


let mut owned_team_grafana_ids = Vec::new();
let mut non_owned_team_grafana_ids = Vec::new();
let mut app_ids: Vec<String> = Vec::new();

let teams = match db
.get_joined_teams_by_user_id(&user_id)
.await
.map_err(|err| {
error!("Failed to get user teams: {:?}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
CloudApiErrors::DatabaseError.to_string(),
)
}) {
Ok(joined_teams) => joined_teams,
Err(err) => {
error!("Failed to get user teams: {:?}", err);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
CloudApiErrors::DatabaseError.to_string(),
));
}
};

for (team, _, _, registered_apps) in teams {
if team.team_admin_id == user_id {
if let Some(grafana_id) = team.grafana_id {
owned_team_grafana_ids.push(grafana_id);
}
for (app, _) in registered_apps {
if app.team_id == team.team_id {
app_ids.push(app.app_id.clone());
}
}
} else {
if let Some(grafana_id) = team.clone().grafana_id {
non_owned_team_grafana_ids.push(grafana_id)
}
}
}
// Grafana, delete teams, apps and user
if is_env_production() {
if let Err(err) = handle_grafana_delete_user_account(
&grafana_conf,
&owned_team_grafana_ids,
&non_owned_team_grafana_ids,
&user.email,
)
.await
{
error!("Failed to delete account in grafana: {:?}", err);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
CloudApiErrors::GrafanaError.to_string(),
));
};
}

// Delete all invites connected to user
if let Err(err) = db
.cancel_all_team_invites_containing_email(&mut tx, &user.email, &user_id)
.await
.cancel_all_team_invites_containing_email(&mut tx, &user.email, &user_id)
.await
{
error!("Failed to delete team invites: {:?}", err);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
CloudApiErrors::DatabaseError.to_string(),
));
}


// Delete all verified domains
if let Err(err) = db
.delete_domain_verifications_for_inactive_apps(&mut tx, &app_ids)
.await
{
error!("Failed to delete domains: {:?}", err);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
CloudApiErrors::DatabaseError.to_string(),
));
}

// Delete all user apps
if let Err(err) = db.deactivate_user_apps(&mut tx, &user_id).await {
error!("Failed to delete user apps: {:?}", err);
Expand All @@ -105,7 +180,7 @@ pub async fn delete_account_finish(
CloudApiErrors::DatabaseError.to_string(),
));
}

// Leave all teams
if let Err(err) = db.remove_inactive_user_from_teams(&mut tx, &user_id).await {
error!("Failed to leave teams: {:?}", err);
Expand All @@ -117,9 +192,9 @@ pub async fn delete_account_finish(

// delete privileges
if let Err(err) = db
.remove_privileges_for_inactive_teams(&mut tx, &user_id)
.remove_privileges_for_inactive_teams(&mut tx, &user_id)
.await
{
{
error!("Failed to leave teams: {:?}", err);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Expand All @@ -135,7 +210,7 @@ pub async fn delete_account_finish(
CloudApiErrors::DatabaseError.to_string(),
));
}

// Deactivate the user
if let Err(err) = db.deactivate_user(&user_id, &mut tx).await {
error!("Failed to delete user: {:?}", err);
Expand All @@ -144,7 +219,7 @@ pub async fn delete_account_finish(
CloudApiErrors::DatabaseError.to_string(),
));
}

// Commit transaction
tx.commit().await.map_err(|err| {
error!("Failed to commit transaction: {:?}", err);
Expand Down
16 changes: 8 additions & 8 deletions server/src/http/cloud/delete_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,22 @@ pub async fn delete_app(
// Start a transaction
let mut tx = db.connection_pool.begin().await.unwrap();

if let Err(err) = db.deactivate_app(&mut tx, &request.app_id).await {
if let Err(err) = db
.remove_privileges_for_inactive_app_within_tx(&mut tx, &request.app_id)
.await
{
let _ = tx
.rollback()
.await
.map_err(|err| error!("Failed to rollback transaction: {:?}", err));
error!("Failed to deactivate app: {:?}", err);
error!("Failed to delete app: {:?}", err);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
CloudApiErrors::DatabaseError.to_string(),
));
}
if let Err(err) = db
.remove_privileges_for_inactive_app_within_tx(&mut tx, &request.app_id)
.delete_domain_verification_for_inactive_app(&mut tx, &request.app_id)
.await
{
let _ = tx
Expand All @@ -97,15 +100,12 @@ pub async fn delete_app(
CloudApiErrors::DatabaseError.to_string(),
));
}
if let Err(err) = db
.delete_domain_verification_for_inactive_app(&mut tx, &request.app_id)
.await
{
if let Err(err) = db.deactivate_app(&mut tx, &request.app_id).await {
let _ = tx
.rollback()
.await
.map_err(|err| error!("Failed to rollback transaction: {:?}", err));
error!("Failed to delete app: {:?}", err);
error!("Failed to deactivate app: {:?}", err);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
CloudApiErrors::DatabaseError.to_string(),
Expand Down
14 changes: 7 additions & 7 deletions server/src/http/cloud/delete_team.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,15 @@ pub async fn delete_team(

// Delete team apps, privileges and domain verifications
for app in registered_apps.iter() {
if let Err(err) = db.deactivate_app(&mut tx, &app.app_id).await {
if let Err(err) = db
.delete_domain_verification_for_inactive_app(&mut tx, &app.app_id)
.await
{
let _ = tx
.rollback()
.await
.map_err(|err| error!("Failed to rollback transaction: {:?}", err));
error!("Failed to deactivate app: {:?}", err);
error!("Failed to create app: {:?}", err);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
CloudApiErrors::DatabaseError.to_string(),
Expand All @@ -134,15 +137,12 @@ pub async fn delete_team(
CloudApiErrors::DatabaseError.to_string(),
));
}
if let Err(err) = db
.delete_domain_verification_for_inactive_app(&mut tx, &app.app_id)
.await
{
if let Err(err) = db.deactivate_app(&mut tx, &app.app_id).await {
let _ = tx
.rollback()
.await
.map_err(|err| error!("Failed to rollback transaction: {:?}", err));
error!("Failed to create app: {:?}", err);
error!("Failed to deactivate app: {:?}", err);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
CloudApiErrors::DatabaseError.to_string(),
Expand Down
36 changes: 7 additions & 29 deletions server/src/http/cloud/grafana_utils/add_user_to_team.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ use axum::http::StatusCode;
use log::{error, warn};
use openapi::{
apis::{
admin_users_api::admin_create_user,
configuration::Configuration,
teams_api::add_team_member,
users_api::{get_user_by_login_or_email, get_user_teams},
},
models::{AddTeamMemberCommand, AdminCreateUserForm},
models::AddTeamMemberCommand,
};
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use std::sync::Arc;

pub async fn handle_grafana_add_user_to_team(
Expand All @@ -23,32 +21,12 @@ pub async fn handle_grafana_add_user_to_team(
// Check if user exists, if not create a new user
let user_id = match get_user_by_login_or_email(&grafana_conf, user_email).await {
Ok(user) => user.id,
Err(_) => {
// Create user with the same email as the user, password can be anything, it won't be used
let random_password: String = thread_rng()
.sample_iter(&Alphanumeric)
.take(30)
.map(char::from)
.collect();

let request = AdminCreateUserForm {
password: Some(random_password),
email: Some(user_email.to_lowercase().clone()),
login: None,
name: None,
org_id: None,
};

match admin_create_user(&grafana_conf, request).await {
Ok(user) => user.id,
Err(err) => {
warn!("Failed to create user: {:?}", err);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
CloudApiErrors::InternalServerError.to_string(),
));
}
}
Err(err) => {
warn!("Failed to get user from grafana: {:?}", err);
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
CloudApiErrors::InternalServerError.to_string(),
));
}
};

Expand Down
Loading

0 comments on commit fd2a875

Please sign in to comment.