make transactions higher level (pass them everywhere)
This commit is contained in:
@@ -202,7 +202,7 @@ pub fn authenticate_via_credentials(
|
||||
|
||||
match user {
|
||||
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),
|
||||
},
|
||||
_ => {
|
||||
@@ -213,18 +213,20 @@ pub fn authenticate_via_credentials(
|
||||
}
|
||||
|
||||
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() {
|
||||
return Err(AuthError::InvalidCredentials);
|
||||
}
|
||||
s.prolong()?;
|
||||
Ok(Some(User::get_by_id(s.user_id)?))
|
||||
s.prolong(&conn)?;
|
||||
Ok(Some(User::get_by_id(&conn, s.user_id)?))
|
||||
}
|
||||
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() {
|
||||
return Err(AuthError::InvalidCredentials);
|
||||
}
|
||||
s.prolong()?;
|
||||
s.prolong(&conn)?;
|
||||
Ok(Some(s))
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ use axum::{
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use chrono::{DateTime, NaiveDate};
|
||||
use rusqlite::{OptionalExtension, ffi::SQLITE_CONSTRAINT_UNIQUE};
|
||||
use rusqlite::{Connection, OptionalExtension, ffi::SQLITE_CONSTRAINT_UNIQUE};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
ISE_MSG,
|
||||
database::{self, DatabaseError},
|
||||
database::DatabaseError,
|
||||
users::{
|
||||
auth::UserPasswordHashing,
|
||||
handle::{UserHandle, UserHandleError},
|
||||
@@ -45,13 +45,11 @@ pub enum UserError {
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn total_count() -> Result<i64, UserError> {
|
||||
let conn = database::conn()?;
|
||||
let count: i64 = conn.query_row("SELECT COUNT(*) FROM users", (), |r| r.get(0))?;
|
||||
Ok(count)
|
||||
pub fn total_count(conn: &Connection) -> Result<i64, UserError> {
|
||||
Ok(conn.query_row("SELECT COUNT(*) FROM users", (), |r| r.get(0))?)
|
||||
}
|
||||
pub fn get_by_id(id: Uuid) -> Result<User, UserError> {
|
||||
let res = database::conn()?
|
||||
pub fn get_by_id(conn: &Connection, id: Uuid) -> Result<User, UserError> {
|
||||
let res = conn
|
||||
.prepare("SELECT handle FROM users WHERE id = ?1")?
|
||||
.query_one((&id,), |r| {
|
||||
Ok(User {
|
||||
@@ -65,8 +63,8 @@ impl User {
|
||||
None => Err(UserError::NoUserWithId(id)),
|
||||
}
|
||||
}
|
||||
pub fn get_by_handle(handle: UserHandle) -> Result<User, UserError> {
|
||||
let res = database::conn()?
|
||||
pub fn get_by_handle(conn: &Connection, handle: UserHandle) -> Result<User, UserError> {
|
||||
let res = conn
|
||||
.prepare("SELECT id, handle FROM users WHERE handle = ?1")?
|
||||
.query_one((&handle,), |r| {
|
||||
Ok(User {
|
||||
@@ -80,8 +78,8 @@ impl User {
|
||||
None => Err(UserError::NoUserWithHandle(handle)),
|
||||
}
|
||||
}
|
||||
pub fn get_all() -> Result<Vec<User>, UserError> {
|
||||
Ok(database::conn()?
|
||||
pub fn get_all(conn: &Connection) -> Result<Vec<User>, UserError> {
|
||||
Ok(conn
|
||||
.prepare("SELECT id, handle FROM users")?
|
||||
.query_map((), |r| {
|
||||
Ok(User {
|
||||
@@ -92,16 +90,18 @@ impl User {
|
||||
.collect::<Result<Vec<User>, _>>()?)
|
||||
}
|
||||
|
||||
pub fn create(handle: UserHandle) -> Result<User, UserError> {
|
||||
let conn = database::conn()?;
|
||||
pub fn create(conn: &Connection, handle: UserHandle) -> Result<User, UserError> {
|
||||
let id = Uuid::now_v7();
|
||||
conn.prepare("INSERT INTO users(id, handle) VALUES (?1, ?2)")?
|
||||
.execute((&id, &handle))?;
|
||||
Ok(User { id, handle })
|
||||
}
|
||||
|
||||
pub fn set_handle(&mut self, new_handle: UserHandle) -> Result<(), UserError> {
|
||||
let conn = database::conn()?;
|
||||
pub fn set_handle(
|
||||
&mut self,
|
||||
conn: &Connection,
|
||||
new_handle: UserHandle,
|
||||
) -> Result<(), UserError> {
|
||||
conn.prepare("UPDATE users SET handle = ?1 WHERE id = ?2")?
|
||||
.execute((&new_handle, self.id))?;
|
||||
self.handle = new_handle;
|
||||
@@ -118,8 +118,11 @@ impl User {
|
||||
|
||||
// DANGEROUS: AUTH
|
||||
impl User {
|
||||
pub fn set_password(&mut self, passw: Option<&str>) -> Result<(), UserError> {
|
||||
let conn = database::conn()?;
|
||||
pub fn set_password(
|
||||
&mut self,
|
||||
conn: &Connection,
|
||||
passw: Option<&str>,
|
||||
) -> Result<(), UserError> {
|
||||
match passw {
|
||||
None => {
|
||||
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
|
||||
/// due to the ramifications of compromise. But it could be used for that,
|
||||
/// 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 {
|
||||
id: Uuid::max(),
|
||||
handle: UserHandle::new("Infradmin")?,
|
||||
};
|
||||
database::conn()?
|
||||
.prepare("INSERT INTO users(id, handle) VALUES (?1, ?2)")?
|
||||
conn.prepare("INSERT INTO users(id, handle) VALUES (?1, ?2)")?
|
||||
.execute((&u.id, &u.handle))?;
|
||||
u.regenerate_infradmin_password()?;
|
||||
u.regenerate_infradmin_password(conn)?;
|
||||
|
||||
Ok(u)
|
||||
}
|
||||
@@ -176,9 +178,9 @@ impl User {
|
||||
/// 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,
|
||||
/// 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);
|
||||
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] Handle: {}", self.handle.as_str());
|
||||
log::info!("[USERS] Password: {}", passw);
|
||||
@@ -192,13 +194,12 @@ impl User {
|
||||
/// for actions performed by Mnemosyne internally.
|
||||
/// It shall not be available for log-in.
|
||||
/// 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 {
|
||||
id: Uuid::nil(),
|
||||
handle: UserHandle::new("Mnemosyne")?,
|
||||
};
|
||||
database::conn()?
|
||||
.prepare("INSERT INTO users(id, handle) VALUES (?1, ?2)")?
|
||||
conn.prepare("INSERT INTO users(id, handle) VALUES (?1, ?2)")?
|
||||
.execute((&u.id, &u.handle))?;
|
||||
|
||||
Ok(u)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use rusqlite::Connection;
|
||||
|
||||
use crate::{database::DatabaseError, users::User};
|
||||
|
||||
/// Infradmin and systemuser have all permissions.
|
||||
@@ -21,6 +23,7 @@ pub enum Permission {
|
||||
impl User {
|
||||
pub fn has_permission(
|
||||
&self,
|
||||
#[allow(unused)] conn: &Connection,
|
||||
#[allow(unused)] permission: Permission,
|
||||
) -> Result<bool, DatabaseError> {
|
||||
// Infradmin and systemuser have all permissions
|
||||
|
||||
@@ -3,13 +3,13 @@ use axum::{
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use rusqlite::OptionalExtension;
|
||||
use rusqlite::{Connection, OptionalExtension};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
database::{self, DatabaseError},
|
||||
database::DatabaseError,
|
||||
users::{
|
||||
User,
|
||||
auth::{self, COOKIE_NAME},
|
||||
@@ -70,8 +70,8 @@ impl IntoResponse for SessionError {
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub fn get_by_id(id: Uuid) -> Result<Session, SessionError> {
|
||||
let res = database::conn()?
|
||||
pub fn get_by_id(conn: &Connection, id: Uuid) -> Result<Session, SessionError> {
|
||||
let res = conn
|
||||
.prepare("SELECT user_id, expiry, revoked, revoked_at, revoked_by FROM sessions WHERE id = ?1")?
|
||||
.query_one((&id,), |r| Ok(Session {
|
||||
id,
|
||||
@@ -90,9 +90,9 @@ impl Session {
|
||||
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 res = database::conn()?
|
||||
let res = conn
|
||||
.prepare("SELECT id, user_id, expiry, revoked, revoked_at, revoked_by FROM sessions WHERE token = ?1")?
|
||||
.query_one((hashed,), |r| Ok(Session {
|
||||
id: r.get(0)?,
|
||||
@@ -111,14 +111,13 @@ impl Session {
|
||||
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 token = auth::generate_token(auth::TokenSize::Char64);
|
||||
let hashed = Sha256::digest(token.as_bytes()).to_vec();
|
||||
let expiry = Utc::now() + Session::DEFAULT_PROLONGATION;
|
||||
|
||||
database::conn()?
|
||||
.prepare("INSERT INTO sessions(id, token, user_id, expiry) VALUES (?1, ?2, ?3, ?4)")?
|
||||
conn.prepare("INSERT INTO sessions(id, token, user_id, expiry) VALUES (?1, ?2, ?3, ?4)")?
|
||||
.execute((&id, &hashed, user.id, expiry))?;
|
||||
let s = Session {
|
||||
id,
|
||||
@@ -131,7 +130,7 @@ impl Session {
|
||||
|
||||
pub const DEFAULT_PROLONGATION: Duration = Duration::days(14);
|
||||
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
|
||||
> Utc::now()
|
||||
{
|
||||
@@ -139,21 +138,19 @@ impl Session {
|
||||
}
|
||||
|
||||
let expiry = Utc::now() + Session::DEFAULT_PROLONGATION;
|
||||
database::conn()?
|
||||
.prepare("UPDATE sessions SET expiry = ?1 WHERE id = ?2")?
|
||||
conn.prepare("UPDATE sessions SET expiry = ?1 WHERE id = ?2")?
|
||||
.execute((&expiry, &self.id))?;
|
||||
self.expiry = expiry;
|
||||
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 id = actor.map(|u| u.id).unwrap_or(Uuid::nil());
|
||||
database::conn()?
|
||||
.prepare(
|
||||
"UPDATE sessions SET revoked = ?1, revoked_at = ?2, revoked_by = ?3 WHERE id = ?4",
|
||||
)?
|
||||
.execute((&true, &now, &id, &self.id))?;
|
||||
conn.prepare(
|
||||
"UPDATE sessions SET revoked = ?1, revoked_at = ?2, revoked_by = ?3 WHERE id = ?4",
|
||||
)?
|
||||
.execute((&true, &now, &id, &self.id))?;
|
||||
self.status = SessionStatus::Revoked {
|
||||
revoked_at: now,
|
||||
revoked_by: id,
|
||||
|
||||
@@ -8,27 +8,33 @@ use crate::{
|
||||
};
|
||||
|
||||
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")?
|
||||
.query_one((&Uuid::nil(),), |_| Ok(()))
|
||||
.optional()?
|
||||
.is_none()
|
||||
{
|
||||
let u = User::create_systemuser()?;
|
||||
LogEntry::new(u, LogAction::Initialize)?;
|
||||
let u = User::create_systemuser(&tx)?;
|
||||
LogEntry::new(&tx, u, LogAction::Initialize)?;
|
||||
}
|
||||
|
||||
if conn
|
||||
if tx
|
||||
.prepare("SELECT handle FROM users WHERE id = ?1")?
|
||||
.query_one((&Uuid::max(),), |_| Ok(()))
|
||||
.optional()?
|
||||
.is_none()
|
||||
{
|
||||
User::create_infradmin()?;
|
||||
LogEntry::new(User::get_by_id(Uuid::nil())?, LogAction::RegenInfradmin)?;
|
||||
User::create_infradmin(&tx)?;
|
||||
LogEntry::new(
|
||||
&tx,
|
||||
User::get_by_id(&tx, Uuid::nil())?,
|
||||
LogAction::RegenInfradmin,
|
||||
)?;
|
||||
}
|
||||
|
||||
tx.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user