{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards       #-}
module Aws.Iam.Core
    ( iamSignQuery
    , iamResponseConsumer
    , IamMetadata(..)
    , IamConfiguration(..)
    , IamError(..)

    , parseDateTime

    , AccessKeyStatus(..)
    , User(..)
    , parseUser
    , Group(..)
    , parseGroup
    , MfaDevice(..)
    , parseMfaDevice
    ) where

import           Aws.Core
import qualified Blaze.ByteString.Builder       as Blaze
import qualified Blaze.ByteString.Builder.Char8 as Blaze8
import           Control.Exception              (Exception)
import           Control.Monad
import           Control.Monad.Trans.Resource   (MonadThrow, throwM)
import           Data.ByteString                (ByteString)
import           Data.IORef
import           Data.List                      (intersperse, sort)
import           Data.Maybe
import           Data.Monoid                    ()
import qualified Data.Semigroup                 as Sem
import           Data.Text                      (Text)
import qualified Data.Text                      as Text
import           Data.Time
import           Data.Typeable
import qualified Network.HTTP.Conduit           as HTTP
import qualified Network.HTTP.Types             as HTTP
#if !MIN_VERSION_time(1,5,0)
import           System.Locale
#endif
import           Text.XML.Cursor                (($//))
import qualified Text.XML.Cursor                as Cu

data IamError
    = IamError {
        IamError -> Status
iamStatusCode   :: HTTP.Status
      , IamError -> Text
iamErrorCode    :: Text
      , IamError -> Text
iamErrorMessage :: Text
      }
    deriving (Int -> IamError -> ShowS
[IamError] -> ShowS
IamError -> String
(Int -> IamError -> ShowS)
-> (IamError -> String) -> ([IamError] -> ShowS) -> Show IamError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [IamError] -> ShowS
$cshowList :: [IamError] -> ShowS
show :: IamError -> String
$cshow :: IamError -> String
showsPrec :: Int -> IamError -> ShowS
$cshowsPrec :: Int -> IamError -> ShowS
Show, Typeable)

instance Exception IamError

data IamMetadata
    = IamMetadata {
        IamMetadata -> Maybe Text
requestId :: Maybe Text
      }
    deriving (Int -> IamMetadata -> ShowS
[IamMetadata] -> ShowS
IamMetadata -> String
(Int -> IamMetadata -> ShowS)
-> (IamMetadata -> String)
-> ([IamMetadata] -> ShowS)
-> Show IamMetadata
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [IamMetadata] -> ShowS
$cshowList :: [IamMetadata] -> ShowS
show :: IamMetadata -> String
$cshow :: IamMetadata -> String
showsPrec :: Int -> IamMetadata -> ShowS
$cshowsPrec :: Int -> IamMetadata -> ShowS
Show, Typeable)

instance Loggable IamMetadata where
    toLogText :: IamMetadata -> Text
toLogText (IamMetadata Maybe Text
r) = Text
"IAM: request ID=" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
Sem.<> Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
"<none>" Maybe Text
r

instance Sem.Semigroup IamMetadata where
    IamMetadata Maybe Text
r1 <> :: IamMetadata -> IamMetadata -> IamMetadata
<> IamMetadata Maybe Text
r2 = Maybe Text -> IamMetadata
IamMetadata (Maybe Text
r1 Maybe Text -> Maybe Text -> Maybe Text
forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
`mplus` Maybe Text
r2)

instance Monoid IamMetadata where
    mempty :: IamMetadata
mempty = Maybe Text -> IamMetadata
IamMetadata Maybe Text
forall a. Maybe a
Nothing
    mappend :: IamMetadata -> IamMetadata -> IamMetadata
mappend = IamMetadata -> IamMetadata -> IamMetadata
forall a. Semigroup a => a -> a -> a
(Sem.<>)

data IamConfiguration qt
    = IamConfiguration {
        IamConfiguration qt -> ByteString
iamEndpoint   :: ByteString
      , IamConfiguration qt -> Int
iamPort       :: Int
      , IamConfiguration qt -> Protocol
iamProtocol   :: Protocol
      , IamConfiguration qt -> Method
iamHttpMethod :: Method
      }
    deriving (Int -> IamConfiguration qt -> ShowS
[IamConfiguration qt] -> ShowS
IamConfiguration qt -> String
(Int -> IamConfiguration qt -> ShowS)
-> (IamConfiguration qt -> String)
-> ([IamConfiguration qt] -> ShowS)
-> Show (IamConfiguration qt)
forall qt. Int -> IamConfiguration qt -> ShowS
forall qt. [IamConfiguration qt] -> ShowS
forall qt. IamConfiguration qt -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [IamConfiguration qt] -> ShowS
$cshowList :: forall qt. [IamConfiguration qt] -> ShowS
show :: IamConfiguration qt -> String
$cshow :: forall qt. IamConfiguration qt -> String
showsPrec :: Int -> IamConfiguration qt -> ShowS
$cshowsPrec :: forall qt. Int -> IamConfiguration qt -> ShowS
Show)

instance DefaultServiceConfiguration (IamConfiguration NormalQuery) where
    defServiceConfig :: IamConfiguration NormalQuery
defServiceConfig   = Method -> Protocol -> ByteString -> IamConfiguration NormalQuery
forall qt. Method -> Protocol -> ByteString -> IamConfiguration qt
iam Method
PostQuery Protocol
HTTPS ByteString
iamEndpointDefault
    debugServiceConfig :: IamConfiguration NormalQuery
debugServiceConfig = Method -> Protocol -> ByteString -> IamConfiguration NormalQuery
forall qt. Method -> Protocol -> ByteString -> IamConfiguration qt
iam Method
PostQuery Protocol
HTTP  ByteString
iamEndpointDefault

instance DefaultServiceConfiguration (IamConfiguration UriOnlyQuery) where
    defServiceConfig :: IamConfiguration UriOnlyQuery
defServiceConfig   = Method -> Protocol -> ByteString -> IamConfiguration UriOnlyQuery
forall qt. Method -> Protocol -> ByteString -> IamConfiguration qt
iam Method
Get Protocol
HTTPS ByteString
iamEndpointDefault
    debugServiceConfig :: IamConfiguration UriOnlyQuery
debugServiceConfig = Method -> Protocol -> ByteString -> IamConfiguration UriOnlyQuery
forall qt. Method -> Protocol -> ByteString -> IamConfiguration qt
iam Method
Get Protocol
HTTP  ByteString
iamEndpointDefault

-- | The default IAM endpoint.
iamEndpointDefault :: ByteString
iamEndpointDefault :: ByteString
iamEndpointDefault = ByteString
"iam.amazonaws.com"

-- | Constructs an IamConfiguration with the specified parameters.
iam :: Method -> Protocol -> ByteString -> IamConfiguration qt
iam :: Method -> Protocol -> ByteString -> IamConfiguration qt
iam Method
method Protocol
protocol ByteString
endpoint
    = IamConfiguration :: forall qt.
ByteString -> Int -> Protocol -> Method -> IamConfiguration qt
IamConfiguration {
        iamEndpoint :: ByteString
iamEndpoint   = ByteString
endpoint
      , iamProtocol :: Protocol
iamProtocol   = Protocol
protocol
      , iamPort :: Int
iamPort       = Protocol -> Int
defaultPort Protocol
protocol
      , iamHttpMethod :: Method
iamHttpMethod = Method
method
      }

-- | Constructs a 'SignedQuery' with the specified request parameters.
iamSignQuery
    :: [(ByteString, ByteString)]
    -- ^ Pairs of parameter names and values that will be passed as part of
    -- the request data.
    -> IamConfiguration qt
    -> SignatureData
    -> SignedQuery
iamSignQuery :: [(ByteString, ByteString)]
-> IamConfiguration qt -> SignatureData -> SignedQuery
iamSignQuery [(ByteString, ByteString)]
q IamConfiguration{Int
ByteString
Method
Protocol
iamHttpMethod :: Method
iamProtocol :: Protocol
iamPort :: Int
iamEndpoint :: ByteString
iamHttpMethod :: forall qt. IamConfiguration qt -> Method
iamProtocol :: forall qt. IamConfiguration qt -> Protocol
iamPort :: forall qt. IamConfiguration qt -> Int
iamEndpoint :: forall qt. IamConfiguration qt -> ByteString
..} SignatureData{UTCTime
AbsoluteTimeInfo
Credentials
signatureCredentials :: SignatureData -> Credentials
signatureTime :: SignatureData -> UTCTime
signatureTimeInfo :: SignatureData -> AbsoluteTimeInfo
signatureCredentials :: Credentials
signatureTime :: UTCTime
signatureTimeInfo :: AbsoluteTimeInfo
..}
    = SignedQuery :: Method
-> Protocol
-> ByteString
-> Int
-> ByteString
-> Query
-> Maybe UTCTime
-> Maybe (IO ByteString)
-> Maybe ByteString
-> Maybe (Digest MD5)
-> RequestHeaders
-> RequestHeaders
-> Maybe RequestBody
-> ByteString
-> SignedQuery
SignedQuery {
        sqMethod :: Method
sqMethod        = Method
iamHttpMethod
      , sqProtocol :: Protocol
sqProtocol      = Protocol
iamProtocol
      , sqHost :: ByteString
sqHost          = ByteString
iamEndpoint
      , sqPort :: Int
sqPort          = Int
iamPort
      , sqPath :: ByteString
sqPath          = ByteString
"/"
      , sqQuery :: Query
sqQuery         = Query
signedQuery
      , sqDate :: Maybe UTCTime
sqDate          = UTCTime -> Maybe UTCTime
forall a. a -> Maybe a
Just UTCTime
signatureTime
      , sqAuthorization :: Maybe (IO ByteString)
sqAuthorization = Maybe (IO ByteString)
forall a. Maybe a
Nothing
      , sqContentType :: Maybe ByteString
sqContentType   = Maybe ByteString
forall a. Maybe a
Nothing
      , sqContentMd5 :: Maybe (Digest MD5)
sqContentMd5    = Maybe (Digest MD5)
forall a. Maybe a
Nothing
      , sqAmzHeaders :: RequestHeaders
sqAmzHeaders    = []
      , sqOtherHeaders :: RequestHeaders
sqOtherHeaders  = []
      , sqBody :: Maybe RequestBody
sqBody          = Maybe RequestBody
forall a. Maybe a
Nothing
      , sqStringToSign :: ByteString
sqStringToSign  = ByteString
stringToSign
      }
    where
      sig :: ByteString
sig             = Credentials -> AuthorizationHash -> ByteString -> ByteString
signature Credentials
signatureCredentials AuthorizationHash
HmacSHA256 ByteString
stringToSign
      signedQuery :: Query
signedQuery     = (ByteString
"Signature", ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
sig)(ByteString, Maybe ByteString) -> Query -> Query
forall a. a -> [a] -> [a]
:Query
expandedQuery
      accessKey :: ByteString
accessKey       = Credentials -> ByteString
accessKeyID Credentials
signatureCredentials
      timestampHeader :: (ByteString, Maybe ByteString)
timestampHeader =
          case AbsoluteTimeInfo
signatureTimeInfo of
            AbsoluteTimestamp UTCTime
time -> (ByteString
"Timestamp", ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString) -> ByteString -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ UTCTime -> ByteString
fmtAmzTime UTCTime
time)
            AbsoluteExpires   UTCTime
time -> (ByteString
"Expires"  , ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString) -> ByteString -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ UTCTime -> ByteString
fmtAmzTime UTCTime
time)
      newline :: Builder
newline         = Char -> Builder
Blaze8.fromChar Char
'\n'
      stringToSign :: ByteString
stringToSign    = Builder -> ByteString
Blaze.toByteString (Builder -> ByteString)
-> ([Builder] -> Builder) -> [Builder] -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder)
-> ([Builder] -> [Builder]) -> [Builder] -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> [Builder] -> [Builder]
forall a. a -> [a] -> [a]
intersperse Builder
newline ([Builder] -> ByteString) -> [Builder] -> ByteString
forall a b. (a -> b) -> a -> b
$
                            (ByteString -> Builder) -> [ByteString] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map ByteString -> Builder
