Skip to content

Commit f39e63e

Browse files
committed
feat: Add a more convenient API on AmazonS3 for creating signed URLs
1 parent 5c2db89 commit f39e63e

File tree

1 file changed

+53
-2
lines changed

1 file changed

+53
-2
lines changed

object_store/src/aws/mod.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ use bytes::Bytes;
3636
use futures::stream::BoxStream;
3737
use futures::{StreamExt, TryStreamExt};
3838
use itertools::Itertools;
39+
use reqwest::Method;
3940
use serde::{Deserialize, Serialize};
4041
use snafu::{ensure, OptionExt, ResultExt, Snafu};
41-
use std::str::FromStr;
42-
use std::sync::Arc;
42+
use std::{str::FromStr, sync::Arc, time::Duration};
4343
use tokio::io::AsyncWrite;
4444
use tracing::info;
4545
use url::Url;
@@ -210,6 +210,57 @@ impl AmazonS3 {
210210
&self.client.config().credentials
211211
}
212212

213+
/// Create a URL containing the relevant [AWS SigV4] query parameters that authorize a request
214+
/// via `method` to the resource at `path` valid for the duration specified in `expires_in`.
215+
///
216+
/// [AWS SigV4]: https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html
217+
///
218+
/// # Example
219+
///
220+
/// This example returns a URL that will enable a user to upload a file to
221+
/// "some-folder/some-file.txt" in the next hour.
222+
///
223+
/// ```
224+
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
225+
/// use object_store::{aws::AmazonS3Builder, path::Path};
226+
/// use reqwest::Method;
227+
/// use std::time::Duration;
228+
///
229+
/// let region = "us-east-1";
230+
/// let s3 = AmazonS3Builder::new()
231+
/// .with_region(region)
232+
/// .with_bucket_name("my-bucket")
233+
/// .with_access_key_id("my-access-key-id")
234+
/// .with_secret_access_key("my-secret-access-key")
235+
/// .build()?;
236+
///
237+
/// let url = s3.signed_url(
238+
/// Method::PUT,
239+
/// &Path::from("some-folder/some-file.txt"),
240+
/// Duration::from_secs(60 * 60)
241+
/// ).await?;
242+
/// # Ok(())
243+
/// # }
244+
/// ```
245+
pub async fn signed_url(
246+
&self,
247+
method: Method,
248+
path: &Path,
249+
expires_in: Duration,
250+
) -> Result<Url> {
251+
let credential = self.credentials().get_credential().await?;
252+
let authorizer =
253+
AwsAuthorizer::new(&credential, "s3", &self.client.config().region);
254+
255+
let path_url = self.path_url(path);
256+
let mut url =
257+
Url::parse(&path_url).context(UnableToParseUrlSnafu { url: path_url })?;
258+
259+
authorizer.sign(method, &mut url, expires_in);
260+
261+
Ok(url)
262+
}
263+
213264
/// Create a full URL to the resource specified by `path` with this instance's configuration.
214265
pub fn path_url(&self, path: &Path) -> String {
215266
self.client.config().path_url(path)

0 commit comments

Comments
 (0)