make transactions higher level (pass them everywhere)
This commit is contained in:
@@ -5,13 +5,16 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::users::{
|
use crate::{
|
||||||
User,
|
database,
|
||||||
auth::{
|
users::{
|
||||||
AuthError, COOKIE_NAME, SessionAuthRequired, SessionAuthenticate, UserAuthRequired,
|
User,
|
||||||
implementation::authenticate_via_credentials,
|
auth::{
|
||||||
|
AuthError, COOKIE_NAME, SessionAuthRequired, SessionAuthenticate, UserAuthRequired,
|
||||||
|
implementation::authenticate_via_credentials,
|
||||||
|
},
|
||||||
|
sessions::Session,
|
||||||
},
|
},
|
||||||
sessions::Session,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -22,7 +25,8 @@ pub struct LoginForm {
|
|||||||
|
|
||||||
fn login_common(creds: LoginForm) -> Result<(String, String), AuthError> {
|
fn login_common(creds: LoginForm) -> Result<(String, String), AuthError> {
|
||||||
let u = authenticate_via_credentials(&creds.handle, &creds.password)?.required()?;
|
let u = authenticate_via_credentials(&creds.handle, &creds.password)?.required()?;
|
||||||
let (_, token) = Session::new_for_user(&u)?;
|
let conn = database::conn()?;
|
||||||
|
let (_, token) = Session::new_for_user(&conn, &u)?;
|
||||||
let secure = match cfg!(debug_assertions) {
|
let secure = match cfg!(debug_assertions) {
|
||||||
false => "; Secure",
|
false => "; Secure",
|
||||||
true => "",
|
true => "",
|
||||||
@@ -49,13 +53,15 @@ pub async fn login_form(Form(creds): Form<LoginForm>) -> Result<Response, AuthEr
|
|||||||
|
|
||||||
pub async fn logout(headers: HeaderMap) -> Result<Response, AuthError> {
|
pub async fn logout(headers: HeaderMap) -> Result<Response, AuthError> {
|
||||||
let mut s = Session::authenticate(&headers)?.required()?;
|
let mut s = Session::authenticate(&headers)?.required()?;
|
||||||
s.revoke(Some(&User::get_by_id(s.user_id)?))?;
|
let conn = database::conn()?;
|
||||||
|
s.revoke(&conn, Some(&User::get_by_id(&conn, s.user_id)?))?;
|
||||||
let cookie = format!("{COOKIE_NAME}=revoking; Path=/; HttpOnly; Max-Age=0");
|
let cookie = format!("{COOKIE_NAME}=revoking; Path=/; HttpOnly; Max-Age=0");
|
||||||
Ok(([(header::SET_COOKIE, cookie)], "Logged out!").into_response())
|
Ok(([(header::SET_COOKIE, cookie)], "Logged out!").into_response())
|
||||||
}
|
}
|
||||||
pub async fn logout_form(headers: HeaderMap) -> Result<Response, AuthError> {
|
pub async fn logout_form(headers: HeaderMap) -> Result<Response, AuthError> {
|
||||||
let mut s = Session::authenticate(&headers)?.required()?;
|
let mut s = Session::authenticate(&headers)?.required()?;
|
||||||
s.revoke(Some(&User::get_by_id(s.user_id)?))?;
|
let conn = database::conn()?;
|
||||||
|
s.revoke(&conn, Some(&User::get_by_id(&conn, s.user_id)?))?;
|
||||||
let cookie = format!("{COOKIE_NAME}=revoking; Path=/; HttpOnly; Max-Age=0");
|
let cookie = format!("{COOKIE_NAME}=revoking; Path=/; HttpOnly; Max-Age=0");
|
||||||
Ok(([(header::SET_COOKIE, cookie)], Redirect::to("/")).into_response())
|
Ok(([(header::SET_COOKIE, cookie)], Redirect::to("/")).into_response())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,5 +85,5 @@ composite_from!(
|
|||||||
PersonError,
|
PersonError,
|
||||||
QuoteError,
|
QuoteError,
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
RedirectViaError
|
RedirectViaError,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::CompositeError,
|
api::CompositeError,
|
||||||
|
database,
|
||||||
persons::{Name, Person},
|
persons::{Name, Person},
|
||||||
users::{
|
users::{
|
||||||
User,
|
User,
|
||||||
@@ -21,21 +22,24 @@ pub const CANT_SET_PRIMARYNAME: &str = "You don't have permission to swap primar
|
|||||||
|
|
||||||
pub async fn get_all(headers: HeaderMap) -> Result<Response, CompositeError> {
|
pub async fn get_all(headers: HeaderMap) -> Result<Response, CompositeError> {
|
||||||
User::authenticate(&headers)?.required()?;
|
User::authenticate(&headers)?.required()?;
|
||||||
Ok(Json(Person::get_all()?).into_response())
|
let conn = database::conn()?;
|
||||||
|
Ok(Json(Person::get_all(&conn)?).into_response())
|
||||||
}
|
}
|
||||||
pub async fn get_by_id(
|
pub async fn get_by_id(
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
User::authenticate(&headers)?.required()?;
|
User::authenticate(&headers)?.required()?;
|
||||||
Ok(Json(Person::get_by_id(id)?).into_response())
|
let conn = database::conn()?;
|
||||||
|
Ok(Json(Person::get_by_id(&conn, id)?).into_response())
|
||||||
}
|
}
|
||||||
pub async fn pid_names(
|
pub async fn pid_names(
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
User::authenticate(&headers)?.required()?;
|
User::authenticate(&headers)?.required()?;
|
||||||
Ok(Json(Person::get_by_id(id)?.get_all_names()?).into_response())
|
let conn = database::conn()?;
|
||||||
|
Ok(Json(Person::get_by_id(&conn, id)?.get_all_names(&conn)?).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -48,7 +52,8 @@ pub async fn create(
|
|||||||
Json(form): Json<PersonNameForm>,
|
Json(form): Json<PersonNameForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
let p = Person::create(form.name, u.id)?;
|
let conn = database::conn()?;
|
||||||
|
let p = Person::create(&conn, form.name, u.id)?;
|
||||||
Ok((StatusCode::CREATED, Json(p)).into_response())
|
Ok((StatusCode::CREATED, Json(p)).into_response())
|
||||||
}
|
}
|
||||||
pub async fn add_name(
|
pub async fn add_name(
|
||||||
@@ -57,27 +62,30 @@ pub async fn add_name(
|
|||||||
Json(form): Json<PersonNameForm>,
|
Json(form): Json<PersonNameForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
let p = Person::get_by_id(id)?;
|
let conn = database::conn()?;
|
||||||
let n = p.add_name(form.name, u.id)?;
|
let p = Person::get_by_id(&conn, id)?;
|
||||||
|
let n = p.add_name(&conn, form.name, u.id)?;
|
||||||
|
|
||||||
Ok((StatusCode::CREATED, Json(n)).into_response())
|
Ok((StatusCode::CREATED, Json(n)).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn n_by_id(Path(id): Path<Uuid>, headers: HeaderMap) -> Result<Response, CompositeError> {
|
pub async fn n_by_id(Path(id): Path<Uuid>, headers: HeaderMap) -> Result<Response, CompositeError> {
|
||||||
User::authenticate(&headers)?.required()?;
|
User::authenticate(&headers)?.required()?;
|
||||||
Ok(Json(Name::get_by_id(id)?).into_response())
|
let conn = database::conn()?;
|
||||||
|
Ok(Json(Name::get_by_id(&conn, id)?).into_response())
|
||||||
}
|
}
|
||||||
pub async fn n_setprimary(
|
pub async fn n_setprimary(
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
if !u.has_permission(Permission::ChangePersonPrimaryName)? {
|
let conn = database::conn()?;
|
||||||
|
if !u.has_permission(&conn, Permission::ChangePersonPrimaryName)? {
|
||||||
return Ok((StatusCode::FORBIDDEN, CANT_SET_PRIMARYNAME).into_response());
|
return Ok((StatusCode::FORBIDDEN, CANT_SET_PRIMARYNAME).into_response());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut n = Name::get_by_id(id)?;
|
let mut n = Name::get_by_id(&conn, id)?;
|
||||||
n.set_primary()?;
|
n.set_primary(&conn)?;
|
||||||
n.is_primary = true;
|
n.is_primary = true;
|
||||||
Ok(Json(n).into_response())
|
Ok(Json(n).into_response())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::CompositeError,
|
api::CompositeError,
|
||||||
|
database,
|
||||||
persons::Name,
|
persons::Name,
|
||||||
quotes::Quote,
|
quotes::Quote,
|
||||||
users::{
|
users::{
|
||||||
@@ -23,7 +24,8 @@ pub async fn get_by_id(
|
|||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
User::authenticate(&headers)?.required()?;
|
User::authenticate(&headers)?.required()?;
|
||||||
Ok(Json(Quote::get_by_id(id)?).into_response())
|
let conn = database::conn()?;
|
||||||
|
Ok(Json(Quote::get_by_id(&conn, id)?).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -46,14 +48,16 @@ pub async fn create(
|
|||||||
Json(form): Json<QuoteCreateForm>,
|
Json(form): Json<QuoteCreateForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
|
let conn = database::conn()?;
|
||||||
|
|
||||||
let lines = form
|
let lines = form
|
||||||
.lines
|
.lines
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|l| Ok((l.content, Name::get_by_id(l.name_id)?)))
|
.map(|l| Ok((l.content, Name::get_by_id(&conn, l.name_id)?)))
|
||||||
.collect::<Result<Vec<(String, Name)>, CompositeError>>()?;
|
.collect::<Result<Vec<(String, Name)>, CompositeError>>()?;
|
||||||
|
|
||||||
let q = Quote::create(
|
let q = Quote::create(
|
||||||
|
&conn,
|
||||||
lines,
|
lines,
|
||||||
form.timestamp,
|
form.timestamp,
|
||||||
form.context,
|
form.context,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::CompositeError,
|
api::CompositeError,
|
||||||
|
database,
|
||||||
users::{
|
users::{
|
||||||
User,
|
User,
|
||||||
auth::{UserAuthRequired, UserAuthenticate},
|
auth::{UserAuthRequired, UserAuthenticate},
|
||||||
@@ -23,10 +24,11 @@ pub async fn get_by_id(
|
|||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
let s = Session::get_by_id(id)?;
|
let conn = database::conn()?;
|
||||||
|
let s = Session::get_by_id(&conn, id)?;
|
||||||
|
|
||||||
match s.user_id == u.id
|
match s.user_id == u.id
|
||||||
|| u.has_permission(Permission::ListOthersSessions)
|
|| u.has_permission(&conn, Permission::ListOthersSessions)
|
||||||
.is_ok_and(|v| v)
|
.is_ok_and(|v| v)
|
||||||
{
|
{
|
||||||
true => Ok(Json(s).into_response()),
|
true => Ok(Json(s).into_response()),
|
||||||
@@ -39,17 +41,18 @@ pub async fn revoke_by_id(
|
|||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
let mut s = Session::get_by_id(id)?;
|
let conn = database::conn()?;
|
||||||
|
let mut s = Session::get_by_id(&conn, id)?;
|
||||||
|
|
||||||
match s.user_id == u.id
|
match s.user_id == u.id
|
||||||
|| u.has_permission(Permission::RevokeOthersSessions)
|
|| u.has_permission(&conn, Permission::RevokeOthersSessions)
|
||||||
.is_ok_and(|v| v)
|
.is_ok_and(|v| v)
|
||||||
{
|
{
|
||||||
true => {
|
true => {
|
||||||
s.revoke(Some(&u))?;
|
s.revoke(&conn, Some(&u))?;
|
||||||
Ok(Json(s).into_response())
|
Ok(Json(s).into_response())
|
||||||
}
|
}
|
||||||
false => match u.has_permission(Permission::ListOthersSessions)? {
|
false => match u.has_permission(&conn, Permission::ListOthersSessions)? {
|
||||||
true => Ok((StatusCode::FORBIDDEN, CANT_REVOKE).into_response()),
|
true => Ok((StatusCode::FORBIDDEN, CANT_REVOKE).into_response()),
|
||||||
false => Err(SessionError::NoSessionWithId(id))?,
|
false => Err(SessionError::NoSessionWithId(id))?,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::CompositeError,
|
api::CompositeError,
|
||||||
|
database,
|
||||||
tags::{Tag, TagName},
|
tags::{Tag, TagName},
|
||||||
users::{
|
users::{
|
||||||
User,
|
User,
|
||||||
@@ -24,7 +25,8 @@ const TAG_DELETED: &str = "Tag deleted successfully.";
|
|||||||
|
|
||||||
pub async fn get_all(headers: HeaderMap) -> Result<Response, CompositeError> {
|
pub async fn get_all(headers: HeaderMap) -> Result<Response, CompositeError> {
|
||||||
User::authenticate(&headers)?.required()?;
|
User::authenticate(&headers)?.required()?;
|
||||||
Ok(Json(Tag::get_all()?).into_response())
|
let conn = database::conn()?;
|
||||||
|
Ok(Json(Tag::get_all(&conn)?).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_by_id(
|
pub async fn get_by_id(
|
||||||
@@ -32,7 +34,8 @@ pub async fn get_by_id(
|
|||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
User::authenticate(&headers)?.required()?;
|
User::authenticate(&headers)?.required()?;
|
||||||
Ok(Json(Tag::get_by_id(id)?).into_response())
|
let conn = database::conn()?;
|
||||||
|
Ok(Json(Tag::get_by_id(&conn, id)?).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_by_name(
|
pub async fn get_by_name(
|
||||||
@@ -40,7 +43,8 @@ pub async fn get_by_name(
|
|||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
User::authenticate(&headers)?.required()?;
|
User::authenticate(&headers)?.required()?;
|
||||||
Ok(Json(Tag::get_by_name(name)?).into_response())
|
let conn = database::conn()?;
|
||||||
|
Ok(Json(Tag::get_by_name(&conn, name)?).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -52,10 +56,11 @@ pub async fn create(
|
|||||||
Json(form): Json<TagNameForm>,
|
Json(form): Json<TagNameForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
if !u.has_permission(Permission::CreateTags)? {
|
let conn = database::conn()?;
|
||||||
|
if !u.has_permission(&conn, Permission::CreateTags)? {
|
||||||
return Ok((StatusCode::FORBIDDEN, CANT_MAKE_TAGS).into_response());
|
return Ok((StatusCode::FORBIDDEN, CANT_MAKE_TAGS).into_response());
|
||||||
}
|
}
|
||||||
Ok(Json(Tag::create(form.name)?).into_response())
|
Ok(Json(Tag::create(&conn, form.name)?).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn rename(
|
pub async fn rename(
|
||||||
@@ -64,19 +69,21 @@ pub async fn rename(
|
|||||||
Json(form): Json<TagNameForm>,
|
Json(form): Json<TagNameForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
if !u.has_permission(Permission::RenameTags)? {
|
let conn = database::conn()?;
|
||||||
|
if !u.has_permission(&conn, Permission::RenameTags)? {
|
||||||
return Ok((StatusCode::FORBIDDEN, CANT_RENAME_TAGS).into_response());
|
return Ok((StatusCode::FORBIDDEN, CANT_RENAME_TAGS).into_response());
|
||||||
}
|
}
|
||||||
let mut tag = Tag::get_by_id(id)?;
|
let mut tag = Tag::get_by_id(&conn, id)?;
|
||||||
tag.rename(form.name)?;
|
tag.rename(&conn, form.name)?;
|
||||||
Ok(Json(tag).into_response())
|
Ok(Json(tag).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete(Path(id): Path<Uuid>, headers: HeaderMap) -> Result<Response, CompositeError> {
|
pub async fn delete(Path(id): Path<Uuid>, headers: HeaderMap) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
if !u.has_permission(Permission::DeleteTags)? {
|
let conn = database::conn()?;
|
||||||
|
if !u.has_permission(&conn, Permission::DeleteTags)? {
|
||||||
return Ok((StatusCode::FORBIDDEN, CANT_DEL_TAGS).into_response());
|
return Ok((StatusCode::FORBIDDEN, CANT_DEL_TAGS).into_response());
|
||||||
}
|
}
|
||||||
Tag::get_by_id(id)?.delete()?;
|
Tag::get_by_id(&conn, id)?.delete(&conn)?;
|
||||||
Ok((StatusCode::OK, TAG_DELETED).into_response())
|
Ok((StatusCode::OK, TAG_DELETED).into_response())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::CompositeError,
|
api::CompositeError,
|
||||||
|
database,
|
||||||
users::{
|
users::{
|
||||||
User,
|
User,
|
||||||
auth::{UserAuthRequired, UserAuthenticate},
|
auth::{UserAuthRequired, UserAuthenticate},
|
||||||
@@ -32,7 +33,8 @@ pub async fn get_by_id(
|
|||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
User::authenticate(&headers)?.required()?;
|
User::authenticate(&headers)?.required()?;
|
||||||
Ok(Json(User::get_by_id(id)?).into_response())
|
let conn = database::conn()?;
|
||||||
|
Ok(Json(User::get_by_id(&conn, id)?).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_by_handle(
|
pub async fn get_by_handle(
|
||||||
@@ -40,12 +42,14 @@ pub async fn get_by_handle(
|
|||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
User::authenticate(&headers)?.required()?;
|
User::authenticate(&headers)?.required()?;
|
||||||
Ok(Json(User::get_by_handle(handle)?).into_response())
|
let conn = database::conn()?;
|
||||||
|
Ok(Json(User::get_by_handle(&conn, handle)?).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_all(headers: HeaderMap) -> Result<Response, CompositeError> {
|
pub async fn get_all(headers: HeaderMap) -> Result<Response, CompositeError> {
|
||||||
User::authenticate(&headers)?.required()?;
|
User::authenticate(&headers)?.required()?;
|
||||||
Ok(Json(User::get_all()?).into_response())
|
let conn = database::conn()?;
|
||||||
|
Ok(Json(User::get_all(&conn)?).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -57,10 +61,11 @@ pub async fn create(
|
|||||||
Json(form): Json<HandleForm>,
|
Json(form): Json<HandleForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
if !u.has_permission(Permission::ManuallyCreateUsers)? {
|
let conn = database::conn()?;
|
||||||
|
if !u.has_permission(&conn, Permission::ManuallyCreateUsers)? {
|
||||||
return Ok((StatusCode::FORBIDDEN, CANT_MANUALLY_MAKE_USERS).into_response());
|
return Ok((StatusCode::FORBIDDEN, CANT_MANUALLY_MAKE_USERS).into_response());
|
||||||
}
|
}
|
||||||
Ok(Json(User::create(form.handle)?).into_response())
|
Ok(Json(User::create(&conn, form.handle)?).into_response())
|
||||||
}
|
}
|
||||||
pub async fn change_handle(
|
pub async fn change_handle(
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
@@ -68,15 +73,17 @@ pub async fn change_handle(
|
|||||||
Json(form): Json<HandleForm>,
|
Json(form): Json<HandleForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
|
let conn = database::conn()?;
|
||||||
|
|
||||||
let mut target = if u.id == id {
|
let mut target = if u.id == id {
|
||||||
u
|
u
|
||||||
} else {
|
} else {
|
||||||
if !u.has_permission(Permission::ChangeOthersHandles)? {
|
if !u.has_permission(&conn, Permission::ChangeOthersHandles)? {
|
||||||
return Ok((StatusCode::FORBIDDEN, CANT_CHANGE_OTHERS_HANDLE).into_response());
|
return Ok((StatusCode::FORBIDDEN, CANT_CHANGE_OTHERS_HANDLE).into_response());
|
||||||
}
|
}
|
||||||
User::get_by_id(id)?
|
User::get_by_id(&conn, id)?
|
||||||
};
|
};
|
||||||
target.set_handle(form.handle)?;
|
target.set_handle(&conn, form.handle)?;
|
||||||
Ok(HANDLE_CHANGED_SUCCESS.into_response())
|
Ok(HANDLE_CHANGED_SUCCESS.into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,14 +97,15 @@ pub async fn change_password(
|
|||||||
Json(form): Json<ChangePasswordForm>,
|
Json(form): Json<ChangePasswordForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
|
let conn = database::conn()?;
|
||||||
let mut target = if u.id == id {
|
let mut target = if u.id == id {
|
||||||
u
|
u
|
||||||
} else {
|
} else {
|
||||||
if !u.has_permission(Permission::ChangeOthersPasswords)? {
|
if !u.has_permission(&conn, Permission::ChangeOthersPasswords)? {
|
||||||
return Ok((StatusCode::FORBIDDEN, CANT_CHANGE_OTHERS_PASSW).into_response());
|
return Ok((StatusCode::FORBIDDEN, CANT_CHANGE_OTHERS_PASSW).into_response());
|
||||||
}
|
}
|
||||||
User::get_by_id(id)?
|
User::get_by_id(&conn, id)?
|
||||||
};
|
};
|
||||||
target.set_password(Some(&form.password))?;
|
target.set_password(&conn, Some(&form.password))?;
|
||||||
Ok(PASSW_CHANGED_SUCCESS.into_response())
|
Ok(PASSW_CHANGED_SUCCESS.into_response())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ impl IntoResponse for DatabaseError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn conn() -> Result<Connection, rusqlite::Error> {
|
pub fn conn() -> Result<Connection, DatabaseError> {
|
||||||
let conn = Connection::open(&*DB_URL)?;
|
let conn = Connection::open(&*DB_URL)?;
|
||||||
for pragma in CONNECTION_PRAGMAS {
|
for pragma in CONNECTION_PRAGMAS {
|
||||||
conn.query_row(pragma, (), |_| Ok(())).optional()?;
|
conn.query_row(pragma, (), |_| Ok(())).optional()?;
|
||||||
|
|||||||
15
src/logs.rs
15
src/logs.rs
@@ -1,11 +1,9 @@
|
|||||||
|
use rusqlite::Connection;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum::IntoStaticStr;
|
use strum::IntoStaticStr;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{database::DatabaseError, users::User};
|
||||||
database::{self, DatabaseError},
|
|
||||||
users::User,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LogEntry {
|
pub struct LogEntry {
|
||||||
@@ -15,13 +13,12 @@ pub struct LogEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LogEntry {
|
impl LogEntry {
|
||||||
pub fn new(actor: User, data: LogAction) -> Result<LogEntry, DatabaseError> {
|
pub fn new(conn: &Connection, actor: User, data: LogAction) -> Result<LogEntry, DatabaseError> {
|
||||||
let log = LogEntry {
|
let log = LogEntry {
|
||||||
id: Uuid::now_v7(),
|
id: Uuid::now_v7(),
|
||||||
actor,
|
actor,
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
let conn = database::conn()?;
|
|
||||||
let actiontype: &'static str = (&log.data).into();
|
let actiontype: &'static str = (&log.data).into();
|
||||||
let payload = serde_json::to_string(&log.data).unwrap();
|
let payload = serde_json::to_string(&log.data).unwrap();
|
||||||
conn.prepare(
|
conn.prepare(
|
||||||
@@ -36,14 +33,14 @@ impl LogEntry {
|
|||||||
))?;
|
))?;
|
||||||
Ok(log)
|
Ok(log)
|
||||||
}
|
}
|
||||||
pub fn get_all() -> Result<Vec<LogEntry>, DatabaseError> {
|
pub fn get_all(conn: &Connection) -> Result<Vec<LogEntry>, DatabaseError> {
|
||||||
Ok(database::conn()?
|
Ok(conn
|
||||||
.prepare("SELECT id, actor, target, actiontype, payload FROM logs ORDER BY id DESC")?
|
.prepare("SELECT id, actor, target, actiontype, payload FROM logs ORDER BY id DESC")?
|
||||||
.query_map((), |r| {
|
.query_map((), |r| {
|
||||||
let payload: String = r.get(4)?;
|
let payload: String = r.get(4)?;
|
||||||
Ok(LogEntry {
|
Ok(LogEntry {
|
||||||
id: r.get(0)?,
|
id: r.get(0)?,
|
||||||
actor: User::get_by_id(r.get(1)?).unwrap(),
|
actor: User::get_by_id(conn, r.get(1)?).unwrap(),
|
||||||
data: serde_json::from_str(&payload).unwrap(),
|
data: serde_json::from_str(&payload).unwrap(),
|
||||||
})
|
})
|
||||||
})?
|
})?
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ use axum::{
|
|||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use rusqlite::OptionalExtension;
|
use rusqlite::{Connection, OptionalExtension};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::database::{self, DatabaseError};
|
use crate::database::DatabaseError;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
@@ -39,13 +39,11 @@ pub enum PersonError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Person {
|
impl Person {
|
||||||
pub fn total_count() -> Result<i64, PersonError> {
|
pub fn total_count(conn: &Connection) -> Result<i64, PersonError> {
|
||||||
let conn = database::conn()?;
|
Ok(conn.query_row("SELECT COUNT(*) FROM persons", (), |r| r.get(0))?)
|
||||||
let count: i64 = conn.query_row("SELECT COUNT(*) FROM persons", (), |r| r.get(0))?;
|
|
||||||
Ok(count)
|
|
||||||
}
|
}
|
||||||
pub fn get_all() -> Result<Vec<Person>, PersonError> {
|
pub fn get_all(conn: &Connection) -> Result<Vec<Person>, PersonError> {
|
||||||
Ok(database::conn()?
|
Ok(conn
|
||||||
.prepare("SELECT p.id, p.created_by, n.name FROM persons p JOIN names n ON p.id = n.person_id AND n.is_primary = 1")?
|
.prepare("SELECT p.id, p.created_by, n.name FROM persons p JOIN names n ON p.id = n.person_id AND n.is_primary = 1")?
|
||||||
.query_map((), |r| {
|
.query_map((), |r| {
|
||||||
Ok(Person {
|
Ok(Person {
|
||||||
@@ -57,8 +55,8 @@ impl Person {
|
|||||||
.collect::<Result<Vec<Person>, _>>()?)
|
.collect::<Result<Vec<Person>, _>>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_by_id(id: Uuid) -> Result<Person, PersonError> {
|
pub fn get_by_id(conn: &Connection, id: Uuid) -> Result<Person, PersonError> {
|
||||||
let res = database::conn()?
|
let res = conn
|
||||||
.prepare("SELECT p.created_by, n.name FROM persons p JOIN names n ON p.id = n.person_id AND n.is_primary = 1 WHERE p.id = ?1")?
|
.prepare("SELECT p.created_by, n.name FROM persons p JOIN names n ON p.id = n.person_id AND n.is_primary = 1 WHERE p.id = ?1")?
|
||||||
.query_one((&id,), |r| {
|
.query_one((&id,), |r| {
|
||||||
Ok(Person {
|
Ok(Person {
|
||||||
@@ -74,8 +72,8 @@ impl Person {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_in_quote_count(&self) -> Result<i64, PersonError> {
|
pub fn get_in_quote_count(&self, conn: &Connection) -> Result<i64, PersonError> {
|
||||||
Ok(database::conn()?
|
Ok(conn
|
||||||
.prepare(
|
.prepare(
|
||||||
r#"
|
r#"
|
||||||
SELECT COUNT(DISTINCT l.quote_id) AS quote_count
|
SELECT COUNT(DISTINCT l.quote_id) AS quote_count
|
||||||
@@ -86,8 +84,8 @@ impl Person {
|
|||||||
.query_one((self.id,), |r| Ok(r.get(0)?))?)
|
.query_one((self.id,), |r| Ok(r.get(0)?))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_names(&self) -> Result<Vec<Name>, PersonError> {
|
pub fn get_all_names(&self, conn: &Connection) -> Result<Vec<Name>, PersonError> {
|
||||||
Ok(database::conn()?
|
Ok(conn
|
||||||
.prepare("SELECT id, is_primary, person_id, created_by, name FROM names WHERE person_id = ?1")?
|
.prepare("SELECT id, is_primary, person_id, created_by, name FROM names WHERE person_id = ?1")?
|
||||||
.query_map((&self.id,), |r| {
|
.query_map((&self.id,), |r| {
|
||||||
Ok(Name {
|
Ok(Name {
|
||||||
@@ -101,10 +99,14 @@ impl Person {
|
|||||||
.collect::<Result<Vec<Name>, _>>()?)
|
.collect::<Result<Vec<Name>, _>>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_name(&self, name: String, created_by: Uuid) -> Result<Name, PersonError> {
|
pub fn add_name(
|
||||||
|
&self,
|
||||||
|
conn: &Connection,
|
||||||
|
name: String,
|
||||||
|
created_by: Uuid,
|
||||||
|
) -> Result<Name, PersonError> {
|
||||||
let id = Uuid::now_v7();
|
let id = Uuid::now_v7();
|
||||||
database::conn()?
|
conn.prepare("INSERT INTO names VALUES (?1, ?2, ?3, ?4, ?5)")?
|
||||||
.prepare("INSERT INTO names VALUES (?1, ?2, ?3, ?4, ?5)")?
|
|
||||||
.execute((id, 0, self.id, created_by, &name))?;
|
.execute((id, 0, self.id, created_by, &name))?;
|
||||||
Ok(Name {
|
Ok(Name {
|
||||||
id,
|
id,
|
||||||
@@ -115,18 +117,18 @@ impl Person {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(primary_name: String, created_by: Uuid) -> Result<Person, PersonError> {
|
pub fn create(
|
||||||
|
conn: &Connection,
|
||||||
|
primary_name: String,
|
||||||
|
created_by: Uuid,
|
||||||
|
) -> Result<Person, PersonError> {
|
||||||
let person_id = Uuid::now_v7();
|
let person_id = Uuid::now_v7();
|
||||||
let name_id = Uuid::now_v7();
|
let name_id = Uuid::now_v7();
|
||||||
|
|
||||||
let conn = database::conn()?;
|
|
||||||
conn.execute("BEGIN TRANSACTION", ())?;
|
|
||||||
|
|
||||||
conn.prepare("INSERT INTO persons(id, created_by) VALUES (?1, ?2)")?
|
conn.prepare("INSERT INTO persons(id, created_by) VALUES (?1, ?2)")?
|
||||||
.execute((person_id, created_by))?;
|
.execute((person_id, created_by))?;
|
||||||
conn.prepare("INSERT INTO names VALUES (?1, ?2, ?3, ?4, ?5)")?
|
conn.prepare("INSERT INTO names VALUES (?1, ?2, ?3, ?4, ?5)")?
|
||||||
.execute((name_id, 1, person_id, created_by, &primary_name))?;
|
.execute((name_id, 1, person_id, created_by, &primary_name))?;
|
||||||
conn.execute("COMMIT", ())?;
|
|
||||||
|
|
||||||
Ok(Person {
|
Ok(Person {
|
||||||
id: person_id,
|
id: person_id,
|
||||||
@@ -137,8 +139,8 @@ impl Person {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Name {
|
impl Name {
|
||||||
pub fn get_by_id(id: Uuid) -> Result<Name, PersonError> {
|
pub fn get_by_id(conn: &Connection, id: Uuid) -> Result<Name, PersonError> {
|
||||||
let res = database::conn()?
|
let res = conn
|
||||||
.prepare("SELECT id, is_primary, person_id, created_by, name FROM names WHERE id = ?1")?
|
.prepare("SELECT id, is_primary, person_id, created_by, name FROM names WHERE id = ?1")?
|
||||||
.query_one((&id,), |r| {
|
.query_one((&id,), |r| {
|
||||||
Ok(Name {
|
Ok(Name {
|
||||||
@@ -155,20 +157,15 @@ impl Name {
|
|||||||
None => Err(PersonError::NoNameWithId(id)),
|
None => Err(PersonError::NoNameWithId(id)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn set_primary(&mut self) -> Result<(), PersonError> {
|
pub fn set_primary(&mut self, conn: &Connection) -> Result<(), PersonError> {
|
||||||
if self.is_primary {
|
if self.is_primary {
|
||||||
return Err(PersonError::AlreadyPrimary);
|
return Err(PersonError::AlreadyPrimary);
|
||||||
}
|
}
|
||||||
|
|
||||||
let conn = database::conn()?;
|
|
||||||
conn.execute("BEGIN TRANSACTION", ())?;
|
|
||||||
|
|
||||||
conn.prepare("UPDATE names SET is_primary = 0 WHERE person_id = ?1 AND is_primary = 1")?
|
conn.prepare("UPDATE names SET is_primary = 0 WHERE person_id = ?1 AND is_primary = 1")?
|
||||||
.execute((&self.person_id,))?;
|
.execute((&self.person_id,))?;
|
||||||
conn.prepare("UPDATE names SET is_primary = 1 WHERE id = ?1")?
|
conn.prepare("UPDATE names SET is_primary = 1 WHERE id = ?1")?
|
||||||
.execute((&self.id,))?;
|
.execute((&self.id,))?;
|
||||||
|
|
||||||
conn.execute("COMMIT", ())?;
|
|
||||||
self.is_primary = true;
|
self.is_primary = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
use axum::{http::StatusCode, response::IntoResponse};
|
use axum::{http::StatusCode, response::IntoResponse};
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
use rusqlite::OptionalExtension;
|
use rusqlite::{Connection, OptionalExtension};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{database::DatabaseError, persons::Name};
|
||||||
database::{self, DatabaseError},
|
|
||||||
persons::Name,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct Quote {
|
pub struct Quote {
|
||||||
@@ -38,14 +35,10 @@ pub enum QuoteError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Quote {
|
impl Quote {
|
||||||
pub fn total_count() -> Result<i64, QuoteError> {
|
pub fn total_count(conn: &Connection) -> Result<i64, QuoteError> {
|
||||||
let conn = database::conn()?;
|
Ok(conn.query_row("SELECT COUNT(*) FROM quotes", (), |r| r.get(0))?)
|
||||||
let count: i64 = conn.query_row("SELECT COUNT(*) FROM quotes", (), |r| r.get(0))?;
|
|
||||||
Ok(count)
|
|
||||||
}
|
}
|
||||||
pub fn get_by_id(id: Uuid) -> Result<Quote, QuoteError> {
|
pub fn get_by_id(conn: &Connection, id: Uuid) -> Result<Quote, QuoteError> {
|
||||||
let conn = database::conn()?;
|
|
||||||
|
|
||||||
let quotemain = conn
|
let quotemain = conn
|
||||||
.prepare(
|
.prepare(
|
||||||
"SELECT timestamp, location, context, created_by, public FROM quotes WHERE id = ?1",
|
"SELECT timestamp, location, context, created_by, public FROM quotes WHERE id = ?1",
|
||||||
@@ -100,6 +93,7 @@ impl Quote {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn create(
|
pub fn create(
|
||||||
|
conn: &Connection,
|
||||||
lines: Vec<(String, Name)>,
|
lines: Vec<(String, Name)>,
|
||||||
timestamp: DateTime<FixedOffset>,
|
timestamp: DateTime<FixedOffset>,
|
||||||
context: Option<String>,
|
context: Option<String>,
|
||||||
@@ -111,15 +105,12 @@ impl Quote {
|
|||||||
return Err(QuoteError::EmptyQuote);
|
return Err(QuoteError::EmptyQuote);
|
||||||
}
|
}
|
||||||
|
|
||||||
let conn = database::conn()?;
|
|
||||||
let quote_id = Uuid::now_v7();
|
let quote_id = Uuid::now_v7();
|
||||||
let lines: Vec<(Uuid, String, Name)> = lines
|
let lines: Vec<(Uuid, String, Name)> = lines
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(c, a)| (Uuid::now_v7(), c, a))
|
.map(|(c, a)| (Uuid::now_v7(), c, a))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
conn.execute("BEGIN TRANSACTION", ())?;
|
|
||||||
|
|
||||||
let mut quote_stmt = conn.prepare(
|
let mut quote_stmt = conn.prepare(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO quotes (id, timestamp, location, context, created_by, public)
|
INSERT INTO quotes (id, timestamp, location, context, created_by, public)
|
||||||
@@ -138,7 +129,6 @@ impl Quote {
|
|||||||
line_stmt.execute((id, quote_id, content, attr.id, ordering as i64))?;
|
line_stmt.execute((id, quote_id, content, attr.id, ordering as i64))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.execute("COMMIT", ())?;
|
|
||||||
Ok(Quote {
|
Ok(Quote {
|
||||||
id: quote_id,
|
id: quote_id,
|
||||||
lines: lines
|
lines: lines
|
||||||
|
|||||||
41
src/tags.rs
41
src/tags.rs
@@ -5,14 +5,14 @@ use axum::{
|
|||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use rusqlite::{
|
use rusqlite::{
|
||||||
OptionalExtension, Result as RusqliteResult, ToSql,
|
Connection, OptionalExtension, Result as RusqliteResult, ToSql,
|
||||||
ffi::SQLITE_CONSTRAINT_UNIQUE,
|
ffi::SQLITE_CONSTRAINT_UNIQUE,
|
||||||
types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef},
|
types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::database::{self, DatabaseError};
|
use crate::database::DatabaseError;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
@@ -21,13 +21,11 @@ pub struct Tag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Tag {
|
impl Tag {
|
||||||
pub fn total_count() -> Result<i64, TagError> {
|
pub fn total_count(conn: &Connection) -> Result<i64, TagError> {
|
||||||
let conn = database::conn()?;
|
Ok(conn.query_row("SELECT COUNT(*) FROM tags", (), |r| r.get(0))?)
|
||||||
let count: i64 = conn.query_row("SELECT COUNT(*) FROM tags", (), |r| r.get(0))?;
|
|
||||||
Ok(count)
|
|
||||||
}
|
}
|
||||||
pub fn get_all() -> Result<Vec<Tag>, TagError> {
|
pub fn get_all(conn: &Connection) -> Result<Vec<Tag>, TagError> {
|
||||||
Ok(database::conn()?
|
Ok(conn
|
||||||
.prepare("SELECT id, tagname FROM tags")?
|
.prepare("SELECT id, tagname FROM tags")?
|
||||||
.query_map((), |r| {
|
.query_map((), |r| {
|
||||||
Ok(Tag {
|
Ok(Tag {
|
||||||
@@ -37,8 +35,8 @@ impl Tag {
|
|||||||
})?
|
})?
|
||||||
.collect::<Result<Vec<Tag>, _>>()?)
|
.collect::<Result<Vec<Tag>, _>>()?)
|
||||||
}
|
}
|
||||||
pub fn get_by_id(id: Uuid) -> Result<Tag, TagError> {
|
pub fn get_by_id(conn: &Connection, id: Uuid) -> Result<Tag, TagError> {
|
||||||
let res = database::conn()?
|
let res = conn
|
||||||
.prepare("SELECT tagname FROM tags WHERE id = ?1")?
|
.prepare("SELECT tagname FROM tags WHERE id = ?1")?
|
||||||
.query_one((&id,), |r| {
|
.query_one((&id,), |r| {
|
||||||
Ok(Tag {
|
Ok(Tag {
|
||||||
@@ -52,13 +50,13 @@ impl Tag {
|
|||||||
None => Err(TagError::NoTagWithId(id)),
|
None => Err(TagError::NoTagWithId(id)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_tagged_quotes_count(&self) -> Result<i64, TagError> {
|
pub fn get_tagged_quotes_count(&self, conn: &Connection) -> Result<i64, TagError> {
|
||||||
Ok(database::conn()?
|
Ok(conn
|
||||||
.prepare("SELECT COUNT(*) FROM quote_tags WHERE tag_id = ?1")?
|
.prepare("SELECT COUNT(*) FROM quote_tags WHERE tag_id = ?1")?
|
||||||
.query_one((self.id,), |r| Ok(r.get(0)?))?)
|
.query_one((self.id,), |r| Ok(r.get(0)?))?)
|
||||||
}
|
}
|
||||||
pub fn get_by_name(name: TagName) -> Result<Tag, TagError> {
|
pub fn get_by_name(conn: &Connection, name: TagName) -> Result<Tag, TagError> {
|
||||||
let res = database::conn()?
|
let res = conn
|
||||||
.prepare("SELECT id, tagname FROM tags WHERE tagname = ?1")?
|
.prepare("SELECT id, tagname FROM tags WHERE tagname = ?1")?
|
||||||
.query_one((&name,), |r| {
|
.query_one((&name,), |r| {
|
||||||
Ok(Tag {
|
Ok(Tag {
|
||||||
@@ -72,23 +70,20 @@ impl Tag {
|
|||||||
None => Err(TagError::NoTagWithName(name)),
|
None => Err(TagError::NoTagWithName(name)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn create(name: TagName) -> Result<Tag, TagError> {
|
pub fn create(conn: &Connection, name: TagName) -> Result<Tag, TagError> {
|
||||||
let id = Uuid::now_v7();
|
let id = Uuid::now_v7();
|
||||||
database::conn()?
|
conn.prepare("INSERT INTO tags(id, tagname) VALUES (?1, ?2)")?
|
||||||
.prepare("INSERT INTO tags(id, tagname) VALUES (?1, ?2)")?
|
|
||||||
.execute((id, &name))?;
|
.execute((id, &name))?;
|
||||||
Ok(Tag { id, name })
|
Ok(Tag { id, name })
|
||||||
}
|
}
|
||||||
pub fn rename(&mut self, name: TagName) -> Result<(), TagError> {
|
pub fn rename(&mut self, conn: &Connection, name: TagName) -> Result<(), TagError> {
|
||||||
database::conn()?
|
conn.prepare("UPDATE tags SET tagname = ?1 WHERE id = ?2")?
|
||||||
.prepare("UPDATE tags SET tagname = ?1 WHERE id = ?2")?
|
|
||||||
.execute((&name, self.id))?;
|
.execute((&name, self.id))?;
|
||||||
self.name = name;
|
self.name = name;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn delete(self) -> Result<(), TagError> {
|
pub fn delete(self, conn: &Connection) -> Result<(), TagError> {
|
||||||
database::conn()?
|
conn.prepare("DELETE FROM tags WHERE id = ?1")?
|
||||||
.prepare("DELETE FROM tags WHERE id = ?1")?
|
|
||||||
.execute((self.id,))?;
|
.execute((self.id,))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ pub fn authenticate_via_credentials(
|
|||||||
|
|
||||||
match user {
|
match user {
|
||||||
Some((id, Some(passhash))) => match User::match_hash_password(password, &passhash)? {
|
Some((id, Some(passhash))) => match User::match_hash_password(password, &passhash)? {
|
||||||
true => Ok(Some(User::get_by_id(id)?)),
|
true => Ok(Some(User::get_by_id(&conn, id)?)),
|
||||||
false => Err(AuthError::InvalidCredentials),
|
false => Err(AuthError::InvalidCredentials),
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
@@ -213,18 +213,20 @@ pub fn authenticate_via_credentials(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn authenticate_bearer(token: &str) -> Result<Option<User>, AuthError> {
|
fn authenticate_bearer(token: &str) -> Result<Option<User>, AuthError> {
|
||||||
let mut s = Session::get_by_token(token)?;
|
let conn = database::conn().map_err(|e| DatabaseError::from(e))?;
|
||||||
|
let mut s = Session::get_by_token(&conn, token)?;
|
||||||
if s.is_expired_or_revoked() {
|
if s.is_expired_or_revoked() {
|
||||||
return Err(AuthError::InvalidCredentials);
|
return Err(AuthError::InvalidCredentials);
|
||||||
}
|
}
|
||||||
s.prolong()?;
|
s.prolong(&conn)?;
|
||||||
Ok(Some(User::get_by_id(s.user_id)?))
|
Ok(Some(User::get_by_id(&conn, s.user_id)?))
|
||||||
}
|
}
|
||||||
fn authenticate_bearer_with_session(token: &str) -> Result<Option<Session>, AuthError> {
|
fn authenticate_bearer_with_session(token: &str) -> Result<Option<Session>, AuthError> {
|
||||||
let mut s = Session::get_by_token(token)?;
|
let conn = database::conn().map_err(|e| DatabaseError::from(e))?;
|
||||||
|
let mut s = Session::get_by_token(&conn, token)?;
|
||||||
if s.is_expired_or_revoked() {
|
if s.is_expired_or_revoked() {
|
||||||
return Err(AuthError::InvalidCredentials);
|
return Err(AuthError::InvalidCredentials);
|
||||||
}
|
}
|
||||||
s.prolong()?;
|
s.prolong(&conn)?;
|
||||||
Ok(Some(s))
|
Ok(Some(s))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ use axum::{
|
|||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, NaiveDate};
|
use chrono::{DateTime, NaiveDate};
|
||||||
use rusqlite::{OptionalExtension, ffi::SQLITE_CONSTRAINT_UNIQUE};
|
use rusqlite::{Connection, OptionalExtension, ffi::SQLITE_CONSTRAINT_UNIQUE};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ISE_MSG,
|
ISE_MSG,
|
||||||
database::{self, DatabaseError},
|
database::DatabaseError,
|
||||||
users::{
|
users::{
|
||||||
auth::UserPasswordHashing,
|
auth::UserPasswordHashing,
|
||||||
handle::{UserHandle, UserHandleError},
|
handle::{UserHandle, UserHandleError},
|
||||||
@@ -45,13 +45,11 @@ pub enum UserError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub fn total_count() -> Result<i64, UserError> {
|
pub fn total_count(conn: &Connection) -> Result<i64, UserError> {
|
||||||
let conn = database::conn()?;
|
Ok(conn.query_row("SELECT COUNT(*) FROM users", (), |r| r.get(0))?)
|
||||||
let count: i64 = conn.query_row("SELECT COUNT(*) FROM users", (), |r| r.get(0))?;
|
|
||||||
Ok(count)
|
|
||||||
}
|
}
|
||||||
pub fn get_by_id(id: Uuid) -> Result<User, UserError> {
|
pub fn get_by_id(conn: &Connection, id: Uuid) -> Result<User, UserError> {
|
||||||
let res = database::conn()?
|
let res = conn
|
||||||
.prepare("SELECT handle FROM users WHERE id = ?1")?
|
.prepare("SELECT handle FROM users WHERE id = ?1")?
|
||||||
.query_one((&id,), |r| {
|
.query_one((&id,), |r| {
|
||||||
Ok(User {
|
Ok(User {
|
||||||
@@ -65,8 +63,8 @@ impl User {
|
|||||||
None => Err(UserError::NoUserWithId(id)),
|
None => Err(UserError::NoUserWithId(id)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_by_handle(handle: UserHandle) -> Result<User, UserError> {
|
pub fn get_by_handle(conn: &Connection, handle: UserHandle) -> Result<User, UserError> {
|
||||||
let res = database::conn()?
|
let res = conn
|
||||||
.prepare("SELECT id, handle FROM users WHERE handle = ?1")?
|
.prepare("SELECT id, handle FROM users WHERE handle = ?1")?
|
||||||
.query_one((&handle,), |r| {
|
.query_one((&handle,), |r| {
|
||||||
Ok(User {
|
Ok(User {
|
||||||
@@ -80,8 +78,8 @@ impl User {
|
|||||||
None => Err(UserError::NoUserWithHandle(handle)),
|
None => Err(UserError::NoUserWithHandle(handle)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_all() -> Result<Vec<User>, UserError> {
|
pub fn get_all(conn: &Connection) -> Result<Vec<User>, UserError> {
|
||||||
Ok(database::conn()?
|
Ok(conn
|
||||||
.prepare("SELECT id, handle FROM users")?
|
.prepare("SELECT id, handle FROM users")?
|
||||||
.query_map((), |r| {
|
.query_map((), |r| {
|
||||||
Ok(User {
|
Ok(User {
|
||||||
@@ -92,16 +90,18 @@ impl User {
|
|||||||
.collect::<Result<Vec<User>, _>>()?)
|
.collect::<Result<Vec<User>, _>>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(handle: UserHandle) -> Result<User, UserError> {
|
pub fn create(conn: &Connection, handle: UserHandle) -> Result<User, UserError> {
|
||||||
let conn = database::conn()?;
|
|
||||||
let id = Uuid::now_v7();
|
let id = Uuid::now_v7();
|
||||||
conn.prepare("INSERT INTO users(id, handle) VALUES (?1, ?2)")?
|
conn.prepare("INSERT INTO users(id, handle) VALUES (?1, ?2)")?
|
||||||
.execute((&id, &handle))?;
|
.execute((&id, &handle))?;
|
||||||
Ok(User { id, handle })
|
Ok(User { id, handle })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_handle(&mut self, new_handle: UserHandle) -> Result<(), UserError> {
|
pub fn set_handle(
|
||||||
let conn = database::conn()?;
|
&mut self,
|
||||||
|
conn: &Connection,
|
||||||
|
new_handle: UserHandle,
|
||||||
|
) -> Result<(), UserError> {
|
||||||
conn.prepare("UPDATE users SET handle = ?1 WHERE id = ?2")?
|
conn.prepare("UPDATE users SET handle = ?1 WHERE id = ?2")?
|
||||||
.execute((&new_handle, self.id))?;
|
.execute((&new_handle, self.id))?;
|
||||||
self.handle = new_handle;
|
self.handle = new_handle;
|
||||||
@@ -118,8 +118,11 @@ impl User {
|
|||||||
|
|
||||||
// DANGEROUS: AUTH
|
// DANGEROUS: AUTH
|
||||||
impl User {
|
impl User {
|
||||||
pub fn set_password(&mut self, passw: Option<&str>) -> Result<(), UserError> {
|
pub fn set_password(
|
||||||
let conn = database::conn()?;
|
&mut self,
|
||||||
|
conn: &Connection,
|
||||||
|
passw: Option<&str>,
|
||||||
|
) -> Result<(), UserError> {
|
||||||
match passw {
|
match passw {
|
||||||
None => {
|
None => {
|
||||||
conn.prepare("UPDATE users SET password = NULL WHERE id = ?1")?
|
conn.prepare("UPDATE users SET password = NULL WHERE id = ?1")?
|
||||||
@@ -145,15 +148,14 @@ 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.
|
||||||
pub fn create_infradmin() -> Result<User, UserError> {
|
pub fn create_infradmin(conn: &Connection) -> Result<User, UserError> {
|
||||||
let mut u = User {
|
let mut u = User {
|
||||||
id: Uuid::max(),
|
id: Uuid::max(),
|
||||||
handle: UserHandle::new("Infradmin")?,
|
handle: UserHandle::new("Infradmin")?,
|
||||||
};
|
};
|
||||||
database::conn()?
|
conn.prepare("INSERT INTO users(id, handle) VALUES (?1, ?2)")?
|
||||||
.prepare("INSERT INTO users(id, handle) VALUES (?1, ?2)")?
|
|
||||||
.execute((&u.id, &u.handle))?;
|
.execute((&u.id, &u.handle))?;
|
||||||
u.regenerate_infradmin_password()?;
|
u.regenerate_infradmin_password(conn)?;
|
||||||
|
|
||||||
Ok(u)
|
Ok(u)
|
||||||
}
|
}
|
||||||
@@ -176,9 +178,9 @@ 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.
|
||||||
pub fn regenerate_infradmin_password(&mut self) -> Result<(), UserError> {
|
pub fn regenerate_infradmin_password(&mut self, conn: &Connection) -> Result<(), UserError> {
|
||||||
let passw = auth::generate_token(auth::TokenSize::Char16);
|
let passw = auth::generate_token(auth::TokenSize::Char16);
|
||||||
self.set_password(Some(&passw))?;
|
self.set_password(conn, Some(&passw))?;
|
||||||
log::info!("[USERS] The infradmin account password has been (re)generated.");
|
log::info!("[USERS] The infradmin account password has been (re)generated.");
|
||||||
log::info!("[USERS] Handle: {}", self.handle.as_str());
|
log::info!("[USERS] Handle: {}", self.handle.as_str());
|
||||||
log::info!("[USERS] Password: {}", passw);
|
log::info!("[USERS] Password: {}", passw);
|
||||||
@@ -192,13 +194,12 @@ 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.
|
||||||
pub fn create_systemuser() -> Result<User, UserError> {
|
pub fn create_systemuser(conn: &Connection) -> Result<User, UserError> {
|
||||||
let u = User {
|
let u = User {
|
||||||
id: Uuid::nil(),
|
id: Uuid::nil(),
|
||||||
handle: UserHandle::new("Mnemosyne")?,
|
handle: UserHandle::new("Mnemosyne")?,
|
||||||
};
|
};
|
||||||
database::conn()?
|
conn.prepare("INSERT INTO users(id, handle) VALUES (?1, ?2)")?
|
||||||
.prepare("INSERT INTO users(id, handle) VALUES (?1, ?2)")?
|
|
||||||
.execute((&u.id, &u.handle))?;
|
.execute((&u.id, &u.handle))?;
|
||||||
|
|
||||||
Ok(u)
|
Ok(u)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use rusqlite::Connection;
|
||||||
|
|
||||||
use crate::{database::DatabaseError, users::User};
|
use crate::{database::DatabaseError, users::User};
|
||||||
|
|
||||||
/// Infradmin and systemuser have all permissions.
|
/// Infradmin and systemuser have all permissions.
|
||||||
@@ -21,6 +23,7 @@ pub enum Permission {
|
|||||||
impl User {
|
impl User {
|
||||||
pub fn has_permission(
|
pub fn has_permission(
|
||||||
&self,
|
&self,
|
||||||
|
#[allow(unused)] conn: &Connection,
|
||||||
#[allow(unused)] permission: Permission,
|
#[allow(unused)] permission: Permission,
|
||||||
) -> Result<bool, DatabaseError> {
|
) -> Result<bool, DatabaseError> {
|
||||||
// Infradmin and systemuser have all permissions
|
// Infradmin and systemuser have all permissions
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ use axum::{
|
|||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Duration, Utc};
|
use chrono::{DateTime, Duration, Utc};
|
||||||
use rusqlite::OptionalExtension;
|
use rusqlite::{Connection, OptionalExtension};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::{self, DatabaseError},
|
database::DatabaseError,
|
||||||
users::{
|
users::{
|
||||||
User,
|
User,
|
||||||
auth::{self, COOKIE_NAME},
|
auth::{self, COOKIE_NAME},
|
||||||
@@ -70,8 +70,8 @@ impl IntoResponse for SessionError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
pub fn get_by_id(id: Uuid) -> Result<Session, SessionError> {
|
pub fn get_by_id(conn: &Connection, id: Uuid) -> Result<Session, SessionError> {
|
||||||
let res = database::conn()?
|
let res = conn
|
||||||
.prepare("SELECT user_id, expiry, revoked, revoked_at, revoked_by FROM sessions WHERE id = ?1")?
|
.prepare("SELECT user_id, expiry, revoked, revoked_at, revoked_by FROM sessions WHERE id = ?1")?
|
||||||
.query_one((&id,), |r| Ok(Session {
|
.query_one((&id,), |r| Ok(Session {
|
||||||
id,
|
id,
|
||||||
@@ -90,9 +90,9 @@ impl Session {
|
|||||||
None => Err(SessionError::NoSessionWithId(id)),
|
None => Err(SessionError::NoSessionWithId(id)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_by_token(token: &str) -> Result<Session, SessionError> {
|
pub fn get_by_token(conn: &Connection, token: &str) -> Result<Session, SessionError> {
|
||||||
let hashed = Sha256::digest(token.as_bytes()).to_vec();
|
let hashed = Sha256::digest(token.as_bytes()).to_vec();
|
||||||
let res = database::conn()?
|
let res = conn
|
||||||
.prepare("SELECT id, user_id, expiry, revoked, revoked_at, revoked_by FROM sessions WHERE token = ?1")?
|
.prepare("SELECT id, user_id, expiry, revoked, revoked_at, revoked_by FROM sessions WHERE token = ?1")?
|
||||||
.query_one((hashed,), |r| Ok(Session {
|
.query_one((hashed,), |r| Ok(Session {
|
||||||
id: r.get(0)?,
|
id: r.get(0)?,
|
||||||
@@ -111,14 +111,13 @@ impl Session {
|
|||||||
None => Err(SessionError::NoSessionWithToken(token.to_string())),
|
None => Err(SessionError::NoSessionWithToken(token.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_for_user(user: &User) -> Result<(Session, String), SessionError> {
|
pub fn new_for_user(conn: &Connection, user: &User) -> Result<(Session, String), SessionError> {
|
||||||
let id = Uuid::now_v7();
|
let id = Uuid::now_v7();
|
||||||
let token = auth::generate_token(auth::TokenSize::Char64);
|
let token = auth::generate_token(auth::TokenSize::Char64);
|
||||||
let hashed = Sha256::digest(token.as_bytes()).to_vec();
|
let hashed = Sha256::digest(token.as_bytes()).to_vec();
|
||||||
let expiry = Utc::now() + Session::DEFAULT_PROLONGATION;
|
let expiry = Utc::now() + Session::DEFAULT_PROLONGATION;
|
||||||
|
|
||||||
database::conn()?
|
conn.prepare("INSERT INTO sessions(id, token, user_id, expiry) VALUES (?1, ?2, ?3, ?4)")?
|
||||||
.prepare("INSERT INTO sessions(id, token, user_id, expiry) VALUES (?1, ?2, ?3, ?4)")?
|
|
||||||
.execute((&id, &hashed, user.id, expiry))?;
|
.execute((&id, &hashed, user.id, expiry))?;
|
||||||
let s = Session {
|
let s = Session {
|
||||||
id,
|
id,
|
||||||
@@ -131,7 +130,7 @@ impl Session {
|
|||||||
|
|
||||||
pub const DEFAULT_PROLONGATION: Duration = Duration::days(14);
|
pub const DEFAULT_PROLONGATION: Duration = Duration::days(14);
|
||||||
const PROLONGATION_THRESHOLD: Duration = Duration::hours(2);
|
const PROLONGATION_THRESHOLD: Duration = Duration::hours(2);
|
||||||
pub fn prolong(&mut self) -> Result<(), SessionError> {
|
pub fn prolong(&mut self, conn: &Connection) -> Result<(), SessionError> {
|
||||||
if self.expiry - Session::DEFAULT_PROLONGATION + Session::PROLONGATION_THRESHOLD
|
if self.expiry - Session::DEFAULT_PROLONGATION + Session::PROLONGATION_THRESHOLD
|
||||||
> Utc::now()
|
> Utc::now()
|
||||||
{
|
{
|
||||||
@@ -139,21 +138,19 @@ impl Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let expiry = Utc::now() + Session::DEFAULT_PROLONGATION;
|
let expiry = Utc::now() + Session::DEFAULT_PROLONGATION;
|
||||||
database::conn()?
|
conn.prepare("UPDATE sessions SET expiry = ?1 WHERE id = ?2")?
|
||||||
.prepare("UPDATE sessions SET expiry = ?1 WHERE id = ?2")?
|
|
||||||
.execute((&expiry, &self.id))?;
|
.execute((&expiry, &self.id))?;
|
||||||
self.expiry = expiry;
|
self.expiry = expiry;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn revoke(&mut self, actor: Option<&User>) -> Result<(), SessionError> {
|
pub fn revoke(&mut self, conn: &Connection, 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());
|
||||||
database::conn()?
|
conn.prepare(
|
||||||
.prepare(
|
"UPDATE sessions SET revoked = ?1, revoked_at = ?2, revoked_by = ?3 WHERE id = ?4",
|
||||||
"UPDATE sessions SET revoked = ?1, revoked_at = ?2, revoked_by = ?3 WHERE id = ?4",
|
)?
|
||||||
)?
|
.execute((&true, &now, &id, &self.id))?;
|
||||||
.execute((&true, &now, &id, &self.id))?;
|
|
||||||
self.status = SessionStatus::Revoked {
|
self.status = SessionStatus::Revoked {
|
||||||
revoked_at: now,
|
revoked_at: now,
|
||||||
revoked_by: id,
|
revoked_by: id,
|
||||||
|
|||||||
@@ -8,27 +8,33 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn initialise_reserved_users_if_needed() -> Result<(), UserError> {
|
pub fn initialise_reserved_users_if_needed() -> Result<(), UserError> {
|
||||||
let conn = database::conn()?;
|
let mut conn = database::conn()?;
|
||||||
|
let tx = conn.transaction()?;
|
||||||
|
|
||||||
if conn
|
if tx
|
||||||
.prepare("SELECT handle FROM users WHERE id = ?1")?
|
.prepare("SELECT handle FROM users WHERE id = ?1")?
|
||||||
.query_one((&Uuid::nil(),), |_| Ok(()))
|
.query_one((&Uuid::nil(),), |_| Ok(()))
|
||||||
.optional()?
|
.optional()?
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
let u = User::create_systemuser()?;
|
let u = User::create_systemuser(&tx)?;
|
||||||
LogEntry::new(u, LogAction::Initialize)?;
|
LogEntry::new(&tx, u, LogAction::Initialize)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if conn
|
if tx
|
||||||
.prepare("SELECT handle FROM users WHERE id = ?1")?
|
.prepare("SELECT handle FROM users WHERE id = ?1")?
|
||||||
.query_one((&Uuid::max(),), |_| Ok(()))
|
.query_one((&Uuid::max(),), |_| Ok(()))
|
||||||
.optional()?
|
.optional()?
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
User::create_infradmin()?;
|
User::create_infradmin(&tx)?;
|
||||||
LogEntry::new(User::get_by_id(Uuid::nil())?, LogAction::RegenInfradmin)?;
|
LogEntry::new(
|
||||||
|
&tx,
|
||||||
|
User::get_by_id(&tx, Uuid::nil())?,
|
||||||
|
LogAction::RegenInfradmin,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tx.commit()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ use maud::{Markup, PreEscaped, html};
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
api::CompositeError,
|
||||||
|
database::{self, DatabaseError},
|
||||||
persons::{Name, Person},
|
persons::{Name, Person},
|
||||||
quotes::{Quote, QuoteLine},
|
quotes::{Quote, QuoteLine},
|
||||||
tags::Tag,
|
tags::Tag,
|
||||||
@@ -20,9 +22,12 @@ const LINKS: &[(&str, &str, &str)] = &[
|
|||||||
("Add Person", "/persons/add", icons::CONTACT),
|
("Add Person", "/persons/add", icons::CONTACT),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub async fn page(req: Request) -> Markup {
|
pub async fn page(req: Request) -> Result<Markup, CompositeError> {
|
||||||
let u = User::authenticate(req.headers()).ok().flatten();
|
let u = User::authenticate(req.headers()).ok().flatten();
|
||||||
base(
|
let mut conn = database::conn()?;
|
||||||
|
let tx = conn.transaction().map_err(DatabaseError::from)?;
|
||||||
|
|
||||||
|
Ok(base(
|
||||||
"Dashboard | Mnemosyne",
|
"Dashboard | Mnemosyne",
|
||||||
html!(
|
html!(
|
||||||
(nav(u.as_ref(), req.uri().path()))
|
(nav(u.as_ref(), req.uri().path()))
|
||||||
@@ -55,25 +60,25 @@ pub async fn page(req: Request) -> Markup {
|
|||||||
}
|
}
|
||||||
div class="mx-auto max-w-4xl mt-4 flex flex-row gap-2" {
|
div class="mx-auto max-w-4xl mt-4 flex flex-row gap-2" {
|
||||||
(chip(html!({
|
(chip(html!({
|
||||||
@match Quote::total_count() {
|
@match Quote::total_count(&tx) {
|
||||||
Ok(count) => {(count) " QUOTES TOTAL"},
|
Ok(count) => {(count) " QUOTES TOTAL"},
|
||||||
Err(_) => span class="text-red-400" {"QUOTE COUNT ERR"},
|
Err(_) => span class="text-red-400" {"QUOTE COUNT ERR"},
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
(chip(html!({
|
(chip(html!({
|
||||||
@match Person::total_count() {
|
@match Person::total_count(&tx) {
|
||||||
Ok(count) => {(count) " PERSONS TOTAL"},
|
Ok(count) => {(count) " PERSONS TOTAL"},
|
||||||
Err(_) => span class="text-red-400" {"PERSON COUNT ERR"},
|
Err(_) => span class="text-red-400" {"PERSON COUNT ERR"},
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
(chip(html!({
|
(chip(html!({
|
||||||
@match Tag::total_count() {
|
@match Tag::total_count(&tx) {
|
||||||
Ok(count) => {(count) " TAGS TOTAL"},
|
Ok(count) => {(count) " TAGS TOTAL"},
|
||||||
Err(_) => span class="text-red-400" {"TAG COUNT ERR"}
|
Err(_) => span class="text-red-400" {"TAG COUNT ERR"}
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
(chip(html!({
|
(chip(html!({
|
||||||
@match User::total_count() {
|
@match User::total_count(&tx) {
|
||||||
Ok(count) => {(count) " USERS TOTAL"},
|
Ok(count) => {(count) " USERS TOTAL"},
|
||||||
Err(_) => span class="text-red-400" {"USER COUNT ERR"}
|
Err(_) => span class="text-red-400" {"USER COUNT ERR"}
|
||||||
}
|
}
|
||||||
@@ -82,7 +87,7 @@ pub async fn page(req: Request) -> Markup {
|
|||||||
|
|
||||||
div class="text-4xl xs:text-6xl sm:text-8xl text-neutral-800/25 mt-16 text-center font-semibold font-lora select-none" {"Mnemosyne"}
|
div class="text-4xl xs:text-6xl sm:text-8xl text-neutral-800/25 mt-16 text-center font-semibold font-lora select-none" {"Mnemosyne"}
|
||||||
),
|
),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_quote_1() -> Quote {
|
fn sample_quote_1() -> Quote {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use maud::{PreEscaped, html};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::CompositeError,
|
api::CompositeError,
|
||||||
|
database::{self, DatabaseError},
|
||||||
logs::LogEntry,
|
logs::LogEntry,
|
||||||
users::{User, auth::UserAuthenticate, permissions::Permission},
|
users::{User, auth::UserAuthenticate, permissions::Permission},
|
||||||
web::{RedirectViaError, components::nav::nav, icons, pages::base},
|
web::{RedirectViaError, components::nav::nav, icons, pages::base},
|
||||||
@@ -14,14 +15,16 @@ use crate::{
|
|||||||
pub async fn page(req: Request) -> Result<Response, CompositeError> {
|
pub async fn page(req: Request) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(req.headers())?
|
let u = User::authenticate(req.headers())?
|
||||||
.ok_or(RedirectViaError(Redirect::to("/login?re=/logs")))?;
|
.ok_or(RedirectViaError(Redirect::to("/login?re=/logs")))?;
|
||||||
let logs = LogEntry::get_all()?;
|
let mut conn = database::conn()?;
|
||||||
|
let tx = conn.transaction().map_err(DatabaseError::from)?;
|
||||||
|
let logs = LogEntry::get_all(&tx)?;
|
||||||
|
|
||||||
Ok(base(
|
Ok(base(
|
||||||
"Persons | Mnemosyne",
|
"Persons | Mnemosyne",
|
||||||
html!(
|
html!(
|
||||||
(nav(Some(&u), req.uri().path()))
|
(nav(Some(&u), req.uri().path()))
|
||||||
|
|
||||||
@if let Ok(true) = u.has_permission(Permission::BrowseServerLogs) {
|
@if let Ok(true) = u.has_permission(&tx, Permission::BrowseServerLogs) {
|
||||||
div class="max-w-4xl mx-auto px-2" {
|
div class="max-w-4xl mx-auto px-2" {
|
||||||
div class="my-4" {
|
div class="my-4" {
|
||||||
p class="flex items-center gap-2" {
|
p class="flex items-center gap-2" {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::CompositeError,
|
api::CompositeError,
|
||||||
|
database::{self, DatabaseError},
|
||||||
logs::{LogAction, LogEntry},
|
logs::{LogAction, LogEntry},
|
||||||
persons::Person,
|
persons::Person,
|
||||||
users::{
|
users::{
|
||||||
@@ -20,6 +21,8 @@ use crate::{
|
|||||||
|
|
||||||
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||||
let u = User::authenticate(req.headers())?;
|
let u = User::authenticate(req.headers())?;
|
||||||
|
let mut conn = database::conn()?;
|
||||||
|
let tx = conn.transaction()?;
|
||||||
|
|
||||||
Ok(base(
|
Ok(base(
|
||||||
"Persons | Mnemosyne",
|
"Persons | Mnemosyne",
|
||||||
@@ -33,14 +36,14 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
|||||||
span class="text-2xl font-semibold font-lora" {"Persons"}
|
span class="text-2xl font-semibold font-lora" {"Persons"}
|
||||||
}
|
}
|
||||||
p class="text-neutral-500 text-sm font-light" {
|
p class="text-neutral-500 text-sm font-light" {
|
||||||
@if let Ok(c) = Person::total_count() {
|
@if let Ok(c) = Person::total_count(&tx) {
|
||||||
(c) " persons in total."
|
(c) " persons in total."
|
||||||
} @else {
|
} @else {
|
||||||
"Could not get total person count."
|
"Could not get total person count."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@if let Ok(persons) = Person::get_all() {
|
@if let Ok(persons) = Person::get_all(&tx) {
|
||||||
div class="max-w-4xl mx-auto px-2 mt-4 flex flex-wrap gap-2" {
|
div class="max-w-4xl mx-auto px-2 mt-4 flex flex-wrap gap-2" {
|
||||||
@for person in &persons {
|
@for person in &persons {
|
||||||
div class="rounded px-4 py-2 bg-neutral-200/10 border border-neutral-200/15 flex items-center" {
|
div class="rounded px-4 py-2 bg-neutral-200/10 border border-neutral-200/15 flex items-center" {
|
||||||
@@ -49,7 +52,7 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
|||||||
div class="w-px h-2/3 my-auto mx-2 bg-neutral-200/15" {}
|
div class="w-px h-2/3 my-auto mx-2 bg-neutral-200/15" {}
|
||||||
div class="text-xs flex items-center" {
|
div class="text-xs flex items-center" {
|
||||||
(
|
(
|
||||||
if let Ok(i) = person.get_in_quote_count() {
|
if let Ok(i) = person.get_in_quote_count(&tx) {
|
||||||
i.to_string()
|
i.to_string()
|
||||||
} else {
|
} else {
|
||||||
"?".to_string()
|
"?".to_string()
|
||||||
@@ -96,13 +99,18 @@ pub async fn create(
|
|||||||
Form(form): Form<PersonNameForm>,
|
Form(form): Form<PersonNameForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
let p = Person::create(form.primary_name, u.id)?;
|
let mut conn = database::conn()?;
|
||||||
|
let tx = conn.transaction().map_err(DatabaseError::from)?;
|
||||||
|
|
||||||
|
let p = Person::create(&tx, form.primary_name, u.id)?;
|
||||||
LogEntry::new(
|
LogEntry::new(
|
||||||
|
&tx,
|
||||||
u,
|
u,
|
||||||
LogAction::CreatePerson {
|
LogAction::CreatePerson {
|
||||||
id: p.id,
|
id: p.id,
|
||||||
pname: p.primary_name,
|
pname: p.primary_name,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
tx.commit().map_err(DatabaseError::from)?;
|
||||||
Ok(Redirect::to("/persons").into_response())
|
Ok(Redirect::to("/persons").into_response())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::CompositeError,
|
api::CompositeError,
|
||||||
|
database::{self, DatabaseError},
|
||||||
logs::{LogAction, LogEntry},
|
logs::{LogAction, LogEntry},
|
||||||
tags::{Tag, TagName},
|
tags::{Tag, TagName},
|
||||||
users::{
|
users::{
|
||||||
@@ -20,6 +21,7 @@ use crate::{
|
|||||||
|
|
||||||
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||||
let u = User::authenticate(req.headers())?;
|
let u = User::authenticate(req.headers())?;
|
||||||
|
let conn = database::conn()?;
|
||||||
|
|
||||||
Ok(base(
|
Ok(base(
|
||||||
"Tags | Mnemosyne",
|
"Tags | Mnemosyne",
|
||||||
@@ -33,14 +35,14 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
|||||||
span class="text-2xl font-semibold font-lora" {"Tags"}
|
span class="text-2xl font-semibold font-lora" {"Tags"}
|
||||||
}
|
}
|
||||||
p class="text-neutral-500 text-sm font-light" {
|
p class="text-neutral-500 text-sm font-light" {
|
||||||
@if let Ok(c) = Tag::total_count() {
|
@if let Ok(c) = Tag::total_count(&conn) {
|
||||||
(c) " tags in total."
|
(c) " tags in total."
|
||||||
} @else {
|
} @else {
|
||||||
"Could not get total tag count."
|
"Could not get total tag count."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@if let Ok(tags) = Tag::get_all() {
|
@if let Ok(tags) = Tag::get_all(&conn) {
|
||||||
div class="max-w-4xl mx-auto mt-4 flex flex-wrap gap-2" {
|
div class="max-w-4xl mx-auto mt-4 flex flex-wrap gap-2" {
|
||||||
@for tag in &tags {
|
@for tag in &tags {
|
||||||
div class="rounded-full px-3 py-1 bg-neutral-200/10 border border-neutral-200/15 flex" {
|
div class="rounded-full px-3 py-1 bg-neutral-200/10 border border-neutral-200/15 flex" {
|
||||||
@@ -49,7 +51,7 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
|||||||
div class="w-px h-2/3 my-auto mx-2 bg-neutral-200/15" {}
|
div class="w-px h-2/3 my-auto mx-2 bg-neutral-200/15" {}
|
||||||
div class="text-xs flex items-center" {
|
div class="text-xs flex items-center" {
|
||||||
(
|
(
|
||||||
if let Ok(i) = tag.get_tagged_quotes_count() {
|
if let Ok(i) = tag.get_tagged_quotes_count(&conn) {
|
||||||
i.to_string()
|
i.to_string()
|
||||||
} else {
|
} else {
|
||||||
"?".to_string()
|
"?".to_string()
|
||||||
@@ -96,13 +98,18 @@ pub async fn create(
|
|||||||
Form(form): Form<TagForm>,
|
Form(form): Form<TagForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
let t = Tag::create(form.tagname)?;
|
let mut conn = database::conn()?;
|
||||||
|
let tx = conn.transaction().map_err(DatabaseError::from)?;
|
||||||
|
|
||||||
|
let t = Tag::create(&tx, form.tagname)?;
|
||||||
LogEntry::new(
|
LogEntry::new(
|
||||||
|
&tx,
|
||||||
u,
|
u,
|
||||||
LogAction::CreateTag {
|
LogAction::CreateTag {
|
||||||
id: t.id,
|
id: t.id,
|
||||||
name: t.name.to_string(),
|
name: t.name.to_string(),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
tx.commit().map_err(DatabaseError::from)?;
|
||||||
Ok(Redirect::to("/tags").into_response())
|
Ok(Redirect::to("/tags").into_response())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use axum::{
|
|||||||
use maud::{PreEscaped, html};
|
use maud::{PreEscaped, html};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
database,
|
||||||
users::{
|
users::{
|
||||||
User,
|
User,
|
||||||
auth::{AuthError, UserAuthenticate},
|
auth::{AuthError, UserAuthenticate},
|
||||||
@@ -22,8 +23,9 @@ pub mod profile;
|
|||||||
|
|
||||||
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||||
let u = User::authenticate(req.headers())?;
|
let u = User::authenticate(req.headers())?;
|
||||||
|
let conn = database::conn()?;
|
||||||
let us = match u.is_some() {
|
let us = match u.is_some() {
|
||||||
true => User::get_all(),
|
true => User::get_all(&conn),
|
||||||
false => Ok(vec![]),
|
false => Ok(vec![]),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -44,7 +46,7 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
|||||||
} @else {
|
} @else {
|
||||||
"Could not fetch user count."
|
"Could not fetch user count."
|
||||||
}
|
}
|
||||||
@if let Ok(true) = u.has_permission(Permission::ManuallyCreateUsers) {
|
@if let Ok(true) = u.has_permission(&conn, Permission::ManuallyCreateUsers) {
|
||||||
" "
|
" "
|
||||||
a href="/users/create" class="text-blue-500 hover:text-blue-400 hover:underline" {
|
a href="/users/create" class="text-blue-500 hover:text-blue-400 hover:underline" {
|
||||||
"Create a new user"
|
"Create a new user"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::CompositeError,
|
api::CompositeError,
|
||||||
|
database::{self, DatabaseError},
|
||||||
logs::{LogAction, LogEntry},
|
logs::{LogAction, LogEntry},
|
||||||
users::{
|
users::{
|
||||||
User,
|
User,
|
||||||
@@ -21,6 +22,7 @@ use crate::{
|
|||||||
|
|
||||||
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||||
let u = User::authenticate(req.headers())?;
|
let u = User::authenticate(req.headers())?;
|
||||||
|
let conn = database::conn()?;
|
||||||
|
|
||||||
Ok(base(
|
Ok(base(
|
||||||
"Users | Mnemosyne",
|
"Users | Mnemosyne",
|
||||||
@@ -34,7 +36,7 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
|||||||
span class="text-2xl font-semibold font-lora" {"Create a new user"}
|
span class="text-2xl font-semibold font-lora" {"Create a new user"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@if let Ok(true) = u.has_permission(Permission::ManuallyCreateUsers) {
|
@if let Ok(true) = u.has_permission(&conn, Permission::ManuallyCreateUsers) {
|
||||||
div class="mx-auto max-w-4xl px-2 mt-4" {
|
div class="mx-auto max-w-4xl px-2 mt-4" {
|
||||||
form action="/users/create-form" method="post" class="flex flex-col" {
|
form action="/users/create-form" method="post" class="flex flex-col" {
|
||||||
label for="handle" class="font-light text-neutral-500" {"Handle"}
|
label for="handle" class="font-light text-neutral-500" {"Handle"}
|
||||||
@@ -71,17 +73,22 @@ pub async fn create_user(
|
|||||||
Form(form): Form<CreateUserWithPasswordForm>,
|
Form(form): Form<CreateUserWithPasswordForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(&headers)?.required()?;
|
let u = User::authenticate(&headers)?.required()?;
|
||||||
if !u.has_permission(Permission::ManuallyCreateUsers)? {
|
let mut conn = database::conn().map_err(DatabaseError::from)?;
|
||||||
|
let tx = conn.transaction().map_err(DatabaseError::from)?;
|
||||||
|
|
||||||
|
if !u.has_permission(&tx, Permission::ManuallyCreateUsers)? {
|
||||||
return Ok((StatusCode::FORBIDDEN).into_response());
|
return Ok((StatusCode::FORBIDDEN).into_response());
|
||||||
}
|
}
|
||||||
let mut nu = User::create(form.handle)?;
|
let mut nu = User::create(&tx, form.handle)?;
|
||||||
nu.set_password(Some(&form.password))?;
|
nu.set_password(&tx, Some(&form.password))?;
|
||||||
LogEntry::new(
|
LogEntry::new(
|
||||||
|
&tx,
|
||||||
u,
|
u,
|
||||||
LogAction::CreateUser {
|
LogAction::CreateUser {
|
||||||
id: nu.id,
|
id: nu.id,
|
||||||
handle: nu.handle.as_str().to_string(),
|
handle: nu.handle.as_str().to_string(),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
tx.commit().map_err(DatabaseError::from)?;
|
||||||
Ok(Redirect::to("/users").into_response())
|
Ok(Redirect::to("/users").into_response())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,11 @@ use maud::{PreEscaped, html};
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
api::CompositeError,
|
||||||
|
database::{self, DatabaseError},
|
||||||
persons::Name,
|
persons::Name,
|
||||||
quotes::{Quote, QuoteLine},
|
quotes::{Quote, QuoteLine},
|
||||||
users::{
|
users::{User, UserError, auth::UserAuthenticate},
|
||||||
User, UserError,
|
|
||||||
auth::{AuthError, UserAuthenticate},
|
|
||||||
},
|
|
||||||
web::{
|
web::{
|
||||||
components::{nav::nav, quote::quote},
|
components::{nav::nav, quote::quote},
|
||||||
icons,
|
icons,
|
||||||
@@ -20,12 +19,15 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn page(Path(id): Path<Uuid>, req: Request) -> Result<Response, AuthError> {
|
pub async fn page(Path(id): Path<Uuid>, req: Request) -> Result<Response, CompositeError> {
|
||||||
let u = match User::authenticate(req.headers())? {
|
let u = match User::authenticate(req.headers())? {
|
||||||
Some(u) => u,
|
Some(u) => u,
|
||||||
None => return Ok(Redirect::to("/users").into_response()),
|
None => return Ok(Redirect::to("/users").into_response()),
|
||||||
};
|
};
|
||||||
let user = match User::get_by_id(id) {
|
let mut conn = database::conn().map_err(DatabaseError::from)?;
|
||||||
|
let tx = conn.transaction().map_err(DatabaseError::from)?;
|
||||||
|
|
||||||
|
let user = match User::get_by_id(&tx, id) {
|
||||||
Ok(u) => u,
|
Ok(u) => u,
|
||||||
Err(UserError::NoUserWithId(_)) => {
|
Err(UserError::NoUserWithId(_)) => {
|
||||||
return Ok(base(
|
return Ok(base(
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::CompositeError,
|
api::CompositeError,
|
||||||
|
database::{self, DatabaseError},
|
||||||
logs::{LogAction, LogEntry},
|
logs::{LogAction, LogEntry},
|
||||||
users::{
|
users::{
|
||||||
User,
|
User,
|
||||||
@@ -81,9 +82,13 @@ pub async fn change_handle(
|
|||||||
Form(form): Form<HandleForm>,
|
Form(form): Form<HandleForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let mut u = User::authenticate(&headers)?.required()?;
|
let mut u = User::authenticate(&headers)?.required()?;
|
||||||
|
let mut conn = database::conn()?;
|
||||||
|
let tx = conn.transaction().map_err(DatabaseError::from)?;
|
||||||
|
|
||||||
let oldhandle = u.handle.as_str().to_string();
|
let oldhandle = u.handle.as_str().to_string();
|
||||||
u.set_handle(form.handle)?;
|
u.set_handle(&tx, form.handle)?;
|
||||||
LogEntry::new(
|
LogEntry::new(
|
||||||
|
&tx,
|
||||||
u.clone(),
|
u.clone(),
|
||||||
LogAction::ChangeUserHandle {
|
LogAction::ChangeUserHandle {
|
||||||
id: u.id,
|
id: u.id,
|
||||||
@@ -103,6 +108,9 @@ pub async fn change_password(
|
|||||||
Form(form): Form<PasswordForm>,
|
Form(form): Form<PasswordForm>,
|
||||||
) -> Result<Response, CompositeError> {
|
) -> Result<Response, CompositeError> {
|
||||||
let mut u = User::authenticate(&headers)?.required()?;
|
let mut u = User::authenticate(&headers)?.required()?;
|
||||||
u.set_password(Some(&form.password))?;
|
let mut conn = database::conn()?;
|
||||||
|
let tx = conn.transaction().map_err(DatabaseError::from)?;
|
||||||
|
|
||||||
|
u.set_password(&tx, Some(&form.password))?;
|
||||||
Ok(Redirect::to("/user-settings").into_response())
|
Ok(Redirect::to("/user-settings").into_response())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user