Просмотр исходного кода

AWS credential profiles in config file are also recognized + Bug fix: AWS credentials file path was resolved using AWS_CONFIG_FILE environment variable instead of correct AWS_SHARED_CREDENTIALS_FILE

Source commit: fd25d9213d71c28c9b3ea62e2ae995d5572959c1
Martin Prikryl 1 год назад
Родитель
Сommit
481218e7d7
1 измененных файлов с 85 добавлено и 51 удалено
  1. 85 51
      source/core/S3FileSystem.cpp

+ 85 - 51
source/core/S3FileSystem.cpp

@@ -34,9 +34,11 @@
 #define AWS_ACCESS_KEY_ID L"AWS_ACCESS_KEY_ID"
 #define AWS_SECRET_ACCESS_KEY L"AWS_SECRET_ACCESS_KEY"
 #define AWS_SESSION_TOKEN L"AWS_SESSION_TOKEN"
+#define AWS_SHARED_CREDENTIALS_FILE L"AWS_SHARED_CREDENTIALS_FILE"
 #define AWS_CONFIG_FILE L"AWS_CONFIG_FILE"
 #define AWS_PROFILE L"AWS_PROFILE"
 #define AWS_PROFILE_DEFAULT L"default"
+#define AWS_CONFIG_PROFILE_PREFIX L"profile "
 //---------------------------------------------------------------------------
 static std::unique_ptr<TCriticalSection> LibS3Section(TraceInitPtr(new TCriticalSection()));
 //---------------------------------------------------------------------------
@@ -57,6 +59,38 @@ UnicodeString __fastcall S3LibDefaultRegion()
   return StrFromS3(S3_DEFAULT_REGION);
 }
 //---------------------------------------------------------------------------
+static void NeedS3Config(
+  UnicodeString & FileName, TDateTime & TimeStamp, std::unique_ptr<TCustomIniFile> & File,
+  const UnicodeString & DefaultName, const UnicodeString & EnvName)
+{
+  if (FileName.IsEmpty())
+  {
+    FileName = GetEnvironmentVariable(EnvName);
+    if (FileName.IsEmpty())
+    {
+      UnicodeString ProfilePath = GetShellFolderPath(CSIDL_PROFILE);
+      UnicodeString DefaultFileName = IncludeTrailingBackslash(ProfilePath) + L".aws\\" + DefaultName;
+      if (FileExists(DefaultFileName))
+      {
+        FileName = DefaultFileName;
+      }
+    }
+  }
+
+  TDateTime CurrentTimestamp;
+  FileAge(FileName, CurrentTimestamp);
+  if (TimeStamp != CurrentTimestamp)
+  {
+    TimeStamp = CurrentTimestamp;
+    // TMemIniFile silently ignores empty paths or non-existing files
+    AppLogFmt(L"Reading AWS '%s' file", (FileName));
+    File.reset(new TMemIniFile(FileName));
+  }
+}
+//---------------------------------------------------------------------------
+UnicodeString S3CredentialsFileName;
+TDateTime S3CredentialsTimestamp;
+std::unique_ptr<TCustomIniFile> S3CredentialsFile;
 UnicodeString S3ConfigFileName;
 TDateTime S3ConfigTimestamp;
 std::unique_ptr<TCustomIniFile> S3ConfigFile;
@@ -79,29 +113,33 @@ static void NeedS3Config()
     }
   }
 
-  if (S3ConfigFileName.IsEmpty())
+  NeedS3Config(S3CredentialsFileName, S3CredentialsTimestamp, S3CredentialsFile, L"credentials", AWS_SHARED_CREDENTIALS_FILE);
+  NeedS3Config(S3ConfigFileName, S3ConfigTimestamp, S3ConfigFile, L"config", AWS_CONFIG_FILE);
+}
+//---------------------------------------------------------------------------
+void GetS3Profiles(TStrings * Profiles, TCustomIniFile * File, const UnicodeString & Prefix = EmptyStr)
+{
+  if (File != NULL)
   {
-    S3ConfigFileName = GetEnvironmentVariable(AWS_CONFIG_FILE);
-    if (S3ConfigFileName.IsEmpty())
+    std::unique_ptr<TStrings> Sections(new TStringList());
+    File->ReadSections(Sections.get());
+    for (int Index = 0; Index < Sections->Count; Index++)
     {
-      UnicodeString ProfilePath = GetShellFolderPath(CSIDL_PROFILE);
-      UnicodeString DefaultConfigFileName = IncludeTrailingBackslash(ProfilePath) + L".aws\\credentials";
-      if (FileExists(DefaultConfigFileName))
+      UnicodeString Section = Sections->Strings[Index];
+      if (Prefix.IsEmpty() || StartsText(Prefix, Section))
       {
-        S3ConfigFileName = DefaultConfigFileName;
+        // This is not consistent with AWS CLI.
+        // AWS CLI fails if one of AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY is set and other is missing:
+        // "Partial credentials found in env, missing: AWS_SECRET_ACCESS_KEY"
+        if (!File->ReadString(Section, AWS_ACCESS_KEY_ID, EmptyStr).IsEmpty() ||
+            !File->ReadString(Section, AWS_SECRET_ACCESS_KEY, EmptyStr).IsEmpty() ||
+            !File->ReadString(Section, AWS_SESSION_TOKEN, EmptyStr).IsEmpty())
+        {
+          Profiles->Add(MidStr(Section, Prefix.Length() + 1));
+        }
       }
     }
   }
-
-  TDateTime Timestamp;
-  FileAge(S3ConfigFileName, Timestamp);
-  if (S3ConfigTimestamp != Timestamp)
-  {
-    S3ConfigTimestamp = Timestamp;
-    // TMemIniFile silently ignores empty paths or non-existing files
-    AppLog(L"Reading AWS credentials file");
-    S3ConfigFile.reset(new TMemIniFile(S3ConfigFileName));
-  }
 }
 //---------------------------------------------------------------------------
 TStrings * GetS3Profiles()
