Adds rule processing for mapping
Adds a function to compare mapping reference rules to incoming assertion headers, returns a set of identity values. bp mapping-distributed-admin Change-Id: If11539fb66dfeb778755e4f5d2ba8efb2fab522c
This commit is contained in:
parent
8557e4756e
commit
76dc98d385
|
@ -15,9 +15,16 @@
|
||||||
|
|
||||||
"""Utilities for Federation Extension."""
|
"""Utilities for Federation Extension."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
import jsonschema
|
import jsonschema
|
||||||
|
import six
|
||||||
|
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
|
from keystone.openstack.common import log
|
||||||
|
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
MAPPING_SCHEMA = {
|
MAPPING_SCHEMA = {
|
||||||
|
@ -107,3 +114,272 @@ def validate_mapping_structure(ref):
|
||||||
|
|
||||||
if messages:
|
if messages:
|
||||||
raise exception.ValidationError(messages)
|
raise exception.ValidationError(messages)
|
||||||
|
|
||||||
|
|
||||||
|
class RuleProcessor(object):
|
||||||
|
"""A class to process assertions and mapping rules."""
|
||||||
|
|
||||||
|
class _EvalType(object):
|
||||||
|
"""Mapping rule evaluation types."""
|
||||||
|
ANY_ONE_OF = 'any_one_of'
|
||||||
|
NOT_ANY_OF = 'not_any_of'
|
||||||
|
|
||||||
|
def __init__(self, rules):
|
||||||
|
"""Initialize RuleProcessor.
|
||||||
|
|
||||||
|
Example rules can be found at:
|
||||||
|
:class:`keystone.tests.mapping_fixtures`
|
||||||
|
|
||||||
|
:param rules: rules from a mapping
|
||||||
|
:type rules: dict
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.rules = rules
|
||||||
|
|
||||||
|
def process(self, assertion_data):
|
||||||
|
"""Transform assertion to a dictionary of user name and group ids
|
||||||
|
based on mapping rules.
|
||||||
|
|
||||||
|
This function will iterate through the mapping rules to find
|
||||||
|
assertions that are valid.
|
||||||
|
|
||||||
|
:param assertion_data: an assertion containing values from an IdP
|
||||||
|
:type assertion_data: dict
|
||||||
|
|
||||||
|
Example assertion_data::
|
||||||
|
|
||||||
|
{
|
||||||
|
'Email': 'testacct@example.com',
|
||||||
|
'UserName': 'testacct',
|
||||||
|
'FirstName': 'Test',
|
||||||
|
'LastName': 'Account',
|
||||||
|
'orgPersonType': 'Tester'
|
||||||
|
}
|
||||||
|
|
||||||
|
:returns: dictionary with user and group_ids
|
||||||
|
|
||||||
|
The expected return structure is::
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'foobar',
|
||||||
|
'group_ids': ['abc123', 'def456']
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Assertions will come in as string key-value pairs, and will use a
|
||||||
|
# semi-colon to indicate multiple values, i.e. groups.
|
||||||
|
# This will create a new dictionary where the values are arrays, and
|
||||||
|
# any multiple values are stored in the arrays.
|
||||||
|
assertion = dict((n, v.split(';')) for n, v in assertion_data.items())
|
||||||
|
identity_values = []
|
||||||
|
|
||||||
|
for rule in self.rules:
|
||||||
|
direct_maps = self._verify_all_requirements(rule['remote'],
|
||||||
|
assertion)
|
||||||
|
|
||||||
|
# If the compare comes back as None, then the rule did not apply
|
||||||
|
# to the assertion data, go on to the next rule
|
||||||
|
if direct_maps is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If there are no direct mappings, then add the local mapping
|
||||||
|
# directly to the array of saved values. However, if there is
|
||||||
|
# a direct mapping, then perform variable replacement.
|
||||||
|
if not direct_maps:
|
||||||
|
identity_values += rule['local']
|
||||||
|
else:
|
||||||
|
for local in rule['local']:
|
||||||
|
new_local = self._update_local_mapping(local, direct_maps)
|
||||||
|
identity_values.append(new_local)
|
||||||
|
|
||||||
|
return self._transform(identity_values)
|
||||||
|
|
||||||
|
def _transform(self, identity_values):
|
||||||
|
"""Transform local mappings, to an easier to understand format.
|
||||||
|
|
||||||
|
Transform the incoming array to generate the return value for
|
||||||
|
the process function. Generating content for Keystone tokens will
|
||||||
|
be easier if some pre-processing is done at this level.
|
||||||
|
|
||||||
|
:param identity_values: local mapping from valid evaluations
|
||||||
|
:type identity_values: array of dict
|
||||||
|
|
||||||
|
Example identity_values::
|
||||||
|
|
||||||
|
[{'group': {'id': '0cd5e9'}, 'user': {'email': 'bob@example.com'}}]
|
||||||
|
|
||||||
|
:returns: dictionary with user name and group_ids.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# initialize the group_ids as a set to eliminate duplicates
|
||||||
|
user_name = None
|
||||||
|
group_ids = set()
|
||||||
|
|
||||||
|
for identity_value in identity_values:
|
||||||
|
if 'user' in identity_value:
|
||||||
|
# if a mapping outputs more than one user name, log it
|
||||||
|
if user_name is not None:
|
||||||
|
LOG.warning(_('Ignoring user name %s'),
|
||||||
|
identity_value['user']['name'])
|
||||||
|
else:
|
||||||
|
user_name = identity_value['user']['name']
|
||||||
|
if 'group' in identity_value:
|
||||||
|
group_ids.add(identity_value['group']['id'])
|
||||||
|
|
||||||
|
return {'name': user_name, 'group_ids': list(group_ids)}
|
||||||
|
|
||||||
|
def _update_local_mapping(self, local, direct_maps):
|
||||||
|
"""Replace any {0}, {1} ... values with data from the assertion.
|
||||||
|
|
||||||
|
:param local: local mapping reference that needs to be updated
|
||||||
|
:type local: dict
|
||||||
|
:param direct_maps: list of identity values, used to update local
|
||||||
|
:type direct_maps: list
|
||||||
|
|
||||||
|
Example local::
|
||||||
|
|
||||||
|
{'user': {'name': '{0} {1}', 'email': '{2}'}}
|
||||||
|
|
||||||
|
Example direct_maps::
|
||||||
|
|
||||||
|
['Bob', 'Thompson', 'bob@example.com']
|
||||||
|
|
||||||
|
:returns: new local mapping reference with replaced values.
|
||||||
|
|
||||||
|
The expected return structure is::
|
||||||
|
|
||||||
|
{'user': {'name': 'Bob Thompson', 'email': 'bob@example.org'}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
new = {}
|
||||||
|
for k, v in six.iteritems(local):
|
||||||
|
if isinstance(v, dict):
|
||||||
|
new_value = self._update_local_mapping(v, direct_maps)
|
||||||
|
else:
|
||||||
|
new_value = v.format(*direct_maps)
|
||||||
|
new[k] = new_value
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _verify_all_requirements(self, requirements, assertion):
|
||||||
|
"""Go through the remote requirements of a rule, and compare against
|
||||||
|
the assertion.
|
||||||
|
|
||||||
|
If a value of ``None`` is returned, the rule with this assertion
|
||||||
|
doesn't apply.
|
||||||
|
If an array of zero length is returned, then there are no direct
|
||||||
|
mappings to be performed, but the rule is valid.
|
||||||
|
Otherwise, then it will return the values, in order, to be directly
|
||||||
|
mapped, again, the rule is valid.
|
||||||
|
|
||||||
|
:param requirements: list of remote requirements from rules
|
||||||
|
:type requirements: list
|
||||||
|
|
||||||
|
Example requirements::
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "UserName"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "orgPersonType",
|
||||||
|
"any_one_of": [
|
||||||
|
"Customer"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
:param assertion: dict of attributes from an IdP
|
||||||
|
:type assertion: dict
|
||||||
|
|
||||||
|
Example assertion::
|
||||||
|
|
||||||
|
{
|
||||||
|
'UserName': ['testacct'],
|
||||||
|
'LastName': ['Account'],
|
||||||
|
'orgPersonType': ['Tester'],
|
||||||
|
'Email': ['testacct@example.com'],
|
||||||
|
'FirstName': ['Test']
|
||||||
|
}
|
||||||
|
|
||||||
|
:returns: list of direct mappings or None.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
direct_maps = []
|
||||||
|
|
||||||
|
for requirement in requirements:
|
||||||
|
requirement_type = requirement['type']
|
||||||
|
regex = requirement.get('regex', False)
|
||||||
|
|
||||||
|
any_one_values = requirement.get(self._EvalType.ANY_ONE_OF)
|
||||||
|
if any_one_values is not None:
|
||||||
|
if self._evaluate_requirement(any_one_values,
|
||||||
|
requirement_type,
|
||||||
|
self._EvalType.ANY_ONE_OF,
|
||||||
|
regex,
|
||||||
|
assertion):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
not_any_values = requirement.get(self._EvalType.NOT_ANY_OF)
|
||||||
|
if not_any_values is not None:
|
||||||
|
if self._evaluate_requirement(not_any_values,
|
||||||
|
requirement_type,
|
||||||
|
self._EvalType.NOT_ANY_OF,
|
||||||
|
regex,
|
||||||
|
assertion):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# If 'any_one_of' or 'not_any_of' are not found, then values are
|
||||||
|
# within 'type'. Attempt to find that 'type' within the assertion.
|
||||||
|
direct_map_values = assertion.get(requirement_type)
|
||||||
|
if direct_map_values:
|
||||||
|
direct_maps += direct_map_values
|
||||||
|
|
||||||
|
return direct_maps
|
||||||
|
|
||||||
|
def _evaluate_requirement(self, values, requirement_type,
|
||||||
|
eval_type, regex, assertion):
|
||||||
|
"""Evaluate the incoming requirement and assertion.
|
||||||
|
|
||||||
|
If the requirement type does not exist in the assertion data, then
|
||||||
|
return False. If regex is specified, then compare the values and
|
||||||
|
assertion values. Otherwise, grab the intersection of the values
|
||||||
|
and use that to compare against the evaluation type.
|
||||||
|
|
||||||
|
:param values: list of allowed values, defined in the requirement
|
||||||
|
:type values: list
|
||||||
|
:param requirement_type: key to look for in the assertion
|
||||||
|
:type requirement_type: string
|
||||||
|
:param eval_type: determine how to evaluate requirements
|
||||||
|
:type eval_type: string
|
||||||
|
:param regex: perform evaluation with regex
|
||||||
|
:type regex: boolean
|
||||||
|
:param assertion: dict of attributes from the IdP
|
||||||
|
:type assertion: dict
|
||||||
|
|
||||||
|
:returns: boolean, whether requirement is valid or not.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
assertion_values = assertion.get(requirement_type)
|
||||||
|
if not assertion_values:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if regex:
|
||||||
|
return re.search(values[0], assertion_values[0])
|
||||||
|
|
||||||
|
any_match = bool(set(values).intersection(set(assertion_values)))
|
||||||
|
if any_match and eval_type == self._EvalType.ANY_ONE_OF:
|
||||||
|
return True
|
||||||
|
if not any_match and eval_type == self._EvalType.NOT_ANY_OF:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
|
@ -13,13 +13,23 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
"""Fixtures for Federation Mapping."""
|
||||||
|
|
||||||
|
EMPLOYEE_GROUP_ID = "0cd5e9"
|
||||||
|
CONTRACTOR_GROUP_ID = "85a868"
|
||||||
|
TESTER_GROUP_ID = "123"
|
||||||
|
DEVELOPER_GROUP_ID = "xyz"
|
||||||
|
|
||||||
|
# Mapping summary:
|
||||||
|
# LastName Smith & Not Contractor or SubContractor -> group 0cd5e9
|
||||||
|
# FirstName Jill & Contractor or SubContractor -> to group 85a868
|
||||||
MAPPING_SMALL = {
|
MAPPING_SMALL = {
|
||||||
"rules": [
|
"rules": [
|
||||||
{
|
{
|
||||||
"local": [
|
"local": [
|
||||||
{
|
{
|
||||||
"group": {
|
"group": {
|
||||||
"id": "0cd5e9"
|
"id": EMPLOYEE_GROUP_ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -30,6 +40,12 @@ MAPPING_SMALL = {
|
||||||
"Contractor",
|
"Contractor",
|
||||||
"SubContractor"
|
"SubContractor"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "LastName",
|
||||||
|
"any_one_of": [
|
||||||
|
"Bo"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -37,7 +53,7 @@ MAPPING_SMALL = {
|
||||||
"local": [
|
"local": [
|
||||||
{
|
{
|
||||||
"group": {
|
"group": {
|
||||||
"id": "85a868"
|
"id": CONTRACTOR_GROUP_ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -48,20 +64,33 @@ MAPPING_SMALL = {
|
||||||
"Contractor",
|
"Contractor",
|
||||||
"SubContractor"
|
"SubContractor"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "FirstName",
|
||||||
|
"any_one_of": [
|
||||||
|
"Jill"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Mapping summary:
|
||||||
|
# orgPersonType Admin or Big Cheese -> name {0} {1} email {2} and group 0cd5e9
|
||||||
|
# orgPersonType Customer -> user name {0} email {1}
|
||||||
|
# orgPersonType Test and email ^@example.com$ -> group 123 and xyz
|
||||||
MAPPING_LARGE = {
|
MAPPING_LARGE = {
|
||||||
"rules": [
|
"rules": [
|
||||||
{
|
{
|
||||||
"local": [
|
"local": [
|
||||||
{
|
{
|
||||||
"user": {
|
"user": {
|
||||||
"name": "$0 $1",
|
"name": "{0} {1}",
|
||||||
"email": "$2"
|
"email": "{2}"
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"id": EMPLOYEE_GROUP_ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -76,10 +105,10 @@ MAPPING_LARGE = {
|
||||||
"type": "Email"
|
"type": "Email"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Group",
|
"type": "orgPersonType",
|
||||||
"any_one_of": [
|
"any_one_of": [
|
||||||
"Admin",
|
"Admin",
|
||||||
"God"
|
"Big Cheese"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -88,8 +117,8 @@ MAPPING_LARGE = {
|
||||||
"local": [
|
"local": [
|
||||||
{
|
{
|
||||||
"user": {
|
"user": {
|
||||||
"name": "$0",
|
"name": "{0}",
|
||||||
"email": "$1"
|
"email": "{1}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -101,9 +130,12 @@ MAPPING_LARGE = {
|
||||||
"type": "Email"
|
"type": "Email"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Group",
|
"type": "orgPersonType",
|
||||||
"any_one_of": [
|
"not_any_of": [
|
||||||
"Customer"
|
"Admin",
|
||||||
|
"Employee",
|
||||||
|
"Contractor",
|
||||||
|
"Tester"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -112,26 +144,26 @@ MAPPING_LARGE = {
|
||||||
"local": [
|
"local": [
|
||||||
{
|
{
|
||||||
"group": {
|
"group": {
|
||||||
"id": "123"
|
"id": TESTER_GROUP_ID
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": {
|
"group": {
|
||||||
"id": "xyz"
|
"id": DEVELOPER_GROUP_ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"remote": [
|
"remote": [
|
||||||
{
|
{
|
||||||
"type": "Group",
|
"type": "orgPersonType",
|
||||||
"any_one_of": [
|
"any_one_of": [
|
||||||
"Special"
|
"Tester"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Email",
|
"type": "Email",
|
||||||
"any_one_of": [
|
"any_one_of": [
|
||||||
"^@example.com$"
|
".*@example.com$"
|
||||||
],
|
],
|
||||||
"regex": True
|
"regex": True
|
||||||
}
|
}
|
||||||
|
@ -214,7 +246,7 @@ MAPPING_WRONG_TYPE = {
|
||||||
{
|
{
|
||||||
"local": [
|
"local": [
|
||||||
{
|
{
|
||||||
"user": "$1"
|
"user": "{1}"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"remote": [
|
"remote": [
|
||||||
|
@ -231,7 +263,7 @@ MAPPING_MISSING_TYPE = {
|
||||||
{
|
{
|
||||||
"local": [
|
"local": [
|
||||||
{
|
{
|
||||||
"user": "$1"
|
"user": "{1}"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"remote": [
|
"remote": [
|
||||||
|
@ -331,3 +363,51 @@ MAPPING_EXTRA_RULES_PROPS = {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EMPLOYEE_ASSERTION = {
|
||||||
|
'Email': 'tim@example.com',
|
||||||
|
'UserName': 'tbo',
|
||||||
|
'FirstName': 'Tim',
|
||||||
|
'LastName': 'Bo',
|
||||||
|
'orgPersonType': 'Employee;BuildingX;'
|
||||||
|
}
|
||||||
|
|
||||||
|
CONTRACTOR_ASSERTION = {
|
||||||
|
'Email': 'jill@example.com',
|
||||||
|
'UserName': 'jsmith',
|
||||||
|
'FirstName': 'Jill',
|
||||||
|
'LastName': 'Smith',
|
||||||
|
'orgPersonType': 'Contractor;Non-Dev;'
|
||||||
|
}
|
||||||
|
|
||||||
|
ADMIN_ASSERTION = {
|
||||||
|
'Email': 'bob@example.com',
|
||||||
|
'UserName': 'bob',
|
||||||
|
'FirstName': 'Bob',
|
||||||
|
'LastName': 'Thompson',
|
||||||
|
'orgPersonType': 'Admin;Chief;'
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOMER_ASSERTION = {
|
||||||
|
'Email': 'beth@example.com',
|
||||||
|
'UserName': 'bwilliams',
|
||||||
|
'FirstName': 'Beth',
|
||||||
|
'LastName': 'Williams',
|
||||||
|
'orgPersonType': 'Customer;'
|
||||||
|
}
|
||||||
|
|
||||||
|
TESTER_ASSERTION = {
|
||||||
|
'Email': 'testacct@example.com',
|
||||||
|
'UserName': 'testacct',
|
||||||
|
'FirstName': 'Test',
|
||||||
|
'LastName': 'Account',
|
||||||
|
'orgPersonType': 'Tester;'
|
||||||
|
}
|
||||||
|
|
||||||
|
BAD_TESTER_ASSERTION = {
|
||||||
|
'Email': 'eviltester@example.org',
|
||||||
|
'UserName': 'Evil',
|
||||||
|
'FirstName': 'Test',
|
||||||
|
'LastName': 'Account',
|
||||||
|
'orgPersonType': 'Tester;'
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import uuid
|
||||||
from keystone.common.sql import migration
|
from keystone.common.sql import migration
|
||||||
from keystone import config
|
from keystone import config
|
||||||
from keystone import contrib
|
from keystone import contrib
|
||||||
|
from keystone.contrib.federation import utils as mapping_utils
|
||||||
from keystone.openstack.common import importutils
|
from keystone.openstack.common import importutils
|
||||||
from keystone.openstack.common import jsonutils
|
from keystone.openstack.common import jsonutils
|
||||||
from keystone.openstack.common import log
|
from keystone.openstack.common import log
|
||||||
|
@ -584,3 +585,135 @@ class MappingCRUDTests(FederationTests):
|
||||||
url = self.MAPPING_URL + uuid.uuid4().hex
|
url = self.MAPPING_URL + uuid.uuid4().hex
|
||||||
self.put(url, expected_status=400,
|
self.put(url, expected_status=400,
|
||||||
body={'mapping': mapping_fixtures.MAPPING_EXTRA_RULES_PROPS})
|
body={'mapping': mapping_fixtures.MAPPING_EXTRA_RULES_PROPS})
|
||||||
|
|
||||||
|
|
||||||
|
class MappingRuleEngineTests(FederationTests):
|
||||||
|
"""A class for testing the mapping rule engine."""
|
||||||
|
|
||||||
|
def test_rule_engine_any_one_of_and_direct_mapping(self):
|
||||||
|
"""Should return user's name and group id EMPLOYEE_GROUP_ID.
|
||||||
|
|
||||||
|
The ADMIN_ASSERTION should successfully have a match in MAPPING_LARGE.
|
||||||
|
The will test the case where `any_one_of` is valid, and there is
|
||||||
|
a direct mapping for the users name.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
mapping = mapping_fixtures.MAPPING_LARGE
|
||||||
|
assertion = mapping_fixtures.ADMIN_ASSERTION
|
||||||
|
rp = mapping_utils.RuleProcessor(mapping['rules'])
|
||||||
|
values = rp.process(assertion)
|
||||||
|
|
||||||
|
fn = mapping_fixtures.ADMIN_ASSERTION.get('FirstName')
|
||||||
|
ln = mapping_fixtures.ADMIN_ASSERTION.get('LastName')
|
||||||
|
full_name = '%s %s' % (fn, ln)
|
||||||
|
|
||||||
|
group_ids = values.get('group_ids')
|
||||||
|
name = values.get('name')
|
||||||
|
|
||||||
|
self.assertIn(mapping_fixtures.EMPLOYEE_GROUP_ID, group_ids)
|
||||||
|
self.assertEqual(name, full_name)
|
||||||
|
|
||||||
|
def test_rule_engine_no_regex_match(self):
|
||||||
|
"""Should return no values, the email of the tester won't match.
|
||||||
|
|
||||||
|
This will not match since the email in the assertion will fail
|
||||||
|
the regex test. It is set to match any @example.com address.
|
||||||
|
But the incoming value is set to eviltester@example.org.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
mapping = mapping_fixtures.MAPPING_LARGE
|
||||||
|
assertion = mapping_fixtures.BAD_TESTER_ASSERTION
|
||||||
|
rp = mapping_utils.RuleProcessor(mapping['rules'])
|
||||||
|
values = rp.process(assertion)
|
||||||
|
|
||||||
|
group_ids = values.get('group_ids')
|
||||||
|
name = values.get('name')
|
||||||
|
|
||||||
|
self.assertIsNone(name)
|
||||||
|
self.assertEqual(group_ids, [])
|
||||||
|
|
||||||
|
def test_rule_engine_any_one_of_many_rules(self):
|
||||||
|
"""Should return group CONTRACTOR_GROUP_ID.
|
||||||
|
|
||||||
|
The CONTRACTOR_ASSERTION should successfully have a match in
|
||||||
|
MAPPING_SMALL. This will test the case where many rules
|
||||||
|
must be matched, including an `any_one_of`, and a direct
|
||||||
|
mapping.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
mapping = mapping_fixtures.MAPPING_SMALL
|
||||||
|
assertion = mapping_fixtures.CONTRACTOR_ASSERTION
|
||||||
|
rp = mapping_utils.RuleProcessor(mapping['rules'])
|
||||||
|
values = rp.process(assertion)
|
||||||
|
|
||||||
|
group_ids = values.get('group_ids')
|
||||||
|
name = values.get('name')
|
||||||
|
|
||||||
|
self.assertIsNone(name)
|
||||||
|
self.assertIn(mapping_fixtures.CONTRACTOR_GROUP_ID, group_ids)
|
||||||
|
|
||||||
|
def test_rule_engine_not_any_of_and_direct_mapping(self):
|
||||||
|
"""Should return user's name and email.
|
||||||
|
|
||||||
|
The CUSTOMER_ASSERTION should successfully have a match in
|
||||||
|
MAPPING_LARGE. This will test the case where a requirement
|
||||||
|
has `not_any_of`, and direct mapping to a username, no group.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
mapping = mapping_fixtures.MAPPING_LARGE
|
||||||
|
assertion = mapping_fixtures.CUSTOMER_ASSERTION
|
||||||
|
rp = mapping_utils.RuleProcessor(mapping['rules'])
|
||||||
|
values = rp.process(assertion)
|
||||||
|
|
||||||
|
user_name = mapping_fixtures.CUSTOMER_ASSERTION.get('UserName')
|
||||||
|
group_ids = values.get('group_ids')
|
||||||
|
name = values.get('name')
|
||||||
|
|
||||||
|
self.assertEqual(name, user_name)
|
||||||
|
self.assertEqual(group_ids, [])
|
||||||
|
|
||||||
|
def test_rule_engine_not_any_of_many_rules(self):
|
||||||
|
"""Should return group EMPLOYEE_GROUP_ID.
|
||||||
|
|
||||||
|
The EMPLOYEE_ASSERTION should successfully have a match in
|
||||||
|
MAPPING_SMALL. This will test the case where many remote
|
||||||
|
rules must be matched, including a `not_any_of`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
mapping = mapping_fixtures.MAPPING_SMALL
|
||||||
|
assertion = mapping_fixtures.EMPLOYEE_ASSERTION
|
||||||
|
rp = mapping_utils.RuleProcessor(mapping['rules'])
|
||||||
|
values = rp.process(assertion)
|
||||||
|
|
||||||
|
group_ids = values.get('group_ids')
|
||||||
|
name = values.get('name')
|
||||||
|
|
||||||
|
self.assertIsNone(name)
|
||||||
|
self.assertIn(mapping_fixtures.EMPLOYEE_GROUP_ID, group_ids)
|
||||||
|
|
||||||
|
def test_rule_engine_regex_match_and_many_groups(self):
|
||||||
|
"""Should return group DEVELOPER_GROUP_ID and TESTER_GROUP_ID.
|
||||||
|
|
||||||
|
The TESTER_ASSERTION should successfully have a match in
|
||||||
|
MAPPING_LARGE. This will test a successful regex match
|
||||||
|
for an `any_one_of` evaluation type, and will have many
|
||||||
|
groups returned.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
mapping = mapping_fixtures.MAPPING_LARGE
|
||||||
|
assertion = mapping_fixtures.TESTER_ASSERTION
|
||||||
|
rp = mapping_utils.RuleProcessor(mapping['rules'])
|
||||||
|
values = rp.process(assertion)
|
||||||
|
|
||||||
|
group_ids = values.get('group_ids')
|
||||||
|
name = values.get('name')
|
||||||
|
|
||||||
|
self.assertIsNone(name)
|
||||||
|
self.assertIn(mapping_fixtures.DEVELOPER_GROUP_ID, group_ids)
|
||||||
|
self.assertIn(mapping_fixtures.TESTER_GROUP_ID, group_ids)
|
||||||
|
|
Loading…
Reference in New Issue