module Configuration.Utils.Aws.Credentials
(
CredentialConfigKey(..)
, credentialConfigKeyId
, credentialConfigKeySecret
, validateCredentialConfigKey
, CredentialConfigFile(..)
, credentialConfigFileName
, credentialConfigKeyName
, validateCredentialConfigFile
, CredentialConfig(..)
, credentialConfigKey
, credentialConfigFile
, credentialConfigEnvironment
, credentialConfigInstanceMetadata
, defaultCredentialConfig
, pCredentialConfig
, pCredentialConfig_
, validateCredentialConfig
, credentialsFromConfig
, LoadCredentialsException(..)
) where
import Aws
import Configuration.Utils
import Configuration.Utils.Internal
import Configuration.Utils.Validation
import Control.Exception
import Control.Monad
import Control.Monad.Error.Class
import Control.Monad.IO.Class
import Control.Monad.Unicode
import Data.Maybe
import Data.Monoid.Unicode
import Data.Traversable
import Data.Typeable
import qualified Data.ByteString.Char8 as B8
import qualified Data.Text as T
import Prelude.Unicode
data CredentialConfigKey
= CredentialConfigKey
{ _credentialConfigKeyId ∷ !B8.ByteString
, _credentialConfigKeySecret ∷ !B8.ByteString
} deriving (Show, Read, Eq, Ord, Typeable)
credentialConfigKeyId ∷ Lens' CredentialConfigKey B8.ByteString
credentialConfigKeyId = lens _credentialConfigKeyId $ \a b → a { _credentialConfigKeyId = b }
credentialConfigKeySecret ∷ Lens' CredentialConfigKey B8.ByteString
credentialConfigKeySecret = lens _credentialConfigKeySecret $ \a b → a { _credentialConfigKeySecret = b }
validateCredentialConfigKey ∷ ConfigValidation CredentialConfigKey λ
validateCredentialConfigKey CredentialConfigKey{..} = do
validateNonEmpty "access_key_id" _credentialConfigKeyId
validateNonEmpty "access_key_secret" _credentialConfigKeySecret
instance ToJSON CredentialConfigKey where
toJSON CredentialConfigKey{..} = object
[ "access_key_id" .= B8.unpack _credentialConfigKeyId
, "access_key_secret" .= B8.unpack _credentialConfigKeySecret
]
instance FromJSON CredentialConfigKey where
parseJSON =
withObject "CredentialConfigKey" $ \o →
CredentialConfigKey
<$> (B8.pack <$> o .: "access_key_id")
<*> (B8.pack <$> o .: "access_key_secret")
data CredentialConfigFile
= CredentialConfigFile
{ _credentialConfigFileName ∷ !FilePath
, _credentialConfigKeyName ∷ !T.Text
} deriving (Show, Read, Eq, Ord, Typeable)
credentialConfigFileName ∷ Lens' CredentialConfigFile FilePath
credentialConfigFileName = lens _credentialConfigFileName $ \a b → a { _credentialConfigFileName = b }
credentialConfigKeyName ∷ Lens' CredentialConfigFile T.Text
credentialConfigKeyName = lens _credentialConfigKeyName $ \a b → a { _credentialConfigKeyName = b }
validateCredentialConfigFile ∷ ConfigValidation CredentialConfigFile λ
validateCredentialConfigFile CredentialConfigFile{..} =
validateFileReadable "file_name" _credentialConfigFileName
instance ToJSON CredentialConfigFile where
toJSON CredentialConfigFile{..} = object
[ "file_name" .= _credentialConfigFileName
, "key_name" .= _credentialConfigKeyName
]
instance FromJSON CredentialConfigFile where
parseJSON =
withObject "CredentialConfigFile" $ \o →
CredentialConfigFile
<$> o .: "file_name"
<*> o .:? "key_name" .!= "default"
data CredentialConfig
= CredentialConfig
{ _credentialConfigKey ∷ !(Maybe CredentialConfigKey)
, _credentialConfigFile ∷ !(Maybe CredentialConfigFile)
, _credentialConfigEnvironment ∷ !Bool
, _credentialConfigInstanceMetadata ∷ !Bool
} deriving (Show, Eq, Ord)
credentialConfigKey ∷ Lens' CredentialConfig (Maybe CredentialConfigKey)
credentialConfigKey = lens _credentialConfigKey $ \a b → a { _credentialConfigKey = b}
credentialConfigFile ∷ Lens' CredentialConfig (Maybe CredentialConfigFile)
credentialConfigFile = lens _credentialConfigFile $ \a b → a { _credentialConfigFile = b}
credentialConfigEnvironment ∷ Lens' CredentialConfig Bool
credentialConfigEnvironment = lens _credentialConfigEnvironment $ \a b → a { _credentialConfigEnvironment = b}
credentialConfigInstanceMetadata ∷ Lens' CredentialConfig Bool
credentialConfigInstanceMetadata = lens _credentialConfigInstanceMetadata $ \a b → a { _credentialConfigInstanceMetadata = b}
defaultCredentialConfig ∷ CredentialConfig
defaultCredentialConfig = CredentialConfig
{ _credentialConfigKey = Nothing
, _credentialConfigFile = Nothing
, _credentialConfigEnvironment = False
, _credentialConfigInstanceMetadata = False
}
validateCredentialConfig ∷ ConfigValidation CredentialConfig λ
validateCredentialConfig CredentialConfig{..} = do
void $ for _credentialConfigKey validateCredentialConfigKey
void $ for _credentialConfigFile validateCredentialConfigFile
unless (or enabledList) $
throwError "at least one credentials configuration option must be configured"
where
enabledList =
[ isJust _credentialConfigKey
, isJust _credentialConfigFile
, _credentialConfigEnvironment
, _credentialConfigInstanceMetadata
]
instance ToJSON CredentialConfig where
toJSON CredentialConfig{..} = object
[ "key" .= _credentialConfigKey
, "file" .= _credentialConfigFile
, "environment" .= _credentialConfigEnvironment
, "metadata" .= _credentialConfigInstanceMetadata
]
instance FromJSON (CredentialConfig → CredentialConfig) where
parseJSON =
withObject "CredentialConfig" $ \o → id
<$< credentialConfigKey ..: "key" % o
<*< credentialConfigFile ..: "file" % o
<*< credentialConfigEnvironment ..: "environment" % o
<*< credentialConfigInstanceMetadata ..: "metadata" % o
pCredentialConfig
∷ String
→ MParser CredentialConfig
pCredentialConfig prefix = id
<$< credentialConfigKey .:: fmap Just % pCredentialConfigKey
<*< credentialConfigFile .:: fmap Just % pCredentialConfigFile
<*< credentialConfigEnvironment .:: boolOption
% long (prefix ⊕ "credentials-from-environment")
⊕ help "load AWS access credentials from environment variables (AWS_ACCESS_KEY_ID and AWS_ACCESS_KEY_SECRET)"
<*< credentialConfigInstanceMetadata .:: boolOption
% long (prefix ⊕ "credentials-from-metadata")
⊕ help "load AWS access credentials from the instance metadata"
where
pKeyId =
B8.pack <$> strOption
% long (prefix ⊕ "credentials-key-id")
⊕ metavar "AWS_ACCESS_KEY_ID"
⊕ help "Id of the AWS access key"
pKeySecret =
B8.pack <$> strOption
% long (prefix ⊕ "credentials-key-secret")
⊕ metavar "AWS_ACCESS_KEY_SECRET"
⊕ help "Secret of the AWS access key"
pCredentialConfigKey =
CredentialConfigKey <$> pKeyId <*> pKeySecret
pKeyName =
T.pack <$> strOption
% long (prefix ⊕ "credentials-key-name")
⊕ metavar "STRING"
⊕ help "the name of the access key in the access key file"
⊕ value (T.unpack credentialsDefaultKey)
pFileName =
fileOption
% long (prefix ⊕ "credentials-key-file")
⊕ help "the name of the file with access keys for the AWS API"
pCredentialConfigFile =
CredentialConfigFile <$> pFileName <*> pKeyName
pCredentialConfig_ ∷ MParser CredentialConfig
pCredentialConfig_ = pCredentialConfig ""
data LoadCredentialsException
= LoadCredentialsFromFileFailed CredentialConfigFile
| LoadCredentialsFromEnvironementFailed
| LoadCredentialsFromInstanceMetadataFailed
| NoCredentialLoadingMethodSpecified
deriving (Eq, Show, Typeable)
instance Exception LoadCredentialsException
credentialsFromConfig
∷ (MonadError LoadCredentialsException m, MonadIO m)
⇒ CredentialConfig
→ m Credentials
credentialsFromConfig CredentialConfig{..}
| Just CredentialConfigKey{..} ← _credentialConfigKey =
liftIO $ makeCredentials _credentialConfigKeyId _credentialConfigKeySecret
| Just ccf@(CredentialConfigFile file awsKeyName) ← _credentialConfigFile =
liftIO (loadCredentialsFromFile file awsKeyName) ≫= \case
Nothing → throwError $ LoadCredentialsFromFileFailed ccf
Just c → return c
| _credentialConfigEnvironment =
liftIO loadCredentialsFromEnv ≫= \case
Nothing → throwError LoadCredentialsFromEnvironementFailed
Just c → return c
| _credentialConfigInstanceMetadata =
liftIO loadCredentialsFromInstanceMetadata ≫= \case
Nothing → throwError LoadCredentialsFromInstanceMetadataFailed
Just c → return c
| otherwise = throwError NoCredentialLoadingMethodSpecified