change handle endpoint & why was changepassword a GET?
This commit is contained in:
@@ -23,7 +23,8 @@ pub fn api_router() -> Router {
|
|||||||
.route("/api/users/me", get(users::get_me))
|
.route("/api/users/me", get(users::get_me))
|
||||||
.route("/api/users/{id}", get(users::get_by_id))
|
.route("/api/users/{id}", get(users::get_by_id))
|
||||||
.route("/api/users/@{handle}", get(users::get_by_handle))
|
.route("/api/users/@{handle}", get(users::get_by_handle))
|
||||||
.route("/api/users/{id}/setpassw", get(users::change_password))
|
.route("/api/users/{id}/setpassw", post(users::change_password))
|
||||||
|
.route("/api/users/{id}/sethandle", post(users::change_handle))
|
||||||
.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))
|
||||||
.route("/api/tags/{id}", get(tags::get_by_id))
|
.route("/api/tags/{id}", get(tags::get_by_id))
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 HANDLE_CHANGED_SUCCESS: &str = "Handle changed successfully.";
|
||||||
const PASSW_CHANGED_SUCCESS: &str = "Password changed successfully.";
|
const PASSW_CHANGED_SUCCESS: &str = "Password changed successfully.";
|
||||||
|
|
||||||
pub async fn get_me(headers: HeaderMap) -> Result<Response, CompositeError> {
|
pub async fn get_me(headers: HeaderMap) -> Result<Response, CompositeError> {
|
||||||
@@ -40,6 +42,28 @@ pub async fn get_by_handle(
|
|||||||
Ok(Json(User::get_by_handle(handle)?).into_response())
|
Ok(Json(User::get_by_handle(handle)?).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ChangeHandleForm {
|
||||||
|
handle: UserHandle,
|
||||||
|
}
|
||||||
|
pub async fn change_handle(
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
Json(form): Json<ChangeHandleForm>,
|
||||||
|
) -> Result<Response, CompositeError> {
|
||||||
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
|
let mut target = if u.id == id {
|
||||||
|
u
|
||||||
|
} else {
|
||||||
|
if u.has_permission(Permission::ChangeOthersHandles)? == false {
|
||||||
|
return Ok((StatusCode::FORBIDDEN, CANT_CHANGE_OTHERS_HANDLE).into_response());
|
||||||
|
}
|
||||||
|
User::get_by_id(id)?
|
||||||
|
};
|
||||||
|
target.set_handle(form.handle)?;
|
||||||
|
Ok(HANDLE_CHANGED_SUCCESS.into_response())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct ChangePasswordForm {
|
pub struct ChangePasswordForm {
|
||||||
password: String,
|
password: String,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use axum::{
|
|||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use rusqlite::OptionalExtension;
|
use rusqlite::{ErrorCode, OptionalExtension};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -35,6 +35,8 @@ pub enum UserError {
|
|||||||
NoUserWithId(Uuid),
|
NoUserWithId(Uuid),
|
||||||
#[error("No user found with handle {0}")]
|
#[error("No user found with handle {0}")]
|
||||||
NoUserWithHandle(UserHandle),
|
NoUserWithHandle(UserHandle),
|
||||||
|
#[error("A user with handle {0} already exists")]
|
||||||
|
HandleAlreadyExists(UserHandle),
|
||||||
#[error("Database error: {0}")]
|
#[error("Database error: {0}")]
|
||||||
DatabaseError(String),
|
DatabaseError(String),
|
||||||
#[error("Argon2 passhash error: {0}")]
|
#[error("Argon2 passhash error: {0}")]
|
||||||
@@ -72,6 +74,21 @@ impl User {
|
|||||||
None => Err(UserError::NoUserWithHandle(handle)),
|
None => Err(UserError::NoUserWithHandle(handle)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn set_handle(&mut self, new_handle: UserHandle) -> Result<(), UserError> {
|
||||||
|
let conn = database::conn()?;
|
||||||
|
conn.prepare("UPDATE users SET handle = ?1 WHERE id = ?2")?
|
||||||
|
.execute((&new_handle, self.id))
|
||||||
|
.map_err(|e| {
|
||||||
|
if let Some(e) = e.sqlite_error() {
|
||||||
|
if e.code == ErrorCode::ConstraintViolation {
|
||||||
|
return UserError::HandleAlreadyExists(new_handle.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UserError::from(e)
|
||||||
|
})?;
|
||||||
|
self.handle = new_handle;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DANGEROUS: AUTH
|
// DANGEROUS: AUTH
|
||||||
@@ -123,7 +140,6 @@ impl User {
|
|||||||
/// to do everything and probably should not be used as a regular account
|
/// to do everything and probably should not be used as a regular account
|
||||||
/// due to the ramifications of compromise. But it could be used for that,
|
/// due to the ramifications of compromise. But it could be used for that,
|
||||||
/// and have its name changed.
|
/// and have its name changed.
|
||||||
#[allow(unused)]
|
|
||||||
pub fn is_infradmin(&self) -> bool {
|
pub fn is_infradmin(&self) -> bool {
|
||||||
self.id == Uuid::max()
|
self.id == Uuid::max()
|
||||||
}
|
}
|
||||||
@@ -169,7 +185,6 @@ impl User {
|
|||||||
/// for actions performed by Mnemosyne internally.
|
/// for actions performed by Mnemosyne internally.
|
||||||
/// It shall not be available for log-in.
|
/// It shall not be available for log-in.
|
||||||
/// It should not have its name changed, and should be protected from that.
|
/// It should not have its name changed, and should be protected from that.
|
||||||
#[allow(unused)]
|
|
||||||
pub fn is_systemuser(&self) -> bool {
|
pub fn is_systemuser(&self) -> bool {
|
||||||
self.id == Uuid::nil()
|
self.id == Uuid::nil()
|
||||||
}
|
}
|
||||||
@@ -199,6 +214,7 @@ impl IntoResponse for UserError {
|
|||||||
Self::UserHandleError(_) => (StatusCode::BAD_REQUEST, self.to_string()),
|
Self::UserHandleError(_) => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||||
Self::NoUserWithId(_) => (StatusCode::BAD_REQUEST, self.to_string()),
|
Self::NoUserWithId(_) => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||||
Self::NoUserWithHandle(_) => (StatusCode::BAD_REQUEST, self.to_string()),
|
Self::NoUserWithHandle(_) => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||||
|
Self::HandleAlreadyExists(_) => (StatusCode::CONFLICT, self.to_string()),
|
||||||
}
|
}
|
||||||
.into_response()
|
.into_response()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ pub enum Permission {
|
|||||||
RevokeOthersSessions,
|
RevokeOthersSessions,
|
||||||
// All Users have the right to change their own password
|
// All Users have the right to change their own password
|
||||||
ChangeOthersPasswords,
|
ChangeOthersPasswords,
|
||||||
|
// All Users have the right to change their own handle
|
||||||
|
ChangeOthersHandles,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub fn has_permission(&self, permission: Permission) -> Result<bool, DatabaseError> {
|
pub fn has_permission(&self, permission: Permission) -> Result<bool, DatabaseError> {
|
||||||
|
// Infradmin and systemuser have all permissions
|
||||||
if self.is_infradmin() || self.is_systemuser() {
|
if self.is_infradmin() || self.is_systemuser() {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,6 @@ impl Session {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn revoke(&mut self, actor: Option<&User>) -> Result<(), SessionError> {
|
pub fn revoke(&mut self, actor: Option<&User>) -> Result<(), SessionError> {
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
let id = actor.map(|u| u.id).unwrap_or(Uuid::nil());
|
let id = actor.map(|u| u.id).unwrap_or(Uuid::nil());
|
||||||
|
|||||||
Reference in New Issue
Block a user