packages feed

aws-mfa-credentials-0.1.0.0: AwsMfaCredentials/MainLoop.hs

{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE ScopedTypeVariables #-}
module AwsMfaCredentials.MainLoop where

import AwsMfaCredentials.Effects.AWS (AWS, getSessionToken)
import AwsMfaCredentials.Effects.PasswordPrompt
  (PasswordPrompt, passwordPrompt)
import AwsMfaCredentials.Effects.Wait (waitUntil)
import Control.Lens.Operators ((&), (?~), (.~), (^.))
import Control.Monad.Freer (Eff, Member)
import Control.Monad.Freer.Writer (Writer, tell)
import Data.Text (Text)
import qualified Data.Text as T
import Data.Time.Clock (UTCTime, addUTCTime)
import Network.AWS.STS.Types (Credentials, cExpiration)
import qualified Network.AWS.STS.GetSessionToken as STS
import Numeric.Natural (Natural)

-- | Command line options
data Opts = Opts
  { -- | The MFA device ID
    serialNumber :: Text
  , -- | The duration, in seconds, that the credentials should remain valid
    duration :: Maybe Natural
  , -- | The profile whose credentials we're managing
    profile :: Text
  , -- | The amount of time, in seconds, before credential expiry to refresh
    refreshLeadTime :: Natural
  }

-- | The business logic of the aws-mfa-credentials main loop
mainLoopBody :: forall r . ( Member (PasswordPrompt String Text) r
                           , Member AWS r
                           , Member (Writer (Text, Credentials)) r
                           , Member (Writer UTCTime) r
                           ) => Opts -> Eff r ()
mainLoopBody (Opts {..}) = do
    mfa <- passwordPrompt . T.unpack $ T.concat
      [ "Enter MFA token for "
      , profile
      , " from device "
      , serialNumber
      ]
    let req = STS.getSessionToken & STS.gstTokenCode ?~ mfa
                                  & STS.gstDurationSeconds .~ duration
                                  & STS.gstSerialNumber ?~ serialNumber
    creds <- getSessionToken req
    tell (mfaProfile, creds)
    waitUntil . addUTCTime refreshLeadTime' $ creds ^. cExpiration
  where
    mfaProfile = T.append profile "-mfa"

    refreshLeadTime' = 0 - fromIntegral refreshLeadTime