module Stackctl.AutoSSO ( AutoSSOOption , defaultAutoSSOOption , HasAutoSSOOption(..) , autoSSOOption , envAutoSSOOption , handleAutoSSO ) where import Stackctl.Prelude import Amazonka.SSO (_UnauthorizedException) import Amazonka.Types (Error, ErrorMessage(..), serviceMessage) import Data.Semigroup (Last(..)) import qualified Env import Options.Applicative import Stackctl.Prompt import System.Process.Typed data AutoSSOOption = AutoSSOAlways | AutoSSOAsk | AutoSSONever deriving Semigroup via Last AutoSSOOption defaultAutoSSOOption :: AutoSSOOption defaultAutoSSOOption = AutoSSOAsk readAutoSSO :: String -> Either String AutoSSOOption readAutoSSO = \case "always" -> Right AutoSSOAlways "ask" -> Right AutoSSOAsk "never" -> Right AutoSSONever x -> Left $ "Invalid choice for auto-sso: " <> x <> ", must be always|ask|never" class HasAutoSSOOption env where autoSSOOptionL :: Lens' env AutoSSOOption autoSSOOption :: Parser AutoSSOOption autoSSOOption = option (eitherReader readAutoSSO) $ mconcat [long "auto-sso", help autoSSOHelp, metavar "WHEN"] envAutoSSOOption :: Env.Parser Env.Error AutoSSOOption envAutoSSOOption = Env.var (first Env.UnreadError . readAutoSSO) "AUTO_SSO" $ Env.help autoSSOHelp autoSSOHelp :: IsString a => a autoSSOHelp = "Automatically run aws-sso-login if necessary?" handleAutoSSO :: ( MonadUnliftIO m , MonadReader env m , MonadLogger m , HasLogger env , HasAutoSSOOption options ) => options -> m a -> m a handleAutoSSO options f = do catchJust (preview (_UnauthorizedException @Error)) f $ \ex -> do case options ^. autoSSOOptionL of AutoSSOAlways -> do logWarn $ ssoErrorMessage ex logInfo "Running `aws sso login' automatically" AutoSSOAsk -> do logWarn $ ssoErrorMessage ex promptOrExit "Run `aws sso login'" AutoSSONever -> do logError $ ssoErrorMessage ex exitFailure runProcess_ $ proc "aws" ["sso", "login"] f where ssoErrorMessage ex = "AWS SSO authorization error" :# [ "message" .= fmap fromErrorMessage (ex ^. serviceMessage) , "hint" .= ("Run `aws sso login' and try again" :: Text) ]