Blaze.copyByteString
                                [Method -> ByteString
httpMethod Method
iamHttpMethod, ByteString
iamEndpoint, ByteString
"/"]
                            [Builder] -> [Builder] -> [Builder]
forall a. [a] -> [a] -> [a]
++  [Bool -> Query -> Builder
HTTP.renderQueryBuilder Bool
False Query
expandedQuery]
      expandedQuery :: Query
expandedQuery   = Query -> Query
forall a. QueryLike a => a -> Query
HTTP.toQuery (Query -> Query) -> (Query -> Query) -> Query -> Query
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query -> Query
forall a. Ord a => [a] -> [a]
sort (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ (((ByteString, ByteString) -> (ByteString, Maybe ByteString))
-> [(ByteString, ByteString)] -> Query
forall a b. (a -> b) -> [a] -> [b]
map (\(ByteString
a,ByteString
b) -> (ByteString
a, ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
b)) [(ByteString, ByteString)]
q Query -> Query -> Query
forall a. [a] -> [a] -> [a]
++) [
                            (ByteString
"AWSAccessKeyId"  , ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
accessKey)
                          , (ByteString
"SignatureMethod" , ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString) -> ByteString -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ AuthorizationHash -> ByteString
amzHash AuthorizationHash
HmacSHA256)
                          , (ByteString
"SignatureVersion", ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
"2")
                          , (ByteString
"Version"         , ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
"2010-05-08")
                          , (ByteString, Maybe ByteString)
timestampHeader] Query -> Query -> Query
forall a. [a] -> [a] -> [a]
++
                          Query -> (ByteString -> Query) -> Maybe ByteString -> Query
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (\ByteString
tok -> [ (ByteString
"SecurityToken", ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
tok)]) (Credentials -> Maybe ByteString
iamToken Credentials
signatureCredentials)

