def parse_federation_registration_response(self, resp, **kwargs): """ Receives a dynamic client registration response, :param resp: An entity statement instance :return: A set of metadata claims """ _sc = self.service_context _fe = _sc.federation_entity # Can not collect trust chain. Have to verify the signed JWT with keys I have kj = self.service_context.federation_entity.keyjar _jwt = factory(resp) entity_statement = _jwt.verify_compact(resp, keys=kj.get_jwt_verify_keys( _jwt.jwt)) _trust_anchor_id = self.get_trust_anchor_id(entity_statement) chosen = None for op_statement in _fe.op_statements: if op_statement.fo == _trust_anchor_id: chosen = op_statement break if not chosen: raise ValueError('No matching federation operator') # based on the Federation ID, conclude which OP config to use op_claims = chosen.metadata # _sc.trust_path = (chosen.fo, _fe.op_paths[statement.fo][0]) _sc.provider_info = self.response_cls(**op_claims) # To create RPs metadata collect the trust chains tree = {} for ah in _fe.authority_hints: tree[ah] = _fe.collector.collect_intermediate(_fe.entity_id, ah) _node = {_fe.entity_id: (resp, tree)} chains = branch2lists(_node) # Get the policies policy_chains_tup = [ eval_policy_chain(c, _fe.keyjar, _fe.entity_type) for c in chains ] _policy = combine_policy( policy_chains_tup[0][1], entity_statement['metadata_policy'][_fe.entity_type]) logger.debug("Combined policy: {}".format(_policy)) _uev = unverified_entity_statement(kwargs["request_body"]) logger.debug("Registration request: {}".format(_uev)) _query = _uev["metadata"][_fe.entity_type] _sc.registration_response = apply_policy(_query, _policy) return _sc.registration_response
def test_discovery(self): stmts = self.create_statements() with responses.RequestsMock() as rsps: _url = "https://op.ntnu.no/.well-known/openid-federation?iss=https%3A%2F%2Fop.ntnu.no" rsps.add("GET", _url, body=stmts['subj_sesi'], status=200) _url = "https://ntnu.no/.well-known/openid-federation" rsps.add("GET", _url, body=stmts['inter_sesi'], status=200) _url = "https://feide.no/.well-known/openid-federation" rsps.add("GET", _url, body=stmts['fedop1_sesi'], status=200) _url = "https://swamid.se/.well-known/openid-federation" rsps.add("GET", _url, body=stmts['fedop2_sesi'], status=200) _url = 'https://ntnu.no/api?iss=https://ntnu.no&sub=https%3A%2F%2Fop.ntnu.no' rsps.add("GET", _url, body=stmts['inter_on_sub'], status=200) _url = 'https://feide.no/api?iss=https%3A%2F%2Ffeide.no&sub=https%3A%2F%2Fntnu.no' rsps.add("GET", _url, body=stmts['fedop1_on_inter'], status=200) _url = "https://swamid.se/api?iss=https%3A%2F%2Fswamid.se&sub=https%3A%2F%2Fntnu.no" rsps.add("GET", _url, body=stmts['fedop2_on_inter'], status=200) auth_req = self.rph1.begin('ntnu') iss = self.rph1.hash2issuer['ntnu'] rp = self.rph1.issuer2rp[iss] assert set(auth_req.keys()) == {'state', 'url'} p = urlparse(auth_req['url']) info = parse_qs(p.query) payload = unverified_entity_statement(info["request"][0]) assert payload['client_id'] == rp.client_get("service_context").federation_entity.entity_id # assert iss in payload['aud'] _federation_dump = rp.client_get("service_context").federation_entity.dump() rp2 = self.rph2.init_client(iss) rp2.client_get("service_context").federation_entity.load(_federation_dump) c = rp2.client_get("service_context").federation_entity.collector assert set(c.config_cache.keys()) == {'https://ntnu.no', 'https://feide.no', 'https://swamid.se'} assert set(c.entity_statement_cache.keys()) == { 'https://feide.no!!https://ntnu.no', 'https://feide.no!exp!https://ntnu.no', 'https://ntnu.no!!https://op.ntnu.no', 'https://ntnu.no!exp!https://op.ntnu.no', 'https://swamid.se!!https://ntnu.no', 'https://swamid.se!exp!https://ntnu.no'}
def test_collect_intermediate(self): _collector = self.endpoint.server_get( "endpoint_context").federation_entity.collector subject = 'https://op.ntnu.no' intermediate = 'https://ntnu.no' fedop1 = 'https://feide.no' fedop2 = 'https://swamid.se' # self-signed from subject es_api = FSEntityStatementAPI(ROOT_DIR, iss=get_netloc(subject)) subj_sesi = es_api.create_entity_statement(get_netloc(subject)) # self-signed from intermediate es_api = FSEntityStatementAPI(ROOT_DIR, iss=get_netloc(intermediate)) inter_sesi = es_api.create_entity_statement(get_netloc(intermediate)) # self-signed from fedop es_api = FSEntityStatementAPI(ROOT_DIR, iss=get_netloc(fedop1)) fedop_sesi_1 = es_api.create_entity_statement(get_netloc(fedop1)) es_api = FSEntityStatementAPI(ROOT_DIR, iss=get_netloc(fedop2)) fedop_sesi_2 = es_api.create_entity_statement(get_netloc(fedop2)) # intermediate on subject es_api = FSEntityStatementAPI(ROOT_DIR, iss=get_netloc(intermediate)) inter_on_sub = es_api.create_entity_statement(get_netloc(subject)) # fedop on intermediate es_api = FSEntityStatementAPI(ROOT_DIR, iss=get_netloc(fedop1)) fedop_on_inter_1 = es_api.create_entity_statement( get_netloc(intermediate)) es_api = FSEntityStatementAPI(ROOT_DIR, iss=get_netloc(fedop2)) fedop_on_inter_2 = es_api.create_entity_statement( get_netloc(intermediate)) sleep(1) with responses.RequestsMock() as rsps: _url = "{}/.well-known/openid-federation".format(intermediate) rsps.add("GET", _url, body=inter_sesi, status=200) _url = "{}/.well-known/openid-federation".format(fedop1) rsps.add("GET", _url, body=fedop_sesi_1, status=200) _url = "{}/.well-known/openid-federation".format(fedop2) rsps.add("GET", _url, body=fedop_sesi_2, status=200) _url = 'https://ntnu.no/api?iss=https%3A%2F%2Fntnu.no&sub=https%3A%2F%2Fop.ntnu.no' rsps.add("GET", _url, body=inter_on_sub, status=200) _url = 'https://feide.no/api?iss=https%3A%2F%2Ffeide.no&sub=https%3A%2F%2Fntnu.no' rsps.add("GET", _url, body=fedop_on_inter_1, status=200) _url = 'https://swamid.se/api?iss=https%3A%2F%2Fswamid.se&sub=https%3A%2F%2Fntnu.no' rsps.add("GET", _url, body=fedop_on_inter_2, status=200) tree = _collector.collect_intermediate(subject, 'https://ntnu.no') assert tree assert len(_collector.config_cache) == 3 assert set(_collector.config_cache.keys()) == { 'https://ntnu.no', 'https://feide.no', 'https://swamid.se' } # The unpacked fedop1's self signed entity statement _info = _collector.config_cache['https://feide.no'] assert _info['sub'] == fedop1 assert _info['iss'] == fedop1 assert _info['metadata']['federation_entity'][ 'federation_api_endpoint'] == 'https://feide.no/api' # For each entity statement there is also the expiration time assert len(_collector.entity_statement_cache) == 6 assert set(_collector.entity_statement_cache.keys()) == { 'https://feide.no!!https://ntnu.no', 'https://feide.no!exp!https://ntnu.no', 'https://ntnu.no!!https://op.ntnu.no', 'https://ntnu.no!exp!https://op.ntnu.no', 'https://swamid.se!!https://ntnu.no', 'https://swamid.se!exp!https://ntnu.no' } # have a look at the payload _info = unverified_entity_statement( _collector. entity_statement_cache['https://swamid.se!!https://ntnu.no']) assert _info['sub'] == intermediate assert _info['iss'] == fedop2 assert _info['authority_hints'] == [fedop2] _collector_dump = _collector.dump() _c2 = Collector() _c2.load(_collector_dump) assert len(_c2.config_cache) == 3 assert set(_c2.config_cache.keys()) == { 'https://ntnu.no', 'https://feide.no', 'https://swamid.se' } # The unpacked fedop1's self signed entity statement _info = _c2.config_cache['https://feide.no'] assert _info['sub'] == fedop1 assert _info['iss'] == fedop1 assert _info['metadata']['federation_entity'][ 'federation_api_endpoint'] == 'https://feide.no/api' # For each entity statement there is also the expiration time assert len(_c2.entity_statement_cache) == 6 assert set(_c2.entity_statement_cache.keys()) == { 'https://feide.no!!https://ntnu.no', 'https://feide.no!exp!https://ntnu.no', 'https://ntnu.no!!https://op.ntnu.no', 'https://ntnu.no!exp!https://op.ntnu.no', 'https://swamid.se!!https://ntnu.no', 'https://swamid.se!exp!https://ntnu.no' } # have a look at the payload _info = unverified_entity_statement( _c2.entity_statement_cache['https://swamid.se!!https://ntnu.no']) assert _info['sub'] == intermediate assert _info['iss'] == fedop2 assert _info['authority_hints'] == [fedop2]
def parse_federation_registration_response(self, resp, **kwargs): """ Receives a dynamic client registration response, :param resp: An entity statement instance :return: A set of metadata claims """ _context = self.client_get("service_context") _fe = _context.federation_entity _fe_ctx = _fe.context # Can not collect trust chain. Have to verify the signed JWT with keys I have kj = _fe_ctx.keyjar _jwt = factory(resp) entity_statement = _jwt.verify_compact(resp, keys=kj.get_jwt_verify_keys(_jwt.jwt)) _trust_anchor_id = self.get_trust_anchor_id(entity_statement) logger.debug("trust_anchor_id: {}".format(_trust_anchor_id)) chosen = None for op_statement in _fe_ctx.op_statements: if op_statement.anchor == _trust_anchor_id: chosen = op_statement break if not chosen: raise ValueError('No matching federation operator') # based on the Federation ID, conclude which OP config to use op_claims = chosen.metadata logger.debug("OP claims: {}".format(op_claims)) # _sc.trust_path = (chosen.anchor, _fe.op_paths[statement.anchor][0]) _context.provider_info = ProviderConfigurationResponse(**op_claims) # To create RPs metadata collect the trust chains tree = {} for ah in _fe_ctx.authority_hints: tree[ah] = _fe.collector.collect_intermediate(_fe_ctx.entity_id, ah) _node = {_fe_ctx.entity_id: (resp, tree)} chains = branch2lists(_node) logger.debug("%d chains", len(chains)) logger.debug("Evaluate policy chains") # Get the policies policy_chains_tup = [eval_policy_chain(c, _fe_ctx.keyjar, _fe_ctx.entity_type) for c in chains] # Weed out unusable chains policy_chains_tup = [pct for pct in policy_chains_tup if pct is not None] # Should leave me with one. The one ending in the chosen trust anchor. policy_chains_tup = [pct for pct in policy_chains_tup if pct[0] == _trust_anchor_id] if policy_chains_tup == []: logger.warning("No chain that ends in chosen trust anchor (%s)", _trust_anchor_id) raise ValueError("No trust chain that ends in chosen trust anchor (%s)", _trust_anchor_id) _policy = combine_policy(policy_chains_tup[0][1], entity_statement['metadata_policy'][_fe_ctx.entity_type]) logger.debug("Effective policy: {}".format(_policy)) _req = kwargs.get("request") if _req is None: _req = kwargs.get("request_body") _uev = unverified_entity_statement(_req) logger.debug("Registration request: {}".format(_uev)) _query = _uev["metadata"][_fe_ctx.entity_type] _resp = apply_policy(_query, _policy) _context.set("registration_response", _resp) return _resp