user permission management over api
This commit is contained in:
@@ -28,6 +28,10 @@ pub fn api_router() -> Router<MnemoState> {
|
|||||||
.route("/api/users/@{handle}", get(users::get_by_handle))
|
.route("/api/users/@{handle}", get(users::get_by_handle))
|
||||||
.route("/api/users/{id}/setpassw", post(users::change_password))
|
.route("/api/users/{id}/setpassw", post(users::change_password))
|
||||||
.route("/api/users/{id}/sethandle", post(users::change_handle))
|
.route("/api/users/{id}/sethandle", post(users::change_handle))
|
||||||
|
.route(
|
||||||
|
"/api/users/{id}/permissions/{perm}",
|
||||||
|
get(users::get_permission).put(users::put_permission),
|
||||||
|
)
|
||||||
// sessions
|
// sessions
|
||||||
.route("/api/sessions/{id}", get(sessions::get_by_id))
|
.route("/api/sessions/{id}", get(sessions::get_by_id))
|
||||||
.route("/api/sessions/{id}/revoke", post(sessions::revoke_by_id))
|
.route("/api/sessions/{id}/revoke", post(sessions::revoke_by_id))
|
||||||
|
|||||||
@@ -15,13 +15,14 @@ use crate::{
|
|||||||
User,
|
User,
|
||||||
auth::{UserAuthRequired, UserAuthenticate},
|
auth::{UserAuthRequired, UserAuthenticate},
|
||||||
handle::UserHandle,
|
handle::UserHandle,
|
||||||
permissions::Permission,
|
permissions::{Permission, PermissionState},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const CANT_CHANGE_OTHERS_HANDLE: &str = "You don't have permission to change this user's handle.";
|
const CANT_CHANGE_OTHERS_HANDLE: &str = "You don't have permission to change this user's handle.";
|
||||||
const CANT_CHANGE_OTHERS_PASSW: &str = "You don't have permission to change this user's password.";
|
const CANT_CHANGE_OTHERS_PASSW: &str = "You don't have permission to change this user's password.";
|
||||||
const CANT_MANUALLY_MAKE_USERS: &str = "You don't have permission to manually create new users.";
|
const CANT_MANUALLY_MAKE_USERS: &str = "You don't have permission to manually create new users.";
|
||||||
|
const GO_AWAY: &str = "You don't have permission to look into permissions!";
|
||||||
const HANDLE_CHANGED_SUCCESS: &str = "Handle changed successfully.";
|
const HANDLE_CHANGED_SUCCESS: &str = "Handle changed successfully.";
|
||||||
const PASSW_CHANGED_SUCCESS: &str = "Password changed successfully.";
|
const PASSW_CHANGED_SUCCESS: &str = "Password changed successfully.";
|
||||||
|
|
||||||
@@ -170,3 +171,82 @@ pub async fn change_password(
|
|||||||
|
|
||||||
Ok(PASSW_CHANGED_SUCCESS.into_response())
|
Ok(PASSW_CHANGED_SUCCESS.into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_permission(
|
||||||
|
State(state): State<MnemoState>,
|
||||||
|
Path((uid, perm)): Path<(Uuid, Permission)>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
) -> Result<Response, CompositeError> {
|
||||||
|
let mut conn = state.pool.acquire().await?;
|
||||||
|
let u = User::authenticate(&mut conn, &headers).await?.required()?;
|
||||||
|
if !u.has_permission(&mut conn, Permission::Admin).await? {
|
||||||
|
return Ok((StatusCode::FORBIDDEN, GO_AWAY).into_response());
|
||||||
|
}
|
||||||
|
let target = User::get_by_id(&mut conn, uid).await?;
|
||||||
|
let has: PermissionState = target.permission_dbstate(&mut conn, perm).await?.into();
|
||||||
|
|
||||||
|
Ok((StatusCode::OK, Json(has)).into_response())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn put_permission(
|
||||||
|
State(state): State<MnemoState>,
|
||||||
|
Path((uid, perm)): Path<(Uuid, Permission)>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
Json(newstate): Json<PermissionState>,
|
||||||
|
) -> Result<Response, CompositeError> {
|
||||||
|
let mut tx = state.pool.begin().await?;
|
||||||
|
let u = User::authenticate(&mut tx, &headers).await?.required()?;
|
||||||
|
if !u.has_permission(&mut tx, Permission::Admin).await? {
|
||||||
|
return Ok((StatusCode::FORBIDDEN, GO_AWAY).into_response());
|
||||||
|
}
|
||||||
|
|
||||||
|
let target = User::get_by_id(&mut tx, uid).await?;
|
||||||
|
let os: PermissionState = target.permission_dbstate(&mut tx, perm).await?.into();
|
||||||
|
match newstate {
|
||||||
|
PermissionState::ExplicitlyGranted => {
|
||||||
|
target.grant_permission(&mut tx, perm).await?;
|
||||||
|
LogEntry::new(
|
||||||
|
&mut tx,
|
||||||
|
u,
|
||||||
|
LogAction::UpdatePermission {
|
||||||
|
id: target.id,
|
||||||
|
os,
|
||||||
|
ns: newstate,
|
||||||
|
p: perm,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
PermissionState::ExplicitlyRevoked => {
|
||||||
|
target.revoke_permission(&mut tx, perm).await?;
|
||||||
|
LogEntry::new(
|
||||||
|
&mut tx,
|
||||||
|
u,
|
||||||
|
LogAction::UpdatePermission {
|
||||||
|
id: target.id,
|
||||||
|
os,
|
||||||
|
ns: newstate,
|
||||||
|
p: perm,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
PermissionState::Implicit => {
|
||||||
|
target.reset_permission(&mut tx, perm).await?;
|
||||||
|
LogEntry::new(
|
||||||
|
&mut tx,
|
||||||
|
u,
|
||||||
|
LogAction::UpdatePermission {
|
||||||
|
id: target.id,
|
||||||
|
os,
|
||||||
|
ns: newstate,
|
||||||
|
p: perm,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tx.commit().await?;
|
||||||
|
Ok((StatusCode::OK, Json(newstate)).into_response())
|
||||||
|
}
|
||||||
|
|||||||
21
src/logs.rs
21
src/logs.rs
@@ -4,7 +4,15 @@ use strum::{EnumDiscriminants, EnumIter, IntoStaticStr, VariantNames};
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{database::DatabaseError, quotes::Quote, users::User, web::icons};
|
use crate::{
|
||||||
|
database::DatabaseError,
|
||||||
|
quotes::Quote,
|
||||||
|
users::{
|
||||||
|
User,
|
||||||
|
permissions::{Permission, PermissionState},
|
||||||
|
},
|
||||||
|
web::icons,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LogEntry {
|
pub struct LogEntry {
|
||||||
@@ -106,6 +114,12 @@ pub enum LogAction {
|
|||||||
id: Uuid,
|
id: Uuid,
|
||||||
handle: String,
|
handle: String,
|
||||||
},
|
},
|
||||||
|
UpdatePermission {
|
||||||
|
id: Uuid,
|
||||||
|
os: PermissionState,
|
||||||
|
ns: PermissionState,
|
||||||
|
p: Permission,
|
||||||
|
},
|
||||||
ManuallyChangeUsersPassword {
|
ManuallyChangeUsersPassword {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
},
|
},
|
||||||
@@ -180,6 +194,7 @@ impl LogAction {
|
|||||||
| Self::ManuallyRevokeSession { id }
|
| Self::ManuallyRevokeSession { id }
|
||||||
| Self::RenameTag { id, .. }
|
| Self::RenameTag { id, .. }
|
||||||
| Self::DeleteTag { id, .. }
|
| Self::DeleteTag { id, .. }
|
||||||
|
| Self::UpdatePermission { id, .. }
|
||||||
| Self::ManuallyChangeUsersPassword { id } => Some(*id),
|
| Self::ManuallyChangeUsersPassword { id } => Some(*id),
|
||||||
|
|
||||||
Self::DeleteQuote { quote } => Some(quote.id),
|
Self::DeleteQuote { quote } => Some(quote.id),
|
||||||
@@ -200,6 +215,9 @@ impl LogAction {
|
|||||||
LogAction::CreateUser { id, handle } => {
|
LogAction::CreateUser { id, handle } => {
|
||||||
format!("Created user @{handle} (uid: {id})")
|
format!("Created user @{handle} (uid: {id})")
|
||||||
}
|
}
|
||||||
|
LogAction::UpdatePermission { id, os, ns, p } => {
|
||||||
|
format!("Updated permission {p} of user with id {id} from {os} to {ns}")
|
||||||
|
}
|
||||||
LogAction::ManuallyChangeUsersPassword { id } => {
|
LogAction::ManuallyChangeUsersPassword { id } => {
|
||||||
format!("Manually changed password of user with id: {id}")
|
format!("Manually changed password of user with id: {id}")
|
||||||
}
|
}
|
||||||
@@ -251,6 +269,7 @@ impl LogActionDiscriminant {
|
|||||||
LAD::Initialize => "Mnemosyne Initialization",
|
LAD::Initialize => "Mnemosyne Initialization",
|
||||||
LAD::RegenInfradmin => "Infradmin Regeneration",
|
LAD::RegenInfradmin => "Infradmin Regeneration",
|
||||||
LAD::CreateUser => "User Creation",
|
LAD::CreateUser => "User Creation",
|
||||||
|
LAD::UpdatePermission => "Permission Update",
|
||||||
LAD::ManuallyChangeUsersPassword => "Password Override",
|
LAD::ManuallyChangeUsersPassword => "Password Override",
|
||||||
LAD::CreateTag => "Tag Creation",
|
LAD::CreateTag => "Tag Creation",
|
||||||
LAD::RenameTag => "Tag Rename",
|
LAD::RenameTag => "Tag Rename",
|
||||||
|
|||||||
@@ -1,9 +1,19 @@
|
|||||||
use sqlx::PgConnection;
|
use sqlx::PgConnection;
|
||||||
|
use strum::Display;
|
||||||
|
|
||||||
use crate::{database::DatabaseError, users::User};
|
use crate::{database::DatabaseError, users::User};
|
||||||
|
|
||||||
/// Infradmin and systemuser have all permissions.
|
/// Infradmin and systemuser have all permissions.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, strum::IntoStaticStr)]
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
PartialEq,
|
||||||
|
strum::IntoStaticStr,
|
||||||
|
Display,
|
||||||
|
serde::Deserialize,
|
||||||
|
serde::Serialize,
|
||||||
|
)]
|
||||||
pub enum Permission {
|
pub enum Permission {
|
||||||
// Pass all the permission checks
|
// Pass all the permission checks
|
||||||
// Additionally, only Admins can manage others' permissions.
|
// Additionally, only Admins can manage others' permissions.
|
||||||
@@ -36,8 +46,34 @@ impl Permission {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
PartialEq,
|
||||||
|
strum::IntoStaticStr,
|
||||||
|
Display,
|
||||||
|
serde::Deserialize,
|
||||||
|
serde::Serialize,
|
||||||
|
)]
|
||||||
|
pub enum PermissionState {
|
||||||
|
ExplicitlyGranted,
|
||||||
|
ExplicitlyRevoked,
|
||||||
|
Implicit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Option<bool>> for PermissionState {
|
||||||
|
fn from(state: Option<bool>) -> Self {
|
||||||
|
match state {
|
||||||
|
Some(true) => Self::ExplicitlyGranted,
|
||||||
|
Some(false) => Self::ExplicitlyRevoked,
|
||||||
|
None => Self::Implicit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
async fn permission_dbstate(
|
pub async fn permission_dbstate(
|
||||||
&self,
|
&self,
|
||||||
conn: &mut PgConnection,
|
conn: &mut PgConnection,
|
||||||
permission: Permission,
|
permission: Permission,
|
||||||
|
|||||||
Reference in New Issue
Block a user