def is_authenticated(self, request, **kwargs): user = AnonymousUser() authenticated = False if 'HTTP_AUTHORIZATION' in request.META: auth_header = self.extract_auth_header(request) if auth_header: access_token = get_token_from_auth_header(auth_header) if self.token_is_valid(access_token): obj = AccessToken.objects.get(token=access_token) user = obj.user authenticated = True request.user = user return authenticated
def get_headers(request, url, raw_url): headers = {} cookies = None csrftoken = None if settings.SESSION_COOKIE_NAME in request.COOKIES and is_safe_url( url=raw_url, host=url.hostname): cookies = request.META["HTTP_COOKIE"] for cook in request.COOKIES: name = str(cook) value = request.COOKIES.get(name) if name == 'csrftoken': csrftoken = value cook = "%s=%s" % (name, value) cookies = cook if not cookies else (cookies + '; ' + cook) csrftoken = get_token(request) if not csrftoken else csrftoken if csrftoken: headers['X-Requested-With'] = "XMLHttpRequest" headers['X-CSRFToken'] = csrftoken cook = "%s=%s" % ('csrftoken', csrftoken) cookies = cook if not cookies else (cookies + '; ' + cook) if cookies: if 'JSESSIONID' in request.session and request.session['JSESSIONID']: cookies = cookies + '; JSESSIONID=' + \ request.session['JSESSIONID'] headers['Cookie'] = cookies if request.method in ("POST", "PUT") and "CONTENT_TYPE" in request.META: headers["Content-Type"] = request.META["CONTENT_TYPE"] access_token = None # we give precedence to obtained from Aithorization headers if 'HTTP_AUTHORIZATION' in request.META: auth_header = request.META.get('HTTP_AUTHORIZATION', request.META.get('HTTP_AUTHORIZATION2')) if auth_header: access_token = get_token_from_auth_header(auth_header) # otherwise we check if a session is active elif request and request.user.is_authenticated: access_token = get_token_object_from_session(request.session) # we extend the token in case the session is active but the token expired if access_token and access_token.is_expired(): extend_token(access_token) if access_token: headers['Authorization'] = 'Bearer %s' % access_token site_url = urlsplit(settings.SITEURL) pragma = "no-cache" referer = request.META[ "HTTP_REFERER"] if "HTTP_REFERER" in request.META else \ "{scheme}://{netloc}/".format(scheme=site_url.scheme, netloc=site_url.netloc) encoding = request.META[ "HTTP_ACCEPT_ENCODING"] if "HTTP_ACCEPT_ENCODING" in request.META else "gzip" headers.update({ "Pragma": pragma, "Referer": referer, "Accept-encoding": encoding, }) return (headers, access_token)
def proxy(request, url=None, response_callback=None, sec_chk_hosts=True, sec_chk_rules=True, **kwargs): # Security rules and settings PROXY_ALLOWED_HOSTS = getattr(settings, 'PROXY_ALLOWED_HOSTS', ()) # Sanity url checks if 'url' not in request.GET and not url: return HttpResponse( "The proxy service requires a URL-encoded URL as a parameter.", status=400, content_type="text/plain") raw_url = url or request.GET['url'] raw_url = urljoin(settings.SITEURL, raw_url) if raw_url.startswith("/") else raw_url url = urlsplit(raw_url) locator = str(url.path) if url.query != "": locator += '?' + url.query if url.fragment != "": locator += '#' + url.fragment # White-Black Listing Hosts if sec_chk_hosts and not settings.DEBUG: site_url = urlsplit(settings.SITEURL) if site_url.hostname not in PROXY_ALLOWED_HOSTS: PROXY_ALLOWED_HOSTS += (site_url.hostname, ) if check_ogc_backend(geoserver.BACKEND_PACKAGE): from geonode.geoserver.helpers import ogc_server_settings hostname = ( ogc_server_settings.hostname, ) if ogc_server_settings else () if hostname not in PROXY_ALLOWED_HOSTS: PROXY_ALLOWED_HOSTS += hostname if url.query and ows_regexp.match(url.query): ows_tokens = ows_regexp.match(url.query).groups() if len( ows_tokens ) == 4 and 'version' == ows_tokens[0] and StrictVersion( ows_tokens[1]) >= StrictVersion("1.0.0") and StrictVersion( ows_tokens[1] ) <= StrictVersion("3.0.0") and ows_tokens[2].lower() in ( 'getcapabilities') and ows_tokens[3].upper() in ( 'OWS', 'WCS', 'WFS', 'WMS', 'WPS', 'CSW'): if url.hostname not in PROXY_ALLOWED_HOSTS: PROXY_ALLOWED_HOSTS += (url.hostname, ) if not validate_host(url.hostname, PROXY_ALLOWED_HOSTS): return HttpResponse( "DEBUG is set to False but the host of the path provided to the proxy service" " is not in the PROXY_ALLOWED_HOSTS setting.", status=403, content_type="text/plain") # Security checks based on rules; allow only specific requests if sec_chk_rules: # TODO: Not yet implemented pass # Collecting headers and cookies headers = {} cookies = None csrftoken = None if settings.SESSION_COOKIE_NAME in request.COOKIES and is_safe_url( url=raw_url, host=url.hostname): cookies = request.META["HTTP_COOKIE"] for cook in request.COOKIES: name = str(cook) value = request.COOKIES.get(name) if name == 'csrftoken': csrftoken = value cook = "%s=%s" % (name, value) cookies = cook if not cookies else (cookies + '; ' + cook) csrftoken = get_token(request) if not csrftoken else csrftoken if csrftoken: headers['X-Requested-With'] = "XMLHttpRequest" headers['X-CSRFToken'] = csrftoken cook = "%s=%s" % ('csrftoken', csrftoken) cookies = cook if not cookies else (cookies + '; ' + cook) if cookies: if 'JSESSIONID' in request.session and request.session['JSESSIONID']: cookies = cookies + '; JSESSIONID=' + \ request.session['JSESSIONID'] headers['Cookie'] = cookies if request.method in ("POST", "PUT") and "CONTENT_TYPE" in request.META: headers["Content-Type"] = request.META["CONTENT_TYPE"] access_token = None # we give precedence to obtained from Aithorization headers if 'HTTP_AUTHORIZATION' in request.META: auth_header = request.META.get('HTTP_AUTHORIZATION', request.META.get('HTTP_AUTHORIZATION2')) if auth_header: access_token = get_token_from_auth_header(auth_header) # otherwise we check if a session is active elif request and request.user.is_authenticated: access_token = get_token_object_from_session(request.session) # we extend the token in case the session is active but the token expired if access_token and access_token.is_expired(): extend_token(access_token) if access_token: headers['Authorization'] = 'Bearer %s' % access_token site_url = urlsplit(settings.SITEURL) pragma = "no-cache" referer = request.META[ "HTTP_REFERER"] if "HTTP_REFERER" in request.META else \ "{scheme}://{netloc}/".format(scheme=site_url.scheme, netloc=site_url.netloc) encoding = request.META[ "HTTP_ACCEPT_ENCODING"] if "HTTP_ACCEPT_ENCODING" in request.META else "gzip" headers.update({ "Pragma": pragma, "Referer": referer, "Accept-encoding": encoding, }) if url.scheme == 'https': conn = HTTPSConnection(url.hostname, url.port) else: conn = HTTPConnection(url.hostname, url.port) parsed = urlparse(raw_url) parsed._replace(path=locator.encode('utf8')) _url = parsed.geturl() if request.method == "GET" and access_token and 'access_token' not in _url: query_separator = '&' if '?' in _url else '?' _url = ('%s%saccess_token=%s' % (_url, query_separator, access_token)) conn.request(request.method, _url, request.body, headers) response = conn.getresponse() content = response.read() status = response.status content_type = response.getheader("Content-Type", "text/plain") # decompress GZipped responses if not enabled if content and response.getheader('Content-Encoding') == 'gzip': from StringIO import StringIO import gzip buf = StringIO(content) f = gzip.GzipFile(fileobj=buf) content = f.read() if response_callback: kwargs = {} if not kwargs else kwargs kwargs.update({ 'response': response, 'content': content, 'status': status, 'content_type': content_type }) return response_callback(**kwargs) else: # If we get a redirect, let's add a useful message. if status in (301, 302, 303, 307): _response = HttpResponse( ('This proxy does not support redirects. The server in "%s" ' 'asked for a redirect to "%s"' % (url, response.getheader('Location'))), status=status, content_type=content_type) _response['Location'] = response.getheader('Location') return _response else: return HttpResponse(content=content, status=status, content_type=content_type)
def get_headers(request, url, raw_url): headers = {} cookies = None csrftoken = None if settings.SESSION_COOKIE_NAME in request.COOKIES and is_safe_url( url=raw_url, host=url.hostname): cookies = request.META["HTTP_COOKIE"] for cook in request.COOKIES: name = str(cook) value = request.COOKIES.get(name) if name == 'csrftoken': csrftoken = value cook = "%s=%s" % (name, value) cookies = cook if not cookies else (cookies + '; ' + cook) csrftoken = get_token(request) if not csrftoken else csrftoken if csrftoken: headers['X-Requested-With'] = "XMLHttpRequest" headers['X-CSRFToken'] = csrftoken cook = "%s=%s" % ('csrftoken', csrftoken) cookies = cook if not cookies else (cookies + '; ' + cook) if cookies: if 'JSESSIONID' in request.session and request.session['JSESSIONID']: cookies = cookies + '; JSESSIONID=' + \ request.session['JSESSIONID'] headers['Cookie'] = cookies if request.method in ("POST", "PUT") and "CONTENT_TYPE" in request.META: headers["Content-Type"] = request.META["CONTENT_TYPE"] access_token = None # we give precedence to obtained from Aithorization headers if 'HTTP_AUTHORIZATION' in request.META: auth_header = request.META.get( 'HTTP_AUTHORIZATION', request.META.get('HTTP_AUTHORIZATION2')) if auth_header: access_token = get_token_from_auth_header(auth_header) # otherwise we check if a session is active elif request and request.user.is_authenticated: access_token = get_token_object_from_session(request.session) # we extend the token in case the session is active but the token expired if access_token and access_token.is_expired(): extend_token(access_token) if access_token: headers['Authorization'] = 'Bearer %s' % access_token site_url = urlsplit(settings.SITEURL) pragma = "no-cache" referer = request.META[ "HTTP_REFERER"] if "HTTP_REFERER" in request.META else \ "{scheme}://{netloc}/".format(scheme=site_url.scheme, netloc=site_url.netloc) encoding = request.META["HTTP_ACCEPT_ENCODING"] if "HTTP_ACCEPT_ENCODING" in request.META else "gzip" headers.update({"Pragma": pragma, "Referer": referer, "Accept-encoding": encoding, }) return (headers, access_token)
def proxy(request, url=None, response_callback=None, sec_chk_hosts=True, sec_chk_rules=True, timeout=None, allowed_hosts=[], headers=None, access_token=None, **kwargs): # Request default timeout if not timeout: timeout = TIMEOUT # Security rules and settings PROXY_ALLOWED_HOSTS = getattr(settings, 'PROXY_ALLOWED_HOSTS', ()) # Sanity url checks if 'url' not in request.GET and not url: return HttpResponse( "The proxy service requires a URL-encoded URL as a parameter.", status=400, content_type="text/plain") raw_url = url or request.GET['url'] raw_url = urljoin(settings.SITEURL, raw_url) if raw_url.startswith("/") else raw_url url = urlsplit(raw_url) scheme = str(url.scheme) locator = str(url.path) if url.query != "": locator += f"?{url.query}" if url.fragment != "": locator += f"#{url.fragment}" # White-Black Listing Hosts site_url = urlsplit(settings.SITEURL) if sec_chk_hosts and not settings.DEBUG: # Attach current SITEURL if site_url.hostname not in PROXY_ALLOWED_HOSTS: PROXY_ALLOWED_HOSTS += (site_url.hostname, ) # Attach current hostname if check_ogc_backend(geoserver.BACKEND_PACKAGE): from geonode.geoserver.helpers import ogc_server_settings hostname = ( ogc_server_settings.hostname, ) if ogc_server_settings else () if hostname not in PROXY_ALLOWED_HOSTS: PROXY_ALLOWED_HOSTS += hostname # Check OWS regexp if url.query and ows_regexp.match(url.query): ows_tokens = ows_regexp.match(url.query).groups() if len( ows_tokens ) == 4 and 'version' == ows_tokens[0] and StrictVersion( ows_tokens[1]) >= StrictVersion("1.0.0") and StrictVersion( ows_tokens[1] ) <= StrictVersion("3.0.0") and ows_tokens[2].lower() in ( 'getcapabilities') and ows_tokens[3].upper() in ( 'OWS', 'WCS', 'WFS', 'WMS', 'WPS', 'CSW'): if url.hostname not in PROXY_ALLOWED_HOSTS: PROXY_ALLOWED_HOSTS += (url.hostname, ) # Check Remote Services base_urls from geonode.services.models import Service for _s in Service.objects.all(): _remote_host = urlsplit(_s.base_url).hostname PROXY_ALLOWED_HOSTS += (_remote_host, ) if not validate_host(url.hostname, PROXY_ALLOWED_HOSTS): return HttpResponse( "DEBUG is set to False but the host of the path provided to the proxy service" " is not in the PROXY_ALLOWED_HOSTS setting.", status=403, content_type="text/plain") # Security checks based on rules; allow only specific requests if sec_chk_rules: # TODO: Not yet implemented pass # Collecting headers and cookies if not headers: headers, access_token = get_headers(request, url, raw_url, allowed_hosts=allowed_hosts) if not access_token: auth_header = None if 'Authorization' in headers: auth_header = headers['Authorization'] elif 'HTTP_AUTHORIZATION' in request.META: auth_header = request.META.get( 'HTTP_AUTHORIZATION', request.META.get('HTTP_AUTHORIZATION2')) if auth_header: access_token = get_token_from_auth_header( auth_header, create_if_not_exists=True) user = get_auth_user(access_token) # Inject access_token if necessary parsed = urlparse(raw_url) parsed._replace(path=locator.encode('utf8')) if parsed.netloc == site_url.netloc and scheme != site_url.scheme: parsed = parsed._replace(scheme=site_url.scheme) _url = parsed.geturl() # Some clients / JS libraries generate URLs with relative URL paths, e.g. # "http://host/path/path/../file.css", which the requests library cannot # currently handle (https://github.com/kennethreitz/requests/issues/2982). # We parse and normalise such URLs into absolute paths before attempting # to proxy the request. _url = URL.from_text(_url).normalize().to_text() if request.method == "GET" and access_token and 'access_token' not in _url: query_separator = '&' if '?' in _url else '?' _url = f'{_url}{query_separator}access_token={access_token}' _data = request.body.decode('utf-8') # Avoid translating local geoserver calls into external ones if check_ogc_backend(geoserver.BACKEND_PACKAGE): from geonode.geoserver.helpers import ogc_server_settings _url = _url.replace(f'{settings.SITEURL}geoserver', ogc_server_settings.LOCATION.rstrip('/')) _data = _data.replace(f'{settings.SITEURL}geoserver', ogc_server_settings.LOCATION.rstrip('/')) response, content = http_client.request(_url, method=request.method, data=_data.encode('utf-8'), headers=headers, timeout=timeout, user=user) if response is None: return HttpResponse(content=content, reason=content, status=500) content = response.content or response.reason status = response.status_code response_headers = response.headers content_type = response.headers.get('Content-Type') if status >= 400: _response = HttpResponse(content=content, reason=content, status=status, content_type=content_type) return fetch_response_headers(_response, response_headers) # decompress GZipped responses if not enabled # if content and response and response.getheader('Content-Encoding') == 'gzip': if content and content_type and content_type == 'gzip': buf = io.BytesIO(content) with gzip.GzipFile(fileobj=buf) as f: content = f.read() buf.close() PLAIN_CONTENT_TYPES = ['text', 'plain', 'html', 'json', 'xml', 'gml'] for _ct in PLAIN_CONTENT_TYPES: if content_type and _ct in content_type and not isinstance( content, str): try: content = content.decode() break except Exception: pass if response and response_callback: kwargs = {} if not kwargs else kwargs kwargs.update({ 'response': response, 'content': content, 'status': status, 'response_headers': response_headers, 'content_type': content_type }) return response_callback(**kwargs) else: # If we get a redirect, let's add a useful message. if status and status in (301, 302, 303, 307): _response = HttpResponse(( f"This proxy does not support redirects. The server in '{url}' " f"asked for a redirect to '{response.getheader('Location')}'"), status=status, content_type=content_type) _response['Location'] = response.getheader('Location') return fetch_response_headers(_response, response_headers) else: def _get_message(text): _s = text if isinstance(text, bytes): _s = text.decode("utf-8", "replace") try: found = re.search('<b>Message</b>(.+?)</p>', _s).group(1).strip() except Exception: found = _s return found _response = HttpResponse( content=content, reason=_get_message(content) if status not in (200, 201) else None, status=status, content_type=content_type) return fetch_response_headers(_response, response_headers)
def proxy(request, url=None, response_callback=None, sec_chk_hosts=True, sec_chk_rules=True, **kwargs): # Security rules and settings PROXY_ALLOWED_HOSTS = getattr(settings, 'PROXY_ALLOWED_HOSTS', ()) # Sanity url checks if 'url' not in request.GET and not url: return HttpResponse("The proxy service requires a URL-encoded URL as a parameter.", status=400, content_type="text/plain" ) raw_url = url or request.GET['url'] raw_url = urljoin( settings.SITEURL, raw_url) if raw_url.startswith("/") else raw_url url = urlsplit(raw_url) locator = str(url.path) if url.query != "": locator += '?' + url.query if url.fragment != "": locator += '#' + url.fragment # White-Black Listing Hosts if sec_chk_hosts and not settings.DEBUG: site_url = urlsplit(settings.SITEURL) if site_url.hostname not in PROXY_ALLOWED_HOSTS: PROXY_ALLOWED_HOSTS += (site_url.hostname, ) if check_ogc_backend(geoserver.BACKEND_PACKAGE): from geonode.geoserver.helpers import ogc_server_settings hostname = ( ogc_server_settings.hostname, ) if ogc_server_settings else () if hostname not in PROXY_ALLOWED_HOSTS: PROXY_ALLOWED_HOSTS += hostname if url.query and ows_regexp.match(url.query): ows_tokens = ows_regexp.match(url.query).groups() if len(ows_tokens) == 4 and 'version' == ows_tokens[0] and StrictVersion( ows_tokens[1]) >= StrictVersion("1.0.0") and StrictVersion( ows_tokens[1]) <= StrictVersion("3.0.0") and ows_tokens[2].lower() in ( 'getcapabilities') and ows_tokens[3].upper() in ('OWS', 'WCS', 'WFS', 'WMS', 'WPS', 'CSW'): if url.hostname not in PROXY_ALLOWED_HOSTS: PROXY_ALLOWED_HOSTS += (url.hostname, ) if not validate_host( url.hostname, PROXY_ALLOWED_HOSTS): return HttpResponse("DEBUG is set to False but the host of the path provided to the proxy service" " is not in the PROXY_ALLOWED_HOSTS setting.", status=403, content_type="text/plain" ) # Security checks based on rules; allow only specific requests if sec_chk_rules: # TODO: Not yet implemented pass # Collecting headers and cookies headers = {} cookies = None csrftoken = None if settings.SESSION_COOKIE_NAME in request.COOKIES and is_safe_url( url=raw_url, host=url.hostname): cookies = request.META["HTTP_COOKIE"] for cook in request.COOKIES: name = str(cook) value = request.COOKIES.get(name) if name == 'csrftoken': csrftoken = value cook = "%s=%s" % (name, value) cookies = cook if not cookies else (cookies + '; ' + cook) csrftoken = get_token(request) if not csrftoken else csrftoken if csrftoken: headers['X-Requested-With'] = "XMLHttpRequest" headers['X-CSRFToken'] = csrftoken cook = "%s=%s" % ('csrftoken', csrftoken) cookies = cook if not cookies else (cookies + '; ' + cook) if cookies: if 'JSESSIONID' in request.session and request.session['JSESSIONID']: cookies = cookies + '; JSESSIONID=' + \ request.session['JSESSIONID'] headers['Cookie'] = cookies if request.method in ("POST", "PUT") and "CONTENT_TYPE" in request.META: headers["Content-Type"] = request.META["CONTENT_TYPE"] access_token = None # we give precedence to obtained from Aithorization headers if 'HTTP_AUTHORIZATION' in request.META: auth_header = request.META.get( 'HTTP_AUTHORIZATION', request.META.get('HTTP_AUTHORIZATION2')) if auth_header: access_token = get_token_from_auth_header(auth_header) # otherwise we check if a session is active elif request and request.user.is_authenticated: access_token = get_token_object_from_session(request.session) # we extend the token in case the session is active but the token expired if access_token and access_token.is_expired(): extend_token(access_token) if access_token: headers['Authorization'] = 'Bearer %s' % access_token site_url = urlsplit(settings.SITEURL) pragma = "no-cache" referer = request.META[ "HTTP_REFERER"] if "HTTP_REFERER" in request.META else \ "{scheme}://{netloc}/".format(scheme=site_url.scheme, netloc=site_url.netloc) encoding = request.META["HTTP_ACCEPT_ENCODING"] if "HTTP_ACCEPT_ENCODING" in request.META else "gzip" headers.update({"Pragma": pragma, "Referer": referer, "Accept-encoding": encoding, }) if url.scheme == 'https': conn = HTTPSConnection(url.hostname, url.port) else: conn = HTTPConnection(url.hostname, url.port) parsed = urlparse(raw_url) parsed._replace(path=locator.encode('utf8')) _url = parsed.geturl() if request.method == "GET" and access_token and 'access_token' not in _url: query_separator = '&' if '?' in _url else '?' _url = ('%s%saccess_token=%s' % (_url, query_separator, access_token)) conn.request(request.method, _url, request.body, headers) response = conn.getresponse() content = response.read() status = response.status content_type = response.getheader("Content-Type", "text/plain") # decompress GZipped responses if not enabled if content and response.getheader('Content-Encoding') == 'gzip': from StringIO import StringIO import gzip buf = StringIO(content) f = gzip.GzipFile(fileobj=buf) content = f.read() if response_callback: kwargs = {} if not kwargs else kwargs kwargs.update({ 'response': response, 'content': content, 'status': status, 'content_type': content_type }) return response_callback(**kwargs) else: # If we get a redirect, let's add a useful message. if status in (301, 302, 303, 307): _response = HttpResponse(('This proxy does not support redirects. The server in "%s" ' 'asked for a redirect to "%s"' % (url, response.getheader('Location'))), status=status, content_type=content_type ) _response['Location'] = response.getheader('Location') return _response else: return HttpResponse( content=content, status=status, content_type=content_type)