def test_multiple_fo_one_working(): cms_org = ClientMetadataStatement(signing_keys=KEYS['org']['jwks'], contacts=['*****@*****.**']) # signed by FO ms_org1 = FOP.pack_metadata_statement(cms_org, alg='RS256', scope=['openid']) # signed by FO1 ms_org2 = FO1P.pack_metadata_statement(cms_org, alg='RS256', scope=['openid', 'address']) cms_rp = ClientMetadataStatement( signing_keys=KEYS['admin']['jwks'], redirect_uris=['https://rp.example.com/auth_cb']) ms_rp = ORGOP.pack_metadata_statement( cms_rp, alg='RS256', metadata_statements=Message(**{ FOP.iss: ms_org1, FO1P.iss: ms_org2 })) # only knows about one FO receiver = fo_member(FOP) ri = receiver.unpack_metadata_statement(jwt_ms=ms_rp) assert len(ri.result['metadata_statements']) == 1 _key = list(ri.result['metadata_statements'].keys())[0] _ms = ri.result['metadata_statements'][_key] assert _ms['iss'] == ISSUER['fo']
def federated_client_registration_request(self, **kwargs): """ Constructs a client registration request to be used by a client in a federation. :param kwargs: A set of claims that should be part of the registration. :return: A :py:class:`ClientMetadataStatement` """ req = ClientMetadataStatement() try: req['redirect_uris'] = kwargs['redirect_uris'] except KeyError: try: req["redirect_uris"] = self.redirect_uris except AttributeError: raise MissingRequiredAttribute("redirect_uris", kwargs) else: del kwargs['redirect_uris'] req.update(kwargs) if self.federation: return self.federation_entity.update_request( req, federation=self.federation) elif self.provider_federations: return self.federation_entity.update_request( req, loes=self.provider_federations)
def test_multiple_fo_all_working(): cms_org = ClientMetadataStatement(signing_keys=KEYS['org']['jwks'], contacts=['*****@*****.**']) # signed by FO ms_org1 = FOP.pack_metadata_statement(cms_org, alg='RS256', scope=['openid']) # signed by FO1 ms_org2 = FO1P.pack_metadata_statement(cms_org, alg='RS256', scope=['openid', 'address']) cms_rp = ClientMetadataStatement( signing_keys=KEYS['admin']['jwks'], redirect_uris=['https://rp.example.com/auth_cb']) ms_rp = ORGOP.pack_metadata_statement( cms_rp, alg='RS256', metadata_statements=Message(**{ FOP.iss: ms_org1, FO1P.iss: ms_org2 })) # knows all FO's receiver = fo_member(FOP, FO1P) ri = receiver.unpack_metadata_statement(jwt_ms=ms_rp) assert len(ri.result['metadata_statements']) == 2 _iss = [iss for iss, val in ri.result['metadata_statements'].items()] assert set(_iss) == {ISSUER['fo'], ISSUER['fo1']}
def test_evaluate_metadata_statement_1(): cms_org = ClientMetadataStatement( signing_keys=ORGOP.keyjar.export_jwks(), contacts=['*****@*****.**']) # signed by FO ms_org = FOP.pack_metadata_statement(cms_org, alg='RS256', scope=['openid']) cms_inter = ClientMetadataStatement( signing_keys=KEYS['inter']['jwks'], tos_uri=['https://inter.example.com/tos.html'] ) # signed by org ms_inter = ORGOP.pack_metadata_statement( cms_inter, alg='RS256', metadata_statements=Message(**{FOP.iss: ms_org})) cms_rp = ClientMetadataStatement( signing_keys=KEYS['admin']['jwks'], redirect_uris=['https://rp.example.com/auth_cb'] ) # signed by intermediate ms_rp = INTEROP.pack_metadata_statement( cms_rp, alg='RS256', metadata_statements=Message(**{FOP.iss: ms_inter})) receiver = fo_member(FOP) ri = receiver.unpack_metadata_statement(jwt_ms=ms_rp) res = receiver.evaluate_metadata_statement(ri.result) assert len(res) == 1 assert res[0].iss == ISSUER['org'] assert sorted(list(res[0].keys())) == sorted( ['contacts', 'tos_uri', 'redirect_uris', 'scope'])
def test_pack_and_unpack_ms_lev2(): cms_org = ClientMetadataStatement(signing_keys=KEYS['org']['jwks'], contacts=['*****@*****.**']) # signed by FO ms_org = FOP.pack_metadata_statement(cms_org, alg='RS256', scope=['openid']) cms_inter = ClientMetadataStatement( signing_keys=KEYS['inter']['jwks'], tos_uri=['https://inter.example.com/tos.html']) # signed by org ms_inter = ORGOP.pack_metadata_statement( cms_inter, alg='RS256', metadata_statements=Message(**{FOP.iss: ms_org})) cms_rp = ClientMetadataStatement( signing_keys=KEYS['admin']['jwks'], redirect_uris=['https://rp.example.com/auth_cb']) # signed by intermediate ms_rp = INTEROP.pack_metadata_statement( cms_rp, alg='RS256', metadata_statements=Message(**{FOP.iss: ms_inter})) receiver = fo_member(FOP) ri = receiver.unpack_metadata_statement(jwt_ms=ms_rp) assert ri.result
def test_evaluate_metadata_statement_3(): cms_org = ClientMetadataStatement( signing_keys=KEYS['org']['jwks'], contacts=['*****@*****.**'] ) # signed by FO ms_org1 = FOP.pack_metadata_statement(cms_org, alg='RS256', claims=['email', 'email_verified', 'phone', 'phone_verified'], scope=['openid', 'email', 'phone']) # signed by FO1 ms_org2 = FO1P.pack_metadata_statement(cms_org, alg='RS256', scope=['openid', 'email', 'address']) cms_inter = ClientMetadataStatement( signing_keys=KEYS['inter']['jwks'], tos_uri=['https://inter.example.com/tos.html'] ) ms_inter = {} for k, v in {FOP.iss: ms_org1, FO1P.iss: ms_org2}.items(): # signed by org ms_inter[k] = ORGOP.pack_metadata_statement( cms_inter, alg='RS256', metadata_statements=Message(**{k: v})) cms_rp = ClientMetadataStatement( signing_keys=KEYS['admin']['jwks'], redirect_uris=['https://rp.example.com/auth_cb'], scope=['openid', 'email'] ) # signed by intermediate ms_rp = INTEROP.pack_metadata_statement( cms_rp, alg='RS256', metadata_statements=Message(**ms_inter)) # knows all FO's receiver = fo_member(FOP, FO1P) ri = receiver.unpack_metadata_statement(jwt_ms=ms_rp) res = receiver.evaluate_metadata_statement(ri.result) assert len(res) == 2 assert set([r.fo for r in res]) == {ISSUER['fo'], ISSUER['fo1']} for r in res: if r.fo == ISSUER['fo']: assert sorted(list(r.keys())) == sorted( ['claims', 'contacts', 'tos_uri', 'redirect_uris', 'scope']) assert r['scope'] == ['openid', 'email', 'phone'] else: assert sorted(list(r.keys())) == sorted( ['contacts', 'tos_uri', 'redirect_uris', 'scope']) assert r['scope'] == ['openid', 'email', 'address']
def test_registration_endpoint_fed(self): request = ClientMetadataStatement( redirect_uris=['https://example.com/rp']) rp = Operator(keyjar=keybundle[FO['swamid']], iss=FO['swamid']) sms = rp.pack_metadata_statement(request, alg='RS256') request = rp.extend_with_ms(request, {FO['swamid']: sms}) resp = self.op.registration_endpoint(request.to_dict()) assert isinstance(resp, Created) assert resp.status == "201 Created" clresp = json.loads(resp.message) assert list(clresp['metadata_statements'].keys()) == [FO['swamid']]
def test_evaluate_metadata_statement_4(): """ One 4-level (FO, Org, Inter, admin) and one 2-level (FO1, Inter, admin) """ cms_org = ClientMetadataStatement(signing_keys=KEYS['org']['jwks'], contacts=['*****@*****.**']) # signed by FO ms_org = FOP.pack_metadata_statement( cms_org, alg='RS256', claims=['email', 'email_verified', 'phone', 'phone_verified'], scope=['openid', 'email', 'phone']) cms_inter = ClientMetadataStatement( signing_keys=KEYS['inter']['jwks'], tos_uri=['https://inter.example.com/tos.html']) # signed by org ms_inter0 = ORGOP.pack_metadata_statement( cms_inter, alg='RS256', metadata_statements=Message(**{FOP.iss: ms_org})) ms_inter1 = LIGOOP.pack_metadata_statement(cms_inter, alg='ES256') cms_rp = ClientMetadataStatement( signing_keys=KEYS['admin']['jwks'], redirect_uris=['https://rp.example.com/auth_cb'], scope=['openid', 'email']) # signed by intermediate ms_rp = INTEROP.pack_metadata_statement( cms_rp, alg='RS256', metadata_statements=Message(**{ FOP.iss: ms_inter0, LIGOOP.iss: ms_inter1 })) # knows both FO's receiver = fo_member(FOP, LIGOOP) ri = receiver.unpack_metadata_statement(jwt_ms=ms_rp) _re = receiver.evaluate_metadata_statement(ri.result) res = le_dict(_re) assert set(res.keys()) == {ISSUER['fo'], ISSUER['ligo']} assert sorted(list(res[ISSUER['fo']].keys())) == sorted( ['claims', 'contacts', 'redirect_uris', 'scope', 'tos_uri']) assert res[ISSUER['fo']]['scope'] == ['openid', 'email', 'phone']
def registration_endpoint(self, request, authn=None, **kwargs): """ :param request: :param authn: :param kwargs: :return: """ logger.debug("@registration_endpoint: <<{}>>".format(sanitize(request))) if isinstance(request, dict): request = ClientMetadataStatement(**request) else: try: request = ClientMetadataStatement().deserialize(request, "json") except ValueError: request = ClientMetadataStatement().deserialize(request) try: request.verify() except Exception as err: return error('Invalid request') logger.info( "registration_request:{}".format(sanitize(request.to_dict()))) ms_list = self.federation_entity.get_metadata_statement(request, 'registration') if ms_list: ms = self.federation_entity.pick_by_priority(ms_list) self.federation = ms.fo else: # Nothing I can use return error(error='invalid_request', descr='No signed metadata statement I could use') request = RegistrationRequest(**ms.le) result = self.client_registration_setup(request) if isinstance(result, Response): return result # TODO This is where the OP should sign the response if ms.fo: _fo = ms.fo sms = self.signer.create_signed_metadata_statement( result, 'response', [_fo], single=True) self.federation_entity.extend_with_ms(result, {_fo: sms}) return Created(result.to_json(), content="application/json", headers=[("Cache-Control", "no-store")])
def test_create_client_metadata_statement(): ms = MetadataStatement(signing_keys=KEYS['org']['jwks']) ms_jwt = ms.to_jwt(KEYS['fo']['keyjar'].get_signing_key('rsa')) cms = ClientMetadataStatement( metadata_statements=Message(**{ISSUER['org']: ms_jwt}), contacts=['*****@*****.**']) assert cms
def test_pack_ms_wrong_fo(): cms = ClientMetadataStatement(signing_keys=KEYS['org']['jwks'], contacts=['*****@*****.**']) _jwt = FOP.pack_metadata_statement(cms, alg='RS256', scope=['openid']) member = fo_member(FO1P) pr = member.unpack_metadata_statement(jwt_ms=_jwt) assert pr.result is None assert isinstance(pr.error[_jwt], (MissingSigningKey, KeyError))
def chose_registration_federation(self): """ Chose one among many possible federations. Once the federation has been choosen store the registration info. :return: The :py:class:`LessOrEqual` instance matching the choosen federation. """ _leo = self.chose_federation(self.registration_federations) self.federation = _leo.fo self.store_registration_info(_leo.protected_claims) return ClientMetadataStatement(**_leo.protected_claims())
def test_pack_and_unpack_ms_lev1(): # metadata statement created by the organization cms_org = ClientMetadataStatement(signing_keys=ORGOP.keyjar.export_jwks(), contacts=['*****@*****.**']) # signed by FO ms_org = FOP.pack_metadata_statement(cms_org, alg='RS256', scope=['openid']) # metadata statement created by the admin cms_rp = ClientMetadataStatement( signing_keys=ADMINOP.keyjar.export_jwks(), redirect_uris=['https://rp.example.com/auth_cb']) # signed by the org ms_rp = ORGOP.pack_metadata_statement( cms_rp, alg='RS256', metadata_statements=Message(**{FOP.iss: ms_org})) receiver = fo_member(FOP) ri = receiver.unpack_metadata_statement(jwt_ms=ms_rp) assert ri.result
def test_pack_and_unpack_ms_lev0(): cms = ClientMetadataStatement(signing_keys=FOP.keyjar.export_jwks(), contacts=['*****@*****.**']) _jwt = FOP.pack_metadata_statement(cms, alg='RS256', scope=['openid']) assert _jwt json_ms = unfurl(_jwt) # print(json_ms.keys()) assert set(json_ms.keys()) == {'signing_keys', 'iss', 'iat', 'exp', 'kid', 'scope', 'contacts', 'jti'} # Unpack what you have packed pr = FOP.unpack_metadata_statement(jwt_ms=_jwt) assert pr.result
def registration_endpoint(self, request, authn=None, **kwargs): """ Registration endpoint. This is where a registration request should be handled. :param request: The request, either as a dictionary or as a JSON document :param authn: Authentication information :param kwargs: Extra key work arguments. :return: A request response or an error response. """ logger.debug("@registration_endpoint: <<{}>>".format( sanitize(request))) if isinstance(request, dict): request = ClientMetadataStatement(**request) else: try: request = ClientMetadataStatement().deserialize( request, "json") except ValueError: request = ClientMetadataStatement().deserialize(request) if not self.is_federation_request(request): return provider.Provider.registration_endpoint(self, request.to_json(), authn=None, **kwargs) try: request.verify() except Exception as err: return error('Invalid request') logger.info("registration_request:{}".format( sanitize(request.to_dict()))) les = self.federation_entity.get_metadata_statement( request, 'registration') if les: ms = self.federation_entity.pick_by_priority(les) self.federation = ms.fo else: # Nothing I can use return error(error='invalid_request', descr='No signed metadata statement I could use') _pc = ms.protected_claims() if _pc: request = RegistrationRequest(**_pc) else: request = RegistrationRequest( **ms.unprotected_and_protected_claims()) result = self.client_registration_setup(request) if 'signed_jwks_uri' in _pc: _kb = KeyBundle(source=_pc['signed_jwks_uri'], verify_keys=ms.signing_keys, verify_ssl=False) _kb.do_remote() replace_jwks_key_bundle(self.keyjar, result['client_id'], _kb) result['signed_jwks_uri'] = _pc['signed_jwks_uri'] if isinstance(result, Response): return result # TODO This is where the OP should sign the response if ms.fo: _fo = ms.fo _sig = self._signer() if _sig: sms = _sig.create_signed_metadata_statement(result, 'response', [_fo], single=True) else: raise SigningServiceError('No Signer') self.federation_entity.extend_with_ms(result, {_fo: sms}) return Created(result.to_json(), content="application/json", headers=[("Cache-Control", "no-store")])