forked from didirus/AstralRinth
* chore: inherit dependencies from workspace, optimize some deps out * Update bitflags from 2.9.0 to 2.9.1 * Fix temp directory leak in check_java_at_filepath * Fix build * Fix lint * chore(app-lib): refactor overkill `futures` executor usage to Tokio MPSC * chore: fix Clippy lint * tweak: optimize out dependency on OpenSSL source build Contrary to what I expected before, this was caused due to the Tauri updater plugin using a different TLS stack than everything else. * chore(labrinth): drop now unused dependency * Update zip because 2.6.1 got yanked * Downgrade weezl to 0.1.8 * Mention that p256 is also a blocker for rand 0.9 * chore: sidestep GitHub review requirements * chore: sidestep GitHub review requirements (2) * chore: sidestep GitHub review requirements (3) --------- Co-authored-by: Josiah Glosson <soujournme@gmail.com>
109 lines
3.2 KiB
Rust
109 lines
3.2 KiB
Rust
use super::errors::OAuthError;
|
|
use crate::auth::oauth::OAuthErrorType;
|
|
use crate::database::models::OAuthClientId;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
pub struct OAuthRedirectUris {
|
|
pub original: Option<String>,
|
|
pub validated: ValidatedRedirectUri,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub struct ValidatedRedirectUri(pub String);
|
|
|
|
impl ValidatedRedirectUri {
|
|
pub fn validate<'a>(
|
|
to_validate: &Option<String>,
|
|
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(to_validate) = 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(),
|
|
),
|
|
))
|
|
}
|
|
} else {
|
|
Ok(ValidatedRedirectUri(first_client_redirect_uri.to_string()))
|
|
}
|
|
} else {
|
|
Err(OAuthError::error(
|
|
OAuthErrorType::ClientMissingRedirectURI { client_id },
|
|
))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn same_uri_except_query_components(a: &str, b: &str) -> bool {
|
|
let mut a_components = a.split('?');
|
|
let mut b_components = b.split('?');
|
|
a_components.next() == b_components.next()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
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();
|
|
|
|
assert_eq!(validate_against[0], validated.0);
|
|
}
|
|
|
|
#[test]
|
|
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 validated = ValidatedRedirectUri::validate(
|
|
&Some(to_validate.clone()),
|
|
validate_against,
|
|
OAuthClientId(0),
|
|
)
|
|
.unwrap();
|
|
|
|
assert_eq!(to_validate, validated.0);
|
|
}
|
|
|
|
#[test]
|
|
fn validate_for_invalid_uri_returns_err() {
|
|
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),
|
|
);
|
|
|
|
assert!(validated.is_err_and(|e| matches!(
|
|
e.error_type,
|
|
OAuthErrorType::RedirectUriNotConfigured(_)
|
|
)));
|
|
}
|
|
}
|