-- |
-- Module      : Amazonka.Sign.V2
-- Copyright   : (c) 2013-2021 Brendan Hay
-- License     : Mozilla Public License, v. 2.0.
-- Maintainer  : Brendan Hay <brendan.g.hay+amazonka@gmail.com>
-- Stability   : provisional
-- Portability : non-portable (GHC extensions)
module Amazonka.Sign.V2
  ( v2,
  )
where

import qualified Amazonka.Bytes as Bytes
import qualified Amazonka.Crypto as Crypto
import Amazonka.Data
import Amazonka.Prelude
import Amazonka.Types
import qualified Data.ByteString.Char8 as BS8
import qualified Network.HTTP.Client as Client
import qualified Network.HTTP.Types as HTTP
import qualified Network.HTTP.Types.URI as URI

data V2 = V2
  { V2 -> UTCTime
metaTime :: UTCTime,
    V2 -> Endpoint
metaEndpoint :: Endpoint,
    V2 -> ByteString
metaSignature :: ByteString
  }

instance ToLog V2 where
  build :: V2 -> ByteStringBuilder
build V2 {ByteString
UTCTime
Endpoint
metaSignature :: ByteString
metaEndpoint :: Endpoint
metaTime :: UTCTime
$sel:metaSignature:V2 :: V2 -> ByteString
$sel:metaEndpoint:V2 :: V2 -> Endpoint
$sel:metaTime:V2 :: V2 -> UTCTime
..} =
    [ByteStringBuilder] -> ByteStringBuilder
buildLines
      [ ByteStringBuilder
"[Version 2 Metadata] {",
        ByteStringBuilder
"  time      = " ByteStringBuilder -> ByteStringBuilder -> ByteStringBuilder
forall a. Semigroup a => a -> a -> a
<> UTCTime -> ByteStringBuilder
forall a. ToLog a => a -> ByteStringBuilder
build UTCTime
metaTime,
        ByteStringBuilder
"  endpoint  = " ByteStringBuilder -> ByteStringBuilder -> ByteStringBuilder
forall a. Semigroup a => a -> a -> a
<> ByteString -> ByteStringBuilder
forall a. ToLog a => a -> ByteStringBuilder
build (Endpoint -> ByteString
_endpointHost Endpoint
metaEndpoint),
        ByteStringBuilder
"  signature = " ByteStringBuilder -> ByteStringBuilder -> ByteStringBuilder
forall a. Semigroup a => a -> a -> a
<> ByteString -> ByteStringBuilder
forall a. ToLog a => a -> ByteStringBuilder
build ByteString
metaSignature,
        ByteStringBuilder
"}"
      ]

v2 :: Signer
v2 :: Signer
v2 = (forall a. Algorithm a)
-> (forall a. Seconds -> Algorithm a) -> Signer
Signer forall a. Algorithm a
sign (Algorithm a -> Seconds -> Algorithm a
forall a b. a -> b -> a
const Algorithm a
forall a. Algorithm a
sign) -- FIXME: revisit v2 presigning.

sign :: Algorithm a
sign :: Algorithm a
sign Request {[Header]
StdMethod
QueryString
RawPath
RequestBody
Service
$sel:_requestBody:Request :: forall a. Request a -> RequestBody
$sel:_requestHeaders:Request :: forall a. Request a -> [Header]
$sel:_requestQuery:Request :: forall a. Request a -> QueryString
$sel:_requestPath:Request :: forall a. Request a -> RawPath
$sel:_requestMethod:Request :: forall a. Request a -> StdMethod
$sel:_requestService:Request :: forall a. Request a -> Service
_requestBody :: RequestBody
_requestHeaders :: [Header]
_requestQuery :: QueryString
_requestPath :: RawPath
_requestMethod :: StdMethod
_requestService :: Service
..} AuthEnv {Maybe ISO8601
Maybe (Sensitive SessionToken)
Sensitive SecretKey
AccessKey
$sel:_authExpiration:AuthEnv :: AuthEnv -> Maybe ISO8601
$sel:_authSessionToken:AuthEnv :: AuthEnv -> Maybe (Sensitive SessionToken)
$sel:_authSecretAccessKey:AuthEnv :: AuthEnv -> Sensitive SecretKey
$sel:_authAccessKeyId:AuthEnv :: AuthEnv -> AccessKey
_authExpiration :: Maybe ISO8601
_authSessionToken :: Maybe (Sensitive SessionToken)
_authSecretAccessKey :: Sensitive SecretKey
_authAccessKeyId :: AccessKey
..} Region
r UTCTime
t = Meta -> ClientRequest -> Signed a
forall a. Meta -> ClientRequest -> Signed a
Signed Meta
meta ClientRequest
rq
  where
    meta :: Meta