-- | Reads the metadata from an IAM response and delegates parsing the rest of
-- the data from the response to the given function.
iamResponseConsumer :: (Cu.Cursor -> Response IamMetadata a)
                    -> IORef IamMetadata
                    -> HTTPResponseConsumer a
iamResponseConsumer :: (Cursor -> Response IamMetadata a)
-> IORef IamMetadata -> HTTPResponseConsumer a
iamResponseConsumer Cursor -> Response IamMetadata a
inner IORef IamMetadata
md Response (ConduitM () ByteString (ResourceT IO) ())
resp = (Cursor -> Response IamMetadata a)
-> IORef IamMetadata -> HTTPResponseConsumer a
forall m a.
Monoid m =>
(Cursor -> Response m a) -> IORef m -> HTTPResponseConsumer a
xmlCursorConsumer Cursor -> Response IamMetadata a
parse IORef IamMetadata
md Response (ConduitM () ByteString (ResourceT IO) ())
resp
  where
    parse :: Cursor -> Response IamMetadata a
parse Cursor
cursor = do
      let rid :: Maybe Text
rid = [Text] -> Maybe Text
forall a. [a] -> Maybe a
listToMaybe ([Text] -> Maybe Text) -> [Text] -> Maybe Text
forall a b. (a -> b) -> a -> b
$ Cursor
cursor Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Text -> Cursor -> [Text]
elContent Text
"RequestID"
      IamMetadata -> Response IamMetadata ()
