def token_endpoint(self, request="", authn=None, dtype='urlencoded', **kwargs): try: req = AccessTokenRequest().deserialize(request, dtype) client_id = self.client_authn(self, req, authn) except FailedAuthentication as err: logger.error(err) self.events.store(EV_EXCEPTION, "Failed to verify client due to: {}".format(err)) return error(error="invalid_client", descr=err.args[0]) except Exception as err: logger.error(err) self.events.store(EV_EXCEPTION, "Failed to verify client due to: %s" % err) return error(error="invalid_client", descr="Failed to verify client: {}".format(err)) try: self._update_client_keys(client_id) except TestError: logger.error('No change in client keys') return error(error="incorrect_behavior", descr="No change in client keys") _response = provider.Provider.token_endpoint(self, request, authn, dtype, **kwargs) return _response
def registration_endpoint(self, request, authn=None, **kwargs): try: reg_req = RegistrationRequest().deserialize(request, "json") except ValueError: reg_req = RegistrationRequest().deserialize(request) self.events.store(EV_PROTOCOL_REQUEST, reg_req) try: response_type_cmp(kwargs['test_cnf']['response_type'], reg_req['response_types']) except KeyError: pass try: provider.Provider.verify_redirect_uris(reg_req) except InvalidRedirectURIError as err: return error(error="invalid_configuration_parameter", descr="Invalid redirect_uri: {}".format(err)) # Do initial verification that all endpoints from the client uses # https for endp in ["jwks_uri", "initiate_login_uri"]: try: uris = reg_req[endp] except KeyError: continue if not isinstance(uris, list): uris = [uris] for uri in uris: if not uri.startswith("https://"): return error( error="invalid_configuration_parameter", descr="Non-HTTPS endpoint in '{}'".format(endp)) if not "contacts" in reg_req: return error( error="invalid_configuration_parameter", descr="No \"contacts\" claim provided in registration request." ) elif not "@" in reg_req["contacts"][0]: return error( error="invalid_configuration_parameter", descr= "First address in \"contacts\" value in registration request is not a valid e-mail address." ) _response = provider.Provider.registration_endpoint( self, request, authn, **kwargs) self.events.store(EV_HTTP_RESPONSE, _response) self.init_keys = [] if "jwks_uri" in reg_req: if _response.status == "200 OK": # find the client id req_resp = RegistrationResponse().from_json(_response.message) for kb in self.keyjar[req_resp["client_id"]]: if kb.imp_jwks: self.events.store("Client JWKS", kb.imp_jwks) return _response
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 providerinfo_endpoint(self, handle="", **kwargs): """ The Provider info endpoint. A request for provider info should be handled by this method. It will work as well for requests from federation aware RPs as for non-federation aware RPs. :param handle: (key, timestamp) tuple used at cookie construction :param kwargs: Extra key word arguments. :return: Provider Info response """ logger.info("@providerinfo_endpoint") try: _response = self.create_fed_providerinfo() msg = "provider_info_response: {}" logger.info(msg.format(sanitize(_response.to_dict()))) if self.events: self.events.store('Protocol response', _response) headers = [("Cache-Control", "no-store"), ("x-ffo", "bar")] if handle: (key, timestamp) = handle if key.startswith(STR) and key.endswith(STR): cookie = self.cookie_func(key, self.cookie_name, "pinfo", self.sso_ttl) headers.append(cookie) resp = Response(_response.to_json(), content="application/json", headers=headers) except Exception: message = traceback.format_exception(*sys.exc_info()) logger.error(message) resp = error('service_error', message) return resp
def registration_endpoint(self, request, authn=None, **kwargs): 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) logger.info("registration_request:{}".format( sanitize(request.to_dict()))) res = self.federation_entity.get_metadata_statement(request) if res: request = self.federation_entity.pick_by_priority(res) else: # Nothing I can use return error(error='invalid_request', descr='No signed metadata statement I could use') result = self.client_registration_setup(request) if isinstance(result, Response): return result return Created(result.to_json(), content="application/json", headers=[("Cache-Control", "no-store")])
def providerinfo_endpoint(self, handle="", **kwargs): logger.info("@providerinfo_endpoint") try: _response = self.create_fed_providerinfo() msg = "provider_info_response: {}" logger.info(msg.format(sanitize(_response.to_dict()))) if self.events: self.events.store('Protocol response', _response) headers = [("Cache-Control", "no-store"), ("x-ffo", "bar")] if handle: (key, timestamp) = handle if key.startswith(STR) and key.endswith(STR): cookie = self.cookie_func(key, self.cookie_name, "pinfo", self.sso_ttl) headers.append(cookie) resp = Response(_response.to_json(), content="application/json", headers=headers) except Exception: message = traceback.format_exception(*sys.exc_info()) logger.error(message) resp = error('service_error', message) return resp
def _complete_authz(self, user, areq, sid, **kwargs): _log_debug = logger.debug _log_debug("- in authenticated() -") # Do the authorization try: permission = self.authz(user, client_id=areq['client_id']) self.sdb.update(sid, "permission", permission) except Exception: raise _log_debug("response type: %s" % areq["response_type"]) if self.sdb.is_revoked(sid): return error(error="access_denied", descr="Token is revoked") try: info = self.create_authn_response(areq, sid) except UnSupported as err: return error_response(*err.args) if isinstance(info, Response): return info else: aresp, fragment_enc = info try: redirect_uri = self.get_redirect_uri(areq) except (RedirectURIError, ParameterError) as err: return BadRequest("%s" % err) # Must not use HTTP unless implicit grant type and native application info = self.aresp_check(aresp, areq) if isinstance(info, Response): return info headers = [] try: _kaka = kwargs["cookie"] except KeyError: pass else: if _kaka: if isinstance(_kaka, dict): for name, val in _kaka.items(): _c = SimpleCookie() _c[name] = val _x = _c.output() if PY2: _x = str(_x) headers.append(tuple(_x.split(": ", 1))) else: if PY2: _kaka = newstr(_kaka) _c = SimpleCookie() _c.load(_kaka) for x in _c.output().split('\r\n'): if PY2: x = str(x) headers.append(tuple(x.split(": ", 1))) if self.cookie_name not in _kaka: # Don't overwrite header = self.cookie_func(user, typ="sso", ttl=self.sso_ttl) if header: headers.append(header) else: header = self.cookie_func(user, typ="sso", ttl=self.sso_ttl) if header: headers.append(header) # Now about the response_mode. Should not be set if it's obvious # from the response_type. Knows about 'query', 'fragment' and # 'form_post'. if "response_mode" in areq: try: resp = self.response_mode(areq, fragment_enc, aresp=aresp, redirect_uri=redirect_uri, headers=headers) except InvalidRequest as err: return error("invalid_request", err) else: if resp is not None: return resp return aresp, headers, redirect_uri, fragment_enc
def auth_init(self, request, request_class=AuthorizationRequest): """ :param request: The AuthorizationRequest :return: """ logger.debug("Request: '%s'" % sanitize(request)) # Same serialization used for GET and POST try: areq = self.server.parse_authorization_request( request=request_class, query=request) except (MissingRequiredValue, MissingRequiredAttribute, AuthzError) as err: logger.debug("%s" % err) areq = request_class().deserialize(request, "urlencoded") try: redirect_uri = self.get_redirect_uri(areq) except (RedirectURIError, ParameterError, UnknownClient) as err: return error("invalid_request", "%s" % err) try: _rtype = areq["response_type"] except: _rtype = ["code"] try: _state = areq["state"] except KeyError: _state = '' return redirect_authz_error("invalid_request", redirect_uri, "%s" % err, _state, _rtype) except KeyError: areq = request_class().deserialize(request, "urlencoded") # verify the redirect_uri try: self.get_redirect_uri(areq) except (RedirectURIError, ParameterError) as err: return error("invalid_request", "%s" % err) except Exception as err: message = traceback.format_exception(*sys.exc_info()) logger.error(message) logger.debug("Bad request: %s (%s)" % (err, err.__class__.__name__)) err = ErrorResponse(error='invalid_request', error_description=str(err)) return BadRequest(err.to_json(), content='application/json') if not areq: logger.debug("No AuthzRequest") return error("invalid_request", "Can not parse AuthzRequest") areq = self.filter_request(areq) if self.events: self.events.store('Protocol request', areq) try: _cinfo = self.cdb[areq['client_id']] except KeyError: logger.error('Client ID ({}) not in client database'.format( areq['client_id'])) return error('unauthorized_client', 'unknown client') else: try: rtypes = _cinfo['response_types'] except KeyError: rtypes = ['code'] # default according to OIDC registration if ' '.join(areq["response_type"]) not in rtypes: return error("invalid_request", "Trying to use unregistered response_typ") logger.debug("AuthzRequest: %s" % (sanitize(areq.to_dict()), )) try: redirect_uri = self.get_redirect_uri(areq) except (RedirectURIError, ParameterError, UnknownClient) as err: return error("invalid_request", "%s" % err) try: keyjar = self.keyjar except AttributeError: keyjar = "" try: # verify that the request message is correct areq.verify(keyjar=keyjar, opponent_id=areq["client_id"]) except (MissingRequiredAttribute, ValueError) as err: return redirect_authz_error("invalid_request", redirect_uri, "%s" % err) return {"areq": areq, "redirect_uri": redirect_uri}
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")])
def authorization_endpoint(self, request="", cookie=None, **kwargs): if isinstance(request, dict): _req = request else: _req = {} for key, val in parse_qs(request).items(): if len(val) == 1: _req[key] = val[0] else: _req[key] = val # self.events.store(EV_REQUEST, _req) try: _scope = _req["scope"] except KeyError: return error( error="incorrect_behavior", descr="No scope parameter" ) else: # verify that openid is among the scopes _scopes = _scope.split(" ") if "openid" not in _scopes: return error( error="incorrect_behavior", descr="Scope does not contain 'openid'" ) client_id = _req["client_id"] try: f = response_type_cmp(self.capabilities['response_types_supported'], _req['response_type']) except KeyError: pass else: if f is False: self.events.store( EV_FAULT, 'Wrong response type: {}'.format(_req['response_type'])) return error_response(error="incorrect_behavior", descr="Not supported response_type") _rtypes = _req['response_type'].split(' ') if 'id_token' in _rtypes: try: self._update_client_keys(client_id) except TestError: return error(error="incorrect_behavior", descr="No change in client keys") if isinstance(request, dict): request = urlencode(request) _response = provider.Provider.authorization_endpoint(self, request, cookie, **kwargs) if "rotenc" in self.behavior_type: # Rollover encryption keys rsa_key = RSAKey(kid="rotated_rsa_{}".format(time.time()), use="enc").load_key(RSA.generate(2048)) ec_key = ECKey(kid="rotated_ec_{}".format(time.time()), use="enc").load_key(P256) keys = [rsa_key.serialize(private=True), ec_key.serialize(private=True)] new_keys = {"keys": keys} self.events.store("New encryption keys", new_keys) self.do_key_rollover(new_keys, "%d") self.events.store("Rotated encryption keys", '') logger.info( 'Rotated OP enc keys, new set: {}'.format( key_summary(self.keyjar, ''))) # This is just for logging purposes try: _resp = self.server.http_request(_req["request_uri"]) except KeyError: pass except requests.ConnectionError as err: self.events.store(EV_EXCEPTION, err) err = unwrap_exception(err) return error_response(error="server_error", descr=err) else: if _resp.status_code == 200: self.events.store(EV_REQUEST, "Request from request_uri: {}".format( _resp.text)) return _response