def __init__(self, method, uri, body=None, headers=None): InsecureTransportError.check(uri) self.method = method self.uri = uri self.body = body self.headers = headers or {} self.query = urlparse.urlparse(uri).query self.query_params = url_decode(self.query) self.body_params = extract_params(body) or [] params = {} if self.query_params: params.update(dict(self.query_params)) if self.body_params: params.update(dict(self.body_params)) self.data = params self.user = None self.credential = None self.client = None self._data_keys = { 'client_id', 'code', 'redirect_uri', 'scope', 'state', 'response_type', 'grant_type' }
def __init__(self, method, uri, body=None, headers=None): InsecureTransportError.check(uri) self.method = method self.uri = uri self.body = body self.headers = headers or {} # states namespaces self.client = None self.credential = None self.user = None self.query = urlparse.urlparse(uri).query self.query_params = url_decode(self.query) self.body_params = extract_params(body) or [] self.auth_params, self.realm = _parse_authorization_header(headers) self.signature_type, self.oauth_params = _parse_oauth_params( self.query_params, self.body_params, self.auth_params) params = [] params.extend(self.query_params) params.extend(self.body_params) params.extend(self.auth_params) self.params = params
def __init__(self, method, uri, body=None, headers=None): InsecureTransportError.check(uri) #: HTTP method self.method = method self.uri = uri self.body = body #: HTTP headers self.headers = headers or {} self.query = urlparse.urlparse(uri).query self.query_params = url_decode(self.query) self.body_params = extract_params(body) or [] self.args = dict(self.query_params) self.form = dict(self.body_params) #: dict of query and body params data = {} data.update(self.args) data.update(self.form) self.data = data #: authenticated user on this request self.user = None #: authorization_code or token model instance self.credential = None #: client which sending this request self.client = None
def __call__(self, req): """Add OAuth parameters to the request. Parameters may be included from the body if the content-type is urlencoded, if no content type is set a guess is made. """ # Overwriting url is safe here as request will not modify it past # this point. content_type = to_native(req.headers.get('Content-Type', '')) if self.signature_type == SIGNATURE_TYPE_BODY: content_type = CONTENT_TYPE_FORM_URLENCODED elif not content_type and extract_params(req.body): content_type = CONTENT_TYPE_FORM_URLENCODED if CONTENT_TYPE_FORM_URLENCODED in content_type: req.headers['Content-Type'] = CONTENT_TYPE_FORM_URLENCODED req.url, headers, req.body = self.sign_request(req) elif self.force_include_body: # To allow custom clients to work on non form encoded bodies. req.url, headers, req.body = self.sign_request(req) else: # Omit body data in the signing of non form-encoded requests req.url, headers, _ = self.sign( req.method, req.url, '', req.headers) req.prepare_headers(headers) req.url = to_native_string(req.url) return req
def keycloak_revoke_token_fix(url, headers, body): """ Fix keycloak compliance issues """ params = extract_params(body) # the function prepare_revoke_token_request in authlib places the token as # the first param, so just pop it and rename it. keycloak does not conform # to rfc7009 and instead names the parameter "refresh_token", so we need to # change that here params.insert(0, ("refresh_token", params.pop(0)[1])) return url, headers, add_params_to_qs("", params)
def _render(self, uri, headers, body, oauth_params): if self.signature_type == SIGNATURE_TYPE_HEADER: headers = prepare_headers(oauth_params, headers, realm=self.realm) elif self.signature_type == SIGNATURE_TYPE_BODY: if CONTENT_TYPE_FORM_URLENCODED in headers.get('Content-Type', ''): decoded_body = extract_params(body) or [] body = prepare_form_encoded_body(oauth_params, decoded_body) headers['Content-Type'] = CONTENT_TYPE_FORM_URLENCODED elif self.signature_type == SIGNATURE_TYPE_QUERY: uri = prepare_request_uri_query(oauth_params, uri) else: raise ValueError('Unknown signature type specified.') return uri, headers, body
def prepare_request_uri_query(oauth_params, uri): """Prepare the Request URI Query. Per `section 3.5.3`_ of the spec. .. _`section 3.5.3`: http://tools.ietf.org/html/rfc5849#section-3.5.3 """ # append OAuth params to the existing set of query components sch, net, path, par, query, fra = urlparse.urlparse(uri) query = url_encode( _append_params(oauth_params, extract_params(query) or [])) return urlparse.urlunparse((sch, net, path, par, query, fra))
def __init__(self, method, uri, body=None, headers=None): InsecureTransportError.check(uri) self.method = method self.uri = uri self.body = body self.headers = headers or {} # states namespaces self.client = None self.credential = None self.grant_user = None self.query = urlparse.urlparse(uri).query self.query_params = url_decode(self.query) self.body_params = extract_params(body) or [] auth = headers.get('Authorization') self.realm = None if auth: self.auth_params = _parse_authorization_header(auth) self.realm = dict(self.auth_params).get('realm') else: self.auth_params = [] oauth_params_set = [ (SIGNATURE_TYPE_QUERY, list(_filter_oauth(self.query_params))), (SIGNATURE_TYPE_BODY, list(_filter_oauth(self.body_params))), (SIGNATURE_TYPE_HEADER, list(_filter_oauth(self.auth_params))) ] oauth_params_set = [params for params in oauth_params_set if params[1]] if len(oauth_params_set) > 1: found_types = [p[0] for p in oauth_params_set] raise DuplicatedOAuthProtocolParameterError( '"oauth_" params must come from only 1 signature type ' 'but were found in {}'.format(','.join(found_types)) ) if oauth_params_set: self.signature_type = oauth_params_set[0][0] self.oauth_params = dict(oauth_params_set[0][1]) else: self.signature_type = None self.oauth_params = {} params = [] params.extend(self.query_params) params.extend(self.body_params) params.extend(self.auth_params) self.params = params
def __init__(self, method, uri, body=None, headers=None): InsecureTransportError.check(uri) self.method = method self.uri = uri self.body = body self.headers = headers or {} self.query = urlparse.urlparse(uri).query self.query_params = url_decode(self.query) self.body_params = extract_params(body) or [] params = {} if self.query_params: params.update(dict(self.query_params)) if self.body_params: params.update(dict(self.body_params)) self.data = params self.user = None #: authorization_code or token model instance self.credential = None self.client = None
def prepare(self, method, uri, headers, body): """Add OAuth parameters to the request. Parameters may be included from the body if the content-type is urlencoded, if no content type is set, a guess is made. """ content_type = to_native(headers.get('Content-Type', '')) if self.signature_type == SIGNATURE_TYPE_BODY: content_type = CONTENT_TYPE_FORM_URLENCODED elif not content_type and extract_params(body): content_type = CONTENT_TYPE_FORM_URLENCODED if CONTENT_TYPE_FORM_URLENCODED in content_type: headers['Content-Type'] = CONTENT_TYPE_FORM_URLENCODED uri, headers, body = self.sign(method, uri, headers, body) elif self.force_include_body: # To allow custom clients to work on non form encoded bodies. uri, headers, body = self.sign(method, uri, headers, body) else: # Omit body data in the signing of non form-encoded requests uri, headers, _ = self.sign(method, uri, headers, '') body = '' return uri, headers, body
def collect_parameters(uri_query='', body=None, headers=None, exclude_oauth_signature=True, with_realm=False): """**Parameter Sources** Parameters starting with `oauth_` will be unescaped. Body parameters must be supplied as a dict, a list of 2-tuples, or a formencoded query string. Headers must be supplied as a dict. Per `section 3.4.1.3.1`_ of the spec. For example, the HTTP request:: POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded Authorization: OAuth realm="Example", oauth_consumer_key="9djdj82h48djs9d2", oauth_token="kkk9d7dh3k39sjv7", oauth_signature_method="HMAC-SHA1", oauth_timestamp="137131201", oauth_nonce="7d8f3e4a", oauth_signature="djosJKDKJSD8743243%2Fjdk33klY%3D" c2&a3=2+q contains the following (fully decoded) parameters used in the signature base sting:: +------------------------+------------------+ | Name | Value | +------------------------+------------------+ | b5 | =%3D | | a3 | a | | c@ | | | a2 | r b | | oauth_consumer_key | 9djdj82h48djs9d2 | | oauth_token | kkk9d7dh3k39sjv7 | | oauth_signature_method | HMAC-SHA1 | | oauth_timestamp | 137131201 | | oauth_nonce | 7d8f3e4a | | c2 | | | a3 | 2 q | +------------------------+------------------+ Note that the value of "b5" is "=%3D" and not "==". Both "c@" and "c2" have empty values. While the encoding rules specified in this specification for the purpose of constructing the signature base string exclude the use of a "+" character (ASCII code 43) to represent an encoded space character (ASCII code 32), this practice is widely used in "application/x-www-form-urlencoded" encoded values, and MUST be properly decoded, as demonstrated by one of the "a3" parameter instances (the "a3" parameter is used twice in this request). .. _`section 3.4.1.3.1`: http://tools.ietf.org/html/rfc5849#section-3.4.1.3.1 """ params = [] # The parameters from the following sources are collected into a single # list of name/value pairs: # * The query component of the HTTP request URI as defined by # `RFC3986, Section 3.4`_. The query component is parsed into a list # of name/value pairs by treating it as an # "application/x-www-form-urlencoded" string, separating the names # and values and decoding them as defined by # `W3C.REC-html40-19980424`_, Section 17.13.4. # # .. _`RFC3986, Section 3.4`: http://tools.ietf.org/html/rfc3986#section-3.4 # .. _`W3C.REC-html40-19980424`: http://tools.ietf.org/html/rfc5849#ref-W3C.REC-html40-19980424 if uri_query: params.extend(url_decode(uri_query)) # * The OAuth HTTP "Authorization" header field (`Section 3.5.1`_) if # present. The header's content is parsed into a list of name/value # pairs excluding the "realm" parameter if present. The parameter # values are decoded as defined by `Section 3.5.1`_. # # .. _`Section 3.5.1`: http://tools.ietf.org/html/rfc5849#section-3.5.1 if headers: for k, v in headers.items(): if k.lower() == 'authorization' and v is not None: params.extend(parse_authorization_header(v, with_realm)) # * The HTTP request entity-body, but only if all of the following # conditions are met: # * The entity-body is single-part. # # * The entity-body follows the encoding requirements of the # "application/x-www-form-urlencoded" content-type as defined by # `W3C.REC-html40-19980424`_. # * The HTTP request entity-header includes the "Content-Type" # header field set to "application/x-www-form-urlencoded". # # .._`W3C.REC-html40-19980424`: http://tools.ietf.org/html/rfc5849#ref-W3C.REC-html40-19980424 if body: params.extend(extract_params(body)) # ensure all oauth params are unescaped unescaped_params = [] for k, v in params: # The "oauth_signature" parameter MUST be excluded from the signature if exclude_oauth_signature and k == 'oauth_signature': continue if k.startswith('oauth_'): v = unescape(v) unescaped_params.append((k, v)) return unescaped_params