* computer switch

* some fixes; github action

* added pr to master

* sqlx database setup

* switched intial GHA test db

* removed sqlx database setup

* unfinished patch route

* bug fixes + tests

* more tests, more fixes, cargo fmt

* merge fixes

* more tests, full reorganization

* fmt, clippy

* sqlx-data

* revs

* removed comments

* delete revs
This commit is contained in:
Wyatt Verchere
2023-10-06 09:57:33 -07:00
committed by GitHub
parent a1b59d4545
commit 259c5ef3d0
69 changed files with 4167 additions and 1312 deletions

View File

@@ -3,16 +3,17 @@ use crate::auth::session::issue_session;
use crate::auth::validate::get_user_record_from_bearer_token;
use crate::auth::{get_user_from_headers, AuthenticationError};
use crate::database::models::flow_item::Flow;
use crate::database::redis::RedisPool;
use crate::file_hosting::FileHost;
use crate::models::ids::base62_impl::{parse_base62, to_base62};
use crate::models::ids::random_base62_rng;
use crate::models::pats::Scopes;
use crate::models::users::{Badges, Role};
use crate::parse_strings_from_var;
use crate::queue::session::AuthQueue;
use crate::queue::socket::ActiveSockets;
use crate::routes::ApiError;
use crate::util::captcha::check_turnstile_captcha;
use crate::util::env::parse_strings_from_var;
use crate::util::ext::{get_image_content_type, get_image_ext};
use crate::util::validate::{validation_errors_to_string, RE_URL_SAFE};
use actix_web::web::{scope, Data, Payload, Query, ServiceConfig};
@@ -54,7 +55,7 @@ pub fn config(cfg: &mut ServiceConfig) {
);
}
#[derive(Serialize, Deserialize, Default, Eq, PartialEq, Clone, Copy)]
#[derive(Serialize, Deserialize, Default, Eq, PartialEq, Clone, Copy, Debug)]
#[serde(rename_all = "lowercase")]
pub enum AuthProvider {
#[default]
@@ -84,7 +85,7 @@ impl TempUser {
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
client: &PgPool,
file_host: &Arc<dyn FileHost + Send + Sync>,
redis: &deadpool_redis::Pool,
redis: &RedisPool,
) -> Result<crate::database::models::UserId, AuthenticationError> {
if let Some(email) = &self.email {
if crate::database::models::User::get_email(email, client)
@@ -907,7 +908,7 @@ pub async fn init(
req: HttpRequest,
Query(info): Query<AuthorizationInit>, // callback url
client: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, AuthenticationError> {
let url = url::Url::parse(&info.url).map_err(|_| AuthenticationError::Url)?;
@@ -959,7 +960,7 @@ pub async fn ws_init(
Query(info): Query<WsInit>,
body: Payload,
db: Data<RwLock<ActiveSockets>>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
) -> Result<HttpResponse, actix_web::Error> {
let (res, session, _msg_stream) = actix_ws::handle(&req, body)?;
@@ -967,7 +968,7 @@ pub async fn ws_init(
mut ws_stream: actix_ws::Session,
info: WsInit,
db: Data<RwLock<ActiveSockets>>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
) -> Result<(), Closed> {
let flow = Flow::OAuth {
user_id: None,
@@ -1003,7 +1004,7 @@ pub async fn auth_callback(
active_sockets: Data<RwLock<ActiveSockets>>,
client: Data<PgPool>,
file_host: Data<Arc<dyn FileHost + Send + Sync>>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
) -> Result<HttpResponse, super::templates::ErrorPage> {
let state_string = query
.get("state")
@@ -1210,7 +1211,7 @@ pub struct DeleteAuthProvider {
pub async fn delete_auth_provider(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
delete_provider: web::Json<DeleteAuthProvider>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
@@ -1297,7 +1298,7 @@ pub struct NewAccount {
pub async fn create_account_with_password(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
new_account: web::Json<NewAccount>,
) -> Result<HttpResponse, ApiError> {
new_account
@@ -1414,7 +1415,7 @@ pub struct Login {
pub async fn login_password(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
login: web::Json<Login>,
) -> Result<HttpResponse, ApiError> {
if !check_turnstile_captcha(&req, &login.challenge).await? {
@@ -1478,7 +1479,7 @@ async fn validate_2fa_code(
secret: String,
allow_backup: bool,
user_id: crate::database::models::UserId,
redis: &deadpool_redis::Pool,
redis: &RedisPool,
pool: &PgPool,
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<bool, AuthenticationError> {
@@ -1530,7 +1531,7 @@ async fn validate_2fa_code(
pub async fn login_2fa(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
login: web::Json<Login2FA>,
) -> Result<HttpResponse, ApiError> {
let flow = Flow::get(&login.flow, &redis)
@@ -1577,7 +1578,7 @@ pub async fn login_2fa(
pub async fn begin_2fa_flow(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
let user = get_user_from_headers(
@@ -1616,7 +1617,7 @@ pub async fn begin_2fa_flow(
pub async fn finish_2fa_flow(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
login: web::Json<Login2FA>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
@@ -1739,7 +1740,7 @@ pub struct Remove2FA {
pub async fn remove_2fa(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
login: web::Json<Remove2FA>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
@@ -1821,7 +1822,7 @@ pub struct ResetPassword {
pub async fn reset_password_begin(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
reset_password: web::Json<ResetPassword>,
) -> Result<HttpResponse, ApiError> {
if !check_turnstile_captcha(&req, &reset_password.challenge).await? {
@@ -1866,7 +1867,7 @@ pub struct ChangePassword {
pub async fn change_password(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
change_password: web::Json<ChangePassword>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
@@ -2007,7 +2008,7 @@ pub struct SetEmail {
pub async fn set_email(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
email: web::Json<SetEmail>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
@@ -2073,7 +2074,7 @@ pub async fn set_email(
pub async fn resend_verify_email(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
let user = get_user_from_headers(
@@ -2118,7 +2119,7 @@ pub struct VerifyEmail {
#[post("email/verify")]
pub async fn verify_email(
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
email: web::Json<VerifyEmail>,
) -> Result<HttpResponse, ApiError> {
let flow = Flow::get(&email.flow, &redis).await?;
@@ -2168,7 +2169,7 @@ pub async fn verify_email(
pub async fn subscribe_newsletter(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
let user = get_user_from_headers(

View File

@@ -4,6 +4,7 @@ use crate::database::models::generate_pat_id;
use crate::auth::get_user_from_headers;
use crate::routes::ApiError;
use crate::database::redis::RedisPool;
use actix_web::web::{self, Data};
use actix_web::{delete, get, patch, post, HttpRequest, HttpResponse};
use chrono::{DateTime, Utc};
@@ -30,7 +31,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
pub async fn get_pats(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
let user = get_user_from_headers(
@@ -73,14 +74,14 @@ pub async fn create_pat(
req: HttpRequest,
info: web::Json<NewPersonalAccessToken>,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
info.0
.validate()
.map_err(|err| ApiError::InvalidInput(validation_errors_to_string(err, None)))?;
if info.scopes.restricted() {
if info.scopes.is_restricted() {
return Err(ApiError::InvalidInput(
"Invalid scopes requested!".to_string(),
));
@@ -159,7 +160,7 @@ pub async fn edit_pat(
id: web::Path<(String,)>,
info: web::Json<ModifyPersonalAccessToken>,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
let user = get_user_from_headers(
@@ -180,7 +181,7 @@ pub async fn edit_pat(
let mut transaction = pool.begin().await?;
if let Some(scopes) = &info.scopes {
if scopes.restricted() {
if scopes.is_restricted() {
return Err(ApiError::InvalidInput(
"Invalid scopes requested!".to_string(),
));
@@ -248,7 +249,7 @@ pub async fn delete_pat(
req: HttpRequest,
id: web::Path<(String,)>,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
let user = get_user_from_headers(

View File

@@ -2,6 +2,7 @@ use crate::auth::{get_user_from_headers, AuthenticationError};
use crate::database::models::session_item::Session as DBSession;
use crate::database::models::session_item::SessionBuilder;
use crate::database::models::UserId;
use crate::database::redis::RedisPool;
use crate::models::pats::Scopes;
use crate::models::sessions::Session;
use crate::queue::session::AuthQueue;
@@ -86,7 +87,7 @@ pub async fn issue_session(
req: HttpRequest,
user_id: UserId,
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
redis: &deadpool_redis::Pool,
redis: &RedisPool,
) -> Result<DBSession, AuthenticationError> {
let metadata = get_session_metadata(&req).await?;
@@ -132,7 +133,7 @@ pub async fn issue_session(
pub async fn list(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
let current_user = get_user_from_headers(
@@ -167,7 +168,7 @@ pub async fn delete(
info: web::Path<(String,)>,
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
let current_user = get_user_from_headers(
@@ -206,7 +207,7 @@ pub async fn delete(
pub async fn refresh(
req: HttpRequest,
pool: Data<PgPool>,
redis: Data<deadpool_redis::Pool>,
redis: Data<RedisPool>,
session_queue: Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
let current_user = get_user_from_headers(&req, &**pool, &redis, &session_queue, None)

View File

@@ -2,6 +2,7 @@ use crate::auth::flows::AuthProvider;
use crate::auth::session::get_session_metadata;
use crate::auth::AuthenticationError;
use crate::database::models::user_item;
use crate::database::redis::RedisPool;
use crate::models::pats::Scopes;
use crate::models::users::{Role, User, UserId, UserPayoutData};
use crate::queue::session::AuthQueue;
@@ -12,7 +13,7 @@ use reqwest::header::{HeaderValue, AUTHORIZATION};
pub async fn get_user_from_headers<'a, E>(
req: &HttpRequest,
executor: E,
redis: &deadpool_redis::Pool,
redis: &RedisPool,
session_queue: &AuthQueue,
required_scopes: Option<&[Scopes]>,
) -> Result<(Scopes, User), AuthenticationError>
@@ -82,7 +83,7 @@ pub async fn get_user_record_from_bearer_token<'a, 'b, E>(
req: &HttpRequest,
token: Option<&str>,
executor: E,
redis: &deadpool_redis::Pool,
redis: &RedisPool,
session_queue: &AuthQueue,
) -> Result<Option<(Scopes, user_item::User)>, AuthenticationError>
where
@@ -140,7 +141,7 @@ where
session_queue.add_session(session.id, metadata).await;
}
user.map(|x| (Scopes::ALL, x))
user.map(|x| (Scopes::all(), x))
}
Some(("github", _)) | Some(("gho", _)) | Some(("ghp", _)) => {
let user = AuthProvider::GitHub.get_user(token).await?;
@@ -153,7 +154,7 @@ where
)
.await?;
user.map(|x| (Scopes::NOT_RESTRICTED, x))
user.map(|x| ((Scopes::all() ^ Scopes::restricted()), x))
}
_ => return Err(AuthenticationError::InvalidAuthMethod),
};
@@ -163,13 +164,14 @@ where
pub async fn check_is_moderator_from_headers<'a, 'b, E>(
req: &HttpRequest,
executor: E,
redis: &deadpool_redis::Pool,
redis: &RedisPool,
session_queue: &AuthQueue,
required_scopes: Option<&[Scopes]>,
) -> Result<User, AuthenticationError>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
{
let user = get_user_from_headers(req, executor, redis, session_queue, None)
let user = get_user_from_headers(req, executor, redis, session_queue, required_scopes)
.await?
.1;