forall m. m -> Response m ()
tellMetadata (IamMetadata -> Response IamMetadata ())
-> IamMetadata -> Response IamMetadata ()
forall a b. (a -> b) -> a -> b
$ Maybe Text -> IamMetadata
IamMetadata Maybe Text
rid
      case Cursor
cursor Cursor -> (Cursor -> [Cursor]) -> [Cursor]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Text -> Cursor -> [Cursor]
Cu.laxElement Text
"Error" of
          []      -> Cursor -> Response IamMetadata a
inner Cursor
cursor
          (Cursor
err:[Cursor]
_) -> Cursor -> Response IamMetadata a
fromError Cursor
err
    fromError :: Cursor -> Response IamMetadata a
fromError Cursor
cursor = do
      Text
errCode <- String -> [Text] -> Response IamMetadata Text
forall (m :: * -> *) a. MonadThrow m => String -> [a] -> m a
force String
"Missing Error Code"    ([Text] -> Response IamMetadata Text)
-> [Text] -> Response IamMetadata Text
forall a b. (a -> b) -> a -> b
$ Cursor
cursor Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Text -> Cursor -> [Text]
elContent Text
"Code"
      Text
errMsg  <- String -> [Text] -> Response IamMetadata Text
forall (m :: * -> *) a. MonadThrow m => String -> [a] -> m a
force String
"Missing Error Message" ([Text] -> Response IamMetadata Text)
-> [Text] -> Response IamMetadata Text
forall a b. (a -> b) -> a -> b
$ Cursor
cursor Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Text -> Cursor -> [Text]
elContent Text
"Message"
      IamError -> Response IamMetadata a
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (IamError -> Response IamMetadata a)
-> IamError -> Response IamMetadata a
forall a b. (a -> b) -> a -> b
$ Status -> Text -> Text -> IamError
IamError (Response (ConduitM () ByteString (ResourceT IO) ()) -> Status
forall body. Response body -> Status
HTTP.responseStatus Response (ConduitM () ByteString (ResourceT IO) ())
resp) Text
errCode Text
errMsg

