style: cargo clippy and format
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful

This commit is contained in:
Radical 2025-05-02 15:20:22 +02:00
parent 5d0d666094
commit 97f7595cc5
10 changed files with 289 additions and 159 deletions

View file

@ -1,13 +1,16 @@
use std::time::{SystemTime, UNIX_EPOCH};
use actix_web::{error, post, web, Error, HttpResponse};
use actix_web::{Error, HttpResponse, error, post, web};
use argon2::{PasswordHash, PasswordVerifier};
use futures::StreamExt;
use log::error;
use regex::Regex;
use serde::{Deserialize, Serialize};
use futures::StreamExt;
use crate::{crypto::{generate_access_token, generate_refresh_token}, Data};
use crate::{
Data,
crypto::{generate_access_token, generate_refresh_token},
};
#[derive(Deserialize)]
struct LoginInformation {
@ -25,7 +28,10 @@ pub struct Response {
const MAX_SIZE: usize = 262_144;
#[post("/login")]
pub async fn response(mut payload: web::Payload, data: web::Data<Data>) -> Result<HttpResponse, Error> {
pub async fn response(
mut payload: web::Payload,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let mut body = web::BytesMut::new();
while let Some(chunk) = payload.next().await {
let chunk = chunk?;
@ -51,45 +57,82 @@ pub async fn response(mut payload: web::Payload, data: web::Data<Data>) -> Resul
}
if email_regex.is_match(&login_information.username) {
if let Ok(row) = sqlx::query_as("SELECT CAST(uuid as VARCHAR), password FROM users WHERE email = $1").bind(login_information.username).fetch_one(&data.pool).await {
if let Ok(row) =
sqlx::query_as("SELECT CAST(uuid as VARCHAR), password FROM users WHERE email = $1")
.bind(login_information.username)
.fetch_one(&data.pool)
.await
{
let (uuid, password): (String, String) = row;
return Ok(login(data.clone(), uuid, login_information.password, password, login_information.device_name).await)
return Ok(login(
data.clone(),
uuid,
login_information.password,
password,
login_information.device_name,
)
.await);
}
return Ok(HttpResponse::Unauthorized().finish())
return Ok(HttpResponse::Unauthorized().finish());
} else if username_regex.is_match(&login_information.username) {
if let Ok(row) = sqlx::query_as("SELECT CAST(uuid as VARCHAR), password FROM users WHERE username = $1").bind(login_information.username).fetch_one(&data.pool).await {
if let Ok(row) =
sqlx::query_as("SELECT CAST(uuid as VARCHAR), password FROM users WHERE username = $1")
.bind(login_information.username)
.fetch_one(&data.pool)
.await
{
let (uuid, password): (String, String) = row;
return Ok(login(data.clone(), uuid, login_information.password, password, login_information.device_name).await)
return Ok(login(
data.clone(),
uuid,
login_information.password,
password,
login_information.device_name,
)
.await);
}
return Ok(HttpResponse::Unauthorized().finish())
return Ok(HttpResponse::Unauthorized().finish());
}
Ok(HttpResponse::Unauthorized().finish())
}
async fn login(data: actix_web::web::Data<Data>, uuid: String, request_password: String, database_password: String, device_name: String) -> HttpResponse {
async fn login(
data: actix_web::web::Data<Data>,
uuid: String,
request_password: String,
database_password: String,
device_name: String,
) -> HttpResponse {
if let Ok(parsed_hash) = PasswordHash::new(&database_password) {
if data.argon2.verify_password(request_password.as_bytes(), &parsed_hash).is_ok() {
if data
.argon2
.verify_password(request_password.as_bytes(), &parsed_hash)
.is_ok()
{
let refresh_token = generate_refresh_token();
let access_token = generate_access_token();
if refresh_token.is_err() {
error!("{}", refresh_token.unwrap_err());
return HttpResponse::InternalServerError().finish()
return HttpResponse::InternalServerError().finish();
}
let refresh_token = refresh_token.unwrap();
if access_token.is_err() {
error!("{}", access_token.unwrap_err());
return HttpResponse::InternalServerError().finish()
return HttpResponse::InternalServerError().finish();
}
let access_token = access_token.unwrap();
let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
let current_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as i64;
if let Err(error) = sqlx::query(&format!("INSERT INTO refresh_tokens (token, uuid, created, device_name) VALUES ($1, '{}', $2, $3 )", uuid))
.bind(&refresh_token)
@ -114,10 +157,10 @@ async fn login(data: actix_web::web::Data<Data>, uuid: String, request_password:
return HttpResponse::Ok().json(Response {
access_token,
refresh_token,
})
});
}
return HttpResponse::Unauthorized().finish()
return HttpResponse::Unauthorized().finish();
}
HttpResponse::InternalServerError().finish()

View file

@ -1,13 +1,16 @@
use std::{str::FromStr, time::{SystemTime, UNIX_EPOCH}};
use std::{
str::FromStr,
time::{SystemTime, UNIX_EPOCH},
};
use actix_web::{web, HttpResponse, Scope};
use actix_web::{HttpResponse, Scope, web};
use log::error;
use sqlx::Postgres;
use uuid::Uuid;
mod register;
mod login;
mod refresh;
mod register;
mod revoke;
pub fn web() -> Scope {
@ -18,24 +21,33 @@ pub fn web() -> Scope {
.service(revoke::res)
}
pub async fn check_access_token<'a>(access_token: String, pool: &'a sqlx::Pool<Postgres>) -> Result<Uuid, HttpResponse> {
match sqlx::query_as("SELECT CAST(uuid as VARCHAR), created FROM access_tokens WHERE token = $1")
.bind(&access_token)
.fetch_one(&*pool)
.await {
pub async fn check_access_token(
access_token: String,
pool: &sqlx::Pool<Postgres>,
) -> Result<Uuid, HttpResponse> {
match sqlx::query_as(
"SELECT CAST(uuid as VARCHAR), created FROM access_tokens WHERE token = $1",
)
.bind(&access_token)
.fetch_one(pool)
.await
{
Ok(row) => {
let (uuid, created): (String, i64) = row;
let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
let current_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as i64;
let lifetime = current_time - created;
if lifetime > 3600 {
return Err(HttpResponse::Unauthorized().finish())
return Err(HttpResponse::Unauthorized().finish());
}
Ok(Uuid::from_str(&uuid).unwrap())
},
}
Err(error) => {
error!("{}", error);
Err(HttpResponse::InternalServerError().finish())

View file

@ -1,10 +1,13 @@
use std::time::{SystemTime, UNIX_EPOCH};
use actix_web::{error, post, web, Error, HttpResponse};
use actix_web::{Error, HttpResponse, error, post, web};
use futures::StreamExt;
use log::error;
use serde::{Deserialize, Serialize};
use futures::StreamExt;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::{crypto::{generate_access_token, generate_refresh_token}, Data};
use crate::{
Data,
crypto::{generate_access_token, generate_refresh_token},
};
#[derive(Deserialize)]
struct RefreshRequest {
@ -33,32 +36,45 @@ pub async fn res(mut payload: web::Payload, data: web::Data<Data>) -> Result<Htt
let refresh_request = serde_json::from_slice::<RefreshRequest>(&body)?;
let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
let current_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as i64;
if let Ok(row) = sqlx::query_as("SELECT CAST(uuid as VARCHAR), created FROM refresh_tokens WHERE token = $1").bind(&refresh_request.refresh_token).fetch_one(&data.pool).await {
if let Ok(row) =
sqlx::query_as("SELECT CAST(uuid as VARCHAR), created FROM refresh_tokens WHERE token = $1")
.bind(&refresh_request.refresh_token)
.fetch_one(&data.pool)
.await
{
let (uuid, created): (String, i64) = row;
if let Err(error) = sqlx::query("DELETE FROM access_tokens WHERE refresh_token = $1")
.bind(&refresh_request.refresh_token)
.execute(&data.pool)
.await {
.await
{
error!("{}", error);
}
let lifetime = current_time - created;
if lifetime > 2592000 {
if let Err(error) = sqlx::query("DELETE FROM refresh_tokens WHERE token = $1")
.bind(&refresh_request.refresh_token)
.execute(&data.pool)
.await {
.await
{
error!("{}", error);
}
return Ok(HttpResponse::Unauthorized().finish())
return Ok(HttpResponse::Unauthorized().finish());
}
let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
let current_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as i64;
let mut refresh_token = refresh_request.refresh_token;
@ -67,23 +83,24 @@ pub async fn res(mut payload: web::Payload, data: web::Data<Data>) -> Result<Htt
if new_refresh_token.is_err() {
error!("{}", new_refresh_token.unwrap_err());
return Ok(HttpResponse::InternalServerError().finish())
return Ok(HttpResponse::InternalServerError().finish());
}
let new_refresh_token = new_refresh_token.unwrap();
match sqlx::query("UPDATE refresh_tokens SET token = $1, created = $2 WHERE token = $3")
.bind(&new_refresh_token)
.bind(&current_time)
.bind(current_time)
.bind(&refresh_token)
.execute(&data.pool)
.await {
.await
{
Ok(_) => {
refresh_token = new_refresh_token;
},
}
Err(error) => {
error!("{}", error);
},
}
}
}
@ -91,7 +108,7 @@ pub async fn res(mut payload: web::Payload, data: web::Data<Data>) -> Result<Htt
if access_token.is_err() {
error!("{}", access_token.unwrap_err());
return Ok(HttpResponse::InternalServerError().finish())
return Ok(HttpResponse::InternalServerError().finish());
}
let access_token = access_token.unwrap();
@ -105,11 +122,11 @@ pub async fn res(mut payload: web::Payload, data: web::Data<Data>) -> Result<Htt
error!("{}", error);
return Ok(HttpResponse::InternalServerError().finish())
}
return Ok(HttpResponse::Ok().json(Response {
refresh_token,
access_token
}))
access_token,
}));
}
Ok(HttpResponse::Unauthorized().finish())

View file

@ -1,15 +1,21 @@
use std::time::{SystemTime, UNIX_EPOCH};
use actix_web::{error, post, web, Error, HttpResponse};
use actix_web::{Error, HttpResponse, error, post, web};
use argon2::{
PasswordHasher,
password_hash::{SaltString, rand_core::OsRng},
};
use futures::StreamExt;
use log::error;
use regex::Regex;
use serde::{Deserialize, Serialize};
use futures::StreamExt;
use uuid::Uuid;
use argon2::{password_hash::{rand_core::OsRng, SaltString}, PasswordHasher};
use crate::{crypto::{generate_access_token, generate_refresh_token}, Data};
use super::login::Response;
use crate::{
Data,
crypto::{generate_access_token, generate_refresh_token},
};
#[derive(Deserialize)]
struct AccountInformation {
@ -70,68 +76,76 @@ pub async fn res(mut payload: web::Payload, data: web::Data<Data>) -> Result<Htt
let email_regex = Regex::new(r"[-A-Za-z0-9!#$%&'*+/=?^_`{|}~]+(?:\.[-A-Za-z0-9!#$%&'*+/=?^_`{|}~]+)*@(?:[A-Za-z0-9](?:[-A-Za-z0-9]*[A-Za-z0-9])?\.)+[A-Za-z0-9](?:[-A-Za-z0-9]*[A-Za-z0-9])?").unwrap();
if !email_regex.is_match(&account_information.email) {
return Ok(HttpResponse::Forbidden().json(
ResponseError {
email_valid: false,
..Default::default()
}
))
return Ok(HttpResponse::Forbidden().json(ResponseError {
email_valid: false,
..Default::default()
}));
}
// FIXME: This regex doesnt seem to be working
let username_regex = Regex::new(r"[a-zA-Z0-9.-_]").unwrap();
if !username_regex.is_match(&account_information.identifier) || account_information.identifier.len() < 3 || account_information.identifier.len() > 32 {
return Ok(HttpResponse::Forbidden().json(
ResponseError {
gorb_id_valid: false,
..Default::default()
}
))
if !username_regex.is_match(&account_information.identifier)
|| account_information.identifier.len() < 3
|| account_information.identifier.len() > 32
{
return Ok(HttpResponse::Forbidden().json(ResponseError {
gorb_id_valid: false,
..Default::default()
}));
}
// Password is expected to be hashed using SHA3-384
let password_regex = Regex::new(r"[0-9a-f]{96}").unwrap();
if !password_regex.is_match(&account_information.password) {
return Ok(HttpResponse::Forbidden().json(
ResponseError {
password_hashed: false,
..Default::default()
}
))
return Ok(HttpResponse::Forbidden().json(ResponseError {
password_hashed: false,
..Default::default()
}));
}
let salt = SaltString::generate(&mut OsRng);
if let Ok(hashed_password) = data.argon2.hash_password(account_information.password.as_bytes(), &salt) {
if let Ok(hashed_password) = data
.argon2
.hash_password(account_information.password.as_bytes(), &salt)
{
// TODO: Check security of this implementation
return Ok(match sqlx::query(&format!("INSERT INTO users (uuid, username, password, email) VALUES ( '{}', $1, $2, $3 )", uuid))
return Ok(
match sqlx::query(&format!(
"INSERT INTO users (uuid, username, password, email) VALUES ( '{}', $1, $2, $3 )",
uuid
))
.bind(account_information.identifier)
// FIXME: Password has no security currently, either from a client or server perspective
.bind(hashed_password.to_string())
.bind(account_information.email)
.execute(&data.pool)
.await {
.await
{
Ok(_out) => {
let refresh_token = generate_refresh_token();
let access_token = generate_access_token();
if refresh_token.is_err() {
error!("{}", refresh_token.unwrap_err());
return Ok(HttpResponse::InternalServerError().finish())
return Ok(HttpResponse::InternalServerError().finish());
}
let refresh_token = refresh_token.unwrap();
if access_token.is_err() {
error!("{}", access_token.unwrap_err());
return Ok(HttpResponse::InternalServerError().finish())
return Ok(HttpResponse::InternalServerError().finish());
}
let access_token = access_token.unwrap();
let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
let current_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as i64;
if let Err(error) = sqlx::query(&format!("INSERT INTO refresh_tokens (token, uuid, created, device_name) VALUES ($1, '{}', $2, $3 )", uuid))
.bind(&refresh_token)
@ -153,32 +167,37 @@ pub async fn res(mut payload: web::Payload, data: web::Data<Data>) -> Result<Htt
return Ok(HttpResponse::InternalServerError().finish())
}
HttpResponse::Ok().json(
Response {
access_token,
refresh_token,
}
)
},
HttpResponse::Ok().json(Response {
access_token,
refresh_token,
})
}
Err(error) => {
let err_msg = error.as_database_error().unwrap().message();
match err_msg {
err_msg if err_msg.contains("unique") && err_msg.contains("username_key") => HttpResponse::Forbidden().json(ResponseError {
gorb_id_available: false,
..Default::default()
}),
err_msg if err_msg.contains("unique") && err_msg.contains("email_key") => HttpResponse::Forbidden().json(ResponseError {
email_available: false,
..Default::default()
}),
err_msg
if err_msg.contains("unique") && err_msg.contains("username_key") =>
{
HttpResponse::Forbidden().json(ResponseError {
gorb_id_available: false,
..Default::default()
})
}
err_msg if err_msg.contains("unique") && err_msg.contains("email_key") => {
HttpResponse::Forbidden().json(ResponseError {
email_available: false,
..Default::default()
})
}
_ => {
error!("{}", err_msg);
HttpResponse::InternalServerError().finish()
}
}
},
})
}
},
);
}
Ok(HttpResponse::InternalServerError().finish())

View file

@ -1,10 +1,10 @@
use actix_web::{error, post, web, Error, HttpResponse};
use actix_web::{Error, HttpResponse, error, post, web};
use argon2::{PasswordHash, PasswordVerifier};
use futures::{StreamExt, future};
use log::error;
use serde::{Deserialize, Serialize};
use futures::{future, StreamExt};
use crate::{api::v1::auth::check_access_token, Data};
use crate::{Data, api::v1::auth::check_access_token};
#[derive(Deserialize)]
struct RevokeRequest {
@ -20,9 +20,7 @@ struct Response {
impl Response {
fn new(deleted: bool) -> Self {
Self {
deleted
}
Self { deleted }
}
}
@ -44,18 +42,21 @@ pub async fn res(mut payload: web::Payload, data: web::Data<Data>) -> Result<Htt
let authorized = check_access_token(revoke_request.access_token, &data.pool).await;
if authorized.is_err() {
return Ok(authorized.unwrap_err())
if let Err(error) = authorized {
return Ok(error);
}
let uuid = authorized.unwrap();
let database_password_raw = sqlx::query_scalar(&format!("SELECT password FROM users WHERE uuid = '{}'", uuid))
.fetch_one(&data.pool)
.await;
let database_password_raw = sqlx::query_scalar(&format!(
"SELECT password FROM users WHERE uuid = '{}'",
uuid
))
.fetch_one(&data.pool)
.await;
if database_password_raw.is_err() {
error!("{}", database_password_raw.unwrap_err());
if let Err(error) = database_password_raw {
error!("{}", error);
return Ok(HttpResponse::InternalServerError().json(Response::new(false)));
}
@ -63,25 +64,32 @@ pub async fn res(mut payload: web::Payload, data: web::Data<Data>) -> Result<Htt
let hashed_password_raw = PasswordHash::new(&database_password);
if hashed_password_raw.is_err() {
error!("{}", hashed_password_raw.unwrap_err());
if let Err(error) = hashed_password_raw {
error!("{}", error);
return Ok(HttpResponse::InternalServerError().json(Response::new(false)));
}
let hashed_password = hashed_password_raw.unwrap();
if data.argon2.verify_password(revoke_request.password.as_bytes(), &hashed_password).is_err() {
return Ok(HttpResponse::Unauthorized().finish())
if data
.argon2
.verify_password(revoke_request.password.as_bytes(), &hashed_password)
.is_err()
{
return Ok(HttpResponse::Unauthorized().finish());
}
let tokens_raw = sqlx::query_scalar(&format!("SELECT token FROM refresh_tokens WHERE uuid = '{}' AND device_name = $1", uuid))
.bind(revoke_request.device_name)
.fetch_all(&data.pool)
.await;
let tokens_raw = sqlx::query_scalar(&format!(
"SELECT token FROM refresh_tokens WHERE uuid = '{}' AND device_name = $1",
uuid
))
.bind(revoke_request.device_name)
.fetch_all(&data.pool)
.await;
if tokens_raw.is_err() {
error!("{:?}", tokens_raw);
return Ok(HttpResponse::InternalServerError().json(Response::new(false)))
return Ok(HttpResponse::InternalServerError().json(Response::new(false)));
}
let tokens: Vec<String> = tokens_raw.unwrap();
@ -89,34 +97,45 @@ pub async fn res(mut payload: web::Payload, data: web::Data<Data>) -> Result<Htt
let mut access_tokens_delete = vec![];
let mut refresh_tokens_delete = vec![];
for token in tokens {
access_tokens_delete.push(sqlx::query("DELETE FROM access_tokens WHERE refresh_token = $1")
.bind(token.clone())
.execute(&data.pool));
access_tokens_delete.push(
sqlx::query("DELETE FROM access_tokens WHERE refresh_token = $1")
.bind(token.clone())
.execute(&data.pool),
);
refresh_tokens_delete.push(sqlx::query("DELETE FROM refresh_tokens WHERE token = $1")
.bind(token.clone())
.execute(&data.pool));
refresh_tokens_delete.push(
sqlx::query("DELETE FROM refresh_tokens WHERE token = $1")
.bind(token.clone())
.execute(&data.pool),
);
}
let results_access_tokens = future::join_all(access_tokens_delete).await;
let results_refresh_tokens = future::join_all(refresh_tokens_delete).await;
let access_tokens_errors: Vec<&Result<sqlx::postgres::PgQueryResult, sqlx::Error>> = results_access_tokens.iter().filter(|r| r.is_err()).collect();
let refresh_tokens_errors: Vec<&Result<sqlx::postgres::PgQueryResult, sqlx::Error>> = results_refresh_tokens.iter().filter(|r| r.is_err()).collect();
let access_tokens_errors: Vec<&Result<sqlx::postgres::PgQueryResult, sqlx::Error>> =
results_access_tokens
.iter()
.filter(|r| r.is_err())
.collect();
let refresh_tokens_errors: Vec<&Result<sqlx::postgres::PgQueryResult, sqlx::Error>> =
results_refresh_tokens
.iter()
.filter(|r| r.is_err())
.collect();
if !access_tokens_errors.is_empty() && !refresh_tokens_errors.is_empty() {
error!("{:?}", access_tokens_errors);
error!("{:?}", refresh_tokens_errors);
return Ok(HttpResponse::InternalServerError().finish())
return Ok(HttpResponse::InternalServerError().finish());
} else if !access_tokens_errors.is_empty() {
error!("{:?}", access_tokens_errors);
return Ok(HttpResponse::InternalServerError().finish())
return Ok(HttpResponse::InternalServerError().finish());
} else if !refresh_tokens_errors.is_empty() {
error!("{:?}", refresh_tokens_errors);
return Ok(HttpResponse::InternalServerError().finish())
return Ok(HttpResponse::InternalServerError().finish());
}
Ok(HttpResponse::Ok().json(Response::new(true)))
}

View file

@ -1,7 +1,7 @@
use actix_web::{Scope, web};
mod stats;
mod auth;
mod stats;
mod user;
pub fn web() -> Scope {

View file

@ -18,10 +18,13 @@ struct Response {
#[get("/stats")]
pub async fn res(data: web::Data<Data>) -> impl Responder {
let accounts;
if let Ok(users) = sqlx::query("SELECT uuid FROM users").fetch_all(&data.pool).await {
if let Ok(users) = sqlx::query("SELECT uuid FROM users")
.fetch_all(&data.pool)
.await
{
accounts = users.len();
} else {
return HttpResponse::InternalServerError().finish()
return HttpResponse::InternalServerError().finish();
}
let response = Response {

View file

@ -1,10 +1,10 @@
use actix_web::{error, post, web, Error, HttpResponse};
use actix_web::{Error, HttpResponse, error, post, web};
use futures::StreamExt;
use log::error;
use serde::{Deserialize, Serialize};
use futures::StreamExt;
use uuid::Uuid;
use crate::{api::v1::auth::check_access_token, Data};
use crate::{Data, api::v1::auth::check_access_token};
#[derive(Deserialize)]
struct AuthenticationRequest {
@ -21,7 +21,11 @@ struct Response {
const MAX_SIZE: usize = 262_144;
#[post("/user/{uuid}")]
pub async fn res(mut payload: web::Payload, path: web::Path<(String,)>, data: web::Data<Data>) -> Result<HttpResponse, Error> {
pub async fn res(
mut payload: web::Payload,
path: web::Path<(String,)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let mut body = web::BytesMut::new();
while let Some(chunk) = payload.next().await {
let chunk = chunk?;
@ -38,8 +42,8 @@ pub async fn res(mut payload: web::Payload, path: web::Path<(String,)>, data: we
let authorized = check_access_token(authentication_request.access_token, &data.pool).await;
if authorized.is_err() {
return Ok(authorized.unwrap_err())
if let Err(error) = authorized {
return Ok(error);
}
let mut uuid = authorized.unwrap();
@ -48,23 +52,29 @@ pub async fn res(mut payload: web::Payload, path: web::Path<(String,)>, data: we
let requested_uuid = Uuid::parse_str(&request);
if requested_uuid.is_err() {
return Ok(HttpResponse::BadRequest().json(r#"{ "error": "UUID is invalid!" }"#))
return Ok(HttpResponse::BadRequest().json(r#"{ "error": "UUID is invalid!" }"#));
}
uuid = requested_uuid.unwrap()
}
let row = sqlx::query_as(&format!("SELECT username, display_name FROM users WHERE uuid = '{}'", uuid))
.fetch_one(&data.pool)
.await;
let row = sqlx::query_as(&format!(
"SELECT username, display_name FROM users WHERE uuid = '{}'",
uuid
))
.fetch_one(&data.pool)
.await;
if row.is_err() {
error!("{}", row.unwrap_err());
return Ok(HttpResponse::InternalServerError().finish())
if let Err(error) = row {
error!("{}", error);
return Ok(HttpResponse::InternalServerError().finish());
}
let (username, display_name): (String, Option<String>) = row.unwrap();
Ok(HttpResponse::Ok().json(Response { uuid: uuid.to_string(), username, display_name: display_name.unwrap_or_default() }))
Ok(HttpResponse::Ok().json(Response {
uuid: uuid.to_string(),
username,
display_name: display_name.unwrap_or_default(),
}))
}

View file

@ -4,11 +4,11 @@ use hex::encode;
pub fn generate_access_token() -> Result<String, getrandom::Error> {
let mut buf = [0u8; 16];
fill(&mut buf)?;
Ok(encode(&buf))
Ok(encode(buf))
}
pub fn generate_refresh_token() -> Result<String, getrandom::Error> {
let mut buf = [0u8; 32];
fill(&mut buf)?;
Ok(encode(&buf))
Ok(encode(buf))
}

View file

@ -29,7 +29,12 @@ struct Data {
#[tokio::main]
async fn main() -> Result<(), Error> {
SimpleLogger::new().with_level(log::LevelFilter::Info).with_colors(true).env().init().unwrap();
SimpleLogger::new()
.with_level(log::LevelFilter::Info)
.with_colors(true)
.env()
.init()
.unwrap();
let args = Args::parse();
let config = ConfigBuilder::load(args.config).await?.build();
@ -38,11 +43,12 @@ async fn main() -> Result<(), Error> {
let pool = PgPool::connect_with(config.database.connect_options()).await?;
/*
/*
TODO: Figure out if a table should be used here and if not then what.
Also figure out if these should be different types from what they currently are and if we should add more "constraints"
*/
sqlx::raw_sql(r#"
sqlx::raw_sql(
r#"
CREATE TABLE IF NOT EXISTS users (
uuid uuid PRIMARY KEY UNIQUE NOT NULL,
username varchar(32) UNIQUE NOT NULL,
@ -67,7 +73,8 @@ async fn main() -> Result<(), Error> {
uuid uuid NOT NULL REFERENCES users(uuid),
created int8 NOT NULL
)
"#)
"#,
)
.execute(&pool)
.await?;