Skip to main content

ruma_federation_api/policy/
sign_event.rs

1//! `POST /_matrix/policy/*/sign`
2//!
3//! Ask the [Policy Server] to sign an event.
4//!
5//! This endpoint MUST NOT be called for events which have a type of `m.room.policy` and an empty
6//! string `state_key`. All other events, including state events, non-state `m.room.policy` events,
7//! and `m.room.policy` state events with non-empty string `state_key`s are processed by this
8//! endpoint.
9//!
10//! Whether a signature is required by a Policy Server further depends on whether the room has
11//! enabled a Policy Server.
12//!
13//! [Policy Server]: https://spec.matrix.org/v1.18/server-server-api/#policy-servers
14
15pub mod v1 {
16    //! `/v1/` ([spec])
17    //!
18    //! [spec]: https://spec.matrix.org/v1.18/server-server-api/#post_matrixpolicyv1sign
19
20    use ruma_common::{
21        OwnedServerName, ServerName, ServerSignatures as ServerSignaturesMap,
22        ServerSigningKeyVersion, SigningKeyId,
23        api::{request, response},
24        metadata,
25    };
26    use ruma_events::room::policy::POLICY_SERVER_ED25519_SIGNING_KEY_ID;
27    use serde_json::value::RawValue as RawJsonValue;
28
29    use crate::authentication::ServerSignatures as ServerSignaturesAuth;
30
31    metadata! {
32        method: POST,
33        rate_limited: true,
34        authentication: ServerSignaturesAuth,
35        path: "/_matrix/policy/v1/sign",
36    }
37
38    /// Request type for the `sign_event` endpoint.
39    #[request]
40    pub struct Request {
41        /// The PDU to sign.
42        #[ruma_api(body)]
43        pub pdu: Box<RawJsonValue>,
44    }
45
46    /// Response type for the `sign_event` endpoint.
47    #[response]
48    pub struct Response {
49        /// A map containing the Policy Server's signature of the event.
50        ///
51        /// This signature is to be added to the event before sending or processing the event
52        /// further.
53        ///
54        /// `ed25519:policy_server` is always used for Ed25519 signatures.
55        #[ruma_api(body)]
56        pub signatures: ServerSignaturesMap,
57    }
58
59    impl Request {
60        /// Creates a new `Request` with the given PDU.
61        pub fn new(pdu: Box<RawJsonValue>) -> Self {
62            Self { pdu }
63        }
64    }
65
66    impl Response {
67        /// Creates a new `Response` with the given Policy Server name and event signature.
68        pub fn new(server_name: OwnedServerName, ed25519_signature: String) -> Self {
69            Self {
70                signatures: ServerSignaturesMap::from_iter(std::iter::once((
71                    server_name,
72                    SigningKeyId::parse(POLICY_SERVER_ED25519_SIGNING_KEY_ID)
73                        .expect("Policy Server default ed25519 signing key ID should be valid"),
74                    ed25519_signature,
75                ))),
76            }
77        }
78
79        /// Get the signature of the event for the given Policy Server name, if any.
80        pub fn ed25519_signature(&self, server_name: &ServerName) -> Option<&str> {
81            self.signatures
82                .get(server_name)?
83                .get(
84                    <&SigningKeyId<ServerSigningKeyVersion>>::try_from(
85                        POLICY_SERVER_ED25519_SIGNING_KEY_ID,
86                    )
87                    .expect("Policy Server default ed25519 signing key ID should be valid"),
88                )
89                .map(String::as_str)
90        }
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::v1::Response;
97
98    #[cfg(feature = "server")]
99    #[test]
100    fn construct_and_serialize_response() {
101        use ruma_common::{api::OutgoingResponse, owned_server_name};
102        use serde_json::{Value as JsonValue, from_slice as from_json_slice, json};
103
104        let response = Response::new(owned_server_name!("policy.example.org"), "zLFxllD0pbBuBpfHh8NuHNaICpReF/PAOpUQTsw+bFGKiGfDNAsnhcP7pbrmhhpfbOAxIdLraQLeeiXBryLmBw".to_owned());
105
106        let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
107
108        assert_eq!(
109            from_json_slice::<JsonValue>(http_response.body()).unwrap(),
110            json!({
111                "policy.example.org": {
112                    "ed25519:policy_server": "zLFxllD0pbBuBpfHh8NuHNaICpReF/PAOpUQTsw+bFGKiGfDNAsnhcP7pbrmhhpfbOAxIdLraQLeeiXBryLmBw",
113                },
114            })
115        );
116    }
117
118    #[cfg(feature = "client")]
119    #[test]
120    fn deserialize_response() {
121        use ruma_common::{api::IncomingResponse, server_name};
122        use serde_json::{json, to_vec as to_json_vec};
123
124        let http_response = http::Response::new(to_json_vec(&json!({
125            "policy.example.org": {
126                "ed25519:policy_server": "zLFxllD0pbBuBpfHh8NuHNaICpReF/PAOpUQTsw+bFGKiGfDNAsnhcP7pbrmhhpfbOAxIdLraQLeeiXBryLmBw",
127            },
128        })).unwrap());
129
130        let response = Response::try_from_http_response(http_response).unwrap();
131
132        assert_eq!(
133            response.ed25519_signature(server_name!("policy.example.org")),
134            Some(
135                "zLFxllD0pbBuBpfHh8NuHNaICpReF/PAOpUQTsw+bFGKiGfDNAsnhcP7pbrmhhpfbOAxIdLraQLeeiXBryLmBw"
136            )
137        );
138    }
139}