You've already forked AstralRinth
forked from didirus/AstralRinth
Fix clippy errors + lint, use turbo CI
This commit is contained in:
@@ -10,11 +10,17 @@ use itertools::Itertools;
|
||||
use sqlx::PgPool;
|
||||
|
||||
pub trait ValidateAuthorized {
|
||||
fn validate_authorized(&self, user_option: Option<&User>) -> Result<(), ApiError>;
|
||||
fn validate_authorized(
|
||||
&self,
|
||||
user_option: Option<&User>,
|
||||
) -> Result<(), ApiError>;
|
||||
}
|
||||
|
||||
pub trait ValidateAllAuthorized {
|
||||
fn validate_all_authorized(self, user_option: Option<&User>) -> Result<(), ApiError>;
|
||||
fn validate_all_authorized(
|
||||
self,
|
||||
user_option: Option<&User>,
|
||||
) -> Result<(), ApiError>;
|
||||
}
|
||||
|
||||
impl<'a, T, A> ValidateAllAuthorized for T
|
||||
@@ -22,7 +28,10 @@ where
|
||||
T: IntoIterator<Item = &'a A>,
|
||||
A: ValidateAuthorized + 'a,
|
||||
{
|
||||
fn validate_all_authorized(self, user_option: Option<&User>) -> Result<(), ApiError> {
|
||||
fn validate_all_authorized(
|
||||
self,
|
||||
user_option: Option<&User>,
|
||||
) -> Result<(), ApiError> {
|
||||
self.into_iter()
|
||||
.try_for_each(|c| c.validate_authorized(user_option))
|
||||
}
|
||||
@@ -34,9 +43,14 @@ pub async fn is_visible_project(
|
||||
pool: &PgPool,
|
||||
hide_unlisted: bool,
|
||||
) -> Result<bool, ApiError> {
|
||||
filter_visible_project_ids(vec![project_data], user_option, pool, hide_unlisted)
|
||||
.await
|
||||
.map(|x| !x.is_empty())
|
||||
filter_visible_project_ids(
|
||||
vec![project_data],
|
||||
user_option,
|
||||
pool,
|
||||
hide_unlisted,
|
||||
)
|
||||
.await
|
||||
.map(|x| !x.is_empty())
|
||||
}
|
||||
|
||||
pub async fn is_team_member_project(
|
||||
@@ -99,8 +113,10 @@ pub async fn filter_visible_project_ids(
|
||||
|
||||
// For hidden projects, return a filtered list of projects for which we are enlisted on the team
|
||||
if !check_projects.is_empty() {
|
||||
return_projects
|
||||
.extend(filter_enlisted_projects_ids(check_projects, user_option, pool).await?);
|
||||
return_projects.extend(
|
||||
filter_enlisted_projects_ids(check_projects, user_option, pool)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(return_projects)
|
||||
@@ -143,7 +159,8 @@ pub async fn filter_enlisted_projects_ids(
|
||||
.fetch(pool)
|
||||
.map_ok(|row| {
|
||||
for x in projects.iter() {
|
||||
let bool = Some(x.id.0) == row.id && Some(x.team_id.0) == row.team_id;
|
||||
let bool =
|
||||
Some(x.id.0) == row.id && Some(x.team_id.0) == row.team_id;
|
||||
if bool {
|
||||
return_projects.push(x.id);
|
||||
}
|
||||
@@ -195,7 +212,10 @@ pub async fn filter_visible_versions(
|
||||
}
|
||||
|
||||
impl ValidateAuthorized for models::OAuthClient {
|
||||
fn validate_authorized(&self, user_option: Option<&User>) -> Result<(), ApiError> {
|
||||
fn validate_authorized(
|
||||
&self,
|
||||
user_option: Option<&User>,
|
||||
) -> Result<(), ApiError> {
|
||||
if let Some(user) = user_option {
|
||||
return if user.role.is_mod() || user.id == self.created_by.into() {
|
||||
Ok(())
|
||||
@@ -240,7 +260,8 @@ pub async fn filter_visible_version_ids(
|
||||
|
||||
// Then, get enlisted versions (Versions that are a part of a project we are a member of)
|
||||
let enlisted_version_ids =
|
||||
filter_enlisted_version_ids(versions.clone(), user_option, pool, redis).await?;
|
||||
filter_enlisted_version_ids(versions.clone(), user_option, pool, redis)
|
||||
.await?;
|
||||
|
||||
// Return versions that are not hidden, we are a mod of, or we are enlisted on the team of
|
||||
for version in versions {
|
||||
@@ -248,7 +269,8 @@ pub async fn filter_visible_version_ids(
|
||||
// - it's not hidden and we can see the project
|
||||
// - we are a mod
|
||||
// - we are enlisted on the team of the mod
|
||||
if (!version.status.is_hidden() && visible_project_ids.contains(&version.project_id))
|
||||
if (!version.status.is_hidden()
|
||||
&& visible_project_ids.contains(&version.project_id))
|
||||
|| user_option
|
||||
.as_ref()
|
||||
.map(|x| x.role.is_mod())
|
||||
@@ -292,7 +314,8 @@ pub async fn filter_enlisted_version_ids(
|
||||
.as_ref()
|
||||
.map(|x| x.role.is_mod())
|
||||
.unwrap_or(false)
|
||||
|| (user_option.is_some() && authorized_project_ids.contains(&version.project_id))
|
||||
|| (user_option.is_some()
|
||||
&& authorized_project_ids.contains(&version.project_id))
|
||||
{
|
||||
return_versions.push(version.id);
|
||||
}
|
||||
@@ -307,7 +330,9 @@ pub async fn is_visible_collection(
|
||||
) -> Result<bool, ApiError> {
|
||||
let mut authorized = !collection_data.status.is_hidden();
|
||||
if let Some(user) = &user_option {
|
||||
if !authorized && (user.role.is_mod() || user.id == collection_data.user_id.into()) {
|
||||
if !authorized
|
||||
&& (user.role.is_mod() || user.id == collection_data.user_id.into())
|
||||
{
|
||||
authorized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@ pub enum MailError {
|
||||
Smtp(#[from] lettre::transport::smtp::Error),
|
||||
}
|
||||
|
||||
pub fn send_email_raw(to: String, subject: String, body: String) -> Result<(), MailError> {
|
||||
pub fn send_email_raw(
|
||||
to: String,
|
||||
subject: String,
|
||||
body: String,
|
||||
) -> Result<(), MailError> {
|
||||
let email = Message::builder()
|
||||
.from(Mailbox::new(
|
||||
Some("Modrinth".to_string()),
|
||||
|
||||
@@ -5,8 +5,9 @@ pub mod templates;
|
||||
pub mod validate;
|
||||
pub use crate::auth::email::send_email;
|
||||
pub use checks::{
|
||||
filter_enlisted_projects_ids, filter_enlisted_version_ids, filter_visible_collections,
|
||||
filter_visible_project_ids, filter_visible_projects,
|
||||
filter_enlisted_projects_ids, filter_enlisted_version_ids,
|
||||
filter_visible_collections, filter_visible_project_ids,
|
||||
filter_visible_projects,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
// pub use pat::{generate_pat, PersonalAccessToken};
|
||||
@@ -55,16 +56,22 @@ impl actix_web::ResponseError for AuthenticationError {
|
||||
match self {
|
||||
AuthenticationError::Env(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AuthenticationError::Sqlx(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AuthenticationError::Database(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AuthenticationError::Database(..) => {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
AuthenticationError::SerDe(..) => StatusCode::BAD_REQUEST,
|
||||
AuthenticationError::Reqwest(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AuthenticationError::Reqwest(..) => {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
AuthenticationError::InvalidCredentials => StatusCode::UNAUTHORIZED,
|
||||
AuthenticationError::Decoding(..) => StatusCode::BAD_REQUEST,
|
||||
AuthenticationError::Mail(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AuthenticationError::InvalidAuthMethod => StatusCode::UNAUTHORIZED,
|
||||
AuthenticationError::InvalidClientId => StatusCode::UNAUTHORIZED,
|
||||
AuthenticationError::Url => StatusCode::BAD_REQUEST,
|
||||
AuthenticationError::FileHosting(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AuthenticationError::FileHosting(..) => {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
AuthenticationError::DuplicateUser => StatusCode::BAD_REQUEST,
|
||||
AuthenticationError::SocketError => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
@@ -99,7 +106,9 @@ impl AuthenticationError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Eq, PartialEq, Clone, Copy, Debug)]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Default, Eq, PartialEq, Clone, Copy, Debug,
|
||||
)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum AuthProvider {
|
||||
#[default]
|
||||
|
||||
@@ -77,12 +77,16 @@ impl actix_web::ResponseError for OAuthError {
|
||||
| OAuthErrorType::OnlySupportsAuthorizationCodeGrant(_)
|
||||
| OAuthErrorType::RedirectUriChanged(_)
|
||||
| OAuthErrorType::UnauthorizedClient => StatusCode::BAD_REQUEST,
|
||||
OAuthErrorType::ClientAuthenticationFailed => StatusCode::UNAUTHORIZED,
|
||||
OAuthErrorType::ClientAuthenticationFailed => {
|
||||
StatusCode::UNAUTHORIZED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
if let Some(ValidatedRedirectUri(mut redirect_uri)) = self.valid_redirect_uri.clone() {
|
||||
if let Some(ValidatedRedirectUri(mut redirect_uri)) =
|
||||
self.valid_redirect_uri.clone()
|
||||
{
|
||||
redirect_uri = format!(
|
||||
"{}?error={}&error_description={}",
|
||||
redirect_uri,
|
||||
@@ -114,7 +118,9 @@ pub enum OAuthErrorType {
|
||||
ClientMissingRedirectURI {
|
||||
client_id: crate::database::models::OAuthClientId,
|
||||
},
|
||||
#[error("The provided redirect URI did not match any configured in the client")]
|
||||
#[error(
|
||||
"The provided redirect URI did not match any configured in the client"
|
||||
)]
|
||||
RedirectUriNotConfigured(String),
|
||||
#[error("The provided scope was malformed or did not correspond to known scopes ({0})")]
|
||||
FailedScopeParse(bitflags::parser::ParseError),
|
||||
@@ -159,14 +165,20 @@ impl OAuthErrorType {
|
||||
// IETF RFC 6749 4.1.2.1 (https://datatracker.ietf.org/doc/html/rfc6749#autoid-38)
|
||||
// And 5.2 (https://datatracker.ietf.org/doc/html/rfc6749#section-5.2)
|
||||
match self {
|
||||
Self::RedirectUriNotConfigured(_) | Self::ClientMissingRedirectURI { client_id: _ } => {
|
||||
"invalid_uri"
|
||||
Self::RedirectUriNotConfigured(_)
|
||||
| Self::ClientMissingRedirectURI { client_id: _ } => "invalid_uri",
|
||||
Self::AuthenticationError(_) | Self::InvalidAcceptFlowId => {
|
||||
"server_error"
|
||||
}
|
||||
Self::RedirectUriChanged(_) | Self::MalformedId(_) => {
|
||||
"invalid_request"
|
||||
}
|
||||
Self::AuthenticationError(_) | Self::InvalidAcceptFlowId => "server_error",
|
||||
Self::RedirectUriChanged(_) | Self::MalformedId(_) => "invalid_request",
|
||||
Self::FailedScopeParse(_) | Self::ScopesTooBroad => "invalid_scope",
|
||||
Self::InvalidClientId(_) | Self::ClientAuthenticationFailed => "invalid_client",
|
||||
Self::InvalidAuthCode | Self::OnlySupportsAuthorizationCodeGrant(_) => "invalid_grant",
|
||||
Self::InvalidClientId(_) | Self::ClientAuthenticationFailed => {
|
||||
"invalid_client"
|
||||
}
|
||||
Self::InvalidAuthCode
|
||||
| Self::OnlySupportsAuthorizationCodeGrant(_) => "invalid_grant",
|
||||
Self::UnauthorizedClient => "unauthorized_client",
|
||||
Self::AccessDenied => "access_denied",
|
||||
}
|
||||
|
||||
@@ -84,18 +84,19 @@ pub async fn init_oauth(
|
||||
client.id,
|
||||
)?;
|
||||
|
||||
let requested_scopes = oauth_info
|
||||
.scope
|
||||
.as_ref()
|
||||
.map_or(Ok(client.max_scopes), |s| {
|
||||
Scopes::parse_from_oauth_scopes(s).map_err(|e| {
|
||||
OAuthError::redirect(
|
||||
OAuthErrorType::FailedScopeParse(e),
|
||||
&oauth_info.state,
|
||||
&redirect_uri,
|
||||
)
|
||||
})
|
||||
})?;
|
||||
let requested_scopes =
|
||||
oauth_info
|
||||
.scope
|
||||
.as_ref()
|
||||
.map_or(Ok(client.max_scopes), |s| {
|
||||
Scopes::parse_from_oauth_scopes(s).map_err(|e| {
|
||||
OAuthError::redirect(
|
||||
OAuthErrorType::FailedScopeParse(e),
|
||||
&oauth_info.state,
|
||||
&redirect_uri,
|
||||
)
|
||||
})
|
||||
})?;
|
||||
|
||||
if !client.max_scopes.contains(requested_scopes) {
|
||||
return Err(OAuthError::redirect(
|
||||
@@ -108,9 +109,13 @@ pub async fn init_oauth(
|
||||
let existing_authorization =
|
||||
OAuthClientAuthorization::get(client.id, user.id.into(), &**pool)
|
||||
.await
|
||||
.map_err(|e| OAuthError::redirect(e, &oauth_info.state, &redirect_uri))?;
|
||||
let redirect_uris =
|
||||
OAuthRedirectUris::new(oauth_info.redirect_uri.clone(), redirect_uri.clone());
|
||||
.map_err(|e| {
|
||||
OAuthError::redirect(e, &oauth_info.state, &redirect_uri)
|
||||
})?;
|
||||
let redirect_uris = OAuthRedirectUris::new(
|
||||
oauth_info.redirect_uri.clone(),
|
||||
redirect_uri.clone(),
|
||||
);
|
||||
match existing_authorization {
|
||||
Some(existing_authorization)
|
||||
if existing_authorization.scopes.contains(requested_scopes) =>
|
||||
@@ -130,14 +135,17 @@ pub async fn init_oauth(
|
||||
let flow_id = Flow::InitOAuthAppApproval {
|
||||
user_id: user.id.into(),
|
||||
client_id: client.id,
|
||||
existing_authorization_id: existing_authorization.map(|a| a.id),
|
||||
existing_authorization_id: existing_authorization
|
||||
.map(|a| a.id),
|
||||
scopes: requested_scopes,
|
||||
redirect_uris,
|
||||
state: oauth_info.state.clone(),
|
||||
}
|
||||
.insert(Duration::minutes(30), &redis)
|
||||
.await
|
||||
.map_err(|e| OAuthError::redirect(e, &oauth_info.state, &redirect_uri))?;
|
||||
.map_err(|e| {
|
||||
OAuthError::redirect(e, &oauth_info.state, &redirect_uri)
|
||||
})?;
|
||||
|
||||
let access_request = OAuthClientAccessRequest {
|
||||
client_id: client.id.into(),
|
||||
@@ -169,7 +177,15 @@ pub async fn accept_client_scopes(
|
||||
redis: Data<RedisPool>,
|
||||
session_queue: Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, OAuthError> {
|
||||
accept_or_reject_client_scopes(true, req, accept_body, pool, redis, session_queue).await
|
||||
accept_or_reject_client_scopes(
|
||||
true,
|
||||
req,
|
||||
accept_body,
|
||||
pool,
|
||||
redis,
|
||||
session_queue,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[post("reject")]
|
||||
@@ -180,7 +196,8 @@ pub async fn reject_client_scopes(
|
||||
redis: Data<RedisPool>,
|
||||
session_queue: Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, OAuthError> {
|
||||
accept_or_reject_client_scopes(false, req, body, pool, redis, session_queue).await
|
||||
accept_or_reject_client_scopes(false, req, body, pool, redis, session_queue)
|
||||
.await
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -231,13 +248,17 @@ pub async fn request_token(
|
||||
{
|
||||
// https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
|
||||
if req_client_id != client_id.into() {
|
||||
return Err(OAuthError::error(OAuthErrorType::UnauthorizedClient));
|
||||
return Err(OAuthError::error(
|
||||
OAuthErrorType::UnauthorizedClient,
|
||||
));
|
||||
}
|
||||
|
||||
if original_redirect_uri != req_params.redirect_uri {
|
||||
return Err(OAuthError::error(OAuthErrorType::RedirectUriChanged(
|
||||
req_params.redirect_uri.clone(),
|
||||
)));
|
||||
return Err(OAuthError::error(
|
||||
OAuthErrorType::RedirectUriChanged(
|
||||
req_params.redirect_uri.clone(),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
if req_params.grant_type != "authorization_code" {
|
||||
@@ -251,7 +272,8 @@ pub async fn request_token(
|
||||
let scopes = scopes - Scopes::restricted();
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
let token_id = generate_oauth_access_token_id(&mut transaction).await?;
|
||||
let token_id =
|
||||
generate_oauth_access_token_id(&mut transaction).await?;
|
||||
let token = generate_access_token();
|
||||
let token_hash = OAuthAccessToken::hash_token(&token);
|
||||
let time_until_expiration = OAuthAccessToken {
|
||||
@@ -323,7 +345,9 @@ pub async fn accept_or_reject_client_scopes(
|
||||
}) = flow
|
||||
{
|
||||
if current_user.id != user_id.into() {
|
||||
return Err(OAuthError::error(AuthenticationError::InvalidCredentials));
|
||||
return Err(OAuthError::error(
|
||||
AuthenticationError::InvalidCredentials,
|
||||
));
|
||||
}
|
||||
|
||||
if accept {
|
||||
@@ -331,10 +355,19 @@ pub async fn accept_or_reject_client_scopes(
|
||||
|
||||
let auth_id = match existing_authorization_id {
|
||||
Some(id) => id,
|
||||
None => generate_oauth_client_authorization_id(&mut transaction).await?,
|
||||
None => {
|
||||
generate_oauth_client_authorization_id(&mut transaction)
|
||||
.await?
|
||||
}
|
||||
};
|
||||
OAuthClientAuthorization::upsert(auth_id, client_id, user_id, scopes, &mut transaction)
|
||||
.await?;
|
||||
OAuthClientAuthorization::upsert(
|
||||
auth_id,
|
||||
client_id,
|
||||
user_id,
|
||||
scopes,
|
||||
&mut transaction,
|
||||
)
|
||||
.await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
@@ -402,14 +435,17 @@ async fn init_oauth_code_flow(
|
||||
}
|
||||
.insert(Duration::minutes(10), redis)
|
||||
.await
|
||||
.map_err(|e| OAuthError::redirect(e, &state, &redirect_uris.validated.clone()))?;
|
||||
.map_err(|e| {
|
||||
OAuthError::redirect(e, &state, &redirect_uris.validated.clone())
|
||||
})?;
|
||||
|
||||
let mut redirect_params = vec![format!("code={code}")];
|
||||
if let Some(state) = state {
|
||||
redirect_params.push(format!("state={state}"));
|
||||
}
|
||||
|
||||
let redirect_uri = append_params_to_uri(&redirect_uris.validated.0, &redirect_params);
|
||||
let redirect_uri =
|
||||
append_params_to_uri(&redirect_uris.validated.0, &redirect_params);
|
||||
|
||||
// IETF RFC 6749 Section 4.1.2 (https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2)
|
||||
Ok(HttpResponse::Ok()
|
||||
|
||||
@@ -18,17 +18,20 @@ impl ValidatedRedirectUri {
|
||||
validate_against: impl IntoIterator<Item = &'a str> + Clone,
|
||||
client_id: OAuthClientId,
|
||||
) -> Result<Self, OAuthError> {
|
||||
if let Some(first_client_redirect_uri) = validate_against.clone().into_iter().next() {
|
||||
if let Some(first_client_redirect_uri) =
|
||||
validate_against.clone().into_iter().next()
|
||||
{
|
||||
if let Some(to_validate) = to_validate {
|
||||
if validate_against
|
||||
.into_iter()
|
||||
.any(|uri| same_uri_except_query_components(uri, to_validate))
|
||||
{
|
||||
if validate_against.into_iter().any(|uri| {
|
||||
same_uri_except_query_components(uri, to_validate)
|
||||
}) {
|
||||
Ok(ValidatedRedirectUri(to_validate.clone()))
|
||||
} else {
|
||||
Err(OAuthError::error(OAuthErrorType::RedirectUriNotConfigured(
|
||||
to_validate.clone(),
|
||||
)))
|
||||
Err(OAuthError::error(
|
||||
OAuthErrorType::RedirectUriNotConfigured(
|
||||
to_validate.clone(),
|
||||
),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Ok(ValidatedRedirectUri(first_client_redirect_uri.to_string()))
|
||||
@@ -55,20 +58,26 @@ mod tests {
|
||||
fn validate_for_none_returns_first_valid_uri() {
|
||||
let validate_against = vec!["https://modrinth.com/a"];
|
||||
|
||||
let validated =
|
||||
ValidatedRedirectUri::validate(&None, validate_against.clone(), OAuthClientId(0))
|
||||
.unwrap();
|
||||
let validated = ValidatedRedirectUri::validate(
|
||||
&None,
|
||||
validate_against.clone(),
|
||||
OAuthClientId(0),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(validate_against[0], validated.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_for_valid_uri_returns_first_matching_uri_ignoring_query_params() {
|
||||
fn validate_for_valid_uri_returns_first_matching_uri_ignoring_query_params()
|
||||
{
|
||||
let validate_against = vec![
|
||||
"https://modrinth.com/a?q3=p3&q4=p4",
|
||||
"https://modrinth.com/a/b/c?q1=p1&q2=p2",
|
||||
];
|
||||
let to_validate = "https://modrinth.com/a/b/c?query0=param0&query1=param1".to_string();
|
||||
let to_validate =
|
||||
"https://modrinth.com/a/b/c?query0=param0&query1=param1"
|
||||
.to_string();
|
||||
|
||||
let validated = ValidatedRedirectUri::validate(
|
||||
&Some(to_validate.clone()),
|
||||
@@ -85,10 +94,15 @@ mod tests {
|
||||
let validate_against = vec!["https://modrinth.com/a"];
|
||||
let to_validate = "https://modrinth.com/a/b".to_string();
|
||||
|
||||
let validated =
|
||||
ValidatedRedirectUri::validate(&Some(to_validate), validate_against, OAuthClientId(0));
|
||||
let validated = ValidatedRedirectUri::validate(
|
||||
&Some(to_validate),
|
||||
validate_against,
|
||||
OAuthClientId(0),
|
||||
);
|
||||
|
||||
assert!(validated
|
||||
.is_err_and(|e| matches!(e.error_type, OAuthErrorType::RedirectUriNotConfigured(_))));
|
||||
assert!(validated.is_err_and(|e| matches!(
|
||||
e.error_type,
|
||||
OAuthErrorType::RedirectUriNotConfigured(_)
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,15 @@ where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
// Fetch DB user record and minos user from headers
|
||||
let (scopes, db_user) =
|
||||
get_user_record_from_bearer_token(req, None, executor, redis, session_queue)
|
||||
.await?
|
||||
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
|
||||
let (scopes, db_user) = get_user_record_from_bearer_token(
|
||||
req,
|
||||
None,
|
||||
executor,
|
||||
redis,
|
||||
session_queue,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
|
||||
|
||||
let user = User::from_full(db_user);
|
||||
|
||||
@@ -58,31 +63,37 @@ where
|
||||
let possible_user = match token.split_once('_') {
|
||||
Some(("mrp", _)) => {
|
||||
let pat =
|
||||
crate::database::models::pat_item::PersonalAccessToken::get(token, executor, redis)
|
||||
.await?
|
||||
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
|
||||
crate::database::models::pat_item::PersonalAccessToken::get(
|
||||
token, executor, redis,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
|
||||
|
||||
if pat.expires < Utc::now() {
|
||||
return Err(AuthenticationError::InvalidCredentials);
|
||||
}
|
||||
|
||||
let user = user_item::User::get_id(pat.user_id, executor, redis).await?;
|
||||
let user =
|
||||
user_item::User::get_id(pat.user_id, executor, redis).await?;
|
||||
|
||||
session_queue.add_pat(pat.id).await;
|
||||
|
||||
user.map(|x| (pat.scopes, x))
|
||||
}
|
||||
Some(("mra", _)) => {
|
||||
let session =
|
||||
crate::database::models::session_item::Session::get(token, executor, redis)
|
||||
.await?
|
||||
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
|
||||
let session = crate::database::models::session_item::Session::get(
|
||||
token, executor, redis,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
|
||||
|
||||
if session.expires < Utc::now() {
|
||||
return Err(AuthenticationError::InvalidCredentials);
|
||||
}
|
||||
|
||||
let user = user_item::User::get_id(session.user_id, executor, redis).await?;
|
||||
let user =
|
||||
user_item::User::get_id(session.user_id, executor, redis)
|
||||
.await?;
|
||||
|
||||
let rate_limit_ignore = dotenvy::var("RATE_LIMIT_IGNORE_KEY")?;
|
||||
if !req
|
||||
@@ -111,7 +122,9 @@ where
|
||||
return Err(AuthenticationError::InvalidCredentials);
|
||||
}
|
||||
|
||||
let user = user_item::User::get_id(access_token.user_id, executor, redis).await?;
|
||||
let user =
|
||||
user_item::User::get_id(access_token.user_id, executor, redis)
|
||||
.await?;
|
||||
|
||||
session_queue.add_oauth_access_token(access_token.id).await;
|
||||
|
||||
@@ -119,7 +132,8 @@ where
|
||||
}
|
||||
Some(("github", _)) | Some(("gho", _)) | Some(("ghp", _)) => {
|
||||
let user = AuthProvider::GitHub.get_user(token).await?;
|
||||
let id = AuthProvider::GitHub.get_user_id(&user.id, executor).await?;
|
||||
let id =
|
||||
AuthProvider::GitHub.get_user_id(&user.id, executor).await?;
|
||||
|
||||
let user = user_item::User::get_id(
|
||||
id.ok_or_else(|| AuthenticationError::InvalidCredentials)?,
|
||||
@@ -135,7 +149,9 @@ where
|
||||
Ok(possible_user)
|
||||
}
|
||||
|
||||
pub fn extract_authorization_header(req: &HttpRequest) -> Result<&str, AuthenticationError> {
|
||||
pub fn extract_authorization_header(
|
||||
req: &HttpRequest,
|
||||
) -> Result<&str, AuthenticationError> {
|
||||
let headers = req.headers();
|
||||
let token_val: Option<&HeaderValue> = headers.get(AUTHORIZATION);
|
||||
token_val
|
||||
@@ -154,9 +170,15 @@ pub async fn check_is_moderator_from_headers<'a, 'b, E>(
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
let user = get_user_from_headers(req, executor, redis, session_queue, required_scopes)
|
||||
.await?
|
||||
.1;
|
||||
let user = get_user_from_headers(
|
||||
req,
|
||||
executor,
|
||||
redis,
|
||||
session_queue,
|
||||
required_scopes,
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
if user.role.is_mod() {
|
||||
Ok(user)
|
||||
|
||||
Reference in New Issue
Block a user