"Fossies" - the Fresh Open Source Software Archive

Member "keystone-17.0.0/keystone/tests/unit/test_associate_project_endpoint_extension.py" (13 May 2020, 62551 Bytes) of package /linux/misc/openstack/keystone-17.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_associate_project_endpoint_extension.py": 16.0.1_vs_17.0.0.

    1 # Copyright 2013 OpenStack Foundation
    2 #
    3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
    4 # not use this file except in compliance with the License. You may obtain
    5 # a copy of the License at
    6 #
    7 #      http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 # Unless required by applicable law or agreed to in writing, software
   10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 # License for the specific language governing permissions and limitations
   13 # under the License.
   14 
   15 import copy
   16 import uuid
   17 
   18 import http.client
   19 from testtools import matchers
   20 
   21 from keystone.common import provider_api
   22 from keystone.tests import unit
   23 from keystone.tests.unit import test_v3
   24 
   25 PROVIDERS = provider_api.ProviderAPIs
   26 
   27 
   28 class EndpointFilterTestCase(test_v3.RestfulTestCase):
   29 
   30     def setUp(self):
   31         super(EndpointFilterTestCase, self).setUp()
   32         self.default_request_url = (
   33             '/OS-EP-FILTER/projects/%(project_id)s'
   34             '/endpoints/%(endpoint_id)s' % {
   35                 'project_id': self.default_domain_project_id,
   36                 'endpoint_id': self.endpoint_id})
   37 
   38 
   39 class EndpointFilterCRUDTestCase(EndpointFilterTestCase):
   40 
   41     def test_create_endpoint_project_association(self):
   42         """PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
   43 
   44         Valid endpoint and project id test case.
   45 
   46         """
   47         self.put(self.default_request_url)
   48 
   49     def test_create_endpoint_project_association_with_invalid_project(self):
   50         """PUT OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
   51 
   52         Invalid project id test case.
   53 
   54         """
   55         self.put('/OS-EP-FILTER/projects/%(project_id)s'
   56                  '/endpoints/%(endpoint_id)s' % {
   57                      'project_id': uuid.uuid4().hex,
   58                      'endpoint_id': self.endpoint_id},
   59                  expected_status=http.client.NOT_FOUND)
   60 
   61     def test_create_endpoint_project_association_with_invalid_endpoint(self):
   62         """PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
   63 
   64         Invalid endpoint id test case.
   65 
   66         """
   67         self.put('/OS-EP-FILTER/projects/%(project_id)s'
   68                  '/endpoints/%(endpoint_id)s' % {
   69                      'project_id': self.default_domain_project_id,
   70                      'endpoint_id': uuid.uuid4().hex},
   71                  expected_status=http.client.NOT_FOUND)
   72 
   73     def test_create_endpoint_project_association_with_unexpected_body(self):
   74         """PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
   75 
   76         Unexpected body in request. The body should be ignored.
   77 
   78         """
   79         self.put(self.default_request_url,
   80                  body={'project_id': self.default_domain_project_id})
   81 
   82     def test_check_endpoint_project_association(self):
   83         """HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
   84 
   85         Valid project and endpoint id test case.
   86 
   87         """
   88         self.put(self.default_request_url)
   89         self.head('/OS-EP-FILTER/projects/%(project_id)s'
   90                   '/endpoints/%(endpoint_id)s' % {
   91                       'project_id': self.default_domain_project_id,
   92                       'endpoint_id': self.endpoint_id},
   93                   expected_status=http.client.NO_CONTENT)
   94 
   95     def test_check_endpoint_project_association_with_invalid_project(self):
   96         """HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
   97 
   98         Invalid project id test case.
   99 
  100         """
  101         self.put(self.default_request_url)
  102         self.head('/OS-EP-FILTER/projects/%(project_id)s'
  103                   '/endpoints/%(endpoint_id)s' % {
  104                       'project_id': uuid.uuid4().hex,
  105                       'endpoint_id': self.endpoint_id},
  106                   expected_status=http.client.NOT_FOUND)
  107 
  108     def test_check_endpoint_project_association_with_invalid_endpoint(self):
  109         """HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
  110 
  111         Invalid endpoint id test case.
  112 
  113         """
  114         self.put(self.default_request_url)
  115         self.head('/OS-EP-FILTER/projects/%(project_id)s'
  116                   '/endpoints/%(endpoint_id)s' % {
  117                       'project_id': self.default_domain_project_id,
  118                       'endpoint_id': uuid.uuid4().hex},
  119                   expected_status=http.client.NOT_FOUND)
  120 
  121     def test_get_endpoint_project_association(self):
  122         """GET /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
  123 
  124         Valid project and endpoint id test case.
  125 
  126         """
  127         self.put(self.default_request_url)
  128         self.get('/OS-EP-FILTER/projects/%(project_id)s'
  129                  '/endpoints/%(endpoint_id)s' % {
  130                      'project_id': self.default_domain_project_id,
  131                      'endpoint_id': self.endpoint_id},
  132                  expected_status=http.client.NO_CONTENT)
  133 
  134     def test_get_endpoint_project_association_with_invalid_project(self):
  135         """GET /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
  136 
  137         Invalid project id test case.
  138 
  139         """
  140         self.put(self.default_request_url)
  141         self.get('/OS-EP-FILTER/projects/%(project_id)s'
  142                  '/endpoints/%(endpoint_id)s' % {
  143                      'project_id': uuid.uuid4().hex,
  144                      'endpoint_id': self.endpoint_id},
  145                  expected_status=http.client.NOT_FOUND)
  146 
  147     def test_get_endpoint_project_association_with_invalid_endpoint(self):
  148         """GET /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
  149 
  150         Invalid endpoint id test case.
  151 
  152         """
  153         self.put(self.default_request_url)
  154         self.get('/OS-EP-FILTER/projects/%(project_id)s'
  155                  '/endpoints/%(endpoint_id)s' % {
  156                      'project_id': self.default_domain_project_id,
  157                      'endpoint_id': uuid.uuid4().hex},
  158                  expected_status=http.client.NOT_FOUND)
  159 
  160     def test_list_endpoints_associated_with_valid_project(self):
  161         """GET & HEAD /OS-EP-FILTER/projects/{project_id}/endpoints.
  162 
  163         Valid project and endpoint id test case.
  164 
  165         """
  166         self.put(self.default_request_url)
  167         resource_url = '/OS-EP-FILTER/projects/%(project_id)s/endpoints' % {
  168                        'project_id': self.default_domain_project_id}
  169         r = self.get(resource_url)
  170         self.assertValidEndpointListResponse(r, self.endpoint,
  171                                              resource_url=resource_url)
  172         self.head(resource_url, expected_status=http.client.OK)
  173 
  174     def test_list_endpoints_associated_with_invalid_project(self):
  175         """GET & HEAD /OS-EP-FILTER/projects/{project_id}/endpoints.
  176 
  177         Invalid project id test case.
  178 
  179         """
  180         self.put(self.default_request_url)
  181         url = ('/OS-EP-FILTER/projects/%(project_id)s/endpoints' % {
  182             'project_id': uuid.uuid4().hex}
  183         )
  184         self.get(url, expected_status=http.client.NOT_FOUND)
  185         self.head(url, expected_status=http.client.NOT_FOUND)
  186 
  187     def test_list_projects_associated_with_endpoint(self):
  188         """GET & HEAD /OS-EP-FILTER/endpoints/{endpoint_id}/projects.
  189 
  190         Valid endpoint-project association test case.
  191 
  192         """
  193         self.put(self.default_request_url)
  194         resource_url = '/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' % {
  195                        'endpoint_id': self.endpoint_id}
  196         r = self.get(resource_url, expected_status=http.client.OK)
  197         self.assertValidProjectListResponse(r, self.default_domain_project,
  198                                             resource_url=resource_url)
  199         self.head(resource_url, expected_status=http.client.OK)
  200 
  201     def test_list_projects_with_no_endpoint_project_association(self):
  202         """GET & HEAD /OS-EP-FILTER/endpoints/{endpoint_id}/projects.
  203 
  204         Valid endpoint id but no endpoint-project associations test case.
  205 
  206         """
  207         url = (
  208             '/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' %
  209             {'endpoint_id': self.endpoint_id}
  210         )
  211         r = self.get(url, expected_status=http.client.OK)
  212         self.assertValidProjectListResponse(r, expected_length=0)
  213         self.head(url, expected_status=http.client.OK)
  214 
  215     def test_list_projects_associated_with_invalid_endpoint(self):
  216         """GET & HEAD /OS-EP-FILTER/endpoints/{endpoint_id}/projects.
  217 
  218         Invalid endpoint id test case.
  219 
  220         """
  221         url = (
  222             '/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' %
  223             {'endpoint_id': uuid.uuid4().hex}
  224         )
  225         self.get(url, expected_status=http.client.NOT_FOUND)
  226         self.head(url, expected_status=http.client.NOT_FOUND)
  227 
  228     def test_remove_endpoint_project_association(self):
  229         """DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
  230 
  231         Valid project id and endpoint id test case.
  232 
  233         """
  234         self.put(self.default_request_url)
  235         self.delete('/OS-EP-FILTER/projects/%(project_id)s'
  236                     '/endpoints/%(endpoint_id)s' % {
  237                         'project_id': self.default_domain_project_id,
  238                         'endpoint_id': self.endpoint_id})
  239 
  240     def test_remove_endpoint_project_association_with_invalid_project(self):
  241         """DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
  242 
  243         Invalid project id test case.
  244 
  245         """
  246         self.put(self.default_request_url)
  247         self.delete('/OS-EP-FILTER/projects/%(project_id)s'
  248                     '/endpoints/%(endpoint_id)s' % {
  249                         'project_id': uuid.uuid4().hex,
  250                         'endpoint_id': self.endpoint_id},
  251                     expected_status=http.client.NOT_FOUND)
  252 
  253     def test_remove_endpoint_project_association_with_invalid_endpoint(self):
  254         """DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}.
  255 
  256         Invalid endpoint id test case.
  257 
  258         """
  259         self.put(self.default_request_url)
  260         self.delete('/OS-EP-FILTER/projects/%(project_id)s'
  261                     '/endpoints/%(endpoint_id)s' % {
  262                         'project_id': self.default_domain_project_id,
  263                         'endpoint_id': uuid.uuid4().hex},
  264                     expected_status=http.client.NOT_FOUND)
  265 
  266     def test_endpoint_project_association_cleanup_when_project_deleted(self):
  267         self.put(self.default_request_url)
  268         association_url = ('/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' %
  269                            {'endpoint_id': self.endpoint_id})
  270         r = self.get(association_url)
  271         self.assertValidProjectListResponse(r, expected_length=1)
  272 
  273         self.delete('/projects/%(project_id)s' % {
  274             'project_id': self.default_domain_project_id})
  275 
  276         r = self.get(association_url)
  277         self.assertValidProjectListResponse(r, expected_length=0)
  278 
  279     def test_endpoint_project_association_cleanup_when_endpoint_deleted(self):
  280         self.put(self.default_request_url)
  281         association_url = '/OS-EP-FILTER/projects/%(project_id)s/endpoints' % {
  282             'project_id': self.default_domain_project_id}
  283         r = self.get(association_url)
  284         self.assertValidEndpointListResponse(r, expected_length=1)
  285 
  286         self.delete('/endpoints/%(endpoint_id)s' % {
  287             'endpoint_id': self.endpoint_id})
  288 
  289         r = self.get(association_url)
  290         self.assertValidEndpointListResponse(r, expected_length=0)
  291 
  292     @unit.skip_if_cache_disabled('catalog')
  293     def test_create_endpoint_project_association_invalidates_cache(self):
  294         # NOTE(davechen): create another endpoint which will be added to
  295         # default project, this should be done at first since
  296         # `create_endpoint` will also invalidate cache.
  297         endpoint_id2 = uuid.uuid4().hex
  298         endpoint2 = unit.new_endpoint_ref(service_id=self.service_id,
  299                                           region_id=self.region_id,
  300                                           interface='public',
  301                                           id=endpoint_id2)
  302         PROVIDERS.catalog_api.create_endpoint(endpoint_id2, endpoint2.copy())
  303 
  304         # create endpoint project association.
  305         self.put(self.default_request_url)
  306 
  307         # should get back only one endpoint that was just created.
  308         user_id = uuid.uuid4().hex
  309         catalog = PROVIDERS.catalog_api.get_v3_catalog(
  310             user_id,
  311             self.default_domain_project_id)
  312 
  313         # there is only one endpoints associated with the default project.
  314         self.assertEqual(1, len(catalog[0]['endpoints']))
  315         self.assertEqual(self.endpoint_id, catalog[0]['endpoints'][0]['id'])
  316 
  317         # add the second endpoint to default project, bypassing
  318         # catalog_api API manager.
  319         PROVIDERS.catalog_api.driver.add_endpoint_to_project(
  320             endpoint_id2,
  321             self.default_domain_project_id)
  322 
  323         # but, we can just get back one endpoint from the cache, since the
  324         # catalog is pulled out from cache and its haven't been invalidated.
  325         catalog = PROVIDERS.catalog_api.get_v3_catalog(
  326             user_id,
  327             self.default_domain_project_id)
  328 
  329         self.assertEqual(1, len(catalog[0]['endpoints']))
  330 
  331         # remove the endpoint2 from the default project, and add it again via
  332         # catalog_api API manager.
  333         PROVIDERS.catalog_api.driver.remove_endpoint_from_project(
  334             endpoint_id2,
  335             self.default_domain_project_id)
  336 
  337         # add second endpoint to default project, this can be done by calling
  338         # the catalog_api API manager directly but call the REST API
  339         # instead for consistency.
  340         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  341                  '/endpoints/%(endpoint_id)s' % {
  342                      'project_id': self.default_domain_project_id,
  343                      'endpoint_id': endpoint_id2})
  344 
  345         # should get back two endpoints since the cache has been
  346         # invalidated when the second endpoint was added to default project.
  347         catalog = self.catalog_api.get_v3_catalog(
  348             user_id,
  349             self.default_domain_project_id)
  350 
  351         self.assertEqual(2, len(catalog[0]['endpoints']))
  352 
  353         ep_id_list = [catalog[0]['endpoints'][0]['id'],
  354                       catalog[0]['endpoints'][1]['id']]
  355         self.assertItemsEqual([self.endpoint_id, endpoint_id2], ep_id_list)
  356 
  357     @unit.skip_if_cache_disabled('catalog')
  358     def test_remove_endpoint_from_project_invalidates_cache(self):
  359         endpoint_id2 = uuid.uuid4().hex
  360         endpoint2 = unit.new_endpoint_ref(service_id=self.service_id,
  361                                           region_id=self.region_id,
  362                                           interface='public',
  363                                           id=endpoint_id2)
  364         PROVIDERS.catalog_api.create_endpoint(endpoint_id2, endpoint2.copy())
  365         # create endpoint project association.
  366         self.put(self.default_request_url)
  367 
  368         # add second endpoint to default project.
  369         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  370                  '/endpoints/%(endpoint_id)s' % {
  371                      'project_id': self.default_domain_project_id,
  372                      'endpoint_id': endpoint_id2})
  373 
  374         # should get back only one endpoint that was just created.
  375         user_id = uuid.uuid4().hex
  376         catalog = PROVIDERS.catalog_api.get_v3_catalog(
  377             user_id,
  378             self.default_domain_project_id)
  379 
  380         # there are two endpoints associated with the default project.
  381         ep_id_list = [catalog[0]['endpoints'][0]['id'],
  382                       catalog[0]['endpoints'][1]['id']]
  383         self.assertEqual(2, len(catalog[0]['endpoints']))
  384         self.assertItemsEqual([self.endpoint_id, endpoint_id2], ep_id_list)
  385 
  386         # remove the endpoint2 from the default project, bypassing
  387         # catalog_api API manager.
  388         PROVIDERS.catalog_api.driver.remove_endpoint_from_project(
  389             endpoint_id2,
  390             self.default_domain_project_id)
  391 
  392         # but, we can just still get back two endpoints from the cache,
  393         # since the catalog is pulled out from cache and its haven't
  394         # been invalidated.
  395         catalog = PROVIDERS.catalog_api.get_v3_catalog(
  396             user_id,
  397             self.default_domain_project_id)
  398 
  399         self.assertEqual(2, len(catalog[0]['endpoints']))
  400 
  401         # add back the endpoint2 to the default project, and remove it by
  402         # catalog_api API manage.
  403         PROVIDERS.catalog_api.driver.add_endpoint_to_project(
  404             endpoint_id2,
  405             self.default_domain_project_id)
  406 
  407         # remove the endpoint2 from the default project, this can be done
  408         # by calling the catalog_api API manager directly but call
  409         # the REST API instead for consistency.
  410         self.delete('/OS-EP-FILTER/projects/%(project_id)s'
  411                     '/endpoints/%(endpoint_id)s' % {
  412                         'project_id': self.default_domain_project_id,
  413                         'endpoint_id': endpoint_id2})
  414 
  415         # should only get back one endpoint since the cache has been
  416         # invalidated after the endpoint project association was removed.
  417         catalog = PROVIDERS.catalog_api.get_v3_catalog(
  418             user_id,
  419             self.default_domain_project_id)
  420 
  421         self.assertEqual(1, len(catalog[0]['endpoints']))
  422         self.assertEqual(self.endpoint_id, catalog[0]['endpoints'][0]['id'])
  423 
  424 
  425 class EndpointFilterTokenRequestTestCase(EndpointFilterTestCase):
  426 
  427     def test_project_scoped_token_using_endpoint_filter(self):
  428         """Verify endpoints from project scoped token filtered."""
  429         # create a project to work with
  430         ref = unit.new_project_ref(domain_id=self.domain_id)
  431         r = self.post('/projects', body={'project': ref})
  432         project = self.assertValidProjectResponse(r, ref)
  433 
  434         # grant the user a role on the project
  435         self.put(
  436             '/projects/%(project_id)s/users/%(user_id)s/roles/%(role_id)s' % {
  437                 'user_id': self.user['id'],
  438                 'project_id': project['id'],
  439                 'role_id': self.role['id']})
  440 
  441         # set the user's preferred project
  442         body = {'user': {'default_project_id': project['id']}}
  443         r = self.patch('/users/%(user_id)s' % {
  444             'user_id': self.user['id']},
  445             body=body)
  446         self.assertValidUserResponse(r)
  447 
  448         # add one endpoint to the project
  449         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  450                  '/endpoints/%(endpoint_id)s' % {
  451                      'project_id': project['id'],
  452                      'endpoint_id': self.endpoint_id})
  453 
  454         # attempt to authenticate without requesting a project
  455         auth_data = self.build_authentication_request(
  456             user_id=self.user['id'],
  457             password=self.user['password'])
  458         r = self.post('/auth/tokens', body=auth_data)
  459         self.assertValidProjectScopedTokenResponse(
  460             r,
  461             require_catalog=True,
  462             endpoint_filter=True,
  463             ep_filter_assoc=1)
  464         self.assertEqual(project['id'], r.result['token']['project']['id'])
  465 
  466     def test_default_scoped_token_using_endpoint_filter(self):
  467         """Verify endpoints from default scoped token filtered."""
  468         # add one endpoint to default project
  469         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  470                  '/endpoints/%(endpoint_id)s' % {
  471                      'project_id': self.project['id'],
  472                      'endpoint_id': self.endpoint_id})
  473 
  474         auth_data = self.build_authentication_request(
  475             user_id=self.user['id'],
  476             password=self.user['password'],
  477             project_id=self.project['id'])
  478         r = self.post('/auth/tokens', body=auth_data)
  479         self.assertValidProjectScopedTokenResponse(
  480             r,
  481             require_catalog=True,
  482             endpoint_filter=True,
  483             ep_filter_assoc=1)
  484         self.assertEqual(self.project['id'],
  485                          r.result['token']['project']['id'])
  486 
  487         # Ensure name of the service exists
  488         self.assertIn('name', r.result['token']['catalog'][0])
  489 
  490         # region and region_id should be the same in endpoints
  491         endpoint = r.result['token']['catalog'][0]['endpoints'][0]
  492         self.assertIn('region', endpoint)
  493         self.assertIn('region_id', endpoint)
  494         self.assertEqual(endpoint['region'], endpoint['region_id'])
  495 
  496     def test_scoped_token_with_no_catalog_using_endpoint_filter(self):
  497         """Verify endpoint filter does not affect no catalog."""
  498         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  499                  '/endpoints/%(endpoint_id)s' % {
  500                      'project_id': self.project['id'],
  501                      'endpoint_id': self.endpoint_id})
  502 
  503         auth_data = self.build_authentication_request(
  504             user_id=self.user['id'],
  505             password=self.user['password'],
  506             project_id=self.project['id'])
  507         r = self.post('/auth/tokens?nocatalog', body=auth_data)
  508         self.assertValidProjectScopedTokenResponse(
  509             r,
  510             require_catalog=False)
  511         self.assertEqual(self.project['id'],
  512                          r.result['token']['project']['id'])
  513 
  514     def test_invalid_endpoint_project_association(self):
  515         """Verify an invalid endpoint-project association is handled."""
  516         # add first endpoint to default project
  517         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  518                  '/endpoints/%(endpoint_id)s' % {
  519                      'project_id': self.project['id'],
  520                      'endpoint_id': self.endpoint_id})
  521 
  522         # create a second temporary endpoint
  523         endpoint_id2 = uuid.uuid4().hex
  524         endpoint2 = unit.new_endpoint_ref(service_id=self.service_id,
  525                                           region_id=self.region_id,
  526                                           interface='public',
  527                                           id=endpoint_id2)
  528         PROVIDERS.catalog_api.create_endpoint(endpoint_id2, endpoint2.copy())
  529 
  530         # add second endpoint to default project
  531         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  532                  '/endpoints/%(endpoint_id)s' % {
  533                      'project_id': self.project['id'],
  534                      'endpoint_id': endpoint_id2})
  535 
  536         # remove the temporary reference
  537         # this will create inconsistency in the endpoint filter table
  538         # which is fixed during the catalog creation for token request
  539         PROVIDERS.catalog_api.delete_endpoint(endpoint_id2)
  540 
  541         auth_data = self.build_authentication_request(
  542             user_id=self.user['id'],
  543             password=self.user['password'],
  544             project_id=self.project['id'])
  545         r = self.post('/auth/tokens', body=auth_data)
  546         self.assertValidProjectScopedTokenResponse(
  547             r,
  548             require_catalog=True,
  549             endpoint_filter=True,
  550             ep_filter_assoc=1)
  551         self.assertEqual(self.project['id'],
  552                          r.result['token']['project']['id'])
  553 
  554     def test_disabled_endpoint(self):
  555         """Test that a disabled endpoint is handled."""
  556         # Add an enabled endpoint to the default project
  557         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  558                  '/endpoints/%(endpoint_id)s' % {
  559                      'project_id': self.project['id'],
  560                      'endpoint_id': self.endpoint_id})
  561 
  562         # Add a disabled endpoint to the default project.
  563 
  564         # Create a disabled endpoint that's like the enabled one.
  565         disabled_endpoint_ref = copy.copy(self.endpoint)
  566         disabled_endpoint_id = uuid.uuid4().hex
  567         disabled_endpoint_ref.update({
  568             'id': disabled_endpoint_id,
  569             'enabled': False,
  570             'interface': 'internal'
  571         })
  572         PROVIDERS.catalog_api.create_endpoint(
  573             disabled_endpoint_id, disabled_endpoint_ref
  574         )
  575 
  576         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  577                  '/endpoints/%(endpoint_id)s' % {
  578                      'project_id': self.project['id'],
  579                      'endpoint_id': disabled_endpoint_id})
  580 
  581         # Authenticate to get token with catalog
  582         auth_data = self.build_authentication_request(
  583             user_id=self.user['id'],
  584             password=self.user['password'],
  585             project_id=self.project['id'])
  586         r = self.post('/auth/tokens', body=auth_data)
  587 
  588         endpoints = r.result['token']['catalog'][0]['endpoints']
  589         endpoint_ids = [ep['id'] for ep in endpoints]
  590         self.assertEqual([self.endpoint_id], endpoint_ids)
  591 
  592     def test_multiple_endpoint_project_associations(self):
  593 
  594         def _create_an_endpoint():
  595             endpoint_ref = unit.new_endpoint_ref(service_id=self.service_id,
  596                                                  interface='public',
  597                                                  region_id=self.region_id)
  598             r = self.post('/endpoints', body={'endpoint': endpoint_ref})
  599             return r.result['endpoint']['id']
  600 
  601         # create three endpoints
  602         endpoint_id1 = _create_an_endpoint()
  603         endpoint_id2 = _create_an_endpoint()
  604         _create_an_endpoint()
  605 
  606         # only associate two endpoints with project
  607         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  608                  '/endpoints/%(endpoint_id)s' % {
  609                      'project_id': self.project['id'],
  610                      'endpoint_id': endpoint_id1})
  611         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  612                  '/endpoints/%(endpoint_id)s' % {
  613                      'project_id': self.project['id'],
  614                      'endpoint_id': endpoint_id2})
  615 
  616         # there should be only two endpoints in token catalog
  617         auth_data = self.build_authentication_request(
  618             user_id=self.user['id'],
  619             password=self.user['password'],
  620             project_id=self.project['id'])
  621         r = self.post('/auth/tokens', body=auth_data)
  622         self.assertValidProjectScopedTokenResponse(
  623             r,
  624             require_catalog=True,
  625             endpoint_filter=True,
  626             ep_filter_assoc=2)
  627 
  628     def test_get_auth_catalog_using_endpoint_filter(self):
  629         # add one endpoint to default project
  630         self.put('/OS-EP-FILTER/projects/%(project_id)s'
  631                  '/endpoints/%(endpoint_id)s' % {
  632                      'project_id': self.project['id'],
  633                      'endpoint_id': self.endpoint_id})
  634 
  635         auth_data = self.build_authentication_request(
  636             user_id=self.user['id'],
  637             password=self.user['password'],
  638             project_id=self.project['id'])
  639         token_data = self.post('/auth/tokens', body=auth_data)
  640         self.assertValidProjectScopedTokenResponse(
  641             token_data,
  642             require_catalog=True,
  643             endpoint_filter=True,
  644             ep_filter_assoc=1)
  645 
  646         auth_catalog = self.get('/auth/catalog',
  647                                 token=token_data.headers['X-Subject-Token'])
  648         self.assertEqual(token_data.result['token']['catalog'],
  649                          auth_catalog.result['catalog'])
  650 
  651 
  652 class JsonHomeTests(EndpointFilterTestCase, test_v3.JsonHomeTestMixin):
  653     JSON_HOME_DATA = {
  654         'https://docs.openstack.org/api/openstack-identity/3/ext/OS-EP-FILTER/'
  655         '1.0/rel/endpoint_projects': {
  656             'href-template': '/OS-EP-FILTER/endpoints/{endpoint_id}/projects',
  657             'href-vars': {
  658                 'endpoint_id':
  659                 'https://docs.openstack.org/api/openstack-identity/3/param/'
  660                 'endpoint_id',
  661             },
  662         },
  663         'https://docs.openstack.org/api/openstack-identity/3/ext/OS-EP-FILTER/'
  664         '1.0/rel/endpoint_groups': {
  665             'href': '/OS-EP-FILTER/endpoint_groups',
  666         },
  667         'https://docs.openstack.org/api/openstack-identity/3/ext/OS-EP-FILTER/'
  668         '1.0/rel/endpoint_group': {
  669             'href-template': '/OS-EP-FILTER/endpoint_groups/'
  670             '{endpoint_group_id}',
  671             'href-vars': {
  672                 'endpoint_group_id':
  673                 'https://docs.openstack.org/api/openstack-identity/3/'
  674                 'ext/OS-EP-FILTER/1.0/param/endpoint_group_id',
  675             },
  676         },
  677         'https://docs.openstack.org/api/openstack-identity/3/ext/OS-EP-FILTER/'
  678         '1.0/rel/endpoint_group_to_project_association': {
  679             'href-template': '/OS-EP-FILTER/endpoint_groups/'
  680             '{endpoint_group_id}/projects/{project_id}',
  681             'href-vars': {
  682                 'project_id':
  683                 'https://docs.openstack.org/api/openstack-identity/3/param/'
  684                 'project_id',
  685                 'endpoint_group_id':
  686                 'https://docs.openstack.org/api/openstack-identity/3/'
  687                 'ext/OS-EP-FILTER/1.0/param/endpoint_group_id',
  688             },
  689         },
  690         'https://docs.openstack.org/api/openstack-identity/3/ext/OS-EP-FILTER/'
  691         '1.0/rel/projects_associated_with_endpoint_group': {
  692             'href-template': '/OS-EP-FILTER/endpoint_groups/'
  693             '{endpoint_group_id}/projects',
  694             'href-vars': {
  695                 'endpoint_group_id':
  696                 'https://docs.openstack.org/api/openstack-identity/3/'
  697                 'ext/OS-EP-FILTER/1.0/param/endpoint_group_id',
  698             },
  699         },
  700         'https://docs.openstack.org/api/openstack-identity/3/ext/OS-EP-FILTER/'
  701         '1.0/rel/endpoints_in_endpoint_group': {
  702             'href-template': '/OS-EP-FILTER/endpoint_groups/'
  703             '{endpoint_group_id}/endpoints',
  704             'href-vars': {
  705                 'endpoint_group_id':
  706                 'https://docs.openstack.org/api/openstack-identity/3/'
  707                 'ext/OS-EP-FILTER/1.0/param/endpoint_group_id',
  708             },
  709         },
  710         'https://docs.openstack.org/api/openstack-identity/3/ext/OS-EP-FILTER/'
  711         '1.0/rel/project_endpoint_groups': {
  712             'href-template': '/OS-EP-FILTER/projects/{project_id}/'
  713             'endpoint_groups',
  714             'href-vars': {
  715                 'project_id':
  716                 'https://docs.openstack.org/api/openstack-identity/3/param/'
  717                 'project_id',
  718             },
  719         },
  720     }
  721 
  722 
  723 class EndpointGroupCRUDTestCase(EndpointFilterTestCase):
  724 
  725     DEFAULT_ENDPOINT_GROUP_BODY = {
  726         'endpoint_group': {
  727             'description': 'endpoint group description',
  728             'filters': {
  729                 'interface': 'admin'
  730             },
  731             'name': 'endpoint_group_name'
  732         }
  733     }
  734 
  735     DEFAULT_ENDPOINT_GROUP_URL = '/OS-EP-FILTER/endpoint_groups'
  736 
  737     def test_create_endpoint_group(self):
  738         """POST /OS-EP-FILTER/endpoint_groups.
  739 
  740         Valid endpoint group test case.
  741 
  742         """
  743         r = self.post(self.DEFAULT_ENDPOINT_GROUP_URL,
  744                       body=self.DEFAULT_ENDPOINT_GROUP_BODY)
  745         expected_filters = (self.DEFAULT_ENDPOINT_GROUP_BODY
  746                             ['endpoint_group']['filters'])
  747         expected_name = (self.DEFAULT_ENDPOINT_GROUP_BODY
  748                          ['endpoint_group']['name'])
  749         self.assertEqual(expected_filters,
  750                          r.result['endpoint_group']['filters'])
  751         self.assertEqual(expected_name, r.result['endpoint_group']['name'])
  752         self.assertThat(
  753             r.result['endpoint_group']['links']['self'],
  754             matchers.EndsWith(
  755                 '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
  756                     'endpoint_group_id': r.result['endpoint_group']['id']}))
  757 
  758     def test_create_invalid_endpoint_group(self):
  759         """POST /OS-EP-FILTER/endpoint_groups.
  760 
  761         Invalid endpoint group creation test case.
  762 
  763         """
  764         invalid_body = copy.deepcopy(self.DEFAULT_ENDPOINT_GROUP_BODY)
  765         invalid_body['endpoint_group']['filters'] = {'foobar': 'admin'}
  766         self.post(self.DEFAULT_ENDPOINT_GROUP_URL,
  767                   body=invalid_body,
  768                   expected_status=http.client.BAD_REQUEST)
  769 
  770     def test_get_endpoint_group(self):
  771         """GET /OS-EP-FILTER/endpoint_groups/{endpoint_group}.
  772 
  773         Valid endpoint group test case.
  774 
  775         """
  776         # create an endpoint group to work with
  777         response = self.post(self.DEFAULT_ENDPOINT_GROUP_URL,
  778                              body=self.DEFAULT_ENDPOINT_GROUP_BODY)
  779         endpoint_group_id = response.result['endpoint_group']['id']
  780         endpoint_group_filters = response.result['endpoint_group']['filters']
  781         endpoint_group_name = response.result['endpoint_group']['name']
  782         url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
  783             'endpoint_group_id': endpoint_group_id}
  784         self.get(url)
  785         self.assertEqual(endpoint_group_id,
  786                          response.result['endpoint_group']['id'])
  787         self.assertEqual(endpoint_group_filters,
  788                          response.result['endpoint_group']['filters'])
  789         self.assertEqual(endpoint_group_name,
  790                          response.result['endpoint_group']['name'])
  791         self.assertThat(response.result['endpoint_group']['links']['self'],
  792                         matchers.EndsWith(url))
  793 
  794     def test_get_invalid_endpoint_group(self):
  795         """GET /OS-EP-FILTER/endpoint_groups/{endpoint_group}.
  796 
  797         Invalid endpoint group test case.
  798 
  799         """
  800         endpoint_group_id = 'foobar'
  801         url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
  802             'endpoint_group_id': endpoint_group_id}
  803         self.get(url, expected_status=http.client.NOT_FOUND)
  804 
  805     def test_check_endpoint_group(self):
  806         """HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}.
  807 
  808         Valid endpoint_group_id test case.
  809 
  810         """
  811         # create an endpoint group to work with
  812         endpoint_group_id = self._create_valid_endpoint_group(
  813             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
  814         url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
  815             'endpoint_group_id': endpoint_group_id}
  816         self.head(url, expected_status=http.client.OK)
  817 
  818     def test_check_invalid_endpoint_group(self):
  819         """HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}.
  820 
  821         Invalid endpoint_group_id test case.
  822 
  823         """
  824         endpoint_group_id = 'foobar'
  825         url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
  826             'endpoint_group_id': endpoint_group_id}
  827         self.head(url, expected_status=http.client.NOT_FOUND)
  828 
  829     def test_patch_endpoint_group(self):
  830         """PATCH /OS-EP-FILTER/endpoint_groups/{endpoint_group}.
  831 
  832         Valid endpoint group patch test case.
  833 
  834         """
  835         body = copy.deepcopy(self.DEFAULT_ENDPOINT_GROUP_BODY)
  836         body['endpoint_group']['filters'] = {'region_id': 'UK'}
  837         body['endpoint_group']['name'] = 'patch_test'
  838         # create an endpoint group to work with
  839         endpoint_group_id = self._create_valid_endpoint_group(
  840             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
  841         url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
  842             'endpoint_group_id': endpoint_group_id}
  843         r = self.patch(url, body=body)
  844         self.assertEqual(endpoint_group_id,
  845                          r.result['endpoint_group']['id'])
  846         self.assertEqual(body['endpoint_group']['filters'],
  847                          r.result['endpoint_group']['filters'])
  848         self.assertThat(r.result['endpoint_group']['links']['self'],
  849                         matchers.EndsWith(url))
  850 
  851     def test_patch_nonexistent_endpoint_group(self):
  852         """PATCH /OS-EP-FILTER/endpoint_groups/{endpoint_group}.
  853 
  854         Invalid endpoint group patch test case.
  855 
  856         """
  857         body = {
  858             'endpoint_group': {
  859                 'name': 'patch_test'
  860             }
  861         }
  862         url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
  863             'endpoint_group_id': 'ABC'}
  864         self.patch(url, body=body, expected_status=http.client.NOT_FOUND)
  865 
  866     def test_patch_invalid_endpoint_group(self):
  867         """PATCH /OS-EP-FILTER/endpoint_groups/{endpoint_group}.
  868 
  869         Valid endpoint group patch test case.
  870 
  871         """
  872         body = {
  873             'endpoint_group': {
  874                 'description': 'endpoint group description',
  875                 'filters': {
  876                     'region': 'UK'
  877                 },
  878                 'name': 'patch_test'
  879             }
  880         }
  881         # create an endpoint group to work with
  882         endpoint_group_id = self._create_valid_endpoint_group(
  883             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
  884         url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
  885             'endpoint_group_id': endpoint_group_id}
  886         self.patch(url, body=body, expected_status=http.client.BAD_REQUEST)
  887 
  888         # Perform a GET call to ensure that the content remains
  889         # the same (as DEFAULT_ENDPOINT_GROUP_BODY) after attempting to update
  890         # with an invalid filter
  891         url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
  892             'endpoint_group_id': endpoint_group_id}
  893         r = self.get(url)
  894         del r.result['endpoint_group']['id']
  895         del r.result['endpoint_group']['links']
  896         self.assertDictEqual(self.DEFAULT_ENDPOINT_GROUP_BODY, r.result)
  897 
  898     def test_delete_endpoint_group(self):
  899         """GET /OS-EP-FILTER/endpoint_groups/{endpoint_group}.
  900 
  901         Valid endpoint group test case.
  902 
  903         """
  904         # create an endpoint group to work with
  905         endpoint_group_id = self._create_valid_endpoint_group(
  906             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
  907         url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
  908             'endpoint_group_id': endpoint_group_id}
  909         self.delete(url)
  910         self.get(url, expected_status=http.client.NOT_FOUND)
  911 
  912     def test_delete_invalid_endpoint_group(self):
  913         """GET /OS-EP-FILTER/endpoint_groups/{endpoint_group}.
  914 
  915         Invalid endpoint group test case.
  916 
  917         """
  918         endpoint_group_id = 'foobar'
  919         url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
  920             'endpoint_group_id': endpoint_group_id}
  921         self.delete(url, expected_status=http.client.NOT_FOUND)
  922 
  923     def test_add_endpoint_group_to_project(self):
  924         """Create a valid endpoint group and project association."""
  925         endpoint_group_id = self._create_valid_endpoint_group(
  926             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
  927         self._create_endpoint_group_project_association(endpoint_group_id,
  928                                                         self.project_id)
  929 
  930     def test_add_endpoint_group_to_project_with_invalid_project_id(self):
  931         """Create an invalid endpoint group and project association."""
  932         # create an endpoint group to work with
  933         endpoint_group_id = self._create_valid_endpoint_group(
  934             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
  935 
  936         # associate endpoint group with project
  937         project_id = uuid.uuid4().hex
  938         url = self._get_project_endpoint_group_url(
  939             endpoint_group_id, project_id)
  940         self.put(url, expected_status=http.client.NOT_FOUND)
  941 
  942     def test_get_endpoint_group_in_project(self):
  943         """Test retrieving project endpoint group association."""
  944         # create an endpoint group to work with
  945         endpoint_group_id = self._create_valid_endpoint_group(
  946             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
  947 
  948         # associate endpoint group with project
  949         url = self._get_project_endpoint_group_url(
  950             endpoint_group_id, self.project_id)
  951         self.put(url)
  952         response = self.get(url)
  953         self.assertEqual(
  954             endpoint_group_id,
  955             response.result['project_endpoint_group']['endpoint_group_id'])
  956         self.assertEqual(
  957             self.project_id,
  958             response.result['project_endpoint_group']['project_id'])
  959 
  960     def test_get_invalid_endpoint_group_in_project(self):
  961         """Test retrieving project endpoint group association."""
  962         endpoint_group_id = uuid.uuid4().hex
  963         project_id = uuid.uuid4().hex
  964         url = self._get_project_endpoint_group_url(
  965             endpoint_group_id, project_id)
  966         self.get(url, expected_status=http.client.NOT_FOUND)
  967 
  968     def test_list_endpoint_groups_in_project(self):
  969         """GET & HEAD /OS-EP-FILTER/projects/{project_id}/endpoint_groups."""
  970         # create an endpoint group to work with
  971         endpoint_group_id = self._create_valid_endpoint_group(
  972             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
  973 
  974         # associate endpoint group with project
  975         url = self._get_project_endpoint_group_url(
  976             endpoint_group_id, self.project_id)
  977         self.put(url)
  978 
  979         url = ('/OS-EP-FILTER/projects/%(project_id)s/endpoint_groups' %
  980                {'project_id': self.project_id})
  981         response = self.get(url, expected_status=http.client.OK)
  982 
  983         self.assertEqual(
  984             endpoint_group_id,
  985             response.result['endpoint_groups'][0]['id'])
  986 
  987         self.head(url, expected_status=http.client.OK)
  988 
  989     def test_list_endpoint_groups_in_invalid_project(self):
  990         """Test retrieving from invalid project."""
  991         project_id = uuid.uuid4().hex
  992         url = ('/OS-EP-FILTER/projects/%(project_id)s/endpoint_groups' %
  993                {'project_id': project_id})
  994         self.get(url, expected_status=http.client.NOT_FOUND)
  995         self.head(url, expected_status=http.client.NOT_FOUND)
  996 
  997     def test_empty_endpoint_groups_in_project(self):
  998         """Test when no endpoint groups associated with the project."""
  999         url = ('/OS-EP-FILTER/projects/%(project_id)s/endpoint_groups' %
 1000                {'project_id': self.project_id})
 1001         response = self.get(url, expected_status=http.client.OK)
 1002 
 1003         self.assertEqual(0, len(response.result['endpoint_groups']))
 1004 
 1005         self.head(url, expected_status=http.client.OK)
 1006 
 1007     def test_check_endpoint_group_to_project(self):
 1008         """Test HEAD with a valid endpoint group and project association."""
 1009         endpoint_group_id = self._create_valid_endpoint_group(
 1010             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
 1011         self._create_endpoint_group_project_association(endpoint_group_id,
 1012                                                         self.project_id)
 1013         url = self._get_project_endpoint_group_url(
 1014             endpoint_group_id, self.project_id)
 1015         self.head(url, expected_status=http.client.OK)
 1016 
 1017     def test_check_endpoint_group_to_project_with_invalid_project_id(self):
 1018         """Test HEAD with an invalid endpoint group and project association."""
 1019         # create an endpoint group to work with
 1020         endpoint_group_id = self._create_valid_endpoint_group(
 1021             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
 1022 
 1023         # create an endpoint group to project association
 1024         url = self._get_project_endpoint_group_url(
 1025             endpoint_group_id, self.project_id)
 1026         self.put(url)
 1027 
 1028         # send a head request with an invalid project id
 1029         project_id = uuid.uuid4().hex
 1030         url = self._get_project_endpoint_group_url(
 1031             endpoint_group_id, project_id)
 1032         self.head(url, expected_status=http.client.NOT_FOUND)
 1033 
 1034     def test_list_endpoint_groups(self):
 1035         """GET & HEAD /OS-EP-FILTER/endpoint_groups."""
 1036         # create an endpoint group to work with
 1037         endpoint_group_id = self._create_valid_endpoint_group(
 1038             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
 1039 
 1040         # recover all endpoint groups
 1041         url = '/OS-EP-FILTER/endpoint_groups'
 1042         r = self.get(url, expected_status=http.client.OK)
 1043         self.assertNotEmpty(r.result['endpoint_groups'])
 1044         self.assertEqual(endpoint_group_id,
 1045                          r.result['endpoint_groups'][0].get('id'))
 1046 
 1047         self.head(url, expected_status=http.client.OK)
 1048 
 1049     def test_list_endpoint_groups_by_name(self):
 1050         """GET & HEAD /OS-EP-FILTER/endpoint_groups."""
 1051         # create an endpoint group to work with
 1052         endpoint_group_id = self._create_valid_endpoint_group(
 1053             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
 1054 
 1055         # retrieve the single endpointgroup by name
 1056         url = ('/OS-EP-FILTER/endpoint_groups?name=%(name)s' %
 1057                {'name': 'endpoint_group_name'})
 1058         r = self.get(url, expected_status=http.client.OK)
 1059         self.assertNotEmpty(r.result['endpoint_groups'])
 1060         self.assertEqual(1, len(r.result['endpoint_groups']))
 1061         self.assertEqual(endpoint_group_id,
 1062                          r.result['endpoint_groups'][0].get('id'))
 1063 
 1064         self.head(url, expected_status=http.client.OK)
 1065 
 1066         # try to retrieve a non existant one
 1067         url = ('/OS-EP-FILTER/endpoint_groups?name=%(name)s' %
 1068                {'name': 'fake'})
 1069         r = self.get(url, expected_status=http.client.OK)
 1070         self.assertEqual(0, len(r.result['endpoint_groups']))
 1071 
 1072     def test_list_projects_associated_with_endpoint_group(self):
 1073         """GET & HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects.
 1074 
 1075         Valid endpoint group test case.
 1076 
 1077         """
 1078         # create an endpoint group to work with
 1079         endpoint_group_id = self._create_valid_endpoint_group(
 1080             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
 1081 
 1082         # associate endpoint group with project
 1083         self._create_endpoint_group_project_association(endpoint_group_id,
 1084                                                         self.project_id)
 1085 
 1086         # recover list of projects associated with endpoint group
 1087         url = ('/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s'
 1088                '/projects' %
 1089                {'endpoint_group_id': endpoint_group_id})
 1090         self.get(url, expected_status=http.client.OK)
 1091         self.head(url, expected_status=http.client.OK)
 1092 
 1093     def test_list_endpoints_associated_with_endpoint_group(self):
 1094         """GET & HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group}/endpoints.
 1095 
 1096         Valid endpoint group test case.
 1097 
 1098         """
 1099         # create a service
 1100         service_ref = unit.new_service_ref()
 1101         response = self.post(
 1102             '/services',
 1103             body={'service': service_ref})
 1104 
 1105         service_id = response.result['service']['id']
 1106 
 1107         # create an endpoint
 1108         endpoint_ref = unit.new_endpoint_ref(service_id=service_id,
 1109                                              interface='public',
 1110                                              region_id=self.region_id)
 1111         response = self.post('/endpoints', body={'endpoint': endpoint_ref})
 1112         endpoint_id = response.result['endpoint']['id']
 1113 
 1114         # create an endpoint group
 1115         body = copy.deepcopy(self.DEFAULT_ENDPOINT_GROUP_BODY)
 1116         body['endpoint_group']['filters'] = {'service_id': service_id}
 1117         endpoint_group_id = self._create_valid_endpoint_group(
 1118             self.DEFAULT_ENDPOINT_GROUP_URL, body)
 1119 
 1120         # create association
 1121         self._create_endpoint_group_project_association(endpoint_group_id,
 1122                                                         self.project_id)
 1123 
 1124         # recover list of endpoints associated with endpoint group
 1125         url = ('/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s'
 1126                '/endpoints' % {'endpoint_group_id': endpoint_group_id})
 1127         r = self.get(url, expected_status=http.client.OK)
 1128         self.assertNotEmpty(r.result['endpoints'])
 1129         self.assertEqual(endpoint_id, r.result['endpoints'][0].get('id'))
 1130         self.head(url, expected_status=http.client.OK)
 1131 
 1132     def test_list_endpoints_associated_with_project_endpoint_group(self):
 1133         """GET & HEAD /OS-EP-FILTER/projects/{project_id}/endpoints.
 1134 
 1135         Valid project, endpoint id, and endpoint group test case.
 1136 
 1137         """
 1138         # create a temporary service
 1139         service_ref = unit.new_service_ref()
 1140         response = self.post('/services', body={'service': service_ref})
 1141         service_id2 = response.result['service']['id']
 1142 
 1143         # create additional endpoints
 1144         self._create_endpoint_and_associations(
 1145             self.default_domain_project_id, service_id2)
 1146         self._create_endpoint_and_associations(
 1147             self.default_domain_project_id)
 1148 
 1149         # create project and endpoint association with default endpoint:
 1150         self.put(self.default_request_url)
 1151 
 1152         # create an endpoint group that contains a different endpoint
 1153         body = copy.deepcopy(self.DEFAULT_ENDPOINT_GROUP_BODY)
 1154         body['endpoint_group']['filters'] = {'service_id': service_id2}
 1155         endpoint_group_id = self._create_valid_endpoint_group(
 1156             self.DEFAULT_ENDPOINT_GROUP_URL, body)
 1157 
 1158         # associate endpoint group with project
 1159         self._create_endpoint_group_project_association(
 1160             endpoint_group_id, self.default_domain_project_id)
 1161 
 1162         # Now get a list of the filtered endpoints
 1163         endpoints_url = '/OS-EP-FILTER/projects/%(project_id)s/endpoints' % {
 1164             'project_id': self.default_domain_project_id}
 1165         r = self.get(endpoints_url, expected_status=http.client.OK)
 1166         endpoints = self.assertValidEndpointListResponse(r)
 1167         self.assertEqual(2, len(endpoints))
 1168         self.head(endpoints_url, expected_status=http.client.OK)
 1169 
 1170         # Ensure catalog includes the endpoints from endpoint_group project
 1171         # association, this is needed when a project scoped token is issued
 1172         # and "endpoint_filter.sql" backend driver is in place.
 1173         user_id = uuid.uuid4().hex
 1174         catalog_list = PROVIDERS.catalog_api.get_v3_catalog(
 1175             user_id,
 1176             self.default_domain_project_id)
 1177         self.assertEqual(2, len(catalog_list))
 1178 
 1179         # Now remove project endpoint group association
 1180         url = self._get_project_endpoint_group_url(
 1181             endpoint_group_id, self.default_domain_project_id)
 1182         self.delete(url)
 1183 
 1184         # Now remove endpoint group
 1185         url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % {
 1186             'endpoint_group_id': endpoint_group_id}
 1187         self.delete(url)
 1188 
 1189         r = self.get(endpoints_url)
 1190         endpoints = self.assertValidEndpointListResponse(r)
 1191         self.assertEqual(1, len(endpoints))
 1192 
 1193         catalog_list = PROVIDERS.catalog_api.get_v3_catalog(
 1194             user_id,
 1195             self.default_domain_project_id)
 1196         self.assertEqual(1, len(catalog_list))
 1197 
 1198     def test_endpoint_group_project_cleanup_with_project(self):
 1199         # create endpoint group
 1200         endpoint_group_id = self._create_valid_endpoint_group(
 1201             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
 1202 
 1203         # create new project and associate with endpoint_group
 1204         project_ref = unit.new_project_ref(domain_id=self.domain_id)
 1205         r = self.post('/projects', body={'project': project_ref})
 1206         project = self.assertValidProjectResponse(r, project_ref)
 1207         url = self._get_project_endpoint_group_url(endpoint_group_id,
 1208                                                    project['id'])
 1209         self.put(url)
 1210 
 1211         # check that we can recover the project endpoint group association
 1212         self.get(url, expected_status=http.client.OK)
 1213         self.get(url, expected_status=http.client.OK)
 1214 
 1215         # Now delete the project and then try and retrieve the project
 1216         # endpoint group association again
 1217         self.delete('/projects/%(project_id)s' % {
 1218             'project_id': project['id']})
 1219         self.get(url, expected_status=http.client.NOT_FOUND)
 1220         self.head(url, expected_status=http.client.NOT_FOUND)
 1221 
 1222     def test_endpoint_group_project_cleanup_with_endpoint_group(self):
 1223         # create endpoint group
 1224         endpoint_group_id = self._create_valid_endpoint_group(
 1225             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
 1226 
 1227         # create new project and associate with endpoint_group
 1228         project_ref = unit.new_project_ref(domain_id=self.domain_id)
 1229         r = self.post('/projects', body={'project': project_ref})
 1230         project = self.assertValidProjectResponse(r, project_ref)
 1231         url = self._get_project_endpoint_group_url(endpoint_group_id,
 1232                                                    project['id'])
 1233         self.put(url)
 1234 
 1235         # check that we can recover the project endpoint group association
 1236         self.get(url)
 1237 
 1238         # now remove the project endpoint group association
 1239         self.delete(url)
 1240         self.get(url, expected_status=http.client.NOT_FOUND)
 1241 
 1242     def test_removing_an_endpoint_group_project(self):
 1243         # create an endpoint group
 1244         endpoint_group_id = self._create_valid_endpoint_group(
 1245             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
 1246 
 1247         # create an endpoint_group project
 1248         url = self._get_project_endpoint_group_url(
 1249             endpoint_group_id, self.default_domain_project_id)
 1250         self.put(url)
 1251 
 1252         # remove the endpoint group project
 1253         self.delete(url)
 1254         self.get(url, expected_status=http.client.NOT_FOUND)
 1255 
 1256     def test_remove_endpoint_group_with_project_association(self):
 1257         # create an endpoint group
 1258         endpoint_group_id = self._create_valid_endpoint_group(
 1259             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
 1260 
 1261         # create an endpoint_group project
 1262         project_endpoint_group_url = self._get_project_endpoint_group_url(
 1263             endpoint_group_id, self.default_domain_project_id)
 1264         self.put(project_endpoint_group_url)
 1265 
 1266         # remove endpoint group, the associated endpoint_group project will
 1267         # be removed as well.
 1268         endpoint_group_url = ('/OS-EP-FILTER/endpoint_groups/'
 1269                               '%(endpoint_group_id)s'
 1270                               % {'endpoint_group_id': endpoint_group_id})
 1271         self.delete(endpoint_group_url)
 1272         self.get(endpoint_group_url, expected_status=http.client.NOT_FOUND)
 1273         self.get(project_endpoint_group_url,
 1274                  expected_status=http.client.NOT_FOUND)
 1275 
 1276     @unit.skip_if_cache_disabled('catalog')
 1277     def test_add_endpoint_group_to_project_invalidates_catalog_cache(self):
 1278         # create another endpoint with 'admin' interface which matches
 1279         # 'filters' definition in endpoint group, then there should be two
 1280         # endpoints returned when retrieving v3 catalog if cache works as
 1281         # expected.
 1282         # this should be done at first since `create_endpoint` will also
 1283         # invalidate cache.
 1284         endpoint_id2 = uuid.uuid4().hex
 1285         endpoint2 = unit.new_endpoint_ref(service_id=self.service_id,
 1286                                           region_id=self.region_id,
 1287                                           interface='admin',
 1288                                           id=endpoint_id2)
 1289         PROVIDERS.catalog_api.create_endpoint(endpoint_id2, endpoint2)
 1290 
 1291         # create a project and endpoint association.
 1292         self.put(self.default_request_url)
 1293 
 1294         # there is only one endpoint associated with the default project.
 1295         user_id = uuid.uuid4().hex
 1296         catalog = PROVIDERS.catalog_api.get_v3_catalog(
 1297             user_id,
 1298             self.default_domain_project_id)
 1299 
 1300         self.assertThat(catalog[0]['endpoints'], matchers.HasLength(1))
 1301 
 1302         # create an endpoint group.
 1303         endpoint_group_id = self._create_valid_endpoint_group(
 1304             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
 1305 
 1306         # add the endpoint group to default project, bypassing
 1307         # catalog_api API manager.
 1308         PROVIDERS.catalog_api.driver.add_endpoint_group_to_project(
 1309             endpoint_group_id,
 1310             self.default_domain_project_id)
 1311 
 1312         # can get back only one endpoint from the cache, since the catalog
 1313         # is pulled out from cache.
 1314         invalid_catalog = PROVIDERS.catalog_api.get_v3_catalog(
 1315             user_id,
 1316             self.default_domain_project_id)
 1317 
 1318         self.assertThat(invalid_catalog[0]['endpoints'],
 1319                         matchers.HasLength(1))
 1320         self.assertEqual(catalog, invalid_catalog)
 1321 
 1322         # remove the endpoint group from default project, and add it again via
 1323         # catalog_api API manager.
 1324         PROVIDERS.catalog_api.driver.remove_endpoint_group_from_project(
 1325             endpoint_group_id,
 1326             self.default_domain_project_id)
 1327 
 1328         # add the endpoint group to default project.
 1329         PROVIDERS.catalog_api.add_endpoint_group_to_project(
 1330             endpoint_group_id,
 1331             self.default_domain_project_id)
 1332 
 1333         catalog = PROVIDERS.catalog_api.get_v3_catalog(
 1334             user_id,
 1335             self.default_domain_project_id)
 1336 
 1337         # now, it will return 2 endpoints since the cache has been
 1338         # invalidated.
 1339         self.assertThat(catalog[0]['endpoints'], matchers.HasLength(2))
 1340 
 1341         ep_id_list = [catalog[0]['endpoints'][0]['id'],
 1342                       catalog[0]['endpoints'][1]['id']]
 1343         self.assertItemsEqual([self.endpoint_id, endpoint_id2], ep_id_list)
 1344 
 1345     @unit.skip_if_cache_disabled('catalog')
 1346     def test_remove_endpoint_group_from_project_invalidates_cache(self):
 1347         # create another endpoint with 'admin' interface which matches
 1348         # 'filters' definition in endpoint group, then there should be two
 1349         # endpoints returned when retrieving v3 catalog. But only one
 1350         # endpoint will return after the endpoint group's deletion if cache
 1351         # works as expected.
 1352         # this should be done at first since `create_endpoint` will also
 1353         # invalidate cache.
 1354         endpoint_id2 = uuid.uuid4().hex
 1355         endpoint2 = unit.new_endpoint_ref(service_id=self.service_id,
 1356                                           region_id=self.region_id,
 1357                                           interface='admin',
 1358                                           id=endpoint_id2)
 1359         PROVIDERS.catalog_api.create_endpoint(endpoint_id2, endpoint2)
 1360 
 1361         # create project and endpoint association.
 1362         self.put(self.default_request_url)
 1363 
 1364         # create an endpoint group.
 1365         endpoint_group_id = self._create_valid_endpoint_group(
 1366             self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY)
 1367 
 1368         # add the endpoint group to default project.
 1369         PROVIDERS.catalog_api.add_endpoint_group_to_project(
 1370             endpoint_group_id,
 1371             self.default_domain_project_id)
 1372 
 1373         # should get back two endpoints, one from endpoint project
 1374         # association, the other one is from endpoint_group project
 1375         # association.
 1376         user_id = uuid.uuid4().hex
 1377         catalog = PROVIDERS.catalog_api.get_v3_catalog(
 1378             user_id,
 1379             self.default_domain_project_id)
 1380 
 1381         self.assertThat(catalog[0]['endpoints'], matchers.HasLength(2))
 1382 
 1383         ep_id_list = [catalog[0]['endpoints'][0]['id'],
 1384                       catalog[0]['endpoints'][1]['id']]
 1385         self.assertItemsEqual([self.endpoint_id, endpoint_id2], ep_id_list)
 1386 
 1387         # remove endpoint_group project association, bypassing
 1388         # catalog_api API manager.
 1389         PROVIDERS.catalog_api.driver.remove_endpoint_group_from_project(
 1390             endpoint_group_id,
 1391             self.default_domain_project_id)
 1392 
 1393         # still get back two endpoints, since the catalog is pulled out
 1394         # from cache and the cache haven't been invalidated.
 1395         invalid_catalog = PROVIDERS.catalog_api.get_v3_catalog(
 1396             user_id,
 1397             self.default_domain_project_id)
 1398 
 1399         self.assertThat(invalid_catalog[0]['endpoints'],
 1400                         matchers.HasLength(2))
 1401         self.assertEqual(catalog, invalid_catalog)
 1402 
 1403         # add back the endpoint_group project association and remove it from
 1404         # manager.
 1405         PROVIDERS.catalog_api.driver.add_endpoint_group_to_project(
 1406             endpoint_group_id,
 1407             self.default_domain_project_id)
 1408 
 1409         PROVIDERS.catalog_api.remove_endpoint_group_from_project(
 1410             endpoint_group_id,
 1411             self.default_domain_project_id)
 1412 
 1413         # should only get back one endpoint since the cache has been
 1414         # invalidated after the endpoint_group project association was
 1415         # removed.
 1416         catalog = PROVIDERS.catalog_api.get_v3_catalog(
 1417             user_id,
 1418             self.default_domain_project_id)
 1419 
 1420         self.assertThat(catalog[0]['endpoints'], matchers.HasLength(1))
 1421         self.assertEqual(self.endpoint_id, catalog[0]['endpoints'][0]['id'])
 1422 
 1423     def _create_valid_endpoint_group(self, url, body):
 1424         r = self.post(url, body=body)
 1425         return r.result['endpoint_group']['id']
 1426 
 1427     def _create_endpoint_group_project_association(self,
 1428                                                    endpoint_group_id,
 1429                                                    project_id):
 1430         url = self._get_project_endpoint_group_url(endpoint_group_id,
 1431                                                    project_id)
 1432         self.put(url)
 1433 
 1434     def _get_project_endpoint_group_url(self,
 1435                                         endpoint_group_id,
 1436                                         project_id):
 1437         return ('/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s'
 1438                 '/projects/%(project_id)s' %
 1439                 {'endpoint_group_id': endpoint_group_id,
 1440                  'project_id': project_id})
 1441 
 1442     def _create_endpoint_and_associations(self, project_id, service_id=None):
 1443         """Create an endpoint associated with service and project."""
 1444         if not service_id:
 1445             # create a new service
 1446             service_ref = unit.new_service_ref()
 1447             response = self.post(
 1448                 '/services', body={'service': service_ref})
 1449             service_id = response.result['service']['id']
 1450 
 1451         # create endpoint
 1452         endpoint_ref = unit.new_endpoint_ref(service_id=service_id,
 1453                                              interface='public',
 1454                                              region_id=self.region_id)
 1455         response = self.post('/endpoints', body={'endpoint': endpoint_ref})
 1456         endpoint = response.result['endpoint']
 1457 
 1458         # now add endpoint to project
 1459         self.put('/OS-EP-FILTER/projects/%(project_id)s'
 1460                  '/endpoints/%(endpoint_id)s' % {
 1461                      'project_id': self.project['id'],
 1462                      'endpoint_id': endpoint['id']})
 1463         return endpoint