{-# LANGUAGE DataKinds #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
module Network.DO.Spaces.Request
( newSpacesRequest
, mkSignature
, mkStringToSign
, mkAuthorization
, finalize
) where
import Control.Monad.Catch ( MonadThrow )
import Crypto.Hash ( SHA256, hashlazy )
import Crypto.MAC.HMAC ( hmac )
import Data.Bifunctor ( first )
import Data.ByteArray ( convert )
import Data.ByteString ( ByteString )
import qualified Data.ByteString.Base16 as B16
import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy as LB
import qualified Data.CaseInsensitive as CI
import Data.Coerce ( coerce )
import Data.Function ( (&) )
import Data.Generics.Product ( HasField(field) )
import Data.Generics.Product.Positions ( HasPosition(position) )
import Data.List ( sort )
import Data.Maybe ( fromMaybe )
import qualified Data.Text as T
import Data.Time
( UTCTime
, defaultTimeLocale
, formatTime
)
import Lens.Micro ( (^.) )
import Network.DO.Spaces.Types
import Network.DO.Spaces.Utils
( bodyLBS
, regionSlug
, toLowerBS
)
import Network.HTTP.Client.Conduit ( Request
, RequestBody(RequestBodyLBS)
)
import qualified Network.HTTP.Client.Conduit as H
import Network.HTTP.Types ( Header, Query, QueryItem )
import qualified Network.HTTP.Types as H
finalize :: SpacesRequest -> Authorization -> Request
finalize :: SpacesRequest -> Authorization -> Request
finalize SpacesRequest
sr Authorization
auth = Request
req { requestHeaders :: RequestHeaders
H.requestHeaders = (CI ByteString, ByteString)
authHeader (CI ByteString, ByteString) -> RequestHeaders -> RequestHeaders
forall a. a -> [a] -> [a]
: RequestHeaders
reqHeaders }
where
authHeader :: (CI ByteString, ByteString)
authHeader = (ByteString -> CI ByteString
forall s. FoldCase s => s -> CI s
CI.mk ByteString
"authorization", Authorization -> ByteString
forall (a :: ComputedTag). Computed a -> ByteString
uncompute Authorization
auth)
req :: Request
req = SpacesRequest
sr SpacesRequest -> Getting Request SpacesRequest Request -> Request
forall s a. s -> Getting a s a -> a
^. forall s t a b. HasField "request" s t a b => Lens s t a b
forall (field :: Symbol) s t a b.
HasField field s t a b =>
Lens s t a b
field @"request"
reqHeaders :: RequestHeaders
reqHeaders = Request
req Request -> (Request -> RequestHeaders) -> RequestHeaders
forall a b. a -> (a -> b) -> b
& Request -> RequestHeaders
H.requestHeaders
newSpacesRequest
:: MonadThrow m => SpacesRequestBuilder -> UTCTime -> m SpacesRequest
newSpacesRequest :: SpacesRequestBuilder -> UTCTime -> m SpacesRequest
newSpacesRequest SpacesRequestBuilder { RequestHeaders
Maybe Query
Maybe RequestBody
Maybe Object
Maybe Bucket
Maybe Method
Maybe Region
Spaces
$sel:overrideRegion:SpacesRequestBuilder :: SpacesRequestBuilder -> Maybe Region
$sel:subresources:SpacesRequestBuilder :: SpacesRequestBuilder -> Maybe Query
$sel:queryString:SpacesRequestBuilder :: SpacesRequestBuilder -> Maybe Query
$sel:object:SpacesRequestBuilder :: SpacesRequestBuilder -> Maybe Object
$sel:bucket:SpacesRequestBuilder :: SpacesRequestBuilder -> Maybe Bucket
$sel:headers:SpacesRequestBuilder :: SpacesRequestBuilder -> RequestHeaders
$sel:method:SpacesRequestBuilder :: SpacesRequestBuilder -> Maybe Method
$sel:body:SpacesRequestBuilder :: SpacesRequestBuilder -> Maybe RequestBody
$sel:spaces:SpacesRequestBuilder :: SpacesRequestBuilder -> Spaces
overrideRegion :: Maybe Region
subresources :: Maybe Query
queryString :: Maybe Query
object :: Maybe Object
bucket :: Maybe Bucket
headers :: RequestHeaders
method :: Maybe Method
body :: Maybe RequestBody
spaces :: Spaces
.. } UTCTime
time = do
Request
req <- String -> m Request
forall (m :: * -> *). MonadThrow m => String -> m Request
H.parseRequest
(String -> m Request) -> String -> m Request
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall a. Monoid a => [a] -> a
mconcat [ Method -> String
forall a. Show a => a -> String
show Method
reqMethod
, String
" "
, String
"https://"
, String -> (Bucket -> String) -> Maybe Bucket -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
forall a. Monoid a => a
mempty ((String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
".") (String -> String) -> (Bucket -> String) -> Bucket -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack (Text -> String) -> (Bucket -> Text) -> Bucket -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bucket -> Text
coerce) Maybe Bucket
bucket
, Region -> String
forall a. IsString a => Region -> a
regionSlug
(Region -> String) -> Region -> String
forall a b. (a -> b) -> a -> b
$ Region -> Maybe Region -> Region
forall a. a -> Maybe a -> a
fromMaybe (Spaces
spaces Spaces -> Getting Region Spaces Region -> Region
forall s a. s -> Getting a s a -> a
^. forall s t a b. HasField "region" s t a b => Lens s t a b
forall (field :: Symbol) s t a b.
HasField field s t a b =>
Lens s t a b
field @"region") Maybe Region
overrideRegion
, String
"."
, String
"digitaloceanspaces.com/"
, String -> (Object -> String) -> Maybe Object -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
forall a. Monoid a => a
mempty (Text -> String
T.unpack (Text -> String) -> (Object -> Text) -> Object -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Object -> Text
coerce) Maybe Object
object
]
ByteString
payload <- RequestBody -> m ByteString
forall (m :: * -> *). MonadThrow m => RequestBody -> m ByteString
bodyLBS RequestBody
reqBody
let payloadHash :: Hashed
payloadHash = ByteString -> Hashed
hashHex ByteString
payload
newHeaders :: RequestHeaders
newHeaders = Request -> Hashed -> UTCTime -> RequestHeaders
overrideReqHeaders Request
req Hashed
payloadHash UTCTime
time
request :: Request
request = Request
req
{ requestHeaders :: RequestHeaders
H.requestHeaders = RequestHeaders
headers RequestHeaders -> RequestHeaders -> RequestHeaders
forall a. Semigroup a => a -> a -> a
<> RequestHeaders
newHeaders
, queryString :: ByteString
H.queryString =
ByteString -> (Query -> ByteString) -> Maybe Query -> ByteString
forall b a. b -> (a -> b) -> Maybe a -> b
maybe ByteString
forall a. Monoid a => a
mempty (Bool -> Query -> ByteString
H.renderQuery Bool
True) Maybe Query
subresources
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString -> (Query -> ByteString) -> Maybe Query -> ByteString
forall b a. b -> (a -> b) -> Maybe a -> b
maybe ByteString
forall a. Monoid a => a
mempty (Bool -> Query -> ByteString
H.renderQuery Bool
False) Maybe Query
queryString
, requestBody :: RequestBody
H.requestBody = RequestBody
reqBody
}
canonicalRequest :: Canonicalized Request
canonicalRequest = Query -> Query -> Request -> Hashed -> Canonicalized Request
mkCanonicalized (Query -> Maybe Query -> Query
forall a. a -> Maybe a -> a
fromMaybe Query
forall a. Monoid a => a
mempty Maybe Query
subresources)
(Query -> Maybe Query -> Query
forall a. a -> Maybe a -> a
fromMaybe Query
forall a. Monoid a => a
mempty Maybe Query
queryString)
Request
request
Hashed
payloadHash
SpacesRequest -> m SpacesRequest
forall (m :: * -> *) a. Monad m => a -> m a
return
(SpacesRequest -> m SpacesRequest)
-> SpacesRequest -> m SpacesRequest
forall a b. (a -> b) -> a -> b
$ SpacesRequest :: Request
-> Spaces
-> RequestHeaders
-> Method
-> Hashed
-> Canonicalized Request
-> UTCTime
-> SpacesRequest
SpacesRequest
{ $sel:method:SpacesRequest :: Method
method = Method
reqMethod, $sel:headers:SpacesRequest :: RequestHeaders
headers = RequestHeaders
headers RequestHeaders -> RequestHeaders -> RequestHeaders
forall a. Semigroup a => a -> a -> a
<> RequestHeaders
newHeaders, UTCTime
Request
Hashed
Canonicalized Request
Spaces
$sel:time:SpacesRequest :: UTCTime
$sel:canonicalRequest:SpacesRequest :: Canonicalized Request
$sel:payloadHash:SpacesRequest :: Hashed
$sel:spaces:SpacesRequest :: Spaces
$sel:request:SpacesRequest :: Request
canonicalRequest :: Canonicalized Request
request :: Request
payloadHash :: Hashed
time :: UTCTime
spaces :: Spaces
.. }
where
reqMethod :: Method
reqMethod = Method -> Maybe Method -> Method
forall a. a -> Maybe a -> a
fromMaybe Method
GET Maybe Method
method
reqBody :: RequestBody
reqBody = RequestBody -> Maybe RequestBody -> RequestBody
forall a. a -> Maybe a -> a
fromMaybe (ByteString -> RequestBody
RequestBodyLBS ByteString
LB.empty) Maybe RequestBody
body
mkCanonicalized :: Query
-> Query
-> Request
-> Hashed
-> Canonicalized Request
mkCanonicalized :: Query -> Query -> Request -> Hashed -> Canonicalized Request
mkCanonicalized Query
subresources Query
query Request
request Hashed
payloadHash = ByteString -> Canonicalized Request
forall a. ByteString -> Canonicalized a
Canonicalized
(ByteString -> Canonicalized Request)
-> ByteString -> Canonicalized Request
forall a b. (a -> b) -> a -> b
$ ByteString -> [ByteString] -> ByteString
C.intercalate ByteString
"\n"
[ Request
request Request -> (Request -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& Request -> ByteString
H.method
, Request
request Request -> (Request -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& Request -> ByteString
H.path
, Query -> ByteString
renderSubresources Query
subresources
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Bool -> Query -> ByteString
H.renderQuery Bool
False Query
query
, Request
request
Request -> (Request -> RequestHeaders) -> RequestHeaders
forall a b. a -> (a -> b) -> b
& Request -> RequestHeaders
H.requestHeaders
RequestHeaders
-> (RequestHeaders -> Canonicalized RequestHeaders)
-> Canonicalized RequestHeaders
forall a b. a -> (a -> b) -> b
& RequestHeaders -> Canonicalized RequestHeaders
canonicalizeHeaders
Canonicalized RequestHeaders
-> (Canonicalized RequestHeaders -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& Canonicalized RequestHeaders -> ByteString
forall a. Canonicalized a -> ByteString
unCanonicalized
, Request
request Request -> (Request -> RequestHeaders) -> RequestHeaders
forall a b. a -> (a -> b) -> b
& Request -> RequestHeaders
H.requestHeaders RequestHeaders -> (RequestHeaders -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& RequestHeaders -> ByteString
joinHeaderNames
, Hashed -> ByteString
forall (a :: ComputedTag). Computed a -> ByteString
uncompute Hashed
payloadHash
]
renderSubresources :: Query -> ByteString
renderSubresources :: Query -> ByteString
renderSubresources = ByteString -> [ByteString] -> ByteString
C.intercalate ByteString
"&" ([ByteString] -> ByteString)
-> (Query -> [ByteString]) -> Query -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (QueryItem -> ByteString) -> Query -> [ByteString]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap QueryItem -> ByteString
renderQueryItem (Query -> [ByteString])
-> (Query -> Query) -> Query -> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query -> Query
forall a. Ord a => [a] -> [a]
sort
where
renderQueryItem :: QueryItem -> ByteString
renderQueryItem :: QueryItem -> ByteString
renderQueryItem (ByteString
k, Maybe ByteString
Nothing) = ByteString
k ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
"="
renderQueryItem (ByteString
k, Just ByteString
v) = ByteString
k ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
"=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
v
mkStringToSign :: SpacesRequest -> StringToSign
mkStringToSign :: SpacesRequest -> StringToSign
mkStringToSign req :: SpacesRequest
req@SpacesRequest { RequestHeaders
UTCTime
Request
Hashed
Canonicalized Request
Method
Spaces
time :: UTCTime
canonicalRequest :: Canonicalized Request
payloadHash :: Hashed
method :: Method
headers :: RequestHeaders
spaces :: Spaces
request :: Request
$sel:time:SpacesRequest :: SpacesRequest -> UTCTime
$sel:canonicalRequest:SpacesRequest :: SpacesRequest -> Canonicalized Request
$sel:payloadHash:SpacesRequest :: SpacesRequest -> Hashed
$sel:spaces:SpacesRequest :: SpacesRequest -> Spaces
$sel:request:SpacesRequest :: SpacesRequest -> Request
$sel:headers:SpacesRequest :: SpacesRequest -> RequestHeaders
$sel:method:SpacesRequest :: SpacesRequest -> Method
.. } = ByteString -> StringToSign
StringToSign
(ByteString -> StringToSign) -> ByteString -> StringToSign
forall a b. (a -> b) -> a -> b
$ ByteString -> [ByteString] -> ByteString
C.intercalate ByteString
"\n"
[ ByteString
"AWS4-HMAC-SHA256"
, UTCTime -> ByteString
fmtAmzTime UTCTime
time
, SpacesRequest -> Credentials
mkCredentials SpacesRequest
req Credentials -> (Credentials -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& Credentials -> ByteString
forall (a :: ComputedTag). Computed a -> ByteString
uncompute
, Canonicalized Request
canonicalRequest
Canonicalized Request
-> (Canonicalized Request -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& Canonicalized Request -> ByteString
coerce
ByteString -> (ByteString -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& ByteString -> ByteString
LB.fromStrict
ByteString -> (ByteString -> Hashed) -> Hashed
forall a b. a -> (a -> b) -> b
& ByteString -> Hashed
hashHex
Hashed -> (Hashed -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& Hashed -> ByteString
forall (a :: ComputedTag). Computed a -> ByteString
uncompute
]
mkSignature :: SpacesRequest -> StringToSign -> Signature
mkSignature :: SpacesRequest -> StringToSign -> Signature
mkSignature SpacesRequest { RequestHeaders
UTCTime
Request
Hashed
Canonicalized Request
Method
Spaces
time :: UTCTime
canonicalRequest :: Canonicalized Request
payloadHash :: Hashed
method :: Method
headers :: RequestHeaders
spaces :: Spaces
request :: Request
$sel:time:SpacesRequest :: SpacesRequest -> UTCTime
$sel:canonicalRequest:SpacesRequest :: SpacesRequest -> Canonicalized Request
$sel:payloadHash:SpacesRequest :: SpacesRequest -> Hashed
$sel:spaces:SpacesRequest :: SpacesRequest -> Spaces
$sel:request:SpacesRequest :: SpacesRequest -> Request
$sel:headers:SpacesRequest :: SpacesRequest -> RequestHeaders
$sel:method:SpacesRequest :: SpacesRequest -> Method
.. } StringToSign
str = ByteString -> Signature
Signature
(ByteString -> Signature)
-> (ByteString -> ByteString) -> ByteString -> Signature
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
B16.encode
(ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString -> ByteString
keyedHash (StringToSign -> ByteString
forall (a :: ComputedTag). Computed a -> ByteString
uncompute StringToSign
str)
(ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString -> ByteString
keyedHash ByteString
"aws4_request"
(ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString -> ByteString
keyedHash ByteString
"s3"
(ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString -> ByteString
keyedHash (Spaces
spaces Spaces -> Getting Region Spaces Region -> Region
forall s a. s -> Getting a s a -> a
^. forall s t a b. HasField "region" s t a b => Lens s t a b
forall (field :: Symbol) s t a b.
HasField field s t a b =>
Lens s t a b
field @"region" Region -> (Region -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& Region -> ByteString
forall a. IsString a => Region -> a
regionSlug)
(ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString -> ByteString
keyedHash (UTCTime -> ByteString
fmtAmzDate UTCTime
time)
(ByteString -> Signature) -> ByteString -> Signature
forall a b. (a -> b) -> a -> b
$ ByteString
"AWS4" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> (Spaces
spaces Spaces -> Getting SecretKey Spaces SecretKey -> SecretKey
forall s a. s -> Getting a s a -> a
^. forall s t a b. HasField "secretKey" s t a b => Lens s t a b
forall (field :: Symbol) s t a b.
HasField field s t a b =>
Lens s t a b
field @"secretKey" SecretKey -> (SecretKey -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& SecretKey -> ByteString
coerce)
mkAuthorization :: SpacesRequest -> StringToSign -> Authorization
mkAuthorization :: SpacesRequest -> StringToSign -> Authorization
mkAuthorization req :: SpacesRequest
req@SpacesRequest { RequestHeaders
UTCTime
Request
Hashed
Canonicalized Request
Method
Spaces
time :: UTCTime
canonicalRequest :: Canonicalized Request
payloadHash :: Hashed
method :: Method
headers :: RequestHeaders
spaces :: Spaces
request :: Request
$sel:time:SpacesRequest :: SpacesRequest -> UTCTime
$sel:canonicalRequest:SpacesRequest :: SpacesRequest -> Canonicalized Request
$sel:payloadHash:SpacesRequest :: SpacesRequest -> Hashed
$sel:spaces:SpacesRequest :: SpacesRequest -> Spaces
$sel:request:SpacesRequest :: SpacesRequest -> Request
$sel:headers:SpacesRequest :: SpacesRequest -> RequestHeaders
$sel:method:SpacesRequest :: SpacesRequest -> Method
.. } StringToSign
str = ByteString -> Authorization
Authorization
(ByteString -> Authorization) -> ByteString -> Authorization
forall a b. (a -> b) -> a -> b
$ [ByteString] -> ByteString
C.concat [ ByteString
"AWS4-HMAC-SHA256 Credential="
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Spaces
spaces Spaces -> Getting ByteString Spaces ByteString -> ByteString
forall s a. s -> Getting a s a -> a
^. forall s t a b. HasField "accessKey" s t a b => Lens s t a b
forall (field :: Symbol) s t a b.
HasField field s t a b =>
Lens s t a b
field @"accessKey" ((AccessKey -> Const ByteString AccessKey)
-> Spaces -> Const ByteString Spaces)
-> ((ByteString -> Const ByteString ByteString)
-> AccessKey -> Const ByteString AccessKey)
-> Getting ByteString Spaces ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s t a b. HasPosition 1 s t a b => Lens s t a b
forall (i :: Nat) s t a b. HasPosition i s t a b => Lens s t a b
position @1
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
"/"
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Credentials -> ByteString
forall (a :: ComputedTag). Computed a -> ByteString
uncompute Credentials
cred
, ByteString
", SignedHeaders=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> RequestHeaders -> ByteString
joinHeaderNames RequestHeaders
headers
, ByteString
", Signature=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Signature -> ByteString
forall (a :: ComputedTag). Computed a -> ByteString
uncompute Signature
sig
]
where
cred :: Credentials
cred = SpacesRequest -> Credentials
mkCredentials SpacesRequest
req
sig :: Signature
sig = SpacesRequest -> StringToSign -> Signature
mkSignature SpacesRequest
req StringToSign
str
mkCredentials :: SpacesRequest -> Credentials
mkCredentials :: SpacesRequest -> Credentials
mkCredentials SpacesRequest { RequestHeaders
UTCTime
Request
Hashed
Canonicalized Request
Method
Spaces
time :: UTCTime
canonicalRequest :: Canonicalized Request
payloadHash :: Hashed
method :: Method
headers :: RequestHeaders
spaces :: Spaces
request :: Request
$sel:time:SpacesRequest :: SpacesRequest -> UTCTime
$sel:canonicalRequest:SpacesRequest :: SpacesRequest -> Canonicalized Request
$sel:payloadHash:SpacesRequest :: SpacesRequest -> Hashed
$sel:spaces:SpacesRequest :: SpacesRequest -> Spaces
$sel:request:SpacesRequest :: SpacesRequest -> Request
$sel:headers:SpacesRequest :: SpacesRequest -> RequestHeaders
$sel:method:SpacesRequest :: SpacesRequest -> Method
.. } = ByteString -> Credentials
Credentials
(ByteString -> Credentials) -> ByteString -> Credentials
forall a b. (a -> b) -> a -> b
$ ByteString -> [ByteString] -> ByteString
C.intercalate ByteString
"/"
[ UTCTime -> ByteString
fmtAmzDate UTCTime
time
, Spaces
spaces Spaces -> Getting Region Spaces Region -> Region
forall s a. s -> Getting a s a -> a
^. forall s t a b. HasField "region" s t a b => Lens s t a b
forall (field :: Symbol) s t a b.
HasField field s t a b =>
Lens s t a b
field @"region" Region -> (Region -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& Region -> ByteString
forall a. IsString a => Region -> a
regionSlug
, ByteString
"s3"
, ByteString
"aws4_request"
]
overrideReqHeaders
:: Request
-> Hashed
-> UTCTime
-> [Header]
Request
req Hashed
hb UTCTime
time = (Request
req Request -> (Request -> RequestHeaders) -> RequestHeaders
forall a b. a -> (a -> b) -> b
& Request -> RequestHeaders
H.requestHeaders) RequestHeaders -> RequestHeaders -> RequestHeaders
forall a. Semigroup a => a -> a -> a
<> RequestHeaders
newHeaders
where
newHeaders :: RequestHeaders
newHeaders = [ (ByteString -> CI ByteString
forall s. FoldCase s => s -> CI s
CI.mk ByteString
"host", Request
req Request -> (Request -> ByteString) -> ByteString
forall a b. a -> (a -> b) -> b
& Request -> ByteString
H.host)
, (ByteString -> CI ByteString
forall s. FoldCase s => s -> CI s
CI.mk ByteString
"x-amz-content-sha256", Hashed -> ByteString
forall (a :: ComputedTag). Computed a -> ByteString
uncompute Hashed
hb)
, (ByteString -> CI ByteString
forall s. FoldCase s => s -> CI s
CI.mk ByteString
"x-amz-date", UTCTime -> ByteString
fmtAmzTime UTCTime
time)
]
canonicalizeHeaders :: [Header] -> Canonicalized [Header]
= ByteString -> Canonicalized RequestHeaders
forall a. ByteString -> Canonicalized a
Canonicalized
(ByteString -> Canonicalized RequestHeaders)
-> (RequestHeaders -> ByteString)
-> RequestHeaders
-> Canonicalized RequestHeaders
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> ByteString
C.unlines
([ByteString] -> ByteString)
-> (RequestHeaders -> [ByteString]) -> RequestHeaders -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((ByteString, ByteString) -> ByteString)
-> [(ByteString, ByteString)] -> [ByteString]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(ByteString
n, ByteString
v) -> ByteString
n ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
":" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
v)
([(ByteString, ByteString)] -> [ByteString])
-> (RequestHeaders -> [(ByteString, ByteString)])
-> RequestHeaders
-> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(ByteString, ByteString)] -> [(ByteString, ByteString)]
forall a. Ord a => [a] -> [a]
sort
([(ByteString, ByteString)] -> [(ByteString, ByteString)])
-> (RequestHeaders -> [(ByteString, ByteString)])
-> RequestHeaders
-> [(ByteString, ByteString)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((CI ByteString, ByteString) -> (ByteString, ByteString))
-> RequestHeaders -> [(ByteString, ByteString)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((CI ByteString -> ByteString)
-> (CI ByteString, ByteString) -> (ByteString, ByteString)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (ByteString -> ByteString
toLowerBS (ByteString -> ByteString)
-> (CI ByteString -> ByteString) -> CI ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CI ByteString -> ByteString
forall s. CI s -> s
CI.original))
joinHeaderNames :: [Header] -> ByteString
=
ByteString -> [ByteString] -> ByteString
C.intercalate ByteString
";" ([ByteString] -> ByteString)
-> (RequestHeaders -> [ByteString]) -> RequestHeaders -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> [ByteString]
forall a. Ord a => [a] -> [a]
sort ([ByteString] -> [ByteString])
-> (RequestHeaders -> [ByteString])
-> RequestHeaders
-> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((CI ByteString, ByteString) -> ByteString)
-> RequestHeaders -> [ByteString]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (ByteString -> ByteString
toLowerBS (ByteString -> ByteString)
-> ((CI ByteString, ByteString) -> ByteString)
-> (CI ByteString, ByteString)
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CI ByteString -> ByteString
forall s. CI s -> s
CI.original (CI ByteString -> ByteString)
-> ((CI ByteString, ByteString) -> CI ByteString)
-> (CI ByteString, ByteString)
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CI ByteString, ByteString) -> CI ByteString
forall a b. (a, b) -> a
fst)
fmtAmzDate :: UTCTime -> ByteString
fmtAmzDate :: UTCTime -> ByteString
fmtAmzDate = String -> ByteString
C.pack (String -> ByteString)
-> (UTCTime -> String) -> UTCTime -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TimeLocale -> String -> UTCTime -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%Y%m%d"
fmtAmzTime :: UTCTime -> ByteString
fmtAmzTime :: UTCTime -> ByteString
fmtAmzTime = String -> ByteString
C.pack (String -> ByteString)
-> (UTCTime -> String) -> UTCTime -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TimeLocale -> String -> UTCTime -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%Y%m%dT%H%M%SZ"
keyedHash :: ByteString -> ByteString -> ByteString
keyedHash :: ByteString -> ByteString -> ByteString
keyedHash ByteString
bs ByteString
k = HMAC SHA256 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
convert (HMAC SHA256 -> ByteString) -> HMAC SHA256 -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString -> HMAC SHA256
forall key message a.
(ByteArrayAccess key, ByteArrayAccess message, HashAlgorithm a) =>
key -> message -> HMAC a
hmac @_ @_ @SHA256 ByteString
k ByteString
bs
hashHex :: LB.ByteString -> Hashed
hashHex :: ByteString -> Hashed
hashHex = ByteString -> Hashed
Hashed (ByteString -> Hashed)
-> (ByteString -> ByteString) -> ByteString -> Hashed
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
B16.encode (ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Digest SHA256 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
convert (Digest SHA256 -> ByteString)
-> (ByteString -> Digest SHA256) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashAlgorithm SHA256 => ByteString -> Digest SHA256
forall a. HashAlgorithm a => ByteString -> Digest a
hashlazy @SHA256