Skip to main content

multistore_sts/
responses.rs

1//! STS XML response serialization.
2
3use multistore::error::ProxyError;
4use multistore::types::TemporaryCredentials;
5use quick_xml::se::to_string as xml_to_string;
6use serde::Serialize;
7
8/// STS AssumeRoleWithWebIdentity response.
9#[derive(Debug, Serialize)]
10#[serde(rename = "AssumeRoleWithWebIdentityResponse")]
11pub struct AssumeRoleWithWebIdentityResponse {
12    #[serde(rename = "AssumeRoleWithWebIdentityResult")]
13    pub result: AssumeRoleWithWebIdentityResult,
14}
15
16/// The result payload nested inside an `AssumeRoleWithWebIdentityResponse`.
17#[derive(Debug, Serialize)]
18pub struct AssumeRoleWithWebIdentityResult {
19    #[serde(rename = "Credentials")]
20    pub credentials: StsCredentials,
21    #[serde(rename = "AssumedRoleUser")]
22    pub assumed_role_user: AssumedRoleUser,
23}
24
25/// Temporary AWS credentials returned by an STS assume-role call.
26#[derive(Debug, Serialize)]
27pub struct StsCredentials {
28    #[serde(rename = "AccessKeyId")]
29    pub access_key_id: String,
30    #[serde(rename = "SecretAccessKey")]
31    pub secret_access_key: String,
32    #[serde(rename = "SessionToken")]
33    pub session_token: String,
34    #[serde(rename = "Expiration")]
35    pub expiration: String,
36}
37
38/// Identity information for the assumed role session.
39#[derive(Debug, Serialize)]
40pub struct AssumedRoleUser {
41    #[serde(rename = "AssumedRoleId")]
42    pub assumed_role_id: String,
43    #[serde(rename = "Arn")]
44    pub arn: String,
45}
46
47impl AssumeRoleWithWebIdentityResponse {
48    /// Serialize this response to an XML string with an XML declaration header.
49    pub fn to_xml(&self) -> String {
50        format!(
51            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n{}",
52            xml_to_string(self).unwrap_or_default()
53        )
54    }
55}
56
57/// Build an STS success response (status code + XML body) from temporary credentials.
58pub fn build_sts_response(creds: &TemporaryCredentials) -> (u16, String) {
59    let response = AssumeRoleWithWebIdentityResponse {
60        result: AssumeRoleWithWebIdentityResult {
61            credentials: StsCredentials {
62                access_key_id: creds.access_key_id.clone(),
63                secret_access_key: creds.secret_access_key.clone(),
64                session_token: creds.session_token.clone(),
65                expiration: creds.expiration.to_rfc3339(),
66            },
67            assumed_role_user: AssumedRoleUser {
68                assumed_role_id: creds.assumed_role_id.clone(),
69                arn: creds.assumed_role_id.clone(),
70            },
71        },
72    };
73    (200, response.to_xml())
74}
75
76/// Build an STS error response (status code + XML body) from a ProxyError.
77pub fn build_sts_error_response(err: &ProxyError) -> (u16, String) {
78    let (status, code, message) = match err {
79        ProxyError::RoleNotFound(r) => (
80            400,
81            "MalformedPolicyDocument",
82            format!("role not found: {}", r),
83        ),
84        ProxyError::InvalidOidcToken(msg) => (400, "InvalidIdentityToken", msg.clone()),
85        ProxyError::InvalidRequest(msg) => (400, "InvalidParameterValue", msg.clone()),
86        ProxyError::AccessDenied => (403, "AccessDenied", "access denied".to_string()),
87        _ => (500, "InternalError", "internal error".to_string()),
88    };
89
90    let xml = format!(
91        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
92         <ErrorResponse>\
93           <Error>\
94             <Code>{}</Code>\
95             <Message>{}</Message>\
96           </Error>\
97         </ErrorResponse>",
98        code, message
99    );
100    (status, xml)
101}