Repository: boto/botocore
Pull request #989 (2016-07-28 20:46:13)

Raise error on partial creds provided
This builds on #987 to make this condition an error case.
A user providing just an access key or secret key is never
valid and should result in an error.

This is also consistent with other cred providers that raise
PartialCredentialsError for partial creds.

https://github.com/boto/boto3/issues/686

cc @kyleknap @JordonPhillips 


Patch:
diff --git a/.changes/next-release/bugfix-Credentials-86559.json b/.changes/next-release/bugfix-Credentials-86559.json
new file mode 100644
index 000000000..e4d4972e7
--- /dev/null
+++ b/.changes/next-release/bugfix-Credentials-86559.json
@@ -0,0 +1,5 @@
+{
+  "category": "Credentials", 
+  "type": "bugfix", 
+  "description": "Raise error when partial hard coded creds are provided when creating a client."
+}
diff --git a/botocore/session.py b/botocore/session.py
index 67da55fc9..2fb8c1c97 100644
--- a/botocore/session.py
+++ b/botocore/session.py
@@ -26,7 +26,7 @@ import botocore.configloader
 import botocore.credentials
 import botocore.client
 from botocore.exceptions import ConfigNotFound, ProfileNotFound
-from botocore.exceptions import UnknownServiceError
+from botocore.exceptions import UnknownServiceError, PartialCredentialsError
 from botocore import handlers
 from botocore.hooks import HierarchicalEmitter, first_non_none_response
 from botocore.loaders import create_loader
@@ -800,11 +800,17 @@ class Session(object):
         event_emitter = self.get_component('event_emitter')
         response_parser_factory = self.get_component(
             'response_parser_factory')
-        if None not in (aws_secret_access_key, aws_access_key_id):
+        if aws_access_key_id is not None and aws_secret_access_key is not None:
             credentials = botocore.credentials.Credentials(
                 access_key=aws_access_key_id,
                 secret_key=aws_secret_access_key,
                 token=aws_session_token)
+        elif self._missing_cred_vars(aws_access_key_id,
+                                     aws_secret_access_key):
+            raise PartialCredentialsError(
+                provider='explicit',
+                cred_var=self._missing_cred_vars(aws_access_key_id,
+                                                 aws_secret_access_key))
         else:
             credentials = self.get_credentials()
         endpoint_resolver = self.get_component('endpoint_resolver')
@@ -818,6 +824,13 @@ class Session(object):
             client_config=config, api_version=api_version)
         return client
 
+    def _missing_cred_vars(self, access_key, secret_key):
+        if access_key is not None and secret_key is None:
+            return 'aws_secret_access_key'
+        if secret_key is not None and access_key is None:
+            return 'aws_access_key_id'
+        return None
+
     def get_available_partitions(self):
         """Lists the available partitions found on disk
 


Test patch:
diff --git a/tests/integration/test_credentials.py b/tests/integration/test_credentials.py
index f9b65c3f3..78c64814f 100644
--- a/tests/integration/test_credentials.py
+++ b/tests/integration/test_credentials.py
@@ -122,17 +122,3 @@ class TestCredentialPrecedence(BaseEnvVar):
 
             self.assertEqual(credentials.access_key, 'custom1')
             self.assertEqual(credentials.secret_key, 'custom2')
-
-    def test_none_type_credentials(self):
-        # Test to ensure we catch when either access key id or secret are None
-        # and we try to use a client made with those bad credentials
-        s = Session()
-        client = s.create_client('s3', aws_access_key_id=None,
-                                 aws_secret_access_key='secret')
-        with self.assertRaises(botocore.exceptions.NoCredentialsError):
-            client.list_buckets()
-
-        client = s.create_client('s3', aws_access_key_id='access',
-                                 aws_secret_access_key=None)
-        with self.assertRaises(botocore.exceptions.NoCredentialsError):
-            client.list_buckets()
diff --git a/tests/unit/test_session.py b/tests/unit/test_session.py
index e3bba7792..ac88eead8 100644
--- a/tests/unit/test_session.py
+++ b/tests/unit/test_session.py
@@ -432,6 +432,20 @@ class TestCreateClient(BaseSessionTest):
                          "explicit credentials were provided to the "
                          "create_client call.")
 
+    def test_cred_provider_called_when_partial_creds_provided(self):
+        with self.assertRaises(botocore.exceptions.PartialCredentialsError):
+            self.session.create_client(
+                'sts', 'us-west-2',
+                aws_access_key_id='foo',
+                aws_secret_access_key=None
+            )
+        with self.assertRaises(botocore.exceptions.PartialCredentialsError):
+            self.session.create_client(
+                'sts', 'us-west-2',
+                aws_access_key_id=None,
+                aws_secret_access_key='foo',
+            )
+
     @mock.patch('botocore.client.ClientCreator')
     def test_config_passed_to_client_creator(self, client_creator):
         # Make sure there is no default set