Compare commits
2 Commits
eb6aa532ff
...
ad354f73ed
Author | SHA1 | Date |
---|---|---|
antifallobst | ad354f73ed | |
antifallobst | b5f948a95e |
|
@ -12,6 +12,6 @@
|
||||||
- [X] `POST` /new
|
- [X] `POST` /new
|
||||||
- [ ] `GET` /list
|
- [ ] `GET` /list
|
||||||
- /relay
|
- /relay
|
||||||
- [ ] `POST` /create
|
- [X] `POST` /create
|
||||||
- [ ] `POST` /join
|
- [ ] `POST` /join
|
||||||
- [ ] `POST` /leave
|
- [ ] `POST` /leave
|
|
@ -0,0 +1,2 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
|
@ -0,0 +1 @@
|
||||||
|
imports_granularity = "Crate"
|
|
@ -11,7 +11,7 @@ struct NewResponse {
|
||||||
|
|
||||||
#[post("/account/invite/new")]
|
#[post("/account/invite/new")]
|
||||||
pub async fn new(backend: web::Data<Backend>, auth: BearerAuth) -> impl Responder {
|
pub async fn new(backend: web::Data<Backend>, auth: BearerAuth) -> impl Responder {
|
||||||
match backend.create_invite(auth.token()).await {
|
match backend.create_invite_token(auth.token()).await {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("{e}");
|
error!("{e}");
|
||||||
HttpResponse::InternalServerError().finish()
|
HttpResponse::InternalServerError().finish()
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
pub mod account;
|
pub mod account;
|
||||||
|
pub mod relay;
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
use crate::backend::Backend;
|
||||||
|
use actix_web::{post, web, HttpResponse, Responder};
|
||||||
|
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
|
use log::error;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct CreateResponse {
|
||||||
|
id: String,
|
||||||
|
secret: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/relay/create")]
|
||||||
|
pub async fn create(backend: web::Data<Backend>, auth: BearerAuth) -> impl Responder {
|
||||||
|
match backend.create_relay(auth.token()).await {
|
||||||
|
Err(e) => {
|
||||||
|
error!("{e}");
|
||||||
|
HttpResponse::InternalServerError().finish()
|
||||||
|
}
|
||||||
|
Ok(res) => match res {
|
||||||
|
Err(e) => e.into(),
|
||||||
|
Ok((uuid, secret)) => HttpResponse::Ok().json(CreateResponse {
|
||||||
|
id: uuid.to_string(),
|
||||||
|
secret,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::backend::Backend;
|
use crate::{backend::Backend, config::Config};
|
||||||
use crate::config::Config;
|
|
||||||
use actix_web::{web, App, HttpServer};
|
use actix_web::{web, App, HttpServer};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
@ -13,6 +12,7 @@ pub async fn start(config: &Config, backend: Backend) -> Result<()> {
|
||||||
.service(endpoints::account::register)
|
.service(endpoints::account::register)
|
||||||
.service(endpoints::account::auth)
|
.service(endpoints::account::auth)
|
||||||
.service(endpoints::account::invite::new)
|
.service(endpoints::account::invite::new)
|
||||||
|
.service(endpoints::relay::create)
|
||||||
})
|
})
|
||||||
.bind((config.addr.as_str(), config.port))?;
|
.bind((config.addr.as_str(), config.port))?;
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,18 @@
|
||||||
mod db_structures;
|
mod db_structures;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod permissions;
|
mod permissions;
|
||||||
|
mod relay;
|
||||||
|
mod tokens;
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
use crate::backend::{error::Error, permissions::Permission, user::IntoUser};
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use anyhow::{bail, Result};
|
use anyhow::Result;
|
||||||
use argon2::{
|
|
||||||
password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
|
|
||||||
Argon2,
|
|
||||||
};
|
|
||||||
use chrono::Days;
|
|
||||||
use db_structures::{AuthTokensRow, InviteTokensRow, UsersRow};
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use rand::distributions::DistString;
|
use sqlx::MySqlPool;
|
||||||
use sqlx::{types::chrono::Utc, MySqlPool};
|
|
||||||
use std::str::FromStr;
|
|
||||||
use user::User;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
pool: MySqlPool,
|
pub pool: MySqlPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Backend {
|
impl Backend {
|
||||||
|
@ -70,169 +61,4 @@ impl Backend {
|
||||||
|
|
||||||
Ok(Self { pool })
|
Ok(Self { pool })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the UUID of the user who owns the auth token.
|
|
||||||
pub async fn resolve_auth_token(&self, token: &str) -> Result<Result<Uuid, Error>> {
|
|
||||||
match sqlx::query_as!(
|
|
||||||
AuthTokensRow,
|
|
||||||
r#"SELECT * FROM AuthTokens WHERE token = ?;"#,
|
|
||||||
token
|
|
||||||
)
|
|
||||||
.fetch_one(&self.pool)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Err(e) => match e {
|
|
||||||
sqlx::Error::RowNotFound => Ok(Err(Error::InvalidToken)),
|
|
||||||
_ => Err(e.into()),
|
|
||||||
},
|
|
||||||
Ok(row) => {
|
|
||||||
if row.expire > Utc::now().naive_utc() {
|
|
||||||
Ok(Ok(Uuid::from_str(&row.userid)?))
|
|
||||||
} else {
|
|
||||||
sqlx::query!(r#"DELETE FROM AuthTokens WHERE token = ?;"#, token)
|
|
||||||
.execute(&self.pool)
|
|
||||||
.await?;
|
|
||||||
Ok(Err(Error::TokenExpired))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check whether an invite-token is valid or not.
|
|
||||||
async fn check_invite_token(&self, token: &str) -> Result<Result<(), Error>> {
|
|
||||||
match sqlx::query_as!(
|
|
||||||
InviteTokensRow,
|
|
||||||
r#"SELECT * FROM InviteTokens WHERE token = ?;"#,
|
|
||||||
token
|
|
||||||
)
|
|
||||||
.fetch_one(&self.pool)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Err(e) => match e {
|
|
||||||
sqlx::Error::RowNotFound => Ok(Err(Error::InvalidToken)),
|
|
||||||
_ => Err(e.into()),
|
|
||||||
},
|
|
||||||
Ok(row) => {
|
|
||||||
sqlx::query!(r#"DELETE FROM InviteTokens WHERE token = ?;"#, token)
|
|
||||||
.execute(&self.pool)
|
|
||||||
.await?;
|
|
||||||
if row.expire > Utc::now().naive_utc() {
|
|
||||||
Ok(Ok(()))
|
|
||||||
} else {
|
|
||||||
Ok(Err(Error::TokenExpired))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns detailed information about the user identified by the UUID.
|
|
||||||
async fn get_user(&self, userid: Uuid) -> Result<Result<User, Error>> {
|
|
||||||
match sqlx::query_as!(
|
|
||||||
UsersRow,
|
|
||||||
r#"SELECT * FROM Users WHERE userid = ?;"#,
|
|
||||||
userid.as_bytes().as_slice()
|
|
||||||
)
|
|
||||||
.fetch_one(&self.pool)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Err(e) => match e {
|
|
||||||
sqlx::Error::RowNotFound => Ok(Err(Error::UserNotFound)),
|
|
||||||
_ => Err(e.into()),
|
|
||||||
},
|
|
||||||
Ok(row) => Ok(Ok(row.try_into()?)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new account and returns its UUID.
|
|
||||||
pub async fn account_register(
|
|
||||||
&self,
|
|
||||||
token: String,
|
|
||||||
password: String,
|
|
||||||
) -> Result<Result<Uuid, Error>> {
|
|
||||||
if let Err(e) = self.check_invite_token(&token).await? {
|
|
||||||
return Ok(Err(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
let salt = SaltString::generate(&mut OsRng);
|
|
||||||
|
|
||||||
let hash = Argon2::default()
|
|
||||||
.hash_password(password.as_bytes(), &salt)
|
|
||||||
.map_err(|_| anyhow::Error::msg("Failed to hash the password"))?
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let userid = Uuid::new_v4();
|
|
||||||
|
|
||||||
sqlx::query!(
|
|
||||||
r#"INSERT INTO Users VALUES (?, ?, 0);"#,
|
|
||||||
userid.as_bytes().as_slice(),
|
|
||||||
hash
|
|
||||||
)
|
|
||||||
.execute(&self.pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Ok(userid))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates an auth token for a user, given their UUID and password.
|
|
||||||
pub async fn authenticate(
|
|
||||||
&self,
|
|
||||||
userid: Uuid,
|
|
||||||
password: String,
|
|
||||||
) -> Result<Result<String, Error>> {
|
|
||||||
let user = match userid.into_user(&self).await? {
|
|
||||||
Ok(user) => user,
|
|
||||||
Err(e) => return Ok(Err(e)),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !user.verify_password(&password)? {
|
|
||||||
return Ok(Err(Error::AuthenticationFailure));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut token = rand::distributions::Alphanumeric.sample_string(&mut OsRng, 48);
|
|
||||||
// just for the case, that there's some duplication
|
|
||||||
loop {
|
|
||||||
match self.resolve_auth_token(&token).await? {
|
|
||||||
Ok(_) => token = rand::distributions::Alphanumeric.sample_string(&mut OsRng, 48),
|
|
||||||
Err(Error::InvalidToken) | Err(Error::TokenExpired) => break,
|
|
||||||
Err(e) => bail!("!THIS ERROR SHOULDN'T BE HERE! -> {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlx::query!(
|
|
||||||
r#"INSERT INTO AuthTokens VALUES (?, ?, ?);"#,
|
|
||||||
token,
|
|
||||||
userid.as_bytes().as_slice(),
|
|
||||||
Utc::now().naive_utc().checked_add_days(Days::new(14))
|
|
||||||
)
|
|
||||||
.execute(&self.pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Ok(token))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a new invite token, if the user identified by the UUID has the permission to do so.
|
|
||||||
pub async fn create_invite(&self, user: impl IntoUser) -> Result<Result<String, Error>> {
|
|
||||||
let user = match user.into_user(&self).await? {
|
|
||||||
Ok(user) => user,
|
|
||||||
Err(e) => return Ok(Err(e)),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !user.has_permission(Permission::GenerateInviteTokens) {
|
|
||||||
return Ok(Err(Error::PermissionDenied(
|
|
||||||
"This user is not authorized to generate invite codes",
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let token = rand::distributions::Alphanumeric.sample_string(&mut OsRng, 48);
|
|
||||||
|
|
||||||
sqlx::query!(
|
|
||||||
r#"INSERT INTO InviteTokens VALUES (?, ?);"#,
|
|
||||||
token,
|
|
||||||
Utc::now().naive_utc().checked_add_days(Days::new(7))
|
|
||||||
)
|
|
||||||
.execute(&self.pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Ok(token))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
use crate::backend::{error::Error, user::IntoUser, Backend};
|
||||||
|
use argon2::{
|
||||||
|
password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
|
||||||
|
Argon2,
|
||||||
|
};
|
||||||
|
use rand::distributions::DistString;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
impl Backend {
|
||||||
|
/// Creates the structures for a new relay
|
||||||
|
pub async fn create_relay(
|
||||||
|
&self,
|
||||||
|
user: impl IntoUser,
|
||||||
|
) -> anyhow::Result<anyhow::Result<(Uuid, String), Error>> {
|
||||||
|
let _user = match user.into_user(&self).await? {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(e) => return Ok(Err(e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let relay_id = Uuid::new_v4();
|
||||||
|
let secret = rand::distributions::Alphanumeric.sample_string(&mut OsRng, 48);
|
||||||
|
|
||||||
|
let salt = SaltString::generate(&mut OsRng);
|
||||||
|
|
||||||
|
let secret_hash = Argon2::default()
|
||||||
|
.hash_password(secret.as_bytes(), &salt)
|
||||||
|
.map_err(|_| anyhow::Error::msg("Failed to hash the relay secret"))?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
sqlx::query!(
|
||||||
|
r#"INSERT INTO Relays VALUES (?, ?);"#,
|
||||||
|
relay_id.as_bytes().as_slice(),
|
||||||
|
secret_hash
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Ok((relay_id, secret)))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
use crate::backend::{
|
||||||
|
db_structures::{AuthTokensRow, InviteTokensRow},
|
||||||
|
error::Error,
|
||||||
|
permissions::Permission,
|
||||||
|
user::IntoUser,
|
||||||
|
Backend,
|
||||||
|
};
|
||||||
|
use argon2::password_hash::rand_core::OsRng;
|
||||||
|
use chrono::{Days, Utc};
|
||||||
|
use rand::distributions::DistString;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
impl Backend {
|
||||||
|
/// Returns the UUID of the user who owns the auth token.
|
||||||
|
pub async fn resolve_auth_token(
|
||||||
|
&self,
|
||||||
|
token: &str,
|
||||||
|
) -> anyhow::Result<anyhow::Result<Uuid, Error>> {
|
||||||
|
match sqlx::query_as!(
|
||||||
|
AuthTokensRow,
|
||||||
|
r#"SELECT * FROM AuthTokens WHERE token = ?;"#,
|
||||||
|
token
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Err(e) => match e {
|
||||||
|
sqlx::Error::RowNotFound => Ok(Err(Error::InvalidToken)),
|
||||||
|
_ => Err(e.into()),
|
||||||
|
},
|
||||||
|
Ok(row) => {
|
||||||
|
if row.expire > Utc::now().naive_utc() {
|
||||||
|
Ok(Ok(Uuid::from_str(&row.userid)?))
|
||||||
|
} else {
|
||||||
|
sqlx::query!(r#"DELETE FROM AuthTokens WHERE token = ?;"#, token)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await?;
|
||||||
|
Ok(Err(Error::TokenExpired))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether an invite-token is valid or not.
|
||||||
|
pub async fn check_invite_token(
|
||||||
|
&self,
|
||||||
|
token: &str,
|
||||||
|
) -> anyhow::Result<anyhow::Result<(), Error>> {
|
||||||
|
match sqlx::query_as!(
|
||||||
|
InviteTokensRow,
|
||||||
|
r#"SELECT * FROM InviteTokens WHERE token = ?;"#,
|
||||||
|
token
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Err(e) => match e {
|
||||||
|
sqlx::Error::RowNotFound => Ok(Err(Error::InvalidToken)),
|
||||||
|
_ => Err(e.into()),
|
||||||
|
},
|
||||||
|
Ok(row) => {
|
||||||
|
sqlx::query!(r#"DELETE FROM InviteTokens WHERE token = ?;"#, token)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await?;
|
||||||
|
if row.expire > Utc::now().naive_utc() {
|
||||||
|
Ok(Ok(()))
|
||||||
|
} else {
|
||||||
|
Ok(Err(Error::TokenExpired))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a new invite token, if the user identified by the UUID has the permission to do so.
|
||||||
|
pub async fn create_invite_token(
|
||||||
|
&self,
|
||||||
|
user: impl IntoUser,
|
||||||
|
) -> anyhow::Result<anyhow::Result<String, Error>> {
|
||||||
|
let user = match user.into_user(&self).await? {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(e) => return Ok(Err(e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !user.has_permission(Permission::GenerateInviteTokens) {
|
||||||
|
return Ok(Err(Error::PermissionDenied(
|
||||||
|
"This user is not authorized to generate invite codes",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = rand::distributions::Alphanumeric.sample_string(&mut OsRng, 48);
|
||||||
|
|
||||||
|
sqlx::query!(
|
||||||
|
r#"INSERT INTO InviteTokens VALUES (?, ?);"#,
|
||||||
|
token,
|
||||||
|
Utc::now().naive_utc().checked_add_days(Days::new(7))
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Ok(token))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,12 @@
|
||||||
use crate::backend::db_structures::UsersRow;
|
use crate::backend::{db_structures::UsersRow, error::Error, permissions::Permission, Backend};
|
||||||
use crate::backend::error::Error;
|
use anyhow::{bail, Result};
|
||||||
use crate::backend::{permissions::Permission, Backend};
|
use argon2::{
|
||||||
use anyhow::Result;
|
password_hash::{rand_core::OsRng, SaltString},
|
||||||
use argon2::PasswordVerifier;
|
Argon2, PasswordHasher, PasswordVerifier,
|
||||||
|
};
|
||||||
|
use chrono::Days;
|
||||||
|
use rand::distributions::DistString;
|
||||||
|
use sqlx::types::chrono::Utc;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -80,3 +84,90 @@ impl IntoUser for User {
|
||||||
Ok(Ok(self))
|
Ok(Ok(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Backend {
|
||||||
|
/// Returns detailed information about the user identified by the UUID.
|
||||||
|
async fn get_user(&self, userid: Uuid) -> Result<Result<User, Error>> {
|
||||||
|
match sqlx::query_as!(
|
||||||
|
UsersRow,
|
||||||
|
r#"SELECT * FROM Users WHERE userid = ?;"#,
|
||||||
|
userid.as_bytes().as_slice()
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Err(e) => match e {
|
||||||
|
sqlx::Error::RowNotFound => Ok(Err(Error::UserNotFound)),
|
||||||
|
_ => Err(e.into()),
|
||||||
|
},
|
||||||
|
Ok(row) => Ok(Ok(row.try_into()?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new account and returns its UUID.
|
||||||
|
pub async fn account_register(
|
||||||
|
&self,
|
||||||
|
token: String,
|
||||||
|
password: String,
|
||||||
|
) -> Result<Result<Uuid, Error>> {
|
||||||
|
if let Err(e) = self.check_invite_token(&token).await? {
|
||||||
|
return Ok(Err(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
let salt = SaltString::generate(&mut OsRng);
|
||||||
|
|
||||||
|
let hash = Argon2::default()
|
||||||
|
.hash_password(password.as_bytes(), &salt)
|
||||||
|
.map_err(|_| anyhow::Error::msg("Failed to hash the password"))?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let userid = Uuid::new_v4();
|
||||||
|
|
||||||
|
sqlx::query!(
|
||||||
|
r#"INSERT INTO Users VALUES (?, ?, 0);"#,
|
||||||
|
userid.as_bytes().as_slice(),
|
||||||
|
hash
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Ok(userid))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates an auth token for a user, given their UUID and password.
|
||||||
|
pub async fn authenticate(
|
||||||
|
&self,
|
||||||
|
userid: Uuid,
|
||||||
|
password: String,
|
||||||
|
) -> Result<Result<String, Error>> {
|
||||||
|
let user = match userid.into_user(&self).await? {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(e) => return Ok(Err(e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !user.verify_password(&password)? {
|
||||||
|
return Ok(Err(Error::AuthenticationFailure));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut token = rand::distributions::Alphanumeric.sample_string(&mut OsRng, 48);
|
||||||
|
// just for the case, that there's some duplication
|
||||||
|
loop {
|
||||||
|
match self.resolve_auth_token(&token).await? {
|
||||||
|
Ok(_) => token = rand::distributions::Alphanumeric.sample_string(&mut OsRng, 48),
|
||||||
|
Err(Error::InvalidToken) | Err(Error::TokenExpired) => break,
|
||||||
|
Err(e) => bail!("!THIS ERROR SHOULDN'T BE HERE! -> {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlx::query!(
|
||||||
|
r#"INSERT INTO AuthTokens VALUES (?, ?, ?);"#,
|
||||||
|
token,
|
||||||
|
userid.as_bytes().as_slice(),
|
||||||
|
Utc::now().naive_utc().checked_add_days(Days::new(14))
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Ok(token))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue