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