Skip to content
Merged
6 changes: 3 additions & 3 deletions src/handlers/http/cluster/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,21 +331,21 @@ pub async fn sync_user_creation_with_ingestors(
if let Some(role) = role {
user.roles.clone_from(role);
}
let username = user.username();
let userid = user.userid();

let user_data = to_vec(&user).map_err(|err| {
error!("Fatal: failed to serialize user: {:?}", err);
RBACError::SerdeError(err)
})?;

let username = username.to_string();
let userid = userid.to_string();

for_each_live_ingestor(move |ingestor| {
let url = format!(
"{}{}/user/{}/sync",
ingestor.domain_name,
base_path_without_preceding_slash(),
username
userid
);

let user_data = user_data.clone();
Expand Down
75 changes: 48 additions & 27 deletions src/handlers/http/modal/ingest/ingestor_rbac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,20 @@
use std::collections::HashSet;

use actix_web::{Responder, web};
use tokio::sync::Mutex;

use crate::{
handlers::http::{modal::utils::rbac_utils::get_metadata, rbac::RBACError},
handlers::http::{
modal::utils::rbac_utils::get_metadata,
rbac::{RBACError, UPDATE_LOCK},
},
rbac::{
Users,
map::roles,
map::{roles, users},
user::{self, User as ParseableUser},
},
storage,
};

// async aware lock for updating storage metadata and user map atomicically
static UPDATE_LOCK: Mutex<()> = Mutex::const_new(());

// Handler for POST /api/v1/user/{username}
// Creates a new user by username if it does not exists
pub async fn post_user(
Expand All @@ -55,34 +54,49 @@ pub async fn post_user(
Ok(generated_password)
}

// Handler for DELETE /api/v1/user/delete/{username}
pub async fn delete_user(username: web::Path<String>) -> Result<impl Responder, RBACError> {
let username = username.into_inner();
let _ = UPDATE_LOCK.lock().await;
// Handler for DELETE /api/v1/user/delete/{userid}
pub async fn delete_user(userid: web::Path<String>) -> Result<impl Responder, RBACError> {
let userid = userid.into_inner();
let _guard = UPDATE_LOCK.lock().await;
// fail this request if the user does not exists
if !Users.contains(&username) {
if !Users.contains(&userid) {
return Err(RBACError::UserDoesNotExist);
};

// find username by userid, for native users, username is userid, for oauth users, we need to look up
let username = if let Some(user) = users().get(&userid) {
user.username_by_userid()
} else {
return Err(RBACError::UserDoesNotExist);
};

// delete from parseable.json first
let mut metadata = get_metadata().await?;
metadata.users.retain(|user| user.username() != username);
metadata.users.retain(|user| user.userid() != userid);

let _ = storage::put_staging_metadata(&metadata);

// update in mem table
Users.delete_user(&username);
Users.delete_user(&userid);
Ok(format!("deleted user: {username}"))
}

// Handler PATCH /user/{username}/role/sync/add => Add roles to a user
// Handler PATCH /user/{userid}/role/sync/add => Add roles to a user
pub async fn add_roles_to_user(
username: web::Path<String>,
userid: web::Path<String>,
roles_to_add: web::Json<HashSet<String>>,
) -> Result<String, RBACError> {
let username = username.into_inner();
let userid = userid.into_inner();
let roles_to_add = roles_to_add.into_inner();

if !Users.contains(&username) {
if !Users.contains(&userid) {
return Err(RBACError::UserDoesNotExist);
};

// find username by userid, for native users, username is userid, for oauth users, we need to look up
let username = if let Some(user) = users().get(&userid) {
user.username_by_userid()
} else {
return Err(RBACError::UserDoesNotExist);
};

Expand All @@ -103,7 +117,7 @@ pub async fn add_roles_to_user(
if let Some(user) = metadata
.users
.iter_mut()
.find(|user| user.username() == username)
.find(|user| user.userid() == userid)
{
user.roles.extend(roles_to_add.clone());
} else {
Expand All @@ -113,20 +127,27 @@ pub async fn add_roles_to_user(

let _ = storage::put_staging_metadata(&metadata);
// update in mem table
Users.add_roles(&username.clone(), roles_to_add.clone());
Users.add_roles(&userid.clone(), roles_to_add.clone());

Ok(format!("Roles updated successfully for {username}"))
}

// Handler PATCH /user/{username}/role/sync/add => Add roles to a user
// Handler PATCH /user/{userid}/role/sync/add => Add roles to a user
pub async fn remove_roles_from_user(
username: web::Path<String>,
userid: web::Path<String>,
roles_to_remove: web::Json<HashSet<String>>,
) -> Result<String, RBACError> {
let username = username.into_inner();
let userid = userid.into_inner();
let roles_to_remove = roles_to_remove.into_inner();

if !Users.contains(&username) {
if !Users.contains(&userid) {
return Err(RBACError::UserDoesNotExist);
};

// find username by userid, for native users, username is userid, for oauth users, we need to look up
let username = if let Some(user) = users().get(&userid) {
user.username_by_userid()
} else {
return Err(RBACError::UserDoesNotExist);
};

Expand All @@ -143,7 +164,7 @@ pub async fn remove_roles_from_user(
}

// check that user actually has these roles
let user_roles: HashSet<String> = HashSet::from_iter(Users.get_role(&username));
let user_roles: HashSet<String> = HashSet::from_iter(Users.get_role(&userid));
let roles_not_with_user: HashSet<String> =
HashSet::from_iter(roles_to_remove.difference(&user_roles).cloned());

Expand All @@ -153,12 +174,12 @@ pub async fn remove_roles_from_user(
)));
}

// update parseable.json first
// update parseable.json in staging first
let mut metadata = get_metadata().await?;
if let Some(user) = metadata
.users
.iter_mut()
.find(|user| user.username() == username)
.find(|user| user.userid() == userid)
{
let diff: HashSet<String> =
HashSet::from_iter(user.roles.difference(&roles_to_remove).cloned());
Expand All @@ -170,7 +191,7 @@ pub async fn remove_roles_from_user(

let _ = storage::put_staging_metadata(&metadata);
// update in mem table
Users.remove_roles(&username.clone(), roles_to_remove.clone());
Users.remove_roles(&userid.clone(), roles_to_remove.clone());

Ok(format!("Roles updated successfully for {username}"))
}
Expand Down
12 changes: 6 additions & 6 deletions src/handlers/http/modal/ingest/ingestor_role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,21 @@ pub async fn put(
// refresh the sessions of all users using this role
// for this, iterate over all user_groups and users and create a hashset of users
let mut session_refresh_users: HashSet<String> = HashSet::new();
for user_group in read_user_groups().values().cloned() {
for user_group in read_user_groups().values() {
if user_group.roles.contains(&name) {
session_refresh_users.extend(user_group.users);
session_refresh_users.extend(user_group.users.iter().map(|u| u.userid().to_string()));
}
}

// iterate over all users to see if they have this role
for user in users().values().cloned() {
for user in users().values() {
if user.roles.contains(&name) {
session_refresh_users.insert(user.username().to_string());
session_refresh_users.insert(user.userid().to_string());
}
}

for username in session_refresh_users {
mut_sessions().remove_user(&username);
for userid in session_refresh_users {
mut_sessions().remove_user(&userid);
}

Ok(HttpResponse::Ok().finish())
Expand Down
10 changes: 5 additions & 5 deletions src/handlers/http/modal/ingest_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ impl IngestServer {
.to(ingestor_rbac::post_user)
.authorize(Action::PutUser),
)
// DELETE /user/{username} => Sync deletion of a user
// DELETE /user/{userid} => Sync deletion of a user
.route(
web::delete()
.to(ingestor_rbac::delete_user)
Expand All @@ -199,8 +199,8 @@ impl IngestServer {
.wrap(DisAllowRootUser),
)
.service(
web::resource("/{username}/role/sync/add")
// PATCH /user/{username}/role/sync/add => Add roles to a user
web::resource("/{userid}/role/sync/add")
// PATCH /user/{userid}/role/sync/add => Add roles to a user
.route(
web::patch()
.to(ingestor_rbac::add_roles_to_user)
Expand All @@ -209,8 +209,8 @@ impl IngestServer {
),
)
.service(
web::resource("/{username}/role/sync/remove")
// PATCH /user/{username}/role/sync/remove => Remove roles from a user
web::resource("/{userid}/role/sync/remove")
// PATCH /user/{userid}/role/sync/remove => Remove roles from a user
.route(
web::patch()
.to(ingestor_rbac::remove_roles_from_user)
Expand Down
Loading
Loading