You've already forked AstralRinth
forked from didirus/AstralRinth
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
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
#![allow(dead_code)]
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::test::{self, TestRequest};
|
||||
use labrinth::{
|
||||
models::projects::Project,
|
||||
models::{organizations::Organization, pats::Scopes, projects::Version},
|
||||
models::{
|
||||
oauth_clients::OAuthClient, organizations::Organization, pats::Scopes, projects::Version,
|
||||
},
|
||||
};
|
||||
use serde_json::json;
|
||||
use sqlx::Executor;
|
||||
@@ -11,11 +14,14 @@ use crate::common::{actix::AppendsMultipart, database::USER_USER_PAT};
|
||||
|
||||
use super::{
|
||||
actix::{MultipartSegment, MultipartSegmentData},
|
||||
asserts::assert_status,
|
||||
database::USER_USER_ID,
|
||||
environment::TestEnvironment,
|
||||
get_json_val_str,
|
||||
request_data::get_public_project_creation_data,
|
||||
};
|
||||
|
||||
pub const DUMMY_DATA_UPDATE: i64 = 1;
|
||||
pub const DUMMY_DATA_UPDATE: i64 = 3;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub const DUMMY_CATEGORIES: &[&str] = &[
|
||||
@@ -28,6 +34,8 @@ pub const DUMMY_CATEGORIES: &[&str] = &[
|
||||
"optimization",
|
||||
];
|
||||
|
||||
pub const DUMMY_OAUTH_CLIENT_ALPHA_SECRET: &str = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum DummyJarFile {
|
||||
DummyProjectAlpha,
|
||||
@@ -43,16 +51,80 @@ pub enum DummyImage {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DummyData {
|
||||
/// Alpha project:
|
||||
/// This is a dummy project created by USER user.
|
||||
/// It's approved, listed, and visible to the public.
|
||||
pub project_alpha: DummyProjectAlpha,
|
||||
|
||||
/// Beta project:
|
||||
/// This is a dummy project created by USER user.
|
||||
/// It's not approved, unlisted, and not visible to the public.
|
||||
pub project_beta: DummyProjectBeta,
|
||||
|
||||
/// Zeta organization:
|
||||
/// This is a dummy organization created by USER user.
|
||||
/// There are no projects in it.
|
||||
pub organization_zeta: DummyOrganizationZeta,
|
||||
|
||||
/// Alpha OAuth Client:
|
||||
/// This is a dummy OAuth client created by USER user.
|
||||
///
|
||||
/// All scopes are included in its max scopes
|
||||
///
|
||||
/// It has one valid redirect URI
|
||||
pub oauth_client_alpha: DummyOAuthClientAlpha,
|
||||
}
|
||||
|
||||
impl DummyData {
|
||||
pub fn new(
|
||||
project_alpha: Project,
|
||||
project_alpha_version: Version,
|
||||
project_beta: Project,
|
||||
project_beta_version: Version,
|
||||
organization_zeta: Organization,
|
||||
oauth_client_alpha: OAuthClient,
|
||||
) -> Self {
|
||||
DummyData {
|
||||
project_alpha: DummyProjectAlpha {
|
||||
team_id: project_alpha.team.to_string(),
|
||||
project_id: project_alpha.id.to_string(),
|
||||
project_slug: project_alpha.slug.unwrap(),
|
||||
version_id: project_alpha_version.id.to_string(),
|
||||
thread_id: project_alpha.thread_id.to_string(),
|
||||
file_hash: project_alpha_version.files[0].hashes["sha1"].clone(),
|
||||
},
|
||||
|
||||
project_beta: DummyProjectBeta {
|
||||
team_id: project_beta.team.to_string(),
|
||||
project_id: project_beta.id.to_string(),
|
||||
project_slug: project_beta.slug.unwrap(),
|
||||
version_id: project_beta_version.id.to_string(),
|
||||
thread_id: project_beta.thread_id.to_string(),
|
||||
file_hash: project_beta_version.files[0].hashes["sha1"].clone(),
|
||||
},
|
||||
|
||||
organization_zeta: DummyOrganizationZeta {
|
||||
organization_id: organization_zeta.id.to_string(),
|
||||
team_id: organization_zeta.team_id.to_string(),
|
||||
organization_title: organization_zeta.title,
|
||||
},
|
||||
|
||||
oauth_client_alpha: DummyOAuthClientAlpha {
|
||||
client_id: get_json_val_str(oauth_client_alpha.id),
|
||||
client_secret: DUMMY_OAUTH_CLIENT_ALPHA_SECRET.to_string(),
|
||||
valid_redirect_uri: oauth_client_alpha
|
||||
.redirect_uris
|
||||
.first()
|
||||
.unwrap()
|
||||
.uri
|
||||
.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DummyProjectAlpha {
|
||||
// Alpha project:
|
||||
// This is a dummy project created by USER user.
|
||||
// It's approved, listed, and visible to the public.
|
||||
pub project_id: String,
|
||||
pub project_slug: String,
|
||||
pub version_id: String,
|
||||
@@ -63,9 +135,6 @@ pub struct DummyProjectAlpha {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DummyProjectBeta {
|
||||
// Beta project:
|
||||
// This is a dummy project created by USER user.
|
||||
// It's not approved, unlisted, and not visible to the public.
|
||||
pub project_id: String,
|
||||
pub project_slug: String,
|
||||
pub version_id: String,
|
||||
@@ -76,14 +145,18 @@ pub struct DummyProjectBeta {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DummyOrganizationZeta {
|
||||
// Zeta organization:
|
||||
// This is a dummy organization created by USER user.
|
||||
// There are no projects in it.
|
||||
pub organization_id: String,
|
||||
pub organization_title: String,
|
||||
pub team_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DummyOAuthClientAlpha {
|
||||
pub client_id: String,
|
||||
pub client_secret: String,
|
||||
pub valid_redirect_uri: String,
|
||||
}
|
||||
|
||||
pub async fn add_dummy_data(test_env: &TestEnvironment) -> DummyData {
|
||||
// Adds basic dummy data to the database directly with sql (user, pats)
|
||||
let pool = &test_env.db.pool.clone();
|
||||
@@ -101,37 +174,22 @@ pub async fn add_dummy_data(test_env: &TestEnvironment) -> DummyData {
|
||||
|
||||
let zeta_organization = add_organization_zeta(test_env).await;
|
||||
|
||||
let oauth_client_alpha = get_oauth_client_alpha(test_env).await;
|
||||
|
||||
sqlx::query("INSERT INTO dummy_data (update_id) VALUES ($1)")
|
||||
.bind(DUMMY_DATA_UPDATE)
|
||||
.execute(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
DummyData {
|
||||
project_alpha: DummyProjectAlpha {
|
||||
team_id: alpha_project.team.to_string(),
|
||||
project_id: alpha_project.id.to_string(),
|
||||
project_slug: alpha_project.slug.unwrap(),
|
||||
version_id: alpha_version.id.to_string(),
|
||||
thread_id: alpha_project.thread_id.to_string(),
|
||||
file_hash: alpha_version.files[0].hashes["sha1"].clone(),
|
||||
},
|
||||
|
||||
project_beta: DummyProjectBeta {
|
||||
team_id: beta_project.team.to_string(),
|
||||
project_id: beta_project.id.to_string(),
|
||||
project_slug: beta_project.slug.unwrap(),
|
||||
version_id: beta_version.id.to_string(),
|
||||
thread_id: beta_project.thread_id.to_string(),
|
||||
file_hash: beta_version.files[0].hashes["sha1"].clone(),
|
||||
},
|
||||
|
||||
organization_zeta: DummyOrganizationZeta {
|
||||
organization_id: zeta_organization.id.to_string(),
|
||||
team_id: zeta_organization.team_id.to_string(),
|
||||
organization_title: zeta_organization.title,
|
||||
},
|
||||
}
|
||||
DummyData::new(
|
||||
alpha_project,
|
||||
alpha_version,
|
||||
beta_project,
|
||||
beta_version,
|
||||
zeta_organization,
|
||||
oauth_client_alpha,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_dummy_data(test_env: &TestEnvironment) -> DummyData {
|
||||
@@ -139,31 +197,17 @@ pub async fn get_dummy_data(test_env: &TestEnvironment) -> DummyData {
|
||||
let (beta_project, beta_version) = get_project_beta(test_env).await;
|
||||
|
||||
let zeta_organization = get_organization_zeta(test_env).await;
|
||||
DummyData {
|
||||
project_alpha: DummyProjectAlpha {
|
||||
team_id: alpha_project.team.to_string(),
|
||||
project_id: alpha_project.id.to_string(),
|
||||
project_slug: alpha_project.slug.unwrap(),
|
||||
version_id: alpha_version.id.to_string(),
|
||||
thread_id: alpha_project.thread_id.to_string(),
|
||||
file_hash: alpha_version.files[0].hashes["sha1"].clone(),
|
||||
},
|
||||
|
||||
project_beta: DummyProjectBeta {
|
||||
team_id: beta_project.team.to_string(),
|
||||
project_id: beta_project.id.to_string(),
|
||||
project_slug: beta_project.slug.unwrap(),
|
||||
version_id: beta_version.id.to_string(),
|
||||
thread_id: beta_project.thread_id.to_string(),
|
||||
file_hash: beta_version.files[0].hashes["sha1"].clone(),
|
||||
},
|
||||
let oauth_client_alpha = get_oauth_client_alpha(test_env).await;
|
||||
|
||||
organization_zeta: DummyOrganizationZeta {
|
||||
organization_id: zeta_organization.id.to_string(),
|
||||
team_id: zeta_organization.team_id.to_string(),
|
||||
organization_title: zeta_organization.title,
|
||||
},
|
||||
}
|
||||
DummyData::new(
|
||||
alpha_project,
|
||||
alpha_version,
|
||||
beta_project,
|
||||
beta_version,
|
||||
zeta_organization,
|
||||
oauth_client_alpha,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn add_project_alpha(test_env: &TestEnvironment) -> (Project, Version) {
|
||||
@@ -282,6 +326,7 @@ pub async fn get_project_beta(test_env: &TestEnvironment) -> (Project, Version)
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
let project: Project = test::read_body_json(resp).await;
|
||||
|
||||
// Get project's versions
|
||||
@@ -290,6 +335,7 @@ pub async fn get_project_beta(test_env: &TestEnvironment) -> (Project, Version)
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
let versions: Vec<Version> = test::read_body_json(resp).await;
|
||||
let version = versions.into_iter().next().unwrap();
|
||||
|
||||
@@ -308,6 +354,14 @@ pub async fn get_organization_zeta(test_env: &TestEnvironment) -> Organization {
|
||||
organization
|
||||
}
|
||||
|
||||
pub async fn get_oauth_client_alpha(test_env: &TestEnvironment) -> OAuthClient {
|
||||
let oauth_clients = test_env
|
||||
.v3
|
||||
.get_user_oauth_clients(USER_USER_ID, USER_USER_PAT)
|
||||
.await;
|
||||
oauth_clients.into_iter().next().unwrap()
|
||||
}
|
||||
|
||||
impl DummyJarFile {
|
||||
pub fn filename(&self) -> String {
|
||||
match self {
|
||||
|
||||
Reference in New Issue
Block a user