Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-keystonemiddleware for 
openSUSE:Factory checked in at 2026-03-10 17:56:22
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-keystonemiddleware (Old)
 and      /work/SRC/openSUSE:Factory/.python-keystonemiddleware.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-keystonemiddleware"

Tue Mar 10 17:56:22 2026 rev:22 rq:1337889 version:12.0.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-keystonemiddleware/python-keystonemiddleware.changes
      2026-01-19 18:42:36.393233683 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-keystonemiddleware.new.8177/python-keystonemiddleware.changes
    2026-03-10 17:59:35.303091450 +0100
@@ -1,0 +2,20 @@
+Tue Mar 10 07:55:13 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 12.0.0:
+  * Remove ec2\_token and s3\_token middleware
+  * Ignore B105 error
+  * Fix privilege escalation via spoofed identity headers
+  * Drop workaround for Python < 2.7.4
+  * Add cryptography package as an optional dependency
+  * Revert "Wipe context before returning request response"
+  * s3token: Stop loading deprecated options
+  * reno: Update master for unmaintained/2024.1
+  * Wipe context before returning request response
+  * Drop flake8-docstrings
+  * Remove unused bandit target
+  * Update master for stable/2025.2
+- drop
+  0001-Fix-privilege-escalation-via-spoofed-identity-header.patch
+  (upstream)
+
+-------------------------------------------------------------------

Old:
----
  0001-Fix-privilege-escalation-via-spoofed-identity-header.patch
  keystonemiddleware-10.12.0.tar.gz

New:
----
  keystonemiddleware-12.0.0.tar.gz

----------(Old B)----------
  Old:- drop
  0001-Fix-privilege-escalation-via-spoofed-identity-header.patch
  (upstream)
----------(Old E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-keystonemiddleware.spec ++++++
--- /var/tmp/diff_new_pack.H9KYNF/_old  2026-03-10 17:59:36.315133059 +0100
+++ /var/tmp/diff_new_pack.H9KYNF/_new  2026-03-10 17:59:36.319133223 +0100
@@ -17,15 +17,13 @@
 
 
 Name:           python-keystonemiddleware
-Version:        10.12.0
+Version:        12.0.0
 Release:        0
 Summary:        Middleware for OpenStack Identity
 License:        Apache-2.0
 Group:          Development/Languages/Python
 URL:            https://docs.openstack.org/keystonemiddleware
 Source0:        
https://files.pythonhosted.org/packages/source/k/keystonemiddleware/keystonemiddleware-%{version}.tar.gz
-# PATCH-FIX-UPSTREAM
-Patch1:         0001-Fix-privilege-escalation-via-spoofed-identity-header.patch
 BuildRequires:  %{python_module WebOb >= 1.7.1}
 BuildRequires:  %{python_module WebTest}
 BuildRequires:  %{python_module cryptography}

++++++ keystonemiddleware-10.12.0.tar.gz -> keystonemiddleware-12.0.0.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/keystonemiddleware-10.12.0/AUTHORS 
new/keystonemiddleware-12.0.0/AUTHORS
--- old/keystonemiddleware-10.12.0/AUTHORS      2025-08-21 11:34:47.000000000 
+0200
+++ new/keystonemiddleware-12.0.0/AUTHORS       2026-02-17 15:49:49.000000000 
+0100
@@ -1,4 +1,5 @@
 Abhishek Sharma <[email protected]>
+Adam Oswick <[email protected]>
 Adam Young <[email protected]>
 Alan Pevec <[email protected]>
 Alexander Makarov <[email protected]>
@@ -12,6 +13,7 @@
 Andrey Pavlov <[email protected]>
 Anh Tran <[email protected]>
 Anthony Young <[email protected]>
+Arnaud Morin <[email protected]>
 Artem Vasilyev <[email protected]>
 Arun Kant <[email protected]>
 Ayumu Ueha <[email protected]>
@@ -70,6 +72,7 @@
 Gage Hugo <[email protected]>
 Ghanshyam Mann <[email protected]>
 Gordon Chung <[email protected]>
+Grzegorz Grasza <[email protected]>
 Guang Yee <[email protected]>
 Guang Yee <[email protected]>
 Guang Yee <[email protected]>
@@ -101,6 +104,7 @@
 Kevin Benton <[email protected]>
 Kevin L. Mitchell <[email protected]>
 Kieran Spear <[email protected]>
+Koya Watanabe <[email protected]>
 Kristi Nikolla <[email protected]>
 Kui Shi <[email protected]>
 Kun Huang <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/keystonemiddleware-10.12.0/ChangeLog 
new/keystonemiddleware-12.0.0/ChangeLog
--- old/keystonemiddleware-10.12.0/ChangeLog    2025-08-21 11:34:47.000000000 
+0200
+++ new/keystonemiddleware-12.0.0/ChangeLog     2026-02-17 15:49:49.000000000 
+0100
@@ -1,6 +1,26 @@
 CHANGES
 =======
 
+12.0.0
+------
+
+* Remove ec2\_token and s3\_token middleware
+* Ignore B105 error
+
+11.0.0
+------
+
+* Fix privilege escalation via spoofed identity headers
+* Drop workaround for Python < 2.7.4
+* Add cryptography package as an optional dependency
+* Revert "Wipe context before returning request response"
+* s3token: Stop loading deprecated options
+* reno: Update master for unmaintained/2024.1
+* Wipe context before returning request response
+* Drop flake8-docstrings
+* Remove unused bandit target
+* Update master for stable/2025.2
+
 10.12.0
 -------
 
@@ -63,6 +83,7 @@
 
 * reno: Update master for unmaintained/yoga
 * tox: Drop envdir
+* Replace CRLF by LF
 * Update python classifier in setup.cfg
 * Remove unnecessary setup\_hook
 * Python 3.12: do not use utcnow()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/keystonemiddleware-10.12.0/PKG-INFO 
new/keystonemiddleware-12.0.0/PKG-INFO
--- old/keystonemiddleware-10.12.0/PKG-INFO     2025-08-21 11:34:47.926037800 
+0200
+++ new/keystonemiddleware-12.0.0/PKG-INFO      2026-02-17 15:49:50.080432700 
+0100
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: keystonemiddleware
-Version: 10.12.0
+Version: 12.0.0
 Summary: Middleware for OpenStack Identity
 Home-page: https://docs.openstack.org/keystonemiddleware/latest/
 Author: OpenStack
@@ -35,9 +35,10 @@
 Requires-Dist: WebOb>=1.7.1
 Provides-Extra: audit-notifications
 Requires-Dist: oslo.messaging>=5.29.0; extra == "audit-notifications"
+Provides-Extra: memcache-encryption
+Requires-Dist: cryptography>=2.7; extra == "memcache-encryption"
 Provides-Extra: test
 Requires-Dist: hacking~=6.1.0; extra == "test"
-Requires-Dist: flake8-docstrings~=1.7.0; extra == "test"
 Requires-Dist: coverage>=4.0; extra == "test"
 Requires-Dist: cryptography>=3.0; extra == "test"
 Requires-Dist: fixtures>=3.0.0; extra == "test"
@@ -53,6 +54,16 @@
 Requires-Dist: oslo.messaging>=5.29.0; extra == "test"
 Requires-Dist: PyJWT>=2.4.0; extra == "test"
 Requires-Dist: bandit>=1.1.0; extra == "test"
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: home-page
+Dynamic: license-file
+Dynamic: provides-extra
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
 
 ========================
 Team and repository tags
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/doc/source/installation.rst 
new/keystonemiddleware-12.0.0/doc/source/installation.rst
--- old/keystonemiddleware-10.12.0/doc/source/installation.rst  2025-08-21 
11:33:29.000000000 +0200
+++ new/keystonemiddleware-12.0.0/doc/source/installation.rst   2026-02-17 
15:48:39.000000000 +0100
@@ -23,3 +23,7 @@
 To install support for audit notifications::
 
     $ pip install keystonemiddleware[audit_notifications]
+
+To install support for memcache encryption::
+
+    $ pip install keystonemiddleware[memcache_encryption]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware/ec2_token.py 
new/keystonemiddleware-12.0.0/keystonemiddleware/ec2_token.py
--- old/keystonemiddleware-10.12.0/keystonemiddleware/ec2_token.py      
2025-08-21 11:33:29.000000000 +0200
+++ new/keystonemiddleware-12.0.0/keystonemiddleware/ec2_token.py       
1970-01-01 01:00:00.000000000 +0100
@@ -1,213 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# Copyright 2010 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""Starting point for routing EC2 requests."""
-
-import hashlib
-
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_serialization import jsonutils
-import requests
-import webob.dec
-
-from keystonemiddleware.i18n import _
-
-
-keystone_ec2_opts = [
-    cfg.StrOpt('url',
-               default='http://localhost:5000/v3/ec2tokens',
-               help='URL to get token from ec2 request.'),
-    cfg.StrOpt('keyfile',
-               help='Required if EC2 server requires client certificate.'),
-    cfg.StrOpt('certfile',
-               help='Client certificate key filename. Required if EC2 server '
-                    'requires client certificate.'),
-    cfg.StrOpt('cafile',
-               help='A PEM encoded certificate authority to use when '
-                    'verifying HTTPS connections. Defaults to the system '
-                    'CAs.'),
-    cfg.BoolOpt('insecure', default=False,
-                help='Disable SSL certificate verification.'),
-    cfg.IntOpt('timeout', default=60,
-               help='Timeout to obtain token.'),
-]
-
-CONF = cfg.CONF
-CONF.register_opts(keystone_ec2_opts, group='keystone_ec2_token')
-
-
-PROTOCOL_NAME = 'EC2 Token Authentication'
-
-
-class EC2Token(object):
-    """Authenticate an EC2 request with keystone and convert to token."""
-
-    def __init__(self, application, conf):
-        super(EC2Token, self).__init__()
-        self._application = application
-        self._logger = logging.getLogger(conf.get('log_name', __name__))
-        self._logger.debug('Starting the %s component', PROTOCOL_NAME)
-
-    def _ec2_error_response(self, code, message):
-        """Helper to construct an EC2 compatible error message."""
-        self._logger.debug('EC2 error response: %(code)s: %(message)s',
-                           {'code': code, 'message': message})
-        resp = webob.Response()
-        resp.status = 400
-        resp.headers['Content-Type'] = 'text/xml'
-        error_msg = str('<?xml version="1.0"?>\n'
-                        '<Response><Errors><Error><Code>%s</Code>'
-                        '<Message>%s</Message></Error></Errors></Response>' %
-                        (code, message))
-        error_msg = error_msg.encode()
-        resp.body = error_msg
-        return resp
-
-    def _get_signature(self, req):
-        """Extract the signature from the request.
-
-        This can be a get/post variable or for version 4 also in a header
-        called 'Authorization'.
-        - params['Signature'] == version 0,1,2,3
-        - params['X-Amz-Signature'] == version 4
-        - header 'Authorization' == version 4
-        """
-        sig = req.params.get('Signature') or req.params.get('X-Amz-Signature')
-        if sig is None and 'Authorization' in req.headers:
-            auth_str = req.headers['Authorization']
-            sig = auth_str.partition("Signature=")[2].split(',')[0]
-
-        return sig
-
-    def _get_access(self, req):
-        """Extract the access key identifier.
-
-        For version 0/1/2/3 this is passed as the AccessKeyId parameter, for
-        version 4 it is either an X-Amz-Credential parameter or a Credential=
-        field in the 'Authorization' header string.
-        """
-        access = req.params.get('AWSAccessKeyId')
-        if access is None:
-            cred_param = req.params.get('X-Amz-Credential')
-            if cred_param:
-                access = cred_param.split("/")[0]
-
-        if access is None and 'Authorization' in req.headers:
-            auth_str = req.headers['Authorization']
-            cred_str = auth_str.partition("Credential=")[2].split(',')[0]
-            access = cred_str.split("/")[0]
-
-        return access
-
-    @webob.dec.wsgify()
-    def __call__(self, req):
-        # NOTE(alevine): We need to calculate the hash here because
-        # subsequent access to request modifies the req.body so the hash
-        # calculation will yield invalid results.
-        body_hash = hashlib.sha256(req.body).hexdigest()
-
-        signature = self._get_signature(req)
-        if not signature:
-            msg = _("Signature not provided")
-            return self._ec2_error_response("AuthFailure", msg)
-        access = self._get_access(req)
-        if not access:
-            msg = _("Access key not provided")
-            return self._ec2_error_response("AuthFailure", msg)
-
-        if 'X-Amz-Signature' in req.params or 'Authorization' in req.headers:
-            auth_params = {}
-        else:
-            # Make a copy of args for authentication and signature verification
-            auth_params = dict(req.params)
-            # Not part of authentication args
-            auth_params.pop('Signature', None)
-
-        headers = req.headers
-        # NOTE(andrey-mp): jsonutils dumps it as list of keys without
-        # conversion instead real dict
-        headers = {k: headers[k] for k in headers}
-        cred_dict = {
-            'access': access,
-            'signature': signature,
-            'host': req.host,
-            'verb': req.method,
-            'path': req.path,
-            'params': auth_params,
-            'headers': headers,
-            'body_hash': body_hash
-        }
-        if "ec2" in CONF.keystone_ec2_token.url:
-            creds = {'ec2Credentials': cred_dict}
-        else:
-            creds = {'auth': {'OS-KSEC2:ec2Credentials': cred_dict}}
-        creds_json = jsonutils.dumps(creds)
-        headers = {'Content-Type': 'application/json'}
-
-        verify = not CONF.keystone_ec2_token.insecure
-        if verify and CONF.keystone_ec2_token.cafile:
-            verify = CONF.keystone_ec2_token.cafile
-
-        cert = None
-        if (CONF.keystone_ec2_token.certfile and
-                CONF.keystone_ec2_token.keyfile):
-            cert = (CONF.keystone_ec2_certfile,
-                    CONF.keystone_ec2_token.keyfile)
-        elif CONF.keystone_ec2_token.certfile:
-            cert = CONF.keystone_ec2_token.certfile
-
-        response = requests.post(CONF.keystone_ec2_token.url,
-                                 data=creds_json, headers=headers,
-                                 verify=verify, cert=cert,
-                                 timeout=CONF.keystone_ec2_token.timeout)
-
-        # NOTE(vish): We could save a call to keystone by
-        #             having keystone return token, tenant,
-        #             user, and roles from this call.
-
-        status_code = response.status_code
-        if status_code != 200:
-            msg = _('Error response from keystone: %s') % response.reason
-            self._logger.debug(msg)
-            return self._ec2_error_response("AuthFailure", msg)
-        try:
-            token_id = response.headers['x-subject-token']
-        except (AttributeError, KeyError):
-            msg = _("Failure parsing response from keystone")
-            self._logger.exception(msg)
-            return self._ec2_error_response("AuthFailure", msg)
-
-        # Authenticated!
-        req.headers['X-Auth-Token'] = token_id
-        return self._application
-
-
-def filter_factory(global_conf, **local_conf):
-    """Return a WSGI filter app for use with paste.deploy."""
-    conf = global_conf.copy()
-    conf.update(local_conf)
-
-    def auth_filter(app):
-        return EC2Token(app, conf)
-    return auth_filter
-
-
-def app_factory(global_conf, **local_conf):
-    conf = global_conf.copy()
-    conf.update(local_conf)
-    return EC2Token(None, conf)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware/echo/service.py 
new/keystonemiddleware-12.0.0/keystonemiddleware/echo/service.py
--- old/keystonemiddleware-10.12.0/keystonemiddleware/echo/service.py   
2025-08-21 11:33:29.000000000 +0200
+++ new/keystonemiddleware-12.0.0/keystonemiddleware/echo/service.py    
2026-02-17 15:48:39.000000000 +0100
@@ -43,7 +43,7 @@
 
     def __init__(self):
         # hardcode any non-default configuration here
-        conf = {'auth_protocol': 'http', 'admin_token': 'ADMIN'}
+        conf = {'auth_protocol': 'http', 'admin_token': 'ADMIN'}  # nosec: B105
         app = auth_token.AuthProtocol(echo_app, conf)
         server = simple_server.make_server('', 8000, app)
         print('Serving on port 8000 (Ctrl+C to end)...')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware/external_oauth2_token.py 
new/keystonemiddleware-12.0.0/keystonemiddleware/external_oauth2_token.py
--- old/keystonemiddleware-10.12.0/keystonemiddleware/external_oauth2_token.py  
2025-08-21 11:33:29.000000000 +0200
+++ new/keystonemiddleware-12.0.0/keystonemiddleware/external_oauth2_token.py   
2026-02-17 15:48:39.000000000 +0100
@@ -33,6 +33,7 @@
 
 from keystonemiddleware._common import config
 from keystonemiddleware.auth_token import _cache
+from keystonemiddleware.auth_token import _request
 from keystonemiddleware.exceptions import ConfigurationError
 from keystonemiddleware.exceptions import KeystoneMiddlewareException
 from keystonemiddleware.i18n import _
@@ -246,7 +247,7 @@
         the auth method 'client_secret_basic'.
         """
         req_data = {'token': access_token,
-                    'token_type_hint': 'access_token'}
+                    'token_type_hint': 'access_token'}  # nosec: B105
         auth = requests.auth.HTTPBasicAuth(self.client_id,
                                            self.client_secret)
         http_response = self.session.request(
@@ -279,7 +280,7 @@
             'client_id': self.client_id,
             'client_secret': self.client_secret,
             'token': access_token,
-            'token_type_hint': 'access_token'
+            'token_type_hint': 'access_token'  # nosec: B105
         }
         http_response = self.session.request(
             self.introspect_endpoint,
@@ -301,7 +302,7 @@
         req_data = {
             'client_id': self.client_id,
             'token': access_token,
-            'token_type_hint': 'access_token'
+            'token_type_hint': 'access_token'  # nosec: B105
         }
         http_response = self.session.request(
             self.introspect_endpoint,
@@ -382,7 +383,7 @@
                 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
             'client_assertion': client_assertion,
             'token': access_token,
-            'token_type_hint': 'access_token'
+            'token_type_hint': 'access_token'  # nosec: B105
         }
         http_response = self.session.request(
             self.introspect_endpoint,
@@ -442,7 +443,7 @@
                 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
             'client_assertion': client_assertion,
             'token': access_token,
-            'token_type_hint': 'access_token'
+            'token_type_hint': 'access_token'  # nosec: B105
         }
         http_response = self.session.request(
             self.introspect_endpoint,
@@ -534,7 +535,7 @@
                                            **cache_kwargs)
         return _cache.TokenCache(self._log, **cache_kwargs)
 
-    @webob.dec.wsgify()
+    @webob.dec.wsgify(RequestClass=_request._AuthTokenRequest)
     def __call__(self, req):
         """Handle incoming request."""
         self.process_request(req)
@@ -545,8 +546,10 @@
         """Process request.
 
         :param request: Incoming request
-        :type request: _request.AuthTokenRequest
+        :type request: _request._AuthTokenRequest
         """
+        request.remove_auth_headers()
+
         access_token = None
         if (request.authorization and
                 request.authorization.authtype == 'Bearer'):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware/s3_token.py 
new/keystonemiddleware-12.0.0/keystonemiddleware/s3_token.py
--- old/keystonemiddleware-10.12.0/keystonemiddleware/s3_token.py       
2025-08-21 11:33:29.000000000 +0200
+++ new/keystonemiddleware-12.0.0/keystonemiddleware/s3_token.py        
1970-01-01 01:00:00.000000000 +0100
@@ -1,238 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# Copyright 2010 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
-# Copyright 2011,2012 Akira YOSHIYAMA <[email protected]>
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-# This source code is based ./auth_token.py and ./ec2_token.py.
-# See them for their copyright.
-
-"""
-S3 Token Middleware.
-
-This WSGI component:
-
-* Gets a request from the swift3 middleware with an S3 Authorization
-  access key.
-* Validates s3 token in Keystone.
-* Transforms the account name to AUTH_%(tenant_name).
-
-"""
-
-import webob
-
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_serialization import jsonutils
-from oslo_utils import strutils
-import requests
-
-s3_opts = [
-    cfg.IntOpt('timeout', default=60,
-               help='Timeout to obtain token.'),
-]
-
-CONF = cfg.CONF
-CONF.register_opts(s3_opts, group='s3_token')
-
-PROTOCOL_NAME = 'S3 Token Authentication'
-
-
-class ServiceError(Exception):
-    pass
-
-
-class S3Token(object):
-    """Middleware that handles S3 authentication."""
-
-    def __init__(self, app, conf):
-        """Common initialization code."""
-        self._app = app
-        self._logger = logging.getLogger(conf.get('log_name', __name__))
-        self._logger.debug('Starting the %s component', PROTOCOL_NAME)
-        self._reseller_prefix = conf.get('reseller_prefix', 'AUTH_')
-        # where to find the auth service (we use this to validate tokens)
-
-        self._request_uri = conf.get('www_authenticate_uri')
-        auth_uri = conf.get('auth_uri')
-        if not self._request_uri and auth_uri:
-            self._logger.warning(
-                "Use of the auth_uri option was deprecated "
-                "in the Queens release in favor of www_authenticate_uri. This "
-                "option will be removed in the S release.")
-            self._request_uri = auth_uri
-        if not self._request_uri:
-            self._logger.warning(
-                "Use of the auth_host, auth_port, and auth_protocol "
-                "configuration options was deprecated in the Newton release "
-                "in favor of www_authenticate_uri. These options will be "
-                "removed in the S release.")
-            auth_host = conf.get('auth_host')
-            auth_port = int(conf.get('auth_port', 35357))
-            auth_protocol = conf.get('auth_protocol', 'https')
-
-            self._request_uri = '%s://%s:%s' % (auth_protocol, auth_host,
-                                                auth_port)
-
-        # SSL
-        insecure = strutils.bool_from_string(conf.get('insecure', False))
-        cert_file = conf.get('certfile')
-        key_file = conf.get('keyfile')
-
-        if insecure:
-            self._verify = False
-        elif cert_file and key_file:
-            self._verify = (cert_file, key_file)
-        elif cert_file:
-            self._verify = cert_file
-        else:
-            self._verify = None
-
-    def _deny_request(self, code):
-        error_table = {
-            'AccessDenied': (401, 'Access denied'),
-            'InvalidURI': (400, 'Could not parse the specified URI'),
-        }
-        resp = webob.Response(content_type='text/xml')
-        resp.status = error_table[code][0]
-        error_msg = ('<?xml version="1.0" encoding="UTF-8"?>\r\n'
-                     '<Error>\r\n  <Code>%s</Code>\r\n  '
-                     '<Message>%s</Message>\r\n</Error>\r\n' %
-                     (code, error_table[code][1]))
-        error_msg = error_msg.encode()
-        resp.body = error_msg
-        return resp
-
-    def _json_request(self, creds_json):
-        headers = {'Content-Type': 'application/json'}
-        try:
-            response = requests.post('%s/v3/s3tokens' % self._request_uri,
-                                     headers=headers, data=creds_json,
-                                     verify=self._verify,
-                                     timeout=CONF.s3_token.timeout)
-        except requests.exceptions.RequestException as e:
-            self._logger.info('HTTP connection exception: %s', e)
-            resp = self._deny_request('InvalidURI')
-            raise ServiceError(resp)
-
-        if response.status_code < 200 or response.status_code >= 300:
-            self._logger.debug('Keystone reply error: status=%s reason=%s',
-                               response.status_code, response.reason)
-            resp = self._deny_request('AccessDenied')
-            raise ServiceError(resp)
-
-        return response
-
-    def __call__(self, environ, start_response):
-        """Handle incoming request. authenticate and send downstream."""
-        req = webob.Request(environ)
-        self._logger.debug('Calling S3Token middleware.')
-
-        try:
-            parts = strutils.split_path(req.path, 1, 4, True)
-            version, account, container, obj = parts
-        except ValueError:
-            msg = 'Not a path query, skipping.'
-            self._logger.debug(msg)
-            return self._app(environ, start_response)
-
-        # Read request signature and access id.
-        if 'Authorization' not in req.headers:
-            msg = 'No Authorization header. skipping.'
-            self._logger.debug(msg)
-            return self._app(environ, start_response)
-
-        token = req.headers.get('X-Auth-Token',
-                                req.headers.get('X-Storage-Token'))
-        if not token:
-            msg = 'You did not specify an auth or a storage token. skipping.'
-            self._logger.debug(msg)
-            return self._app(environ, start_response)
-
-        auth_header = req.headers['Authorization']
-        try:
-            access, signature = auth_header.split(' ')[-1].rsplit(':', 1)
-        except ValueError:
-            msg = 'You have an invalid Authorization header: %s'
-            self._logger.debug(msg, auth_header)
-            return self._deny_request('InvalidURI')(environ, start_response)
-
-        # NOTE(chmou): This is to handle the special case with nova
-        # when we have the option s3_affix_tenant. We will force it to
-        # connect to another account than the one
-        # authenticated. Before people start getting worried about
-        # security, I should point that we are connecting with
-        # username/token specified by the user but instead of
-        # connecting to its own account we will force it to go to an
-        # another account. In a normal scenario if that user don't
-        # have the reseller right it will just fail but since the
-        # reseller account can connect to every account it is allowed
-        # by the swift_auth middleware.
-        force_tenant = None
-        if ':' in access:
-            access, force_tenant = access.split(':')
-
-        # Authenticate request.
-        creds = {'credentials': {'access': access,
-                                 'token': token,
-                                 'signature': signature}}
-        creds_json = jsonutils.dumps(creds)
-        self._logger.debug('Connecting to Keystone sending this JSON: %s',
-                           creds_json)
-        # NOTE(vish): We could save a call to keystone by having
-        #             keystone return token, tenant, user, and roles
-        #             from this call.
-        #
-        # NOTE(chmou): We still have the same problem we would need to
-        #              change token_auth to detect if we already
-        #              identified and not doing a second query and just
-        #              pass it through to swiftauth in this case.
-        try:
-            resp = self._json_request(creds_json)
-        except ServiceError as e:
-            resp = e.args[0]
-            msg = 'Received error, exiting middleware with error: %s'
-            self._logger.debug(msg, resp.status_code)
-            return resp(environ, start_response)
-
-        self._logger.debug('Keystone Reply: Status: %d, Output: %s',
-                           resp.status_code, resp.content)
-
-        try:
-            identity_info = resp.json()
-            token_id = str(identity_info['access']['token']['id'])
-            tenant = identity_info['access']['token']['tenant']
-        except (ValueError, KeyError):
-            error = 'Error on keystone reply: %d %s'
-            self._logger.debug(error, resp.status_code, resp.content)
-            return self._deny_request('InvalidURI')(environ, start_response)
-
-        req.headers['X-Auth-Token'] = token_id
-        tenant_to_connect = force_tenant or tenant['id']
-        self._logger.debug('Connecting with tenant: %s', tenant_to_connect)
-        new_tenant_name = '%s%s' % (self._reseller_prefix, tenant_to_connect)
-        environ['PATH_INFO'] = environ['PATH_INFO'].replace(account,
-                                                            new_tenant_name)
-        return self._app(environ, start_response)
-
-
-def filter_factory(global_conf, **local_conf):
-    """Return a WSGI filter app for use with paste.deploy."""
-    conf = global_conf.copy()
-    conf.update(local_conf)
-
-    def auth_filter(app):
-        return S3Token(app, conf)
-    return auth_filter
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware/tests/unit/test_ec2_token_middleware.py
 
new/keystonemiddleware-12.0.0/keystonemiddleware/tests/unit/test_ec2_token_middleware.py
--- 
old/keystonemiddleware-10.12.0/keystonemiddleware/tests/unit/test_ec2_token_middleware.py
   2025-08-21 11:33:29.000000000 +0200
+++ 
new/keystonemiddleware-12.0.0/keystonemiddleware/tests/unit/test_ec2_token_middleware.py
    1970-01-01 01:00:00.000000000 +0100
@@ -1,180 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from unittest import mock
-
-from oslo_serialization import jsonutils
-import requests
-import webob
-
-from keystonemiddleware import ec2_token
-from keystonemiddleware.tests.unit import utils
-
-
-TOKEN_ID = 'fake-token-id'
-EMPTY_RESPONSE = {}
-
-
-class FakeResponse(object):
-    reason = "Test Reason"
-    headers = {'x-subject-token': TOKEN_ID}
-
-    def __init__(self, json, status_code=400):
-        self._json = json
-        self.status_code = status_code
-
-    def json(self):
-        return self._json
-
-
-class FakeApp(object):
-    """This represents a WSGI app protected by the auth_token middleware."""
-
-    def __call__(self, env, start_response):
-        resp = webob.Response()
-        resp.environ = env
-        return resp(env, start_response)
-
-
-class EC2TokenMiddlewareTestBase(utils.TestCase):
-
-    TEST_PROTOCOL = 'https'
-    TEST_HOST = 'fakehost'
-    TEST_PORT = 35357
-    TEST_URL = '%s://%s:%d/v3/ec2tokens' % (TEST_PROTOCOL,
-                                            TEST_HOST,
-                                            TEST_PORT)
-
-    def setUp(self):
-        super(EC2TokenMiddlewareTestBase, self).setUp()
-        self.middleware = ec2_token.EC2Token(FakeApp(), {})
-
-    def _validate_ec2_error(self, response, http_status, ec2_code):
-        self.assertEqual(http_status, response.status_code,
-                         'Expected HTTP status %s' % http_status)
-        error_msg = '<Code>%s</Code>' % ec2_code
-        error_msg = error_msg.encode()
-        self.assertIn(error_msg, response.body)
-
-
-class EC2TokenMiddlewareTestGood(EC2TokenMiddlewareTestBase):
-    @mock.patch.object(
-        requests, 'post',
-        return_value=FakeResponse(EMPTY_RESPONSE, status_code=200))
-    def test_protocol_old_versions(self, mock_request):
-        req = webob.Request.blank('/test')
-        req.GET['Signature'] = 'test-signature'
-        req.GET['AWSAccessKeyId'] = 'test-key-id'
-        req.body = b'Action=ListUsers&Version=2010-05-08'
-        resp = req.get_response(self.middleware)
-        self.assertEqual(200, resp.status_code)
-        self.assertEqual(TOKEN_ID, req.headers['X-Auth-Token'])
-
-        mock_request.assert_called_with(
-            'http://localhost:5000/v3/ec2tokens',
-            data=mock.ANY, headers={'Content-Type': 'application/json'},
-            verify=True, cert=None, timeout=mock.ANY)
-
-        data = jsonutils.loads(mock_request.call_args[1]['data'])
-        expected_data = {
-            'ec2Credentials': {
-                'access': 'test-key-id',
-                'headers': {'Host': 'localhost:80', 'Content-Length': '35'},
-                'host': 'localhost:80',
-                'verb': 'GET',
-                'params': {'AWSAccessKeyId': 'test-key-id'},
-                'signature': 'test-signature',
-                'path': '/test',
-                'body_hash': 'b6359072c78d70ebee1e81adcbab4f01'
-                             'bf2c23245fa365ef83fe8f1f955085e2'}}
-        self.assertDictEqual(expected_data, data)
-
-    @mock.patch.object(
-        requests, 'post',
-        return_value=FakeResponse(EMPTY_RESPONSE, status_code=200))
-    def test_protocol_v4(self, mock_request):
-        req = webob.Request.blank('/test')
-        auth_str = (
-            'AWS4-HMAC-SHA256'
-            ' Credential=test-key-id/20110909/us-east-1/iam/aws4_request,'
-            ' SignedHeaders=content-type;host;x-amz-date,'
-            ' Signature=test-signature')
-        req.headers['Authorization'] = auth_str
-        req.body = b'Action=ListUsers&Version=2010-05-08'
-        resp = req.get_response(self.middleware)
-        self.assertEqual(200, resp.status_code)
-        self.assertEqual(TOKEN_ID, req.headers['X-Auth-Token'])
-
-        mock_request.assert_called_with(
-            'http://localhost:5000/v3/ec2tokens',
-            data=mock.ANY, headers={'Content-Type': 'application/json'},
-            verify=True, cert=None, timeout=mock.ANY)
-
-        data = jsonutils.loads(mock_request.call_args[1]['data'])
-        expected_data = {
-            'ec2Credentials': {
-                'access': 'test-key-id',
-                'headers': {'Host': 'localhost:80',
-                            'Content-Length': '35',
-                            'Authorization': auth_str},
-                'host': 'localhost:80',
-                'verb': 'GET',
-                'params': {},
-                'signature': 'test-signature',
-                'path': '/test',
-                'body_hash': 'b6359072c78d70ebee1e81adcbab4f01'
-                             'bf2c23245fa365ef83fe8f1f955085e2'}}
-        self.assertDictEqual(expected_data, data)
-
-
-class EC2TokenMiddlewareTestBad(EC2TokenMiddlewareTestBase):
-
-    def test_no_signature(self):
-        req = webob.Request.blank('/test')
-        resp = req.get_response(self.middleware)
-        self._validate_ec2_error(resp, 400, 'AuthFailure')
-
-    def test_no_key_id(self):
-        req = webob.Request.blank('/test')
-        req.GET['Signature'] = 'test-signature'
-        resp = req.get_response(self.middleware)
-        self._validate_ec2_error(resp, 400, 'AuthFailure')
-
-    @mock.patch.object(
-        requests, 'post',
-        return_value=FakeResponse(EMPTY_RESPONSE))
-    def test_communication_failure(self, mock_request):
-        req = webob.Request.blank('/test')
-        req.GET['Signature'] = 'test-signature'
-        req.GET['AWSAccessKeyId'] = 'test-key-id'
-        resp = req.get_response(self.middleware)
-        self._validate_ec2_error(resp, 400, 'AuthFailure')
-        mock_request.assert_called_with(
-            'http://localhost:5000/v3/ec2tokens',
-            data=mock.ANY, headers=mock.ANY,
-            verify=mock.ANY, cert=mock.ANY, timeout=mock.ANY)
-
-    @mock.patch.object(
-        requests, 'post',
-        return_value=FakeResponse(EMPTY_RESPONSE))
-    def test_no_result_data(self, mock_request):
-        req = webob.Request.blank('/test')
-        req.GET['Signature'] = 'test-signature'
-        req.GET['AWSAccessKeyId'] = 'test-key-id'
-        resp = req.get_response(self.middleware)
-        self._validate_ec2_error(resp, 400, 'AuthFailure')
-        mock_request.assert_called_with(
-            'http://localhost:5000/v3/ec2tokens',
-            data=mock.ANY, headers=mock.ANY,
-            verify=mock.ANY, cert=mock.ANY, timeout=mock.ANY)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware/tests/unit/test_entry_points.py
 
new/keystonemiddleware-12.0.0/keystonemiddleware/tests/unit/test_entry_points.py
--- 
old/keystonemiddleware-10.12.0/keystonemiddleware/tests/unit/test_entry_points.py
   2025-08-21 11:33:29.000000000 +0200
+++ 
new/keystonemiddleware-12.0.0/keystonemiddleware/tests/unit/test_entry_points.py
    2026-02-17 15:48:39.000000000 +0100
@@ -22,8 +22,6 @@
         expected_factory_names = [
             'audit',
             'auth_token',
-            'ec2_token',
-            's3_token',
         ]
         em = stevedore.ExtensionManager('paste.filter_factory')
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware/tests/unit/test_external_oauth2_token_middleware.py
 
new/keystonemiddleware-12.0.0/keystonemiddleware/tests/unit/test_external_oauth2_token_middleware.py
--- 
old/keystonemiddleware-10.12.0/keystonemiddleware/tests/unit/test_external_oauth2_token_middleware.py
       2025-08-21 11:33:29.000000000 +0200
+++ 
new/keystonemiddleware-12.0.0/keystonemiddleware/tests/unit/test_external_oauth2_token_middleware.py
        2026-02-17 15:48:39.000000000 +0100
@@ -1823,6 +1823,82 @@
         self.assertEqual(resp.headers.get('WWW-Authenticate'),
                          'Authorization OAuth 2.0 uri="%s"' % self._audience)
 
+    def test_spoofed_headers_are_sanitized(self):
+        """Test that spoofed identity headers are removed and replaced.
+
+        This test verifies the fix for a privilege escalation vulnerability
+        where an attacker could send spoofed identity headers that would not
+        be cleared by the middleware, allowing unauthorized access.
+        """
+        conf = copy.deepcopy(self._test_conf)
+        self.set_middleware(conf=conf)
+
+        # Use non-admin roles in the token metadata
+        non_admin_roles = 'member,reader'
+        non_admin_metadata = copy.deepcopy(self._default_metadata)
+        non_admin_metadata['roles'] = non_admin_roles
+
+        def mock_resp(request, context):
+            return self._introspect_response(
+                request, context,
+                auth_method=self._auth_method,
+                introspect_client_id=self._test_client_id,
+                introspect_client_secret=self._test_client_secret,
+                access_token=self._token,
+                active=True,
+                metadata=non_admin_metadata
+            )
+
+        self.requests_mock.post(self._introspect_endpoint,
+                                json=mock_resp)
+        self.requests_mock.get(self._auth_url,
+                               json=VERSION_LIST_v3,
+                               status_code=300)
+
+        # Attempt to spoof multiple identity headers
+        spoofed_headers = get_authorization_header(self._token)
+        spoofed_headers.update({
+            'X-Identity-Status': 'Confirmed',
+            'X-Is-Admin-Project': 'true',
+            'X-User-Id': 'spoofed_admin_user_id',
+            'X-User-Name': 'spoofed_admin',
+            'X-Roles': 'admin,superuser',
+            'X-Project-Id': 'spoofed_project_id',
+            'X-User-Domain-Id': 'spoofed_domain_id',
+            'X-User-Domain-Name': 'spoofed_domain',
+        })
+
+        resp = self.call_middleware(
+            headers=spoofed_headers,
+            expected_status=200,
+            method='GET', path='/vnfpkgm/v1/vnf_packages',
+            environ={'wsgi.input': FakeWsgiInput(FakeSocket(None))}
+        )
+        self.assertEqual(FakeApp.SUCCESS, resp.body)
+
+        # Verify spoofed headers were replaced with actual token values
+        env = resp.request.environ
+
+        # X-Is-Admin-Project should not be present (not the spoofed 'true')
+        # because the token has non-admin roles and the middleware only sets
+        # this header when is_admin is true
+        self.assertNotIn('HTTP_X_IS_ADMIN_PROJECT', env)
+
+        # User info should match the token, not the spoofed values
+        self.assertEqual(self._user_id, env['HTTP_X_USER_ID'])
+        self.assertEqual(self._user_name, env['HTTP_X_USER_NAME'])
+        self.assertEqual(self._user_domain_id, env['HTTP_X_USER_DOMAIN_ID'])
+        self.assertEqual(
+            self._user_domain_name,
+            env['HTTP_X_USER_DOMAIN_NAME']
+        )
+
+        # Roles should be from the token, not spoofed
+        self.assertEqual(non_admin_roles, env['HTTP_X_ROLES'])
+
+        # Project info should match the token
+        self.assertEqual(self._project_id, env['HTTP_X_PROJECT_ID'])
+
 
 class ExternalAuth2ProtocolTest(BaseExternalOauth2TokenMiddlewareTest):
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware/tests/unit/test_s3_token_middleware.py
 
new/keystonemiddleware-12.0.0/keystonemiddleware/tests/unit/test_s3_token_middleware.py
--- 
old/keystonemiddleware-10.12.0/keystonemiddleware/tests/unit/test_s3_token_middleware.py
    2025-08-21 11:33:29.000000000 +0200
+++ 
new/keystonemiddleware-12.0.0/keystonemiddleware/tests/unit/test_s3_token_middleware.py
     1970-01-01 01:00:00.000000000 +0100
@@ -1,249 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from unittest import mock
-import urllib.parse
-
-import fixtures
-from oslo_serialization import jsonutils
-import requests
-from requests_mock.contrib import fixture as rm_fixture
-from testtools import matchers
-import webob
-
-from keystonemiddleware import s3_token
-from keystonemiddleware.tests.unit import utils
-
-
-GOOD_RESPONSE = {'access': {'token': {'id': 'TOKEN_ID',
-                                      'tenant': {'id': 'TENANT_ID'}}}}
-
-
-class FakeApp(object):
-    """This represents a WSGI app protected by the auth_token middleware."""
-
-    def __call__(self, env, start_response):
-        resp = webob.Response()
-        resp.environ = env
-        return resp(env, start_response)
-
-
-class S3TokenMiddlewareTestBase(utils.TestCase):
-
-    TEST_WWW_AUTHENTICATE_URI = 'https://fakehost/identity'
-    TEST_URL = '%s/v3/s3tokens' % (TEST_WWW_AUTHENTICATE_URI, )
-
-    def setUp(self):
-        super(S3TokenMiddlewareTestBase, self).setUp()
-
-        self.conf = {
-            'www_authenticate_uri': self.TEST_WWW_AUTHENTICATE_URI,
-        }
-
-        self.requests_mock = self.useFixture(rm_fixture.Fixture())
-
-    def start_fake_response(self, status, headers):
-        self.response_status = int(status.split(' ', 1)[0])
-        self.response_headers = dict(headers)
-
-
-class S3TokenMiddlewareTestGood(S3TokenMiddlewareTestBase):
-
-    def setUp(self):
-        super(S3TokenMiddlewareTestGood, self).setUp()
-        self.middleware = s3_token.S3Token(FakeApp(), self.conf)
-
-        self.requests_mock.post(self.TEST_URL,
-                                status_code=201,
-                                json=GOOD_RESPONSE)
-
-    # Ignore the request and pass to the next middleware in the
-    # pipeline if no path has been specified.
-    def test_no_path_request(self):
-        req = webob.Request.blank('/')
-        self.middleware(req.environ, self.start_fake_response)
-        self.assertEqual(self.response_status, 200)
-
-    # Ignore the request and pass to the next middleware in the
-    # pipeline if no Authorization header has been specified
-    def test_without_authorization(self):
-        req = webob.Request.blank('/v1/AUTH_cfa/c/o')
-        self.middleware(req.environ, self.start_fake_response)
-        self.assertEqual(self.response_status, 200)
-
-    def test_without_auth_storage_token(self):
-        req = webob.Request.blank('/v1/AUTH_cfa/c/o')
-        req.headers['Authorization'] = 'badboy'
-        self.middleware(req.environ, self.start_fake_response)
-        self.assertEqual(self.response_status, 200)
-
-    def test_authorized(self):
-        req = webob.Request.blank('/v1/AUTH_cfa/c/o')
-        req.headers['Authorization'] = 'access:signature'
-        req.headers['X-Storage-Token'] = 'token'
-        req.get_response(self.middleware)
-        self.assertTrue(req.path.startswith('/v1/AUTH_TENANT_ID'))
-        self.assertEqual(req.headers['X-Auth-Token'], 'TOKEN_ID')
-
-    def test_authorized_http(self):
-        protocol = 'http'
-        host = 'fakehost'
-        port = 35357
-        self.requests_mock.post(
-            '%s://%s:%s/v3/s3tokens' % (protocol, host, port),
-            status_code=201, json=GOOD_RESPONSE)
-
-        self.middleware = (
-            s3_token.filter_factory({'auth_protocol': protocol,
-                                     'auth_host': host,
-                                     'auth_port': port})(FakeApp()))
-        req = webob.Request.blank('/v1/AUTH_cfa/c/o')
-        req.headers['Authorization'] = 'access:signature'
-        req.headers['X-Storage-Token'] = 'token'
-        req.get_response(self.middleware)
-        self.assertTrue(req.path.startswith('/v1/AUTH_TENANT_ID'))
-        self.assertEqual(req.headers['X-Auth-Token'], 'TOKEN_ID')
-
-    def test_authorization_nova_toconnect(self):
-        req = webob.Request.blank('/v1/AUTH_swiftint/c/o')
-        req.headers['Authorization'] = 'access:FORCED_TENANT_ID:signature'
-        req.headers['X-Storage-Token'] = 'token'
-        req.get_response(self.middleware)
-        path = req.environ['PATH_INFO']
-        self.assertTrue(path.startswith('/v1/AUTH_FORCED_TENANT_ID'))
-
-    @mock.patch.object(requests, 'post')
-    def test_insecure(self, MOCK_REQUEST):
-        self.middleware = (
-            s3_token.filter_factory({'insecure': 'True'})(FakeApp()))
-
-        text_return_value = jsonutils.dumps(GOOD_RESPONSE).encode()
-        MOCK_REQUEST.return_value = utils.TestResponse({
-            'status_code': 201,
-            'text': text_return_value})
-
-        req = webob.Request.blank('/v1/AUTH_cfa/c/o')
-        req.headers['Authorization'] = 'access:signature'
-        req.headers['X-Storage-Token'] = 'token'
-        req.get_response(self.middleware)
-
-        self.assertTrue(MOCK_REQUEST.called)
-        mock_args, mock_kwargs = MOCK_REQUEST.call_args
-        self.assertIs(mock_kwargs['verify'], False)
-
-    def test_insecure_option(self):
-        # insecure is passed as a string.
-
-        # Some non-secure values.
-        true_values = ['true', 'True', '1', 'yes']
-        for val in true_values:
-            config = {'insecure': val, 'certfile': 'false_ind'}
-            middleware = s3_token.filter_factory(config)(FakeApp())
-            self.assertIs(False, middleware._verify)
-
-        # Some "secure" values, including unexpected value.
-        false_values = ['false', 'False', '0', 'no', 'someweirdvalue']
-        for val in false_values:
-            config = {'insecure': val, 'certfile': 'false_ind'}
-            middleware = s3_token.filter_factory(config)(FakeApp())
-            self.assertEqual('false_ind', middleware._verify)
-
-        # Default is secure.
-        config = {'certfile': 'false_ind'}
-        middleware = s3_token.filter_factory(config)(FakeApp())
-        self.assertIs('false_ind', middleware._verify)
-
-    def test_unicode_path(self):
-        url = u'/v1/AUTH_cfa/c/euro\u20ac'.encode('utf8')
-        req = webob.Request.blank(urllib.parse.quote(url))
-        req.headers['Authorization'] = 'access:signature'
-        req.headers['X-Storage-Token'] = 'token'
-        req.get_response(self.middleware)
-
-
-class S3TokenMiddlewareTestBad(S3TokenMiddlewareTestBase):
-    def setUp(self):
-        super(S3TokenMiddlewareTestBad, self).setUp()
-        self.middleware = s3_token.S3Token(FakeApp(), self.conf)
-
-    def test_unauthorized_token(self):
-        ret = {"error":
-               {"message": "EC2 access key not found.",
-                "code": 401,
-                "title": "Unauthorized"}}
-        self.requests_mock.post(self.TEST_URL, status_code=403, json=ret)
-        req = webob.Request.blank('/v1/AUTH_cfa/c/o')
-        req.headers['Authorization'] = 'access:signature'
-        req.headers['X-Storage-Token'] = 'token'
-        resp = req.get_response(self.middleware)
-        s3_denied_req = self.middleware._deny_request('AccessDenied')
-        self.assertEqual(resp.body, s3_denied_req.body)
-        self.assertEqual(resp.status_int, s3_denied_req.status_int)
-
-    def test_bogus_authorization(self):
-        req = webob.Request.blank('/v1/AUTH_cfa/c/o')
-        req.headers['Authorization'] = 'badboy'
-        req.headers['X-Storage-Token'] = 'token'
-        resp = req.get_response(self.middleware)
-        self.assertEqual(resp.status_int, 400)
-        s3_invalid_req = self.middleware._deny_request('InvalidURI')
-        self.assertEqual(resp.body, s3_invalid_req.body)
-        self.assertEqual(resp.status_int, s3_invalid_req.status_int)
-
-    def test_fail_to_connect_to_keystone(self):
-        with mock.patch.object(self.middleware, '_json_request') as o:
-            s3_invalid_req = self.middleware._deny_request('InvalidURI')
-            o.side_effect = s3_token.ServiceError(s3_invalid_req)
-
-            req = webob.Request.blank('/v1/AUTH_cfa/c/o')
-            req.headers['Authorization'] = 'access:signature'
-            req.headers['X-Storage-Token'] = 'token'
-            resp = req.get_response(self.middleware)
-            self.assertEqual(resp.body, s3_invalid_req.body)
-            self.assertEqual(resp.status_int, s3_invalid_req.status_int)
-
-    def test_bad_reply(self):
-        self.requests_mock.post(self.TEST_URL,
-                                status_code=201,
-                                text="<badreply>")
-
-        req = webob.Request.blank('/v1/AUTH_cfa/c/o')
-        req.headers['Authorization'] = 'access:signature'
-        req.headers['X-Storage-Token'] = 'token'
-        resp = req.get_response(self.middleware)
-        s3_invalid_req = self.middleware._deny_request('InvalidURI')
-        self.assertEqual(resp.body, s3_invalid_req.body)
-        self.assertEqual(resp.status_int, s3_invalid_req.status_int)
-
-
-class S3TokenMiddlewareTestDeprecatedOptions(S3TokenMiddlewareTestBase):
-    def setUp(self):
-        super(S3TokenMiddlewareTestDeprecatedOptions, self).setUp()
-        self.conf = {
-            'auth_uri': self.TEST_WWW_AUTHENTICATE_URI,
-        }
-        self.logger = self.useFixture(fixtures.FakeLogger())
-        self.middleware = s3_token.S3Token(FakeApp(), self.conf)
-
-        self.requests_mock.post(self.TEST_URL,
-                                status_code=201,
-                                json=GOOD_RESPONSE)
-
-    def test_logs_warning(self):
-        req = webob.Request.blank('/')
-        self.middleware(req.environ, self.start_fake_response)
-        self.assertEqual(self.response_status, 200)
-        log = "Use of the auth_uri option was deprecated in the Queens " \
-            "release in favor of www_authenticate_uri."
-        self.assertThat(self.logger.output, matchers.Contains(log))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware.egg-info/PKG-INFO 
new/keystonemiddleware-12.0.0/keystonemiddleware.egg-info/PKG-INFO
--- old/keystonemiddleware-10.12.0/keystonemiddleware.egg-info/PKG-INFO 
2025-08-21 11:34:47.000000000 +0200
+++ new/keystonemiddleware-12.0.0/keystonemiddleware.egg-info/PKG-INFO  
2026-02-17 15:49:50.000000000 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: keystonemiddleware
-Version: 10.12.0
+Version: 12.0.0
 Summary: Middleware for OpenStack Identity
 Home-page: https://docs.openstack.org/keystonemiddleware/latest/
 Author: OpenStack
@@ -35,9 +35,10 @@
 Requires-Dist: WebOb>=1.7.1
 Provides-Extra: audit-notifications
 Requires-Dist: oslo.messaging>=5.29.0; extra == "audit-notifications"
+Provides-Extra: memcache-encryption
+Requires-Dist: cryptography>=2.7; extra == "memcache-encryption"
 Provides-Extra: test
 Requires-Dist: hacking~=6.1.0; extra == "test"
-Requires-Dist: flake8-docstrings~=1.7.0; extra == "test"
 Requires-Dist: coverage>=4.0; extra == "test"
 Requires-Dist: cryptography>=3.0; extra == "test"
 Requires-Dist: fixtures>=3.0.0; extra == "test"
@@ -53,6 +54,16 @@
 Requires-Dist: oslo.messaging>=5.29.0; extra == "test"
 Requires-Dist: PyJWT>=2.4.0; extra == "test"
 Requires-Dist: bandit>=1.1.0; extra == "test"
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: home-page
+Dynamic: license-file
+Dynamic: provides-extra
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
 
 ========================
 Team and repository tags
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware.egg-info/SOURCES.txt 
new/keystonemiddleware-12.0.0/keystonemiddleware.egg-info/SOURCES.txt
--- old/keystonemiddleware-10.12.0/keystonemiddleware.egg-info/SOURCES.txt      
2025-08-21 11:34:47.000000000 +0200
+++ new/keystonemiddleware-12.0.0/keystonemiddleware.egg-info/SOURCES.txt       
2026-02-17 15:49:50.000000000 +0100
@@ -28,7 +28,6 @@
 doc/source/images/graphs_authComp.svg
 doc/source/images/graphs_authCompDelegate.svg
 keystonemiddleware/__init__.py
-keystonemiddleware/ec2_token.py
 keystonemiddleware/exceptions.py
 keystonemiddleware/external_oauth2_token.py
 keystonemiddleware/fixture.py
@@ -36,7 +35,6 @@
 keystonemiddleware/oauth2_mtls_token.py
 keystonemiddleware/oauth2_token.py
 keystonemiddleware/opts.py
-keystonemiddleware/s3_token.py
 keystonemiddleware.egg-info/PKG-INFO
 keystonemiddleware.egg-info/SOURCES.txt
 keystonemiddleware.egg-info/dependency_links.txt
@@ -69,14 +67,12 @@
 keystonemiddleware/tests/unit/__init__.py
 keystonemiddleware/tests/unit/client_fixtures.py
 keystonemiddleware/tests/unit/test_access_rules.py
-keystonemiddleware/tests/unit/test_ec2_token_middleware.py
 keystonemiddleware/tests/unit/test_entry_points.py
 keystonemiddleware/tests/unit/test_external_oauth2_token_middleware.py
 keystonemiddleware/tests/unit/test_fixtures.py
 keystonemiddleware/tests/unit/test_oauth2_mtls_token_middleware.py
 keystonemiddleware/tests/unit/test_oauth2_token_middleware.py
 keystonemiddleware/tests/unit/test_opts.py
-keystonemiddleware/tests/unit/test_s3_token_middleware.py
 keystonemiddleware/tests/unit/utils.py
 keystonemiddleware/tests/unit/audit/__init__.py
 keystonemiddleware/tests/unit/audit/base.py
@@ -135,11 +131,13 @@
 releasenotes/notes/fix-cache-data-corrupted-issue-d1bd546625690581.yaml
 releasenotes/notes/interface-option-ed551d2a3162668d.yaml
 releasenotes/notes/ksm_4.1.0-3cd78446d8e63616.yaml
+releasenotes/notes/remove-ec2-s3-token-middleware-e0b9b63428224600.yaml
 releasenotes/notes/remove-py38-438c67d4d2da02a3.yaml
 releasenotes/notes/remove-py39-ffac30d683a53099.yaml
 releasenotes/notes/remove_kwargs_to_fetch_token-20e3451ed192ab6a.yaml
 releasenotes/notes/removed-as-of-ussuri-4e1ea485ba8801c9.yaml
 releasenotes/notes/rename-auth-uri-d223d883f5898aee.yaml
+releasenotes/notes/s3token-remove-deprecated-opts-a58a7715b275a89c.yaml
 releasenotes/notes/s3token_auth_uri-490c1287d90b9df7.yaml
 releasenotes/notes/tls-support-via-oslo-cache-51d744dd8a3f6ba0.yaml
 releasenotes/notes/x-is-admin-project-header-97f1882e209fe727.yaml
@@ -148,6 +146,7 @@
 releasenotes/source/2024.1.rst
 releasenotes/source/2024.2.rst
 releasenotes/source/2025.1.rst
+releasenotes/source/2025.2.rst
 releasenotes/source/conf.py
 releasenotes/source/index.rst
 releasenotes/source/mitaka.rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware.egg-info/entry_points.txt 
new/keystonemiddleware-12.0.0/keystonemiddleware.egg-info/entry_points.txt
--- old/keystonemiddleware-10.12.0/keystonemiddleware.egg-info/entry_points.txt 
2025-08-21 11:34:47.000000000 +0200
+++ new/keystonemiddleware-12.0.0/keystonemiddleware.egg-info/entry_points.txt  
2026-02-17 15:49:50.000000000 +0100
@@ -5,8 +5,6 @@
 [paste.filter_factory]
 audit = keystonemiddleware.audit:filter_factory
 auth_token = keystonemiddleware.auth_token:filter_factory
-ec2_token = keystonemiddleware.ec2_token:filter_factory
 external_oauth2_token = keystonemiddleware.external_oauth2_token:filter_factory
 oauth2_mtls_token = keystonemiddleware.oauth2_mtls_token:filter_factory
 oauth2_token = keystonemiddleware.oauth2_token:filter_factory
-s3_token = keystonemiddleware.s3_token:filter_factory
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware.egg-info/pbr.json 
new/keystonemiddleware-12.0.0/keystonemiddleware.egg-info/pbr.json
--- old/keystonemiddleware-10.12.0/keystonemiddleware.egg-info/pbr.json 
2025-08-21 11:34:47.000000000 +0200
+++ new/keystonemiddleware-12.0.0/keystonemiddleware.egg-info/pbr.json  
2026-02-17 15:49:50.000000000 +0100
@@ -1 +1 @@
-{"git_version": "c893b62", "is_release": true}
\ No newline at end of file
+{"git_version": "fe744ab", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/keystonemiddleware.egg-info/requires.txt 
new/keystonemiddleware-12.0.0/keystonemiddleware.egg-info/requires.txt
--- old/keystonemiddleware-10.12.0/keystonemiddleware.egg-info/requires.txt     
2025-08-21 11:34:47.000000000 +0200
+++ new/keystonemiddleware-12.0.0/keystonemiddleware.egg-info/requires.txt      
2026-02-17 15:49:50.000000000 +0100
@@ -16,9 +16,11 @@
 [audit_notifications]
 oslo.messaging>=5.29.0
 
+[memcache_encryption]
+cryptography>=2.7
+
 [test]
 hacking~=6.1.0
-flake8-docstrings~=1.7.0
 coverage>=4.0
 cryptography>=3.0
 fixtures>=3.0.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/releasenotes/notes/bp-enhance-oauth2-interoperability-dd998d4e0eafed3c.yaml
 
new/keystonemiddleware-12.0.0/releasenotes/notes/bp-enhance-oauth2-interoperability-dd998d4e0eafed3c.yaml
--- 
old/keystonemiddleware-10.12.0/releasenotes/notes/bp-enhance-oauth2-interoperability-dd998d4e0eafed3c.yaml
  2025-08-21 11:33:29.000000000 +0200
+++ 
new/keystonemiddleware-12.0.0/releasenotes/notes/bp-enhance-oauth2-interoperability-dd998d4e0eafed3c.yaml
   2026-02-17 15:48:39.000000000 +0100
@@ -1,8 +1,8 @@
----
-features:
-  - |
-    [`blueprint enhance-oauth2-interoperability 
<https://blueprints.launchpad.net/keystone/+spec/enhance-oauth2-interoperability>`_]
-    The external_oauth2_token filter has been added for accepting or denying
-    incoming requests containing OAuth 2.0 access tokens that are obtained
-    from an external authorization server by users through their OAuth 2.0
-    credentials.
+---
+features:
+  - |
+    [`blueprint enhance-oauth2-interoperability 
<https://blueprints.launchpad.net/keystone/+spec/enhance-oauth2-interoperability>`_]
+    The external_oauth2_token filter has been added for accepting or denying
+    incoming requests containing OAuth 2.0 access tokens that are obtained
+    from an external authorization server by users through their OAuth 2.0
+    credentials.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/releasenotes/notes/remove-ec2-s3-token-middleware-e0b9b63428224600.yaml
 
new/keystonemiddleware-12.0.0/releasenotes/notes/remove-ec2-s3-token-middleware-e0b9b63428224600.yaml
--- 
old/keystonemiddleware-10.12.0/releasenotes/notes/remove-ec2-s3-token-middleware-e0b9b63428224600.yaml
      1970-01-01 01:00:00.000000000 +0100
+++ 
new/keystonemiddleware-12.0.0/releasenotes/notes/remove-ec2-s3-token-middleware-e0b9b63428224600.yaml
       2026-02-17 15:48:39.000000000 +0100
@@ -0,0 +1,9 @@
+---
+upgrade:
+  - |
+    The ``ec2_token`` and ``s3_token`` middleware have been removed.
+    These middlewares were designed for use with Nova's EC2 API (removed in
+    Mitaka) and Swift's swift3 middleware (superseded by 
swift.common.middleware.s3api).
+    Neither middleware has active consumers in current OpenStack deployments.
+    For EC2 API compatibility, use the standalone ec2-api project. For S3 API
+    compatibility with Swift, use Swift's built-in s3api and s3token 
middleware.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/releasenotes/notes/s3token-remove-deprecated-opts-a58a7715b275a89c.yaml
 
new/keystonemiddleware-12.0.0/releasenotes/notes/s3token-remove-deprecated-opts-a58a7715b275a89c.yaml
--- 
old/keystonemiddleware-10.12.0/releasenotes/notes/s3token-remove-deprecated-opts-a58a7715b275a89c.yaml
      1970-01-01 01:00:00.000000000 +0100
+++ 
new/keystonemiddleware-12.0.0/releasenotes/notes/s3token-remove-deprecated-opts-a58a7715b275a89c.yaml
       2026-02-17 15:48:39.000000000 +0100
@@ -0,0 +1,10 @@
+---
+upgrade:
+  - |
+    The s3token middleware now requires the ``www_authenticate_uri`` option and
+    no longer loads the following deprecated options.
+
+    - ``auth_uri``
+    - ``auth_host``
+    - ``auth_port``
+    - ``auth_protocol``
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/releasenotes/source/2024.1.rst 
new/keystonemiddleware-12.0.0/releasenotes/source/2024.1.rst
--- old/keystonemiddleware-10.12.0/releasenotes/source/2024.1.rst       
2025-08-21 11:33:29.000000000 +0200
+++ new/keystonemiddleware-12.0.0/releasenotes/source/2024.1.rst        
2026-02-17 15:48:39.000000000 +0100
@@ -3,4 +3,4 @@
 ===========================
 
 .. release-notes::
-   :branch: stable/2024.1
+   :branch: unmaintained/2024.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/releasenotes/source/2025.2.rst 
new/keystonemiddleware-12.0.0/releasenotes/source/2025.2.rst
--- old/keystonemiddleware-10.12.0/releasenotes/source/2025.2.rst       
1970-01-01 01:00:00.000000000 +0100
+++ new/keystonemiddleware-12.0.0/releasenotes/source/2025.2.rst        
2026-02-17 15:48:39.000000000 +0100
@@ -0,0 +1,6 @@
+===========================
+2025.2 Series Release Notes
+===========================
+
+.. release-notes::
+   :branch: stable/2025.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/keystonemiddleware-10.12.0/releasenotes/source/index.rst 
new/keystonemiddleware-12.0.0/releasenotes/source/index.rst
--- old/keystonemiddleware-10.12.0/releasenotes/source/index.rst        
2025-08-21 11:33:29.000000000 +0200
+++ new/keystonemiddleware-12.0.0/releasenotes/source/index.rst 2026-02-17 
15:48:39.000000000 +0100
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   2025.2
    2025.1
    2024.2
    2024.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/keystonemiddleware-10.12.0/setup.cfg 
new/keystonemiddleware-12.0.0/setup.cfg
--- old/keystonemiddleware-10.12.0/setup.cfg    2025-08-21 11:34:47.930037700 
+0200
+++ new/keystonemiddleware-12.0.0/setup.cfg     2026-02-17 15:49:50.080432700 
+0100
@@ -28,6 +28,8 @@
 [extras]
 audit_notifications = 
        oslo.messaging>=5.29.0 # Apache-2.0
+memcache_encryption = 
+       cryptography>=2.7 # BSD/Apache-2.0
 
 [entry_points]
 oslo.config.opts = 
@@ -36,8 +38,6 @@
 paste.filter_factory = 
        auth_token = keystonemiddleware.auth_token:filter_factory
        audit = keystonemiddleware.audit:filter_factory
-       ec2_token = keystonemiddleware.ec2_token:filter_factory
-       s3_token = keystonemiddleware.s3_token:filter_factory
        oauth2_token = keystonemiddleware.oauth2_token:filter_factory
        oauth2_mtls_token = keystonemiddleware.oauth2_mtls_token:filter_factory
        external_oauth2_token = 
keystonemiddleware.external_oauth2_token:filter_factory
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/keystonemiddleware-10.12.0/setup.py 
new/keystonemiddleware-12.0.0/setup.py
--- old/keystonemiddleware-10.12.0/setup.py     2025-08-21 11:33:29.000000000 
+0200
+++ new/keystonemiddleware-12.0.0/setup.py      2026-02-17 15:48:39.000000000 
+0100
@@ -13,17 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
 import setuptools
 
-# In python < 2.7.4, a lazy loading of package `pbr` will break
-# setuptools if some other modules registered functions in `atexit`.
-# solution from: http://bugs.python.org/issue15881#msg170215
-try:
-    import multiprocessing  # noqa
-except ImportError:
-    pass
-
 setuptools.setup(
     setup_requires=['pbr>=2.0.0'],
     pbr=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/keystonemiddleware-10.12.0/test-requirements.txt 
new/keystonemiddleware-12.0.0/test-requirements.txt
--- old/keystonemiddleware-10.12.0/test-requirements.txt        2025-08-21 
11:33:29.000000000 +0200
+++ new/keystonemiddleware-12.0.0/test-requirements.txt 2026-02-17 
15:48:39.000000000 +0100
@@ -1,5 +1,4 @@
 hacking~=6.1.0 # Apache-2.0
-flake8-docstrings~=1.7.0 # MIT
 
 coverage>=4.0 # Apache-2.0
 cryptography>=3.0 # BSD/Apache-2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/keystonemiddleware-10.12.0/tox.ini 
new/keystonemiddleware-12.0.0/tox.ini
--- old/keystonemiddleware-10.12.0/tox.ini      2025-08-21 11:33:29.000000000 
+0200
+++ new/keystonemiddleware-12.0.0/tox.ini       2026-02-17 15:48:39.000000000 
+0100
@@ -20,11 +20,6 @@
   flake8
   bandit -r keystonemiddleware -x tests -n5
 
-[testenv:bandit]
-# NOTE(browne): This is required for the integration test job of the bandit
-# project. Please do not remove.
-commands = bandit -r keystonemiddleware -x tests -n5
-
 [testenv:venv]
 commands = {posargs}
 

Reply via email to