matrix-sdk: require_auth_for_profile_requests feature and force_auth request config

forces authentication for `get_avatar` which was previously done with
the unstable-synapse-quirks feature in ruma
master
Johannes Becker 2021-04-26 17:31:25 +02:00
parent 5c882f89e8
commit 242d46c9a1
6 changed files with 59 additions and 21 deletions

View File

@ -102,6 +102,7 @@ jobs:
- linux / features-markdown - linux / features-markdown
- linux / features-socks - linux / features-socks
- linux / features-sso_login - linux / features-sso_login
- linux / features-require_auth_for_profile_requests
include: include:
- name: linux / features-no-encryption - name: linux / features-no-encryption
@ -119,6 +120,9 @@ jobs:
- name: linux / features-rustls-tls - name: linux / features-rustls-tls
cargo_args: --no-default-features --features rustls-tls cargo_args: --no-default-features --features rustls-tls
- name: linux / features-require_auth_for_profile_requests
cargo_args: --no-default-features --features "require_auth_for_profile_requests, native-tls"
- name: linux / features-markdown - name: linux / features-markdown
cargo_args: --features markdown cargo_args: --features markdown

View File

@ -15,7 +15,7 @@ features = ["docs"]
rustdoc-args = ["--cfg", "feature=\"docs\""] rustdoc-args = ["--cfg", "feature=\"docs\""]
[features] [features]
default = ["encryption", "sled_cryptostore", "sled_state_store", "native-tls"] default = ["encryption", "sled_cryptostore", "sled_state_store", "require_auth_for_profile_requests", "native-tls"]
encryption = ["matrix-sdk-base/encryption"] encryption = ["matrix-sdk-base/encryption"]
sled_state_store = ["matrix-sdk-base/sled_state_store"] sled_state_store = ["matrix-sdk-base/sled_state_store"]
@ -25,6 +25,7 @@ native-tls = ["reqwest/native-tls"]
rustls-tls = ["reqwest/rustls-tls"] rustls-tls = ["reqwest/rustls-tls"]
socks = ["reqwest/socks"] socks = ["reqwest/socks"]
sso_login = ["warp", "rand", "tokio-stream"] sso_login = ["warp", "rand", "tokio-stream"]
require_auth_for_profile_requests = []
docs = ["encryption", "sled_cryptostore", "sled_state_store", "sso_login"] docs = ["encryption", "sled_cryptostore", "sled_state_store", "sso_login"]

View File

@ -405,6 +405,7 @@ pub struct RequestConfig {
pub(crate) timeout: Duration, pub(crate) timeout: Duration,
pub(crate) retry_limit: Option<u64>, pub(crate) retry_limit: Option<u64>,
pub(crate) retry_timeout: Option<Duration>, pub(crate) retry_timeout: Option<Duration>,
pub(crate) force_auth: bool,
} }
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
@ -425,6 +426,7 @@ impl Default for RequestConfig {
timeout: DEFAULT_REQUEST_TIMEOUT, timeout: DEFAULT_REQUEST_TIMEOUT,
retry_limit: Default::default(), retry_limit: Default::default(),
retry_timeout: Default::default(), retry_timeout: Default::default(),
force_auth: false,
} }
} }
} }
@ -459,6 +461,14 @@ impl RequestConfig {
self.retry_timeout = Some(retry_timeout); self.retry_timeout = Some(retry_timeout);
self self
} }
/// Force sending authorization even if the endpoint does not require it. Default is only
/// sending authorization if it is required
#[cfg(feature = "require_auth_for_profile_requests")]
pub(crate) fn force_auth(mut self) -> Self {
self.force_auth = true;
self
}
} }
impl Client { impl Client {
@ -610,7 +620,14 @@ impl Client {
pub async fn avatar_url(&self) -> Result<Option<MxcUri>> { pub async fn avatar_url(&self) -> Result<Option<MxcUri>> {
let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?; let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?;
let request = get_avatar_url::Request::new(&user_id); let request = get_avatar_url::Request::new(&user_id);
let response = self.send(request, None).await?;
#[cfg(not(feature = "require_auth_for_profile_requests"))]
let config = None;
#[cfg(feature = "require_auth_for_profile_requests")]
let config = Some(RequestConfig::new().force_auth());
let response = self.send(request, config).await?;
Ok(response.avatar_url) Ok(response.avatar_url)
} }

View File

@ -47,6 +47,10 @@ pub enum HttpError {
#[error("the queried endpoint requires authentication but was called before logging in")] #[error("the queried endpoint requires authentication but was called before logging in")]
AuthenticationRequired, AuthenticationRequired,
/// Client tried to force authentication but did not provide an access token.
#[error("tried to force authentication but no access token was provided")]
ForcedAuthenticationWithoutAccessToken,
/// Queried endpoint is not meant for clients. /// Queried endpoint is not meant for clients.
#[error("the queried endpoint is not meant for clients")] #[error("the queried endpoint is not meant for clients")]
NotClientRequest, NotClientRequest,

View File

@ -105,9 +105,22 @@ impl HttpClient {
session: Arc<RwLock<Option<Session>>>, session: Arc<RwLock<Option<Session>>>,
config: Option<RequestConfig>, config: Option<RequestConfig>,
) -> Result<http::Response<Bytes>, HttpError> { ) -> Result<http::Response<Bytes>, HttpError> {
let config = match config {
Some(config) => config,
None => self.request_config,
};
let request = { let request = {
let read_guard; let read_guard;
let access_token = match Request::METADATA.authentication { let access_token = if config.force_auth {
read_guard = session.read().await;
if let Some(session) = read_guard.as_ref() {
SendAccessToken::Always(session.access_token.as_str())
} else {
return Err(HttpError::ForcedAuthenticationWithoutAccessToken);
}
} else {
match Request::METADATA.authentication {
AuthScheme::AccessToken => { AuthScheme::AccessToken => {
read_guard = session.read().await; read_guard = session.read().await;
@ -119,16 +132,12 @@ impl HttpClient {
} }
AuthScheme::None => SendAccessToken::None, AuthScheme::None => SendAccessToken::None,
_ => return Err(HttpError::NotClientRequest), _ => return Err(HttpError::NotClientRequest),
}
}; };
request.try_into_http_request(&self.homeserver.to_string(), access_token)? request.try_into_http_request(&self.homeserver.to_string(), access_token)?
}; };
let config = match config {
Some(config) => config,
None => self.request_config,
};
self.inner.send_request(request, config).await self.inner.send_request(request, config).await
} }

View File

@ -44,6 +44,9 @@
//! * `markdown`: Support for sending markdown formatted messages. //! * `markdown`: Support for sending markdown formatted messages.
//! * `socks`: Enables SOCKS support in reqwest, the default HTTP client. //! * `socks`: Enables SOCKS support in reqwest, the default HTTP client.
//! * `sso_login`: Enables SSO login with a local http server. //! * `sso_login`: Enables SSO login with a local http server.
//! * `require_auth_for_profile_requests`: Whether to send the access token in the authentication
//! header when calling endpoints that retrieve profile data. This matches the synapse
//! configuration `require_auth_for_profile_requests`. Enabled by default.
#![deny( #![deny(
missing_debug_implementations, missing_debug_implementations,