def _func(self, conv): _response = conv.last_response res = {} try: _loc = _response.headers["location"] if "?" in _loc: query = _loc.split("?")[1] elif "#" in _loc: query = _loc.split("#")[1] else: # ??? self._message = "Expected redirect" self._status = CRITICAL return res except (KeyError, AttributeError): self._message = "Expected redirect" self._status = CRITICAL return res if _response.status_code == 302: err = ErrorResponse().deserialize(query, "urlencoded") try: err.verify() res["content"] = err.to_json() conv.protocol_response.append((err, query)) except MissingRequiredAttribute: self._message = "Expected error message" self._status = CRITICAL else: self._message = "Expected error message" self._status = CRITICAL return res
def _func(self, conv): res = {} response = conv.last_response if response.status_code == 302: _loc = response.headers["location"] if "?" in _loc: _query = _loc.split("?")[1] elif "#" in _loc: _query = _loc.split("#")[1] else: self._message = "Faulty error message" self._status = ERROR return try: err = ErrorResponse().deserialize(_query, "urlencoded") err.verify() #res["temp"] = err res["message"] = err.to_dict() except Exception: self._message = "Faulty error message" self._status = ERROR else: self._message = "Expected a redirect with an error message" self._status = ERROR return res
def _func(self, conv): _response = conv.last_response _content = conv.last_content res = {} if _response.status_code >= 400: content_type = _response.headers["content-type"] if content_type is None: res["content"] = _content elif CONT_JSON in content_type: try: self.err = ErrorResponse().deserialize(_content, "json") self.err.verify() res["content"] = self.err.to_json() #res["temp"] = err except Exception: res["content"] = _content else: res["content"] = _content else: # might still be an error message try: self.err = ErrorResponse().deserialize(_content, "json") self.err.verify() res["content"] = self.err.to_json() except Exception: self._message = "Expected error message" self._status = CRITICAL res["url"] = conv.position return res
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 _func(self, conv): _response = conv.last_response _content = conv.last_content res = {} if _response.status_code == 400: err = ErrorResponse().deserialize(_content, "json") err.verify() res["content"] = err.to_json() conv.protocol_response.append((err, _content)) else: self._message = "Expected a 400 error message" self._status = CRITICAL return res
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 _func(self, conv): _response = conv.last_response _content = conv.last_content res = {} if _response.status_code >= 400: self._status = self.status self._message = self.msg if CONT_JSON in _response.headers["content-type"]: try: err = ErrorResponse().deserialize(_content, "json") self._message = err.to_json() except Exception: res["content"] = _content else: res["content"] = _content res["url"] = conv.position res["http_status"] = _response.status_code else: # might still be an error message try: err = ErrorResponse().deserialize(_content, "json") err.verify() self._message = err.to_json() self._status = self.status except Exception: pass res["url"] = conv.position return res
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 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 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
class CheckErrorResponse(ExpectedError): """ Checks that the HTTP response status is outside the 200 or 300 range or that an JSON encoded error message has been received """ cid = "check-error-response" msg = "OP error" def _func(self, conv): _response = conv.last_response _content = conv.last_content res = {} if _response.status_code >= 400: content_type = _response.headers["content-type"] if content_type is None: res["content"] = _content elif CONT_JSON in content_type: try: self.err = ErrorResponse().deserialize(_content, "json") self.err.verify() res["content"] = self.err.to_json() #res["temp"] = err except Exception: res["content"] = _content else: res["content"] = _content else: # might still be an error message try: self.err = ErrorResponse().deserialize(_content, "json") self.err.verify() res["content"] = self.err.to_json() except Exception: self._message = "Expected error message" self._status = CRITICAL res["url"] = conv.position return res
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 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
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 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