Skip to content
This repository was archived by the owner on Sep 10, 2024. It is now read-only.

Commit 1bdad26

Browse files
committed
Disallow OAuth 2.0 use of the GraphQL API by default
1 parent eb4072f commit 1bdad26

File tree

6 files changed

+70
-14
lines changed

6 files changed

+70
-14
lines changed

crates/cli/src/server.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,13 @@ pub fn build_router(
201201
mas_config::HttpResource::Human => {
202202
router.merge(mas_handlers::human_router::<AppState>(templates.clone()))
203203
}
204-
mas_config::HttpResource::GraphQL { playground } => {
205-
router.merge(mas_handlers::graphql_router::<AppState>(*playground))
206-
}
204+
mas_config::HttpResource::GraphQL {
205+
playground,
206+
undocumented_oauth2_access,
207+
} => router.merge(mas_handlers::graphql_router::<AppState>(
208+
*playground,
209+
*undocumented_oauth2_access,
210+
)),
207211
mas_config::HttpResource::Assets { path } => {
208212
let static_service = ServeDir::new(path)
209213
.append_index_html_on_directories(false)

crates/config/src/sections/http.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,12 @@ pub enum Resource {
291291
/// GraphQL endpoint
292292
GraphQL {
293293
/// Enabled the GraphQL playground
294-
#[serde(default)]
294+
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
295295
playground: bool,
296+
297+
/// Allow access for OAuth 2.0 clients (undocumented)
298+
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
299+
undocumented_oauth2_access: bool,
296300
},
297301

298302
/// OAuth-related APIs
@@ -379,7 +383,10 @@ impl Default for HttpConfig {
379383
Resource::Human,
380384
Resource::OAuth,
381385
Resource::Compat,
382-
Resource::GraphQL { playground: true },
386+
Resource::GraphQL {
387+
playground: false,
388+
undocumented_oauth2_access: false,
389+
},
383390
Resource::Assets {
384391
path: http_listener_assets_path_default(),
385392
},

crates/handlers/src/graphql/mod.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use axum::{
2727
extract::{RawQuery, State as AxumState},
2828
http::StatusCode,
2929
response::{Html, IntoResponse, Response},
30-
Json,
30+
Extension, Json,
3131
};
3232
use axum_extra::typed_header::TypedHeader;
3333
use chrono::{DateTime, Utc};
@@ -65,6 +65,13 @@ use crate::{impl_from_error_for_route, passwords::PasswordManager, BoundActivity
6565
#[cfg(test)]
6666
mod tests;
6767

68+
/// Extra parameters we get from the listener configuration, because they are
69+
/// per-listener options. We pass them through request extensions.
70+
#[derive(Debug, Clone)]
71+
pub struct ExtraRouterParameters {
72+
pub undocumented_oauth2_access: bool,
73+
}
74+
6875
struct GraphQLState {
6976
pool: PgPool,
7077
homeserver_connection: Arc<dyn HomeserverConnection<Error = anyhow::Error>>,
@@ -217,13 +224,19 @@ impl IntoResponse for RouteError {
217224
}
218225

219226
async fn get_requester(
227+
undocumented_oauth2_access: bool,
220228
clock: &impl Clock,
221229
activity_tracker: &BoundActivityTracker,
222230
mut repo: BoxRepository,
223231
session_info: SessionInfo,
224232
token: Option<&str>,
225233
) -> Result<Requester, RouteError> {
226234
let requester = if let Some(token) = token {
235+
// If we haven't enabled undocumented_oauth2_access on the listener, we bail out
236+
if !undocumented_oauth2_access {
237+
return Err(RouteError::InvalidToken);
238+
}
239+
227240
let token = repo
228241
.oauth2_access_token()
229242
.find_by_token(token)
@@ -281,6 +294,9 @@ async fn get_requester(
281294

282295
pub async fn post(
283296
AxumState(schema): AxumState<Schema>,
297+
Extension(ExtraRouterParameters {
298+
undocumented_oauth2_access,
299+
}): Extension<ExtraRouterParameters>,
284300
clock: BoxClock,
285301
repo: BoxRepository,
286302
activity_tracker: BoundActivityTracker,
@@ -294,7 +310,15 @@ pub async fn post(
294310
.as_ref()
295311
.map(|TypedHeader(Authorization(bearer))| bearer.token());
296312
let (session_info, _cookie_jar) = cookie_jar.session_info();
297-
let requester = get_requester(&clock, &activity_tracker, repo, session_info, token).await?;
313+
let requester = get_requester(
314+
undocumented_oauth2_access,
315+
&clock,
316+
&activity_tracker,
317+
repo,
318+
session_info,
319+
token,
320+
)
321+
.await?;
298322

299323
let content_type = content_type.map(|TypedHeader(h)| h.to_string());
300324

@@ -323,6 +347,9 @@ pub async fn post(
323347

324348
pub async fn get(
325349
AxumState(schema): AxumState<Schema>,
350+
Extension(ExtraRouterParameters {
351+
undocumented_oauth2_access,
352+
}): Extension<ExtraRouterParameters>,
326353
clock: BoxClock,
327354
repo: BoxRepository,
328355
activity_tracker: BoundActivityTracker,
@@ -334,7 +361,15 @@ pub async fn get(
334361
.as_ref()
335362
.map(|TypedHeader(Authorization(bearer))| bearer.token());
336363
let (session_info, _cookie_jar) = cookie_jar.session_info();
337-
let requester = get_requester(&clock, &activity_tracker, repo, session_info, token).await?;
364+
let requester = get_requester(
365+
undocumented_oauth2_access,
366+
&clock,
367+
&activity_tracker,
368+
repo,
369+
session_info,
370+
token,
371+
)
372+
.await?;
338373

339374
let request =
340375
async_graphql::http::parse_query_string(&query.unwrap_or_default())?.data(requester);

crates/handlers/src/lib.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ use axum::{
3030
http::Method,
3131
response::{Html, IntoResponse},
3232
routing::{get, post},
33-
Router,
33+
Extension, Router,
3434
};
35+
use graphql::ExtraRouterParameters;
3536
use headers::HeaderName;
3637
use hyper::{
3738
header::{
@@ -108,7 +109,7 @@ where
108109
Router::new().route(mas_router::Healthcheck::route(), get(self::health::get))
109110
}
110111

111-
pub fn graphql_router<S>(playground: bool) -> Router<S>
112+
pub fn graphql_router<S>(playground: bool, undocumented_oauth2_access: bool) -> Router<S>
112113
where
113114
S: Clone + Send + Sync + 'static,
114115
graphql::Schema: FromRef<S>,
@@ -123,6 +124,11 @@ where
123124
mas_router::GraphQL::route(),
124125
get(self::graphql::get).post(self::graphql::post),
125126
)
127+
// Pass the undocumented_oauth2_access parameter through the request extension, as it is
128+
// per-listener
129+
.layer(Extension(ExtraRouterParameters {
130+
undocumented_oauth2_access,
131+
}))
126132
.layer(
127133
CorsLayer::new()
128134
.allow_origin(Any)

crates/handlers/src/test_utils.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,9 @@ impl TestState {
249249
.merge(crate::api_router())
250250
.merge(crate::compat_router())
251251
.merge(crate::human_router(self.templates.clone()))
252-
.merge(crate::graphql_router(false))
252+
// We enable undocumented_oauth2_access for the tests, as it is easier to query the API
253+
// with it
254+
.merge(crate::graphql_router(false, true))
253255
.merge(crate::admin_api_router().1)
254256
.with_state(self.clone())
255257
.into_service();

docs/config.schema.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@
3535
"name": "compat"
3636
},
3737
{
38-
"name": "graphql",
39-
"playground": true
38+
"name": "graphql"
4039
},
4140
{
4241
"name": "assets"
@@ -742,7 +741,10 @@
742741
},
743742
"playground": {
744743
"description": "Enabled the GraphQL playground",
745-
"default": false,
744+
"type": "boolean"
745+
},
746+
"undocumented_oauth2_access": {
747+
"description": "Allow access for OAuth 2.0 clients (undocumented)",
746748
"type": "boolean"
747749
}
748750
}

0 commit comments

Comments
 (0)