def handle_response(self, response, issuer, func, response_cls): """ Handle a request response. Depending on which type of response it is different functions *func* will be used to handle it. If something went wrong an exception will be raised. :param response: A requests.request response :param issuer: who was the request sent to :param func: A function to use for handling a correct response :param response_cls: The response should match this class """ err_msg = 'Got error response: {}' unk_msg = 'Unknown response: {}' if response.status_code in [200, 201]: resp = response_cls().deserialize(response.text, "json") # Some implementations sends back a 200 with an error message inside if resp.verify(): # got a proper response func(resp, issuer) else: resp = ErrorResponse().deserialize(response.text, "json") if resp.verify(): logger.error(err_msg.format(sanitize(resp.to_json()))) if self.events: self.events.store('protocol response', resp) raise RegistrationError(resp.to_dict()) else: # Something else logger.error(unk_msg.format(sanitize(response.text))) raise RegistrationError(response.text) else: try: resp = ErrorResponse().deserialize(response.text, "json") except _decode_err: logger.error(unk_msg.format(sanitize(response.text))) raise RegistrationError(response.text) if resp.verify(): logger.error(err_msg.format(sanitize(resp.to_json()))) if self.events: self.events.store('protocol response', resp) raise RegistrationError(resp.to_dict()) else: # Something else logger.error(unk_msg.format(sanitize(response.text))) raise RegistrationError(response.text)
def handle_registration_info(self, response): if response.status_code == 200: resp = ClientInfoResponse().deserialize(response.text, "json") self.store_registration_info(resp) else: err = ErrorResponse().deserialize(response.text, "json") raise PyoidcError("Registration failed: %s" % err.get_json()) return resp
def expected_error_response(self, response): if isinstance(response, Response): # requests response response = ErrorResponse().from_json(response.content) if isinstance(response, ErrorResponse): if response["error"] not in self.expect_error["error"]: raise Break("Wrong error, got {} expected {}".format( response["error"], self.expect_error["error"])) if self.expect_error["stop"]: raise Break("Stop requested after received expected error") else: self.conv.trace.error("Expected error, didn't get it") raise Break("Did not receive expected error")
def handle_registration_info(self, response): err_msg = 'Got error response: {}' unk_msg = 'Unknown response: {}' if response.status_code in [200, 201]: resp = RegistrationResponse().deserialize(response.text, "json") # Some implementations sends back a 200 with an error message inside if resp.verify(): # got a proper registration response resp = self.get_metadata_statement(resp) if resp is None: # No metadata statement that I can use raise RegistrationError('No trusted metadata') self.store_response(resp, response.text) self.store_registration_info(resp) else: resp = ErrorResponse().deserialize(response.text, "json") if resp.verify(): logger.error(err_msg.format(sanitize(resp.to_json()))) if self.events: self.events.store('protocol response', resp) raise RegistrationError(resp.to_dict()) else: # Something else logger.error(unk_msg.format(sanitize(response.text))) raise RegistrationError(response.text) else: try: resp = ErrorResponse().deserialize(response.text, "json") except _decode_err: logger.error(unk_msg.format(sanitize(response.text))) raise RegistrationError(response.text) if resp.verify(): logger.error(err_msg.format(sanitize(resp.to_json()))) if self.events: self.events.store('protocol response', resp) raise RegistrationError(resp.to_dict()) else: # Something else logger.error(unk_msg.format(sanitize(response.text))) raise RegistrationError(response.text) return resp
def authorization(self, **kwargs): if cherrypy.request.method == "OPTIONS": cherrypy_cors.preflight( allowed_methods=["GET"], origins='*', allowed_headers=['Authorization', 'content-type']) else: logger.debug('AuthorizationRequest') try: args = {'cookie': cherrypy.request.headers['Cookie']} except KeyError: args = {} try: _claims = json.loads(kwargs['claims']) except json.JSONDecodeError: try: _claims = json.loads( kwargs['claims'].replace("\'", '"').replace('True', 'true')) except json.JSONDecodeError: _err = ErrorResponse( error="invalid_request", error_description="Invalid claims value" ) raise cherrypy.HTTPError(400, as_bytes(_err.to_json())) else: kwargs['claims'] = _claims except KeyError: pass else: kwargs['claims'] = _claims try: resp = self.op.authorization_endpoint(kwargs, **args) except Exception as err: raise cherrypy.HTTPError(message=err) else: return conv_response(resp)
def expected_error_response(self, response): if isinstance(response, Response): # requests response # don't want bytes _txt = response.content.decode('utf8') response = ErrorResponse().from_json(_txt) if isinstance(response, ErrorResponse): self.conv.events.store(EV_PROTOCOL_RESPONSE, response, sender=self.__class__.__name__) if response["error"] not in self.expect_error["error"]: raise Break("Wrong error, got {} expected {}".format( response["error"], self.expect_error["error"])) try: if self.expect_error["stop"]: raise Break("Stop requested after received expected error") except KeyError: pass else: self.conv.trace.error("Expected error, didn't get it") raise Break("Did not receive expected error") return response
def get_info(self, url, trace, method, req, data=None): if data: kwargs = {"data": data} else: kwargs = {} if "authn_method" in req.kw_args: if req.kw_args["authn_method"] == "bearer_header": kwargs["headers"] = { "Authorization": "Bearer {}".format(req.request_args["access_token"]) } r = self.http_request(url, method, **kwargs) trace.reply("STATUS: %s" % r.status_code) trace.reply("BODY: %s" % r.text) if r.status_code == 200: info = json.loads(r.text) if "error" in info: info = ErrorResponse(**info) return info else: return None
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() areq.lax = True if isinstance(request, dict): areq.from_dict(request) else: areq.deserialize(request, "urlencoded") try: redirect_uri = self.get_redirect_uri(areq) except (RedirectURIError, ParameterError, UnknownClient) as err: return error_response("invalid_request", "%s" % err) try: _rtype = areq["response_type"] except KeyError: _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_response("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_response("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_response('unauthorized_client', 'unknown client') else: try: _registered = [set(rt.split(' ')) for rt in _cinfo['response_types']] except KeyError: # If no response_type is registered by the client then we'll # code which it the default according to the OIDC spec. _registered = [{'code'}] _wanted = set(areq["response_type"]) if _wanted not in _registered: return error_response("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_response("invalid_request", "{}:{}".format(err.__class__.__name__, 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, MissingRequiredValue) as err: return redirect_authz_error("invalid_request", redirect_uri, "%s" % err) return {"areq": areq, "redirect_uri": redirect_uri}
def func_error(error, error_description=''): return ErrorResponse(error=error, error_description=error_description)
def do_user_info_request(self, method="POST", state="", scope="openid", request="openid", **kwargs): kwargs["request"] = request path, body, method, h_args = self.user_info_request( method, state, scope, **kwargs) logger.debug( "[do_user_info_request] PATH:{} BODY:{} H_ARGS: {}".format( path, body, h_args)) if self.events: self.events.store('Request', {'body': body}) self.events.store('request_url', path) self.events.store('request_http_args', h_args) try: resp = self.http_request(path, method, data=body, **h_args) except MissingRequiredAttribute: raise if resp.status_code == 200: try: assert "application/json" in resp.headers["content-type"] sformat = "json" except AssertionError: assert "application/jwt" in resp.headers["content-type"] sformat = "jwt" elif resp.status_code == 500: raise PyoidcError("ERROR: Something went wrong: %s" % resp.text) elif resp.status_code == 401: try: www_auth_header = resp.headers["WWW-Authenticate"] res = ErrorResponse( error="invalid_token", error_description= "Server returned 401 Unauthorized. WWW-Authenticate header:{}" .format(www_auth_header)) except KeyError: #no www-authenticate header. https://tools.ietf.org/html/rfc6750#section-3 reads: #the resource server MUST include the HTTP "WWW-Authenticate" response header field res = ErrorResponse( error="invalid_token", error_description= "Server returned 401 Unauthorized without a WWW-Authenticate header which is required as per https://tools.ietf.org/html/rfc6750#section-3" ) self.store_response(res, resp.text) return res elif 400 <= resp.status_code < 500: # the response text might be a OIDC message try: res = ErrorResponse().from_json(resp.text) except Exception: raise RequestError(resp.text) else: self.store_response(res, resp.text) return res else: raise PyoidcError("ERROR: Something went wrong [%s]: %s" % (resp.status_code, resp.text)) try: _schema = kwargs["user_info_schema"] except KeyError: _schema = OpenIDSchema logger.debug("Reponse text: '{}'".format(resp.text)) _txt = resp.text if sformat == "json": res = _schema().from_json(txt=_txt) else: verify = kwargs.get('verify', True) res = _schema().from_jwt(_txt, keyjar=self.keyjar, sender=self.provider_info["issuer"], verify=verify) if 'error' in res: # Error response res = UserInfoErrorResponse(**res.to_dict()) # TODO verify issuer:sub against what's returned in the ID Token self.store_response(res, _txt) return res
def do_user_info_request(self, method="POST", state="", scope="openid", request="openid", **kwargs): kwargs["request"] = request path, body, method, h_args = self.user_info_request( method, state, scope, **kwargs) logger.debug( "[do_user_info_request] PATH:{} BODY:{} H_ARGS: {}".format( path, body, h_args)) if self.events: self.events.store('Request', {'body': body}) self.events.store('request_url', path) self.events.store('request_http_args', h_args) try: resp = self.http_request(path, method, data=body, **h_args) except MissingRequiredAttribute: raise if resp.status_code == 200: try: assert "application/json" in resp.headers["content-type"] sformat = "json" except AssertionError: assert "application/jwt" in resp.headers["content-type"] sformat = "jwt" elif resp.status_code == 500: raise PyoidcError("ERROR: Something went wrong: %s" % resp.text) elif 400 <= resp.status_code < 500: # the response text might be a OIDC message try: res = ErrorResponse().from_json(resp.text) except Exception: raise RequestError(resp.text) else: self.store_response(res, resp.text) return res else: raise PyoidcError("ERROR: Something went wrong [%s]: %s" % (resp.status_code, resp.text)) try: _schema = kwargs["user_info_schema"] except KeyError: _schema = OpenIDSchema logger.debug("Reponse text: '{}'".format(resp.text)) _txt = resp.text if sformat == "json": res = _schema().from_json(txt=_txt) else: verify = kwargs.get('verify', True) res = _schema().from_jwt(_txt, keyjar=self.keyjar, sender=self.provider_info["issuer"], verify=verify) if 'error' in res: # Error response res = UserInfoErrorResponse(**res.to_dict()) # TODO verify issuer:sub against what's returned in the ID Token self.store_response(res, _txt) return res