-- | Parses IAM @DateTime@ data type.
parseDateTime :: MonadThrow m => String -> m UTCTime
parseDateTime :: String -> m UTCTime
parseDateTime String
x
    = case Bool -> TimeLocale -> String -> String -> Maybe UTCTime
forall (m :: * -> *) t.
(MonadFail m, ParseTime t) =>
Bool -> TimeLocale -> String -> String -> m t
parseTimeM Bool
True TimeLocale
defaultTimeLocale String
iso8601UtcDate String
x of
        Maybe UTCTime
Nothing -> XmlException -> m UTCTime
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (XmlException -> m UTCTime) -> XmlException -> m UTCTime
forall a b. (a -> b) -> a -> b
$ String -> XmlException
XmlException (String -> XmlException) -> String -> XmlException
forall a b. (a -> b) -> a -> b
$ String
"Invalid DateTime: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
x
        Just UTCTime
dt -> UTCTime -> m UTCTime
forall (m :: * -> *) a. Monad m => a -> m a
return UTCTime
dt

-- | The IAM @User@ data type.
--
-- <http://docs.aws.amazon.com/IAM/latest/APIReference/API_User.html>
data User
    = User {
        User -> Text
userArn        :: Text
      -- ^ ARN used to refer to this user.
      , User -> UTCTime
userCreateDate :: UTCTime
      -- ^ Date and time at which the user was created.
      , User -> Text
userPath       :: Text
      -- ^ Path under which the user was created.
      , User -> Text
userUserId     :: Text
      -- ^ Unique identifier used to refer to this user. 
      , User -> Text
userUserName   :: Text
      -- ^ Name of the user.
      }
    deriving (User -> User -> Bool
(User -> User -> Bool) -> (User -> User -> Bool) -> Eq User
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: User -> User -> Bool
$c/= :: User -> User -> Bool
== :: User -> User -> Bool
$c== :: User -> User -> Bool
Eq, Eq User
Eq User
-> (User -> User -> Ordering)
-> (User -> User -> Bool)
-> (User -> User -> Bool)
-> (User -> User -> Bool)
-> (User -> User -> Bool)
-> (User -> User -> User)
-> (User -> User -> User)
-> Ord User
User -> User -> Bool
User -> User -> Ordering
User -> User -> User
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: User -> User -> User
$cmin :: User -> User -> User
max :: User -> User -> User
$cmax :: User -> User -> User
>= :: User -> User -> Bool
$c>= :: User -> User -> Bool
> :: User -> User -> Bool
$c> :: User -> User -> Bool
<= :: User -> User -> Bool
$c<= :: User -> User -> Bool
< :: User -> User -> Bool
$c< :: User -> User -> Bool
compare :: User -> User -> Ordering
$ccompare :: User -> User -> Ordering
$cp1Ord :: Eq User
Ord, Int -> User -> ShowS
[User] -> ShowS
User -> String
(Int -> User -> ShowS)
-> (User -> String) -> ([User] -> ShowS) -> Show User
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [User] -> ShowS
$cshowList :: [User] -> ShowS
show :: User -> String
$cshow :: User -> String
showsPrec :: Int -> User -> ShowS
$cshowsPrec :: Int -> User -> ShowS
Show, Typeable)

-- | Parses the IAM @User@ data type.
parseUser :: MonadThrow m => Cu.Cursor -> m User
parseUser :: Cursor -> m User
parseUser Cursor
cursor = do
    Text
userArn        <- Text -> m Text
attr Text
"Arn"
    UTCTime
userCreateDate <- Text -> m Text
attr Text
"CreateDate" m Text -> (Text -> m UTCTime) -> m UTCTime
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> m UTCTime
forall (m :: * -> *). MonadThrow m => String -> m UTCTime
parseDateTime (String -> m UTCTime) -> (Text -> String) -> Text -> m UTCTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
Text.unpack
    Text
userPath       <- Text -> m Text
attr Text
"Path"
    Text
userUserId     <- Text -> m Text
attr Text
"UserId"
    Text
userUserName   <- Text -> m Text
attr Text
"UserName"
    User -> m User
forall (m :: * -> *) a. Monad m => a -> m a
return User :: Text -> UTCTime -> Text -> Text -> Text -> User
User{UTCTime
Text
userUserName :: Text
userUserId :: Text
userPath :: Text
userCreateDate :: UTCTime
userArn :: Text
userUserName :: Text
userUserId :: Text
userPath :: Text
userCreateDate :: UTCTime
userArn :: Text
..}
  where
    attr :: Text -> m Text
