mirror of https://github.com/buster-so/buster.git
Merge pull request #37 from buster-so/dallin/user-permissions
Dallin/user-permissions
This commit is contained in:
commit
b69508d20b
|
@ -3,6 +3,7 @@ mod assets;
|
||||||
mod data_sources;
|
mod data_sources;
|
||||||
mod dataset_groups;
|
mod dataset_groups;
|
||||||
mod datasets;
|
mod datasets;
|
||||||
|
mod organizations;
|
||||||
mod permission_groups;
|
mod permission_groups;
|
||||||
mod sql;
|
mod sql;
|
||||||
mod users;
|
mod users;
|
||||||
|
@ -21,6 +22,7 @@ pub fn router() -> Router {
|
||||||
.nest("/permission_groups", permission_groups::router())
|
.nest("/permission_groups", permission_groups::router())
|
||||||
.nest("/dataset_groups", dataset_groups::router())
|
.nest("/dataset_groups", dataset_groups::router())
|
||||||
.nest("/sql", sql::router())
|
.nest("/sql", sql::router())
|
||||||
|
.nest("/organizations", organizations::router())
|
||||||
.route_layer(middleware::from_fn(auth)),
|
.route_layer(middleware::from_fn(auth)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
use axum::{routing::get, Router};
|
||||||
|
|
||||||
|
mod users;
|
||||||
|
|
||||||
|
pub fn router() -> Router {
|
||||||
|
Router::new()
|
||||||
|
.route("/:id/users", get(users::list_organization_users))
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use axum::{extract::Path, http::StatusCode, Extension};
|
||||||
|
use diesel::{ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl};
|
||||||
|
use diesel_async::RunQueryDsl;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
database::{
|
||||||
|
enums::{UserOrganizationRole, UserOrganizationStatus},
|
||||||
|
lib::get_pg_pool,
|
||||||
|
models::User,
|
||||||
|
schema::{users, users_to_organizations},
|
||||||
|
},
|
||||||
|
routes::rest::ApiResponse,
|
||||||
|
utils::clients::sentry_utils::send_sentry_error,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct UserResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub email: String,
|
||||||
|
pub role: UserOrganizationRole,
|
||||||
|
pub status: UserOrganizationStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn list_organization_users(
|
||||||
|
Extension(user): Extension<User>,
|
||||||
|
Path(organization_id): Path<Uuid>,
|
||||||
|
) -> Result<ApiResponse<Vec<UserResponse>>, (StatusCode, &'static str)> {
|
||||||
|
let users = match list_organization_users_handler(organization_id).await {
|
||||||
|
Ok(users) => users,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Error listing organization users: {:?}", e);
|
||||||
|
send_sentry_error(&e.to_string(), Some(&user.id));
|
||||||
|
return Err((
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
"Error listing organization users",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ApiResponse::JsonData(users))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_organization_users_handler(organization_id: Uuid) -> Result<Vec<UserResponse>> {
|
||||||
|
let mut conn = get_pg_pool().get().await?;
|
||||||
|
|
||||||
|
let users = users::table
|
||||||
|
.inner_join(users_to_organizations::table.on(users::id.eq(users_to_organizations::user_id)))
|
||||||
|
.select((
|
||||||
|
users::id,
|
||||||
|
users::email,
|
||||||
|
users::name.nullable(),
|
||||||
|
users_to_organizations::role,
|
||||||
|
users_to_organizations::status,
|
||||||
|
))
|
||||||
|
.filter(users_to_organizations::organization_id.eq(organization_id))
|
||||||
|
.filter(users_to_organizations::deleted_at.is_null())
|
||||||
|
.load::<(
|
||||||
|
Uuid,
|
||||||
|
String,
|
||||||
|
Option<String>,
|
||||||
|
UserOrganizationRole,
|
||||||
|
UserOrganizationStatus,
|
||||||
|
)>(&mut conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(users
|
||||||
|
.into_iter()
|
||||||
|
.map(|(id, email, name, role, status)| UserResponse {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
role,
|
||||||
|
status,
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use axum::{extract::Path, Extension};
|
||||||
|
use diesel_async::RunQueryDsl;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
database::{
|
||||||
|
enums::{UserOrganizationRole, UserOrganizationStatus},
|
||||||
|
lib::get_pg_pool,
|
||||||
|
models::User,
|
||||||
|
schema::{users, users_to_organizations},
|
||||||
|
},
|
||||||
|
routes::rest::ApiResponse,
|
||||||
|
utils::clients::sentry_utils::send_sentry_error,
|
||||||
|
};
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use diesel::{ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct UserResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub email: String,
|
||||||
|
pub role: UserOrganizationRole,
|
||||||
|
pub status: UserOrganizationStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_user_by_id(
|
||||||
|
Extension(user): Extension<User>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<ApiResponse<UserResponse>, (StatusCode, &'static str)> {
|
||||||
|
let user_info = match get_user_information(&id).await {
|
||||||
|
Ok(user_info) => user_info,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Error getting user information: {:?}", e);
|
||||||
|
send_sentry_error(&e.to_string(), Some(&user.id));
|
||||||
|
return Err((
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
"Error getting user information",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ApiResponse::JsonData(user_info))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_user_information(user_id: &Uuid) -> Result<UserResponse> {
|
||||||
|
let pg_pool = get_pg_pool();
|
||||||
|
let mut conn = pg_pool.get().await?;
|
||||||
|
|
||||||
|
let (user, (role, status)) = users::table
|
||||||
|
.inner_join(users_to_organizations::table.on(users::id.eq(users_to_organizations::user_id)))
|
||||||
|
.select((
|
||||||
|
(users::id, users::email, users::name.nullable()),
|
||||||
|
(users_to_organizations::role, users_to_organizations::status),
|
||||||
|
))
|
||||||
|
.filter(users::id.eq(user_id))
|
||||||
|
.first::<(
|
||||||
|
(Uuid, String, Option<String>),
|
||||||
|
(UserOrganizationRole, UserOrganizationStatus),
|
||||||
|
)>(&mut conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let (id, email, name) = user;
|
||||||
|
|
||||||
|
Ok(UserResponse {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
role,
|
||||||
|
status,
|
||||||
|
})
|
||||||
|
}
|
|
@ -3,11 +3,13 @@ use axum::{
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod get_user;
|
mod get_user;
|
||||||
pub mod update_user;
|
mod get_user_by_id;
|
||||||
|
mod update_user;
|
||||||
|
|
||||||
pub fn router() -> Router {
|
pub fn router() -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", get(get_user::get_user))
|
.route("/", get(get_user::get_user))
|
||||||
.route("/", put(update_user::update_user))
|
.route("/:id", put(update_user::update_user))
|
||||||
|
.route("/:id", get(get_user_by_id::get_user_by_id))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,41 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use axum::extract::Path;
|
||||||
use axum::{Extension, Json};
|
use axum::{Extension, Json};
|
||||||
use diesel_async::RunQueryDsl;
|
|
||||||
|
|
||||||
use crate::database::lib::get_pg_pool;
|
use crate::database::enums::UserOrganizationStatus;
|
||||||
use crate::database::models::User;
|
use crate::database::models::User;
|
||||||
use crate::database::schema::users;
|
use crate::database::schema::{users, users_to_organizations};
|
||||||
|
use crate::database::{enums::UserOrganizationRole, lib::get_pg_pool};
|
||||||
use crate::routes::rest::ApiResponse;
|
use crate::routes::rest::ApiResponse;
|
||||||
use crate::utils::clients::sentry_utils::send_sentry_error;
|
use crate::utils::clients::sentry_utils::send_sentry_error;
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use diesel::{update, ExpressionMethods};
|
use diesel::{update, ExpressionMethods};
|
||||||
use serde::Deserialize;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct UserResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub email: String,
|
||||||
|
pub role: UserOrganizationRole,
|
||||||
|
pub status: UserOrganizationStatus,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct UpdateUserRequest {
|
pub struct UpdateUserRequest {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
pub role: Option<UserOrganizationRole>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_user(
|
pub async fn update_user(
|
||||||
Extension(user): Extension<User>,
|
Extension(user): Extension<User>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
Json(body): Json<UpdateUserRequest>,
|
Json(body): Json<UpdateUserRequest>,
|
||||||
) -> Result<ApiResponse<User>, (StatusCode, &'static str)> {
|
) -> Result<ApiResponse<()>, (StatusCode, &'static str)> {
|
||||||
let user_info_object = match update_user_handler(&user.id, body.name).await {
|
match update_user_handler(&id, body).await {
|
||||||
Ok(user_info_object) => user_info_object,
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Error getting user information: {:?}", e);
|
tracing::error!("Error getting user information: {:?}", e);
|
||||||
send_sentry_error(&e.to_string(), Some(&user.id));
|
send_sentry_error(&e.to_string(), Some(&user.id));
|
||||||
|
@ -33,10 +46,10 @@ pub async fn update_user(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ApiResponse::JsonData(user_info_object))
|
Ok(ApiResponse::NoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_user_handler(user_id: &Uuid, name: Option<String>) -> Result<User> {
|
pub async fn update_user_handler(user_id: &Uuid, change: UpdateUserRequest) -> Result<()> {
|
||||||
let pg_pool = get_pg_pool();
|
let pg_pool = get_pg_pool();
|
||||||
|
|
||||||
let mut conn = match pg_pool.get().await {
|
let mut conn = match pg_pool.get().await {
|
||||||
|
@ -46,16 +59,34 @@ pub async fn update_user_handler(user_id: &Uuid, name: Option<String>) -> Result
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let user = match update(users::table)
|
if let Some(name) = change.name {
|
||||||
.filter(users::id.eq(user_id))
|
match update(users::table)
|
||||||
.set(users::name.eq(name))
|
.filter(users::id.eq(user_id))
|
||||||
.returning(users::all_columns)
|
.set(users::name.eq(name))
|
||||||
.get_result::<User>(&mut conn)
|
.execute(&mut conn)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(user) => user,
|
Ok(user) => user,
|
||||||
Err(e) => return Err(anyhow::anyhow!("Error updating user: {:?}", e)),
|
Err(e) => return Err(anyhow::anyhow!("Error updating user: {:?}", e)),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Ok(user)
|
if let Some(role) = change.role {
|
||||||
|
match update(users_to_organizations::table)
|
||||||
|
.filter(users_to_organizations::user_id.eq(user_id))
|
||||||
|
.set(users_to_organizations::role.eq(role))
|
||||||
|
.execute(&mut conn)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(user_organization_role_update) => user_organization_role_update,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Error updating user organization role: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue