File: bug-2119646-swift.patch

package info (click to toggle)
swift 2.36.0-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 23,092 kB
  • sloc: python: 286,210; javascript: 1,059; sh: 619; pascal: 295; makefile: 81; xml: 32
file content (124 lines) | stat: -rw-r--r-- 5,640 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
Description: Swift patch for LP: 2119646
Author: Tim Burke
Origin: upstream, https://bugs.launchpad.net/keystone/+bug/2119646 comment 28
Last-Update: 2025-10-31

diff --git a/swift/common/middleware/s3api/s3token.py b/swift/common/middleware/s3api/s3token.py
index 0c3f15d89..b1a3ca476 100644
--- a/swift/common/middleware/s3api/s3token.py
+++ b/swift/common/middleware/s3api/s3token.py
@@ -180,31 +180,45 @@ class S3Token(object):
         self._secret_cache_duration = int(conf.get('secret_cache_duration', 0))
         if self._secret_cache_duration < 0:
             raise ValueError('secret_cache_duration must be non-negative')
-        if self._secret_cache_duration:
-            try:
-                auth_plugin = keystone_loading.get_plugin_loader(
-                    conf.get('auth_type', 'password'))
-                available_auth_options = auth_plugin.get_options()
-                auth_options = {}
-                for option in available_auth_options:
-                    name = option.name.replace('-', '_')
-                    value = conf.get(name)
-                    if value:
-                        auth_options[name] = value
 
+        # Service authentication for s3tokens API calls
+        self.keystoneclient = None
+        try:
+            auth_plugin = keystone_loading.get_plugin_loader(
+                conf.get('auth_type', 'password'))
+            available_auth_options = auth_plugin.get_options()
+            auth_options = {}
+            for option in available_auth_options:
+                name = option.name.replace('-', '_')
+                value = conf.get(name)
+                if value:
+                    auth_options[name] = value
+
+            if not auth_options:
+                self._logger.warning(
+                    "No service auth configuration. "
+                    "s3tokens API calls will be unauthenticated. "
+                    "New versions of keystone require service auth.")
+            else:
                 auth = auth_plugin.load_from_options(**auth_options)
                 session = keystone_session.Session(auth=auth)
                 self.keystoneclient = keystone_client.Client(
                     session=session,
                     region_name=conf.get('region_name'))
-                self._logger.info("Caching s3tokens for %s seconds",
-                                  self._secret_cache_duration)
-            except Exception:
-                self._logger.warning("Unable to load keystone auth_plugin. "
-                                     "Secret caching will be unavailable.",
-                                     exc_info=True)
-                self.keystoneclient = None
-                self._secret_cache_duration = 0
+                self._logger.info(
+                    "Service authentication configured for s3tokens API")
+        except Exception:
+            self._logger.warning(
+                "Unable to load service auth configuration. "
+                "s3tokens API calls will be unauthenticated "
+                "and secret caching will be unavailable.",
+                exc_info=True)
+
+        if self._secret_cache_duration and self.keystoneclient:
+            self._logger.info("Caching s3tokens for %s seconds",
+                              self._secret_cache_duration)
+        else:
+            self._secret_cache_duration = 0
 
     def _deny_request(self, code):
         error_cls, message = {
@@ -222,6 +236,16 @@ class S3Token(object):
 
     def _json_request(self, creds_json):
         headers = {'Content-Type': 'application/json'}
+
+        # Add service authentication headers if configured
+        if self.keystoneclient:
+            try:
+                headers.update(
+                    self.keystoneclient.session.get_auth_headers())
+            except Exception:
+                self._logger.warning("Failed to get service token",
+                                     exc_info=True)
+
         try:
             response = requests.post(self._request_uri,
                                      headers=headers, data=creds_json,
diff --git a/test/unit/common/middleware/s3api/test_s3token.py b/test/unit/common/middleware/s3api/test_s3token.py
index ee02c243d..2ff197ff0 100644
--- a/test/unit/common/middleware/s3api/test_s3token.py
+++ b/test/unit/common/middleware/s3api/test_s3token.py
@@ -589,6 +589,9 @@ class S3TokenMiddlewareTestGood(S3TokenMiddlewareTestBase):
         cache.get.return_value = None
 
         keystone_client = MOCK_KEYSTONE.return_value
+        keystone_client.session.get_auth_headers.return_value = {
+            'X-Auth-Token': 'bearer token',
+        }
         keystone_client.ec2.get.return_value = mock.Mock(secret='secret')
 
         MOCK_REQUEST.return_value = FakeResponse({
@@ -615,6 +618,18 @@ class S3TokenMiddlewareTestGood(S3TokenMiddlewareTestBase):
         }
 
         self.assertTrue(MOCK_REQUEST.called)
+        self.assertEqual(MOCK_REQUEST.mock_calls, [
+            mock.call('http://example.com/s3tokens', headers={
+                'Content-Type': 'application/json',
+                'X-Auth-Token': 'bearer token',
+            }, data=json.dumps({
+                "credentials": {
+                    "access": "access",
+                    "token": "dG9rZW4=",
+                    "signature": "signature",
+                }
+            }), verify=None, timeout=10.0)
+        ])
         tenant = GOOD_RESPONSE_V2['access']['token']['tenant']
         expected_cache = (expected_headers, tenant, 'secret')
         cache.set.assert_called_once_with('s3secret/access', expected_cache,