"Fossies" - the Fresh Open Source Software Archive

Member "keystone-17.0.0/keystone/tests/unit/contrib/federation/test_utils.py" (13 May 2020, 40681 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_utils.py": 16.0.1_vs_17.0.0.

    1 # Licensed under the Apache License, Version 2.0 (the "License"); you may
    2 # not use this file except in compliance with the License. You may obtain
    3 # a copy of the License at
    4 #
    5 #      http://www.apache.org/licenses/LICENSE-2.0
    6 #
    7 # Unless required by applicable law or agreed to in writing, software
    8 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    9 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   10 # License for the specific language governing permissions and limitations
   11 # under the License.
   12 
   13 import flask
   14 import uuid
   15 
   16 from oslo_config import fixture as config_fixture
   17 from oslo_serialization import jsonutils
   18 
   19 from keystone.auth.plugins import mapped
   20 import keystone.conf
   21 from keystone import exception
   22 from keystone.federation import utils as mapping_utils
   23 from keystone.tests import unit
   24 from keystone.tests.unit import mapping_fixtures
   25 
   26 
   27 CONF = keystone.conf.CONF
   28 FAKE_MAPPING_ID = uuid.uuid4().hex
   29 
   30 
   31 class MappingRuleEngineTests(unit.BaseTestCase):
   32     """A class for testing the mapping rule engine."""
   33 
   34     def setUp(self):
   35         super(MappingRuleEngineTests, self).setUp()
   36         # create dummy app so we can setup a request context for our
   37         # tests.
   38         self.flask_app = flask.Flask(__name__)
   39         self.cleanup_instance('flask_app')
   40 
   41     def assertValidMappedUserObject(self, mapped_properties,
   42                                     user_type='ephemeral',
   43                                     domain_id=None):
   44         """Check whether mapped properties object has 'user' within.
   45 
   46         According to today's rules, RuleProcessor does not have to issue user's
   47         id or name. What's actually required is user's type.
   48         """
   49         self.assertIn('user', mapped_properties,
   50                       message='Missing user object in mapped properties')
   51         user = mapped_properties['user']
   52         self.assertIn('type', user)
   53         self.assertEqual(user_type, user['type'])
   54 
   55         if domain_id:
   56             domain = user['domain']
   57             domain_name_or_id = domain.get('id') or domain.get('name')
   58             self.assertEqual(domain_id, domain_name_or_id)
   59 
   60     def test_rule_engine_any_one_of_and_direct_mapping(self):
   61         """Should return user's name and group id EMPLOYEE_GROUP_ID.
   62 
   63         The ADMIN_ASSERTION should successfully have a match in MAPPING_LARGE.
   64         They will test the case where `any_one_of` is valid, and there is
   65         a direct mapping for the users name.
   66 
   67         """
   68         mapping = mapping_fixtures.MAPPING_LARGE
   69         assertion = mapping_fixtures.ADMIN_ASSERTION
   70         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
   71         values = rp.process(assertion)
   72 
   73         fn = assertion.get('FirstName')
   74         ln = assertion.get('LastName')
   75         full_name = '%s %s' % (fn, ln)
   76         group_ids = values.get('group_ids')
   77         user_name = values.get('user', {}).get('name')
   78 
   79         self.assertIn(mapping_fixtures.EMPLOYEE_GROUP_ID, group_ids)
   80         self.assertEqual(full_name, user_name)
   81 
   82     def test_rule_engine_no_regex_match(self):
   83         """Should deny authorization, the email of the tester won't match.
   84 
   85         This will not match since the email in the assertion will fail
   86         the regex test. It is set to match any @example.com address.
   87         But the incoming value is set to eviltester@example.org.
   88         RuleProcessor should raise ValidationError.
   89 
   90         """
   91         mapping = mapping_fixtures.MAPPING_LARGE
   92         assertion = mapping_fixtures.BAD_TESTER_ASSERTION
   93         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
   94         self.assertRaises(exception.ValidationError,
   95                           rp.process,
   96                           assertion)
   97 
   98     def test_rule_engine_regex_many_groups(self):
   99         """Should return group CONTRACTOR_GROUP_ID.
  100 
  101         The TESTER_ASSERTION should successfully have a match in
  102         MAPPING_TESTER_REGEX. This will test the case where many groups
  103         are in the assertion, and a regex value is used to try and find
  104         a match.
  105 
  106         """
  107         mapping = mapping_fixtures.MAPPING_TESTER_REGEX
  108         assertion = mapping_fixtures.TESTER_ASSERTION
  109         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  110         values = rp.process(assertion)
  111 
  112         self.assertValidMappedUserObject(values)
  113         user_name = assertion.get('UserName')
  114         group_ids = values.get('group_ids')
  115         name = values.get('user', {}).get('name')
  116 
  117         self.assertEqual(user_name, name)
  118         self.assertIn(mapping_fixtures.TESTER_GROUP_ID, group_ids)
  119 
  120     def test_rule_engine_any_one_of_many_rules(self):
  121         """Should return group CONTRACTOR_GROUP_ID.
  122 
  123         The CONTRACTOR_ASSERTION should successfully have a match in
  124         MAPPING_SMALL. This will test the case where many rules
  125         must be matched, including an `any_one_of`, and a direct
  126         mapping.
  127 
  128         """
  129         mapping = mapping_fixtures.MAPPING_SMALL
  130         assertion = mapping_fixtures.CONTRACTOR_ASSERTION
  131         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  132         values = rp.process(assertion)
  133 
  134         self.assertValidMappedUserObject(values)
  135         user_name = assertion.get('UserName')
  136         group_ids = values.get('group_ids')
  137         name = values.get('user', {}).get('name')
  138 
  139         self.assertEqual(user_name, name)
  140         self.assertIn(mapping_fixtures.CONTRACTOR_GROUP_ID, group_ids)
  141 
  142     def test_rule_engine_not_any_of_and_direct_mapping(self):
  143         """Should return user's name and email.
  144 
  145         The CUSTOMER_ASSERTION should successfully have a match in
  146         MAPPING_LARGE. This will test the case where a requirement
  147         has `not_any_of`, and direct mapping to a username, no group.
  148 
  149         """
  150         mapping = mapping_fixtures.MAPPING_LARGE
  151         assertion = mapping_fixtures.CUSTOMER_ASSERTION
  152         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  153         values = rp.process(assertion)
  154 
  155         self.assertValidMappedUserObject(values)
  156         user_name = assertion.get('UserName')
  157         group_ids = values.get('group_ids')
  158         name = values.get('user', {}).get('name')
  159 
  160         self.assertEqual(user_name, name)
  161         self.assertEqual([], group_ids,)
  162 
  163     def test_rule_engine_not_any_of_many_rules(self):
  164         """Should return group EMPLOYEE_GROUP_ID.
  165 
  166         The EMPLOYEE_ASSERTION should successfully have a match in
  167         MAPPING_SMALL. This will test the case where many remote
  168         rules must be matched, including a `not_any_of`.
  169 
  170         """
  171         mapping = mapping_fixtures.MAPPING_SMALL
  172         assertion = mapping_fixtures.EMPLOYEE_ASSERTION
  173         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  174         values = rp.process(assertion)
  175 
  176         self.assertValidMappedUserObject(values)
  177         user_name = assertion.get('UserName')
  178         group_ids = values.get('group_ids')
  179         name = values.get('user', {}).get('name')
  180 
  181         self.assertEqual(user_name, name)
  182         self.assertIn(mapping_fixtures.EMPLOYEE_GROUP_ID, group_ids)
  183 
  184     def test_rule_engine_not_any_of_regex_verify_pass(self):
  185         """Should return group DEVELOPER_GROUP_ID.
  186 
  187         The DEVELOPER_ASSERTION should successfully have a match in
  188         MAPPING_DEVELOPER_REGEX. This will test the case where many
  189         remote rules must be matched, including a `not_any_of`, with
  190         regex set to True.
  191 
  192         """
  193         mapping = mapping_fixtures.MAPPING_DEVELOPER_REGEX
  194         assertion = mapping_fixtures.DEVELOPER_ASSERTION
  195         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  196         values = rp.process(assertion)
  197 
  198         self.assertValidMappedUserObject(values)
  199         user_name = assertion.get('UserName')
  200         group_ids = values.get('group_ids')
  201         name = values.get('user', {}).get('name')
  202 
  203         self.assertEqual(user_name, name)
  204         self.assertIn(mapping_fixtures.DEVELOPER_GROUP_ID, group_ids)
  205 
  206     def test_rule_engine_not_any_of_regex_verify_fail(self):
  207         """Should deny authorization.
  208 
  209         The email in the assertion will fail the regex test.
  210         It is set to reject any @example.org address, but the
  211         incoming value is set to evildeveloper@example.org.
  212         RuleProcessor should yield ValidationError.
  213 
  214         """
  215         mapping = mapping_fixtures.MAPPING_DEVELOPER_REGEX
  216         assertion = mapping_fixtures.BAD_DEVELOPER_ASSERTION
  217         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  218         self.assertRaises(exception.ValidationError,
  219                           rp.process,
  220                           assertion)
  221 
  222     def _rule_engine_regex_match_and_many_groups(self, assertion):
  223         """Should return group DEVELOPER_GROUP_ID and TESTER_GROUP_ID.
  224 
  225         A helper function injecting assertion passed as an argument.
  226         Expect DEVELOPER_GROUP_ID and TESTER_GROUP_ID in the results.
  227 
  228         """
  229         mapping = mapping_fixtures.MAPPING_LARGE
  230         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  231         values = rp.process(assertion)
  232 
  233         user_name = assertion.get('UserName')
  234         group_ids = values.get('group_ids')
  235         name = values.get('user', {}).get('name')
  236 
  237         self.assertValidMappedUserObject(values)
  238         self.assertEqual(user_name, name)
  239         self.assertIn(mapping_fixtures.DEVELOPER_GROUP_ID, group_ids)
  240         self.assertIn(mapping_fixtures.TESTER_GROUP_ID, group_ids)
  241 
  242     def test_rule_engine_regex_match_and_many_groups(self):
  243         """Should return group DEVELOPER_GROUP_ID and TESTER_GROUP_ID.
  244 
  245         The TESTER_ASSERTION should successfully have a match in
  246         MAPPING_LARGE. This will test a successful regex match
  247         for an `any_one_of` evaluation type, and will have many
  248         groups returned.
  249 
  250         """
  251         self._rule_engine_regex_match_and_many_groups(
  252             mapping_fixtures.TESTER_ASSERTION)
  253 
  254     def test_rule_engine_discards_nonstring_objects(self):
  255         """Check whether RuleProcessor discards non string objects.
  256 
  257         Despite the fact that assertion is malformed and contains
  258         non string objects, RuleProcessor should correctly discard them and
  259         successfully have a match in MAPPING_LARGE.
  260 
  261         """
  262         self._rule_engine_regex_match_and_many_groups(
  263             mapping_fixtures.MALFORMED_TESTER_ASSERTION)
  264 
  265     def test_rule_engine_fails_after_discarding_nonstring(self):
  266         """Check whether RuleProcessor discards non string objects.
  267 
  268         Expect RuleProcessor to discard non string object, which
  269         is required for a correct rule match. RuleProcessor will result with
  270         ValidationError.
  271 
  272         """
  273         mapping = mapping_fixtures.MAPPING_SMALL
  274         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  275         assertion = mapping_fixtures.CONTRACTOR_MALFORMED_ASSERTION
  276         self.assertRaises(exception.ValidationError,
  277                           rp.process,
  278                           assertion)
  279 
  280     def test_using_remote_direct_mapping_that_doesnt_exist_fails(self):
  281         """Test for the correct error when referring to a bad remote match.
  282 
  283         The remote match must exist in a rule when a local section refers to
  284         a remote matching using the format (e.g. {0} in a local section).
  285         """
  286         mapping = mapping_fixtures.MAPPING_DIRECT_MAPPING_THROUGH_KEYWORD
  287         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  288         assertion = mapping_fixtures.CUSTOMER_ASSERTION
  289 
  290         self.assertRaises(exception.DirectMappingError,
  291                           rp.process,
  292                           assertion)
  293 
  294     def test_rule_engine_returns_group_names(self):
  295         """Check whether RuleProcessor returns group names with their domains.
  296 
  297         RuleProcessor should return 'group_names' entry with a list of
  298         dictionaries with two entries 'name' and 'domain' identifying group by
  299         its name and domain.
  300 
  301         """
  302         mapping = mapping_fixtures.MAPPING_GROUP_NAMES
  303         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  304         assertion = mapping_fixtures.EMPLOYEE_ASSERTION
  305         mapped_properties = rp.process(assertion)
  306         self.assertIsNotNone(mapped_properties)
  307         self.assertValidMappedUserObject(mapped_properties)
  308         reference = {
  309             mapping_fixtures.DEVELOPER_GROUP_NAME:
  310             {
  311                 "name": mapping_fixtures.DEVELOPER_GROUP_NAME,
  312                 "domain": {
  313                     "name": mapping_fixtures.DEVELOPER_GROUP_DOMAIN_NAME
  314                 }
  315             },
  316             mapping_fixtures.TESTER_GROUP_NAME:
  317             {
  318                 "name": mapping_fixtures.TESTER_GROUP_NAME,
  319                 "domain": {
  320                     "id": mapping_fixtures.DEVELOPER_GROUP_DOMAIN_ID
  321                 }
  322             }
  323         }
  324         for rule in mapped_properties['group_names']:
  325             self.assertDictEqual(reference.get(rule.get('name')), rule)
  326 
  327     def test_rule_engine_whitelist_and_direct_groups_mapping(self):
  328         """Should return user's groups Developer and Contractor.
  329 
  330         The EMPLOYEE_ASSERTION_MULTIPLE_GROUPS should successfully have a match
  331         in MAPPING_GROUPS_WHITELIST. It will test the case where 'whitelist'
  332         correctly filters out Manager and only allows Developer and Contractor.
  333 
  334         """
  335         mapping = mapping_fixtures.MAPPING_GROUPS_WHITELIST
  336         assertion = mapping_fixtures.EMPLOYEE_ASSERTION_MULTIPLE_GROUPS
  337         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  338         mapped_properties = rp.process(assertion)
  339         self.assertIsNotNone(mapped_properties)
  340 
  341         reference = {
  342             mapping_fixtures.DEVELOPER_GROUP_NAME:
  343             {
  344                 "name": mapping_fixtures.DEVELOPER_GROUP_NAME,
  345                 "domain": {
  346                     "id": mapping_fixtures.DEVELOPER_GROUP_DOMAIN_ID
  347                 }
  348             },
  349             mapping_fixtures.CONTRACTOR_GROUP_NAME:
  350             {
  351                 "name": mapping_fixtures.CONTRACTOR_GROUP_NAME,
  352                 "domain": {
  353                     "id": mapping_fixtures.DEVELOPER_GROUP_DOMAIN_ID
  354                 }
  355             }
  356         }
  357         for rule in mapped_properties['group_names']:
  358             self.assertDictEqual(reference.get(rule.get('name')), rule)
  359 
  360         self.assertEqual('tbo', mapped_properties['user']['name'])
  361         self.assertEqual([], mapped_properties['group_ids'])
  362 
  363     def test_rule_engine_blacklist_and_direct_groups_mapping(self):
  364         """Should return user's group Developer.
  365 
  366         The EMPLOYEE_ASSERTION_MULTIPLE_GROUPS should successfully have a match
  367         in MAPPING_GROUPS_BLACKLIST. It will test the case where 'blacklist'
  368         correctly filters out Manager and Developer and only allows Contractor.
  369 
  370         """
  371         mapping = mapping_fixtures.MAPPING_GROUPS_BLACKLIST
  372         assertion = mapping_fixtures.EMPLOYEE_ASSERTION_MULTIPLE_GROUPS
  373         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  374         mapped_properties = rp.process(assertion)
  375         self.assertIsNotNone(mapped_properties)
  376 
  377         reference = {
  378             mapping_fixtures.CONTRACTOR_GROUP_NAME:
  379             {
  380                 "name": mapping_fixtures.CONTRACTOR_GROUP_NAME,
  381                 "domain": {
  382                     "id": mapping_fixtures.DEVELOPER_GROUP_DOMAIN_ID
  383                 }
  384             }
  385         }
  386         for rule in mapped_properties['group_names']:
  387             self.assertDictEqual(reference.get(rule.get('name')), rule)
  388         self.assertEqual('tbo', mapped_properties['user']['name'])
  389         self.assertEqual([], mapped_properties['group_ids'])
  390 
  391     def test_rule_engine_blacklist_and_direct_groups_mapping_multiples(self):
  392         """Test matching multiple values before the blacklist.
  393 
  394         Verifies that the local indexes are correct when matching multiple
  395         remote values for a field when the field occurs before the blacklist
  396         entry in the remote rules.
  397 
  398         """
  399         mapping = mapping_fixtures.MAPPING_GROUPS_BLACKLIST_MULTIPLES
  400         assertion = mapping_fixtures.EMPLOYEE_ASSERTION_MULTIPLE_GROUPS
  401         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  402         mapped_properties = rp.process(assertion)
  403         self.assertIsNotNone(mapped_properties)
  404 
  405         reference = {
  406             mapping_fixtures.CONTRACTOR_GROUP_NAME:
  407             {
  408                 "name": mapping_fixtures.CONTRACTOR_GROUP_NAME,
  409                 "domain": {
  410                     "id": mapping_fixtures.DEVELOPER_GROUP_DOMAIN_ID
  411                 }
  412             }
  413         }
  414         for rule in mapped_properties['group_names']:
  415             self.assertDictEqual(reference.get(rule.get('name')), rule)
  416         self.assertEqual('tbo', mapped_properties['user']['name'])
  417         self.assertEqual([], mapped_properties['group_ids'])
  418 
  419     def test_rule_engine_whitelist_direct_group_mapping_missing_domain(self):
  420         """Test if the local rule is rejected upon missing domain value.
  421 
  422         This is a variation with a ``whitelist`` filter.
  423 
  424         """
  425         mapping = mapping_fixtures.MAPPING_GROUPS_WHITELIST_MISSING_DOMAIN
  426         assertion = mapping_fixtures.EMPLOYEE_ASSERTION_MULTIPLE_GROUPS
  427         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  428         self.assertRaises(exception.ValidationError, rp.process, assertion)
  429 
  430     def test_rule_engine_blacklist_direct_group_mapping_missing_domain(self):
  431         """Test if the local rule is rejected upon missing domain value.
  432 
  433         This is a variation with a ``blacklist`` filter.
  434 
  435         """
  436         mapping = mapping_fixtures.MAPPING_GROUPS_BLACKLIST_MISSING_DOMAIN
  437         assertion = mapping_fixtures.EMPLOYEE_ASSERTION_MULTIPLE_GROUPS
  438         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  439         self.assertRaises(exception.ValidationError, rp.process, assertion)
  440 
  441     def test_rule_engine_no_groups_allowed(self):
  442         """Should return user mapped to no groups.
  443 
  444         The EMPLOYEE_ASSERTION should successfully have a match
  445         in MAPPING_GROUPS_WHITELIST, but 'whitelist' should filter out
  446         the group values from the assertion and thus map to no groups.
  447 
  448         """
  449         mapping = mapping_fixtures.MAPPING_GROUPS_WHITELIST
  450         assertion = mapping_fixtures.EMPLOYEE_ASSERTION
  451         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  452         mapped_properties = rp.process(assertion)
  453         self.assertIsNotNone(mapped_properties)
  454         self.assertListEqual(mapped_properties['group_names'], [])
  455         self.assertListEqual(mapped_properties['group_ids'], [])
  456         self.assertEqual('tbo', mapped_properties['user']['name'])
  457 
  458     def test_mapping_federated_domain_specified(self):
  459         """Test mapping engine when domain 'ephemeral' is explicitly set.
  460 
  461         For that, we use mapping rule MAPPING_EPHEMERAL_USER and assertion
  462         EMPLOYEE_ASSERTION
  463 
  464         """
  465         mapping = mapping_fixtures.MAPPING_EPHEMERAL_USER
  466         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  467         assertion = mapping_fixtures.EMPLOYEE_ASSERTION
  468         mapped_properties = rp.process(assertion)
  469         self.assertIsNotNone(mapped_properties)
  470         self.assertValidMappedUserObject(mapped_properties)
  471 
  472     def test_set_ephemeral_domain_to_ephemeral_users(self):
  473         """Test auto assigning service domain to ephemeral users.
  474 
  475         Test that ephemeral users will always become members of federated
  476         service domain. The check depends on ``type`` value which must be set
  477         to ``ephemeral`` in case of ephemeral user.
  478 
  479         """
  480         mapping = mapping_fixtures.MAPPING_EPHEMERAL_USER_LOCAL_DOMAIN
  481         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  482         assertion = mapping_fixtures.CONTRACTOR_ASSERTION
  483         mapped_properties = rp.process(assertion)
  484         self.assertIsNotNone(mapped_properties)
  485         self.assertValidMappedUserObject(mapped_properties)
  486 
  487     def test_local_user_local_domain(self):
  488         """Test that local users can have non-service domains assigned."""
  489         mapping = mapping_fixtures.MAPPING_LOCAL_USER_LOCAL_DOMAIN
  490         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  491         assertion = mapping_fixtures.CONTRACTOR_ASSERTION
  492         mapped_properties = rp.process(assertion)
  493         self.assertIsNotNone(mapped_properties)
  494         self.assertValidMappedUserObject(
  495             mapped_properties, user_type='local',
  496             domain_id=mapping_fixtures.LOCAL_DOMAIN)
  497 
  498     def test_user_identifications_name(self):
  499         """Test various mapping options and how users are identified.
  500 
  501         This test calls mapped.setup_username() for propagating user object.
  502 
  503         Test plan:
  504         - Check if the user has proper domain ('federated') set
  505         - Check if the user has property type set ('ephemeral')
  506         - Check if user's name is properly mapped from the assertion
  507         - Check if unique_id is properly set and equal to display_name,
  508         as it was not explicitly specified in the mapping.
  509 
  510         """
  511         mapping = mapping_fixtures.MAPPING_USER_IDS
  512         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  513         assertion = mapping_fixtures.CONTRACTOR_ASSERTION
  514         mapped_properties = rp.process(assertion)
  515         self.assertIsNotNone(mapped_properties)
  516         self.assertValidMappedUserObject(mapped_properties)
  517         self.assertEqual('jsmith', mapped_properties['user']['name'])
  518         unique_id, display_name = mapped.get_user_unique_id_and_display_name(
  519             mapped_properties)
  520         self.assertEqual('jsmith', unique_id)
  521         self.assertEqual('jsmith', display_name)
  522 
  523     def test_user_identifications_name_and_federated_domain(self):
  524         """Test various mapping options and how users are identified.
  525 
  526         This test calls mapped.setup_username() for propagating user object.
  527 
  528         Test plan:
  529         - Check if the user has proper domain ('federated') set
  530         - Check if the user has propert type set ('ephemeral')
  531         - Check if user's name is properly mapped from the assertion
  532         - Check if the unique_id and display_name are properly set
  533 
  534         """
  535         mapping = mapping_fixtures.MAPPING_USER_IDS
  536         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  537         assertion = mapping_fixtures.EMPLOYEE_ASSERTION
  538         mapped_properties = rp.process(assertion)
  539         self.assertIsNotNone(mapped_properties)
  540         self.assertValidMappedUserObject(mapped_properties)
  541         unique_id, display_name = mapped.get_user_unique_id_and_display_name(
  542             mapped_properties)
  543         self.assertEqual('tbo', display_name)
  544         self.assertEqual('abc123%40example.com', unique_id)
  545 
  546     def test_user_identification_id(self):
  547         """Test various mapping options and how users are identified.
  548 
  549         This test calls mapped.setup_username() for propagating user object.
  550 
  551         Test plan:
  552         - Check if the user has proper domain ('federated') set
  553         - Check if the user has propert type set ('ephemeral')
  554         - Check if user's display_name is properly set and equal to unique_id,
  555         as it was not explicitly specified in the mapping.
  556 
  557         """
  558         mapping = mapping_fixtures.MAPPING_USER_IDS
  559         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  560         assertion = mapping_fixtures.ADMIN_ASSERTION
  561         mapped_properties = rp.process(assertion)
  562         self.assertIsNotNone(mapped_properties)
  563         self.assertValidMappedUserObject(mapped_properties)
  564         with self.flask_app.test_request_context():
  565             unique_id, display_name = (
  566                 mapped.get_user_unique_id_and_display_name(mapped_properties))
  567         self.assertEqual('bob', unique_id)
  568         self.assertEqual('bob', display_name)
  569 
  570     def test_get_user_unique_id_and_display_name(self):
  571 
  572         mapping = mapping_fixtures.MAPPING_USER_IDS
  573         assertion = mapping_fixtures.ADMIN_ASSERTION
  574         FAKE_MAPPING_ID = uuid.uuid4().hex
  575         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  576         mapped_properties = rp.process(assertion)
  577         self.assertIsNotNone(mapped_properties)
  578         self.assertValidMappedUserObject(mapped_properties)
  579         with self.flask_app.test_request_context(
  580                 environ_base={'REMOTE_USER': 'remote_user'}):
  581             unique_id, display_name = (
  582                 mapped.get_user_unique_id_and_display_name(mapped_properties))
  583         self.assertEqual('bob', unique_id)
  584         self.assertEqual('remote_user', display_name)
  585 
  586     def test_user_identification_id_and_name(self):
  587         """Test various mapping options and how users are identified.
  588 
  589         This test calls mapped.setup_username() for propagating user object.
  590 
  591         Test plan:
  592         - Check if the user has proper domain ('federated') set
  593         - Check if the user has proper type set ('ephemeral')
  594         - Check if display_name is properly set from the assertion
  595         - Check if unique_id is properly set and equal to value hardcoded
  596         in the mapping
  597 
  598         This test does two iterations with different assertions used as input
  599         for the Mapping Engine.  Different assertions will be matched with
  600         different rules in the ruleset, effectively issuing different user_id
  601         (hardcoded values). In the first iteration, the hardcoded user_id is
  602         not url-safe and we expect Keystone to make it url safe. In the latter
  603         iteration, provided user_id is already url-safe and we expect server
  604         not to change it.
  605 
  606         """
  607         testcases = [(mapping_fixtures.CUSTOMER_ASSERTION, 'bwilliams'),
  608                      (mapping_fixtures.EMPLOYEE_ASSERTION, 'tbo')]
  609         for assertion, exp_user_name in testcases:
  610             mapping = mapping_fixtures.MAPPING_USER_IDS
  611             rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  612             mapped_properties = rp.process(assertion)
  613             self.assertIsNotNone(mapped_properties)
  614             self.assertValidMappedUserObject(mapped_properties)
  615             unique_id, display_name = (
  616                 mapped.get_user_unique_id_and_display_name(mapped_properties)
  617             )
  618             self.assertEqual(exp_user_name, display_name)
  619             self.assertEqual('abc123%40example.com', unique_id)
  620 
  621     def test_whitelist_pass_through(self):
  622         mapping = mapping_fixtures.MAPPING_GROUPS_WHITELIST_PASS_THROUGH
  623         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  624         assertion = mapping_fixtures.DEVELOPER_ASSERTION
  625         mapped_properties = rp.process(assertion)
  626         self.assertValidMappedUserObject(mapped_properties)
  627 
  628         self.assertEqual('developacct', mapped_properties['user']['name'])
  629         self.assertEqual('Developer',
  630                          mapped_properties['group_names'][0]['name'])
  631 
  632     def test_mapping_validation_with_incorrect_local_keys(self):
  633         mapping = mapping_fixtures.MAPPING_BAD_LOCAL_SETUP
  634         self.assertRaises(exception.ValidationError,
  635                           mapping_utils.validate_mapping_structure,
  636                           mapping)
  637 
  638     def test_mapping_validation_with_user_name_and_domain_name(self):
  639         mapping = mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME
  640         mapping_utils.validate_mapping_structure(mapping)
  641 
  642     def test_mapping_validation_with_user_name_and_domain_id(self):
  643         mapping = mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINID
  644         mapping_utils.validate_mapping_structure(mapping)
  645 
  646     def test_mapping_validation_with_user_id_and_domain_id(self):
  647         mapping = mapping_fixtures.MAPPING_WITH_USERID_AND_DOMAINID
  648         mapping_utils.validate_mapping_structure(mapping)
  649 
  650     def test_mapping_validation_with_group_name_and_domain(self):
  651         mapping = mapping_fixtures.MAPPING_GROUP_NAMES
  652         mapping_utils.validate_mapping_structure(mapping)
  653 
  654     def test_mapping_validation_bad_domain(self):
  655         mapping = mapping_fixtures.MAPPING_BAD_DOMAIN
  656         self.assertRaises(exception.ValidationError,
  657                           mapping_utils.validate_mapping_structure,
  658                           mapping)
  659 
  660     def test_mapping_validation_bad_group(self):
  661         mapping = mapping_fixtures.MAPPING_BAD_GROUP
  662         self.assertRaises(exception.ValidationError,
  663                           mapping_utils.validate_mapping_structure,
  664                           mapping)
  665 
  666     def test_mapping_validation_with_group_name_without_domain(self):
  667         mapping = mapping_fixtures.MAPPING_GROUP_NAME_WITHOUT_DOMAIN
  668         self.assertRaises(exception.ValidationError,
  669                           mapping_utils.validate_mapping_structure,
  670                           mapping)
  671 
  672     def test_mapping_validation_with_group_id_and_domain(self):
  673         mapping = mapping_fixtures.MAPPING_GROUP_ID_WITH_DOMAIN
  674         self.assertRaises(exception.ValidationError,
  675                           mapping_utils.validate_mapping_structure,
  676                           mapping)
  677 
  678     def test_mapping_validation_with_bad_local_type_user_in_assertion(self):
  679         mapping = mapping_fixtures.MAPPING_BAD_LOCAL_TYPE_USER_IN_ASSERTION
  680         self.assertRaises(exception.ValidationError,
  681                           mapping_utils.validate_mapping_structure,
  682                           mapping)
  683 
  684     def test_mapping_validation_no_local(self):
  685         mapping = mapping_fixtures.MAPPING_MISSING_LOCAL
  686         self.assertRaises(exception.ValidationError,
  687                           mapping_utils.validate_mapping_structure,
  688                           mapping)
  689 
  690     def test_mapping_validataion_no_remote(self):
  691         mapping = mapping_fixtures.MAPPING_NO_REMOTE
  692         self.assertRaises(exception.ValidationError,
  693                           mapping_utils.validate_mapping_structure,
  694                           mapping)
  695 
  696     def test_mapping_validation_no_type(self):
  697         mapping = mapping_fixtures.MAPPING_MISSING_TYPE
  698         self.assertRaises(exception.ValidationError,
  699                           mapping_utils.validate_mapping_structure,
  700                           mapping)
  701 
  702     def test_type_not_in_assertion(self):
  703         """Test that if the remote "type" is not in the assertion it fails."""
  704         mapping = mapping_fixtures.MAPPING_GROUPS_WHITELIST_PASS_THROUGH
  705         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  706         assertion = {uuid.uuid4().hex: uuid.uuid4().hex}
  707         self.assertRaises(exception.ValidationError,
  708                           rp.process,
  709                           assertion)
  710 
  711     def test_rule_engine_groups_mapping_only_one_group(self):
  712         """Test mapping engine when groups is explicitly set.
  713 
  714         If the groups list has only one group,
  715         test if the transformation is done correctly
  716 
  717         """
  718         mapping = mapping_fixtures.MAPPING_GROUPS_WITH_EMAIL
  719         assertion = mapping_fixtures.GROUPS_ASSERTION_ONLY_ONE_GROUP
  720         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  721         mapped_properties = rp.process(assertion)
  722         self.assertIsNotNone(mapped_properties)
  723         self.assertEqual('jsmith', mapped_properties['user']['name'])
  724         self.assertEqual('jill@example.com',
  725                          mapped_properties['user']['email'])
  726         self.assertEqual('ALL USERS',
  727                          mapped_properties['group_names'][0]['name'])
  728 
  729     def test_rule_engine_group_ids_mapping_whitelist(self):
  730         """Test mapping engine when group_ids is explicitly set.
  731 
  732         Also test whitelists on group ids
  733 
  734         """
  735         mapping = mapping_fixtures.MAPPING_GROUPS_IDS_WHITELIST
  736         assertion = mapping_fixtures.GROUP_IDS_ASSERTION
  737         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  738         mapped_properties = rp.process(assertion)
  739         self.assertIsNotNone(mapped_properties)
  740         self.assertEqual('opilotte', mapped_properties['user']['name'])
  741         self.assertListEqual([], mapped_properties['group_names'])
  742         self.assertItemsEqual(['abc123', 'ghi789', 'klm012'],
  743                               mapped_properties['group_ids'])
  744 
  745     def test_rule_engine_group_ids_mapping_blacklist(self):
  746         """Test mapping engine when group_ids is explicitly set.
  747 
  748         Also test blacklists on group ids
  749 
  750         """
  751         mapping = mapping_fixtures.MAPPING_GROUPS_IDS_BLACKLIST
  752         assertion = mapping_fixtures.GROUP_IDS_ASSERTION
  753         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  754         mapped_properties = rp.process(assertion)
  755         self.assertIsNotNone(mapped_properties)
  756         self.assertEqual('opilotte', mapped_properties['user']['name'])
  757         self.assertListEqual([], mapped_properties['group_names'])
  758         self.assertItemsEqual(['abc123', 'ghi789', 'klm012'],
  759                               mapped_properties['group_ids'])
  760 
  761     def test_rule_engine_group_ids_mapping_only_one_group(self):
  762         """Test mapping engine when group_ids is explicitly set.
  763 
  764         If the group ids list has only one group,
  765         test if the transformation is done correctly
  766 
  767         """
  768         mapping = mapping_fixtures.MAPPING_GROUPS_IDS_WHITELIST
  769         assertion = mapping_fixtures.GROUP_IDS_ASSERTION_ONLY_ONE_GROUP
  770         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  771         mapped_properties = rp.process(assertion)
  772         self.assertIsNotNone(mapped_properties)
  773         self.assertEqual('opilotte', mapped_properties['user']['name'])
  774         self.assertListEqual([], mapped_properties['group_names'])
  775         self.assertItemsEqual(['210mlk', '321cba'],
  776                               mapped_properties['group_ids'])
  777 
  778     def test_mapping_projects(self):
  779         mapping = mapping_fixtures.MAPPING_PROJECTS
  780         assertion = mapping_fixtures.EMPLOYEE_ASSERTION
  781         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  782         values = rp.process(assertion)
  783 
  784         self.assertValidMappedUserObject(values)
  785         expected_username = mapping_fixtures.EMPLOYEE_ASSERTION['UserName']
  786         self.assertEqual(expected_username, values['user']['name'])
  787 
  788         expected_projects = [
  789             {
  790                 "name": "Production",
  791                 "roles": [{"name": "observer"}]
  792             },
  793             {
  794                 "name": "Staging",
  795                 "roles": [{"name": "member"}]
  796             },
  797             {
  798                 "name": "Project for %s" % expected_username,
  799                 "roles": [{"name": "admin"}]
  800             }
  801         ]
  802         self.assertEqual(expected_projects, values['projects'])
  803 
  804     def test_rule_engine_for_groups_and_domain(self):
  805         """Should return user's groups and group domain.
  806 
  807         The GROUP_DOMAIN_ASSERTION should successfully have a match in
  808         MAPPING_GROUPS_DOMAIN_OF_USER. This will test the case where a groups
  809         with its domain will exist`, and return user's groups and group domain.
  810 
  811         """
  812         mapping = mapping_fixtures.MAPPING_GROUPS_DOMAIN_OF_USER
  813         assertion = mapping_fixtures.GROUPS_DOMAIN_ASSERTION
  814         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  815         values = rp.process(assertion)
  816 
  817         self.assertValidMappedUserObject(values)
  818         user_name = assertion.get('openstack_user')
  819         user_groups = ['group1', 'group2']  # since we know the input assertion
  820         groups = values.get('group_names', {})
  821         group_list = [g.get('name') for g in groups]
  822         group_ids = values.get('group_ids')
  823         name = values.get('user', {}).get('name')
  824 
  825         self.assertEqual(user_name, name)
  826         self.assertEqual(user_groups, group_list)
  827         self.assertEqual([], group_ids, )
  828 
  829 
  830 class TestUnicodeAssertionData(unit.BaseTestCase):
  831     """Ensure that unicode data in the assertion headers works.
  832 
  833     Bug #1525250 reported that something was not getting correctly encoded
  834     and/or decoded when assertion data contained non-ASCII characters.
  835 
  836     This test class mimics what happens in a real HTTP request.
  837     """
  838 
  839     def setUp(self):
  840         super(TestUnicodeAssertionData, self).setUp()
  841         self.config_fixture = self.useFixture(config_fixture.Config(CONF))
  842         self.config_fixture.config(group='federation',
  843                                    assertion_prefix='PFX')
  844 
  845     def _pull_mapping_rules_from_the_database(self):
  846         # NOTE(dstanek): In a live system. The rules are dumped into JSON bytes
  847         # before being # stored in the database. Upon retrieval the bytes are
  848         # loaded and the resulting dictionary is full of unicode text strings.
  849         # Most of tests in this file incorrectly assume the mapping fixture
  850         # dictionary is the same as what it would look like coming out of the
  851         # database. The string, when coming out of the database, are all text.
  852         return jsonutils.loads(jsonutils.dumps(
  853             mapping_fixtures.MAPPING_UNICODE))
  854 
  855     def _pull_assertion_from_the_request_headers(self):
  856         # NOTE(dstanek): In a live system the bytes for the assertion are
  857         # pulled from the HTTP headers. These bytes may be decodable as
  858         # ISO-8859-1 according to Section 3.2.4 of RFC 7230. Let's assume
  859         # that our web server plugins are correctly encoding the data.
  860         # Create a dummy application
  861         app = flask.Flask(__name__)
  862         with app.test_request_context(
  863                 path='/path',
  864                 environ_overrides=mapping_fixtures.UNICODE_NAME_ASSERTION):
  865             data = mapping_utils.get_assertion_params_from_env()
  866             # NOTE(dstanek): keystone.auth.plugins.mapped
  867             return dict(data)
  868 
  869     def test_unicode(self):
  870         mapping = self._pull_mapping_rules_from_the_database()
  871         assertion = self._pull_assertion_from_the_request_headers()
  872 
  873         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, mapping['rules'])
  874         values = rp.process(assertion)
  875 
  876         fn = assertion.get('PFX_FirstName')
  877         ln = assertion.get('PFX_LastName')
  878         full_name = '%s %s' % (fn, ln)
  879         user_name = values.get('user', {}).get('name')
  880         self.assertEqual(full_name, user_name)
  881 
  882 
  883 class TestMappingLocals(unit.BaseTestCase):
  884     mapping_split = {
  885         'rules': [
  886             {
  887                 'local': [
  888                     {
  889                         'user': {'name': '{0}'},
  890                     },
  891                     {
  892                         'group': {'id': 'd34db33f'}
  893                     }
  894                 ],
  895                 'remote': [
  896                     {'type': 'idp_username'}
  897                 ]
  898             }
  899         ]
  900     }
  901     mapping_combined = {
  902         'rules': [
  903             {
  904                 'local': [
  905                     {
  906                         'user': {'name': '{0}'},
  907                         'group': {'id': 'd34db33f'}
  908                     }
  909                 ],
  910                 'remote': [
  911                     {'type': 'idp_username'}
  912                 ]
  913             }
  914         ]
  915     }
  916     mapping_with_duplicate = {
  917         'rules': [
  918             {
  919                 'local': [
  920                     {'user': {'name': 'test_{0}'}},
  921                     {'user': {'name': '{0}'}}
  922                 ],
  923                 'remote': [{'type': 'idp_username'}]
  924             }
  925         ]
  926     }
  927     assertion = {
  928         'idp_username': 'a_user'
  929     }
  930 
  931     def process(self, rules):
  932         rp = mapping_utils.RuleProcessor(FAKE_MAPPING_ID, rules)
  933         return rp.process(self.assertion)
  934 
  935     def test_local_list_gets_squashed_into_a_single_dictionary(self):
  936         expected = {
  937             'user': {
  938                 'name': 'a_user',
  939                 'type': 'ephemeral'
  940             },
  941             'projects': [],
  942             'group_ids': ['d34db33f'],
  943             'group_names': []
  944         }
  945 
  946         mapped_split = self.process(self.mapping_split['rules'])
  947         mapped_combined = self.process(self.mapping_combined['rules'])
  948 
  949         self.assertEqual(expected, mapped_split)
  950         self.assertEqual(mapped_split, mapped_combined)
  951 
  952     def test_when_local_list_gets_squashed_first_dict_wins(self):
  953         expected = {
  954             'user': {
  955                 'name': 'test_a_user',
  956                 'type': 'ephemeral'
  957             },
  958             'projects': [],
  959             'group_ids': [],
  960             'group_names': []
  961         }
  962 
  963         mapped = self.process(self.mapping_with_duplicate['rules'])
  964         self.assertEqual(expected, mapped)