Skip to content

Commit

Permalink
feat: Add a more convenient API on AmazonS3 for creating signed URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
carols10cents committed Sep 29, 2023
1 parent 74972e3 commit eea6f17
Showing 1 changed file with 53 additions and 2 deletions.
55 changes: 53 additions & 2 deletions object_store/src/aws/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ use bytes::Bytes;
use futures::stream::BoxStream;
use futures::{StreamExt, TryStreamExt};
use itertools::Itertools;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use snafu::{ensure, OptionExt, ResultExt, Snafu};
use std::str::FromStr;
use std::sync::Arc;
use std::{str::FromStr, sync::Arc, time::Duration};
use tokio::io::AsyncWrite;
use tracing::info;
use url::Url;
Expand Down Expand Up @@ -210,6 +210,57 @@ impl AmazonS3 {
&self.client.config().credentials
}

/// Create a URL containing the relevant [AWS SigV4] query parameters that authorize a request
/// via `method` to the resource at `path` valid for the duration specified in `expires_in`.
///
/// [AWS SigV4]: https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html
///
/// # Example
///
/// This example returns a URL that will enable a user to upload a file to
/// "some-folder/some-file.txt" in the next hour.
///
/// ```
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// use object_store::{aws::AmazonS3Builder, path::Path};
/// use reqwest::Method;
/// use std::time::Duration;
///
/// let region = "us-east-1";
/// let s3 = AmazonS3Builder::new()
/// .with_region(region)
/// .with_bucket_name("my-bucket")
/// .with_access_key_id("my-access-key-id")
/// .with_secret_access_key("my-secret-access-key")
/// .build()?;
///
/// let url = s3.signed_url(
/// Method::PUT,
/// &Path::from("some-folder/some-file.txt"),
/// Duration::from_secs(60 * 60)
/// ).await?;
/// # Ok(())
/// # }
/// ```
pub async fn signed_url(
&self,
method: Method,
path: &Path,
expires_in: Duration,
) -> Result<Url> {
let credential = self.credentials().get_credential().await?;
let authorizer =
AwsAuthorizer::new(&credential, "s3", &self.client.config().region);

let path_url = self.path_url(path);
let mut url =
Url::parse(&path_url).context(UnableToParseUrlSnafu { url: path_url })?;

authorizer.sign(method, &mut url, expires_in);

Ok(url)
}

/// Create a full URL to the resource specified by `path` with this instance's configuration.
pub fn path_url(&self, path: &Path) -> String {
self.client.config().path_url(path)
Expand Down

0 comments on commit eea6f17

Please sign in to comment.