1#   Copyright 2013 Nebula Inc.
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
16import copy
17import datetime
18from unittest import mock
19import uuid
20
21from keystoneauth1 import access
22from keystoneauth1 import fixture
23from osc_lib.cli import format_columns
24
25from openstackclient.tests.unit import fakes
26from openstackclient.tests.unit import utils
27
28base_url = 'http://identity:5000/v3/'
29
30domain_id = 'd1'
31domain_name = 'oftheking'
32domain_description = 'domain description'
33
34DOMAIN = {
35    'id': domain_id,
36    'name': domain_name,
37    'description': domain_description,
38    'enabled': True,
39    'tags': [],
40    'links': base_url + 'domains/' + domain_id,
41}
42
43group_id = 'gr-010'
44group_name = 'spencer davis'
45
46GROUP = {
47    'id': group_id,
48    'name': group_name,
49    'links': base_url + 'groups/' + group_id,
50}
51
52mapping_id = 'test_mapping'
53mapping_rules_file_path = '/tmp/path/to/file'
54# Copied from
55# (https://github.com/openstack/keystone/blob\
56# master/keystone/tests/mapping_fixtures.py
57EMPLOYEE_GROUP_ID = "0cd5e9"
58DEVELOPER_GROUP_ID = "xyz"
59MAPPING_RULES = [
60    {
61        "local": [
62            {
63                "group": {
64                    "id": EMPLOYEE_GROUP_ID
65                }
66            }
67        ],
68        "remote": [
69            {
70                "type": "orgPersonType",
71                "not_any_of": [
72                    "Contractor",
73                    "Guest"
74                ]
75            }
76        ]
77    }
78]
79
80MAPPING_RULES_2 = [
81    {
82        "local": [
83            {
84                "group": {
85                    "id": DEVELOPER_GROUP_ID
86                }
87            }
88        ],
89        "remote": [
90            {
91                "type": "orgPersonType",
92                "any_one_of": [
93                    "Contractor"
94                ]
95            }
96        ]
97    }
98]
99
100
101MAPPING_RESPONSE = {
102    "id": mapping_id,
103    "rules": MAPPING_RULES
104}
105
106MAPPING_RESPONSE_2 = {
107    "id": mapping_id,
108    "rules": MAPPING_RULES_2
109}
110
111project_id = '8-9-64'
112project_name = 'beatles'
113project_description = 'Fab Four'
114
115PROJECT = {
116    'id': project_id,
117    'name': project_name,
118    'description': project_description,
119    'enabled': True,
120    'domain_id': domain_id,
121    'tags': [],
122    'links': base_url + 'projects/' + project_id,
123}
124
125PROJECT_2 = {
126    'id': project_id + '-2222',
127    'name': project_name + ' reprise',
128    'description': project_description + 'plus four more',
129    'enabled': True,
130    'domain_id': domain_id,
131    'tags': [],
132    'links': base_url + 'projects/' + project_id,
133}
134
135region_id = 'region_one'
136region_parent_region_id = 'region_two'
137region_description = 'region one'
138
139REGION = {
140    'id': region_id,
141    'description': region_description,
142    'parent_region_id': region_parent_region_id,
143    'links': base_url + 'regions/' + region_id,
144}
145
146PROJECT_WITH_PARENT = {
147    'id': project_id + '-with-parent',
148    'name': project_name + ' and their parents',
149    'description': project_description + ' plus another four',
150    'enabled': True,
151    'domain_id': domain_id,
152    'parent_id': project_id,
153    'tags': [],
154    'links': base_url + 'projects/' + (project_id + '-with-parent'),
155}
156
157PROJECT_WITH_GRANDPARENT = {
158    'id': project_id + '-with-grandparent',
159    'name': project_name + ', granny and grandpa',
160    'description': project_description + ' plus another eight?',
161    'enabled': True,
162    'domain_id': domain_id,
163    'parent_id': PROJECT_WITH_PARENT['id'],
164    'tags': [],
165    'links': base_url + 'projects/' + (project_id + '-with-grandparent'),
166}
167
168parents = [{'project': PROJECT}]
169grandparents = [{'project': PROJECT}, {'project': PROJECT_WITH_PARENT}]
170ids_for_parents = [PROJECT['id']]
171ids_for_parents_and_grandparents = [PROJECT['id'], PROJECT_WITH_PARENT['id']]
172
173children = [{'project': PROJECT_WITH_GRANDPARENT}]
174ids_for_children = [PROJECT_WITH_GRANDPARENT['id']]
175
176
177role_id = 'r1'
178role_name = 'roller'
179role_description = 'role description'
180
181ROLE = {
182    'id': role_id,
183    'name': role_name,
184    'domain': None,
185    'links': base_url + 'roles/' + role_id,
186}
187
188ROLE_2 = {
189    'id': 'r2',
190    'name': 'Rolls Royce',
191    'domain': domain_id,
192    'links': base_url + 'roles/' + 'r2',
193}
194
195ROLES = [ROLE, ROLE_2]
196
197service_id = 's-123'
198service_name = 'Texaco'
199service_type = 'gas'
200service_description = 'oil brand'
201
202SERVICE = {
203    'id': service_id,
204    'name': service_name,
205    'type': service_type,
206    'description': service_description,
207    'enabled': True,
208    'links': base_url + 'services/' + service_id,
209}
210
211SERVICE_WITHOUT_NAME = {
212    'id': service_id,
213    'type': service_type,
214    'description': service_description,
215    'enabled': True,
216    'links': base_url + 'services/' + service_id,
217}
218
219endpoint_id = 'e-123'
220endpoint_url = 'http://127.0.0.1:35357'
221endpoint_region = 'RegionOne'
222endpoint_interface = 'admin'
223
224ENDPOINT = {
225    'id': endpoint_id,
226    'url': endpoint_url,
227    'region': endpoint_region,
228    'interface': endpoint_interface,
229    'service_id': service_id,
230    'enabled': True,
231    'links': base_url + 'endpoints/' + endpoint_id,
232}
233
234endpoint_group_id = 'eg-123'
235endpoint_group_description = 'eg 123 description'
236endpoint_group_filters = {
237    'service_id': service_id,
238    'region_id': endpoint_region,
239}
240endpoint_group_filters_2 = {
241    'region_id': endpoint_region,
242}
243endpoint_group_file_path = '/tmp/path/to/file'
244
245ENDPOINT_GROUP = {
246    'id': endpoint_group_id,
247    'filters': endpoint_group_filters,
248    'description': endpoint_group_description,
249    'links': base_url + 'endpoint_groups/' + endpoint_group_id,
250}
251
252user_id = 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa'
253user_name = 'paul'
254user_description = 'Sir Paul'
255user_email = 'paul@applecorps.com'
256
257USER = {
258    'id': user_id,
259    'name': user_name,
260    'default_project_id': project_id,
261    'email': user_email,
262    'enabled': True,
263    'domain_id': domain_id,
264    'links': base_url + 'users/' + user_id,
265}
266
267trust_id = 't-456'
268trust_expires = None
269trust_impersonation = False
270trust_roles = {"id": role_id, "name": role_name},
271
272TRUST = {
273    'expires_at': trust_expires,
274    'id': trust_id,
275    'impersonation': trust_impersonation,
276    'links': base_url + 'trusts/' + trust_id,
277    'project_id': project_id,
278    'roles': trust_roles,
279    'trustee_user_id': user_id,
280    'trustor_user_id': user_id,
281}
282
283token_expires = '2016-09-05T18:04:52+0000'
284token_id = 'tttttttt-tttt-tttt-tttt-tttttttttttt'
285
286UNSCOPED_TOKEN = {
287    'expires': token_expires,
288    'id': token_id,
289    'user_id': user_id,
290}
291
292TOKEN_WITH_PROJECT_ID = {
293    'expires': token_expires,
294    'id': token_id,
295    'project_id': project_id,
296    'user_id': user_id,
297}
298
299TOKEN_WITH_DOMAIN_ID = {
300    'expires': token_expires,
301    'id': token_id,
302    'domain_id': domain_id,
303    'user_id': user_id,
304}
305
306idp_id = 'test_idp'
307idp_description = 'super exciting IdP description'
308idp_remote_ids = ['entity1', 'entity2']
309formatted_idp_remote_ids = format_columns.ListColumn(idp_remote_ids)
310
311IDENTITY_PROVIDER = {
312    'id': idp_id,
313    'remote_ids': idp_remote_ids,
314    'enabled': True,
315    'description': idp_description,
316    'domain_id': domain_id,
317}
318
319protocol_id = 'protocol'
320
321mapping_id = 'test_mapping'
322mapping_id_updated = 'prod_mapping'
323
324sp_id = 'BETA'
325sp_description = 'Service Provider to burst into'
326service_provider_url = 'https://beta.example.com/Shibboleth.sso/POST/SAML'
327sp_auth_url = ('https://beta.example.com/v3/OS-FEDERATION/identity_providers/'
328               'idp/protocol/saml2/auth')
329
330SERVICE_PROVIDER = {
331    'id': sp_id,
332    'enabled': True,
333    'description': sp_description,
334    'sp_url': service_provider_url,
335    'auth_url': sp_auth_url
336}
337
338PROTOCOL_ID_MAPPING = {
339    'id': protocol_id,
340    'mapping': mapping_id
341}
342
343PROTOCOL_OUTPUT = {
344    'id': protocol_id,
345    'mapping_id': mapping_id,
346    'identity_provider': idp_id
347}
348
349PROTOCOL_OUTPUT_UPDATED = {
350    'id': protocol_id,
351    'mapping_id': mapping_id_updated,
352    'identity_provider': idp_id
353}
354
355# Assignments
356
357ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID = {
358    'scope': {'project': {'id': project_id}},
359    'user': {'id': user_id},
360    'role': {'id': role_id},
361}
362
363ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INCLUDE_NAMES = {
364    'scope': {
365        'project': {
366            'domain': {'id': domain_id,
367                       'name': domain_name},
368            'id': project_id,
369            'name': project_name}},
370    'user': {
371        'domain': {'id': domain_id,
372                   'name': domain_name},
373        'id': user_id,
374        'name': user_name},
375    'role': {'id': role_id,
376             'name': role_name},
377}
378
379ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INHERITED = {
380    'scope': {'project': {'id': project_id},
381              'OS-INHERIT:inherited_to': 'projects'},
382    'user': {'id': user_id},
383    'role': {'id': role_id},
384}
385
386ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID = {
387    'scope': {'project': {'id': project_id}},
388    'group': {'id': group_id},
389    'role': {'id': role_id},
390}
391
392ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID = {
393    'scope': {'domain': {'id': domain_id}},
394    'user': {'id': user_id},
395    'role': {'id': role_id},
396}
397
398ASSIGNMENT_WITH_DOMAIN_ROLE = {
399    'scope': {'domain': {'id': domain_id}},
400    'user': {'id': user_id},
401    'role': {'id': ROLE_2['id']},
402}
403
404ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INCLUDE_NAMES = {
405    'scope': {
406        'domain': {'id': domain_id,
407                   'name': domain_name}},
408    'user': {
409        'domain': {'id': domain_id,
410                   'name': domain_name},
411        'id': user_id,
412        'name': user_name},
413    'role': {'id': role_id,
414             'name': role_name},
415}
416
417ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INHERITED = {
418    'scope': {'domain': {'id': domain_id},
419              'OS-INHERIT:inherited_to': 'projects'},
420    'user': {'id': user_id},
421    'role': {'id': role_id},
422}
423
424ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID = {
425    'scope': {'domain': {'id': domain_id}},
426    'group': {'id': group_id},
427    'role': {'id': role_id},
428}
429
430consumer_id = 'test consumer id'
431consumer_description = 'someone we trust'
432consumer_secret = 'test consumer secret'
433
434OAUTH_CONSUMER = {
435    'id': consumer_id,
436    'secret': consumer_secret,
437    'description': consumer_description
438}
439
440access_token_id = 'test access token id'
441access_token_secret = 'test access token secret'
442access_token_expires = '2014-05-18T03:13:18.152071Z'
443
444OAUTH_ACCESS_TOKEN = {
445    'id': access_token_id,
446    'expires': access_token_expires,
447    'key': access_token_id,
448    'secret': access_token_secret
449}
450
451request_token_id = 'test request token id'
452request_token_secret = 'test request token secret'
453request_token_expires = '2014-05-17T11:10:51.511336Z'
454
455OAUTH_REQUEST_TOKEN = {
456    'id': request_token_id,
457    'expires': request_token_expires,
458    'key': request_token_id,
459    'secret': request_token_secret
460}
461
462oauth_verifier_pin = '6d74XaDS'
463OAUTH_VERIFIER = {
464    'oauth_verifier': oauth_verifier_pin
465}
466
467app_cred_id = 'app-cred-id'
468app_cred_name = 'testing_app_cred'
469app_cred_role = {"id": role_id, "name": role_name, "domain": None},
470app_cred_description = 'app credential for testing'
471app_cred_expires = datetime.datetime(2022, 1, 1, 0, 0)
472app_cred_expires_str = app_cred_expires.strftime('%Y-%m-%dT%H:%M:%S%z')
473app_cred_secret = 'moresecuresecret'
474app_cred_access_rules = (
475    '[{"path": "/v2.1/servers", "method": "GET", "service": "compute"}]'
476)
477app_cred_access_rules_path = '/tmp/access_rules.json'
478access_rule_id = 'access-rule-id'
479access_rule_service = 'compute'
480access_rule_path = '/v2.1/servers'
481access_rule_method = 'GET'
482APP_CRED_BASIC = {
483    'id': app_cred_id,
484    'name': app_cred_name,
485    'project_id': project_id,
486    'roles': app_cred_role,
487    'description': None,
488    'expires_at': None,
489    'unrestricted': False,
490    'secret': app_cred_secret,
491    'access_rules': None
492}
493APP_CRED_OPTIONS = {
494    'id': app_cred_id,
495    'name': app_cred_name,
496    'project_id': project_id,
497    'roles': app_cred_role,
498    'description': app_cred_description,
499    'expires_at': app_cred_expires_str,
500    'unrestricted': False,
501    'secret': app_cred_secret,
502    'access_rules': None,
503}
504ACCESS_RULE = {
505    'id': access_rule_id,
506    'service': access_rule_service,
507    'path': access_rule_path,
508    'method': access_rule_method,
509}
510APP_CRED_ACCESS_RULES = {
511    'id': app_cred_id,
512    'name': app_cred_name,
513    'project_id': project_id,
514    'roles': app_cred_role,
515    'description': None,
516    'expires_at': None,
517    'unrestricted': False,
518    'secret': app_cred_secret,
519    'access_rules': app_cred_access_rules
520}
521
522registered_limit_id = 'registered-limit-id'
523registered_limit_default_limit = 10
524registered_limit_description = 'default limit of foobars'
525registered_limit_resource_name = 'foobars'
526REGISTERED_LIMIT = {
527    'id': registered_limit_id,
528    'default_limit': registered_limit_default_limit,
529    'resource_name': registered_limit_resource_name,
530    'service_id': service_id,
531    'description': None,
532    'region_id': None
533}
534REGISTERED_LIMIT_OPTIONS = {
535    'id': registered_limit_id,
536    'default_limit': registered_limit_default_limit,
537    'resource_name': registered_limit_resource_name,
538    'service_id': service_id,
539    'description': registered_limit_description,
540    'region_id': region_id
541}
542
543limit_id = 'limit-id'
544limit_resource_limit = 15
545limit_description = 'limit of foobars'
546limit_resource_name = 'foobars'
547LIMIT = {
548    'id': limit_id,
549    'project_id': project_id,
550    'resource_limit': limit_resource_limit,
551    'resource_name': limit_resource_name,
552    'service_id': service_id,
553    'description': None,
554    'region_id': None
555}
556LIMIT_OPTIONS = {
557    'id': limit_id,
558    'project_id': project_id,
559    'resource_limit': limit_resource_limit,
560    'resource_name': limit_resource_name,
561    'service_id': service_id,
562    'description': limit_description,
563    'region_id': region_id
564}
565
566
567def fake_auth_ref(fake_token, fake_service=None):
568    """Create an auth_ref using keystoneauth's fixtures"""
569    token_copy = copy.deepcopy(fake_token)
570    token_id = token_copy.pop('id')
571    token = fixture.V3Token(**token_copy)
572    # An auth_ref is actually an access info object
573    auth_ref = access.create(
574        body=token,
575        auth_token=token_id,
576    )
577
578    # Create a service catalog
579    if fake_service:
580        service = token.add_service(
581            fake_service['type'],
582            fake_service['name'],
583        )
584        # TODO(dtroyer): Add an 'id' element to KSA's _Service fixure
585        service['id'] = fake_service['id']
586        for e in fake_service['endpoints']:
587            region = e.get('region_id') or e.get('region', '<none>')
588            service.add_endpoint(
589                e['interface'],
590                e['url'],
591                region=region,
592            )
593
594    return auth_ref
595
596
597class FakeAuth(object):
598
599    def __init__(self, auth_method_class=None):
600        self._auth_method_class = auth_method_class
601
602    def get_token(self, *args, **kwargs):
603        return token_id
604
605
606class FakeSession(object):
607
608    def __init__(self, **kwargs):
609        self.auth = FakeAuth()
610
611
612class FakeIdentityv3Client(object):
613
614    def __init__(self, **kwargs):
615        self.domains = mock.Mock()
616        self.domains.resource_class = fakes.FakeResource(None, {})
617        self.credentials = mock.Mock()
618        self.credentials.resource_class = fakes.FakeResource(None, {})
619        self.endpoints = mock.Mock()
620        self.endpoints.resource_class = fakes.FakeResource(None, {})
621        self.endpoint_filter = mock.Mock()
622        self.endpoint_filter.resource_class = fakes.FakeResource(None, {})
623        self.endpoint_groups = mock.Mock()
624        self.endpoint_groups.resource_class = fakes.FakeResource(None, {})
625        self.groups = mock.Mock()
626        self.groups.resource_class = fakes.FakeResource(None, {})
627        self.oauth1 = mock.Mock()
628        self.oauth1.resource_class = fakes.FakeResource(None, {})
629        self.projects = mock.Mock()
630        self.projects.resource_class = fakes.FakeResource(None, {})
631        self.regions = mock.Mock()
632        self.regions.resource_class = fakes.FakeResource(None, {})
633        self.roles = mock.Mock()
634        self.roles.resource_class = fakes.FakeResource(None, {})
635        self.services = mock.Mock()
636        self.services.resource_class = fakes.FakeResource(None, {})
637        self.session = mock.Mock()
638        self.session.auth.auth_ref.service_catalog.resource_class = \
639            fakes.FakeResource(None, {})
640        self.tokens = mock.Mock()
641        self.tokens.resource_class = fakes.FakeResource(None, {})
642        self.trusts = mock.Mock()
643        self.trusts.resource_class = fakes.FakeResource(None, {})
644        self.users = mock.Mock()
645        self.users.resource_class = fakes.FakeResource(None, {})
646        self.role_assignments = mock.Mock()
647        self.role_assignments.resource_class = fakes.FakeResource(None, {})
648        self.auth_token = kwargs['token']
649        self.management_url = kwargs['endpoint']
650        self.auth = FakeAuth()
651        self.auth.client = mock.Mock()
652        self.auth.client.resource_class = fakes.FakeResource(None, {})
653        self.application_credentials = mock.Mock()
654        self.application_credentials.resource_class = fakes.FakeResource(None,
655                                                                         {})
656        self.access_rules = mock.Mock()
657        self.access_rules.resource_class = fakes.FakeResource(None, {})
658        self.inference_rules = mock.Mock()
659        self.inference_rules.resource_class = fakes.FakeResource(None, {})
660        self.registered_limits = mock.Mock()
661        self.registered_limits.resource_class = fakes.FakeResource(None, {})
662        self.limits = mock.Mock()
663        self.limits.resource_class = fakes.FakeResource(None, {})
664
665
666class FakeFederationManager(object):
667
668    def __init__(self, **kwargs):
669        self.identity_providers = mock.Mock()
670        self.identity_providers.resource_class = fakes.FakeResource(None, {})
671        self.mappings = mock.Mock()
672        self.mappings.resource_class = fakes.FakeResource(None, {})
673        self.protocols = mock.Mock()
674        self.protocols.resource_class = fakes.FakeResource(None, {})
675        self.projects = mock.Mock()
676        self.projects.resource_class = fakes.FakeResource(None, {})
677        self.domains = mock.Mock()
678        self.domains.resource_class = fakes.FakeResource(None, {})
679        self.service_providers = mock.Mock()
680        self.service_providers.resource_class = fakes.FakeResource(None, {})
681
682
683class FakeFederatedClient(FakeIdentityv3Client):
684
685    def __init__(self, **kwargs):
686        super(FakeFederatedClient, self).__init__(**kwargs)
687        self.federation = FakeFederationManager()
688
689
690class FakeOAuth1Client(FakeIdentityv3Client):
691
692    def __init__(self, **kwargs):
693        super(FakeOAuth1Client, self).__init__(**kwargs)
694
695        self.access_tokens = mock.Mock()
696        self.access_tokens.resource_class = fakes.FakeResource(None, {})
697        self.consumers = mock.Mock()
698        self.consumers.resource_class = fakes.FakeResource(None, {})
699        self.request_tokens = mock.Mock()
700        self.request_tokens.resource_class = fakes.FakeResource(None, {})
701
702
703class TestIdentityv3(utils.TestCommand):
704
705    def setUp(self):
706        super(TestIdentityv3, self).setUp()
707
708        self.app.client_manager.identity = FakeIdentityv3Client(
709            endpoint=fakes.AUTH_URL,
710            token=fakes.AUTH_TOKEN,
711        )
712
713
714class TestFederatedIdentity(utils.TestCommand):
715
716    def setUp(self):
717        super(TestFederatedIdentity, self).setUp()
718
719        self.app.client_manager.identity = FakeFederatedClient(
720            endpoint=fakes.AUTH_URL,
721            token=fakes.AUTH_TOKEN
722        )
723
724
725class TestOAuth1(utils.TestCommand):
726
727    def setUp(self):
728        super(TestOAuth1, self).setUp()
729
730        self.app.client_manager.identity = FakeOAuth1Client(
731            endpoint=fakes.AUTH_URL,
732            token=fakes.AUTH_TOKEN
733        )
734
735
736class FakeProject(object):
737    """Fake one or more project."""
738
739    @staticmethod
740    def create_one_project(attrs=None):
741        """Create a fake project.
742
743        :param Dictionary attrs:
744            A dictionary with all attributes
745        :return:
746            A FakeResource object, with id, name, and so on
747        """
748
749        attrs = attrs or {}
750
751        # set default attributes.
752        project_info = {
753            'id': 'project-id-' + uuid.uuid4().hex,
754            'name': 'project-name-' + uuid.uuid4().hex,
755            'description': 'project-description-' + uuid.uuid4().hex,
756            'enabled': True,
757            'is_domain': False,
758            'domain_id': 'domain-id-' + uuid.uuid4().hex,
759            'parent_id': 'parent-id-' + uuid.uuid4().hex,
760            'tags': [],
761            'links': 'links-' + uuid.uuid4().hex,
762        }
763        project_info.update(attrs)
764
765        project = fakes.FakeResource(info=copy.deepcopy(project_info),
766                                     loaded=True)
767        return project
768
769    @staticmethod
770    def create_projects(attrs=None, count=2):
771        """Create multiple fake projects.
772
773        :param Dictionary attrs:
774            A dictionary with all attributes
775        :param int count:
776            The number of projects to fake
777        :return:
778            A list of FakeResource objects faking the projects
779        """
780
781        projects = []
782        for i in range(0, count):
783            projects.append(FakeProject.create_one_project(attrs))
784        return projects
785
786
787class FakeDomain(object):
788    """Fake one or more domain."""
789
790    @staticmethod
791    def create_one_domain(attrs=None):
792        """Create a fake domain.
793
794        :param Dictionary attrs:
795            A dictionary with all attributes
796        :return:
797            A FakeResource object, with id, name, and so on
798        """
799
800        attrs = attrs or {}
801
802        # set default attributes.
803        domain_info = {
804            'id': 'domain-id-' + uuid.uuid4().hex,
805            'name': 'domain-name-' + uuid.uuid4().hex,
806            'description': 'domain-description-' + uuid.uuid4().hex,
807            'enabled': True,
808            'tags': [],
809            'links': 'links-' + uuid.uuid4().hex,
810        }
811        domain_info.update(attrs)
812
813        domain = fakes.FakeResource(info=copy.deepcopy(domain_info),
814                                    loaded=True)
815        return domain
816
817
818class FakeCredential(object):
819    """Fake one or more credential."""
820
821    @staticmethod
822    def create_one_credential(attrs=None):
823        """Create a fake credential.
824
825        :param Dictionary attrs:
826            A dictionary with all attributes
827        :return:
828            A FakeResource object, with id, type, and so on
829        """
830
831        attrs = attrs or {}
832
833        # set default attributes.
834        credential_info = {
835            'id': 'credential-id-' + uuid.uuid4().hex,
836            'type': 'cert',
837            'user_id': 'user-id-' + uuid.uuid4().hex,
838            'blob': 'credential-data-' + uuid.uuid4().hex,
839            'project_id': 'project-id-' + uuid.uuid4().hex,
840            'links': 'links-' + uuid.uuid4().hex,
841        }
842        credential_info.update(attrs)
843
844        credential = fakes.FakeResource(
845            info=copy.deepcopy(credential_info), loaded=True)
846        return credential
847
848    @staticmethod
849    def create_credentials(attrs=None, count=2):
850        """Create multiple fake credentials.
851
852        :param Dictionary attrs:
853            A dictionary with all attributes
854        :param int count:
855            The number of credentials to fake
856        :return:
857            A list of FakeResource objects faking the credentials
858        """
859        credentials = []
860        for i in range(0, count):
861            credential = FakeCredential.create_one_credential(attrs)
862            credentials.append(credential)
863
864        return credentials
865
866    @staticmethod
867    def get_credentials(credentials=None, count=2):
868        """Get an iterable MagicMock object with a list of faked credentials.
869
870        If credentials list is provided, then initialize the Mock object with
871        the list. Otherwise create one.
872
873        :param List credentials:
874            A list of FakeResource objects faking credentials
875        :param Integer count:
876            The number of credentials to be faked
877        :return:
878            An iterable Mock object with side_effect set to a list of faked
879            credentials
880        """
881        if credentials is None:
882            credentials = FakeCredential.create_credentials(count)
883
884        return mock.Mock(side_effect=credentials)
885
886
887class FakeUser(object):
888    """Fake one or more user."""
889
890    @staticmethod
891    def create_one_user(attrs=None):
892        """Create a fake user.
893
894        :param Dictionary attrs:
895            A dictionary with all attributes
896        :return:
897            A FakeResource object, with id, name, and so on
898        """
899
900        attrs = attrs or {}
901
902        # set default attributes.
903        user_info = {
904            'id': 'user-id-' + uuid.uuid4().hex,
905            'name': 'user-name-' + uuid.uuid4().hex,
906            'default_project_id': 'project-' + uuid.uuid4().hex,
907            'email': 'user-email-' + uuid.uuid4().hex,
908            'enabled': True,
909            'domain_id': 'domain-id-' + uuid.uuid4().hex,
910            'links': 'links-' + uuid.uuid4().hex,
911        }
912        user_info.update(attrs)
913
914        user = fakes.FakeResource(info=copy.deepcopy(user_info),
915                                  loaded=True)
916        return user
917
918    @staticmethod
919    def create_users(attrs=None, count=2):
920        """Create multiple fake users.
921
922        :param Dictionary attrs:
923            A dictionary with all attributes
924        :param int count:
925            The number of users to fake
926        :return:
927            A list of FakeResource objects faking the users
928        """
929        users = []
930        for i in range(0, count):
931            user = FakeUser.create_one_user(attrs)
932            users.append(user)
933
934        return users
935
936    @staticmethod
937    def get_users(users=None, count=2):
938        """Get an iterable MagicMock object with a list of faked users.
939
940        If users list is provided, then initialize the Mock object with
941        the list. Otherwise create one.
942
943        :param List users:
944            A list of FakeResource objects faking users
945        :param Integer count:
946            The number of users to be faked
947        :return
948            An iterable Mock object with side_effect set to a list of faked
949            users
950        """
951        if users is None:
952            users = FakeUser.create_users(count)
953
954        return mock.Mock(side_effect=users)
955
956
957class FakeGroup(object):
958    """Fake one or more group."""
959
960    @staticmethod
961    def create_one_group(attrs=None):
962        """Create a fake group.
963
964        :param Dictionary attrs:
965            A dictionary with all attributes
966        :return:
967            A FakeResource object, with id, name, and so on
968        """
969
970        attrs = attrs or {}
971
972        # set default attributes.
973        group_info = {
974            'id': 'group-id-' + uuid.uuid4().hex,
975            'name': 'group-name-' + uuid.uuid4().hex,
976            'links': 'links-' + uuid.uuid4().hex,
977            'domain_id': 'domain-id-' + uuid.uuid4().hex,
978            'description': 'group-description-' + uuid.uuid4().hex,
979        }
980        group_info.update(attrs)
981
982        group = fakes.FakeResource(info=copy.deepcopy(group_info),
983                                   loaded=True)
984        return group
985
986    @staticmethod
987    def create_groups(attrs=None, count=2):
988        """Create multiple fake groups.
989
990        :param Dictionary attrs:
991            A dictionary with all attributes
992        :param int count:
993            The number of groups to fake
994        :return:
995            A list of FakeResource objects faking the groups
996        """
997        groups = []
998        for i in range(0, count):
999            group = FakeGroup.create_one_group(attrs)
1000            groups.append(group)
1001
1002        return groups
1003
1004    @staticmethod
1005    def get_groups(groups=None, count=2):
1006        """Get an iterable MagicMock object with a list of faked groups.
1007
1008        If groups list is provided, then initialize the Mock object with
1009        the list. Otherwise create one.
1010
1011        :param List groups:
1012            A list of FakeResource objects faking groups
1013        :param Integer count:
1014            The number of groups to be faked
1015        :return:
1016            An iterable Mock object with side_effect set to a list of faked
1017            groups
1018        """
1019        if groups is None:
1020            groups = FakeGroup.create_groups(count)
1021
1022        return mock.Mock(side_effect=groups)
1023
1024
1025class FakeEndpoint(object):
1026    """Fake one or more endpoint."""
1027
1028    @staticmethod
1029    def create_one_endpoint(attrs=None):
1030        """Create a fake endpoint.
1031
1032        :param Dictionary attrs:
1033            A dictionary with all attributes
1034        :return:
1035            A FakeResource object, with id, url, and so on
1036        """
1037
1038        attrs = attrs or {}
1039
1040        # set default attributes.
1041        endpoint_info = {
1042            'id': 'endpoint-id-' + uuid.uuid4().hex,
1043            'url': 'url-' + uuid.uuid4().hex,
1044            'region': 'endpoint-region-' + uuid.uuid4().hex,
1045            'interface': 'admin',
1046            'service_id': 'service-id-' + uuid.uuid4().hex,
1047            'enabled': True,
1048            'links': 'links-' + uuid.uuid4().hex,
1049        }
1050        endpoint_info.update(attrs)
1051
1052        endpoint = fakes.FakeResource(info=copy.deepcopy(endpoint_info),
1053                                      loaded=True)
1054        return endpoint
1055
1056    @staticmethod
1057    def create_one_endpoint_filter(attrs=None):
1058        """Create a fake endpoint project relationship.
1059
1060        :param Dictionary attrs:
1061            A dictionary with all attributes of endpoint filter
1062        :return:
1063            A FakeResource object with project, endpoint and so on
1064        """
1065        attrs = attrs or {}
1066
1067        # Set default attribute
1068        endpoint_filter_info = {
1069            'project': 'project-id-' + uuid.uuid4().hex,
1070            'endpoint': 'endpoint-id-' + uuid.uuid4().hex,
1071        }
1072
1073        # Overwrite default attributes if there are some attributes set
1074        endpoint_filter_info.update(attrs)
1075
1076        endpoint_filter = fakes.FakeModel(
1077            copy.deepcopy(endpoint_filter_info))
1078
1079        return endpoint_filter
1080
1081
1082class FakeEndpointGroup(object):
1083    """Fake one or more endpoint group."""
1084
1085    @staticmethod
1086    def create_one_endpointgroup(attrs=None):
1087        """Create a fake endpoint group.
1088
1089        :param Dictionary attrs:
1090            A dictionary with all attributes
1091        :return:
1092            A FakeResource object, with id, url, and so on
1093        """
1094
1095        attrs = attrs or {}
1096
1097        # set default attributes.
1098        endpointgroup_info = {
1099            'id': 'endpoint-group-id-' + uuid.uuid4().hex,
1100            'name': 'endpoint-group-name-' + uuid.uuid4().hex,
1101            'filters': {
1102                'region': 'region-' + uuid.uuid4().hex,
1103                'service_id': 'service-id-' + uuid.uuid4().hex,
1104            },
1105            'description': 'endpoint-group-description-' + uuid.uuid4().hex,
1106            'links': 'links-' + uuid.uuid4().hex,
1107        }
1108        endpointgroup_info.update(attrs)
1109
1110        endpoint = fakes.FakeResource(info=copy.deepcopy(endpointgroup_info),
1111                                      loaded=True)
1112        return endpoint
1113
1114    @staticmethod
1115    def create_one_endpointgroup_filter(attrs=None):
1116        """Create a fake endpoint project relationship.
1117
1118        :param Dictionary attrs:
1119            A dictionary with all attributes of endpointgroup filter
1120        :return:
1121            A FakeResource object with project, endpointgroup and so on
1122        """
1123        attrs = attrs or {}
1124
1125        # Set default attribute
1126        endpointgroup_filter_info = {
1127            'project': 'project-id-' + uuid.uuid4().hex,
1128            'endpointgroup': 'endpointgroup-id-' + uuid.uuid4().hex,
1129        }
1130
1131        # Overwrite default attributes if there are some attributes set
1132        endpointgroup_filter_info.update(attrs)
1133
1134        endpointgroup_filter = fakes.FakeModel(
1135            copy.deepcopy(endpointgroup_filter_info))
1136
1137        return endpointgroup_filter
1138
1139
1140class FakeService(object):
1141    """Fake one or more service."""
1142
1143    @staticmethod
1144    def create_one_service(attrs=None):
1145        """Create a fake service.
1146
1147        :param Dictionary attrs:
1148            A dictionary with all attributes
1149        :return:
1150            A FakeResource object, with id, name, and so on
1151        """
1152
1153        attrs = attrs or {}
1154
1155        # set default attributes.
1156        service_info = {
1157            'id': 'service-id-' + uuid.uuid4().hex,
1158            'name': 'service-name-' + uuid.uuid4().hex,
1159            'type': 'service-type-' + uuid.uuid4().hex,
1160            'description': 'service-description-' + uuid.uuid4().hex,
1161            'enabled': True,
1162            'links': 'links-' + uuid.uuid4().hex,
1163        }
1164        service_info.update(attrs)
1165
1166        service = fakes.FakeResource(info=copy.deepcopy(service_info),
1167                                     loaded=True)
1168        return service
1169
1170
1171class FakeRoleAssignment(object):
1172    """Fake one or more role assignment."""
1173
1174    @staticmethod
1175    def create_one_role_assignment(attrs=None):
1176        """Create a fake role assignment.
1177
1178        :param Dictionary attrs:
1179            A dictionary with all attributes
1180        :return:
1181            A FakeResource object, with scope, user, and so on
1182        """
1183
1184        attrs = attrs or {}
1185
1186        # set default attributes.
1187        role_assignment_info = {
1188            'scope': {'project': {'id': 'project-id-' + uuid.uuid4().hex}},
1189            'user': {'id': 'user-id-' + uuid.uuid4().hex},
1190            'role': {'id': 'role-id-' + uuid.uuid4().hex},
1191        }
1192        role_assignment_info.update(attrs)
1193
1194        role_assignment = fakes.FakeResource(
1195            info=copy.deepcopy(role_assignment_info), loaded=True)
1196
1197        return role_assignment
1198
1199
1200class FakeImpliedRoleResponse(object):
1201    """Fake one or more role assignment."""
1202    def __init__(self, prior_role, implied_roles):
1203        self.prior_role = prior_role
1204        self.implies = [role for role in implied_roles]
1205
1206    @staticmethod
1207    def create_list():
1208        """Create a fake implied role list response.
1209
1210        :return:
1211            A list of FakeImpliedRoleResponse objects
1212        """
1213
1214        # set default attributes.
1215        implied_roles = [
1216            FakeImpliedRoleResponse(ROLES[0], [ROLES[1]])
1217        ]
1218
1219        return implied_roles
1220