meta = V2 -> Meta
forall a. ToLog a => a -> Meta
Meta (UTCTime -> Endpoint -> ByteString -> V2
V2 UTCTime
t Endpoint
end ByteString
signature)

    rq :: ClientRequest
rq =
      (Endpoint -> Maybe Seconds -> ClientRequest
newClientRequest Endpoint
end Maybe Seconds
_serviceTimeout)
        { method :: ByteString
Client.method = ByteString
meth,
          path :: ByteString
Client.path = ByteString
path',
          queryString :: ByteString
Client.queryString = QueryString -> ByteString
forall a. ToByteString a => a -> ByteString
toBS QueryString
authorised,
          requestHeaders :: [Header]
Client.requestHeaders = [Header]
headers,
          requestBody :: RequestBody
Client.requestBody = RequestBody -> RequestBody
toRequestBody RequestBody
_requestBody
        }

    meth :: ByteString
meth = StdMethod -> ByteString
forall a. ToByteString a => a -> ByteString
toBS StdMethod
_requestMethod
    path' :: ByteString
path' = EscapedPath -> ByteString
forall a. ToByteString a => a -> ByteString
toBS (RawPath -> EscapedPath
forall (a :: Encoding). Path a -> EscapedPath
escapePath RawPath
_requestPath)

    end :: Endpoint
end@Endpoint {Bool
Int
ByteString
$sel:_endpointScope:Endpoint :: Endpoint -> ByteString
$sel:_endpointPort:Endpoint :: Endpoint -> Int
$sel:_endpointSecure:Endpoint :: Endpoint -> Bool
_endpointScope :: ByteString
_endpointPort :: Int
_endpointSecure :: Bool
_endpointHost :: ByteString
$sel:_endpointHost:Endpoint :: Endpoint -> ByteString
..} = Region -> Endpoint
_serviceEndpoint Region
r

    Service {Maybe Seconds
ByteString
Signer
Retry
Abbrev
Status -> Bool
Status -> [Header] -> ByteStringLazy -> Error
Region -> Endpoint
$sel:_serviceRetry:Service :: Service -> Retry
$sel:_serviceError:Service :: Service -> Status -> [Header] -> ByteStringLazy -> Error
$sel:_serviceCheck:Service :: Service -> Status -> Bool
$sel:_serviceTimeout:Service :: Service -> Maybe Seconds
$sel:_serviceEndpoint:Service :: Service -> Region -> Endpoint
$sel:_serviceEndpointPrefix:Service :: Service -> ByteString
$sel:_serviceVersion:Service :: Service -> ByteString
$sel:_serviceSigningName:Service :: Service -> ByteString
$sel:_serviceSigner:Service :: Service -> Signer
$sel:_serviceAbbrev:Service :: Service -> Abbrev
_serviceRetry :: Retry
_serviceError :: Status -> [Header] -> ByteStringLazy -> Error
_serviceCheck :: Status -> Bool
_serviceEndpointPrefix :: ByteString
_serviceVersion :: ByteString
_serviceSigningName :: ByteString
_serviceSigner :: Signer
_serviceAbbrev :: Abbrev
_serviceEndpoint :: Region -> Endpoint
_serviceTimeout :: Maybe Seconds
..} = Service
_requestService

    authorised :: QueryString
authorised = ByteString -> ByteString -> QueryString -> QueryString
forall a.
ToQuery a =>
ByteString -> a -> QueryString -> QueryString
pair ByteString
"Signature" (Bool -> ByteString -> ByteString
URI.urlEncode Bool
True ByteString
signature) QueryString
query

    signature :: ByteString
