You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
GraphQL API: query oauth2 sessions and clients
This commit is contained in:
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::string::ToString;
|
||||
use std::{collections::HashMap, string::ToString};
|
||||
|
||||
use mas_data_model::{Client, JwksOrJwksUri};
|
||||
use mas_iana::{
|
||||
@ -250,6 +250,54 @@ impl TryInto<Client<PostgresqlBackend>> for OAuth2ClientLookup {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
pub async fn lookup_clients(
|
||||
executor: impl PgExecutor<'_>,
|
||||
ids: impl IntoIterator<Item = Ulid> + Send,
|
||||
) -> Result<HashMap<Ulid, Client<PostgresqlBackend>>, ClientFetchError> {
|
||||
let ids: Vec<Uuid> = ids.into_iter().map(Uuid::from).collect();
|
||||
let res = sqlx::query_as!(
|
||||
OAuth2ClientLookup,
|
||||
r#"
|
||||
SELECT
|
||||
c.oauth2_client_id,
|
||||
c.encrypted_client_secret,
|
||||
ARRAY(
|
||||
SELECT redirect_uri
|
||||
FROM oauth2_client_redirect_uris r
|
||||
WHERE r.oauth2_client_id = c.oauth2_client_id
|
||||
) AS "redirect_uris!",
|
||||
c.grant_type_authorization_code,
|
||||
c.grant_type_refresh_token,
|
||||
c.client_name,
|
||||
c.logo_uri,
|
||||
c.client_uri,
|
||||
c.policy_uri,
|
||||
c.tos_uri,
|
||||
c.jwks_uri,
|
||||
c.jwks,
|
||||
c.id_token_signed_response_alg,
|
||||
c.userinfo_signed_response_alg,
|
||||
c.token_endpoint_auth_method,
|
||||
c.token_endpoint_auth_signing_alg,
|
||||
c.initiate_login_uri
|
||||
FROM oauth2_clients c
|
||||
|
||||
WHERE c.oauth2_client_id = ANY($1::uuid[])
|
||||
"#,
|
||||
&ids,
|
||||
)
|
||||
.fetch_all(executor)
|
||||
.await?;
|
||||
|
||||
let clients: Result<HashMap<Ulid, Client<PostgresqlBackend>>, _> = res
|
||||
.into_iter()
|
||||
.map(|r| r.try_into().map(|c: Client<PostgresqlBackend>| (c.data, c)))
|
||||
.collect();
|
||||
|
||||
clients
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
skip_all,
|
||||
fields(client.id = %id),
|
||||
|
@ -12,11 +12,21 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use mas_data_model::Session;
|
||||
use sqlx::PgExecutor;
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
|
||||
use anyhow::Context;
|
||||
use mas_data_model::{BrowserSession, Session, User};
|
||||
use sqlx::{postgres::PgArguments, Arguments, PgConnection, PgExecutor};
|
||||
use tracing::{info_span, Instrument};
|
||||
use ulid::Ulid;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{Clock, PostgresqlBackend};
|
||||
use self::client::lookup_clients;
|
||||
use crate::{
|
||||
pagination::{generate_pagination, process_page},
|
||||
user::lookup_active_session,
|
||||
Clock, PostgresqlBackend,
|
||||
};
|
||||
|
||||
pub mod access_token;
|
||||
pub mod authorization_grant;
|
||||
@ -56,3 +66,113 @@ pub async fn end_oauth_session(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct OAuthSessionLookup {
|
||||
oauth2_session_id: Uuid,
|
||||
user_session_id: Uuid,
|
||||
oauth2_client_id: Uuid,
|
||||
scope: String,
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
skip_all,
|
||||
fields(
|
||||
user.id = %user.data,
|
||||
user.username = user.username,
|
||||
),
|
||||
err(Display),
|
||||
)]
|
||||
pub async fn get_paginated_user_oauth_sessions(
|
||||
conn: &mut PgConnection,
|
||||
user: &User<PostgresqlBackend>,
|
||||
before: Option<Ulid>,
|
||||
after: Option<Ulid>,
|
||||
first: Option<usize>,
|
||||
last: Option<usize>,
|
||||
) -> Result<(bool, bool, Vec<Session<PostgresqlBackend>>), anyhow::Error> {
|
||||
let mut query = String::from(
|
||||
r#"
|
||||
SELECT
|
||||
os.oauth2_session_id,
|
||||
os.user_session_id,
|
||||
os.oauth2_client_id,
|
||||
os.scope,
|
||||
os.created_at,
|
||||
os.finished_at
|
||||
FROM oauth2_sessions os
|
||||
LEFT JOIN user_sessions us
|
||||
USING (user_session_id)
|
||||
"#,
|
||||
);
|
||||
|
||||
let mut arguments = PgArguments::default();
|
||||
|
||||
query += " WHERE us.user_id = ";
|
||||
arguments.add(Uuid::from(user.data));
|
||||
arguments.format_placeholder(&mut query)?;
|
||||
|
||||
generate_pagination(
|
||||
&mut query,
|
||||
"oauth2_session_id",
|
||||
&mut arguments,
|
||||
before,
|
||||
after,
|
||||
first,
|
||||
last,
|
||||
)?;
|
||||
|
||||
let page: Vec<OAuthSessionLookup> = sqlx::query_as_with(&query, arguments)
|
||||
.fetch_all(&mut *conn)
|
||||
.instrument(info_span!(
|
||||
"Fetch paginated user oauth sessions",
|
||||
query = query
|
||||
))
|
||||
.await?;
|
||||
|
||||
let (has_previous_page, has_next_page, page) = process_page(page, first, last)?;
|
||||
|
||||
let client_ids: BTreeSet<Ulid> = page
|
||||
.iter()
|
||||
.map(|i| Ulid::from(i.oauth2_client_id))
|
||||
.collect();
|
||||
|
||||
let browser_session_ids: BTreeSet<Ulid> =
|
||||
page.iter().map(|i| Ulid::from(i.user_session_id)).collect();
|
||||
|
||||
let clients = lookup_clients(&mut *conn, client_ids).await?;
|
||||
|
||||
// TODO: this can generate N queries instead of batching. This is less than
|
||||
// ideal
|
||||
let mut browser_sessions: HashMap<Ulid, BrowserSession<PostgresqlBackend>> = HashMap::new();
|
||||
for id in browser_session_ids {
|
||||
let v = lookup_active_session(&mut *conn, id).await?;
|
||||
browser_sessions.insert(id, v);
|
||||
}
|
||||
|
||||
let page: Result<Vec<_>, _> = page
|
||||
.into_iter()
|
||||
.map(|item| {
|
||||
let client = clients
|
||||
.get(&Ulid::from(item.oauth2_client_id))
|
||||
.context("client was not fetched")?
|
||||
.clone();
|
||||
|
||||
let browser_session = browser_sessions
|
||||
.get(&Ulid::from(item.user_session_id))
|
||||
.context("browser session was not fetched")?
|
||||
.clone();
|
||||
|
||||
let scope = item.scope.parse()?;
|
||||
|
||||
anyhow::Ok(Session {
|
||||
data: Ulid::from(item.oauth2_session_id),
|
||||
client,
|
||||
browser_session,
|
||||
scope,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok((has_previous_page, has_next_page, page?))
|
||||
}
|
||||
|
Reference in New Issue
Block a user