postgres via sqlx - workable?

This commit is contained in:
2026-04-20 01:17:30 +02:00
parent acfd8a6d72
commit 879c5ee3d3
42 changed files with 2536 additions and 1184 deletions

View File

@@ -1,12 +1,14 @@
use axum::{
Form, Json,
extract::State,
http::{HeaderMap, header},
response::{IntoResponse, Redirect, Response},
};
use serde::Deserialize;
use sqlx::PgPool;
use crate::{
database,
MnemoState,
users::{
User,
auth::{
@@ -23,10 +25,14 @@ pub struct LoginForm {
password: String,
}
fn login_common(creds: LoginForm) -> Result<(String, String), AuthError> {
let u = authenticate_via_credentials(&creds.handle, &creds.password)?.required()?;
let conn = database::conn()?;
let (_, token) = Session::new_for_user(&conn, &u)?;
async fn login_common(pool: &PgPool, creds: LoginForm) -> Result<(String, String), AuthError> {
let mut conn = pool.acquire().await?;
let u = authenticate_via_credentials(&mut conn, &creds.handle, &creds.password)
.await?
.required()?;
let (_, token) = Session::new_for_user(&mut conn, &u).await?;
let secure = match cfg!(debug_assertions) {
false => "; Secure",
true => "",
@@ -38,12 +44,20 @@ fn login_common(creds: LoginForm) -> Result<(String, String), AuthError> {
);
Ok((token, cookie))
}
pub async fn login(Json(creds): Json<LoginForm>) -> Result<Response, AuthError> {
let (token, cookie) = login_common(creds)?;
pub async fn login(
State(state): State<MnemoState>,
Json(creds): Json<LoginForm>,
) -> Result<Response, AuthError> {
let (token, cookie) = login_common(&state.pool, creds).await?;
Ok(([(header::SET_COOKIE, cookie)], token).into_response())
}
pub async fn login_form(Form(creds): Form<LoginForm>) -> Result<Response, AuthError> {
match login_common(creds) {
pub async fn login_form(
State(state): State<MnemoState>,
Form(creds): Form<LoginForm>,
) -> Result<Response, AuthError> {
match login_common(&state.pool, creds).await {
Ok((_, cookie)) => {
Ok(([(header::SET_COOKIE, cookie)], Redirect::to("/dashboard")).into_response())
}
@@ -51,17 +65,32 @@ pub async fn login_form(Form(creds): Form<LoginForm>) -> Result<Response, AuthEr
}
}
pub async fn logout(headers: HeaderMap) -> Result<Response, AuthError> {
let mut s = Session::authenticate(&headers)?.required()?;
let conn = database::conn()?;
s.revoke(&conn, Some(&User::get_by_id(&conn, s.user_id)?))?;
pub async fn logout(
State(state): State<MnemoState>,
headers: HeaderMap,
) -> Result<Response, AuthError> {
let mut conn = state.pool.acquire().await?;
let mut s = Session::authenticate(&mut conn, &headers)
.await?
.required()?;
let user = User::get_by_id(&mut conn, s.user_id).await?;
s.revoke(&mut conn, Some(&user)).await?;
let cookie = format!("{COOKIE_NAME}=revoking; Path=/; HttpOnly; Max-Age=0");
Ok(([(header::SET_COOKIE, cookie)], "Logged out!").into_response())
}
pub async fn logout_form(headers: HeaderMap) -> Result<Response, AuthError> {
let mut s = Session::authenticate(&headers)?.required()?;
let conn = database::conn()?;
s.revoke(&conn, Some(&User::get_by_id(&conn, s.user_id)?))?;
pub async fn logout_form(
State(state): State<MnemoState>,
headers: HeaderMap,
) -> Result<Response, AuthError> {
let mut conn = state.pool.acquire().await?;
let mut s = Session::authenticate(&mut conn, &headers)
.await?
.required()?;
let user = User::get_by_id(&mut conn, s.user_id).await?;
s.revoke(&mut conn, Some(&user)).await?;
let cookie = format!("{COOKIE_NAME}=revoking; Path=/; HttpOnly; Max-Age=0");
Ok(([(header::SET_COOKIE, cookie)], Redirect::to("/")).into_response())
}

View File

@@ -3,6 +3,8 @@ use axum::{
routing::{delete, get, patch, post},
};
use crate::MnemoState;
mod auth;
mod persons;
mod quotes;
@@ -10,7 +12,7 @@ mod sessions;
mod tags;
mod users;
pub fn api_router() -> Router {
pub fn api_router() -> Router<MnemoState> {
Router::new()
.route("/api/live", get(async || "Mnemosyne lives"))
// auth

View File

@@ -1,6 +1,6 @@
use axum::{
Json,
extract::Path,
extract::{Path, State},
http::{HeaderMap, StatusCode},
response::{IntoResponse, Response},
};
@@ -8,7 +8,7 @@ use serde::Deserialize;
use uuid::Uuid;
use crate::{
database::{self},
MnemoState,
error::CompositeError,
logs::{LogAction, LogEntry},
persons::{Name, Person},
@@ -21,26 +21,34 @@ use crate::{
pub const CANT_SET_PRIMARYNAME: &str = "You don't have permission to swap primary names.";
pub async fn get_all(headers: HeaderMap) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(Person::get_all(&conn)?).into_response())
pub async fn get_all(
State(state): State<MnemoState>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
Ok(Json(Person::get_all(&mut conn).await?).into_response())
}
pub async fn get_by_id(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(Person::get_by_id(&conn, id)?).into_response())
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
Ok(Json(Person::get_by_id(&mut conn, id).await?).into_response())
}
pub async fn pid_names(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(Person::get_by_id(&conn, id)?.get_all_names(&conn)?).into_response())
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
let person = Person::get_by_id(&mut conn, id).await?;
Ok(Json(person.get_all_names(&mut conn).await?).into_response())
}
#[derive(Deserialize)]
@@ -49,38 +57,41 @@ pub struct PersonNameForm {
}
pub async fn create(
State(state): State<MnemoState>,
headers: HeaderMap,
Json(form): Json<PersonNameForm>,
) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let mut conn = database::conn()?;
let tx = conn.transaction()?;
let mut tx = state.pool.begin().await?;
let u = User::authenticate(&mut tx, &headers).await?.required()?;
let p = Person::create(&tx, form.name, u.id)?;
let p = Person::create(&mut tx, form.name, u.id).await?;
LogEntry::new(
&tx,
&mut tx,
u,
LogAction::CreatePerson {
id: p.id,
pname: p.primary_name.as_str().to_string(),
pname: p.primary_name.clone(),
},
)?;
tx.commit()?;
)
.await?;
tx.commit().await?;
Ok((StatusCode::CREATED, Json(p)).into_response())
}
pub async fn add_name(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
Json(form): Json<PersonNameForm>,
) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let mut conn = database::conn()?;
let tx = conn.transaction()?;
let mut tx = state.pool.begin().await?;
let u = User::authenticate(&mut tx, &headers).await?.required()?;
let p = Person::get_by_id(&tx, id)?;
let n = p.add_name(&tx, form.name, u.id)?;
let p = Person::get_by_id(&mut tx, id).await?;
let n = p.add_name(&mut tx, form.name, u.id).await?;
LogEntry::new(
&tx,
&mut tx,
u,
LogAction::AddPersonName {
pid: p.id,
@@ -88,39 +99,54 @@ pub async fn add_name(
pn: p.primary_name,
nn: n.name.clone(),
},
)?;
tx.commit()?;
)
.await?;
tx.commit().await?;
Ok((StatusCode::CREATED, Json(n)).into_response())
}
pub async fn n_all(headers: HeaderMap) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(Name::get_all(&conn)?).into_response())
pub async fn n_all(
State(state): State<MnemoState>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
Ok(Json(Name::get_all(&mut conn).await?).into_response())
}
pub async fn n_by_id(Path(id): Path<Uuid>, headers: HeaderMap) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(Name::get_by_id(&conn, id)?).into_response())
}
pub async fn n_setprimary(
pub async fn n_by_id(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let mut conn = database::conn()?;
let tx = conn.transaction()?;
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
Ok(Json(Name::get_by_id(&mut conn, id).await?).into_response())
}
if !u.has_permission(&tx, Permission::ChangePersonPrimaryName)? {
pub async fn n_setprimary(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
) -> 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::ChangePersonPrimaryName)
.await?
{
return Ok((StatusCode::FORBIDDEN, CANT_SET_PRIMARYNAME).into_response());
}
let mut n = Name::get_by_id(&tx, id)?;
let p = Person::get_by_id(&tx, n.person_id)?;
n.set_primary(&tx)?;
let mut n = Name::get_by_id(&mut tx, id).await?;
let p = Person::get_by_id(&mut tx, n.person_id).await?;
n.set_primary(&mut tx).await?;
n.is_primary = true;
LogEntry::new(
&tx,
&mut tx,
u,
LogAction::SetPersonPrimaryName {
pid: p.id,
@@ -128,8 +154,9 @@ pub async fn n_setprimary(
on: p.primary_name,
nn: n.name.clone(),
},
)?;
tx.commit()?;
)
.await?;
tx.commit().await?;
Ok(Json(n).into_response())
}

View File

@@ -1,15 +1,15 @@
use axum::{
Json,
extract::Path,
extract::{Path, State},
http::{HeaderMap, StatusCode},
response::{IntoResponse, Response},
};
use chrono::{DateTime, FixedOffset};
use chrono::NaiveDateTime;
use serde::Deserialize;
use uuid::Uuid;
use crate::{
database::{self},
MnemoState,
error::CompositeError,
logs::{LogAction, LogEntry},
persons::Name,
@@ -21,12 +21,13 @@ use crate::{
};
pub async fn get_by_id(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(Quote::get_by_id(&conn, id)?).into_response())
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
Ok(Json(Quote::get_by_id(&mut conn, id).await?).into_response())
}
#[derive(Deserialize)]
@@ -38,37 +39,38 @@ pub struct QuoteLineForm {
#[derive(Deserialize)]
pub struct QuoteCreateForm {
pub lines: Vec<QuoteLineForm>,
pub timestamp: DateTime<FixedOffset>,
pub timestamp: NaiveDateTime,
pub context: Option<String>,
pub location: Option<String>,
pub public: bool,
}
pub async fn create(
State(state): State<MnemoState>,
headers: HeaderMap,
Json(form): Json<QuoteCreateForm>,
) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let mut conn = database::conn()?;
let tx = conn.transaction()?;
let mut tx = state.pool.begin().await?;
let u = User::authenticate(&mut tx, &headers).await?.required()?;
let lines = form
.lines
.into_iter()
.map(|l| Ok((l.content, Name::get_by_id(&tx, l.name_id)?)))
.collect::<Result<Vec<(String, Name)>, CompositeError>>()?;
let mut lines = Vec::with_capacity(form.lines.len());
for l in form.lines {
let name = Name::get_by_id(&mut tx, l.name_id).await?;
lines.push((l.content, name));
}
let q = Quote::create(
&tx,
&mut tx,
lines,
form.timestamp,
form.context,
form.location,
u.id,
form.public,
)?;
)
.await?;
LogEntry::new(&tx, u, LogAction::CreateQuote { id: q.id })?;
tx.commit()?;
LogEntry::new(&mut tx, u, LogAction::CreateQuote { id: q.id }).await?;
tx.commit().await?;
Ok((StatusCode::CREATED, Json(q)).into_response())
}

View File

@@ -1,13 +1,13 @@
use axum::{
Json,
extract::Path,
extract::{Path, State},
http::{HeaderMap, StatusCode},
response::{IntoResponse, Response},
};
use uuid::Uuid;
use crate::{
database::{self},
MnemoState,
error::CompositeError,
logs::{LogAction, LogEntry},
users::{
@@ -21,15 +21,17 @@ use crate::{
const CANT_REVOKE: &str = "You don't have permission to revoke this user's sessions.";
pub async fn get_by_id(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
let s = Session::get_by_id(&conn, id)?;
let mut conn = state.pool.acquire().await?;
let u = User::authenticate(&mut conn, &headers).await?.required()?;
let s = Session::get_by_id(&mut conn, id).await?;
match s.user_id == u.id
|| u.has_permission(&conn, Permission::ListOthersSessions)
|| u.has_permission(&mut conn, Permission::ListOthersSessions)
.await
.is_ok_and(|v| v)
{
true => Ok(Json(s).into_response()),
@@ -38,25 +40,29 @@ pub async fn get_by_id(
}
pub async fn revoke_by_id(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let mut conn = database::conn()?;
let tx = conn.transaction()?;
let mut tx = state.pool.begin().await?;
let u = User::authenticate(&mut tx, &headers).await?.required()?;
let mut s = Session::get_by_id(&tx, id)?;
let mut s = Session::get_by_id(&mut tx, id).await?;
match s.user_id == u.id
|| u.has_permission(&tx, Permission::RevokeOthersSessions)
|| u.has_permission(&mut tx, Permission::RevokeOthersSessions)
.await
.is_ok_and(|v| v)
{
true => {
s.revoke(&tx, Some(&u))?;
LogEntry::new(&tx, u, LogAction::ManuallyRevokeSession { id })?;
tx.commit()?;
s.revoke(&mut tx, Some(&u)).await?;
LogEntry::new(&mut tx, u, LogAction::ManuallyRevokeSession { id }).await?;
tx.commit().await?;
Ok(Json(s).into_response())
}
false => match u.has_permission(&tx, Permission::ListOthersSessions)? {
false => match u
.has_permission(&mut tx, Permission::ListOthersSessions)
.await?
{
true => Ok((StatusCode::FORBIDDEN, CANT_REVOKE).into_response()),
false => Err(SessionError::NoSessionWithId(id))?,
},

View File

@@ -1,6 +1,6 @@
use axum::{
Json,
extract::Path,
extract::{Path, State},
http::{HeaderMap, StatusCode},
response::{IntoResponse, Response},
};
@@ -8,7 +8,7 @@ use serde::Deserialize;
use uuid::Uuid;
use crate::{
database::{self},
MnemoState,
error::CompositeError,
logs::{LogAction, LogEntry},
tags::{Tag, TagName},
@@ -24,101 +24,112 @@ const CANT_DEL_TAGS: &str = "You don't have permission to delete tags.";
const CANT_RENAME_TAGS: &str = "You don't have permission to rename tags.";
const TAG_DELETED: &str = "Tag deleted successfully.";
pub async fn get_all(headers: HeaderMap) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(Tag::get_all(&conn)?).into_response())
pub async fn get_all(
State(state): State<MnemoState>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
Ok(Json(Tag::get_all(&mut conn).await?).into_response())
}
pub async fn get_by_id(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(Tag::get_by_id(&conn, id)?).into_response())
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
Ok(Json(Tag::get_by_id(&mut conn, id).await?).into_response())
}
pub async fn get_by_name(
State(state): State<MnemoState>,
Path(name): Path<TagName>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(Tag::get_by_name(&conn, name)?).into_response())
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
Ok(Json(Tag::get_by_name(&mut conn, name).await?).into_response())
}
#[derive(Deserialize)]
pub struct TagNameForm {
name: TagName,
}
pub async fn create(
State(state): State<MnemoState>,
headers: HeaderMap,
Json(form): Json<TagNameForm>,
) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let mut conn = database::conn()?;
let tx = conn.transaction()?;
let mut tx = state.pool.begin().await?;
let u = User::authenticate(&mut tx, &headers).await?.required()?;
if !u.has_permission(&tx, Permission::CreateTags)? {
if !u.has_permission(&mut tx, Permission::CreateTags).await? {
return Ok((StatusCode::FORBIDDEN, CANT_MAKE_TAGS).into_response());
}
let t = Tag::create(&tx, form.name)?;
let t = Tag::create(&mut tx, form.name).await?;
LogEntry::new(
&tx,
&mut tx,
u,
LogAction::CreateTag {
id: t.id,
name: t.name.as_str().to_string(),
},
)?;
tx.commit()?;
)
.await?;
tx.commit().await?;
Ok(Json(t).into_response())
}
pub async fn rename(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
Json(form): Json<TagNameForm>,
) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let mut conn = database::conn()?;
let tx = conn.transaction()?;
let mut tx = state.pool.begin().await?;
let u = User::authenticate(&mut tx, &headers).await?.required()?;
if !u.has_permission(&tx, Permission::RenameTags)? {
if !u.has_permission(&mut tx, Permission::RenameTags).await? {
return Ok((StatusCode::FORBIDDEN, CANT_RENAME_TAGS).into_response());
}
let mut tag = Tag::get_by_id(&tx, id)?;
let mut tag = Tag::get_by_id(&mut tx, id).await?;
let on = tag.name.as_str().to_string();
tag.rename(&tx, form.name)?;
tag.rename(&mut tx, form.name).await?;
LogEntry::new(
&tx,
&mut tx,
u,
LogAction::RenameTag {
id,
on,
nn: tag.name.as_str().to_string(),
},
)?;
tx.commit()?;
)
.await?;
tx.commit().await?;
Ok(Json(tag).into_response())
}
pub async fn delete(Path(id): Path<Uuid>, headers: HeaderMap) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let mut conn = database::conn()?;
let tx = conn.transaction()?;
pub async fn delete(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
let mut tx = state.pool.begin().await?;
let u = User::authenticate(&mut tx, &headers).await?.required()?;
if !u.has_permission(&tx, Permission::DeleteTags)? {
if !u.has_permission(&mut tx, Permission::DeleteTags).await? {
return Ok((StatusCode::FORBIDDEN, CANT_DEL_TAGS).into_response());
}
let t = Tag::get_by_id(&tx, id)?;
let t = Tag::get_by_id(&mut tx, id).await?;
let name = t.name.as_str().to_string();
t.delete(&tx)?;
LogEntry::new(&tx, u, LogAction::DeleteTag { id, name })?;
tx.commit()?;
t.delete(&mut tx).await?;
LogEntry::new(&mut tx, u, LogAction::DeleteTag { id, name }).await?;
tx.commit().await?;
Ok((StatusCode::OK, TAG_DELETED).into_response())
}

View File

@@ -1,6 +1,6 @@
use axum::{
Json,
extract::Path,
extract::{Path, State},
http::{HeaderMap, StatusCode},
response::{IntoResponse, Response},
};
@@ -8,7 +8,7 @@ use serde::Deserialize;
use uuid::Uuid;
use crate::{
database::{self},
MnemoState,
error::CompositeError,
logs::{LogAction, LogEntry},
users::{
@@ -25,32 +25,41 @@ const CANT_MANUALLY_MAKE_USERS: &str = "You don't have permission to manually cr
const HANDLE_CHANGED_SUCCESS: &str = "Handle changed successfully.";
const PASSW_CHANGED_SUCCESS: &str = "Password changed successfully.";
pub async fn get_me(headers: HeaderMap) -> Result<Response, CompositeError> {
Ok(Json(User::authenticate(&headers)?.required()?).into_response())
pub async fn get_me(
State(state): State<MnemoState>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
let mut conn = state.pool.acquire().await?;
Ok(Json(User::authenticate(&mut conn, &headers).await?.required()?).into_response())
}
pub async fn get_by_id(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(User::get_by_id(&conn, id)?).into_response())
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
Ok(Json(User::get_by_id(&mut conn, id).await?).into_response())
}
pub async fn get_by_handle(
State(state): State<MnemoState>,
Path(handle): Path<UserHandle>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(User::get_by_handle(&conn, handle)?).into_response())
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
Ok(Json(User::get_by_handle(&mut conn, handle).await?).into_response())
}
pub async fn get_all(headers: HeaderMap) -> Result<Response, CompositeError> {
User::authenticate(&headers)?.required()?;
let conn = database::conn()?;
Ok(Json(User::get_all(&conn)?).into_response())
pub async fn get_all(
State(state): State<MnemoState>,
headers: HeaderMap,
) -> Result<Response, CompositeError> {
let mut conn = state.pool.acquire().await?;
User::authenticate(&mut conn, &headers).await?.required()?;
Ok(Json(User::get_all(&mut conn).await?).into_response())
}
#[derive(Deserialize)]
@@ -58,60 +67,69 @@ pub struct HandleForm {
handle: UserHandle,
}
pub async fn create(
State(state): State<MnemoState>,
headers: HeaderMap,
Json(form): Json<HandleForm>,
) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let mut conn = database::conn()?;
let tx = conn.transaction()?;
let mut tx = state.pool.begin().await?;
let u = User::authenticate(&mut tx, &headers).await?.required()?;
if !u.has_permission(&tx, Permission::ManuallyCreateUsers)? {
if !u
.has_permission(&mut tx, Permission::ManuallyCreateUsers)
.await?
{
return Ok((StatusCode::FORBIDDEN, CANT_MANUALLY_MAKE_USERS).into_response());
}
let nu = User::create(&tx, form.handle)?;
let nu = User::create(&mut tx, form.handle).await?;
LogEntry::new(
&tx,
&mut tx,
u,
LogAction::CreateUser {
id: nu.id,
handle: nu.handle.as_str().to_string(),
},
)?;
tx.commit()?;
)
.await?;
tx.commit().await?;
Ok(Json(nu).into_response())
}
pub async fn change_handle(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
Json(form): Json<HandleForm>,
) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let mut conn = database::conn()?;
let tx = conn.transaction()?;
let mut tx = state.pool.begin().await?;
let u = User::authenticate(&mut tx, &headers).await?.required()?;
let mut target = if u.id == id {
u.clone()
} else {
if !u.has_permission(&tx, Permission::ChangeOthersHandles)? {
if !u
.has_permission(&mut tx, Permission::ChangeOthersHandles)
.await?
{
return Ok((StatusCode::FORBIDDEN, CANT_CHANGE_OTHERS_HANDLE).into_response());
}
User::get_by_id(&tx, id)?
User::get_by_id(&mut tx, id).await?
};
let old_handle = target.handle.as_str().to_string();
target.set_handle(&tx, form.handle)?;
target.set_handle(&mut tx, form.handle).await?;
LogEntry::new(
&tx,
&mut tx,
u,
LogAction::ChangeUserHandle {
id: target.id,
old: old_handle,
new: target.handle.as_str().to_string(),
},
)?;
tx.commit()?;
)
.await?;
tx.commit().await?;
Ok(HANDLE_CHANGED_SUCCESS.into_response())
}
@@ -121,30 +139,34 @@ pub struct ChangePasswordForm {
password: String,
}
pub async fn change_password(
State(state): State<MnemoState>,
Path(id): Path<Uuid>,
headers: HeaderMap,
Json(form): Json<ChangePasswordForm>,
) -> Result<Response, CompositeError> {
let u = User::authenticate(&headers)?.required()?;
let mut conn = database::conn()?;
let tx = conn.transaction()?;
let mut tx = state.pool.begin().await?;
let u = User::authenticate(&mut tx, &headers).await?.required()?;
let mut target = if u.id == id {
u.clone()
} else {
if !u.has_permission(&tx, Permission::ChangeOthersPasswords)? {
if !u
.has_permission(&mut tx, Permission::ChangeOthersPasswords)
.await?
{
return Ok((StatusCode::FORBIDDEN, CANT_CHANGE_OTHERS_PASSW).into_response());
}
User::get_by_id(&tx, id)?
User::get_by_id(&mut tx, id).await?
};
target.set_password(&tx, Some(&form.password))?;
target.set_password(&mut tx, Some(&form.password)).await?;
LogEntry::new(
&tx,
&mut tx,
u,
LogAction::ManuallyChangeUsersPassword { id: target.id },
)?;
tx.commit()?;
)
.await?;
tx.commit().await?;
Ok(PASSW_CHANGED_SUCCESS.into_response())
}