def verify_header(reqresp, body_type): """ :param reqresp: Class instance with attributes: ['status', 'text', 'headers', 'url'] :param body_type: If information returned in the body part :return: Verified body content type """ logger.debug("resp.headers: %s" % (sanitize(reqresp.headers),)) logger.debug("resp.txt: %s" % (sanitize(reqresp.text),)) try: _ctype = reqresp.headers["content-type"] except KeyError: if body_type: return body_type else: return 'txt' # reasonable default ?? logger.debug('Expected body type: "{}"'.format(body_type)) if body_type == "": if match_to_("application/json", _ctype) or match_to_( 'application/jrd+json', _ctype): body_type = 'json' elif match_to_("application/jwt", _ctype): body_type = "jwt" elif match_to_(URL_ENCODED, _ctype): body_type = 'urlencoded' else: body_type = 'txt' # reasonable default ?? elif body_type == "json": if match_to_("application/json", _ctype) or match_to_( 'application/jrd+json', _ctype): pass elif match_to_("application/jwt", _ctype): body_type = "jwt" else: raise WrongContentType(_ctype) elif body_type == "jwt": if not match_to_("application/jwt", _ctype): raise WrongContentType(_ctype) elif body_type == "urlencoded": if not match_to_(DEFAULT_POST_CONTENT_TYPE, _ctype): # I can live with text/plain if not match_to_("text/plain", _ctype): raise WrongContentType(_ctype) elif body_type == 'txt': if match_to_("text/plain", _ctype): pass elif match_to_("text/html", _ctype): pass else: raise WrongContentType(_ctype) else: raise ValueError("Unknown return format: %s" % body_type) logger.debug('Got body type: "{}"'.format(body_type)) return body_type
def verify(self, areq, **kwargs): try: try: argv = {'sender': areq['client_id']} except KeyError: argv = {} bjwt = AuthnToken().from_jwt(areq["client_assertion"], keyjar=self.srv.keyjar, **argv) except (Invalid, MissingKey) as err: logger.info("%s" % sanitize(err)) raise AuthnFailure("Could not verify client_assertion.") logger.debug("authntoken: %s" % sanitize(bjwt.to_dict())) areq['parsed_client_assertion'] = bjwt # logger.debug("known clients: %s" % sanitize(self.cli.cdb.keys())) try: cid = kwargs["client_id"] except KeyError: cid = bjwt["iss"] try: # There might not be a client_id in the request assert str(cid) in self.srv.cdb # It's a client I know except KeyError: pass # aud can be a string or a list _aud = bjwt["aud"] logger.debug("audience: %s, baseurl: %s" % (_aud, self.srv.baseurl)) # figure out authn method if alg2keytype(bjwt.jws_header['alg']) == 'oct': # Symmetric key authn_method = 'client_secret_jwt' else: authn_method = 'private_key_jwt' try: if isinstance(_aud, six.string_types): assert str(_aud).startswith(self.srv.baseurl) else: for target in _aud: if target.startswith(self.srv.baseurl): return cid, authn_method raise NotForMe("Not for me!") except AssertionError: raise NotForMe("Not for me!") return cid, authn_method
def __call__(self, url, method="GET", **kwargs): """ Send a HTTP request to a URL using a specified method :param url: The URL to access :param method: The method to use (GET, POST, ..) :param kwargs: extra HTTP request parameters :return: A Response """ # copy the default set before starting to modify it. _kwargs = copy.copy(self.request_args) if kwargs: _kwargs.update(kwargs) # If I have cookies add them all to the request if self.cookiejar: _kwargs["cookies"] = self._cookies() logger.debug("SENT {} COOKIES".format(len(_kwargs["cookies"]))) # If I want to modify the request arguments based on URL, method # and current arguments I can use this call back function. if self.req_callback is not None: _kwargs = self.req_callback(method, url, **_kwargs) try: # Do the request r = requests.request(method, url, **_kwargs) except Exception as err: logger.error( "http_request failed: %s, url: %s, htargs: %s, method: %s" % ( err, url, sanitize(_kwargs), method)) raise if self.events is not None: self.events.store('HTTP response', r, ref=url) try: _cookie = r.headers["set-cookie"] logger.debug("RECEIVED COOKIE") try: # add received cookies to the cookie jar set_cookie(self.cookiejar, SimpleCookie(_cookie)) except CookieError as err: logger.error(err) raise NonFatalException(r, "{}".format(err)) except (AttributeError, KeyError) as err: pass # return the response return r
def set_cookie(cookiejar, kaka): """PLaces a cookie (a cookielib.Cookie based on a set-cookie header line) in the cookie jar. Always chose the shortest expires time. :param cookiejar: :param kaka: Cookie """ # default rfc2109=False # max-age, httponly for cookie_name, morsel in kaka.items(): std_attr = ATTRS.copy() std_attr["name"] = cookie_name _tmp = morsel.coded_value if _tmp.startswith('"') and _tmp.endswith('"'): std_attr["value"] = _tmp[1:-1] else: std_attr["value"] = _tmp std_attr["version"] = 0 attr = "" # copy attributes that have values try: for attr in morsel.keys(): if attr in ATTRS: if morsel[attr]: if attr == "expires": std_attr[attr] = http2time(morsel[attr]) else: std_attr[attr] = morsel[attr] elif attr == "max-age": if morsel[attr]: std_attr["expires"] = http2time(morsel[attr]) except TimeFormatError: # Ignore cookie logger.info( "Time format error on %s parameter in received cookie" % ( sanitize(attr),)) continue for att, spec in PAIRS.items(): if std_attr[att]: std_attr[spec] = True if std_attr["domain"] and std_attr["domain"].startswith("."): std_attr["domain_initial_dot"] = True if morsel["max-age"] is 0: try: cookiejar.clear(domain=std_attr["domain"], path=std_attr["path"], name=std_attr["name"]) except ValueError: pass else: # Fix for Microsoft cookie error if "version" in std_attr: try: std_attr["version"] = std_attr["version"].split(",")[0] except (TypeError, AttributeError): pass new_cookie = Cookie(**std_attr) cookiejar.set_cookie(new_cookie)
def get_client_id(cdb, req, authn): """ Verify the client and return the client id :param req: The request :param authn: Authentication information from the HTTP header :return: """ logger.debug("REQ: %s" % sanitize(req.to_dict())) if authn: if authn.startswith("Basic "): logger.debug("Basic auth") (_id, _secret) = base64.b64decode( authn[6:].encode("utf-8")).decode("utf-8").split(":") _bid = as_bytes(_id) _cinfo = None try: _cinfo = cdb[_id] except KeyError: try: _cinfo[_bid] except AttributeError: pass if not _cinfo: logger.debug("Unknown client_id") raise FailedAuthentication("Unknown client_id") else: if not valid_client_info(_cinfo): logger.debug("Invalid Client info") raise FailedAuthentication("Invalid Client") if _secret != _cinfo["client_secret"]: logger.debug("Incorrect secret") raise FailedAuthentication("Incorrect secret") else: if authn[:6].lower() == "bearer": logger.debug("Bearer auth") _token = authn[7:] else: raise FailedAuthentication("AuthZ type I don't know") try: _id = cdb[_token] except KeyError: logger.debug("Unknown access token") raise FailedAuthentication("Unknown access token") else: try: _id = str(req["client_id"]) if _id not in cdb: logger.debug("Unknown client_id") raise FailedAuthentication("Unknown client_id") if not valid_client_info(cdb[_id]): raise FailedAuthentication("Invalid client_id") except KeyError: raise FailedAuthentication("Missing client_id") return _id
def construct(self, request, cli_info=None, http_args=None, **kwargs): """ Constructs a client assertion and signs it with a key. The request is modified as a side effect. :param request: The request :param cli_info: A :py:class:`oiccli.client_info.ClientInfo` instance :param http_args: HTTP arguments :param kwargs: Extra arguments :return: Constructed HTTP arguments, in this case none """ if 'client_assertion' in kwargs: request["client_assertion"] = kwargs['client_assertion'] if 'client_assertion_type' in kwargs: request[ 'client_assertion_type'] = kwargs['client_assertion_type'] else: request["client_assertion_type"] = JWT_BEARER elif 'client_assertion' in request: if 'client_assertion_type' not in request: request["client_assertion_type"] = JWT_BEARER else: algorithm = None # audience for the signed JWT depends on which endpoint # we're talking to. if kwargs['authn_endpoint'] in ['token', 'refresh']: try: algorithm = cli_info.registration_info[ 'token_endpoint_auth_signing_alg'] except (KeyError, AttributeError): pass audience = cli_info.provider_info['token_endpoint'] else: audience = cli_info.provider_info['issuer'] if not algorithm: algorithm = self.choose_algorithm(**kwargs) ktype = alg2keytype(algorithm) try: if 'kid' in kwargs: signing_key = [self.get_key_by_kid(kwargs["kid"], algorithm, cli_info)] elif ktype in cli_info.kid["sig"]: try: signing_key = [self.get_key_by_kid( cli_info.kid["sig"][ktype], algorithm, cli_info)] except KeyError: signing_key = self.get_signing_key(algorithm, cli_info) else: signing_key = self.get_signing_key(algorithm, cli_info) except NoMatchingKey as err: logger.error("%s" % sanitize(err)) raise try: _args = {'lifetime': kwargs['lifetime']} except KeyError: _args = {} # construct the signed JWT with the assertions and add # it as value to the 'client_assertion' claim of the request request["client_assertion"] = assertion_jwt( cli_info.client_id, signing_key, audience, algorithm, **_args) request["client_assertion_type"] = JWT_BEARER try: del request["client_secret"] except KeyError: pass # If client_id is not required to be present, remove it. if not request.c_param["client_id"][VREQUIRED]: try: del request["client_id"] except KeyError: pass return {}