@@ -109,30 +147,9 @@ TStrings * GetS3Profiles()
   NeedS3Config();
   // S3 allegedly treats the section case-sensitively, but our GetS3ConfigValue (ReadString) does not,
   // so consistently we return case-insensitive list.
-  std::unique_ptr<TStrings> Result(new TStringList());
-  if (S3ConfigFile.get() != NULL)
-  {
-    S3ConfigFile->ReadSections(Result.get());
-    int Index = 0;
-    while (Index < Result->Count)
-    {
-      UnicodeString Section = Result->Strings[Index];
-      // This is not consistent with AWS CLI.
-      // AWS CLI fails if one of AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY is set and other is missing:
-      // "Partial credentials found in env, missing: AWS_SECRET_ACCESS_KEY"
-      if (S3ConfigFile->ReadString(Section, AWS_ACCESS_KEY_ID, EmptyStr).IsEmpty() &&
-          S3ConfigFile->ReadString(Section, AWS_SECRET_ACCESS_KEY, EmptyStr).IsEmpty() &&
-          S3ConfigFile->ReadString(Section, AWS_SESSION_TOKEN, EmptyStr).IsEmpty())
-      {
-        Result->Delete(Index);
-      }
-      else
-      {
-        Index++;
-      }
-    }
-  }
-
+  std::unique_ptr<TStrings> Result(CreateSortedStringList());
+  GetS3Profiles(Result.get(), S3CredentialsFile.get());
+  GetS3Profiles(Result.get(), S3ConfigFile.get(), AWS_CONFIG_PROFILE_PREFIX);
   return Result.release();
 }
 //---------------------------------------------------------------------------
@@ -145,6 +162,29 @@ UnicodeString ReadUrl(const UnicodeString & Url)
   return Http->Response.Trim();
 }
 //---------------------------------------------------------------------------
+static UnicodeString GetS3ConfigValue(
+  TCustomIniFile * File, const UnicodeString & Profile, const UnicodeString & Name, const UnicodeString & Prefix, UnicodeString & Source)
+{
+  UnicodeString Result;
+  if (File != NULL)
+  {
+    UnicodeString ConfigSection = Profile;
+    if (!SameText(ConfigSection, AWS_PROFILE_DEFAULT))
+    {
+      ConfigSection = Prefix + ConfigSection;
+    }
+    // This is not consistent with AWS CLI.
+    // AWS CLI fails if one of aws_access_key_id or aws_secret_access_key is set and other is missing:
+    // "Partial credentials found in shared-credentials-file, missing: aws_secret_access_key"
+    Result = File->ReadString(ConfigSection, Name, EmptyStr);
+    if (!Result.IsEmpty())
+    {
+      Source = FORMAT(L"%s/%s", (ExtractFileName(File->FileName), Profile));
+    }
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
 UnicodeString GetS3ConfigValue(
   const UnicodeString & Profile, const UnicodeString & Name, const UnicodeString & CredentialsName, UnicodeString * Source)
 {
@@ -166,17 +206,11 @@ UnicodeString GetS3ConfigValue(
     {
       NeedS3Config();
 
-      if (S3ConfigFile.get() != NULL)
+      UnicodeString AProfile = DefaultStr(Profile, S3Profile);
+      Result = GetS3ConfigValue(S3CredentialsFile.get(), AProfile, Name, EmptyStr, ASource);
+      if (Result.IsEmpty())
       {
-        UnicodeString AProfile = DefaultStr(Profile, S3Profile);
-        // This is not consistent with AWS CLI.
-        // AWS CLI fails if one of aws_access_key_id or aws_secret_access_key is set and other is missing:
-        // "Partial credentials found in shared-credentials-file, missing: aws_secret_access_key"
-        Result = S3ConfigFile->ReadString(AProfile, Name, EmptyStr);
-        if (!Result.IsEmpty())
-        {
-          ASource = FORMAT(L"%s/%s", (ExtractFileName(S3ConfigFile->FileName), AProfile));
-        }
+        Result = GetS3ConfigValue(S3ConfigFile.get(), AProfile, Name, AWS_CONFIG_PROFILE_PREFIX, ASource);
       }
     }
   }