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:
Jackson Kruger
2023-10-30 11:14:38 -05:00
committed by GitHub
parent 8803e11945
commit 6cfd4637db
54 changed files with 3658 additions and 135 deletions

View File

@@ -7,6 +7,8 @@ use url::Url;
use crate::common::{dummy_data, environment::TestEnvironment};
use super::dummy_data::DUMMY_DATA_UPDATE;
// The dummy test database adds a fair bit of 'dummy' data to test with.
// Some constants are used to refer to that data, and are described here.
// The rest can be accessed in the TestEnvironment 'dummy' field.
@@ -119,11 +121,7 @@ impl TemporaryDatabase {
.await
.unwrap();
if db_exists.is_none() {
let create_db_query = format!("CREATE DATABASE {TEMPLATE_DATABASE_NAME}");
sqlx::query(&create_db_query)
.execute(&main_pool)
.await
.expect("Database creation failed");
create_template_database(&main_pool).await;
}
// Switch to template
@@ -135,30 +133,52 @@ impl TemporaryDatabase {
.await
.expect("Connection to database failed");
// Run migrations on the template
let migrations = sqlx::migrate!("./migrations");
migrations.run(&pool).await.expect("Migrations failed");
// Check if dummy data exists- a fake 'dummy_data' table is created if it does
let dummy_data_exists: bool =
let mut dummy_data_exists: bool =
sqlx::query_scalar("SELECT to_regclass('dummy_data') IS NOT NULL")
.fetch_one(&pool)
.await
.unwrap();
if dummy_data_exists {
// Check if the dummy data needs to be updated
let dummy_data_update =
sqlx::query_scalar::<_, i64>("SELECT update_id FROM dummy_data")
.fetch_optional(&pool)
.await
.unwrap();
let needs_update = !dummy_data_update.is_some_and(|d| d == DUMMY_DATA_UPDATE);
if needs_update {
println!("Dummy data updated, so template DB tables will be dropped and re-created");
// Drop all tables in the database so they can be re-created and later filled with updated dummy data
sqlx::query("DROP SCHEMA public CASCADE;")
.execute(&pool)
.await
.unwrap();
sqlx::query("CREATE SCHEMA public;")
.execute(&pool)
.await
.unwrap();
dummy_data_exists = false;
}
}
// Run migrations on the template
let migrations = sqlx::migrate!("./migrations");
migrations.run(&pool).await.expect("Migrations failed");
if !dummy_data_exists {
// Add dummy data
let temporary_test_env = TestEnvironment::build_with_db(TemporaryDatabase {
pool: pool.clone(),
database_name: TEMPLATE_DATABASE_NAME.to_string(),
redis_pool: RedisPool::new(None),
redis_pool: RedisPool::new(Some(generate_random_name("test_template_"))),
})
.await;
dummy_data::add_dummy_data(&temporary_test_env).await;
temporary_test_env.db.pool.close().await;
}
pool.close().await;
// Switch back to main database (as we cant create from template while connected to it)
let pool = PgPool::connect(url.as_str()).await.unwrap();
drop(pool);
// Create the temporary database from the template
let create_db_query = format!(
@@ -167,7 +187,7 @@ impl TemporaryDatabase {
);
sqlx::query(&create_db_query)
.execute(&pool)
.execute(&main_pool)
.await
.expect("Database creation failed");
@@ -216,6 +236,14 @@ impl TemporaryDatabase {
}
}
async fn create_template_database(pool: &sqlx::Pool<sqlx::Postgres>) {
let create_db_query = format!("CREATE DATABASE {TEMPLATE_DATABASE_NAME}");
sqlx::query(&create_db_query)
.execute(pool)
.await
.expect("Database creation failed");
}
// Appends a random 8-digit number to the end of the str
pub fn generate_random_name(str: &str) -> String {
let mut str = String::from(str);