Skip to content

Instantly share code, notes, and snippets.

@zerg000000
Last active November 12, 2022 23:03
Show Gist options
  • Save zerg000000/40978e85a971286e926dfcde0af7188a to your computer and use it in GitHub Desktop.
Save zerg000000/40978e85a971286e926dfcde0af7188a to your computer and use it in GitHub Desktop.
S3 presign (post) example aws-api
(require '[buddy.core.codecs.base64 :as base64]
'[cognitect.aws.util :as util]
'[clojure.string :as str]
'[jsonista.core :as json]
'[cognitect.aws.credentials :as creds]
'[cognitect.aws.http :as http])
(import '[java.util Date])
(defn host-style-bucket-uri [bucket]
(str "http://" bucket ".s3.amazonaws.com/"))
(defn new-x-amz-date
([] (new-x-amz-date (Date.)))
([d] (->> (util/format-date util/x-amz-date-format d)
(util/parse-date util/x-amz-date-format))))
(defn credential-scope
[{:keys [region service] :as auth-info} x-amz-date]
(str/join "/" [(get-in auth-info [:aws/access-key-id])
(->> x-amz-date
(util/format-date util/x-amz-date-only-format))
region
service
"aws4_request"]))
(defn signature
[{:keys [region service] :as auth-info} string x-amz-date]
(util/hex-encode
(util/hmac-sha-256 (-> (.getBytes (str "AWS4" (get-in auth-info [:aws/secret-access-key])) "UTF-8")
(util/hmac-sha-256 (util/format-date util/x-amz-date-only-format x-amz-date))
(util/hmac-sha-256 region)
(util/hmac-sha-256 service)
(util/hmac-sha-256 "aws4_request"))
string)))
(defn new-policy
"Create Policy for S3 Presign POST upload"
[{:keys [bucket] :as auth-info} key expiration conditions]
(let [x-amz-date (new-x-amz-date)]
{"conditions" (cond-> [{"bucket" bucket}
{"key" key}
{"x-amz-credential" (credential-scope auth-info x-amz-date)}
{"x-amz-algorithm" "AWS4-HMAC-SHA256"}
{"x-amz-date" (util/format-date util/x-amz-date-format x-amz-date)}]
conditions
(into conditions)
(:aws/session-token auth-info)
(conj {"x-amz-security-token" (:aws/session-token auth-info)}))
"expiration" (util/format-date util/iso8601-msecs-date-format expiration)}))
(defn get-cred-from-env []
(creds/fetch (creds/default-credentials-provider (http/resolve-http-client nil))))
(defn presign
"Create a browser based Image Upload payload as mentioned in
https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html"
[s3 key content-type expiration conditions]
(let [auth-info (merge (get-cred-from-env)
s3)
now (new-x-amz-date)
policy-string (-> (new-policy auth-info key expiration conditions)
(json/write-value-as-bytes)
(base64/encode)
(String.))]
(cond-> {:key key
:Content-Type content-type
:X-Amz-Credential (credential-scope auth-info now)
:X-Amz-Algorithm "AWS4-HMAC-SHA256"
:X-Amz-Date (util/format-date util/x-amz-date-format now)
:Policy policy-string
:X-Amz-Signature (signature auth-info policy-string now)}
(:aws/session-token auth-info)
(assoc :X-Amz-Security-Token (:aws/session-token auth-info)))))
;;;; Usage
(let [_id "my-file-name"
bucket {:bucket "my-s3-bucket"
:folder "my-private-folder"
:aws-region "ap-eastnorth-1" ; assume inside ecs container, aws-region need to provided by ourselves, since aws-api cannot fetch it from env correctly
:conditions [["starts-with" "$Content-Type" "image/"]
; limit size to 1byte - 10M
["content-length-range" 1 10485760]]
:service "s3"}]
{:uri (host-style-bucket-uri (:bucket bucket))
:form-data (presign bucket
(str (:folder bucket) "/" (.toString _id))
"image/jpeg"
expire
(:conditions bucket))
:expires-on expire
:_id _id})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment