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
Cut down a lot on compilation time
This commit is contained in:
@ -19,7 +19,7 @@ use sqlx::{pool::PoolConnection, PgPool, Postgres};
|
|||||||
use tracing::{info_span, Instrument};
|
use tracing::{info_span, Instrument};
|
||||||
use warp::{filters::BoxedFilter, reply::with_header, Filter, Rejection, Reply};
|
use warp::{filters::BoxedFilter, reply::with_header, Filter, Rejection, Reply};
|
||||||
|
|
||||||
pub fn filter(pool: &PgPool) -> BoxedFilter<(impl Reply,)> {
|
pub fn filter(pool: &PgPool) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
warp::path!("health")
|
warp::path!("health")
|
||||||
.and(warp::get())
|
.and(warp::get())
|
||||||
.and(connection(pool))
|
.and(connection(pool))
|
||||||
@ -27,7 +27,7 @@ pub fn filter(pool: &PgPool) -> BoxedFilter<(impl Reply,)> {
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get(mut conn: PoolConnection<Postgres>) -> Result<impl Reply, Rejection> {
|
async fn get(mut conn: PoolConnection<Postgres>) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
sqlx::query("SELECT $1")
|
sqlx::query("SELECT $1")
|
||||||
.bind(1_i64)
|
.bind(1_i64)
|
||||||
.execute(&mut conn)
|
.execute(&mut conn)
|
||||||
@ -35,5 +35,9 @@ async fn get(mut conn: PoolConnection<Postgres>) -> Result<impl Reply, Rejection
|
|||||||
.await
|
.await
|
||||||
.wrap_error()?;
|
.wrap_error()?;
|
||||||
|
|
||||||
Ok(with_header("ok", CONTENT_TYPE, TEXT_PLAIN.to_string()))
|
Ok(Box::new(with_header(
|
||||||
|
"ok",
|
||||||
|
CONTENT_TYPE,
|
||||||
|
TEXT_PLAIN.to_string(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
@ -40,16 +40,25 @@ pub fn root(
|
|||||||
templates: &Templates,
|
templates: &Templates,
|
||||||
config: &RootConfig,
|
config: &RootConfig,
|
||||||
) -> BoxedFilter<(impl Reply,)> {
|
) -> BoxedFilter<(impl Reply,)> {
|
||||||
health(pool)
|
let health = health(pool);
|
||||||
.or(oauth2(pool, templates, &config.oauth2, &config.cookies))
|
let oauth2 = oauth2(pool, templates, &config.oauth2, &config.cookies);
|
||||||
.or(views(
|
let views = views(
|
||||||
pool,
|
pool,
|
||||||
templates,
|
templates,
|
||||||
&config.oauth2,
|
&config.oauth2,
|
||||||
&config.csrf,
|
&config.csrf,
|
||||||
&config.cookies,
|
&config.cookies,
|
||||||
))
|
);
|
||||||
.or(static_files(config.http.web_root.clone()))
|
let static_files = static_files(config.http.web_root.clone());
|
||||||
.with(warp::log(module_path!()))
|
|
||||||
|
let filter = health
|
||||||
|
.or(views)
|
||||||
|
.unify()
|
||||||
|
.or(static_files)
|
||||||
|
.unify()
|
||||||
.boxed()
|
.boxed()
|
||||||
|
.or(oauth2)
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
filter.with(warp::log(module_path!())).boxed()
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ use serde_json::Value;
|
|||||||
use sqlx::{PgExecutor, PgPool, Postgres, Transaction};
|
use sqlx::{PgExecutor, PgPool, Postgres, Transaction};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use warp::{
|
use warp::{
|
||||||
|
filters::BoxedFilter,
|
||||||
redirect::see_other,
|
redirect::see_other,
|
||||||
reject::InvalidQuery,
|
reject::InvalidQuery,
|
||||||
reply::{html, with_header},
|
reply::{html, with_header},
|
||||||
@ -216,7 +217,7 @@ pub fn filter(
|
|||||||
templates: &Templates,
|
templates: &Templates,
|
||||||
oauth2_config: &OAuth2Config,
|
oauth2_config: &OAuth2Config,
|
||||||
cookies_config: &CookiesConfig,
|
cookies_config: &CookiesConfig,
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
let clients = oauth2_config.clients.clone();
|
let clients = oauth2_config.clients.clone();
|
||||||
let authorize = warp::path!("oauth2" / "authorize")
|
let authorize = warp::path!("oauth2" / "authorize")
|
||||||
.and(warp::get())
|
.and(warp::get())
|
||||||
@ -243,6 +244,7 @@ pub fn filter(
|
|||||||
.and(warp::any().map(move || clients.clone()))
|
.and(warp::any().map(move || clients.clone()))
|
||||||
.and(with_templates(templates))
|
.and(with_templates(templates))
|
||||||
.and_then(actually_reply)
|
.and_then(actually_reply)
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn recover(rejection: Rejection) -> Result<ReplyOrBackToClient, Rejection> {
|
async fn recover(rejection: Rejection) -> Result<ReplyOrBackToClient, Rejection> {
|
||||||
@ -258,7 +260,7 @@ async fn actually_reply(
|
|||||||
q: PartialParams,
|
q: PartialParams,
|
||||||
clients: Vec<OAuth2ClientConfig>,
|
clients: Vec<OAuth2ClientConfig>,
|
||||||
templates: Templates,
|
templates: Templates,
|
||||||
) -> Result<impl Reply, Rejection> {
|
) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
let (redirect_uri, response_mode, state, params) = match rep {
|
let (redirect_uri, response_mode, state, params) = match rep {
|
||||||
ReplyOrBackToClient::Reply(r) => return Ok(r),
|
ReplyOrBackToClient::Reply(r) => return Ok(r),
|
||||||
ReplyOrBackToClient::BackToClient {
|
ReplyOrBackToClient::BackToClient {
|
||||||
|
@ -14,19 +14,15 @@
|
|||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use hyper::Method;
|
|
||||||
use mas_config::OAuth2Config;
|
use mas_config::OAuth2Config;
|
||||||
use mas_warp_utils::filters::cors::cors;
|
|
||||||
use oauth2_types::{
|
use oauth2_types::{
|
||||||
oidc::{Metadata, SigningAlgorithm},
|
oidc::{Metadata, SigningAlgorithm},
|
||||||
pkce::CodeChallengeMethod,
|
pkce::CodeChallengeMethod,
|
||||||
requests::{ClientAuthenticationMethod, GrantType, ResponseMode},
|
requests::{ClientAuthenticationMethod, GrantType, ResponseMode},
|
||||||
};
|
};
|
||||||
use warp::{Filter, Rejection, Reply};
|
use warp::{filters::BoxedFilter, Filter, Reply};
|
||||||
|
|
||||||
pub(super) fn filter(
|
pub(super) fn filter(config: &OAuth2Config) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
config: &OAuth2Config,
|
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
|
||||||
let base = config.issuer.clone();
|
let base = config.issuer.clone();
|
||||||
|
|
||||||
let response_modes_supported = Some({
|
let response_modes_supported = Some({
|
||||||
@ -97,9 +93,11 @@ pub(super) fn filter(
|
|||||||
code_challenge_methods_supported,
|
code_challenge_methods_supported,
|
||||||
};
|
};
|
||||||
|
|
||||||
warp::path!(".well-known" / "openid-configuration").and(
|
warp::path!(".well-known" / "openid-configuration")
|
||||||
warp::get()
|
.and(warp::get())
|
||||||
.map(move || warp::reply::json(&metadata))
|
.map(move || {
|
||||||
.with(cors().allow_method(Method::GET)),
|
let ret: Box<dyn Reply> = Box::new(warp::reply::json(&metadata));
|
||||||
)
|
ret
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use hyper::Method;
|
|
||||||
use mas_config::{OAuth2ClientConfig, OAuth2Config};
|
use mas_config::{OAuth2ClientConfig, OAuth2Config};
|
||||||
use mas_data_model::TokenType;
|
use mas_data_model::TokenType;
|
||||||
use mas_storage::oauth2::{
|
use mas_storage::oauth2::{
|
||||||
@ -20,33 +19,32 @@ use mas_storage::oauth2::{
|
|||||||
};
|
};
|
||||||
use mas_warp_utils::{
|
use mas_warp_utils::{
|
||||||
errors::WrapError,
|
errors::WrapError,
|
||||||
filters::{client::client_authentication, cors::cors, database::connection},
|
filters::{client::client_authentication, database::connection},
|
||||||
};
|
};
|
||||||
use oauth2_types::requests::{
|
use oauth2_types::requests::{
|
||||||
ClientAuthenticationMethod, IntrospectionRequest, IntrospectionResponse, TokenTypeHint,
|
ClientAuthenticationMethod, IntrospectionRequest, IntrospectionResponse, TokenTypeHint,
|
||||||
};
|
};
|
||||||
use sqlx::{pool::PoolConnection, PgPool, Postgres};
|
use sqlx::{pool::PoolConnection, PgPool, Postgres};
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
use warp::{Filter, Rejection, Reply};
|
use warp::{filters::BoxedFilter, Filter, Rejection, Reply};
|
||||||
|
|
||||||
pub fn filter(
|
pub fn filter(pool: &PgPool, oauth2_config: &OAuth2Config) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
pool: &PgPool,
|
|
||||||
oauth2_config: &OAuth2Config,
|
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
|
||||||
let audience = oauth2_config
|
let audience = oauth2_config
|
||||||
.issuer
|
.issuer
|
||||||
.join("/oauth2/introspect")
|
.join("/oauth2/introspect")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
warp::path!("oauth2" / "introspect").and(
|
warp::path!("oauth2" / "introspect")
|
||||||
|
.and(
|
||||||
warp::post()
|
warp::post()
|
||||||
.and(connection(pool))
|
.and(connection(pool))
|
||||||
.and(client_authentication(oauth2_config, audience))
|
.and(client_authentication(oauth2_config, audience))
|
||||||
.and_then(introspect)
|
.and_then(introspect)
|
||||||
.recover(recover)
|
.recover(recover)
|
||||||
.with(cors().allow_method(Method::POST)),
|
.unify(),
|
||||||
)
|
)
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
const INACTIVE: IntrospectionResponse = IntrospectionResponse {
|
const INACTIVE: IntrospectionResponse = IntrospectionResponse {
|
||||||
@ -69,12 +67,12 @@ async fn introspect(
|
|||||||
auth: ClientAuthenticationMethod,
|
auth: ClientAuthenticationMethod,
|
||||||
client: OAuth2ClientConfig,
|
client: OAuth2ClientConfig,
|
||||||
params: IntrospectionRequest,
|
params: IntrospectionRequest,
|
||||||
) -> Result<impl Reply, Rejection> {
|
) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
// Token introspection is only allowed by confidential clients
|
// Token introspection is only allowed by confidential clients
|
||||||
if auth.public() {
|
if auth.public() {
|
||||||
warn!(?client, "Client tried to introspect");
|
warn!(?client, "Client tried to introspect");
|
||||||
// TODO: have a nice error here
|
// TODO: have a nice error here
|
||||||
return Ok(warp::reply::json(&INACTIVE));
|
return Ok(Box::new(warp::reply::json(&INACTIVE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = ¶ms.token;
|
let token = ¶ms.token;
|
||||||
@ -82,7 +80,7 @@ async fn introspect(
|
|||||||
if let Some(hint) = params.token_type_hint {
|
if let Some(hint) = params.token_type_hint {
|
||||||
if token_type != hint {
|
if token_type != hint {
|
||||||
info!("Token type hint did not match");
|
info!("Token type hint did not match");
|
||||||
return Ok(warp::reply::json(&INACTIVE));
|
return Ok(Box::new(warp::reply::json(&INACTIVE)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,13 +128,13 @@ async fn introspect(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(warp::reply::json(&reply))
|
Ok(Box::new(warp::reply::json(&reply)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn recover(rejection: Rejection) -> Result<impl Reply, Rejection> {
|
async fn recover(rejection: Rejection) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
if rejection.is_not_found() {
|
if rejection.is_not_found() {
|
||||||
Err(rejection)
|
Err(rejection)
|
||||||
} else {
|
} else {
|
||||||
Ok(warp::reply::json(&INACTIVE))
|
Ok(Box::new(warp::reply::json(&INACTIVE)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,19 +12,16 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use hyper::Method;
|
|
||||||
use mas_config::OAuth2Config;
|
use mas_config::OAuth2Config;
|
||||||
use mas_warp_utils::filters::cors::cors;
|
use warp::{filters::BoxedFilter, Filter, Reply};
|
||||||
use warp::{Filter, Rejection, Reply};
|
|
||||||
|
|
||||||
pub(super) fn filter(
|
pub(super) fn filter(config: &OAuth2Config) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
config: &OAuth2Config,
|
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
|
||||||
let jwks = config.keys.to_public_jwks();
|
let jwks = config.keys.to_public_jwks();
|
||||||
|
|
||||||
warp::path!("oauth2" / "keys.json").and(
|
warp::path!("oauth2" / "keys.json")
|
||||||
warp::get()
|
.and(warp::get().map(move || {
|
||||||
.map(move || warp::reply::json(&jwks))
|
let ret: Box<dyn Reply> = Box::new(warp::reply::json(&jwks));
|
||||||
.with(cors().allow_method(Method::GET)),
|
ret
|
||||||
)
|
}))
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use hyper::Method;
|
||||||
use mas_config::{CookiesConfig, OAuth2Config};
|
use mas_config::{CookiesConfig, OAuth2Config};
|
||||||
use mas_templates::Templates;
|
use mas_templates::Templates;
|
||||||
|
use mas_warp_utils::filters::cors::cors;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use warp::{filters::BoxedFilter, Filter, Reply};
|
use warp::{filters::BoxedFilter, Filter, Reply};
|
||||||
|
|
||||||
@ -37,16 +39,28 @@ pub fn filter(
|
|||||||
oauth2_config: &OAuth2Config,
|
oauth2_config: &OAuth2Config,
|
||||||
cookies_config: &CookiesConfig,
|
cookies_config: &CookiesConfig,
|
||||||
) -> BoxedFilter<(impl Reply,)> {
|
) -> BoxedFilter<(impl Reply,)> {
|
||||||
discovery(oauth2_config)
|
let discovery = discovery(oauth2_config);
|
||||||
.or(keys(oauth2_config))
|
let keys = keys(oauth2_config);
|
||||||
.or(authorization(
|
let authorization = authorization(pool, templates, oauth2_config, cookies_config);
|
||||||
pool,
|
let userinfo = userinfo(pool, oauth2_config);
|
||||||
templates,
|
let introspection = introspection(pool, oauth2_config);
|
||||||
oauth2_config,
|
let token = token(pool, oauth2_config);
|
||||||
cookies_config,
|
|
||||||
))
|
let filter = discovery
|
||||||
.or(userinfo(pool, oauth2_config))
|
.or(keys)
|
||||||
.or(introspection(pool, oauth2_config))
|
.unify()
|
||||||
.or(token(pool, oauth2_config))
|
|
||||||
.boxed()
|
.boxed()
|
||||||
|
.or(userinfo)
|
||||||
|
.unify()
|
||||||
|
.boxed()
|
||||||
|
.or(token)
|
||||||
|
.unify()
|
||||||
|
.boxed()
|
||||||
|
.or(introspection)
|
||||||
|
.unify()
|
||||||
|
.boxed()
|
||||||
|
.with(cors().allow_methods([Method::POST, Method::GET]))
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
filter.or(authorization).boxed()
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ use anyhow::Context;
|
|||||||
use chrono::{DateTime, Duration, Utc};
|
use chrono::{DateTime, Duration, Utc};
|
||||||
use data_encoding::BASE64URL_NOPAD;
|
use data_encoding::BASE64URL_NOPAD;
|
||||||
use headers::{CacheControl, Pragma};
|
use headers::{CacheControl, Pragma};
|
||||||
use hyper::{Method, StatusCode};
|
use hyper::StatusCode;
|
||||||
use jwt_compact::{Claims, Header, TimeOptions};
|
use jwt_compact::{Claims, Header, TimeOptions};
|
||||||
use mas_config::{KeySet, OAuth2ClientConfig, OAuth2Config};
|
use mas_config::{KeySet, OAuth2ClientConfig, OAuth2Config};
|
||||||
use mas_data_model::{AuthorizationGrantStage, TokenType};
|
use mas_data_model::{AuthorizationGrantStage, TokenType};
|
||||||
@ -30,7 +30,7 @@ use mas_storage::{
|
|||||||
};
|
};
|
||||||
use mas_warp_utils::{
|
use mas_warp_utils::{
|
||||||
errors::WrapError,
|
errors::WrapError,
|
||||||
filters::{client::client_authentication, cors::cors, database::connection, with_keys},
|
filters::{client::client_authentication, database::connection, with_keys},
|
||||||
reply::with_typed_header,
|
reply::with_typed_header,
|
||||||
};
|
};
|
||||||
use oauth2_types::{
|
use oauth2_types::{
|
||||||
@ -49,6 +49,7 @@ use sqlx::{pool::PoolConnection, Acquire, PgPool, Postgres};
|
|||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use warp::{
|
use warp::{
|
||||||
|
filters::BoxedFilter,
|
||||||
reject::Reject,
|
reject::Reject,
|
||||||
reply::{json, with_status},
|
reply::{json, with_status},
|
||||||
Filter, Rejection, Reply,
|
Filter, Rejection, Reply,
|
||||||
@ -88,10 +89,7 @@ where
|
|||||||
Err(Error { json, status }.into())
|
Err(Error { json, status }.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filter(
|
pub fn filter(pool: &PgPool, oauth2_config: &OAuth2Config) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
pool: &PgPool,
|
|
||||||
oauth2_config: &OAuth2Config,
|
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
|
||||||
let audience = oauth2_config
|
let audience = oauth2_config
|
||||||
.issuer
|
.issuer
|
||||||
.join("/oauth2/token")
|
.join("/oauth2/token")
|
||||||
@ -99,7 +97,8 @@ pub fn filter(
|
|||||||
.to_string();
|
.to_string();
|
||||||
let issuer = oauth2_config.issuer.clone();
|
let issuer = oauth2_config.issuer.clone();
|
||||||
|
|
||||||
warp::path!("oauth2" / "token").and(
|
warp::path!("oauth2" / "token")
|
||||||
|
.and(
|
||||||
warp::post()
|
warp::post()
|
||||||
.and(client_authentication(oauth2_config, audience))
|
.and(client_authentication(oauth2_config, audience))
|
||||||
.and(with_keys(oauth2_config))
|
.and(with_keys(oauth2_config))
|
||||||
@ -107,13 +106,14 @@ pub fn filter(
|
|||||||
.and(connection(pool))
|
.and(connection(pool))
|
||||||
.and_then(token)
|
.and_then(token)
|
||||||
.recover(recover)
|
.recover(recover)
|
||||||
.with(cors().allow_method(Method::POST)),
|
.unify(),
|
||||||
)
|
)
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn recover(rejection: Rejection) -> Result<impl Reply, Rejection> {
|
async fn recover(rejection: Rejection) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
if let Some(Error { json, status }) = rejection.find::<Error>() {
|
if let Some(Error { json, status }) = rejection.find::<Error>() {
|
||||||
Ok(with_status(warp::reply::json(json), *status))
|
Ok(Box::new(with_status(warp::reply::json(json), *status)))
|
||||||
} else {
|
} else {
|
||||||
Err(rejection)
|
Err(rejection)
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ async fn token(
|
|||||||
keys: KeySet,
|
keys: KeySet,
|
||||||
issuer: Url,
|
issuer: Url,
|
||||||
mut conn: PoolConnection<Postgres>,
|
mut conn: PoolConnection<Postgres>,
|
||||||
) -> Result<impl Reply, Rejection> {
|
) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
let reply = match req {
|
let reply = match req {
|
||||||
AccessTokenRequest::AuthorizationCode(grant) => {
|
AccessTokenRequest::AuthorizationCode(grant) => {
|
||||||
let reply = authorization_code_grant(&grant, &client, &keys, issuer, &mut conn).await?;
|
let reply = authorization_code_grant(&grant, &client, &keys, issuer, &mut conn).await?;
|
||||||
@ -144,7 +144,7 @@ async fn token(
|
|||||||
|
|
||||||
let reply = with_typed_header(CacheControl::new().with_no_store(), reply);
|
let reply = with_typed_header(CacheControl::new().with_no_store(), reply);
|
||||||
let reply = with_typed_header(Pragma::no_cache(), reply);
|
let reply = with_typed_header(Pragma::no_cache(), reply);
|
||||||
Ok(reply)
|
Ok(Box::new(reply))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash<H: Digest>(mut hasher: H, token: &str) -> anyhow::Result<String> {
|
fn hash<H: Digest>(mut hasher: H, token: &str) -> anyhow::Result<String> {
|
||||||
|
@ -12,17 +12,13 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use hyper::Method;
|
|
||||||
use mas_config::OAuth2Config;
|
use mas_config::OAuth2Config;
|
||||||
use mas_data_model::{AccessToken, Session};
|
use mas_data_model::{AccessToken, Session};
|
||||||
use mas_storage::PostgresqlBackend;
|
use mas_storage::PostgresqlBackend;
|
||||||
use mas_warp_utils::filters::{
|
use mas_warp_utils::filters::authenticate::{authentication, recover_unauthorized};
|
||||||
authenticate::{authentication, recover_unauthorized},
|
|
||||||
cors::cors,
|
|
||||||
};
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use warp::{Filter, Rejection, Reply};
|
use warp::{filters::BoxedFilter, Filter, Rejection, Reply};
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct UserInfo {
|
struct UserInfo {
|
||||||
@ -30,28 +26,27 @@ struct UserInfo {
|
|||||||
username: String,
|
username: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn filter(
|
pub(super) fn filter(pool: &PgPool, _config: &OAuth2Config) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
pool: &PgPool,
|
warp::path!("oauth2" / "userinfo")
|
||||||
_config: &OAuth2Config,
|
.and(
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
|
||||||
warp::path!("oauth2" / "userinfo").and(
|
|
||||||
warp::get()
|
warp::get()
|
||||||
.or(warp::post())
|
.or(warp::post())
|
||||||
.unify()
|
.unify()
|
||||||
.and(authentication(pool))
|
.and(authentication(pool))
|
||||||
.and_then(userinfo)
|
.and_then(userinfo)
|
||||||
.recover(recover_unauthorized)
|
.recover(recover_unauthorized)
|
||||||
.with(cors().allow_methods([Method::GET, Method::POST])),
|
.unify(),
|
||||||
)
|
)
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn userinfo(
|
async fn userinfo(
|
||||||
_token: AccessToken<PostgresqlBackend>,
|
_token: AccessToken<PostgresqlBackend>,
|
||||||
session: Session<PostgresqlBackend>,
|
session: Session<PostgresqlBackend>,
|
||||||
) -> Result<impl Reply, Rejection> {
|
) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
let user = session.browser_session.user;
|
let user = session.browser_session.user;
|
||||||
Ok(warp::reply::json(&UserInfo {
|
Ok(Box::new(warp::reply::json(&UserInfo {
|
||||||
sub: user.sub,
|
sub: user.sub,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
}))
|
})))
|
||||||
}
|
}
|
||||||
|
@ -32,23 +32,20 @@ use mas_warp_utils::{
|
|||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::{pool::PoolConnection, PgExecutor, PgPool, Postgres, Transaction};
|
use sqlx::{pool::PoolConnection, PgExecutor, PgPool, Postgres, Transaction};
|
||||||
use warp::{reply::html, Filter, Rejection, Reply};
|
use warp::{filters::BoxedFilter, reply::html, Filter, Rejection, Reply};
|
||||||
|
|
||||||
pub(super) fn filter(
|
pub(super) fn filter(
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
templates: &Templates,
|
templates: &Templates,
|
||||||
csrf_config: &CsrfConfig,
|
csrf_config: &CsrfConfig,
|
||||||
cookies_config: &CookiesConfig,
|
cookies_config: &CookiesConfig,
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
let get = with_templates(templates)
|
let get = with_templates(templates)
|
||||||
.and(encrypted_cookie_saver(cookies_config))
|
.and(encrypted_cookie_saver(cookies_config))
|
||||||
.and(updated_csrf_token(cookies_config, csrf_config))
|
.and(updated_csrf_token(cookies_config, csrf_config))
|
||||||
.and(session(pool, cookies_config))
|
.and(session(pool, cookies_config))
|
||||||
.and(connection(pool))
|
.and(connection(pool))
|
||||||
.and_then(get)
|
.and_then(get);
|
||||||
.with(warp::filters::trace::trace(|_info| {
|
|
||||||
tracing::info_span!("GET /account")
|
|
||||||
}));
|
|
||||||
|
|
||||||
let post = with_templates(templates)
|
let post = with_templates(templates)
|
||||||
.and(encrypted_cookie_saver(cookies_config))
|
.and(encrypted_cookie_saver(cookies_config))
|
||||||
@ -56,14 +53,11 @@ pub(super) fn filter(
|
|||||||
.and(session(pool, cookies_config))
|
.and(session(pool, cookies_config))
|
||||||
.and(transaction(pool))
|
.and(transaction(pool))
|
||||||
.and(protected_form(cookies_config))
|
.and(protected_form(cookies_config))
|
||||||
.and_then(post)
|
.and_then(post);
|
||||||
.with(warp::filters::trace::trace(|_info| {
|
|
||||||
tracing::info_span!("POST /account")
|
|
||||||
}));
|
|
||||||
|
|
||||||
let filter = warp::get().and(get).or(warp::post().and(post));
|
let filter = warp::get().and(get).or(warp::post().and(post)).unify();
|
||||||
|
|
||||||
warp::path!("account").and(filter)
|
warp::path!("account").and(filter).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -24,7 +24,7 @@ use mas_warp_utils::filters::{
|
|||||||
};
|
};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use warp::{reply::html, Filter, Rejection, Reply};
|
use warp::{filters::BoxedFilter, reply::html, Filter, Rejection, Reply};
|
||||||
|
|
||||||
pub(super) fn filter(
|
pub(super) fn filter(
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
@ -32,7 +32,7 @@ pub(super) fn filter(
|
|||||||
oauth2_config: &OAuth2Config,
|
oauth2_config: &OAuth2Config,
|
||||||
csrf_config: &CsrfConfig,
|
csrf_config: &CsrfConfig,
|
||||||
cookies_config: &CookiesConfig,
|
cookies_config: &CookiesConfig,
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
let discovery_url = oauth2_config.discovery_url();
|
let discovery_url = oauth2_config.discovery_url();
|
||||||
warp::path::end()
|
warp::path::end()
|
||||||
.and(warp::get())
|
.and(warp::get())
|
||||||
@ -42,6 +42,7 @@ pub(super) fn filter(
|
|||||||
.and(updated_csrf_token(cookies_config, csrf_config))
|
.and(updated_csrf_token(cookies_config, csrf_config))
|
||||||
.and(optional_session(pool, cookies_config))
|
.and(optional_session(pool, cookies_config))
|
||||||
.and_then(get)
|
.and_then(get)
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get(
|
async fn get(
|
||||||
@ -50,7 +51,7 @@ async fn get(
|
|||||||
cookie_saver: EncryptedCookieSaver,
|
cookie_saver: EncryptedCookieSaver,
|
||||||
csrf_token: CsrfToken,
|
csrf_token: CsrfToken,
|
||||||
maybe_session: Option<BrowserSession<PostgresqlBackend>>,
|
maybe_session: Option<BrowserSession<PostgresqlBackend>>,
|
||||||
) -> Result<impl Reply, Rejection> {
|
) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
let ctx = IndexContext::new(discovery_url)
|
let ctx = IndexContext::new(discovery_url)
|
||||||
.maybe_with_session(maybe_session)
|
.maybe_with_session(maybe_session)
|
||||||
.with_csrf(csrf_token.form_value());
|
.with_csrf(csrf_token.form_value());
|
||||||
|
@ -29,7 +29,7 @@ use mas_warp_utils::{
|
|||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::{pool::PoolConnection, PgPool, Postgres};
|
use sqlx::{pool::PoolConnection, PgPool, Postgres};
|
||||||
use warp::{reply::html, Filter, Rejection, Reply};
|
use warp::{filters::BoxedFilter, reply::html, Filter, Rejection, Reply};
|
||||||
|
|
||||||
use super::{shared::PostAuthAction, RegisterRequest};
|
use super::{shared::PostAuthAction, RegisterRequest};
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ pub(super) fn filter(
|
|||||||
templates: &Templates,
|
templates: &Templates,
|
||||||
csrf_config: &CsrfConfig,
|
csrf_config: &CsrfConfig,
|
||||||
cookies_config: &CookiesConfig,
|
cookies_config: &CookiesConfig,
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
let get = warp::get()
|
let get = warp::get()
|
||||||
.and(with_templates(templates))
|
.and(with_templates(templates))
|
||||||
.and(connection(pool))
|
.and(connection(pool))
|
||||||
@ -113,7 +113,7 @@ pub(super) fn filter(
|
|||||||
.and(warp::query())
|
.and(warp::query())
|
||||||
.and_then(post);
|
.and_then(post);
|
||||||
|
|
||||||
warp::path!("login").and(get.or(post))
|
warp::path!("login").and(get.or(post).unify()).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get(
|
async fn get(
|
||||||
|
@ -20,27 +20,28 @@ use mas_warp_utils::{
|
|||||||
filters::{csrf::protected_form, database::transaction, session::session},
|
filters::{csrf::protected_form, database::transaction, session::session},
|
||||||
};
|
};
|
||||||
use sqlx::{PgPool, Postgres, Transaction};
|
use sqlx::{PgPool, Postgres, Transaction};
|
||||||
use warp::{hyper::Uri, Filter, Rejection, Reply};
|
use warp::{filters::BoxedFilter, hyper::Uri, Filter, Rejection, Reply};
|
||||||
|
|
||||||
pub(super) fn filter(
|
pub(super) fn filter(
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
cookies_config: &CookiesConfig,
|
cookies_config: &CookiesConfig,
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
warp::path!("logout")
|
warp::path!("logout")
|
||||||
.and(warp::post())
|
.and(warp::post())
|
||||||
.and(session(pool, cookies_config))
|
.and(session(pool, cookies_config))
|
||||||
.and(transaction(pool))
|
.and(transaction(pool))
|
||||||
.and(protected_form(cookies_config))
|
.and(protected_form(cookies_config))
|
||||||
.and_then(post)
|
.and_then(post)
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post(
|
async fn post(
|
||||||
session: BrowserSession<PostgresqlBackend>,
|
session: BrowserSession<PostgresqlBackend>,
|
||||||
mut txn: Transaction<'_, Postgres>,
|
mut txn: Transaction<'_, Postgres>,
|
||||||
_form: (),
|
_form: (),
|
||||||
) -> Result<impl Reply, Rejection> {
|
) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
end_session(&mut txn, &session).await.wrap_error()?;
|
end_session(&mut txn, &session).await.wrap_error()?;
|
||||||
txn.commit().await.wrap_error()?;
|
txn.commit().await.wrap_error()?;
|
||||||
|
|
||||||
Ok(warp::redirect(Uri::from_static("/login")))
|
Ok(Box::new(warp::redirect(Uri::from_static("/login"))))
|
||||||
}
|
}
|
||||||
|
@ -39,12 +39,16 @@ pub(super) fn filter(
|
|||||||
oauth2_config: &OAuth2Config,
|
oauth2_config: &OAuth2Config,
|
||||||
csrf_config: &CsrfConfig,
|
csrf_config: &CsrfConfig,
|
||||||
cookies_config: &CookiesConfig,
|
cookies_config: &CookiesConfig,
|
||||||
) -> BoxedFilter<(impl Reply,)> {
|
) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
index(pool, templates, oauth2_config, csrf_config, cookies_config)
|
let index = index(pool, templates, oauth2_config, csrf_config, cookies_config);
|
||||||
.or(account(pool, templates, csrf_config, cookies_config))
|
let account = account(pool, templates, csrf_config, cookies_config);
|
||||||
.or(login(pool, templates, csrf_config, cookies_config))
|
let login = login(pool, templates, csrf_config, cookies_config);
|
||||||
.or(register(pool, templates, csrf_config, cookies_config))
|
let register = register(pool, templates, csrf_config, cookies_config);
|
||||||
.or(logout(pool, cookies_config))
|
let logout = logout(pool, cookies_config);
|
||||||
.or(reauth(pool, templates, csrf_config, cookies_config))
|
let reauth = reauth(pool, templates, csrf_config, cookies_config);
|
||||||
.boxed()
|
|
||||||
|
let f1 = index.or(account).unify().boxed();
|
||||||
|
let f2 = login.or(register).unify().boxed();
|
||||||
|
let f3 = logout.or(reauth).unify().boxed();
|
||||||
|
f1.or(f2).unify().or(f3).unify().boxed()
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ use mas_warp_utils::{
|
|||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::{pool::PoolConnection, PgPool, Postgres, Transaction};
|
use sqlx::{pool::PoolConnection, PgPool, Postgres, Transaction};
|
||||||
use warp::{hyper::Uri, reply::html, Filter, Rejection, Reply};
|
use warp::{filters::BoxedFilter, hyper::Uri, reply::html, Filter, Rejection, Reply};
|
||||||
|
|
||||||
use super::PostAuthAction;
|
use super::PostAuthAction;
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ pub(super) fn filter(
|
|||||||
templates: &Templates,
|
templates: &Templates,
|
||||||
csrf_config: &CsrfConfig,
|
csrf_config: &CsrfConfig,
|
||||||
cookies_config: &CookiesConfig,
|
cookies_config: &CookiesConfig,
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
let get = warp::get()
|
let get = warp::get()
|
||||||
.and(with_templates(templates))
|
.and(with_templates(templates))
|
||||||
.and(connection(pool))
|
.and(connection(pool))
|
||||||
@ -110,7 +110,7 @@ pub(super) fn filter(
|
|||||||
.and(warp::query())
|
.and(warp::query())
|
||||||
.and_then(post);
|
.and_then(post);
|
||||||
|
|
||||||
warp::path!("reauth").and(get.or(post))
|
warp::path!("reauth").and(get.or(post).unify()).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get(
|
async fn get(
|
||||||
@ -120,7 +120,7 @@ async fn get(
|
|||||||
csrf_token: CsrfToken,
|
csrf_token: CsrfToken,
|
||||||
session: BrowserSession<PostgresqlBackend>,
|
session: BrowserSession<PostgresqlBackend>,
|
||||||
query: ReauthRequest<PostgresqlBackend>,
|
query: ReauthRequest<PostgresqlBackend>,
|
||||||
) -> Result<impl Reply, Rejection> {
|
) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
let ctx = ReauthContext::default();
|
let ctx = ReauthContext::default();
|
||||||
let ctx = match query.post_auth_action {
|
let ctx = match query.post_auth_action {
|
||||||
Some(next) => {
|
Some(next) => {
|
||||||
@ -134,7 +134,7 @@ async fn get(
|
|||||||
let content = templates.render_reauth(&ctx).await?;
|
let content = templates.render_reauth(&ctx).await?;
|
||||||
let reply = html(content);
|
let reply = html(content);
|
||||||
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
let reply = cookie_saver.save_encrypted(&csrf_token, reply)?;
|
||||||
Ok(reply)
|
Ok(Box::new(reply))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post(
|
async fn post(
|
||||||
@ -142,12 +142,12 @@ async fn post(
|
|||||||
mut txn: Transaction<'_, Postgres>,
|
mut txn: Transaction<'_, Postgres>,
|
||||||
form: ReauthForm,
|
form: ReauthForm,
|
||||||
query: ReauthRequest<PostgresqlBackend>,
|
query: ReauthRequest<PostgresqlBackend>,
|
||||||
) -> Result<impl Reply, Rejection> {
|
) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
// TODO: recover from errors here
|
// TODO: recover from errors here
|
||||||
authenticate_session(&mut txn, &mut session, form.password)
|
authenticate_session(&mut txn, &mut session, form.password)
|
||||||
.await
|
.await
|
||||||
.wrap_error()?;
|
.wrap_error()?;
|
||||||
txn.commit().await.wrap_error()?;
|
txn.commit().await.wrap_error()?;
|
||||||
|
|
||||||
Ok(query.redirect()?)
|
Ok(Box::new(query.redirect()?))
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ use mas_warp_utils::{
|
|||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::{pool::PoolConnection, PgPool, Postgres, Transaction};
|
use sqlx::{pool::PoolConnection, PgPool, Postgres, Transaction};
|
||||||
use warp::{reply::html, Filter, Rejection, Reply};
|
use warp::{filters::BoxedFilter, reply::html, Filter, Rejection, Reply};
|
||||||
|
|
||||||
use super::{LoginRequest, PostAuthAction};
|
use super::{LoginRequest, PostAuthAction};
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ pub(super) fn filter(
|
|||||||
templates: &Templates,
|
templates: &Templates,
|
||||||
csrf_config: &CsrfConfig,
|
csrf_config: &CsrfConfig,
|
||||||
cookies_config: &CookiesConfig,
|
cookies_config: &CookiesConfig,
|
||||||
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone + Send + Sync + 'static {
|
) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||||
let get = warp::get()
|
let get = warp::get()
|
||||||
.and(with_templates(templates))
|
.and(with_templates(templates))
|
||||||
.and(connection(pool))
|
.and(connection(pool))
|
||||||
@ -117,7 +117,7 @@ pub(super) fn filter(
|
|||||||
.and(warp::query())
|
.and(warp::query())
|
||||||
.and_then(post);
|
.and_then(post);
|
||||||
|
|
||||||
warp::path!("register").and(get.or(post))
|
warp::path!("register").and(get.or(post).unify()).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get(
|
async fn get(
|
||||||
@ -154,7 +154,7 @@ async fn post(
|
|||||||
cookie_saver: EncryptedCookieSaver,
|
cookie_saver: EncryptedCookieSaver,
|
||||||
form: RegisterForm,
|
form: RegisterForm,
|
||||||
query: RegisterRequest<PostgresqlBackend>,
|
query: RegisterRequest<PostgresqlBackend>,
|
||||||
) -> Result<impl Reply, Rejection> {
|
) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
// TODO: display nice form errors
|
// TODO: display nice form errors
|
||||||
if form.password != form.password_confirm {
|
if form.password != form.password_confirm {
|
||||||
return Err(anyhow::anyhow!("password mismatch")).wrap_error();
|
return Err(anyhow::anyhow!("password mismatch")).wrap_error();
|
||||||
@ -172,5 +172,5 @@ async fn post(
|
|||||||
let session_cookie = SessionCookie::from_session(&session_info);
|
let session_cookie = SessionCookie::from_session(&session_info);
|
||||||
let reply = query.redirect()?;
|
let reply = query.redirect()?;
|
||||||
let reply = cookie_saver.save_encrypted(&session_cookie, reply)?;
|
let reply = cookie_saver.save_encrypted(&session_cookie, reply)?;
|
||||||
Ok(reply)
|
Ok(Box::new(reply))
|
||||||
}
|
}
|
||||||
|
@ -136,13 +136,13 @@ async fn recover(
|
|||||||
/// per [RFC6750]. This is not intended for user-facing endpoints.
|
/// per [RFC6750]. This is not intended for user-facing endpoints.
|
||||||
///
|
///
|
||||||
/// [RFC6750]: https://www.rfc-editor.org/rfc/rfc6750.html
|
/// [RFC6750]: https://www.rfc-editor.org/rfc/rfc6750.html
|
||||||
pub async fn recover_unauthorized(rejection: Rejection) -> Result<impl Reply, Rejection> {
|
pub async fn recover_unauthorized(rejection: Rejection) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
if rejection.find::<AuthenticationError>().is_some() {
|
if rejection.find::<AuthenticationError>().is_some() {
|
||||||
// TODO: have the issuer/realm here
|
// TODO: have the issuer/realm here
|
||||||
let reply = "invalid token";
|
let reply = "invalid token";
|
||||||
let reply = with_status(reply, StatusCode::UNAUTHORIZED);
|
let reply = with_status(reply, StatusCode::UNAUTHORIZED);
|
||||||
let reply = with_header(reply, "WWW-Authenticate", r#"Bearer error="invalid_token""#);
|
let reply = with_header(reply, "WWW-Authenticate", r#"Bearer error="invalid_token""#);
|
||||||
return Ok(reply);
|
return Ok(Box::new(reply));
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(rejection)
|
Err(rejection)
|
||||||
|
Reference in New Issue
Block a user