Repository: minio/minio-py
Pull request #1174 (2022-01-31 16:50:47)

fix EnvAWSProvider/EnvMinioProvider to fetch access/secret keys and session token always from os.environ
Some providers, eg, EnvAWSProvider and EnvMinioProvider, creates the Credential object during __init__. However, if a key is None, a ValueError is raised. Therefore, when these providers are used within a ChainedProvider, the exception is raised before actually retrieving the credentials.

This bug is highlighted by the new test ChainedProviderTest.test_chain_retrieve_second, which fails without the proposed fix.


Patch:
diff --git a/minio/credentials/providers.py b/minio/credentials/providers.py
index 44ad97b..fb2821b 100644
--- a/minio/credentials/providers.py
+++ b/minio/credentials/providers.py
@@ -205,38 +205,30 @@ class ChainedProvider(Provider):
 class EnvAWSProvider(Provider):
     """Credential provider from AWS environment variables."""
 
-    def __init__(self):
-        access_key = (
-            os.environ.get("AWS_ACCESS_KEY_ID") or
-            os.environ.get("AWS_ACCESS_KEY")
-        )
-        secret_key = (
-            os.environ.get("AWS_SECRET_ACCESS_KEY") or
-            os.environ.get("AWS_SECRET_KEY")
-        )
-        self._credentials = Credentials(
-            access_key,
-            secret_key,
-            session_token=os.environ.get("AWS_SESSION_TOKEN"),
-        )
-
     def retrieve(self):
         """Retrieve credentials."""
-        return self._credentials
+        return Credentials(
+            access_key=(
+                os.environ.get("AWS_ACCESS_KEY_ID") or
+                os.environ.get("AWS_ACCESS_KEY")
+            ),
+            secret_key=(
+                os.environ.get("AWS_SECRET_ACCESS_KEY") or
+                os.environ.get("AWS_SECRET_KEY")
+            ),
+            session_token=os.environ.get("AWS_SESSION_TOKEN"),
+        )
 
 
 class EnvMinioProvider(Provider):
     """Credential provider from MinIO environment variables."""
 
-    def __init__(self):
-        self._credentials = Credentials(
-            os.environ.get("MINIO_ACCESS_KEY"),
-            os.environ.get("MINIO_SECRET_KEY"),
-        )
-
     def retrieve(self):
         """Retrieve credentials."""
-        return self._credentials
+        return Credentials(
+            access_key=os.environ.get("MINIO_ACCESS_KEY"),
+            secret_key=os.environ.get("MINIO_SECRET_KEY"),
+        )
 
 
 class AWSConfigProvider(Provider):


Test patch:
diff --git a/tests/unit/credentials_test.py b/tests/unit/credentials_test.py
index 7fe0cca..b04f028 100644
--- a/tests/unit/credentials_test.py
+++ b/tests/unit/credentials_test.py
@@ -94,13 +94,33 @@ class ChainedProviderTest(TestCase):
                 EnvAWSProvider(), EnvMinioProvider(),
             ]
         )
-        # retireve provider (env_aws) has priority
+        # retrieve provider (env_aws) has priority
         creds = provider.retrieve()
         # assert provider credentials
         self.assertEqual(creds.access_key, "access_aws")
         self.assertEqual(creds.secret_key, "secret_aws")
         self.assertEqual(creds.session_token, "token_aws")
 
+    def test_chain_retrieve_failed_provider(self):
+        # clear environment
+        os.environ.clear()
+        # prepare env for env_minio
+        os.environ["MINIO_ACCESS_KEY"] = "access_minio"
+        os.environ["MINIO_SECRET_KEY"] = "secret_minio"
+        # create chain provider with env_aws and env_minio providers
+
+        provider = ChainedProvider(
+            [
+                EnvAWSProvider(), EnvMinioProvider(),
+            ]
+        )
+        # retrieve provider: (env_minio) will be retrieved
+        creds = provider.retrieve()
+        # assert provider credentials
+        self.assertEqual(creds.access_key, "access_minio")
+        self.assertEqual(creds.secret_key, "secret_minio")
+        self.assertEqual(creds.session_token, None)
+
 
 class EnvAWSProviderTest(TestCase):
     def test_env_aws_retrieve(self):