signature =
      HMAC SHA256 -> ByteString
forall a. ByteArrayAccess a => a -> ByteString
Bytes.encodeBase64
        (HMAC SHA256 -> ByteString)
-> (ByteString -> HMAC SHA256) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString -> HMAC SHA256
forall a. ByteArrayAccess a => ByteString -> a -> HMAC SHA256
Crypto.hmacSHA256 (Sensitive SecretKey -> ByteString
forall a. ToByteString a => a -> ByteString
toBS Sensitive SecretKey
_authSecretAccessKey)
        (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> [ByteString] -> ByteString
BS8.intercalate
          ByteString
"\n"
          [ ByteString
meth,
            ByteString
_endpointHost,
            ByteString
path',
            QueryString -> ByteString
forall a. ToByteString a => a -> ByteString
toBS QueryString
query
          ]

    query :: QueryString
query =
      ByteString -> ByteString -> QueryString -> QueryString
forall a.
ToQuery a =>
ByteString -> a -> QueryString -> QueryString
pair ByteString
"Version" ByteString
_serviceVersion
        (QueryString -> QueryString)
-> (QueryString -> QueryString) -> QueryString -> QueryString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString -> QueryString -> QueryString
forall a.
ToQuery a =>
ByteString -> a -> QueryString -> QueryString
pair ByteString
"SignatureVersion" (ByteString
"2" :: ByteString)
        (QueryString -> QueryString)
-> (QueryString -> QueryString) -> QueryString -> QueryString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString -> QueryString -> QueryString
forall a.
ToQuery a =>
ByteString -> a -> QueryString -> QueryString
pair ByteString
"SignatureMethod" (ByteString
"HmacSHA256" :: ByteString)
        (QueryString -> QueryString)
-> (QueryString -> QueryString) -> QueryString -> QueryString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString -> QueryString -> QueryString
forall a.
ToQuery a =>
ByteString -> a -> QueryString -> QueryString
pair ByteString
"Timestamp" ByteString
time
        (QueryString -> QueryString)
-> (QueryString -> QueryString) -> QueryString -> QueryString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString -> QueryString -> QueryString
forall a.
ToQuery a =>
ByteString -> a -> QueryString -> QueryString
pair ByteString
"AWSAccessKeyId" (AccessKey -> ByteString
forall a. ToByteString a => a -> ByteString
toBS AccessKey
_authAccessKeyId)
        (QueryString -> QueryString) -> QueryString -> QueryString
forall a b. (a -> b) -> a -> b
$ QueryString
_requestQuery QueryString -> QueryString -> QueryString
forall a. Semigroup a => a -> a -> a
<> QueryString
-> ((ByteString, ByteString) -> QueryString)
-> Maybe (ByteString, ByteString)
-> QueryString
forall b a. b -> (a -> b) -> Maybe a -> b
maybe QueryString
forall a. Monoid a => a
mempty (ByteString, ByteString) -> QueryString
forall a. ToQuery a => a -> QueryString
toQuery Maybe (ByteString, ByteString)
token

    token :: Maybe (ByteString, ByteString)
token = (ByteString
"SecurityToken" :: ByteString,) (ByteString -> (ByteString, ByteString))
-> (Sensitive SessionToken -> ByteString)
-> Sensitive SessionToken
-> (ByteString, ByteString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Sensitive SessionToken -> ByteString
forall a. ToByteString a => a -> ByteString
toBS (Sensitive SessionToken -> (ByteString, ByteString))
-> Maybe (Sensitive SessionToken) -> Maybe (ByteString, ByteString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe (Sensitive SessionToken)
_authSessionToken

    headers :: [Header]
headers = HeaderName -> ByteString -> [Header] -> [Header]
hdr HeaderName
HTTP.hDate ByteString
time [Header]
_requestHeaders

    time :: ByteString
time = ISO8601 -> ByteString
forall a. ToByteString a => a -> ByteString
toBS (UTCTime -> ISO8601
forall (a :: Format). UTCTime -> Time a
Time UTCTime
t :: ISO8601)