-- |
-- Module      : Amazonka.Auth.Keys
-- Copyright   : (c) 2013-2023 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)
--
-- Authentication via directly-provided access keys, including
-- optional session token and environment variable lookup.
module Amazonka.Auth.Keys where

import Amazonka.Auth.Exception (_MissingEnvError)
import Amazonka.Core.Lens.Internal (throwingM)
import Amazonka.Data
import Amazonka.Env (Env, Env' (..))
import Amazonka.Prelude
import Amazonka.Types
import Control.Monad.Trans.Maybe (MaybeT (..))
import qualified Data.ByteString.Char8 as BS8
import Data.Foldable (asum)
import qualified System.Environment as Environment

-- | Explicit access and secret keys.
fromKeys :: AccessKey -> SecretKey -> Env' withAuth -> Env
fromKeys :: forall (withAuth :: * -> *).
AccessKey -> SecretKey -> Env' withAuth -> Env
fromKeys AccessKey
a SecretKey
s Env' withAuth
env =
  Env' withAuth
env {auth = Identity . Auth $ AuthEnv a (Sensitive s) Nothing Nothing}

-- | Temporary credentials from a STS session consisting of
-- the access key, secret key, and session token.
--
-- /See:/ 'fromTemporarySession'
fromSession ::
  AccessKey -> SecretKey -> SessionToken -> Env' withAuth -> Env
fromSession :: forall (withAuth :: * -> *).
AccessKey -> SecretKey -> SessionToken -> Env' withAuth -> Env
fromSession AccessKey
a SecretKey
s SessionToken
t Env' withAuth
env =
  Env' withAuth
env
    { auth =
        Identity . Auth $
          AuthEnv a (Sensitive s) (Just (Sensitive t)) Nothing
    }

-- | Temporary credentials from a STS session consisting of
-- the access key, secret key, session token, and expiration time.
--
-- /See:/ 'fromSession'
fromTemporarySession ::
  AccessKey ->
  SecretKey ->
  SessionToken ->
  UTCTime ->
  Env' withAuth ->
  Env
fromTemporarySession :: forall (withAuth :: * -> *).
AccessKey
-> SecretKey -> SessionToken -> UTCTime -> Env' withAuth -> Env
fromTemporarySession AccessKey
a SecretKey
s SessionToken
t UTCTime
e Env' withAuth
env =
  Env' withAuth
env
    { auth =
        Identity . Auth $
          AuthEnv a (Sensitive s) (Just (Sensitive t)) (Just (Time e))
    }

-- | Retrieve access key, secret key and a session token from
-- environment variables. We copy the behaviour of the SDKs and
-- respect the following variables:
--
-- * @AWS_ACCESS_KEY_ID@ (and its alternate name, @AWS_ACCESS_KEY@)
-- * @AWS_SECRET_ACCESS_KEY@ (and its alternate name, @AWS_SECRET_KEY@)
-- * @AWS_SESSION_TOKEN@ (if present)
--
-- Throws 'MissingEnvError' if a required environment variable is
-- empty or unset.
fromKeysEnv :: MonadIO m => Env' withAuth -> m Env
fromKeysEnv :: forall (m :: * -> *) (withAuth :: * -> *).
MonadIO m =>
Env' withAuth -> m Env
fromKeysEnv Env' withAuth
env = IO Env -> m Env
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Env -> m Env) -> IO Env -> m Env
forall a b. (a -> b) -> a -> b
$ do
  Auth
keys <- AuthEnv -> Auth
Auth (AuthEnv -> Auth) -> IO AuthEnv -> IO Auth
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO AuthEnv
lookupKeys
  Env -> IO Env
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Env -> IO Env) -> Env -> IO Env
forall a b. (a -> b) -> a -> b
$ Env' withAuth
env {auth = Identity keys}
  where
    lookupKeys :: IO AuthEnv
lookupKeys =
      AccessKey
-> Sensitive SecretKey
-> Maybe (Sensitive SessionToken)
-> Maybe ISO8601
-> AuthEnv
AuthEnv
        (AccessKey
 -> Sensitive SecretKey
 -> Maybe (Sensitive SessionToken)
 -> Maybe ISO8601
 -> AuthEnv)
-> IO AccessKey
-> IO
     (Sensitive SecretKey
      -> Maybe (Sensitive SessionToken) -> Maybe ISO8601 -> AuthEnv)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO AccessKey
lookupAccessKey
        IO
  (Sensitive SecretKey
   -> Maybe (Sensitive SessionToken) -> Maybe ISO8601 -> AuthEnv)
-> IO (Sensitive SecretKey)
-> IO (Maybe (Sensitive SessionToken) -> Maybe ISO8601 -> AuthEnv)
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IO (Sensitive SecretKey)
lookupSecretKey
        IO (Maybe (Sensitive SessionToken) -> Maybe ISO8601 -> AuthEnv)
-> IO (Maybe (Sensitive SessionToken))
-> IO (Maybe ISO8601 -> AuthEnv)
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IO (Maybe (Sensitive SessionToken))
lookupSessionToken
        IO (Maybe ISO8601 -> AuthEnv) -> IO (Maybe ISO8601) -> IO AuthEnv
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Maybe ISO8601 -> IO (Maybe ISO8601)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe ISO8601
forall a. Maybe a
Nothing

    lookupAccessKey :: IO AccessKey
    lookupAccessKey :: IO AccessKey
