diff options
20 files changed, 312 insertions, 408 deletions
@@ -3,7 +3,7 @@ - openstack-cover-jobs - openstack-lower-constraints-jobs - openstack-python-jobs - - openstack-python3-train-jobs + - openstack-python3-ussuri-jobs - publish-openstack-docs-pti - check-requirements - lib-forward-testing @@ -28,7 +28,7 @@ For information on contributing, see ``CONTRIBUTING.rst``. * License: Apache License, Version 2.0 * Documentation: https://docs.openstack.org/keystonemiddleware/latest/ -* Source: https://git.openstack.org/cgit/openstack/keystonemiddleware +* Source: https://opendev.org/openstack/keystonemiddleware * Bugs: https://bugs.launchpad.net/keystonemiddleware * Release notes: https://docs.openstack.org/releasenotes/keystonemiddleware/ diff --git a/doc/requirements.txt b/doc/requirements.txt index 42dda5b..ddd05be 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -10,6 +10,9 @@ sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2;python_version>='3.4' # BSD sphinxcontrib-apidoc>=0.2.0 # BSD +# PDF Docs +sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD + # For autodoc builds mock>=2.0.0 # BSD oslotest>=3.2.0 # Apache-2.0 diff --git a/doc/source/conf.py b/doc/source/conf.py index ccea01f..4428242 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -42,6 +42,7 @@ extensions = ['sphinx.ext.todo', 'openstackdocstheme', 'oslo_config.sphinxconfiggen', 'sphinxcontrib.apidoc', + 'sphinxcontrib.rsvgconverter', ] # sphinxcontrib.apidoc options @@ -200,12 +201,24 @@ htmlhelp_basename = 'keystonemiddlewaredoc' # (source start file, target name, title, author, documentclass [howto/manual]) # . latex_documents = [ - ('index', 'keystonmiddleware.tex', - 'keystonemiddleware Documentation', - 'Nebula Inc, based on work by Rackspace and Jacob Kaplan-Moss', + ('index', 'doc-keystonemiddleware.tex', + u'keystonemiddleware Documentation', + u'Openstack Developers', 'manual'), ] +# Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664 +latex_use_xindy = False + +latex_domain_indices = False + +latex_elements = { + 'extraclassoptions': 'openany', + 'makeindex': '', + 'printindex': '', + 'preamble': r'\setcounter{tocdepth}{3}', + 'maxlistdepth': 10, +} # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None diff --git a/keystonemiddleware/auth_token/__init__.py b/keystonemiddleware/auth_token/__init__.py index b29b63a..d961f59 100644 --- a/keystonemiddleware/auth_token/__init__.py +++ b/keystonemiddleware/auth_token/__init__.py @@ -282,14 +282,14 @@ def _path_matches(request_path, path_pattern): # The fnmatch module doesn't provide the ability to match * versus **, # so convert to regex. token_regex = (r'(?P<tag>{[^}]*})|' # {tag} # nosec - '(?P<wild>\*(?=$|[^\*]))|' # * - '(?P<rec_wild>\*\*)|' # ** - '(?P<literal>[^{}\*])') # anything else + r'(?P<wild>\*(?=$|[^\*]))|' # * + r'(?P<rec_wild>\*\*)|' # ** + r'(?P<literal>[^{}\*])') # anything else path_regex = '' for match in re.finditer(token_regex, path_pattern): token = match.groupdict() if token['tag'] or token['wild']: - path_regex += '[^\/]+' + path_regex += r'[^\/]+' if token['rec_wild']: path_regex += '.*' if token['literal']: diff --git a/keystonemiddleware/auth_token/_identity.py b/keystonemiddleware/auth_token/_identity.py index 49bdf9a..53d3819 100644 --- a/keystonemiddleware/auth_token/_identity.py +++ b/keystonemiddleware/auth_token/_identity.py @@ -13,7 +13,6 @@ from keystoneauth1 import discover from keystoneauth1 import exceptions as ksa_exceptions from keystoneauth1 import plugin -from keystoneclient.v2_0 import client as v2_client from keystoneclient.v3 import client as v3_client from six.moves import urllib @@ -37,25 +36,6 @@ class _RequestStrategy(object): pass -class _V2RequestStrategy(_RequestStrategy): - - AUTH_VERSION = (2, 0) - - def __init__(self, adap, **kwargs): - super(_V2RequestStrategy, self).__init__(adap, **kwargs) - self._client = v2_client.Client(session=adap) - - def verify_token(self, token, allow_expired=False): - # NOTE(jamielennox): allow_expired is ignored on V2 - auth_ref = self._client.tokens.validate_access_info(token) - - if not auth_ref: - msg = _('Failed to fetch token data from identity server') - raise ksm_exceptions.InvalidToken(msg) - - return {'access': auth_ref} - - class _V3RequestStrategy(_RequestStrategy): AUTH_VERSION = (3, 0) @@ -81,7 +61,7 @@ class _V3RequestStrategy(_RequestStrategy): return {'token': auth_ref} -_REQUEST_STRATEGIES = [_V3RequestStrategy, _V2RequestStrategy] +_REQUEST_STRATEGIES = [_V3RequestStrategy] class IdentityServer(object): @@ -137,13 +117,12 @@ class IdentityServer(object): def _get_strategy_class(self): if self._requested_auth_version: - # A specific version was requested. - if discover.version_match(_V3RequestStrategy.AUTH_VERSION, - self._requested_auth_version): - return _V3RequestStrategy - - # The version isn't v3 so we don't know what to do. Just assume V2. - return _V2RequestStrategy + if not discover.version_match(_V3RequestStrategy.AUTH_VERSION, + self._requested_auth_interface): + self._LOG.info('A version other than v3 was requested: %s', + self._requested_auth_interface) + # Return v3, even if the request is unknown + return _V3RequestStrategy # Specific version was not requested then we fall through to # discovering available versions from the server diff --git a/keystonemiddleware/auth_token/_request.py b/keystonemiddleware/auth_token/_request.py index 269c3d3..cba28bf 100644 --- a/keystonemiddleware/auth_token/_request.py +++ b/keystonemiddleware/auth_token/_request.py @@ -16,20 +16,16 @@ from oslo_serialization import jsonutils import webob -def _v3_to_v2_catalog(catalog): - """Convert a catalog to v2 format. - - X_SERVICE_CATALOG must be specified in v2 format. If you get a token - that is in v3 convert it. - """ - v2_services = [] +def _normalize_catalog(catalog): + """Convert a catalog to a compatible format.""" + services = [] for v3_service in catalog: # first copy over the entries we allow for the service - v2_service = {'type': v3_service['type']} + service = {'type': v3_service['type']} try: - v2_service['name'] = v3_service['name'] + service['name'] = v3_service['name'] except KeyError: # nosec - # v3 service doesn't have a name, so v2_service doesn't either. + # v3 service doesn't have a name, move on. pass # now convert the endpoints. Because in v3 we specify region per @@ -47,10 +43,10 @@ def _v3_to_v2_catalog(catalog): interface_name = v3_endpoint['interface'].lower() + 'URL' region[interface_name] = v3_endpoint['url'] - v2_service['endpoints'] = list(regions.values()) - v2_services.append(v2_service) + service['endpoints'] = list(regions.values()) + services.append(service) - return v2_services + return services def _is_admin_project(auth_ref): @@ -194,7 +190,7 @@ class _AuthTokenRequest(webob.Request): catalog = auth_ref.service_catalog.catalog if auth_ref.version == 'v3': - catalog = _v3_to_v2_catalog(catalog) + catalog = _normalize_catalog(catalog) c = jsonutils.dumps(catalog) self.headers[self._SERVICE_CATALOG_HEADER] = c diff --git a/keystonemiddleware/ec2_token.py b/keystonemiddleware/ec2_token.py index 5fe6096..faa5968 100644 --- a/keystonemiddleware/ec2_token.py +++ b/keystonemiddleware/ec2_token.py @@ -31,7 +31,7 @@ from keystonemiddleware.i18n import _ keystone_ec2_opts = [ cfg.StrOpt('url', - default='http://localhost:5000/v2.0/ec2tokens', + 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.'), @@ -185,13 +185,8 @@ class EC2Token(object): msg = _('Error response from keystone: %s') % response.reason self._logger.debug(msg) return self._ec2_error_response("AuthFailure", msg) - result = response.json() try: - if 'token' in result: - # NOTE(andrey-mp): response from keystone v3 - token_id = response.headers['x-subject-token'] - else: - token_id = result['access']['token']['id'] + token_id = response.headers['x-subject-token'] except (AttributeError, KeyError): msg = _("Failure parsing response from keystone") self._logger.exception(msg) diff --git a/keystonemiddleware/locale/en_GB/LC_MESSAGES/keystonemiddleware.po b/keystonemiddleware/locale/en_GB/LC_MESSAGES/keystonemiddleware.po index 0f043bf..ee8f5c3 100644 --- a/keystonemiddleware/locale/en_GB/LC_MESSAGES/keystonemiddleware.po +++ b/keystonemiddleware/locale/en_GB/LC_MESSAGES/keystonemiddleware.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: keystonemiddleware VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" -"POT-Creation-Date: 2018-04-21 04:01+0000\n" +"POT-Creation-Date: 2019-12-21 02:49+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -27,10 +27,6 @@ msgstr "Error response from Keystone: %s" msgid "Failed to fetch token data from identity server" msgstr "Failed to fetch token data from identity server" -#, python-format -msgid "Failed to fetch token revocation list: %d" -msgstr "Failed to fetch token revocation list: %d" - msgid "Failure parsing response from keystone" msgstr "Failure parsing response from Keystone" @@ -46,9 +42,6 @@ msgstr "Invalid version asked for in auth_token plugin" msgid "No compatible apis supported by server" msgstr "No compatible APIs supported by server" -msgid "Revocation list improperly formatted." -msgstr "Revocation list improperly formatted." - msgid "Signature not provided" msgstr "Signature not provided" @@ -58,9 +51,6 @@ msgstr "The request you have made requires authentication." msgid "Token authorization failed" msgstr "Token authorisation failed" -msgid "Token has been revoked" -msgstr "Token has been revoked" - msgid "Unable to determine service tenancy." msgstr "Unable to determine service tenancy." @@ -70,7 +60,3 @@ msgid "" msgstr "" "memcache_secret_key must be defined when a memcache_security_strategy is " "defined" - -#, python-format -msgid "unable to access signing_dir %s" -msgstr "unable to access signing_dir %s" diff --git a/keystonemiddleware/locale/ko_KR/LC_MESSAGES/keystonemiddleware.po b/keystonemiddleware/locale/ko_KR/LC_MESSAGES/keystonemiddleware.po index 02221ae..7a550a9 100644 --- a/keystonemiddleware/locale/ko_KR/LC_MESSAGES/keystonemiddleware.po +++ b/keystonemiddleware/locale/ko_KR/LC_MESSAGES/keystonemiddleware.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: keystonemiddleware VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" -"POT-Creation-Date: 2018-02-20 19:27+0000\n" +"POT-Creation-Date: 2019-12-21 02:49+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -27,10 +27,6 @@ msgstr "keystone 오류 응답: %s" msgid "Failed to fetch token data from identity server" msgstr "인증 서버로부터 토큰 데이터를 가져올 수 없습니다" -#, python-format -msgid "Failed to fetch token revocation list: %d" -msgstr "토큰 revocation 목록을 가져올 수 없습니다: %d" - msgid "Failure parsing response from keystone" msgstr "keystone 응답 파싱 실패" diff --git a/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py b/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py index a09d269..9801540 100644 --- a/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py +++ b/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py @@ -22,7 +22,6 @@ from keystoneauth1 import exceptions as ksa_exceptions from keystoneauth1 import fixture from keystoneauth1 import loading from keystoneauth1 import session -from keystoneclient import exceptions as ksc_exceptions import mock import oslo_cache from oslo_log import log as logging @@ -590,7 +589,7 @@ class CommonAuthTokenMiddlewareTest(object): return self.middleware._token_cache.get(token) def test_memcache_set_invalid_uuid(self): - invalid_uri = "%s/v2.0/tokens/invalid-token" % BASE_URI + invalid_uri = "%s/v3/tokens/invalid-token" % BASE_URI self.requests_mock.get(invalid_uri, status_code=404) token = 'invalid-token' @@ -601,7 +600,7 @@ class CommonAuthTokenMiddlewareTest(object): def test_memcache_hit_invalid_token(self): token = 'invalid-token' - invalid_uri = '%s/v2.0/tokens/invalid-token' % BASE_URI + invalid_uri = '%s/v3/tokens/invalid-token' % BASE_URI self.requests_mock.get(invalid_uri, status_code=404) # Call once to cache token's invalid state; verify it cached as such @@ -1016,169 +1015,6 @@ def request_timeout_response(request, context): "Request to https://host/token/path timed out") -class v2AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, - CommonAuthTokenMiddlewareTest, - testresources.ResourcedTestCase): - """v2 token specific tests. - - There are some differences between how the auth-token middleware handles - v2 and v3 tokens over and above the token formats, namely: - - - A v3 keystone server will auto scope a token to a user's default project - if no scope is specified. A v2 server assumes that the auth-token - middleware will do that. - - A v2 keystone server may issue a token without a catalog, even with a - tenant - - The tests below were originally part of the generic AuthTokenMiddlewareTest - class, but now, since they really are v2 specific, they are included here. - - """ - - resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)] - - def setUp(self): - super(v2AuthTokenMiddlewareTest, self).setUp() - - self.token_dict = { - 'uuid_token_default': self.examples.UUID_TOKEN_DEFAULT, - 'uuid_token_unscoped': self.examples.UUID_TOKEN_UNSCOPED, - 'uuid_token_bind': self.examples.UUID_TOKEN_BIND, - 'uuid_token_unknown_bind': self.examples.UUID_TOKEN_UNKNOWN_BIND, - 'uuid_service_token_default': - self.examples.UUID_SERVICE_TOKEN_DEFAULT, - } - - self.requests_mock.get(BASE_URI, - json=VERSION_LIST_v2, - status_code=300) - - self.requests_mock.post('%s/v2.0/tokens' % BASE_URI, - text=FAKE_ADMIN_TOKEN) - - for token in (self.examples.UUID_TOKEN_DEFAULT, - self.examples.UUID_TOKEN_UNSCOPED, - self.examples.UUID_TOKEN_BIND, - self.examples.UUID_TOKEN_UNKNOWN_BIND, - self.examples.UUID_TOKEN_NO_SERVICE_CATALOG, - self.examples.UUID_SERVICE_TOKEN_DEFAULT,): - url = "%s/v2.0/tokens/%s" % (BASE_URI, token) - text = self.examples.JSON_TOKEN_RESPONSES[token] - self.requests_mock.get(url, text=text) - - url = '%s/v2.0/tokens/%s' % (BASE_URI, ERROR_TOKEN) - self.requests_mock.get(url, text=network_error_response) - - url = '%s/v2.0/tokens/%s' % (BASE_URI, TIMEOUT_TOKEN) - self.requests_mock.get(url, text=request_timeout_response) - - self.set_middleware() - - def assert_unscoped_default_tenant_auto_scopes(self, token): - """Unscoped v2 requests with a default tenant should ``auto-scope``. - - The implied scope is the user's tenant ID. - - """ - resp = self.call_middleware(headers={'X-Auth-Token': token}) - self.assertEqual(FakeApp.SUCCESS, resp.body) - self.assertIn('keystone.token_info', resp.request.environ) - - def assert_valid_last_url(self, token_id): - self.assertLastPath("/v2.0/tokens/%s" % token_id) - - def test_default_tenant_uuid_token(self): - self.assert_unscoped_default_tenant_auto_scopes( - self.examples.UUID_TOKEN_DEFAULT) - - def assert_unscoped_token_receives_401(self, token): - """Unscoped requests with no default tenant ID should be rejected.""" - resp = self.call_middleware(headers={'X-Auth-Token': token}, - expected_status=401) - self.assertEqual('Keystone uri="https://keystone.example.com:1234"', - resp.headers['WWW-Authenticate']) - - def test_request_prevent_service_catalog_injection(self): - token = self.examples.UUID_TOKEN_NO_SERVICE_CATALOG - resp = self.call_middleware(headers={'X-Service-Catalog': '[]', - 'X-Auth-Token': token}) - - self.assertFalse(resp.request.headers.get('X-Service-Catalog')) - self.assertEqual(FakeApp.SUCCESS, resp.body) - - def test_user_plugin_token_properties(self): - token = self.examples.UUID_TOKEN_DEFAULT - token_data = self.examples.TOKEN_RESPONSES[token] - service = self.examples.UUID_SERVICE_TOKEN_DEFAULT - - resp = self.call_middleware(headers={'X-Service-Catalog': '[]', - 'X-Auth-Token': token, - 'X-Service-Token': service}) - - self.assertEqual(FakeApp.SUCCESS, resp.body) - - token_auth = resp.request.environ['keystone.token_auth'] - - self.assertTrue(token_auth.has_user_token) - self.assertTrue(token_auth.has_service_token) - - self.assertEqual(token_data.user_id, token_auth.user.user_id) - self.assertEqual(token_data.tenant_id, token_auth.user.project_id) - - self.assertThat(token_auth.user.role_names, matchers.HasLength(2)) - self.assertIn('role1', token_auth.user.role_names) - self.assertIn('role2', token_auth.user.role_names) - - self.assertIsNone(token_auth.user.trust_id) - self.assertIsNone(token_auth.user.user_domain_id) - self.assertIsNone(token_auth.user.project_domain_id) - - self.assertThat(token_auth.service.role_names, matchers.HasLength(2)) - self.assertIn('service', token_auth.service.role_names) - self.assertIn('service_role2', token_auth.service.role_names) - - self.assertIsNone(token_auth.service.trust_id) - - -class CrossVersionAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, - testresources.ResourcedTestCase): - - resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)] - - def test_valid_uuid_request_forced_to_2_0(self): - """Test forcing auth_token to use lower api version. - - By installing the v3 http hander, auth_token will be get - a version list that looks like a v3 server - from which it - would normally chose v3.0 as the auth version. However, here - we specify v2.0 in the configuration - which should force - auth_token to use that version instead. - - """ - conf = { - 'auth_version': 'v2.0' - } - - self.requests_mock.get(BASE_URI, - json=VERSION_LIST_v3, - status_code=300) - - self.requests_mock.post('%s/v2.0/tokens' % BASE_URI, - text=FAKE_ADMIN_TOKEN) - - token = self.examples.UUID_TOKEN_DEFAULT - url = "%s/v2.0/tokens/%s" % (BASE_URI, token) - text = self.examples.JSON_TOKEN_RESPONSES[token] - self.requests_mock.get(url, text=text) - - self.set_middleware(conf=conf) - - # This tests will only work is auth_token has chosen to use the - # lower, v2, api version - self.call_middleware(headers={'X-Auth-Token': token}) - self.assertEqual(url, self.requests_mock.last_request.url) - - class v3AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, CommonAuthTokenMiddlewareTest, testresources.ResourcedTestCase): @@ -1190,19 +1026,7 @@ class v3AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, This is done by configuring the AuthTokenMiddlewareTest class via its Setup(), passing in v3 style data that will then be used by - the tests themselves. This approach has been used to ensure we - really are running the same tests for both v2 and v3 tokens. - - There a few additional specific test for v3 only: - - - We allow an unscoped token to be validated (as unscoped), where - as for v2 tokens, the auth_token middleware is expected to try and - auto-scope it (and fail if there is no default tenant) - - Domain scoped tokens - - Since we don't specify an auth version for auth_token to use, by - definition we are thefore implicitely testing that it will use - the highest available auth version, i.e. v3.0 + the tests themselves. """ @@ -1527,7 +1351,7 @@ class DelayedAuthTests(BaseAuthTokenMiddlewareTest): if request.headers.get('X-Subject-Token') == ERROR_TOKEN: msg = 'Network connection refused.' - raise ksc_exceptions.ConnectionRefused(msg) + raise ksa_exceptions.ConnectFailure(msg) # All others just fail context.status_code = 404 @@ -1537,7 +1361,7 @@ class DelayedAuthTests(BaseAuthTokenMiddlewareTest): body = uuid.uuid4().hex www_authenticate_uri = 'http://local.test' conf = {'delay_auth_decision': 'True', - 'auth_version': 'v3.0', + 'auth_version': 'v3', 'www_authenticate_uri': www_authenticate_uri} middleware = self.create_simple_middleware(status='401 Unauthorized', @@ -1841,59 +1665,6 @@ class CommonCompositeAuthTests(object): bind_level='required') -class v2CompositeAuthTests(BaseAuthTokenMiddlewareTest, - CommonCompositeAuthTests, - testresources.ResourcedTestCase): - """Test auth_token middleware with v2 token based composite auth. - - Execute the Composite auth class tests, but with the - auth_token middleware configured to expect v2 tokens back from - a keystone server. - """ - - resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)] - - def setUp(self): - super(v2CompositeAuthTests, self).setUp( - expected_env=EXPECTED_V2_DEFAULT_SERVICE_ENV_RESPONSE, - fake_app=CompositeFakeApp) - - uuid_token_default = self.examples.UUID_TOKEN_DEFAULT - uuid_service_token_default = self.examples.UUID_SERVICE_TOKEN_DEFAULT - uuid_token_bind = self.examples.UUID_TOKEN_BIND - uuid_service_token_bind = self.examples.UUID_SERVICE_TOKEN_BIND - self.token_dict = { - 'uuid_token_default': uuid_token_default, - 'uuid_service_token_default': uuid_service_token_default, - 'uuid_token_bind': uuid_token_bind, - 'uuid_service_token_bind': uuid_service_token_bind, - } - - self.requests_mock.get(BASE_URI, - json=VERSION_LIST_v2, - status_code=300) - - self.requests_mock.post('%s/v2.0/tokens' % BASE_URI, - text=FAKE_ADMIN_TOKEN) - - for token in (self.examples.UUID_TOKEN_DEFAULT, - self.examples.UUID_SERVICE_TOKEN_DEFAULT, - self.examples.UUID_TOKEN_BIND, - self.examples.UUID_SERVICE_TOKEN_BIND): - text = self.examples.JSON_TOKEN_RESPONSES[token] - self.requests_mock.get('%s/v2.0/tokens/%s' % (BASE_URI, token), - text=text) - - for invalid_uri in ("%s/v2.0/tokens/invalid-token" % BASE_URI, - "%s/v2.0/tokens/invalid-service-token" % BASE_URI): - self.requests_mock.get(invalid_uri, text='', status_code=404) - - self.token_expected_env = dict(EXPECTED_V2_DEFAULT_ENV_RESPONSE) - self.service_token_expected_env = dict( - EXPECTED_V2_DEFAULT_SERVICE_ENV_RESPONSE) - self.set_middleware() - - class v3CompositeAuthTests(BaseAuthTokenMiddlewareTest, CommonCompositeAuthTests, testresources.ResourcedTestCase): @@ -1908,7 +1679,7 @@ class v3CompositeAuthTests(BaseAuthTokenMiddlewareTest, def setUp(self): super(v3CompositeAuthTests, self).setUp( - auth_version='v3.0', + auth_version='v3', fake_app=v3CompositeFakeApp) uuid_token_default = self.examples.v3_UUID_TOKEN_DEFAULT @@ -1951,7 +1722,7 @@ class v3CompositeAuthTests(BaseAuthTokenMiddlewareTest, if token_id == ERROR_TOKEN: msg = "Network connection refused." - raise ksc_exceptions.ConnectionRefused(msg) + raise ksa_exceptions.ConnectFailure(msg) elif token_id == TIMEOUT_TOKEN: request_timeout_response(request, context) @@ -1979,7 +1750,7 @@ class OtherTests(BaseAuthTokenMiddlewareTest): self.call_middleware(headers={'X-Auth-Token': uuid.uuid4().hex}, expected_status=503) - self.assertIn('versions [v3.0, v2.0]', self.logger.output) + self.assertIn('versions [v3.0]', self.logger.output) def _assert_auth_version(self, conf_version, identity_server_version): self.set_middleware(conf={'auth_version': conf_version}) @@ -1988,8 +1759,6 @@ class OtherTests(BaseAuthTokenMiddlewareTest): identity_server.auth_version) def test_micro_version(self): - self._assert_auth_version('v2', (2, 0)) - self._assert_auth_version('v2.0', (2, 0)) self._assert_auth_version('v3', (3, 0)) self._assert_auth_version('v3.0', (3, 0)) self._assert_auth_version('v3.1', (3, 0)) @@ -2003,14 +1772,10 @@ class OtherTests(BaseAuthTokenMiddlewareTest): self.requests_mock.get(BASE_URI, json=VERSION_LIST_v3, status_code=300) self._assert_auth_version(None, (3, 0)) - # VERSION_LIST_v2 contains only v2 version elements - self.requests_mock.get(BASE_URI, json=VERSION_LIST_v2, status_code=300) - self._assert_auth_version(None, (2, 0)) - def test_unsupported_auth_version(self): - # If the requested version isn't supported we will use v2 - self._assert_auth_version('v1', (2, 0)) - self._assert_auth_version('v10', (2, 0)) + # If the requested version isn't supported we will use v3 + self._assert_auth_version('v1', (3, 0)) + self._assert_auth_version('v10', (3, 0)) class AuthProtocolLoadingTests(BaseAuthTokenMiddlewareTest): @@ -2020,9 +1785,7 @@ class AuthProtocolLoadingTests(BaseAuthTokenMiddlewareTest): KEYSTONE_BASE_URL = 'http://keystone.url/prefix' CRUD_URL = 'http://crud.url/prefix' - # NOTE(jamielennox): use the /v2.0 prefix here because this is what's most - # likely to be in the service catalog and we should be able to ignore it. - KEYSTONE_URL = KEYSTONE_BASE_URL + '/v2.0' + KEYSTONE_URL = KEYSTONE_BASE_URL + '/v3' def setUp(self): super(AuthProtocolLoadingTests, self).setUp() diff --git a/keystonemiddleware/tests/unit/auth_token/test_request.py b/keystonemiddleware/tests/unit/auth_token/test_request.py index bd8a7b3..011525f 100644 --- a/keystonemiddleware/tests/unit/auth_token/test_request.py +++ b/keystonemiddleware/tests/unit/auth_token/test_request.py @@ -224,7 +224,7 @@ class CatalogConversionTests(utils.TestCase): auth_ref = access.create(body=token) catalog_data = auth_ref.service_catalog.catalog - catalog = _request._v3_to_v2_catalog(catalog_data) + catalog = _request._normalize_catalog(catalog_data) self.assertEqual(1, len(catalog)) service = catalog[0] @@ -248,7 +248,7 @@ class CatalogConversionTests(utils.TestCase): auth_ref = access.create(body=token) catalog_data = auth_ref.service_catalog.catalog - catalog = _request._v3_to_v2_catalog(catalog_data) + catalog = _request._normalize_catalog(catalog_data) self.assertEqual(1, len(catalog)) service = catalog[0] diff --git a/keystonemiddleware/tests/unit/auth_token/test_user_auth_plugin.py b/keystonemiddleware/tests/unit/auth_token/test_user_auth_plugin.py index 8749993..554b2c3 100644 --- a/keystonemiddleware/tests/unit/auth_token/test_user_auth_plugin.py +++ b/keystonemiddleware/tests/unit/auth_token/test_user_auth_plugin.py @@ -80,72 +80,6 @@ class BaseUserPluginTests(object): self.assertTokenDataEqual(service_id, service, plugin.service) -class V2UserPluginTests(BaseUserPluginTests, base.BaseAuthTokenTestCase): - - def setUp(self): - super(V2UserPluginTests, self).setUp() - - self.service_token = fixture.V2Token() - self.service_token.set_scope() - s = self.service_token.add_service('identity', name='keystone') - - s.add_endpoint(public=BASE_URI, - admin=BASE_URI, - internal=BASE_URI) - - self.configure_middleware(auth_type='v2password', - auth_url='%s/v2.0/' % AUTH_URL, - user_id=self.service_token.user_id, - password=uuid.uuid4().hex, - tenant_id=self.service_token.tenant_id) - - auth_discovery = fixture.DiscoveryList(href=AUTH_URL, v3=False) - self.requests_mock.get(AUTH_URL, json=auth_discovery) - - base_discovery = fixture.DiscoveryList(href=BASE_URI, v3=False) - self.requests_mock.get(BASE_URI, json=base_discovery) - - url = '%s/v2.0/tokens' % AUTH_URL - self.requests_mock.post(url, json=self.service_token) - - def get_role_names(self, token): - return [x['name'] for x in token['access']['user'].get('roles', [])] - - def get_token(self, service=False): - token = fixture.V2Token() - token.set_scope() - token.add_role() - if service: - token.add_role('service') - - request_headers = {'X-Auth-Token': self.service_token.token_id} - - url = '%s/v2.0/tokens/%s' % (BASE_URI, token.token_id) - self.requests_mock.get(url, - request_headers=request_headers, - json=token) - - return token.token_id, token - - def assertTokenDataEqual(self, token_id, token, token_data): - super(V2UserPluginTests, self).assertTokenDataEqual(token_id, - token, - token_data) - - self.assertEqual(token.tenant_id, token_data.project_id) - self.assertIsNone(token_data.user_domain_id) - self.assertIsNone(token_data.project_domain_id) - - def test_trust_scope(self): - token_id, token = self.get_token() - token.set_trust() - - plugin = self.get_plugin(token_id) - self.assertEqual(token.trust_id, plugin.user.trust_id) - self.assertEqual(token.trustee_user_id, plugin.user.trustee_user_id) - self.assertIsNone(plugin.user.trustor_user_id) - - class V3UserPluginTests(BaseUserPluginTests, base.BaseAuthTokenTestCase): def setUp(self): diff --git a/keystonemiddleware/tests/unit/test_ec2_token_middleware.py b/keystonemiddleware/tests/unit/test_ec2_token_middleware.py index 5191502..5cd69ff 100644 --- a/keystonemiddleware/tests/unit/test_ec2_token_middleware.py +++ b/keystonemiddleware/tests/unit/test_ec2_token_middleware.py @@ -23,13 +23,12 @@ from keystonemiddleware.tests.unit import utils TOKEN_ID = 'fake-token-id' -GOOD_RESPONSE = {'access': {'token': {'id': TOKEN_ID, - 'tenant': {'id': 'TENANT_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 @@ -53,9 +52,9 @@ class EC2TokenMiddlewareTestBase(utils.TestCase): TEST_PROTOCOL = 'https' TEST_HOST = 'fakehost' TEST_PORT = 35357 - TEST_URL = '%s://%s:%d/v2.0/ec2tokens' % (TEST_PROTOCOL, - TEST_HOST, - TEST_PORT) + TEST_URL = '%s://%s:%d/v3/ec2tokens' % (TEST_PROTOCOL, + TEST_HOST, + TEST_PORT) def setUp(self): super(EC2TokenMiddlewareTestBase, self).setUp() @@ -74,7 +73,7 @@ class EC2TokenMiddlewareTestBase(utils.TestCase): class EC2TokenMiddlewareTestGood(EC2TokenMiddlewareTestBase): @mock.patch.object( requests, 'request', - return_value=FakeResponse(GOOD_RESPONSE, status_code=200)) + 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' @@ -85,7 +84,7 @@ class EC2TokenMiddlewareTestGood(EC2TokenMiddlewareTestBase): self.assertEqual(TOKEN_ID, req.headers['X-Auth-Token']) mock_request.assert_called_with( - 'POST', 'http://localhost:5000/v2.0/ec2tokens', + 'POST', 'http://localhost:5000/v3/ec2tokens', data=mock.ANY, headers={'Content-Type': 'application/json'}, verify=True, cert=None) @@ -105,7 +104,7 @@ class EC2TokenMiddlewareTestGood(EC2TokenMiddlewareTestBase): @mock.patch.object( requests, 'request', - return_value=FakeResponse(GOOD_RESPONSE, status_code=200)) + return_value=FakeResponse(EMPTY_RESPONSE, status_code=200)) def test_protocol_v4(self, mock_request): req = webob.Request.blank('/test') auth_str = ( @@ -120,7 +119,7 @@ class EC2TokenMiddlewareTestGood(EC2TokenMiddlewareTestBase): self.assertEqual(TOKEN_ID, req.headers['X-Auth-Token']) mock_request.assert_called_with( - 'POST', 'http://localhost:5000/v2.0/ec2tokens', + 'POST', 'http://localhost:5000/v3/ec2tokens', data=mock.ANY, headers={'Content-Type': 'application/json'}, verify=True, cert=None) diff --git a/releasenotes/notes/ec2-v2-removal-6a886210cbc9d3e9.yaml b/releasenotes/notes/ec2-v2-removal-6a886210cbc9d3e9.yaml new file mode 100644 index 0000000..e90fa68 --- /dev/null +++ b/releasenotes/notes/ec2-v2-removal-6a886210cbc9d3e9.yaml @@ -0,0 +1,7 @@ +--- +other: + - | + [`bug 1845539 <https://bugs.launchpad.net/keystone/+bug/1845539>`_] + The ec2 'url' config option now defaults to + https://localhost:5000/v3/ec2tokens with the removal of ec2 v2.0 support. + Keystonemiddleware no longer supports ec2tokens using the v2.0 API. diff --git a/releasenotes/notes/removed-as-of-ussuri-4e1ea485ba8801c9.yaml b/releasenotes/notes/removed-as-of-ussuri-4e1ea485ba8801c9.yaml new file mode 100644 index 0000000..1dafbfb --- /dev/null +++ b/releasenotes/notes/removed-as-of-ussuri-4e1ea485ba8801c9.yaml @@ -0,0 +1,7 @@ +--- +upgrade: + - | + [`bug 1845539 <https://bugs.launchpad.net/keystone/+bug/1845539>`_] + [`bug 1777177 <https://bugs.launchpad.net/keystone/+bug/1777177>`_] + keystonemiddleware no longer supports the keystone v2.0 api, all + associated functionality has been removed. diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 62d6b53..7f0eeb6 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + train stein rocky queens diff --git a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po index ff2a6ec..0710425 100644 --- a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po +++ b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po @@ -1,14 +1,15 @@ # Andi Chandler <andi@gowling.com>, 2017. #zanata # Andi Chandler <andi@gowling.com>, 2018. #zanata +# Andi Chandler <andi@gowling.com>, 2019. #zanata msgid "" msgstr "" "Project-Id-Version: keystonemiddleware\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-04-21 04:01+0000\n" +"POT-Creation-Date: 2019-12-21 02:49+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2018-04-30 10:08+0000\n" +"PO-Revision-Date: 2019-12-21 02:38+0000\n" "Last-Translator: Andi Chandler <andi@gowling.com>\n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" @@ -24,6 +25,9 @@ msgstr "4.12.0" msgid "4.16.0" msgstr "4.16.0" +msgid "4.17.1" +msgstr "4.17.1" + msgid "4.18.0" msgstr "4.18.0" @@ -33,6 +37,9 @@ msgstr "4.2.0" msgid "4.20.0" msgstr "4.20.0" +msgid "4.22.0" +msgstr "4.22.0" + msgid "4.3.0" msgstr "4.3.0" @@ -45,8 +52,29 @@ msgstr "4.6.0" msgid "5.0.0" msgstr "5.0.0" -msgid "5.0.0-5" -msgstr "5.0.0-5" +msgid "5.1.0" +msgstr "5.1.0" + +msgid "5.2.0" +msgstr "5.2.0" + +msgid "5.2.1" +msgstr "5.2.1" + +msgid "5.3.0" +msgstr "5.3.0" + +msgid "6.0.0" +msgstr "6.0.0" + +msgid "6.1.0" +msgstr "6.1.0" + +msgid "7.0.0" +msgstr "7.0.0" + +msgid "7.0.1-8" +msgstr "7.0.1-8" msgid "" "A new configuration option for the s3token middleware called auth_uri can be " @@ -135,6 +163,9 @@ msgstr "Prelude" msgid "Queens Series Release Notes" msgstr "Queens Series Release Notes" +msgid "Rocky Series Release Notes" +msgstr "Rocky Series Release Notes" + msgid "Security Issues" msgstr "Security Issues" @@ -170,6 +201,9 @@ msgstr "" "enforce this behaviour. This will become the default setting in future " "releases." +msgid "Stein Series Release Notes" +msgstr "Stein Series Release Notes" + msgid "" "The ``kwargs_to_fetch_token`` setting was removed from the " "``BaseAuthProtocol`` class. Implementations of auth_token now assume kwargs " @@ -201,10 +235,28 @@ msgstr "" "returned when the user needs to be redirected to the Identity service for " "authentication." +msgid "Train Series Release Notes" +msgstr "Train Series Release Notes" + msgid "Upgrade Notes" msgstr "Upgrade Notes" msgid "" +"When ``delay_auth_decision`` is enabled and a Keystone failure prevents a " +"final decision about whether a token is valid or invalid, it will be marked " +"invalid and the application will be responsible for a final auth decision. " +"This is similar to what happens when a token is confirmed *not* valid. This " +"allows a Keystone outage to only affect Keystone users in a multi-auth " +"system." +msgstr "" +"When ``delay_auth_decision`` is enabled and a Keystone failure prevents a " +"final decision about whether a token is valid or invalid, it will be marked " +"invalid and the application will be responsible for a final auth decision. " +"This is similar to what happens when a token is confirmed *not* valid. This " +"allows a Keystone outage to only affect Keystone users in a multi-auth " +"system." + +msgid "" "With the release of 4.2.0 of keystonemiddleware we no longer recommend using " "the in-process token cache. In-process caching may result in inconsistent " "validation, poor UX and race conditions. It is recommended that the " @@ -363,6 +415,19 @@ msgstr "" "look for the given option in local config, then Oslo global config." msgid "" +"[`bug 1649735 <https://bugs.launchpad.net/keystone/+bug/1649735>`_] The " +"auth_token middleware no longer attempts to retrieve the revocation list " +"from the Keystone server. The deprecated options " +"`check_revocations_for_cached` and `check_revocations_for_cached` have been " +"removed." +msgstr "" +"[`bug 1649735 <https://bugs.launchpad.net/keystone/+bug/1649735>`_] The " +"auth_token middleware no longer attempts to retrieve the revocation list " +"from the Keystone server. The deprecated options " +"`check_revocations_for_cached` and `check_revocations_for_cached` have been " +"removed." + +msgid "" "[`bug 1677308 <https://bugs.launchpad.net/keystonemiddleware/" "+bug/1677308>`_] Removes ``pycrypto`` dependency as the library is " "unmaintained, and replaces it with the ``cryptography`` library." @@ -439,6 +504,137 @@ msgstr "" "(Unauthorised) response now is double quoted to follow the RFC requirement." msgid "" +"[`bug 1766731 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1766731>`_] Keystonemiddleware now supports system scoped tokens. When " +"a system-scoped token is parsed by auth_token middleware, it will set the " +"``OpenStack-System-Scope`` header accordingly." +msgstr "" +"[`bug 1766731 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1766731>`_] Keystonemiddleware now supports system scoped tokens. When " +"a system-scoped token is parsed by auth_token middleware, it will set the " +"``OpenStack-System-Scope`` header accordingly." + +msgid "" +"[`bug 1782404 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1782404>`_] Keystonemiddleware incorrectly implemented an abstraction " +"for the memcache client pool that utilized a `queue.Queue` `get` method " +"instead of the supplied `acquire()` context manager. The `acquire()` context " +"manager properly places the client connection back into the pool after " +"`__exit__`." +msgstr "" +"[`bug 1782404 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1782404>`_] Keystonemiddleware incorrectly implemented an abstraction " +"for the memcache client pool that utilized a `queue.Queue` `get` method " +"instead of the supplied `acquire()` context manager. The `acquire()` context " +"manager properly places the client connection back into the pool after " +"`__exit__`." + +msgid "" +"[`bug 1789351 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1789351>`_] Fixed the bug that when initialize `AuthProtocol`, it'll " +"raise \"dictionary changed size during iteration\" error if the input `CONF` " +"object contains deprecated options." +msgstr "" +"[`bug 1789351 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1789351>`_] Fixed the bug that when initialize `AuthProtocol`, it'll " +"raise \"dictionary changed size during iteration\" error if the input `CONF` " +"object contains deprecated options." + +msgid "" +"[`bug 1797584 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1797584>`_] Fixed a bug where the audit code would select the wrong " +"target service if the OpenStack service endpoints were not using unique TCP " +"ports." +msgstr "" +"[`bug 1797584 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1797584>`_] Fixed a bug where the audit code would select the wrong " +"target service if the OpenStack service endpoints were not using unique TCP " +"ports." + +msgid "" +"[`bug 1800017 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1800017>`_] Fix audit middleware service catalog parsing for the " +"scenario where a service does not contain any endpoints. In that case, we " +"should just skip over that service." +msgstr "" +"[`bug 1800017 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1800017>`_] Fix audit middleware service catalog parsing for the " +"scenario where a service does not contain any endpoints. In that case, we " +"should just skip over that service." + +msgid "" +"[`bug 1803940 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1803940>`_] Request ID and global request ID have been added to CADF " +"notifications." +msgstr "" +"[`bug 1803940 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1803940>`_] Request ID and global request ID have been added to CADF " +"notifications." + +msgid "" +"[`bug 1809101 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1809101>`_] Fix req.context of Keystone audit middleware and Glance " +"conflict with each other issue. The audit middleware now stores the admin " +"context to req.environ['audit.context']." +msgstr "" +"[`bug 1809101 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1809101>`_] Fix req.context of Keystone audit middleware and Glance " +"conflict with each other issue. The audit middleware now stores the admin " +"context to req.environ['audit.context']." + +msgid "" +"[`bug 1813739 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1813739>`_] When admin identity endpoint is not created yet, " +"keystonemiddleware emit EndpointNotFound exception. Even after admin " +"identity endpoint created, auth_token middleware could not be notified of " +"update since it does not invalidate existing auth. Add an invalidation step " +"so that endpoint updates can be detected." +msgstr "" +"[`bug 1813739 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1813739>`_] When admin identity endpoint is not created yet, " +"keystonemiddleware emit EndpointNotFound exception. Even after admin " +"identity endpoint created, auth_token middleware could not be notified of " +"update since it does not invalidate existing auth. Add an invalidation step " +"so that endpoint updates can be detected." + +msgid "" +"[`bug 1830002 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1830002>`_] In order to allow an installation to work without deploying " +"an admin Identity endpoint, a new option `interface` has been added, " +"allowing select the Identity endpoint that is being used when verifying auth " +"tokens. It defaults to `admin` in order to replicate the old behaviour, but " +"may be set to `public` or `internal` as needed." +msgstr "" +"[`bug 1830002 <https://bugs.launchpad.net/keystonemiddleware/" +"+bug/1830002>`_] In order to allow an installation to work without deploying " +"an admin Identity endpoint, a new option `interface` has been added, " +"allowing select the Identity endpoint that is being used when verifying auth " +"tokens. It defaults to `admin` in order to replicate the old behaviour, but " +"may be set to `public` or `internal` as needed." + +msgid "" +"[`bug 1845539 <https://bugs.launchpad.net/keystone/+bug/1845539>`_] The ec2 " +"'url' config option now defaults to https://localhost:5000/v3/ec2tokens with " +"the removal of ec2 v2.0 support. Keystonemiddleware no longer supports " +"ec2tokens using the v2.0 API." +msgstr "" +"[`bug 1845539 <https://bugs.launchpad.net/keystone/+bug/1845539>`_] The ec2 " +"'url' config option now defaults to https://localhost:5000/v3/ec2tokens with " +"the removal of ec2 v2.0 support. Keystonemiddleware no longer supports " +"ec2tokens using the v2.0 API." + +msgid "" +"[`bug 1845539 <https://bugs.launchpad.net/keystone/+bug/1845539>`_] [`bug " +"1777177 <https://bugs.launchpad.net/keystone/+bug/1777177>`_] " +"keystonemiddleware no longer supports the keystone v2.0 api, all associated " +"functionality has been removed." +msgstr "" +"[`bug 1845539 <https://bugs.launchpad.net/keystone/+bug/1845539>`_] [`bug " +"1777177 <https://bugs.launchpad.net/keystone/+bug/1777177>`_] " +"keystonemiddleware no longer supports the Keystone v2.0 api, all associated " +"functionality has been removed." + +msgid "" "[`bug/1747655 <https://bugs.launchpad.net/keystonemiddleware/" "+bug/1747655>`_] When keystone is temporarily unavailable, " "keystonemiddleware correctly sends a 503 response to the HTTP client but was " @@ -453,5 +649,16 @@ msgstr "" "was keystone or the service using keystonemiddleware that was unavailable. " "This change identifies keystone in the error response." +msgid "" +"[`spec <http://specs.openstack.org/openstack/keystone-specs/specs/keystone/" +"train/capabilities-app-creds.html>`_] The auth_token middleware now has " +"support for accepting or denying incoming requests based on access rules " +"provided by users in their keystone application credentials." +msgstr "" +"[`spec <http://specs.openstack.org/openstack/keystone-specs/specs/keystone/" +"train/capabilities-app-creds.html>`_] The auth_token middleware now has " +"support for accepting or denying incoming requests based on access rules " +"provided by users in their Keystone application credentials." + msgid "keystonemiddleware Release Notes" msgstr "keystonemiddleware Release Notes" diff --git a/releasenotes/source/train.rst b/releasenotes/source/train.rst new file mode 100644 index 0000000..5839003 --- /dev/null +++ b/releasenotes/source/train.rst @@ -0,0 +1,6 @@ +========================== +Train Series Release Notes +========================== + +.. release-notes:: + :branch: stable/train @@ -64,6 +64,18 @@ commands= doc8 doc/source sphinx-build -W -b html doc/source doc/build/html +[testenv:pdf-docs] +basepython = python3 +envdir = {toxworkdir}/docs +deps = {[testenv:docs]deps} +whitelist_externals = + make + rm +commands = + rm -rf doc/build/pdf + sphinx-build -W -b latex doc/source doc/build/pdf + make -C doc/build/pdf + [testenv:releasenotes] basepython = python3 deps = -r{toxinidir}/doc/requirements.txt |