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-socks
- linux / features-sso_login
- linux / features-require_auth_for_profile_requests
include:
- name: linux / features-no-encryption
@ -119,6 +120,9 @@ jobs:
- name: linux / 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
cargo_args: --features markdown

View File

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

View File

@ -405,6 +405,7 @@ pub struct RequestConfig {
pub(crate) timeout: Duration,
pub(crate) retry_limit: Option<u64>,
pub(crate) retry_timeout: Option<Duration>,
pub(crate) force_auth: bool,
}
#[cfg(not(tarpaulin_include))]
@ -425,6 +426,7 @@ impl Default for RequestConfig {
timeout: DEFAULT_REQUEST_TIMEOUT,
retry_limit: Default::default(),
retry_timeout: Default::default(),
force_auth: false,
}
}
}
@ -459,6 +461,14 @@ impl RequestConfig {
self.retry_timeout = Some(retry_timeout);
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 {
@ -610,7 +620,14 @@ impl Client {
pub async fn avatar_url(&self) -> Result<Option<MxcUri>> {
let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?;
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)
}

View File

@ -47,6 +47,10 @@ pub enum HttpError {
#[error("the queried endpoint requires authentication but was called before logging in")]
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.
#[error("the queried endpoint is not meant for clients")]
NotClientRequest,

View File

@ -105,30 +105,39 @@ impl HttpClient {
session: Arc<RwLock<Option<Session>>>,
config: Option<RequestConfig>,
) -> Result<http::Response<Bytes>, HttpError> {
let request = {
let read_guard;
let access_token = match Request::METADATA.authentication {
AuthScheme::AccessToken => {
read_guard = session.read().await;
if let Some(session) = read_guard.as_ref() {
SendAccessToken::IfRequired(session.access_token.as_str())
} else {
return Err(HttpError::AuthenticationRequired);
}
}
AuthScheme::None => SendAccessToken::None,
_ => return Err(HttpError::NotClientRequest),
};
request.try_into_http_request(&self.homeserver.to_string(), access_token)?
};
let config = match config {
Some(config) => config,
None => self.request_config,
};
let request = {
let read_guard;
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 => {
read_guard = session.read().await;
if let Some(session) = read_guard.as_ref() {
SendAccessToken::IfRequired(session.access_token.as_str())
} else {
return Err(HttpError::AuthenticationRequired);
}
}
AuthScheme::None => SendAccessToken::None,
_ => return Err(HttpError::NotClientRequest),
}
};
request.try_into_http_request(&self.homeserver.to_string(), access_token)?
};
self.inner.send_request(request, config).await
}

View File

@ -44,6 +44,9 @@
//! * `markdown`: Support for sending markdown formatted messages.
//! * `socks`: Enables SOCKS support in reqwest, the default HTTP client.
//! * `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(
missing_debug_implementations,