mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-10 00:48:44 -04:00
test(oidc): Use MatrixMockServer for cross-signing test
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
This commit is contained in:
committed by
Damir Jelić
parent
9b406cff87
commit
54ab46dcb4
@@ -983,6 +983,28 @@ impl MatrixMockServer {
|
||||
let mock = Mock::given(method("GET")).and(path_regex(r"^/.well-known/matrix/client"));
|
||||
MockEndpoint { mock, server: &self.server, endpoint: WellKnownEndpoint }
|
||||
}
|
||||
|
||||
/// Creates a prebuilt mock for the endpoint used to publish cross-signing
|
||||
/// keys.
|
||||
pub fn mock_upload_cross_signing_keys(
|
||||
&self,
|
||||
) -> MockEndpoint<'_, UploadCrossSigningKeysEndpoint> {
|
||||
let mock = Mock::given(method("POST"))
|
||||
.and(path_regex(r"^/_matrix/client/v3/keys/device_signing/upload"))
|
||||
.and(header("authorization", "Bearer 1234"));
|
||||
MockEndpoint { mock, server: &self.server, endpoint: UploadCrossSigningKeysEndpoint }
|
||||
}
|
||||
|
||||
/// Creates a prebuilt mock for the endpoint used to publish cross-signing
|
||||
/// signatures.
|
||||
pub fn mock_upload_cross_signing_signatures(
|
||||
&self,
|
||||
) -> MockEndpoint<'_, UploadCrossSigningSignaturesEndpoint> {
|
||||
let mock = Mock::given(method("POST"))
|
||||
.and(path_regex(r"^/_matrix/client/v3/keys/signatures/upload"))
|
||||
.and(header("authorization", "Bearer 1234"));
|
||||
MockEndpoint { mock, server: &self.server, endpoint: UploadCrossSigningSignaturesEndpoint }
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameter to [`MatrixMockServer::sync_room`].
|
||||
@@ -2446,3 +2468,46 @@ impl<'a> MockEndpoint<'a, WellKnownEndpoint> {
|
||||
MatrixMock { server: self.server, mock }
|
||||
}
|
||||
}
|
||||
|
||||
/// A prebuilt mock for `POST /keys/device_signing/upload` request.
|
||||
pub struct UploadCrossSigningKeysEndpoint;
|
||||
|
||||
impl<'a> MockEndpoint<'a, UploadCrossSigningKeysEndpoint> {
|
||||
/// Returns a successful empty response.
|
||||
pub fn ok(self) -> MatrixMock<'a> {
|
||||
let mock = self.mock.respond_with(ResponseTemplate::new(200).set_body_json(json!({})));
|
||||
|
||||
MatrixMock { server: self.server, mock }
|
||||
}
|
||||
|
||||
/// Returns an error response with an OAuth 2.0 UIAA stage.
|
||||
#[cfg(feature = "experimental-oidc")]
|
||||
pub fn uiaa_oauth(self) -> MatrixMock<'a> {
|
||||
let mock = self.mock.respond_with(ResponseTemplate::new(401).set_body_json(json!({
|
||||
"session": "dummy",
|
||||
"flows": [{
|
||||
"stages": [ "org.matrix.cross_signing_reset" ]
|
||||
}],
|
||||
"params": {
|
||||
"org.matrix.cross_signing_reset": {
|
||||
"url": format!("{}/account/?action=org.matrix.cross_signing_reset", self.server.uri())
|
||||
}
|
||||
},
|
||||
"msg": "To reset your end-to-end encryption cross-signing identity, you first need to approve it and then try again."
|
||||
})));
|
||||
|
||||
MatrixMock { server: self.server, mock }
|
||||
}
|
||||
}
|
||||
|
||||
/// A prebuilt mock for `POST /keys/signatures/upload` request.
|
||||
pub struct UploadCrossSigningSignaturesEndpoint;
|
||||
|
||||
impl<'a> MockEndpoint<'a, UploadCrossSigningSignaturesEndpoint> {
|
||||
/// Returns a successful empty response.
|
||||
pub fn ok(self) -> MatrixMock<'a> {
|
||||
let mock = self.mock.respond_with(ResponseTemplate::new(200).set_body_json(json!({})));
|
||||
|
||||
MatrixMock { server: self.server, mock }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,169 +117,65 @@ async fn test_reset_legacy_auth() {
|
||||
#[cfg(feature = "experimental-oidc")]
|
||||
#[async_test]
|
||||
async fn test_reset_oidc() {
|
||||
use std::sync::{
|
||||
atomic::{AtomicU8, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
use assert_matches2::assert_let;
|
||||
use mas_oidc_client::types::{
|
||||
iana::oauth::OAuthClientAuthenticationMethod,
|
||||
registration::{ClientMetadata, VerifiedClientMetadata},
|
||||
};
|
||||
use matrix_sdk::{
|
||||
authentication::oidc::{
|
||||
registrations::ClientId, OidcSession, OidcSessionTokens, UserSession,
|
||||
},
|
||||
encryption::CrossSigningResetAuthType,
|
||||
};
|
||||
use matrix_sdk::{encryption::CrossSigningResetAuthType, test_utils::mocks::MatrixMockServer};
|
||||
use similar_asserts::assert_eq;
|
||||
use url::Url;
|
||||
use wiremock::MockServer;
|
||||
|
||||
const CLIENT_ID: &str = "test_client_id";
|
||||
const REDIRECT_URI_STRING: &str = "http://matrix.example.com/oidc/callback";
|
||||
|
||||
let (client, server) = no_retry_test_client_with_server().await;
|
||||
|
||||
pub fn mock_registered_client_data() -> (ClientId, VerifiedClientMetadata) {
|
||||
(
|
||||
ClientId(CLIENT_ID.to_owned()),
|
||||
ClientMetadata {
|
||||
redirect_uris: Some(vec![Url::parse(REDIRECT_URI_STRING).unwrap()]),
|
||||
token_endpoint_auth_method: Some(OAuthClientAuthenticationMethod::None),
|
||||
..ClientMetadata::default()
|
||||
}
|
||||
.validate()
|
||||
.expect("validate client metadata"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn mock_session(tokens: OidcSessionTokens, server: &MockServer) -> OidcSession {
|
||||
let (client_id, metadata) = mock_registered_client_data();
|
||||
OidcSession {
|
||||
client_id,
|
||||
metadata,
|
||||
user: UserSession {
|
||||
meta: SessionMeta {
|
||||
user_id: ruma::user_id!("@u:e.uk").to_owned(),
|
||||
device_id: ruma::device_id!("XYZ").to_owned(),
|
||||
},
|
||||
tokens,
|
||||
issuer: server.uri(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let tokens = OidcSessionTokens {
|
||||
access_token: "4cc3ss".to_owned(),
|
||||
refresh_token: Some("r3fr3sh".to_owned()),
|
||||
latest_id_token: None,
|
||||
};
|
||||
|
||||
let session = mock_session(tokens.clone(), &server);
|
||||
|
||||
client
|
||||
.oidc()
|
||||
.restore_session(session.clone())
|
||||
.await
|
||||
.expect("We should be able to restore the OIDC session");
|
||||
let server = MatrixMockServer::new().await;
|
||||
let client = server.client_builder().logged_in_with_oauth(server.server().uri()).build().await;
|
||||
|
||||
assert!(
|
||||
!client.encryption().cross_signing_status().await.unwrap().is_complete(),
|
||||
"Initially we shouldn't have any cross-signin keys",
|
||||
);
|
||||
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/_matrix/client/r0/keys/upload"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
|
||||
"one_time_key_counts": {
|
||||
"signed_curve25519": 50
|
||||
}
|
||||
})))
|
||||
server.mock_upload_keys().ok().expect(1).named("Initial device keys upload").mount().await;
|
||||
|
||||
// Return the UIAA response 5 times.
|
||||
server
|
||||
.mock_upload_cross_signing_keys()
|
||||
.uiaa_oauth()
|
||||
.up_to_n_times(5)
|
||||
.expect(5)
|
||||
.named("Trying to upload the cross-signing keys with UIAA response")
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
// And finally succeed.
|
||||
// This works because the first mocked endpoint that matches the path is used
|
||||
// until it is invalidated by `up_to_n_times`.
|
||||
server
|
||||
.mock_upload_cross_signing_keys()
|
||||
.ok()
|
||||
.expect(1)
|
||||
.named("Initial device keys upload")
|
||||
.mount(&server)
|
||||
.named("Succeeding to upload the cross-signing keys")
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
let uiaa_response_body = json!({
|
||||
"session": "dummy",
|
||||
"flows": [{
|
||||
"stages": [ "org.matrix.cross_signing_reset" ]
|
||||
}],
|
||||
"params": {
|
||||
"org.matrix.cross_signing_reset": {
|
||||
"url": format!("{}/account/?action=org.matrix.cross_signing_reset", server.uri())
|
||||
}
|
||||
},
|
||||
"msg": "To reset your end-to-end encryption cross-signing identity, you first need to approve it and then try again."
|
||||
});
|
||||
|
||||
let reset_handle = {
|
||||
let _guard = Mock::given(method("POST"))
|
||||
.and(path("/_matrix/client/unstable/keys/device_signing/upload"))
|
||||
.respond_with(ResponseTemplate::new(401).set_body_json(uiaa_response_body.clone()))
|
||||
.expect(1)
|
||||
.named("Initial cross-signing upload attempt")
|
||||
.mount_as_scoped(&server)
|
||||
.await;
|
||||
|
||||
let handle = client
|
||||
.encryption()
|
||||
.reset_cross_signing()
|
||||
.await
|
||||
.unwrap()
|
||||
.expect("We should have received a reset handle");
|
||||
|
||||
assert_let!(CrossSigningResetAuthType::Oidc(oidc_info) = handle.auth_type());
|
||||
assert_eq!(
|
||||
oidc_info.approval_url.as_str(),
|
||||
format!("{}/account/?action=org.matrix.cross_signing_reset", server.uri())
|
||||
);
|
||||
|
||||
handle
|
||||
};
|
||||
|
||||
let counter = Arc::new(AtomicU8::default());
|
||||
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/_matrix/client/unstable/keys/device_signing/upload"))
|
||||
.respond_with({
|
||||
let counter = counter.clone();
|
||||
|
||||
move |_: &wiremock::Request| {
|
||||
let current_value = counter.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
println!("Hello {current_value}");
|
||||
// Only allow us to proceed on the 5th attempt, count started at 0, so if the
|
||||
// current value is at 4 it's the 5th attempt.
|
||||
if current_value >= 4 {
|
||||
ResponseTemplate::new(200).set_body_json(json!({}))
|
||||
} else {
|
||||
ResponseTemplate::new(401).set_body_json(uiaa_response_body.clone())
|
||||
}
|
||||
}
|
||||
})
|
||||
.expect(1..)
|
||||
.named("Retrying to upload the cross-signing keys")
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/_matrix/client/unstable/keys/signatures/upload"))
|
||||
.respond_with(move |_: &wiremock::Request| {
|
||||
ResponseTemplate::new(200).set_body_json(json!({}))
|
||||
})
|
||||
server
|
||||
.mock_upload_cross_signing_signatures()
|
||||
.ok()
|
||||
.expect(1)
|
||||
.named("Final signatures upload")
|
||||
.mount(&server)
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
reset_handle.auth(None).await.expect("We should be able to reset the cross-signing keys after some attempts, waiting for the auth issue to allow us to upload");
|
||||
// First requests gives us a reset handle.
|
||||
let reset_handle = client
|
||||
.encryption()
|
||||
.reset_cross_signing()
|
||||
.await
|
||||
.unwrap()
|
||||
.expect("We should have received a reset handle");
|
||||
|
||||
// 5 because we incremented the counter once more in the request handler
|
||||
// closure.
|
||||
assert_eq!(counter.load(Ordering::SeqCst), 5);
|
||||
assert_let!(CrossSigningResetAuthType::Oidc(oidc_info) = reset_handle.auth_type());
|
||||
assert_eq!(
|
||||
oidc_info.approval_url.as_str(),
|
||||
format!("{}/account/?action=org.matrix.cross_signing_reset", server.server().uri())
|
||||
);
|
||||
|
||||
// Then it retries until it succeeds.
|
||||
reset_handle.auth(None).await.expect("We should be able to reset the cross-signing keys after some attempts, waiting for the auth issue to allow us to upload");
|
||||
|
||||
assert!(
|
||||
client.encryption().cross_signing_status().await.unwrap().is_complete(),
|
||||
|
||||
Reference in New Issue
Block a user