Files
AstralRinth/tests/oauth_clients.rs
Jackson Kruger 6cfd4637db OAuth 2.0 Authorization Server [MOD-559] (#733)
* WIP end-of-day push

* Authorize endpoint, accept endpoints, DB stuff for oauth clients, their redirects, and client authorizations

* OAuth Client create route

* Get user clients

* Client delete

* Edit oauth client

* Include redirects in edit client route

* Database stuff for tokens

* Reorg oauth stuff out of auth/flows and into its own module

* Impl OAuth get access token endpoint

* Accept oauth access tokens as auth and update through AuthQueue

* User OAuth authorization management routes

* Forgot to actually add the routes lol

* Bit o cleanup

* Happy path test for OAuth and minor fixes for things it found

* Add dummy data oauth client (and detect/handle dummy data version changes)

* More tests

* Another test

* More tests and reject endpoint

* Test oauth client and authorization management routes

* cargo sqlx prepare

* dead code warning

* Auto clippy fixes

* Uri refactoring

* minor name improvement

* Don't compile-time check the test sqlx queries

* Trying to fix db concurrency problem to get tests to pass

* Try fix from test PR

* Fixes for updated sqlx

* Prevent restricted scopes from being requested or issued

* Get OAuth client(s)

* Remove joined oauth client info from authorization returns

* Add default conversion to OAuthError::error so we can use ?

* Rework routes

* Consolidate scopes into SESSION_ACCESS

* Cargo sqlx prepare

* Parse to OAuthClientId automatically through serde and actix

* Cargo clippy

* Remove validation requiring 1 redirect URI on oauth client creation

* Use serde(flatten) on OAuthClientCreationResult
2023-10-30 09:14:38 -07:00

194 lines
5.8 KiB
Rust

use actix_http::StatusCode;
use actix_web::test;
use common::{
database::{FRIEND_USER_ID, FRIEND_USER_PAT, USER_USER_ID, USER_USER_PAT},
dummy_data::DummyOAuthClientAlpha,
environment::with_test_environment,
get_json_val_str,
};
use labrinth::{
models::{
oauth_clients::{OAuthClient, OAuthClientCreationResult},
pats::Scopes,
},
routes::v3::oauth_clients::OAuthClientEdit,
};
use crate::common::{asserts::assert_status, database::USER_USER_ID_PARSED};
mod common;
#[actix_rt::test]
async fn can_create_edit_get_oauth_client() {
with_test_environment(|env| async move {
let client_name = "test_client".to_string();
let redirect_uris = vec![
"https://modrinth.com".to_string(),
"https://modrinth.com/a".to_string(),
];
let resp = env
.v3
.add_oauth_client(
client_name.clone(),
Scopes::all() - Scopes::restricted(),
redirect_uris.clone(),
FRIEND_USER_PAT,
)
.await;
assert_status(&resp, StatusCode::OK);
let creation_result: OAuthClientCreationResult = test::read_body_json(resp).await;
let client_id = get_json_val_str(creation_result.client.id);
let icon_url = Some("https://modrinth.com/icon".to_string());
let edited_redirect_uris = vec![
redirect_uris[0].clone(),
"https://modrinth.com/b".to_string(),
];
let edit = OAuthClientEdit {
name: None,
icon_url: Some(icon_url.clone()),
max_scopes: None,
redirect_uris: Some(edited_redirect_uris.clone()),
};
let resp = env
.v3
.edit_oauth_client(&client_id, edit, FRIEND_USER_PAT)
.await;
assert_status(&resp, StatusCode::OK);
let clients = env
.v3
.get_user_oauth_clients(FRIEND_USER_ID, FRIEND_USER_PAT)
.await;
assert_eq!(1, clients.len());
assert_eq!(icon_url, clients[0].icon_url);
assert_eq!(client_name, clients[0].name);
assert_eq!(2, clients[0].redirect_uris.len());
assert_eq!(edited_redirect_uris[0], clients[0].redirect_uris[0].uri);
assert_eq!(edited_redirect_uris[1], clients[0].redirect_uris[1].uri);
})
.await;
}
#[actix_rt::test]
async fn create_oauth_client_with_restricted_scopes_fails() {
with_test_environment(|env| async move {
let resp = env
.v3
.add_oauth_client(
"test_client".to_string(),
Scopes::restricted(),
vec!["https://modrinth.com".to_string()],
FRIEND_USER_PAT,
)
.await;
assert_status(&resp, StatusCode::BAD_REQUEST);
})
.await;
}
#[actix_rt::test]
async fn get_oauth_client_for_client_creator_succeeds() {
with_test_environment(|env| async move {
let DummyOAuthClientAlpha { client_id, .. } =
env.dummy.as_ref().unwrap().oauth_client_alpha.clone();
let resp = env
.v3
.get_oauth_client(client_id.clone(), USER_USER_PAT)
.await;
assert_status(&resp, StatusCode::OK);
let client: OAuthClient = test::read_body_json(resp).await;
assert_eq!(get_json_val_str(client.id), client_id);
})
.await;
}
#[actix_rt::test]
async fn get_oauth_client_for_unrelated_user_fails() {
with_test_environment(|env| async move {
let DummyOAuthClientAlpha { client_id, .. } =
env.dummy.as_ref().unwrap().oauth_client_alpha.clone();
let resp = env
.v3
.get_oauth_client(client_id.clone(), FRIEND_USER_PAT)
.await;
assert_status(&resp, StatusCode::UNAUTHORIZED);
})
.await;
}
#[actix_rt::test]
async fn can_delete_oauth_client() {
with_test_environment(|env| async move {
let client_id = env.dummy.unwrap().oauth_client_alpha.client_id.clone();
let resp = env.v3.delete_oauth_client(&client_id, USER_USER_PAT).await;
assert_status(&resp, StatusCode::NO_CONTENT);
let clients = env
.v3
.get_user_oauth_clients(USER_USER_ID, USER_USER_PAT)
.await;
assert_eq!(0, clients.len());
})
.await;
}
#[actix_rt::test]
async fn delete_oauth_client_after_issuing_access_tokens_revokes_tokens() {
with_test_environment(|env| async move {
let DummyOAuthClientAlpha {
client_id,
client_secret,
..
} = env.dummy.as_ref().unwrap().oauth_client_alpha.clone();
let access_token = env
.v3
.complete_full_authorize_flow(
&client_id,
&client_secret,
Some("NOTIFICATION_READ"),
None,
None,
USER_USER_PAT,
)
.await;
env.v3.delete_oauth_client(&client_id, USER_USER_PAT).await;
env.assert_read_notifications_status(USER_USER_ID, &access_token, StatusCode::UNAUTHORIZED)
.await;
})
.await;
}
#[actix_rt::test]
async fn can_list_user_oauth_authorizations() {
with_test_environment(|env| async move {
let DummyOAuthClientAlpha {
client_id,
client_secret,
..
} = env.dummy.as_ref().unwrap().oauth_client_alpha.clone();
env.v3
.complete_full_authorize_flow(
&client_id,
&client_secret,
None,
None,
None,
USER_USER_PAT,
)
.await;
let authorizations = env.v3.get_user_oauth_authorizations(USER_USER_PAT).await;
assert_eq!(1, authorizations.len());
assert_eq!(USER_USER_ID_PARSED, authorizations[0].user_id.0 as i64);
})
.await;
}