Browse Source

Fix the s3tokens endpoint

This was broken when issue_v2_token was removed, and no one noticed
because there are no tests.

The good news is, Swift3 is content to move toward supporting the
v3 format, so just start inheriting from the v3 controller.

Change-Id: I5d0c18121ba4bf8e33209daa48b9d87864951362
Related-Change: I7d3b583cbec9a095ab8cc20c5d6c0a6127e37068
Related-Change: I747de516ab69a47622eecbf8ab3faa34444b3ad5
(cherry picked from commit 3ec1aa4c19)
Tim Burke 2 years ago
parent
commit
5c34cb43d3

+ 15
- 15
keystone/contrib/ec2/controllers.py View File

@@ -254,6 +254,20 @@ class Ec2ControllerCommon(object):
254 254
                 message=_('EC2 access key not found.'))
255 255
         return self._convert_v3_to_ec2_credential(cred)
256 256
 
257
+    def render_token_data_response(self, token_id, token_data):
258
+        """Render token data HTTP response.
259
+
260
+        Stash token ID into the X-Subject-Token header.
261
+
262
+        """
263
+        status = (http_client.OK,
264
+                  http_client.responses[http_client.OK])
265
+        headers = [('X-Subject-Token', token_id)]
266
+
267
+        return wsgi.render_response(body=token_data,
268
+                                    status=status,
269
+                                    headers=headers)
270
+
257 271
 
258 272
 @dependency.requires('policy_api', 'token_provider_api')
259 273
 class Ec2Controller(Ec2ControllerCommon, controller.V2Controller):
@@ -374,7 +388,7 @@ class Ec2ControllerV3(Ec2ControllerCommon, controller.V3Controller):
374 388
 
375 389
         token_id, token_data = self.token_provider_api.issue_token(
376 390
             user_ref['id'], method_names, project_id=project_ref['id'])
377
-        return render_token_data_response(token_id, token_data)
391
+        return self.render_token_data_response(token_id, token_data)
378 392
 
379 393
     @controller.protected(callback=_check_credential_owner_and_user_id_match)
380 394
     def ec2_get_credential(self, request, user_id, credential_id):
@@ -409,17 +423,3 @@ class Ec2ControllerV3(Ec2ControllerCommon, controller.V3Controller):
409 423
             'credential_id': ref['access']}
410 424
         ref.setdefault('links', {})
411 425
         ref['links']['self'] = url
412
-
413
-
414
-def render_token_data_response(token_id, token_data):
415
-    """Render token data HTTP response.
416
-
417
-    Stash token ID into the X-Subject-Token header.
418
-
419
-    """
420
-    headers = [('X-Subject-Token', token_id)]
421
-
422
-    return wsgi.render_response(body=token_data,
423
-                                status=(http_client.OK,
424
-                                        http_client.responses[http_client.OK]),
425
-                                headers=headers)

+ 11
- 1
keystone/contrib/s3/core.py View File

@@ -26,6 +26,7 @@ import hashlib
26 26
 import hmac
27 27
 
28 28
 import six
29
+from six.moves import http_client
29 30
 
30 31
 from keystone.common import extension
31 32
 from keystone.common import json_home
@@ -66,7 +67,7 @@ class S3Extension(wsgi.V3ExtensionRouter):
66 67
                 's3tokens', '1.0', 's3tokens'))
67 68
 
68 69
 
69
-class S3Controller(controllers.Ec2Controller):
70
+class S3Controller(controllers.Ec2ControllerV3):
70 71
     def check_signature(self, creds_ref, credentials):
71 72
         string_to_sign = base64.urlsafe_b64decode(str(credentials['token']))
72 73
 
@@ -123,3 +124,12 @@ class S3Controller(controllers.Ec2Controller):
123 124
 
124 125
         signature = hmac.new(signed, string_to_sign, hashlib.sha256)
125 126
         return signature.hexdigest()
