diff --git a/README.md b/README.md index a95c842..b44c5a6 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,7 @@ To use configurations other than the `default` profile set the `$AWS_PROFILE` environment variable to your desired profile. Since version `0.21.0.58` Rome also supports privilege escalation via [Amazon STS](https://docs.aws.amazon.com/STS/latest/APIReference/Welcome.html) -by specifying `role_arn` and `source_profile` in `~/.aws/credentials` +by specifying `role_arn` and `source_profile` in `~/.aws/config` ### Selecting the AWS Region diff --git a/src/Lib.hs b/src/Lib.hs index 042a2c5..7840fde 100644 --- a/src/Lib.hs +++ b/src/Lib.hs @@ -75,17 +75,17 @@ s3EndpointOverride (URL (Absolute h) _ _) = s3EndpointOverride _ = S3.s3 -- | Tries to get authentication details and region to perform --- | requests to AWS. +-- | requests to AWS. -- | The `AWS_PROFILE` is read from the environment --- | or falls back to `default`. +-- | or falls back to `default`. -- | The `AWS_REGION` is first read from the environment, if not found -- | it is read from `~/.aws/config` based on the profile discovered in the previous step. -- | The `AWS_ACCESS_KEY_ID` & `AWS_SECRET_ACCESS_KEY` are first --- | read from the environment. If not found, then the `~/.aws/crendetilas` +-- | read from the environment. If not found, then the `~/.aws/credentials` -- | file is read. If `source_profile` key is present the reading of the -- | authentication details happens from this profile rather then the `AWS_PROFILE`. --- | Finally, if `role_arn` is specified, the crendials gathered up to now are used --- | to obtain new credentials with STS esclated to `role_arn`. +-- | Finally, if `role_arn` is specified, the credentials gathered up to now are used +-- | to obtain new credentials with STS escalated to `role_arn`. getAWSEnv :: (MonadIO m, MonadCatch m) => ExceptT String m AWS.Env getAWSEnv = do region <- discoverRegion @@ -94,13 +94,15 @@ getAWSEnv = do (lookupEnv (T.unpack "AWS_PROFILE")) credentials <- runExceptT $ (AWS.credentialsFromFile =<< getAWSCredentialsFilePath) `catch` \(e :: IOError) -> ExceptT . return . Left . show $ e + config <- + runExceptT $ (AWS.configFromFile =<< getAWSConfigFilePath) `catch` \(e :: IOError) -> ExceptT . return . Left . show $ e (auth, _) <- AWS.catching AWS._MissingEnvError AWS.fromEnv $ \envError -> either throwError (\cred -> do let finalProfile = fromMaybe profile - (eitherToMaybe $ AWS.sourceProfileOf profile =<< credentials) + (eitherToMaybe $ AWS.sourceProfileOf profile =<< config) let authAndRegion = (,) @@ -109,7 +111,7 @@ getAWSEnv = do T.unpack envError ++ ". " ++ e - ++ " in file ~/.aws/credentilas" + ++ " in file ~/.aws/credentials" ) (AWS.authFromCredentilas finalProfile =<< credentials) <*> pure (pure region) @@ -118,7 +120,7 @@ getAWSEnv = do credentials manager <- liftIO (Conduit.newManager Conduit.tlsManagerSettings) ref <- liftIO (newIORef Nothing) - let roleARN = eitherToMaybe $ AWS.roleARNOf profile =<< credentials + let roleARN = eitherToMaybe $ AWS.roleARNOf profile =<< config let curerntEnv = AWS.Env region (\_ _ -> pure ()) (AWS.retryConnectionFailure 3) @@ -195,7 +197,7 @@ runUtilsCommand command absoluteRomefilePath _ _ = lift $ encodeFile absoluteRomefilePath romeFileEntries _ -> throwError "Error: Programming Error. Only Utils command supported." --- | Runs a command containing a `UDCPayload` +-- | Runs a command containing a `UDCPayload` runUDCCommand :: RomeCommand -> FilePath -> Bool -> RomeVersion -> RomeMonad () runUDCCommand command absoluteRomefilePath verbose romeVersion = do cartfileEntries <- getCartfileEntries @@ -618,7 +620,7 @@ downloadArtifacts mS3BucketName mlCacheDir mEnginePath reverseRepositoryMap fram ) readerEnv -- Use engine - (Nothing, lCacheDir, Just ePath) -> do + (Nothing, lCacheDir, Just ePath) -> do let engineEnv = (cachePrefix, skipLocalCacheFlag, concurrentlyFlag, verbose) let action1 = runReaderT (downloadFrameworksAndArtifactsWithEngine ePath @@ -713,7 +715,7 @@ uploadArtifacts mS3BucketName mlCacheDir mEnginePath reverseRepositoryMap framew >> runReaderT (saveVersionFilesToLocalCache lCacheDir gitRepoNamesAndVersions) readerEnv - -- Engine, maybe Cache + -- Engine, maybe Cache (Nothing, lCacheDir, Just enginePath) -> do let engineEnv = ( cachePrefix @@ -1538,7 +1540,7 @@ downloadFrameworkAndArtifactsWithEngine enginePath (Just lCacheDir) reverseRomeM readerEnv let sayFunc :: MonadIO m => String -> m () sayFunc = if verbose then sayLnWithTime else sayLn - + case eitherFrameworkSuccess of Right _ -> return () Left e -> liftIO $ do diff --git a/src/Network/AWS/Utils.hs b/src/Network/AWS/Utils.hs index 65f3499..77c4d20 100644 --- a/src/Network/AWS/Utils.hs +++ b/src/Network/AWS/Utils.hs @@ -3,6 +3,7 @@ module Network.AWS.Utils ( ConfigFile , credentialsFromFile + , configFromFile , authFromCredentilas , parseConfigFile , regionOf @@ -22,7 +23,7 @@ import Control.Monad ((<=<)) import Data.Either.Utils (maybeToEither) import Data.Either.Extra (mapLeft) import Data.Ini (Ini, lookupValue, parseIni) -import qualified Data.Text as T (Text, null, unpack) +import qualified Data.Text as T (Text, null, pack, unpack) import qualified Data.Text.Encoding as T (encodeUtf8) import qualified Data.Text.IO as T (readFile) import qualified Network.AWS as AWS @@ -54,6 +55,16 @@ credentialsFromFile filePath = do withExceptT (("Could not parse " <> filePath <> ": ") <>) (action file) where action a = ExceptT . return $ parseCredentialsFile a +-- | Reads `ConfigFile` from a file at a given path +configFromFile + :: MonadIO m + => FilePath -- ^ The path to the file containing the config. Usually `~/.aws/config` + -> ExceptT String m ConfigFile +configFromFile filePath = do + file <- liftIO (T.readFile filePath) + withExceptT (("Could not parse " <> filePath <> ": ") <>) (action file) + where action a = ExceptT . return $ parseConfigFile a + authFromCredentilas :: T.Text -> CredentialsFile -> Either String AWS.Auth authFromCredentilas profile credentials = AWS.Auth <$> authEnv where @@ -90,16 +101,33 @@ getPropertyFromCredentials getPropertyFromCredentials profile property = lookupValue profile property . asIni -sourceProfileOf :: T.Text -> CredentialsFile -> Either String T.Text -sourceProfileOf profile credFile = - getPropertyFromCredentials profile "source_profile" credFile - `withError` const (missingKeyError key profile) - where key = "source_profile" +getPropertyFromConfig + :: T.Text -> T.Text -> ConfigFile -> Either String T.Text +getPropertyFromConfig profile property = + lookupValue profile property . asIni -roleARNOf :: T.Text -> CredentialsFile -> Either String T.Text -roleARNOf profile credFile = getPropertyFromCredentials profile key credFile +sourceProfileOf :: T.Text -> ConfigFile -> Either String T.Text +sourceProfileOf profile configFile = + getPropertyFromConfig finalProfile key configFile + `withError` const (missingKeyError key profile) + where + key = "source_profile" + finalProfile = + if profile == "default" then + profile + else + T.pack "profile " <> profile + +roleARNOf :: T.Text -> ConfigFile -> Either String T.Text +roleARNOf profile configFile = getPropertyFromConfig finalProfile key configFile `withError` const (missingKeyError key profile) - where key = "role_arn" + where + key = "role_arn" + finalProfile = + if profile == "default" then + profile + else + T.pack "profile " <> profile accessKeyIdOf :: T.Text -> CredentialsFile -> Either String T.Text accessKeyIdOf profile credFile =