attr Text
name = String -> [Text] -> m Text
forall (m :: * -> *) a. MonadThrow m => String -> [a] -> m a
force (String
"Missing " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
Text.unpack Text
name) ([Text] -> m Text) -> [Text] -> m Text
forall a b. (a -> b) -> a -> b
$
                Cursor
cursor Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Text -> Cursor -> [Text]
elContent Text
name


-- | The IAM @Group@ data type.
--
-- <http://docs.aws.amazon.com/IAM/latest/APIReference/API_Group.html>
data Group
    = Group {
        Group -> Text
groupArn        :: Text
      -- ^ ARN used to refer to this group.
      , Group -> UTCTime
groupCreateDate :: UTCTime
      -- ^ Date and time at which the group was created.
      , Group -> Text
groupPath       :: Text
      -- ^ Path under which the group was created.
      , Group -> Text
groupGroupId     :: Text
      -- ^ Unique identifier used to refer to this group. 
      , Group -> Text
groupGroupName   :: Text
      -- ^ Name of the group.
      }
    deriving (Group -> Group -> Bool
(Group -> Group -> Bool) -> (Group -> Group -> Bool) -> Eq Group
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Group -> Group -> Bool
$c/= :: Group -> Group -> Bool
== :: Group -> Group -> Bool
$c== :: Group -> Group -> Bool
Eq, Eq Group
Eq Group
-> (Group -> Group -> Ordering)
-> (Group -> Group -> Bool)
-> (Group -> Group -> Bool)
-> (Group -> Group -> Bool)
-> (Group -> Group -> Bool)
-> (Group -> Group -> Group)
-> (Group -> Group -> Group)
-> Ord Group
Group -> Group -> Bool
Group -> Group -> Ordering
Group -> Group -> Group
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Group -> Group -> Group
$cmin :: Group -> Group -> Group
max :: Group -> Group -> Group
$cmax :: Group -> Group -> Group
>= :: Group -> Group -> Bool
$c>= :: Group -> Group -> Bool
> :: Group -> Group -> Bool
$c> :: Group -> Group -> Bool
<= :: Group -> Group -> Bool
$c<= :: Group -> Group -> Bool
< :: Group -> Group -> Bool
$c< :: Group -> Group -> Bool
compare :: Group -> Group -> Ordering
$ccompare :: Group -> Group -> Ordering
$cp1Ord :: Eq Group
Ord, Int -> Group -> ShowS
[Group] -> ShowS
Group -> String
(Int -> Group -> ShowS)
-> (Group -> String) -> ([Group] -> ShowS) -> Show Group
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Group] -> ShowS
$cshowList :: [Group] -> ShowS
show :: Group -> String
$cshow :: Group -> String
showsPrec :: Int -> Group -> ShowS
$cshowsPrec :: Int -> Group -> ShowS
Show, Typeable)

-- | Parses the IAM @Group@ data type.
parseGroup :: MonadThrow m => Cu.Cursor -> m Group
parseGroup :: Cursor -> m Group
parseGroup Cursor
cursor = do
    Text
groupArn        <- Text -> m Text
attr Text
"Arn"
    UTCTime
groupCreateDate <- Text -> m Text
attr Text
"CreateDate" m Text -> (Text -> m UTCTime) -> m UTCTime
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> m UTCTime
forall (m :: * -> *). MonadThrow m => String -> m UTCTime
parseDateTime (String -> m UTCTime) -> (Text -> String) -> Text -> m UTCTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
Text.unpack
    Text
groupPath       <- Text -> m Text
attr Text
"Path"
    Text
groupGroupId     <- Text -> m Text
attr Text
"GroupId"
    Text
groupGroupName   <- Text -> m Text
attr Text
"GroupName"
    Group -> m Group
forall (m :: * -> *) a. Monad m => a -> m a
return Group :: Text -> UTCTime -> Text -> Text -> Text -> Group
Group{UTCTime
Text
groupGroupName :: Text
groupGroupId :: Text
groupPath :: Text
groupCreateDate :: UTCTime
groupArn :: Text
groupGroupName :: Text
groupGroupId :: Text
groupPath :: Text
groupCreateDate :: UTCTime
groupArn :: Text
..}
  where
    attr :: Text -> m Text
