Skip to main content

rusty_s3/actions/
get_bucket_policy.rs

1use std::iter;
2use std::time::Duration;
3
4use jiff::Timestamp;
5use serde::Deserialize;
6use url::Url;
7
8use super::S3Action;
9use crate::actions::Method;
10use crate::signing::sign;
11use crate::sorting_iter::SortingIterator;
12use crate::{Bucket, Credentials, Map};
13
14const POLICY_PARAM: &str = "policy";
15
16/// Retrieve a bucket's policy from S3.
17///
18/// Find out more about `GetBucketPolicy` from the [AWS API Reference][api]
19///
20/// [api]: https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketPolicy.html
21#[derive(Debug, Clone)]
22pub struct GetBucketPolicy<'a> {
23    bucket: &'a Bucket,
24    credentials: Option<&'a Credentials>,
25
26    query: Map<'a>,
27    headers: Map<'a>,
28}
29
30#[allow(clippy::module_name_repetitions)]
31#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
32pub struct GetBucketPolicyResponse {
33    #[serde(rename = "Version")]
34    pub version: String,
35    #[serde(rename = "Id")]
36    pub id: Option<String>,
37}
38
39impl<'a> GetBucketPolicy<'a> {
40    #[inline]
41    #[must_use]
42    pub const fn new(bucket: &'a Bucket, credentials: Option<&'a Credentials>) -> Self {
43        Self {
44            bucket,
45            credentials,
46
47            query: Map::new(),
48            headers: Map::new(),
49        }
50    }
51
52    /// Parse the response from S3.
53    ///
54    /// # Errors
55    ///
56    /// If the response cannot be parsed.
57    pub fn parse_response(s: &str) -> Result<GetBucketPolicyResponse, serde_json::Error> {
58        serde_json::from_str(s)
59    }
60}
61
62impl<'a> S3Action<'a> for GetBucketPolicy<'a> {
63    const METHOD: Method = Method::Get;
64
65    fn query_mut(&mut self) -> &mut Map<'a> {
66        &mut self.query
67    }
68
69    fn headers_mut(&mut self) -> &mut Map<'a> {
70        &mut self.headers
71    }
72
73    fn sign_with_time(&self, expires_in: Duration, time: &Timestamp) -> Url {
74        let url = self.bucket.base_url().clone();
75        let query = SortingIterator::new(iter::once((POLICY_PARAM, "")), self.query.iter());
76
77        match self.credentials {
78            Some(credentials) => sign(
79                time,
80                Self::METHOD,
81                url,
82                credentials.key(),
83                credentials.secret(),
84                credentials.token(),
85                self.bucket.region(),
86                expires_in.as_secs(),
87                query,
88                self.headers.iter(),
89            ),
90            None => crate::signing::util::add_query_params(url, self.query.iter()),
91        }
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use pretty_assertions::assert_eq;
98
99    use super::*;
100
101    #[test]
102    fn aws_example() -> Result<(), serde_json::Error> {
103        assert_eq!(
104            GetBucketPolicy::parse_response(r#"{"Version":"1"}"#)?,
105            GetBucketPolicyResponse {
106                version: "1".to_string(),
107                id: None
108            }
109        );
110
111        let content = r#"{
112"Version":"2008-10-17",
113"Id":"aaaa-bbbb-cccc-dddd",
114"Statement" : [
115    {
116        "Effect":"Deny",
117        "Sid":"1",
118        "Principal" : {
119            "AWS":["111122223333","444455556666"]
120        },
121        "Action":["s3:*"],
122        "Resource":"arn:aws:s3:::bucket/*"
123    }
124]
125}
126"#;
127        assert_eq!(
128            GetBucketPolicy::parse_response(content)?,
129            GetBucketPolicyResponse {
130                version: "2008-10-17".to_string(),
131                id: Some("aaaa-bbbb-cccc-dddd".to_string()),
132            }
133        );
134        Ok(())
135    }
136}