Files
Rocketmc/src/database/models/ids.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

431 lines
11 KiB
Rust

use super::DatabaseError;
use crate::models::ids::base62_impl::to_base62;
use crate::models::ids::random_base62_rng;
use censor::Censor;
use serde::{Deserialize, Serialize};
use sqlx::sqlx_macros::Type;
const ID_RETRY_COUNT: usize = 20;
macro_rules! generate_ids {
($vis:vis $function_name:ident, $return_type:ty, $id_length:expr, $select_stmnt:literal, $id_function:expr) => {
$vis async fn $function_name(
con: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<$return_type, DatabaseError> {
let mut rng = rand::thread_rng();
let length = $id_length;
let mut id = random_base62_rng(&mut rng, length);
let mut retry_count = 0;
let censor = Censor::Standard + Censor::Sex;
// Check if ID is unique
loop {
let results = sqlx::query!($select_stmnt, id as i64)
.fetch_one(&mut **con)
.await?;
if results.exists.unwrap_or(true) || censor.check(&*to_base62(id)) {
id = random_base62_rng(&mut rng, length);
} else {
break;
}
retry_count += 1;
if retry_count > ID_RETRY_COUNT {
return Err(DatabaseError::RandomId);
}
}
Ok($id_function(id as i64))
}
};
}
generate_ids!(
pub generate_project_id,
ProjectId,
8,
"SELECT EXISTS(SELECT 1 FROM mods WHERE id=$1)",
ProjectId
);
generate_ids!(
pub generate_version_id,
VersionId,
8,
"SELECT EXISTS(SELECT 1 FROM versions WHERE id=$1)",
VersionId
);
generate_ids!(
pub generate_team_id,
TeamId,
8,
"SELECT EXISTS(SELECT 1 FROM teams WHERE id=$1)",
TeamId
);
generate_ids!(
pub generate_organization_id,
OrganizationId,
8,
"SELECT EXISTS(SELECT 1 FROM organizations WHERE id=$1)",
OrganizationId
);
generate_ids!(
pub generate_collection_id,
CollectionId,
8,
"SELECT EXISTS(SELECT 1 FROM collections WHERE id=$1)",
CollectionId
);
generate_ids!(
pub generate_file_id,
FileId,
8,
"SELECT EXISTS(SELECT 1 FROM files WHERE id=$1)",
FileId
);
generate_ids!(
pub generate_team_member_id,
TeamMemberId,
8,
"SELECT EXISTS(SELECT 1 FROM team_members WHERE id=$1)",
TeamMemberId
);
generate_ids!(
pub generate_pat_id,
PatId,
8,
"SELECT EXISTS(SELECT 1 FROM pats WHERE id=$1)",
PatId
);
generate_ids!(
pub generate_user_id,
UserId,
8,
"SELECT EXISTS(SELECT 1 FROM users WHERE id=$1)",
UserId
);
generate_ids!(
pub generate_report_id,
ReportId,
8,
"SELECT EXISTS(SELECT 1 FROM reports WHERE id=$1)",
ReportId
);
generate_ids!(
pub generate_notification_id,
NotificationId,
8,
"SELECT EXISTS(SELECT 1 FROM notifications WHERE id=$1)",
NotificationId
);
generate_ids!(
pub generate_thread_id,
ThreadId,
8,
"SELECT EXISTS(SELECT 1 FROM threads WHERE id=$1)",
ThreadId
);
generate_ids!(
pub generate_thread_message_id,
ThreadMessageId,
8,
"SELECT EXISTS(SELECT 1 FROM threads_messages WHERE id=$1)",
ThreadMessageId
);
generate_ids!(
pub generate_session_id,
SessionId,
8,
"SELECT EXISTS(SELECT 1 FROM sessions WHERE id=$1)",
SessionId
);
generate_ids!(
pub generate_image_id,
ImageId,
8,
"SELECT EXISTS(SELECT 1 FROM uploaded_images WHERE id=$1)",
ImageId
);
generate_ids!(
pub generate_oauth_client_authorization_id,
OAuthClientAuthorizationId,
8,
"SELECT EXISTS(SELECT 1 FROM oauth_client_authorizations WHERE id=$1)",
OAuthClientAuthorizationId
);
generate_ids!(
pub generate_oauth_client_id,
OAuthClientId,
8,
"SELECT EXISTS(SELECT 1 FROM oauth_clients WHERE id=$1)",
OAuthClientId
);
generate_ids!(
pub generate_oauth_redirect_id,
OAuthRedirectUriId,
8,
"SELECT EXISTS(SELECT 1 FROM oauth_client_redirect_uris WHERE id=$1)",
OAuthRedirectUriId
);
generate_ids!(
pub generate_oauth_access_token_id,
OAuthAccessTokenId,
8,
"SELECT EXISTS(SELECT 1 FROM oauth_access_tokens WHERE id=$1)",
OAuthAccessTokenId
);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Type, Hash, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct UserId(pub i64);
#[derive(Copy, Clone, Debug, Type, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct TeamId(pub i64);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct TeamMemberId(pub i64);
#[derive(Copy, Clone, Debug, Type, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct OrganizationId(pub i64);
#[derive(Copy, Clone, Debug, Type, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct ProjectId(pub i64);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct ProjectTypeId(pub i32);
#[derive(Copy, Clone, Debug, Type)]
#[sqlx(transparent)]
pub struct StatusId(pub i32);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct SideTypeId(pub i32);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct DonationPlatformId(pub i32);
#[derive(Copy, Clone, Debug, Type, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct VersionId(pub i64);
#[derive(Copy, Clone, Debug, Type, Deserialize, Serialize)]
#[sqlx(transparent)]
pub struct GameVersionId(pub i32);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct LoaderId(pub i32);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct CategoryId(pub i32);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct CollectionId(pub i64);
#[derive(Copy, Clone, Debug, Type, Deserialize, Serialize)]
#[sqlx(transparent)]
pub struct ReportId(pub i64);
#[derive(Copy, Clone, Debug, Type)]
#[sqlx(transparent)]
pub struct ReportTypeId(pub i32);
#[derive(Copy, Clone, Debug, Type, Hash, Eq, PartialEq, Deserialize, Serialize)]
#[sqlx(transparent)]
pub struct FileId(pub i64);
#[derive(Copy, Clone, Debug, Type, Deserialize, Serialize, Eq, PartialEq, Hash)]
#[sqlx(transparent)]
pub struct PatId(pub i64);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct NotificationId(pub i64);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct NotificationActionId(pub i32);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize, Eq, PartialEq)]
#[sqlx(transparent)]
pub struct ThreadId(pub i64);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize, Eq, PartialEq, Hash)]
#[sqlx(transparent)]
pub struct ThreadMessageId(pub i64);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize, Eq, PartialEq, Hash)]
#[sqlx(transparent)]
pub struct SessionId(pub i64);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize, Eq, PartialEq, Hash)]
#[sqlx(transparent)]
pub struct ImageId(pub i64);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize, Eq, PartialEq, Hash)]
#[sqlx(transparent)]
pub struct OAuthClientId(pub i64);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize, Eq, PartialEq, Hash)]
#[sqlx(transparent)]
pub struct OAuthClientAuthorizationId(pub i64);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize, Eq, PartialEq, Hash)]
#[sqlx(transparent)]
pub struct OAuthRedirectUriId(pub i64);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize, Eq, PartialEq, Hash)]
#[sqlx(transparent)]
pub struct OAuthAccessTokenId(pub i64);
use crate::models::ids;
impl From<ids::ProjectId> for ProjectId {
fn from(id: ids::ProjectId) -> Self {
ProjectId(id.0 as i64)
}
}
impl From<ProjectId> for ids::ProjectId {
fn from(id: ProjectId) -> Self {
ids::ProjectId(id.0 as u64)
}
}
impl From<ids::UserId> for UserId {
fn from(id: ids::UserId) -> Self {
UserId(id.0 as i64)
}
}
impl From<UserId> for ids::UserId {
fn from(id: UserId) -> Self {
ids::UserId(id.0 as u64)
}
}
impl From<ids::TeamId> for TeamId {
fn from(id: ids::TeamId) -> Self {
TeamId(id.0 as i64)
}
}
impl From<TeamId> for ids::TeamId {
fn from(id: TeamId) -> Self {
ids::TeamId(id.0 as u64)
}
}
impl From<ids::OrganizationId> for OrganizationId {
fn from(id: ids::OrganizationId) -> Self {
OrganizationId(id.0 as i64)
}
}
impl From<OrganizationId> for ids::OrganizationId {
fn from(id: OrganizationId) -> Self {
ids::OrganizationId(id.0 as u64)
}
}
impl From<ids::VersionId> for VersionId {
fn from(id: ids::VersionId) -> Self {
VersionId(id.0 as i64)
}
}
impl From<VersionId> for ids::VersionId {
fn from(id: VersionId) -> Self {
ids::VersionId(id.0 as u64)
}
}
impl From<ids::CollectionId> for CollectionId {
fn from(id: ids::CollectionId) -> Self {
CollectionId(id.0 as i64)
}
}
impl From<CollectionId> for ids::CollectionId {
fn from(id: CollectionId) -> Self {
ids::CollectionId(id.0 as u64)
}
}
impl From<ids::ReportId> for ReportId {
fn from(id: ids::ReportId) -> Self {
ReportId(id.0 as i64)
}
}
impl From<ReportId> for ids::ReportId {
fn from(id: ReportId) -> Self {
ids::ReportId(id.0 as u64)
}
}
impl From<ImageId> for ids::ImageId {
fn from(id: ImageId) -> Self {
ids::ImageId(id.0 as u64)
}
}
impl From<ids::ImageId> for ImageId {
fn from(id: ids::ImageId) -> Self {
ImageId(id.0 as i64)
}
}
impl From<ids::NotificationId> for NotificationId {
fn from(id: ids::NotificationId) -> Self {
NotificationId(id.0 as i64)
}
}
impl From<NotificationId> for ids::NotificationId {
fn from(id: NotificationId) -> Self {
ids::NotificationId(id.0 as u64)
}
}
impl From<ids::ThreadId> for ThreadId {
fn from(id: ids::ThreadId) -> Self {
ThreadId(id.0 as i64)
}
}
impl From<ThreadId> for ids::ThreadId {
fn from(id: ThreadId) -> Self {
ids::ThreadId(id.0 as u64)
}
}
impl From<ids::ThreadMessageId> for ThreadMessageId {
fn from(id: ids::ThreadMessageId) -> Self {
ThreadMessageId(id.0 as i64)
}
}
impl From<ThreadMessageId> for ids::ThreadMessageId {
fn from(id: ThreadMessageId) -> Self {
ids::ThreadMessageId(id.0 as u64)
}
}
impl From<SessionId> for ids::SessionId {
fn from(id: SessionId) -> Self {
ids::SessionId(id.0 as u64)
}
}
impl From<PatId> for ids::PatId {
fn from(id: PatId) -> Self {
ids::PatId(id.0 as u64)
}
}
impl From<OAuthClientId> for ids::OAuthClientId {
fn from(id: OAuthClientId) -> Self {
ids::OAuthClientId(id.0 as u64)
}
}
impl From<ids::OAuthClientId> for OAuthClientId {
fn from(id: ids::OAuthClientId) -> Self {
Self(id.0 as i64)
}
}
impl From<OAuthRedirectUriId> for ids::OAuthRedirectUriId {
fn from(id: OAuthRedirectUriId) -> Self {
ids::OAuthRedirectUriId(id.0 as u64)
}
}
impl From<OAuthClientAuthorizationId> for ids::OAuthClientAuthorizationId {
fn from(id: OAuthClientAuthorizationId) -> Self {
ids::OAuthClientAuthorizationId(id.0 as u64)
}
}