attr Text
name = String -> [Text] -> m Text
forall (m :: * -> *) a. MonadThrow m => String -> [a] -> m a
force (String
"Missing " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
Text.unpack Text
name) ([Text] -> m Text) -> [Text] -> m Text
forall a b. (a -> b) -> a -> b
$
                Cursor
cursor Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Text -> Cursor -> [Text]
elContent Text
name


data AccessKeyStatus = AccessKeyActive | AccessKeyInactive
    deriving (AccessKeyStatus -> AccessKeyStatus -> Bool
(AccessKeyStatus -> AccessKeyStatus -> Bool)
-> (AccessKeyStatus -> AccessKeyStatus -> Bool)
-> Eq AccessKeyStatus
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: AccessKeyStatus -> AccessKeyStatus -> Bool
$c/= :: AccessKeyStatus -> AccessKeyStatus -> Bool
== :: AccessKeyStatus -> AccessKeyStatus -> Bool
$c== :: AccessKeyStatus -> AccessKeyStatus -> Bool
Eq, Eq AccessKeyStatus
Eq AccessKeyStatus
-> (AccessKeyStatus -> AccessKeyStatus -> Ordering)
-> (AccessKeyStatus -> AccessKeyStatus -> Bool)
-> (AccessKeyStatus -> AccessKeyStatus -> Bool)
-> (AccessKeyStatus -> AccessKeyStatus -> Bool)
-> (AccessKeyStatus -> AccessKeyStatus -> Bool)
-> (AccessKeyStatus -> AccessKeyStatus -> AccessKeyStatus)
-> (AccessKeyStatus -> AccessKeyStatus -> AccessKeyStatus)
-> Ord AccessKeyStatus
AccessKeyStatus -> AccessKeyStatus -> Bool
AccessKeyStatus -> AccessKeyStatus -> Ordering
AccessKeyStatus -> AccessKeyStatus -> AccessKeyStatus
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: AccessKeyStatus -> AccessKeyStatus -> AccessKeyStatus
$cmin :: AccessKeyStatus -> AccessKeyStatus -> AccessKeyStatus
max :: AccessKeyStatus -> AccessKeyStatus -> AccessKeyStatus
$cmax :: AccessKeyStatus -> AccessKeyStatus -> AccessKeyStatus
>= :: AccessKeyStatus -> AccessKeyStatus -> Bool
$c>= :: AccessKeyStatus -> AccessKeyStatus -> Bool
> :: AccessKeyStatus -> AccessKeyStatus -> Bool
$c> :: AccessKeyStatus -> AccessKeyStatus -> Bool
<= :: AccessKeyStatus -> AccessKeyStatus -> Bool
$c<= :: AccessKeyStatus -> AccessKeyStatus -> Bool
< :: AccessKeyStatus -> AccessKeyStatus -> Bool
$c< :: AccessKeyStatus -> AccessKeyStatus -> Bool
compare :: AccessKeyStatus -> AccessKeyStatus -> Ordering
$ccompare :: AccessKeyStatus -> AccessKeyStatus -> Ordering
$cp1Ord :: Eq AccessKeyStatus
Ord, Int -> AccessKeyStatus -> ShowS
[AccessKeyStatus] -> ShowS
AccessKeyStatus -> String
(Int -> AccessKeyStatus -> ShowS)
-> (AccessKeyStatus -> String)
-> ([AccessKeyStatus] -> ShowS)
-> Show AccessKeyStatus
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [AccessKeyStatus] -> ShowS
$cshowList :: [AccessKeyStatus] -> ShowS
show :: AccessKeyStatus -> String
$cshow :: AccessKeyStatus -> String
showsPrec :: Int -> AccessKeyStatus -> ShowS
$cshowsPrec :: Int -> AccessKeyStatus -> ShowS
Show, Typeable)

