def limited_parse_qsl(qs, keep_blank_values=False, encoding='utf-8', errors='replace', fields_limit=None): """ Return a list of key/value tuples parsed from query string. Copied from urlparse with an additional "fields_limit" argument. Copyright (C) 2013 Python Software Foundation (see LICENSE.python). Arguments: qs: percent-encoded query string to be parsed keep_blank_values: flag indicating whether blank values in percent-encoded queries should be treated as blank strings. A true value indicates that blanks should be retained as blank strings. The default false value indicates that blank values are to be ignored and treated as if they were not included. encoding and errors: specify how to decode percent-encoded sequences into Unicode characters, as accepted by the bytes.decode() method. fields_limit: maximum number of fields parsed or an exception is raised. None means no limit and is the default. """ if fields_limit: pairs = FIELDS_MATCH.split(qs, fields_limit) if len(pairs) > fields_limit: raise TooManyFieldsSent( 'The number of GET/POST parameters exceeded ' 'settings.DATA_UPLOAD_MAX_NUMBER_FIELDS.') else: pairs = FIELDS_MATCH.split(qs) r = [] for name_value in pairs: if not name_value: continue nv = name_value.split('=', 1) if len(nv) != 2: # Handle case of a control-name with no equal sign if keep_blank_values: nv.append('') else: continue if nv[1] or keep_blank_values: name = nv[0].replace('+', ' ') #FIXED: need to convert 'name' with 'encoding' to UTF-8 before asking urllib.unquote to unquote? name = unquote(name, encoding=encoding, errors=errors) #name = unquote(name) value = nv[1].replace('+', ' ') value = unquote(value, encoding=encoding, errors=errors) #value = unquote(value) r.append((name, value)) return r
def translate_path(self, path): """Translate a /-separated PATH to the local filename syntax. Components that mean special things to the local file system (e.g. drive or directory names) are ignored. (XXX They should probably be diagnosed.) """ # abandon query parameters path = path.split('?', 1)[0] path = path.split('#', 1)[0] path = posixpath.normpath(urllib_parse.unquote(path)) words = path.split('/') words = filter(None, words) base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "chart") path = base_dir for word in words: drive, word = os.path.splitdrive(word) head, word = os.path.split(word) if word in (os.curdir, os.pardir): continue path = os.path.join(path, word) if path == os.path.join(base_dir, "data"): path = self.data_path return path
def parse_header(line): """ Parse the header into a key-value. Input (line): bytes Output: str for key/name, bytes for values which will be decoded later. """ #sanity check if line is None: return None, None plist = _parse_header_params(b';' + line) key = plist.pop(0).lower().decode('ascii') pdict = {} for p in plist: i = p.find(b'=') if i >= 0: has_encoding = False name = p[:i].strip().lower().decode('ascii') if name.endswith('*'): # Lang/encoding embedded in the value (like "filename*=UTF-8''file.ext") # http://tools.ietf.org/html/rfc2231#section-4 name = name[:-1] if p.count(b"'") == 2: has_encoding = True value = p[i + 1:].strip() if has_encoding: encoding, lang, value = value.split(b"'") #will come back to bite us. Investigate -> DONE value = unquote(value.decode(), encoding=encoding.decode()) if len(value) >= 2 and value[:1] == value[-1:] == b'"': value = value[1:-1] value = value.replace(b'\\\\', b'\\').replace(b'\\"', b'"') pdict[name] = value return key, pdict
def _display(self, root, issuer, profile): item = [] logger.debug('curdir:{}'.format(os.getcwd())) if profile: path = os.path.join(root, issuer, profile).replace(":", "%3A") argv = {"issuer": unquote(issuer), "profile": profile} path = with_or_without_slash(path) if path is None: resp = Response("No saved logs") return resp(self.environ, self.start_response) for _name in os.listdir(path): if _name.startswith("."): continue fn = os.path.join(path, _name) if os.path.isfile(fn): item.append((unquote(_name), fn)) else: if issuer: argv = {'issuer': unquote(issuer), 'profile': ''} path = os.path.join(root, issuer).replace(":", "%3A") else: argv = {'issuer': '', 'profile': ''} path = root path = with_or_without_slash(path) if path is None: resp = Response("No saved logs") return resp(self.environ, self.start_response) for _name in os.listdir(path): if _name.startswith("."): continue fn = os.path.join(path, _name) if os.path.isdir(fn): item.append((unquote(_name), os.path.join(path, _name))) resp = Response(mako_template="logs.mako", template_lookup=self.lookup, headers=[]) item.sort() argv["logs"] = item argv["base"] = self.base_url return resp(self.environ, self.start_response, **argv)
def application(environ, start_response): session = environ['beaker.session'] path = environ.get('PATH_INFO', '').lstrip('/') if path == "robots.txt": return static(environ, start_response, "static/robots.txt") if path.startswith("static/"): return static(environ, start_response, path) if path == "logout": session.invalidate() resp = SeeOther("static/log_out_message.html") return resp(environ, start_response) if path == "as": session["callback"] = True request = parse_qs(get_or_post(environ)) _cli = CONSUMER[unquote(request["authzsrv"][0])] session["client"] = _cli resp = SeeOther(_cli.begin(RP_CONF.BASE, path)) return resp(environ, start_response) if path == "rp": session["callback"] = True request = parse_qs(get_or_post(environ)) _cli = CONSUMER[unquote(request["iss"][0])] session["client"] = _cli resp = SeeOther(_cli.begin(RP_CONF.BASE, path)) return resp(environ, start_response) if path == "authz_cb": _cli = session["client"] request = get_or_post(environ) aresp = _cli.handle_authorization_response(request) rargs = {"code": aresp["code"]} atresp = _cli.do_access_token_request(request_args=rargs) #extra_args=None, http_args=None,) # Access token should be stored somewhere for later usage Token[atresp["state"]] = atresp resp = Response("Got access token: %s" % atresp["access_token"]) return resp(environ, start_response) return as_choice(environ, start_response)
def _verify_redirect_uri(self, areq): """ MUST NOT contain a fragment MAY contain query component :return: An error response if the redirect URI is faulty otherwise None """ try: _redirect_uri = unquote(areq["redirect_uri"]) part = urlparse(_redirect_uri) if part.fragment: raise URIError("Contains fragment") (_base, _query) = splitquery(_redirect_uri) if _query: _query = parse_qs(_query) match = False for regbase, rquery in self.cdb[str(areq["client_id"])][ "redirect_uris"]: if _base == regbase or _redirect_uri.startswith(regbase): # every registered query component must exist in the # redirect_uri if rquery: for key, vals in rquery.items(): assert key in _query for val in vals: assert val in _query[key] # and vice versa, every query component in the redirect_uri # must be registered if _query: if rquery is None: raise ValueError for key, vals in _query.items(): assert key in rquery for val in vals: assert val in rquery[key] match = True break if not match: raise RedirectURIError("Doesn't match any registered uris") # ignore query components that are not registered return None except Exception as err: logger.error("Faulty redirect_uri: %s" % areq["redirect_uri"]) try: _cinfo = self.cdb[str(areq["client_id"])] except KeyError: logger.info("Unknown client: %s" % areq["client_id"]) raise UnknownClient(areq["client_id"]) else: logger.info("Registered redirect_uris: %s" % _cinfo) raise RedirectURIError( "Faulty redirect_uri: %s" % areq["redirect_uri"])
def _display(self, root, issuer, profile): item = [] logger.debug('curdir:{}'.format(os.getcwd())) if profile: path = os.path.join(root, issuer, profile).replace(":", "%3A") argv = {"issuer": unquote(issuer), "profile": profile} path = with_or_without_slash(path) if path is None: return "No saved logs" for _name in os.listdir(path): if _name.startswith("."): continue fn = os.path.join(path, _name) if os.path.isfile(fn): item.append((unquote(_name), fn)) else: if issuer: argv = {'issuer': unquote(issuer), 'profile': ''} path = os.path.join(root, issuer).replace(":", "%3A") else: argv = {'issuer': '', 'profile': ''} path = root path = with_or_without_slash(path) if path is None: return b'No saved logs' for _name in os.listdir(path): if _name.startswith("."): continue fn = os.path.join(path, _name) if os.path.isdir(fn): item.append((unquote(_name), os.path.join(path, _name))) item.sort() _msg = self.pre_html['logs.html'].format( display_log=display_log(logs=item, base=self.base_url, **argv), version=self.session.tool_version ) return _msg
def uri_to_iri(uri): """ Does the opposite of iri_to_uri() """ if not uri: return uri #encode the provided bytes/string to UTF-8 #uri = str(uri).encode('utf-8') # unquote(uri) decodes to UNICODE code point, SEE: https://en.wikipedia.org/wiki/List_of_Unicode_characters # We then re-encode the UNICODE to UTF-8 for Python use. return unquote(uri).encode('utf-8')
def list_directory(self, path): """Helper to produce a directory listing (absent index.html). Return value is either a file object, or None (indicating an error). In either case, the headers are sent, making the interface the same as for send_head(). """ try: list = os.listdir(path) except os.error: self.send_error(404, "No permission to list directory") return None list.sort(key=lambda a: a.lower()) r = [] displaypath = html.escape(urllib_parse.unquote(self.path)) enc = sys.getfilesystemencoding() title = 'Directory listing for %s' % displaypath r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" ' '"http://www.w3.org/TR/html4/strict.dtd">') r.append('<html>\n<head>') r.append('<meta http-equiv="Content-Type" ' 'content="text/html; charset=%s">' % enc) r.append('<title>%s</title>\n</head>' % title) r.append('<body>\n<h1>%s</h1>' % title) r.append('<hr>\n<ul>') for name in list: fullname = os.path.join(path, name) displayname = linkname = name # Append / for directories or @ for symbolic links if os.path.isdir(fullname): displayname = name + "/" linkname = name + "/" if os.path.islink(fullname): displayname = name + "@" # Note: a link to a directory displays with @ and links with / r.append('<li><a href="%s">%s</a></li>' % (urllib_parse.quote(linkname), html.escape(displayname))) # # Use this instead: # r.append('<li><a href="%s">%s</a></li>' # % (urllib.quote(linkname), cgi.escape(displayname))) r.append('</ul>\n<hr>\n</body>\n</html>\n') encoded = '\n'.join(r).encode(enc) f = io.BytesIO() f.write(encoded) f.seek(0) self.send_response(200) self.send_header("Content-type", "text/html; charset=%s" % enc) self.send_header("Content-Length", str(len(encoded))) self.end_headers() return f
def authenticated_as(self, cookie=None, authorization="", **kwargs): """ :param cookie: A HTTP Cookie :param authorization: The HTTP Authorization header :param args: extra args :param kwargs: extra key word arguments :return: """ if authorization.startswith("Basic"): authorization = authorization[6:] (user, pwd) = base64.b64decode(authorization).split(":") user = unquote(user) self.verify_password(user, pwd) return {"uid": user}, time.time()
def translate_path(self, path): """Translate a /-separated PATH to the local filename syntax. Components that mean special things to the local file system (e.g. drive or directory names) are ignored. (XXX They should probably be diagnosed.) """ # abandon query parameters path = path.split('?',1)[0] path = path.split('#',1)[0] path = posixpath.normpath(urllib_parse.unquote(path)) words = path.split('/') words = filter(None, words) path = os.getcwd() for word in words: drive, word = os.path.splitdrive(word) head, word = os.path.split(word) if word in (os.curdir, os.pardir): continue path = os.path.join(path, word) return path
def translate_path(self, path): """Translate a /-separated PATH to the local filename syntax. Components that mean special things to the local file system (e.g. drive or directory names) are ignored. (XXX They should probably be diagnosed.) """ # abandon query parameters path = path.split('?', 1)[0] path = path.split('#', 1)[0] path = posixpath.normpath(urllib_parse.unquote(path)) words = path.split('/') words = [_f for _f in words if _f] path = os.getcwd() for word in words: drive, word = os.path.splitdrive(word) head, word = os.path.split(word) if word in (os.curdir, os.pardir): continue path = os.path.join(path, word) return path
def run_cgi(self): """Execute a CGI script.""" path = self.path dir, rest = self.cgi_info i = path.find('/', len(dir) + 1) while i >= 0: nextdir = path[:i] nextrest = path[i+1:] scriptdir = self.translate_path(nextdir) if os.path.isdir(scriptdir): dir, rest = nextdir, nextrest i = path.find('/', len(dir) + 1) else: break # find an explicit query string, if present. i = rest.rfind('?') if i >= 0: rest, query = rest[:i], rest[i+1:] else: query = '' # dissect the part after the directory name into a script name & # a possible additional path, to be stored in PATH_INFO. i = rest.find('/') if i >= 0: script, rest = rest[:i], rest[i:] else: script, rest = rest, '' scriptname = dir + '/' + script scriptfile = self.translate_path(scriptname) if not os.path.exists(scriptfile): self.send_error(404, "No such CGI script (%r)" % scriptname) return if not os.path.isfile(scriptfile): self.send_error(403, "CGI script is not a plain file (%r)" % scriptname) return ispy = self.is_python(scriptname) if self.have_fork or not ispy: if not self.is_executable(scriptfile): self.send_error(403, "CGI script is not executable (%r)" % scriptname) return # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html # XXX Much of the following could be prepared ahead of time! env = copy.deepcopy(os.environ) env['SERVER_SOFTWARE'] = self.version_string() env['SERVER_NAME'] = self.server.server_name env['GATEWAY_INTERFACE'] = 'CGI/1.1' env['SERVER_PROTOCOL'] = self.protocol_version env['SERVER_PORT'] = str(self.server.server_port) env['REQUEST_METHOD'] = self.command uqrest = urllib_parse.unquote(rest) env['PATH_INFO'] = uqrest env['PATH_TRANSLATED'] = self.translate_path(uqrest) env['SCRIPT_NAME'] = scriptname if query: env['QUERY_STRING'] = query env['REMOTE_ADDR'] = self.client_address[0] authorization = self.headers.get("authorization") if authorization: authorization = authorization.split() if len(authorization) == 2: import base64, binascii env['AUTH_TYPE'] = authorization[0] if authorization[0].lower() == "basic": try: authorization = authorization[1].encode('ascii') if utils.PY3: # In Py3.3, was: authorization = base64.decodebytes(authorization).\ decode('ascii') else: # Backport to Py2.7: authorization = base64.decodestring(authorization).\ decode('ascii') except (binascii.Error, UnicodeError): pass else: authorization = authorization.split(':') if len(authorization) == 2: env['REMOTE_USER'] = authorization[0] # XXX REMOTE_IDENT if self.headers.get('content-type') is None: env['CONTENT_TYPE'] = self.headers.get_content_type() else: env['CONTENT_TYPE'] = self.headers['content-type'] length = self.headers.get('content-length') if length: env['CONTENT_LENGTH'] = length referer = self.headers.get('referer') if referer: env['HTTP_REFERER'] = referer accept = [] for line in self.headers.getallmatchingheaders('accept'): if line[:1] in "\t\n\r ": accept.append(line.strip()) else: accept = accept + line[7:].split(',') env['HTTP_ACCEPT'] = ','.join(accept) ua = self.headers.get('user-agent') if ua: env['HTTP_USER_AGENT'] = ua co = filter(None, self.headers.get_all('cookie', [])) cookie_str = ', '.join(co) if cookie_str: env['HTTP_COOKIE'] = cookie_str # XXX Other HTTP_* headers # Since we're setting the env in the parent, provide empty # values to override previously set values for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH', 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'): env.setdefault(k, "") self.send_response(200, "Script output follows") self.flush_headers() decoded_query = query.replace('+', ' ') if self.have_fork: # Unix -- fork as we should args = [script] if '=' not in decoded_query: args.append(decoded_query) nobody = nobody_uid() self.wfile.flush() # Always flush before forking pid = os.fork() if pid != 0: # Parent pid, sts = os.waitpid(pid, 0) # throw away additional data [see bug #427345] while select.select([self.rfile], [], [], 0)[0]: if not self.rfile.read(1): break if sts: self.log_error("CGI script exit status %#x", sts) return # Child try: try: os.setuid(nobody) except os.error: pass os.dup2(self.rfile.fileno(), 0) os.dup2(self.wfile.fileno(), 1) os.execve(scriptfile, args, env) except: self.server.handle_error(self.request, self.client_address) os._exit(127) else: # Non-Unix -- use subprocess import subprocess cmdline = [scriptfile] if self.is_python(scriptfile): interp = sys.executable if interp.lower().endswith("w.exe"): # On Windows, use python.exe, not pythonw.exe interp = interp[:-5] + interp[-4:] cmdline = [interp, '-u'] + cmdline if '=' not in query: cmdline.append(query) self.log_message("command: %s", subprocess.list2cmdline(cmdline)) try: nbytes = int(length) except (TypeError, ValueError): nbytes = 0 p = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env = env ) if self.command.lower() == "post" and nbytes > 0: data = self.rfile.read(nbytes) else: data = None # throw away additional data [see bug #427345] while select.select([self.rfile._sock], [], [], 0)[0]: if not self.rfile._sock.recv(1): break stdout, stderr = p.communicate(data) self.wfile.write(stdout) if stderr: self.log_error('%s', stderr) p.stderr.close() p.stdout.close() status = p.returncode if status: self.log_error("CGI script exit status %#x", status) else: self.log_message("CGI script exited OK")
def run_cgi(self): """Execute a CGI script.""" path = self.path dir, rest = self.cgi_info i = path.find('/', len(dir) + 1) while i >= 0: nextdir = path[:i] nextrest = path[i + 1:] scriptdir = self.translate_path(nextdir) if os.path.isdir(scriptdir): dir, rest = nextdir, nextrest i = path.find('/', len(dir) + 1) else: break # find an explicit query string, if present. i = rest.rfind('?') if i >= 0: rest, query = rest[:i], rest[i + 1:] else: query = '' # dissect the part after the directory name into a script name & # a possible additional path, to be stored in PATH_INFO. i = rest.find('/') if i >= 0: script, rest = rest[:i], rest[i:] else: script, rest = rest, '' scriptname = dir + '/' + script scriptfile = self.translate_path(scriptname) if not os.path.exists(scriptfile): self.send_error(404, "No such CGI script (%r)" % scriptname) return if not os.path.isfile(scriptfile): self.send_error(403, "CGI script is not a plain file (%r)" % scriptname) return ispy = self.is_python(scriptname) if self.have_fork or not ispy: if not self.is_executable(scriptfile): self.send_error( 403, "CGI script is not executable (%r)" % scriptname) return # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html # XXX Much of the following could be prepared ahead of time! env = copy.deepcopy(os.environ) env['SERVER_SOFTWARE'] = self.version_string() env['SERVER_NAME'] = self.server.server_name env['GATEWAY_INTERFACE'] = 'CGI/1.1' env['SERVER_PROTOCOL'] = self.protocol_version env['SERVER_PORT'] = str(self.server.server_port) env['REQUEST_METHOD'] = self.command uqrest = urllib_parse.unquote(rest) env['PATH_INFO'] = uqrest env['PATH_TRANSLATED'] = self.translate_path(uqrest) env['SCRIPT_NAME'] = scriptname if query: env['QUERY_STRING'] = query env['REMOTE_ADDR'] = self.client_address[0] authorization = self.headers.get("authorization") if authorization: authorization = authorization.split() if len(authorization) == 2: import base64, binascii env['AUTH_TYPE'] = authorization[0] if authorization[0].lower() == "basic": try: authorization = authorization[1].encode('ascii') if utils.PY3: # In Py3.3, was: authorization = base64.decodebytes(authorization).\ decode('ascii') else: # Backport to Py2.7: authorization = base64.decodestring(authorization).\ decode('ascii') except (binascii.Error, UnicodeError): pass else: authorization = authorization.split(':') if len(authorization) == 2: env['REMOTE_USER'] = authorization[0] # XXX REMOTE_IDENT if self.headers.get('content-type') is None: env['CONTENT_TYPE'] = self.headers.get_content_type() else: env['CONTENT_TYPE'] = self.headers['content-type'] length = self.headers.get('content-length') if length: env['CONTENT_LENGTH'] = length referer = self.headers.get('referer') if referer: env['HTTP_REFERER'] = referer accept = [] for line in self.headers.getallmatchingheaders('accept'): if line[:1] in "\t\n\r ": accept.append(line.strip()) else: accept = accept + line[7:].split(',') env['HTTP_ACCEPT'] = ','.join(accept) ua = self.headers.get('user-agent') if ua: env['HTTP_USER_AGENT'] = ua co = filter(None, self.headers.get_all('cookie', [])) cookie_str = ', '.join(co) if cookie_str: env['HTTP_COOKIE'] = cookie_str # XXX Other HTTP_* headers # Since we're setting the env in the parent, provide empty # values to override previously set values for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH', 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'): env.setdefault(k, "") self.send_response(200, "Script output follows") self.flush_headers() decoded_query = query.replace('+', ' ') if self.have_fork: # Unix -- fork as we should args = [script] if '=' not in decoded_query: args.append(decoded_query) nobody = nobody_uid() self.wfile.flush() # Always flush before forking pid = os.fork() if pid != 0: # Parent pid, sts = os.waitpid(pid, 0) # throw away additional data [see bug #427345] while select.select([self.rfile], [], [], 0)[0]: if not self.rfile.read(1): break if sts: self.log_error("CGI script exit status %#x", sts) return # Child try: try: os.setuid(nobody) except os.error: pass os.dup2(self.rfile.fileno(), 0) os.dup2(self.wfile.fileno(), 1) os.execve(scriptfile, args, env) except: self.server.handle_error(self.request, self.client_address) os._exit(127) else: # Non-Unix -- use subprocess import subprocess cmdline = [scriptfile] if self.is_python(scriptfile): interp = sys.executable if interp.lower().endswith("w.exe"): # On Windows, use python.exe, not pythonw.exe interp = interp[:-5] + interp[-4:] cmdline = [interp, '-u'] + cmdline if '=' not in query: cmdline.append(query) self.log_message("command: %s", subprocess.list2cmdline(cmdline)) try: nbytes = int(length) except (TypeError, ValueError): nbytes = 0 p = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) if self.command.lower() == "post" and nbytes > 0: data = self.rfile.read(nbytes) else: data = None # throw away additional data [see bug #427345] while select.select([self.rfile._sock], [], [], 0)[0]: if not self.rfile._sock.recv(1): break stdout, stderr = p.communicate(data) self.wfile.write(stdout) if stderr: self.log_error('%s', stderr) p.stderr.close() p.stdout.close() status = p.returncode if status: self.log_error("CGI script exit status %#x", status) else: self.log_message("CGI script exited OK")
def urlunquote(quoted_url): """ A legacy compatibility wrapper to Python's urllib.parse.unquote() function. (was used for unicode handling on Python 2) """ return unquote(quoted_url)
def _verify_redirect_uri(self, areq): """ MUST NOT contain a fragment MAY contain query component :return: An error response if the redirect URI is faulty otherwise None """ try: _redirect_uri = unquote(areq["redirect_uri"]) part = urlparse(_redirect_uri) if part.fragment: raise URIError("Contains fragment") (_base, _query) = splitquery(_redirect_uri) if _query: _query = parse_qs(_query) match = False for regbase, rquery in self.cdb[str(areq["client_id"])][ "redirect_uris"]: # The URI MUST exactly match one of the Redirection URI if _base == regbase: # every registered query component must exist in the # redirect_uri if rquery: for key, vals in rquery.items(): assert key in _query for val in vals: assert val in _query[key] # and vice versa, every query component in the redirect_uri # must be registered if _query: if rquery is None: raise ValueError for key, vals in _query.items(): assert key in rquery for val in vals: assert val in rquery[key] match = True break if not match: raise RedirectURIError("Doesn't match any registered uris") # ignore query components that are not registered return None except Exception: logger.error("Faulty redirect_uri: %s" % areq["redirect_uri"]) try: _cinfo = self.cdb[str(areq["client_id"])] except KeyError: try: cid = areq["client_id"] except KeyError: logger.error('No client id found') raise UnknownClient('No client_id provided') else: logger.info("Unknown client: %s" % cid) raise UnknownClient(areq["client_id"]) else: logger.info("Registered redirect_uris: %s" % sanitize(_cinfo)) raise RedirectURIError( "Faulty redirect_uri: %s" % areq["redirect_uri"])
def _verify_redirect_uri(self, areq): """ MUST NOT contain a fragment MAY contain query component :return: An error response if the redirect URI is faulty otherwise None """ try: _redirect_uri = unquote(areq["redirect_uri"]) part = urlparse(_redirect_uri) if part.fragment: raise URIError("Contains fragment") (_base, _query) = splitquery(_redirect_uri) if _query: _query = parse_qs(_query) match = False for regbase, rquery in self.cdb[str(areq["client_id"])]["redirect_uris"]: # The URI MUST exactly match one of the Redirection URI if _base != regbase: continue if not rquery and not _query: match = True break if not rquery or not _query: continue # every registered query component must exist in the # redirect_uri is_match_query = True for key, vals in _query.items(): if key not in rquery: is_match_query = False break for val in vals: if val not in rquery[key]: is_match_query = False break if not is_match_query: break if not is_match_query: continue match = True break if not match: raise RedirectURIError("Doesn't match any registered uris") # ignore query components that are not registered return None except Exception: logger.error("Faulty redirect_uri: %s" % areq["redirect_uri"]) try: _cinfo = self.cdb[str(areq["client_id"])] except KeyError: try: cid = areq["client_id"] except KeyError: logger.error('No client id found') raise UnknownClient('No client_id provided') else: logger.info("Unknown client: %s" % cid) raise UnknownClient(areq["client_id"]) else: logger.info("Registered redirect_uris: %s" % sanitize(_cinfo)) raise RedirectURIError( "Faulty redirect_uri: %s" % areq["redirect_uri"])