127
+
128
+    def render_token_data_response(self, token_id, token_data):
129
+        """Render token data HTTP response.
130
+
131
+        Note: We neither want nor need to send back the token id.
132
+        """
133
+        status = (http_client.OK,
134
+                  http_client.responses[http_client.OK])
135
+        return wsgi.render_response(body=token_data, status=status)

+ 52
- 1
keystone/tests/unit/test_contrib_s3_core.py View File

@@ -12,21 +12,72 @@
12 12
 # License for the specific language governing permissions and limitations
13 13
 # under the License.
14 14
 
15
+import base64
16
+import hashlib
17
+import hmac
15 18
 import uuid
16 19
 
20
+from six.moves import http_client
21
+
17 22
 from keystone.contrib import s3
18 23
 from keystone import exception
19 24
 from keystone.tests import unit
25
+from keystone.tests.unit import test_v3
20 26
 
21 27
 
22
-class S3ContribCore(unit.TestCase):
28
+class S3ContribCore(test_v3.RestfulTestCase):
23 29
     def setUp(self):
24 30
         super(S3ContribCore, self).setUp()
25 31
 
26 32
         self.load_backends()
27 33
 
34
+        self.cred_blob, self.credential = unit.new_ec2_credential(
35
+            self.user['id'], self.project_id)
36
+        self.credential_api.create_credential(
37
+            self.credential['id'], self.credential)
38
+
28 39
         self.controller = s3.S3Controller()
29 40
 
41
+    def test_good_response(self):
42
+        sts = 'string to sign'  # opaque string from swift3
43
+        sig = hmac.new(self.cred_blob['secret'].encode('ascii'),
44
+                       sts.encode('ascii'), hashlib.sha1).digest()
45
+        resp = self.post(
46
+            '/s3tokens',
47
+            body={'credentials': {
48
+                'access': self.cred_blob['access'],
49
+                'signature': base64.b64encode(sig).strip(),
50
+                'token': base64.b64encode(sts.encode('ascii')).strip(),
51
+            }},
52
+            expected_status=http_client.OK)
53
+        self.assertValidProjectScopedTokenResponse(resp, self.user,
54
+                                                   forbid_token_id=True)
55
+
56
+    def test_bad_request(self):
57
+        self.post(
58
+            '/s3tokens',
59
+            body={},
60
+            expected_status=http_client.BAD_REQUEST)
61
+
62
+        self.post(
63
+            '/s3tokens',
64
+            body="not json",
65
+            expected_status=http_client.BAD_REQUEST)
66
+
67
+        self.post(
68
+            '/s3tokens',
69
+            expected_status=http_client.BAD_REQUEST)
70
+
71
+    def test_bad_response(self):
72
+        self.post(
73
+            '/s3tokens',
74
+            body={'credentials': {
75
+                'access': self.cred_blob['access'],
76
+                'signature': base64.b64encode(b'totally not the sig').strip(),
77
+                'token': base64.b64encode(b'string to sign').strip(),
78
+            }},
79
+            expected_status=http_client.UNAUTHORIZED)
80
+
30 81
     def test_good_signature_v1(self):
31 82
         creds_ref = {'secret':
32 83
                      u'b121dd41cdcc42fe9f70e572e84295aa'}

+ 5
- 2
keystone/tests/unit/test_v3.py View File

@@ -637,8 +637,11 @@ class RestfulTestCase(unit.SQLDriverOverrides, rest.RestfulTestCase,
637 637
             msg = '%s is not a valid ISO 8601 extended format date time.' % dt
638 638
             raise AssertionError(msg)
639 639
 
640
-    def assertValidTokenResponse(self, r, user=None):
641
-        self.assertTrue(r.headers.get('X-Subject-Token'))
640
+    def assertValidTokenResponse(self, r, user=None, forbid_token_id=False):
641
+        if forbid_token_id:
642
+            self.assertNotIn('X-Subject-Token', r.headers)
643
+        else:
644
+            self.assertTrue(r.headers.get('X-Subject-Token'))
642 645
         token = r.result['token']
643 646
 
644 647
         self.assertIsNotNone(token.get('expires_at'))

Loading…
Cancel
Save