def test_simple_template_mapping(self): mapping = { "attributes": { "last_name": { "p1": ["sn"], "p2": ["sn"] }, "first_name": { "p1": ["givenName"], "p2": ["givenName"] }, "name": { "p2": ["cn"] } }, "template_attributes": { "name": { "p2": ["${first_name[0]} ${last_name[0]}"] } } } converter = AttributeMapper(mapping) internal_repr = converter.to_internal("p2", {"givenName": ["Valfrid"], "sn": ["Lindeman"]}) assert "name" in internal_repr assert len(internal_repr["name"]) == 1 assert internal_repr["name"][0] == "Valfrid Lindeman" external_repr = converter.from_internal("p2", internal_repr) assert external_repr["cn"][0] == "Valfrid Lindeman"
def insert_user_in_user_db(self, frontend, user_id): user_attributes = AttributeMapper(frontend.internal_attributes).to_internal( "saml", USERS["testuser1"] ) frontend.user_db[user_id] = frontend.converter.from_internal( "openid", user_attributes )
def test_template_attribute_preserves_existing_attribute_if_template_cant_be_rendered(self): mapping = { "attributes": { "last_name": { "p1": ["sn"], }, "first_name": { "p1": ["givenName"], }, "name": { "p1": ["cn"] } }, "template_attributes": { "name": { "p1": ["${unknown[0]} ${last_name[0]}"] } } } converter = AttributeMapper(mapping) data = {"sn": ["Surname"], "givenName": ["Given"], "cn": ["Common Name"]} internal_repr = converter.to_internal("p1", data) assert len(internal_repr["name"]) == 1 assert internal_repr["name"][0] == "Common Name"
def test_template_attribute_overrides_existing_attribute(self): mapping = { "attributes": { "last_name": { "p1": ["sn"], }, "first_name": { "p1": ["givenName"], }, "name": { "p1": ["cn"] } }, "template_attributes": { "name": { "p1": ["${first_name[0]} ${last_name[0]}"] } } } converter = AttributeMapper(mapping) data = {"sn": ["Surname"], "givenName": ["Given"], "cn": ["Common Name"]} internal_repr = converter.to_internal("p1", data) external_repr = converter.from_internal("p1", internal_repr) assert len(internal_repr["name"]) == 1 assert internal_repr["name"][0] == "Given Surname" assert external_repr["cn"][0] == "Given Surname"
def test_template_attribute_with_multiple_templates_tries_them_all_templates(self): mapping = { "attributes": { "last_name": { "p1": ["sn"], }, "first_name": { "p1": ["givenName"], }, "name": { "p1": ["cn"] } }, "template_attributes": { "name": { "p1": ["${first_name[0]} ${last_name[0]}", "${unknown[0]} ${unknown[1]}", "${first_name[1]} ${last_name[1]}", "${foo} ${bar}"] } } } converter = AttributeMapper(mapping) data = {"sn": ["Surname1", "Surname2"], "givenName": ["Given1", "Given2"], "cn": ["Common Name"]} internal_repr = converter.to_internal("p1", data) assert len(internal_repr["name"]) == 2 assert internal_repr["name"][0] == "Given1 Surname1" assert internal_repr["name"][1] == "Given2 Surname2"
def internal_response(self, idp_conf): auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", idp_conf["entityid"]) internal_response = InternalData(auth_info=auth_info) internal_response.attributes = AttributeMapper( INTERNAL_ATTRIBUTES).to_internal("saml", USERS["testuser1"]) return internal_response
def test_respect_sp_entity_categories(self, context, entity_category, entity_category_module, expected_attributes, idp_conf, sp_conf, internal_response): idp_metadata_str = create_metadata_from_config_dict(idp_conf) idp_conf["service"]["idp"]["policy"]["default"]["entity_categories"] = [entity_category_module] if all(entity_category): # don't insert empty entity category sp_conf["entity_category"] = entity_category if entity_category == [COCO]: sp_conf["service"]["sp"]["required_attributes"] = expected_attributes expected_attributes_in_all_entity_categories = list( itertools.chain(swamid.RELEASE[""], edugain.RELEASE[COCO], refeds.RELEASE[RESEARCH_AND_SCHOLARSHIP], swamid.RELEASE[(RESEARCH_AND_EDUCATION, EU)], swamid.RELEASE[(RESEARCH_AND_EDUCATION, HEI)], swamid.RELEASE[(RESEARCH_AND_EDUCATION, NREN)], swamid.RELEASE[SFS_1993_1153])) attribute_mapping = {} for expected_attribute in expected_attributes_in_all_entity_categories: attribute_mapping[expected_attribute.lower()] = {"saml": [expected_attribute]} internal_attributes = dict(attributes=attribute_mapping) samlfrontend = self.setup_for_authn_req(context, idp_conf, sp_conf, internal_attributes=internal_attributes) user_attributes = {k: "foo" for k in expected_attributes_in_all_entity_categories} internal_response.attributes = AttributeMapper(internal_attributes).to_internal("saml", user_attributes) internal_response.requester = sp_conf["entityid"] resp = self.get_auth_response(samlfrontend, context, internal_response, sp_conf, idp_metadata_str) assert Counter(resp.ava.keys()) == Counter(expected_attributes)
def __init__(self, config: Mapping[str, Any], internal_attributes: Dict[str, Any], *args, **kwargs): super().__init__(*args, **kwargs) self.config = Config(**config) # Setup databases self.eduid_userdb = UserDB(db_uri=self.config.mongo_uri, db_name='eduid_scimapi') logger.info(f'Connected to eduid db: {self.eduid_userdb}') # TODO: Implement real 'data owner' to database lookup data_owner = 'eduid.se' _owner = data_owner.replace('.', '_') # replace dots with underscores coll = f'{_owner}__users' # TODO: rename old collection and remove this if data_owner == 'eduid.se': coll = 'profiles' self._userdbs = { 'eduid.se': ScimApiUserDB(db_uri=self.config.mongo_uri, collection=coll) } self.converter = AttributeMapper(internal_attributes) # Get the internal attribute name for the eduPersonPrincipalName that will be # used to find users in the SCIM database _int = self.converter.to_internal( 'saml', {'eduPersonPrincipalName': 'something'}) self.ext_id_attr = list(_int.keys())[0] logger.debug( f'SCIM externalId internal attribute name: {self.ext_id_attr}')
def test_scoped_template_mapping(self): mapping = { "attributes": { "unscoped_affiliation": { "p1": ["eduPersonAffiliation"] }, "uid": { "p1": ["eduPersonPrincipalName"], }, "affiliation": { "p1": ["eduPersonScopedAffiliation"] } }, "template_attributes": { "affiliation": { "p1": ["${unscoped_affiliation[0]}@${uid[0] | scope}"] } } } converter = AttributeMapper(mapping) internal_repr = converter.to_internal("p1", { "eduPersonAffiliation": ["student"], "eduPersonPrincipalName": ["*****@*****.**"]}) assert "affiliation" in internal_repr assert len(internal_repr["affiliation"]) == 1 assert internal_repr["affiliation"][0] == "*****@*****.**"
def setup_for_authn_response(self, context, frontend, auth_req): context.state[frontend.name] = {"oidc_request": auth_req.to_urlencoded()} auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", "unittest_idp.xml") internal_response = InternalData(auth_info=auth_info) internal_response.attributes = AttributeMapper(INTERNAL_ATTRIBUTES).to_internal("saml", USERS["testuser1"]) internal_response.subject_id = USERS["testuser1"]["eduPersonTargetedID"][0] return internal_response
def test_to_internal_with_missing_attribute_value(self): mapping = { "attributes": { "mail": { "p1": ["emailaddress"], }, } } converter = AttributeMapper(mapping) internal_repr = converter.to_internal("p1", {}) assert not internal_repr
def test_to_internal_with_unknown_attribute_profile(self): mapping = { "attributes": { "mail": { "foo": ["email"], }, } } converter = AttributeMapper(mapping) internal_repr = converter.to_internal("bar", {"email": ["*****@*****.**"]}) assert internal_repr == {}
def test_to_internal_same_attribute_value_from_list_and_single_value(self, attribute_value): mapping = { "attributes": { "mail": { "foo": ["email"], }, }, } converter = AttributeMapper(mapping) internal_repr = converter.to_internal("foo", attribute_value) assert internal_repr["mail"] == ["*****@*****.**"]
def test_from_internal_with_unknown_profile(self): mapping = { "attributes": { "mail": { "foo": ["email"], }, }, } converter = AttributeMapper(mapping) external_repr = converter.from_internal("bar", {"mail": "bob"}) assert external_repr == {}
def test_to_internal_filter_with_unknown_profile(self): mapping = { "attributes": { "mail": { "foo": ["email"], } } } converter = AttributeMapper(mapping) filter = converter.to_internal_filter("bar", ["email"]) assert filter == []
def test_to_internal_filter(self): mapping = { "attributes": { "mail": { "p1": ["email"], }, "identifier": { "p1": ["uid"], }, }, } converter = AttributeMapper(mapping) filter = converter.to_internal_filter("p1", ["uid", "email"]) assert Counter(filter) == Counter(["mail", "identifier"])
def test_map_one_source_attribute_to_multiple_internal_attributes(self): mapping = { "attributes": { "mail": { "p1": ["email"], }, "identifier": { "p1": ["email"], }, }, } converter = AttributeMapper(mapping) internal_repr = converter.to_internal("p1", {"email": ["*****@*****.**"]}) assert internal_repr == {"mail": ["*****@*****.**"], "identifier": ["*****@*****.**"]}
def test_to_internal_filter_profile_missing_attribute_mapping(self): mapping = { "attributes": { "mail": { "foo": ["email"], }, "id": { "foo": ["id"], "bar": ["uid"], } }, } converter = AttributeMapper(mapping) filter = converter.to_internal_filter("bar", ["email", "uid"]) assert filter == ["id"] # mail should not included since its missing in 'bar' profile
def test_nested_attribute_to_internal(self): mapping = { "attributes": { "address": { "openid": ["address.formatted"], }, }, } data = { "address": { "formatted": ["100 Universal City Plaza, Hollywood CA 91608, USA"] } } internal_repr = AttributeMapper(mapping).to_internal("openid", data) assert internal_repr["address"] == data["address"]["formatted"]
def test_to_internal_profile_missing_attribute_mapping(self): mapping = { "attributes": { "mail": { "foo": ["email"], }, "id": { "foo": ["id"], "bar": ["uid"], } }, } converter = AttributeMapper(mapping) internal_repr = converter.to_internal("bar", {"email": ["*****@*****.**"], "uid": ["uid"]}) assert "mail" not in internal_repr # no mapping for the 'mail' attribute in the 'bar' profile assert internal_repr["id"] == ["uid"]
def test_mapping_to_nested_attribute(self): mapping = { "attributes": { "address": { "openid": ["address.formatted"], "saml": ["postaladdress"] }, }, } data = { "postaladdress": ["100 Universal City Plaza, Hollywood CA 91608, USA"] } converter = AttributeMapper(mapping) internal_repr = converter.to_internal("saml", data) external_repr = converter.from_internal("openid", internal_repr) assert external_repr["address"]["formatted"] == data["postaladdress"]
def test_custom_attribute_release_with_less_attributes_than_entity_category(self, context, idp_conf, sp_conf, internal_response): idp_metadata_str = create_metadata_from_config_dict(idp_conf) idp_conf["service"]["idp"]["policy"]["default"]["entity_categories"] = ["swamid"] sp_conf["entity_category"] = [SFS_1993_1153] expected_attributes = swamid.RELEASE[SFS_1993_1153] attribute_mapping = {} for expected_attribute in expected_attributes: attribute_mapping[expected_attribute.lower()] = {"saml": [expected_attribute]} internal_attributes = dict(attributes=attribute_mapping) user_attributes = {k: "foo" for k in expected_attributes} internal_response.attributes = AttributeMapper(internal_attributes).to_internal("saml", user_attributes) custom_attributes = {idp_conf["entityid"]: {sp_conf["entityid"]: {"exclude": ["norEduPersonNIN"]}}} samlfrontend = self.setup_for_authn_req(context, idp_conf, sp_conf, internal_attributes=internal_attributes, extra_config=dict(custom_attribute_release=custom_attributes)) resp = self.get_auth_response(samlfrontend, context, internal_response, sp_conf, idp_metadata_str) assert len(resp.ava.keys()) == 0
def test_multiple_source_attribute_values(self): mapping = { "attributes": { "mail": { "saml": ["mail", "emailAddress", "email"] }, }, } data = { "mail": ["*****@*****.**"], "email": ["*****@*****.**"], "emailAddress": ["*****@*****.**"], } expected = Counter(["*****@*****.**", "*****@*****.**", "*****@*****.**"]) converter = AttributeMapper(mapping) internal_repr = converter.to_internal("saml", data) assert Counter(internal_repr["mail"]) == expected external_repr = converter.from_internal("saml", internal_repr) assert Counter(external_repr[mapping["attributes"]["mail"]["saml"][0]]) == expected
def test_template_attribute_fail_does_not_insert_None_attribute_value(self): mapping = { "attributes": { "last_name": { "p1": ["sn"], }, "first_name": { "p1": ["givenName"], }, "name": { "p1": ["cn"] } }, "template_attributes": { "name": { "p1": ["${first_name[0]} ${last_name[0]}"] } } } converter = AttributeMapper(mapping) internal_repr = converter.to_internal("p1", {}) assert len(internal_repr) == 0
def __init__(self, config, internal_attributes, *args, **kwargs): super().__init__(*args, **kwargs) self.config = config self.converter = AttributeMapper(internal_attributes)