-- | The IAM @MFADevice@ data type.
--
-- <https://docs.aws.amazon.com/IAM/latest/APIReference/API_MFADevice.html>
data MfaDevice = MfaDevice
                 { MfaDevice -> UTCTime
mfaEnableDate   :: UTCTime
                   -- ^ The date when the MFA device was enabled for
                   -- the user.
                 , MfaDevice -> Text
mfaSerialNumber :: Text
                   -- ^ The serial number that uniquely identifies the
                   -- MFA device. For virtual MFA devices, the serial
                   -- number is the device ARN.
                 , MfaDevice -> Text
mfaUserName     :: Text
                   -- ^ The user with whom the MFA device is
                   -- associated. Minimum length of 1. Maximum length
                   -- of 64.
                 } deriving (MfaDevice -> MfaDevice -> Bool
(MfaDevice -> MfaDevice -> Bool)
-> (MfaDevice -> MfaDevice -> Bool) -> Eq MfaDevice
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: MfaDevice -> MfaDevice -> Bool
$c/= :: MfaDevice -> MfaDevice -> Bool
== :: MfaDevice -> MfaDevice -> Bool
$c== :: MfaDevice -> MfaDevice -> Bool
Eq, Eq MfaDevice
Eq MfaDevice
-> (MfaDevice -> MfaDevice -> Ordering)
-> (MfaDevice -> MfaDevice -> Bool)
-> (MfaDevice -> MfaDevice -> Bool)
-> (MfaDevice -> MfaDevice -> Bool)
-> (MfaDevice -> MfaDevice -> Bool)
-> (MfaDevice -> MfaDevice -> MfaDevice)
-> (MfaDevice -> MfaDevice -> MfaDevice)
-> Ord MfaDevice
MfaDevice -> MfaDevice -> Bool
MfaDevice -> MfaDevice -> Ordering
MfaDevice -> MfaDevice -> MfaDevice
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: MfaDevice -> MfaDevice -> MfaDevice
$cmin :: MfaDevice -> MfaDevice -> MfaDevice
max :: MfaDevice -> MfaDevice -> MfaDevice
$cmax :: MfaDevice -> MfaDevice -> MfaDevice
>= :: MfaDevice -> MfaDevice -> Bool
$c>= :: MfaDevice -> MfaDevice -> Bool
> :: MfaDevice -> MfaDevice -> Bool
$c> :: MfaDevice -> MfaDevice -> Bool
<= :: MfaDevice -> MfaDevice -> Bool
$c<= :: MfaDevice -> MfaDevice -> Bool
< :: MfaDevice -> MfaDevice -> Bool
$c< :: MfaDevice -> MfaDevice -> Bool
compare :: MfaDevice -> MfaDevice -> Ordering
$ccompare :: MfaDevice -> MfaDevice -> Ordering
$cp1Ord :: Eq MfaDevice
Ord, Int -> MfaDevice -> ShowS
[MfaDevice] -> ShowS
MfaDevice -> String
(Int -> MfaDevice -> ShowS)
-> (MfaDevice -> String)
-> ([MfaDevice] -> ShowS)
-> Show MfaDevice
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [MfaDevice] -> ShowS
$cshowList :: [MfaDevice] -> ShowS
show :: MfaDevice -> String
$cshow :: MfaDevice -> String
showsPrec :: Int -> MfaDevice -> ShowS
$cshowsPrec :: Int -> MfaDevice -> ShowS
Show, Typeable)

-- | Parses the IAM @MFADevice@ data type.
parseMfaDevice :: MonadThrow m => Cu.Cursor -> m MfaDevice
parseMfaDevice :: Cursor -> m MfaDevice
parseMfaDevice Cursor
cursor = do
  UTCTime
mfaEnableDate   <- Text -> m Text
attr Text
"EnableDate" m Text -> (Text -> m UTCTime) -> m UTCTime
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> m UTCTime
forall (m :: * -> *). MonadThrow m => String -> m UTCTime
parseDateTime (String -> m UTCTime) -> (Text -> String) -> Text -> m UTCTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
Text.unpack
  Text
mfaSerialNumber <- Text -> m Text
attr Text
"SerialNumber"
  Text
mfaUserName     <- Text -> m Text
attr Text
"UserName"
  MfaDevice -> m MfaDevice
forall (m :: * -> *) a. Monad m => a -> m a
return MfaDevice :: UTCTime -> Text -> Text -> MfaDevice
MfaDevice{UTCTime
Text
mfaUserName :: Text
mfaSerialNumber :: Text
mfaEnableDate :: UTCTime
mfaUserName :: Text
mfaSerialNumber :: Text
mfaEnableDate :: UTCTime
..}
 where attr :: Text -> m Text
attr Text
name = String -> [Text] -> m Text
forall (m :: * -> *) a. MonadThrow m => String -> [a] -> m a
force (String
"Missing " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
Text.unpack Text
name) ([Text] -> m Text) -> [Text] -> m Text
forall a b. (a -> b) -> a -> b
$
               Cursor
cursor Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Text -> Cursor -> [Text]
elContent Text
name