lookupAccessKey = do
      Maybe String
mVal <-
        MaybeT IO String -> IO (Maybe String)
forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT (MaybeT IO String -> IO (Maybe String))
-> MaybeT IO String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$
          [MaybeT IO String] -> MaybeT IO String
forall (t :: * -> *) (f :: * -> *) a.
(Foldable t, Alternative f) =>
t (f a) -> f a
asum
            [ IO (Maybe String) -> MaybeT IO String
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT (String -> IO (Maybe String)
nonEmptyEnv String
"AWS_ACCESS_KEY_ID"),
              IO (Maybe String) -> MaybeT IO String
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT (String -> IO (Maybe String)
nonEmptyEnv String
"AWS_ACCESS_KEY")
            ]
      case Maybe String
mVal of
        Maybe String
Nothing ->
          AReview SomeException Text -> Text -> IO AccessKey
forall (m :: * -> *) b r.
MonadThrow m =>
AReview SomeException b -> b -> m r
throwingM
            AReview SomeException Text
forall a. AsAuthError a => Prism' a Text
Prism' SomeException Text
_MissingEnvError
            Text
"Unable to read access key from AWS_ACCESS_KEY_ID (or AWS_ACCESS_KEY)"
        Just String
v -> AccessKey -> IO AccessKey
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (AccessKey -> IO AccessKey)
-> (ByteString -> AccessKey) -> ByteString -> IO AccessKey
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> AccessKey
AccessKey (ByteString -> IO AccessKey) -> ByteString -> IO AccessKey
forall a b. (a -> b) -> a -> b
$ String -> ByteString
BS8.pack String
v

    lookupSecretKey :: IO (Sensitive SecretKey)
    lookupSecretKey :: IO (Sensitive SecretKey)
lookupSecretKey = do
      Maybe String
mVal <-
        MaybeT IO String -> IO (Maybe String)
forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT (MaybeT IO String -> IO (Maybe String))
-> MaybeT IO String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$
          [MaybeT IO String] -> MaybeT IO String
forall (t :: * -> *) (f :: * -> *) a.
(Foldable t, Alternative f) =>
t (f a) -> f a
asum
            [ IO (Maybe String) -> MaybeT IO String
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT (String -> IO (Maybe String)
nonEmptyEnv String
"AWS_SECRET_ACCESS_KEY"),
              IO (Maybe String) -> MaybeT IO String
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT (String -> IO (Maybe String)
nonEmptyEnv String
"AWS_SECRET_KEY")
            ]
      case Maybe String
mVal of
        Maybe String
Nothing ->
          AReview SomeException Text -> Text -> IO (Sensitive SecretKey)
forall (m :: * -> *) b r.
MonadThrow m =>
AReview SomeException b -> b -> m r
throwingM
            AReview SomeException Text
forall a. AsAuthError a => Prism' a Text
Prism' SomeException Text
_MissingEnvError
            Text
"Unable to read secret key from AWS_SECRET_ACCESS_KEY (or AWS_SECRET_KEY)"
        Just String
v -> Sensitive SecretKey -> IO (Sensitive SecretKey)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Sensitive SecretKey -> IO (Sensitive SecretKey))
-> (ByteString -> Sensitive SecretKey)
-> ByteString
-> IO (Sensitive SecretKey)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SecretKey -> Sensitive SecretKey
forall a. a -> Sensitive a
Sensitive (SecretKey -> Sensitive SecretKey)
-> (ByteString -> SecretKey) -> ByteString -> Sensitive SecretKey
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> SecretKey
SecretKey (ByteString -> IO (Sensitive SecretKey))
-> ByteString -> IO (Sensitive SecretKey)
forall a b. (a -> b) -> a -> b
$ String -> ByteString
BS8.pack String
v

    lookupSessionToken :: IO (Maybe (Sensitive SessionToken))
    lookupSessionToken :: IO (Maybe (Sensitive SessionToken))
lookupSessionToken =
      String -> IO (Maybe String)
nonEmptyEnv String
"AWS_SESSION_TOKEN"
        IO (Maybe String)
-> (Maybe String -> Maybe (Sensitive SessionToken))
-> IO (Maybe (Sensitive SessionToken))
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> (String -> Sensitive SessionToken)
-> Maybe String -> Maybe (Sensitive SessionToken)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (SessionToken -> Sensitive SessionToken
forall a. a -> Sensitive a
Sensitive (SessionToken -> Sensitive SessionToken)
-> (String -> SessionToken) -> String -> Sensitive SessionToken
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> SessionToken
SessionToken (ByteString -> SessionToken)
-> (String -> ByteString) -> String -> SessionToken
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ByteString
BS8.pack)

    nonEmptyEnv :: String -> IO (Maybe String)
    nonEmptyEnv :: String -> IO (Maybe String)
nonEmptyEnv String
var =
      String -> IO (Maybe String)
Environment.lookupEnv String
var IO (Maybe String)
-> (Maybe String -> Maybe String) -> IO (Maybe String)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \case
        Maybe String
Nothing -> Maybe String
forall a. Maybe a
Nothing
        Just String
"" -> Maybe String
forall a. Maybe a
Nothing
        Just String
v -> String -> Maybe String
forall a. a -> Maybe a
Just String
v