You've already forked AstralRinth
* 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
96 lines
2.9 KiB
Rust
96 lines
2.9 KiB
Rust
use super::{DatabaseError, OAuthAccessTokenId, OAuthClientAuthorizationId, OAuthClientId, UserId};
|
|
use crate::models::pats::Scopes;
|
|
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use sha2::Digest;
|
|
|
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
|
pub struct OAuthAccessToken {
|
|
pub id: OAuthAccessTokenId,
|
|
pub authorization_id: OAuthClientAuthorizationId,
|
|
pub token_hash: String,
|
|
pub scopes: Scopes,
|
|
pub created: DateTime<Utc>,
|
|
pub expires: DateTime<Utc>,
|
|
pub last_used: Option<DateTime<Utc>>,
|
|
|
|
// Stored separately inside oauth_client_authorizations table
|
|
pub client_id: OAuthClientId,
|
|
pub user_id: UserId,
|
|
}
|
|
|
|
impl OAuthAccessToken {
|
|
pub async fn get(
|
|
token_hash: String,
|
|
exec: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
|
|
) -> Result<Option<OAuthAccessToken>, DatabaseError> {
|
|
let value = sqlx::query!(
|
|
"
|
|
SELECT
|
|
tokens.id,
|
|
tokens.authorization_id,
|
|
tokens.token_hash,
|
|
tokens.scopes,
|
|
tokens.created,
|
|
tokens.expires,
|
|
tokens.last_used,
|
|
auths.client_id,
|
|
auths.user_id
|
|
FROM oauth_access_tokens tokens
|
|
JOIN oauth_client_authorizations auths
|
|
ON tokens.authorization_id = auths.id
|
|
WHERE tokens.token_hash = $1
|
|
",
|
|
token_hash
|
|
)
|
|
.fetch_optional(exec)
|
|
.await?;
|
|
|
|
Ok(value.map(|r| OAuthAccessToken {
|
|
id: OAuthAccessTokenId(r.id),
|
|
authorization_id: OAuthClientAuthorizationId(r.authorization_id),
|
|
token_hash: r.token_hash,
|
|
scopes: Scopes::from_postgres(r.scopes),
|
|
created: r.created,
|
|
expires: r.expires,
|
|
last_used: r.last_used,
|
|
client_id: OAuthClientId(r.client_id),
|
|
user_id: UserId(r.user_id),
|
|
}))
|
|
}
|
|
|
|
/// Inserts and returns the time until the token expires
|
|
pub async fn insert(
|
|
&self,
|
|
exec: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
|
|
) -> Result<chrono::Duration, DatabaseError> {
|
|
let r = sqlx::query!(
|
|
"
|
|
INSERT INTO oauth_access_tokens (
|
|
id, authorization_id, token_hash, scopes, last_used
|
|
)
|
|
VALUES (
|
|
$1, $2, $3, $4, $5
|
|
)
|
|
RETURNING created, expires
|
|
",
|
|
self.id.0,
|
|
self.authorization_id.0,
|
|
self.token_hash,
|
|
self.scopes.to_postgres(),
|
|
Option::<DateTime<Utc>>::None
|
|
)
|
|
.fetch_one(exec)
|
|
.await?;
|
|
|
|
let (created, expires) = (r.created, r.expires);
|
|
let time_until_expiration = expires - created;
|
|
|
|
Ok(time_until_expiration)
|
|
}
|
|
|
|
pub fn hash_token(token: &str) -> String {
|
|
format!("{:x}", sha2::Sha512::digest(token.as_bytes()))
|
|
}
|
|
}
|