def test_compare(self): # ALLOWED_HOSTS with django.http.request.validate_host self.assertTrue( validate_host("192.168.1.2", self.fnmatch_ips) ) self.assertFalse( validate_host("10.0.1.2", self.fnmatch_ips) )
def test_iterable(self): """ this is what Django does internally in django.http.request allowed_cls is synonymous with settings.ALLOWED_HOSTS """ allowed_cls = AllowedSites(defaults=['yay.com']) with self.assertNumQueries(1): self.assertTrue(validate_host('example.com', allowed_cls)) with self.assertNumQueries(1): self.assertTrue(validate_host('example.org', allowed_cls)) with self.assertNumQueries(1): self.assertFalse(validate_host('djangoproject.com', allowed_cls)) # ideally this should be 0 queries, because it's a default ... with self.assertNumQueries(1): self.assertTrue(validate_host('yay.com', allowed_cls))
def proxy(request): PROXY_ALLOWED_HOSTS = (ogc_server_settings.hostname,) + getattr(settings, 'PROXY_ALLOWED_HOSTS', ()) if 'url' not in request.GET: return HttpResponse( "The proxy service requires a URL-encoded URL as a parameter.", status=400, content_type="text/plain" ) raw_url = request.GET['url'] url = urlsplit(raw_url) locator = url.path if url.query != "": locator += '?' + url.query if url.fragment != "": locator += '#' + url.fragment if not settings.DEBUG: 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" ) headers = {} if settings.SESSION_COOKIE_NAME in request.COOKIES and is_safe_url(url=raw_url, host=ogc_server_settings.netloc): headers["Cookie"] = request.META["HTTP_COOKIE"] if request.method in ("POST", "PUT") and "CONTENT_TYPE" in request.META: headers["Content-Type"] = request.META["CONTENT_TYPE"] if url.scheme =='https': conn = HTTPSConnection(url.hostname, url.port) else: conn = HTTPConnection(url.hostname, url.port) conn.request(request.method, locator, request.raw_post_data, headers) result = conn.getresponse() # If we get a redirect, let's add a useful message. if result.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, result.getheader('Location'))), status=result.status, content_type=result.getheader("Content-Type", "text/plain") ) response['Location']=result.getheader('Location') else: response = HttpResponse( result.read(), status=result.status, content_type=result.getheader("Content-Type", "text/plain") ) return response
def validate_redirect_url(self, sub=False): """ Returns the next_url path if next_url matches allowed hosts. """ next_url = self.request.GET.get(REDIRECT_FIELD_NAME, None) if not next_url: return None parts = urlparse.urlparse(next_url) if parts.netloc: domain, _ = split_domain_port(parts.netloc) allowed_hosts = (['*'] if django_settings.DEBUG else django_settings.ALLOWED_HOSTS) if not (domain and validate_host(domain, allowed_hosts)): return None path = parts.path if sub: try: # We replace all ':slug/' by '%(slug)s/' so that we can further # create an instantiated url through Python string expansion. path = re.sub(r':(%s)/' % settings.ACCT_REGEX, r'%(\1)s/', path) % self.kwargs except KeyError: # We don't have all keys necessary. A safe defaults is to remove # them. Most likely a redirect URL is present to pick between # multiple choices. path = re.sub(r'%(\S+)s/', '', path) return urlparse.urlunparse((None, '', path, parts.params, parts.query, parts.fragment))
def get_local_referrer(request): """Get the referrer URL if it is not external to this application""" if "HTTP_REFERER" in request.META: # Adopted from here: # https://github.com/django/django/blob/7fc317ae736e8fda1aaf4d4ede84d95fffaf5281/django/http/request.py#L124-L130 allowed_hosts = settings.ALLOWED_HOSTS if settings.DEBUG and not allowed_hosts: allowed_hosts = [".localhost", "127.0.0.1", "[::1]"] referrer = urlsplit(request.META["HTTP_REFERER"]) if validate_host(referrer.hostname, allowed_hosts): # If the referrer header points to "ourselves", return a "safe copy" # removing everything but the path+query+fragment part return SplitResult( scheme="", netloc="", path=referrer.path, query=referrer.query, fragment=referrer.fragment, ).geturl() else: return None else: return None
def __call__(self, request): host = request._get_raw_host() domain, port = split_domain_port(host) # Set request.site request.site = self.default_site for name, config in settings.XMPP_HOSTS.items(): if validate_host(domain, config.get('ALLOWED_HOSTS', [])): request.site = config # Attach any messages from the database to the messages system # These messages usually come from asynchronous tasks (-> Celery) if request.user.is_anonymous is False: with transaction.atomic(): stored_msgs = CachedMessage.objects.filter(user=request.user) if stored_msgs: for msg in stored_msgs: messages.add_message(request, msg.level, _(msg.message) % msg.payload) stored_msgs.delete() # Attach OS information to request request.os = self.get_os(request) request.os_mobile = request.os in ['android', 'ios', 'any'] response = self.get_response(request) return response
def validate_redirect_url(self, sub=False): """ Returns the next_url path if next_url matches allowed hosts. """ next_url = self.request.GET.get(REDIRECT_FIELD_NAME, None) if not next_url: return None parts = urlparse.urlparse(next_url) if parts.netloc: domain, _ = split_domain_port(parts.netloc) allowed_hosts = (['*'] if django_settings.DEBUG else django_settings.ALLOWED_HOSTS) if not (domain and validate_host(domain, allowed_hosts)): return None path = parts.path if sub: try: # We replace all ':slug/' by '%(slug)s/' so that we can further # create an instantiated url through Python string expansion. path = re.sub(r':(%s)/' % settings.ACCT_REGEX, r'%(\1)s/', path) % self.kwargs except KeyError: # We don't have all keys necessary. A safe defaults is to remove # them. Most likely a redirect URL is present to pick between # multiple choices. path = re.sub(r'%(\S+)s/', '', path) return urlparse.urlunparse( (None, '', path, parts.params, parts.query, parts.fragment))
def validate_redirect_url(next_url, sub=False, **kwargs): """ Returns the next_url path if next_url matches allowed hosts. """ # This method is copy/pasted from signup.auth so we donot need # to add djaodjin-signup as a prerequisites. It is possible # the functionality has already moved into Django proper. if not next_url: return None parts = six.moves.urllib.parse.urlparse(next_url) if parts.netloc: domain, _ = split_domain_port(parts.netloc) allowed_hosts = ['*'] if django_settings.DEBUG \ else django_settings.ALLOWED_HOSTS if not (domain and validate_host(domain, allowed_hosts)): return None path = parts.path if sub: from . import settings try: # We replace all ':slug/' by '%(slug)s/' so that we can further # create an instantiated url through Python string expansion. path = re.sub(r':(%s)/' % settings.ACCT_REGEX, r'%(\1)s/', path) % kwargs except KeyError: # We don't have all keys necessary. A safe defaults is to remove # them. Most likely a redirect URL is present to pick between # multiple choices. path = re.sub(r':(%s)/' % settings.ACCT_REGEX, '', path) return six.moves.urllib.parse.urlunparse( ("", "", path, parts.params, parts.query, parts.fragment))
def filter_for_authorizations( self, scope: Scope, authorizations: models.QuerySet) -> models.QuerySet: # todo implement error if no loose-fk field authorizations_local = [] authorizarions_external = [] for auth in authorizations: loose_fk_host = urlparse(getattr(auth, self.loose_fk_field)).hostname allowed_hosts = settings.ALLOWED_HOSTS if validate_host(loose_fk_host, allowed_hosts): authorizations_local.append(auth) else: authorizarions_external.append(auth) ids_local = self.ids_by_auth(scope, authorizations_local, local=True) ids_external = self.ids_by_auth(scope, authorizarions_external, local=False) queryset = self.filter(pk__in=ids_local.union(ids_external)) return queryset
def process_request(self, request): host = request.get_host().lower() hostname, port = split_domain_port(host) if not hostname: raise DisallowedHost('Invalid hostname in HTTP request') require_exact_match = getattr( settings, 'SITEDATA_MIDDLEWARE_REQUIRE_EXACT_HOSTNAME', False) if validate_host(hostname, settings.ALLOWED_HOSTS): request.sitedata = SiteData( hostname=hostname, require_exact_match=require_exact_match) LOCAL.sitedata_ref = weakref.ref( request.sitedata) # allow sitedata to be accessed from LOCAL # overrides request.urlconf if specified if hasattr(request.sitedata, 'urlconf' ) and request.sitedata.urlconf is not settings.ROOT_URLCONF: request.urlconf = request.sitedata.urlconf # Monkey patches request._get_scheme() to return 'https' instead of 'http' if hasattr(request.sitedata, 'scheme') and request.sitedata.exact_match_found \ and request.sitedata.scheme is 'https': request._get_scheme = _get_scheme_https
def proxy(request): PROXY_ALLOWED_HOSTS = getattr(settings, 'PROXY_ALLOWED_HOSTS', ()) hostname = (ogc_server_settings.hostname,) if ogc_server_settings else () PROXY_ALLOWED_HOSTS += hostname if 'url' not in request.GET: return HttpResponse("The proxy service requires a URL-encoded URL as a parameter.", status=400, content_type="text/plain" ) raw_url = request.GET['url'] url = urlsplit(raw_url) locator = url.path if url.query != "": locator += '?' + url.query if url.fragment != "": locator += '#' + url.fragment if not settings.DEBUG: 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" ) headers = {} if settings.SESSION_COOKIE_NAME in request.COOKIES and is_safe_url(url=raw_url, host=ogc_server_settings.netloc): headers["Cookie"] = request.META["HTTP_COOKIE"] if request.META.get('HTTP_AUTHORIZATION'): headers['AUTHORIZATION'] = request.META.get('HTTP_AUTHORIZATION') if request.method in ("POST", "PUT") and "CONTENT_TYPE" in request.META: headers["Content-Type"] = request.META["CONTENT_TYPE"] if request.META.get('HTTP_ACCEPT'): headers['ACCEPT'] = request.META['HTTP_ACCEPT'] if url.scheme == 'https': conn = HTTPSConnection(url.hostname, url.port) else: conn = HTTPConnection(url.hostname, url.port) conn.request(request.method, locator, request.body, headers) result = conn.getresponse() response = HttpResponse(result.read(), status=result.status, content_type=result.getheader("Content-Type", "text/plain"), ) if result.getheader('www-authenticate'): response['www-authenticate'] = "GeoNode" return response
def clean_spider_inline(url): allowed_hosts = settings.ALLOWED_HOSTS if settings.DEBUG and not allowed_hosts: allowed_hosts = _default_allowed_hosts if validate_host(url, allowed_hosts): inline_domain = urlsplit(url) return inline_domain.netloc or inline_domain.path.split("/", 1)[0] return None
def inline_path(urlsplitted): allowed_hosts = settings.ALLOWED_HOSTS if settings.DEBUG and not allowed_hosts: allowed_hosts = _default_allowed_hosts if validate_host(urlsplitted.netloc, allowed_hosts): # first path parameter is host return urlsplitted.netloc or urlsplitted.path.split("/", 1)[0] return None
def validate_origin(self, message, origin): allowed_hosts = settings.ALLOWED_HOSTS if settings.DEBUG and not allowed_hosts: allowed_hosts = ['localhost', '127.0.0.1', '[::1]'] origin_hostname = urlparse(origin).hostname valid = (origin_hostname and validate_host(origin_hostname, allowed_hosts)) return valid
def process_request(self, request): host = request._get_raw_host() domain, port = split_domain_port(host) for name, config in settings.XMPP_HOSTS.items(): if validate_host(domain, config.get('ALLOWED_HOSTS', [])): request.site = config return request.site = settings.XMPP_HOSTS[settings.DEFAULT_XMPP_HOST]
def apps_proxy(request): PROXY_ALLOWED_HOSTS = (ogc_server_settings.hostname, ) + getattr( settings, 'PROXY_ALLOWED_HOSTS', ()) token = None if 'token' in request.GET: token = request.GET['token'] if token is None: print "The proxy service requires a token." return HttpResponse("The proxy service requires a token.", status=400, content_type="text/plain") if not _check_token(token): print "The provided token is invalid." return HttpResponse("The provided token is invalid.", status=400, content_type="text/plain") if 'url' in request.GET: raw_url = request.GET['url'] else: # querystring = urllib2.unquote(request.META['QUERY_STRING']) querystring = request.META['QUERY_STRING'] raw_url = '%sows?%s' % (settings.OGC_SERVER['default']['LOCATION'], querystring) url = urlsplit(raw_url) if url is None: return HttpResponse( "The proxy service requires a URL-encoded URL as a parameter.", status=400, content_type="text/plain") if not settings.DEBUG: print 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 (%s) is not in the" " PROXY_ALLOWED_HOSTS setting." % url.hostname, status=403, content_type="text/plain") print 'proxying to %s' % raw_url proxy_request = urllib2.Request(raw_url) base64string = base64.encodestring( '%s:%s' % (settings.EXT_APP_USER, settings.EXT_APP_USER_PWD)).replace( '\n', '') proxy_request.add_header("Authorization", "Basic %s" % base64string) result = urllib2.urlopen(proxy_request) response = HttpResponse(result, status=result.code, content_type=result.headers["Content-Type"]) return response
def valid_send_request(request): if not settings.DEPLOY_ENV: return True user_agent = (request.META.get('HTTP_USER_AGENT') or '').lower() http_referer = (request.META.get('HTTP_REFERER') or '').lower() if not user_agent or user_agent.lower().find('windows') > 0: return False domain = http_referer and urlparse(http_referer).hostname if domain and not validate_host(domain, settings.ALLOWED_HOSTS): return False return True
def validate_redirect_url(next_url): """ Returns the next_url path if next_url matches allowed hosts. """ if not next_url: return None parts = urlparse.urlparse(next_url) if parts.netloc: domain, _ = split_domain_port(parts.netloc) allowed_hosts = ["*"] if settings.DEBUG else settings.ALLOWED_HOSTS if not (domain and validate_host(domain, allowed_hosts)): return None return urlparse.urlunparse((parts.scheme, "", parts.path, parts.params, parts.query, parts.fragment))
def is_trusted_origin(origin): if not settings.ALLOW_WEBUSER_LOGIN: return False if origin is None: return True allowed_hosts = django_settings.ALLOWED_HOSTS if django_settings.DEBUG and not allowed_hosts: allowed_hosts = ["localhost", "127.0.0.1", "[::1]"] try: origin_host = urlparse(origin.decode()).hostname return validate_host(origin_host, allowed_hosts) except UnicodeDecodeError: return False
def validate_redirect_url(next_url): """ Returns the next_url path if next_url matches allowed hosts. """ if not next_url: return None parts = urlparse.urlparse(next_url) if parts.netloc: domain, _ = split_domain_port(parts.netloc) allowed_hosts = ['*'] if settings.DEBUG else settings.ALLOWED_HOSTS if not (domain and validate_host(domain, allowed_hosts)): return None return parts.path
def process_request(self, request): host = request.get_host() if validate_host(host, ORIG_ALLOWED_HOSTS): return None for net in self.allowed_cidr_nets: try: if host in net: return None except AddrFormatError: # not an IP break raise DisallowedHost("Invalid HTTP_HOST header: %r." % host)
def validate_redirect_url(next_url): """ Returns the next_url path if next_url matches allowed hosts. """ if not next_url: return None parts = six.moves.urllib.parse.urlparse(next_url) if parts.netloc: domain, _ = split_domain_port(parts.netloc) allowed_hosts = ['*'] if settings.DEBUG else settings.ALLOWED_HOSTS if not (domain and validate_host(domain, allowed_hosts)): return None return six.moves.urllib.parse.urlunparse( ("", "", parts.path, parts.params, parts.query, parts.fragment))
def valid_origin(self, origin): # None is not allowed if origin is None: return False # Get only hostname all allowed origins # Only if not full: https://domain.example.com:8443 -> domain.example.com # or //domain.example.com - > domain.example.com # or .example.com -> .example.com self.allowed_origins = [ urlparse(pattern).hostname or urlparse("//" + pattern).hostname or pattern for pattern in self.allowed_origins ] # Check against our list return validate_host(origin, self.allowed_origins)
def configure_sites(self): """ Setup sites settings in the db. (Sites is conceptually flawed, but needed for allauth.) """ try: from django.contrib.sites.models import Site if validate_host(settings.SITE_CONFIG['domain'], settings.ALLOWED_HOSTS): Site.objects.update_or_create(pk=settings.SITE_ID, defaults=settings.SITE_CONFIG) else: raise ImproperlyConfigured('Specified domain "%s" is not in ALLOWED_HOSTS' % settings.SITE_CONFIG['domain']) except ProgrammingError: # We haven't run the migrations yet pass
def get_next_redirect_url(self, request: HttpRequest) -> Optional[str]: user = getattr(request, 'user', None) if user.is_authenticated: self.logout(request) next_url = get_social_next_from_referer_url(request) next_url = build_absolute_uri(request, next_url) r = urlparse(next_url) host = r.netloc domain, port = split_domain_port(host) allowed_hosts = settings.ALLOWED_HOSTS if domain and validate_host(domain, allowed_hosts): return next_url raise DisallowedRedirect("Attempted access from '%s' denied." % next_url)
def process_request(self, request): domain, port = split_domain_port(request.get_host()) self._changes = {} for site in settings.SITES: site = site.copy() try: if validate_host(domain, site['HOSTS']): site_settings = '.'.join( [self.top_module, site['NAME'], 'settings']) self._enter(import_module(site_settings)) break except ImportError: pass
def validate_storefront_url(url): """Validate the storefront URL. Raise ValidationError if URL isn't in RFC 1808 format or it isn't allowed by ALLOWED_STOREFRONT_HOSTS in settings. """ try: parsed_url = urlparse(url) except ValueError as error: raise ValidationError({"redirectUrl": str(error)}) if not validate_host(parsed_url.netloc, settings.ALLOWED_STOREFRONT_HOSTS): raise ValidationError({ "redirectUrl": "%s this is not valid storefront address." % parsed_url.netloc })
def validate_storefront_url(url): """Validate the storefront URL. Raise ValidationError if URL isn't in RFC 1808 format or it isn't allowed by ALLOWED_CLIENT_HOSTS in settings. """ try: parsed_url = urlparse(url) domain, _ = split_domain_port(parsed_url.netloc) except ValueError as error: raise ValidationError({"redirectUrl": str(error)}) if not validate_host(domain, settings.ALLOWED_CLIENT_HOSTS): error_message = (f"{domain or url} is not allowed. Please check " "`ALLOWED_CLIENT_HOSTS` configuration.") raise ValidationError({"redirectUrl": error_message})
def validate_redirect_url(next_url): """ Returns the next_url path if next_url matches allowed hosts. """ # This method is copy/pasted from signup.auth so we donot need # to add djaodjin-signup as a prerequisites. It is possible # the functionality has already moved into Django proper. if not next_url: return None parts = urlparse.urlparse(next_url) if parts.netloc: domain, _ = split_domain_port(parts.netloc) allowed_hosts = ['*'] if settings.DEBUG else settings.ALLOWED_HOSTS if not (domain and validate_host(domain, allowed_hosts)): return None return parts.path
def get_context_data(self, **kwargs): ctx = super(PaymentDetailsView, self).get_context_data(**kwargs) extra = Source.get_provider( self.checkout_session.payment_method()).extra ctx["payment_method"] = extra.get("verbose_name", extra["name"]) host = ctx.get("source", None) if host: host = host.temp_form if host: host = host.action ctx["is_local_url"] = (host == "") if host is not None and not ctx["is_local_url"]: #try harder domain = split_domain_port(host)[0] if domain and validate_host(domain, self._allowed_hosts): ctx["is_local_url"] = True return ctx
def to_internal_value(self, data): result = super(CaptchaField, self).to_internal_value(data) try: captcha = client.submit(recaptcha_response=result, private_key=settings.RECAPTCHA_PRIVATE_KEY, remoteip=get_client_ip( self.context['request'])) except HTTPError: # Catch timeouts, etc raise serializers.ValidationError( self.error_messages["captcha_error"], code="captcha_error") if not captcha.is_valid or not validate_host( captcha.extra_data['hostname'], settings.ALLOWED_HOSTS): raise serializers.ValidationError('Captcha value is not valid') return result
def _is_allowed(url, require_https=False, *args, **kwargs): """Variation of the `django.http.url_has_allowed_host_and_scheme` function, checking the given URL host against the `ALLOWED_HOSTS` setting (including wildcards.) Note: `args` and `kwargs` are there to mock unused arguments from original function calls.""" url = _urlparse(url) allowed_schemes = {'https'} if require_https else {'http', 'https'} allowed_hosts = settings.ALLOWED_HOSTS # Fallback to localhost-like patterns when debugging with an empty # ALLOWED_HOSTS settings if settings.DEBUG and not allowed_hosts: allowed_hosts = {'.localhost', '127.0.0.1', '[::1]'} return (validate_host(url.netloc, allowed_hosts) and (url.scheme in allowed_schemes))
def process_request(self, request): domain, _ = split_domain_port(request.get_host()) for site in settings.SITES: site = site.copy() try: if validate_host(domain, site["HOSTS"]): urlconf = ".".join([self.top_module, site["NAME"], "urls"]) import_module(urlconf) break except ImportError: pass except Exception: # pylint: disable=broad-except pass else: urlconf = settings.ROOT_URLCONF request.urlconf = urlconf
def clean_token(self): token = self.cleaned_data['token'] try: self.con = con = Concept.active_objects.get(code=token) except Concept.DoesNotExist: raise forms.ValidationError('invalid token') valid_domains = con.user.valid_domains() host, port = split_domain_port(self.host) valid = request.validate_host(host, valid_domains) if not valid: raise forms.ValidationError('invalid domain') return token
def validate_safesens_url(url): """Validate the safesens URL. Raise ValidationError if URL isn't in RFC 1808 format or it isn't allowed by ALLOWED_CLIENT_HOSTS in settings. """ try: parsed_url = urlparse(url) domain, _ = split_domain_port(parsed_url.netloc) if not parsed_url.netloc: raise ValidationError( "Invalid URL. Please check if URL is in RFC 1808 format.") except ValueError as error: raise ValidationError(error) if not validate_host(domain, settings.ALLOWED_CLIENT_HOSTS): error_message = (f"{domain or url} is not allowed. Please check " "`ALLOWED_CLIENT_HOSTS` configuration.") raise ValidationError(error_message)
def process_request(self, request): domain, port = split_domain_port(request.get_host()) for site in settings.SITES: site = site.copy() try: if validate_host(domain, site['HOSTS']): urlconf = '.'.join([self.top_module, site['NAME'], 'urls']) import_module(urlconf) break except ImportError: pass except Exception: pass else: urlconf = settings.ROOT_URLCONF request.urlconf = urlconf
def __call__(self, request): host = request._get_raw_host() domain, port = split_domain_port(host) # Set request.site request.site = self.default_site for name, config in settings.XMPP_HOSTS.items(): if validate_host(domain, config.get('ALLOWED_HOSTS', [])): request.site = config # Attach any messages from the database to the messages system # These messages usually come from asynchronous tasks (-> Celery) if request.user.is_anonymous is False: with transaction.atomic(): stored_msgs = CachedMessage.objects.filter(user=request.user) for msg in stored_msgs: messages.add_message(request, msg.level, _(msg.message) % json.loads(msg.payload)) stored_msgs.delete() # Attach OS information to request request.os = self.get_os(request) request.os_mobile = request.os in ['android', 'ios', 'any'] # Get data that is used with every request and requires database access and cache it cache_key = 'request_context' cached = cache.get(cache_key) if cached is None: cached = { 'menuitems': MenuItem.objects.all(), } for item in cached['menuitems']: item.cached_data # touch cached_data to make sure that all properties serialized cache.set(cache_key, cached) request.hp_request_context = cached response = self.get_response(request) return response
def validate(request): domain, port = split_domain_port(request.META['HTTP_HOST']) if not validate_host(domain, settings.CC_ALLOWED_HOSTS): return HttpResponseForbidden('forbiden') return func(request)
def proxy(request): PROXY_ALLOWED_HOSTS = getattr(settings, 'PROXY_ALLOWED_HOSTS', ()) host = None if ogc_server_settings is not None: if ogc_server_settings: hostname = (ogc_server_settings.hostname,) else: hostname = () PROXY_ALLOWED_HOSTS += hostname host = ogc_server_settings.netloc if 'url' not in request.GET: return HttpResponse("The proxy service requires a " "URL-encoded URL as a parameter.", status=400, content_type="text/plain" ) raw_url = request.GET['url'] url = urlsplit(raw_url) headers = {} # Fix up any possible non-absolute URLs that have no scheme, or even domain if ((callable(protocol_relative_url) and protocol_relative_url(raw_url)) or not url.scheme): if url.netloc and callable(protocol_relative_to_scheme): # Fix up any '//' protocol relative URLs coming from JS map viewers # Use request.scheme to reference origin scheme context # Note: Can't use request.build_absolute_uri(raw_url) for this raw_url = protocol_relative_to_scheme(url.geturl(), scheme=request.scheme) # logger.debug("protocol_relative_to_scheme = ".format(raw_url)) else: raw_url = request.build_absolute_uri(raw_url) # logger.debug("build_absolute_uri = ".format(raw_url)) url = urlsplit(raw_url) if not settings.DEBUG: if not (validate_host(url.hostname, PROXY_ALLOWED_HOSTS) or (callable(has_ssl_config) and has_ssl_config(url.geturl()))): 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 " "or defined to use the proxy in SSL/PKI configurations.", status=403, content_type="text/plain" ) if url.scheme.lower() == 'https' \ and callable(has_ssl_config) and has_ssl_config(url.geturl()): # Adjust request to mock call to pki_request view # Merge queries pki_req_query = request.GET.copy() """django.http.QueryDict""" # Strip the url param from request query del pki_req_query['url'] # Note: leave other query pairs passed to this view, e.g. access_token # Add any query from passed url param's URL url_query = url.query.strip() for k, v in parse_qsl(url_query, keep_blank_values=True): pki_req_query.appendlist(k, v) request.GET = pki_req_query request.META["QUERY_STRING"] = pki_req_query.urlencode() # pki_request view is restricted to local calls request.META["REMOTE_ADDR"] = '127.0.0.1' request.META["REMOTE_HOST"] = 'localhost' # TODO: Update HTTP_X_FORWARDED_FOR? See: api.views.get_client_ip() base_url = urlunsplit((None, url.netloc, url.path, None, None))\ .replace('//', '', 1) # For pki_request view, resource_url has no URL scheme resource_url = quote(base_url) pki_path = reverse('pki_request', kwargs={'resource_url': resource_url}) # Reset view paths attributes request.path = request.path_info = pki_path request.META["PATH_INFO"] = pki_path request.resolver_match = resolve(pki_path) logger.debug("pki_req QueryDict: {0}".format(pki_req_query)) # logger.debug("pki_req META: {0}".format(request.META)) logger.debug("pki_req META['QUERY_STRING']: {0}" .format(request.META["QUERY_STRING"])) logger.debug("Routing through pki proxy: {0}".format(resource_url)) return pki_request(request, resource_url=resource_url) if settings.SESSION_COOKIE_NAME in request.COOKIES and \ is_safe_url(url=raw_url, host=host): headers["Cookie"] = request.META["HTTP_COOKIE"] if request.method in ("POST", "PUT") and "CONTENT_TYPE" in request.META: headers["Content-Type"] = request.META["CONTENT_TYPE"] http_client = requests.session() http_client.verify = True req_method = getattr(http_client, request.method.lower()) resp = req_method(raw_url, headers=headers, data=request.body) if 'Content-Type' in resp.headers: content_type = resp.headers['Content-Type'] else: content_type = 'text/plain' # If we get a redirect, let's add a useful message. if resp.status_code in (301, 302, 303, 307): response = HttpResponse( ('This proxy does not support redirects. The server in "%s" ' 'asked for a redirect to "%s"' % (raw_url, resp.headers['Location'])), status=resp.status_code, content_type=content_type ) response['Location'] = resp.headers['Location'] else: response = HttpResponse( resp.content, status=resp.status_code, content_type=content_type ) return response
def proxy(request): PROXY_ALLOWED_HOSTS = getattr(settings, 'PROXY_ALLOWED_HOSTS', ()) host = None #if 'geonode.geoserver' in settings.INSTALLED_APPS: # from geonode.geoserver.helpers import ogc_server_settings # hostname = (ogc_server_settings.hostname,) if ogc_server_settings else () # PROXY_ALLOWED_HOSTS += hostname # host = ogc_server_settings.netloc if 'url' not in request.GET: return HttpResponse("The proxy service requires a URL-encoded URL as a parameter.", status=400, content_type="text/plain" ) raw_url = request.GET['url'] url = urlsplit(raw_url) locator = url.path if url.query != "": locator += '?' + url.query if url.fragment != "": locator += '#' + url.fragment if not settings.DEBUG: 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" ) headers = {} if settings.SESSION_COOKIE_NAME in request.COOKIES and is_safe_url(url=raw_url, host=host): headers["Cookie"] = request.META["HTTP_COOKIE"] if request.method in ("POST", "PUT") and "CONTENT_TYPE" in request.META: headers["Content-Type"] = request.META["CONTENT_TYPE"] print "Raw URL: "+ raw_url match_regex = None match_tilesource = None # Try to match against existing tile sources #tilesources = TileSource.objects.exclude(pattern__isnull=True).exclude(pattern__exact='') tilesources = getTileSources(proxy=True) for tilesource in tilesources: match = tilesource.match(raw_url) #print tilesource.pattern if match: match_regex = match match_tilesource = tilesource break if match_tilesource and match_regex: return proxy_tilesource(request, match_tilesource, match_regex) #else: # return HttpResponse('No matching tilesource found.',RequestContext(request, {}), status=404) # Try to match against existing origins that can automatically create tile sources (auto=true) match_tileorigin = None #tileorigins = TileOrigin.objects.exclude(pattern__isnull=True).exclude(pattern__exact='').filter(auto=True) tileorigins = getTileOrigins(proxy=True) for tileorigin in tileorigins: match = tileorigin.match(raw_url) if match: #print "Matched against TileOrigin", tileorigin match_regex = match match_tileorigin = tileorigin break if match_tileorigin and match_regex: to = match_tileorigin if to.multiple: slug = getRegexValue(match_regex, 'slug') ts_url = to.url.replace('{slug}', slug) #print "ts_url: "+ts_url if TileSource.objects.filter(url=ts_url).count() > 0: print "Error: This souldn't happen. You should have matched the tilesource earlier so you don't duplicate" return None exts = string_to_list(to.extensions) ts_pattern = url_to_pattern(ts_url, extensions=exts) ts = TileSource(auto=True,url=ts_url,pattern=ts_pattern,name=slug,type=to.type,extensions=exts,origin=to) ts.save() reloadTileSources(proxy=False) reloadTileSources(proxy=True) return proxy_tilesource(request, ts, match_regex) else: ts = TileSource(auto=True,url=to.url,pattern=to.pattern,name=to.name,type=to.type,extensions=to.extensions) ts.save() reloadTileSources(proxy=False) reloadTileSources(proxy=True) return proxy_tilesource(request, ts, match_regex) else: return HttpResponse('No matching tile origin or tile source found.',RequestContext(request, {}), status=404)
def test_compare(self): # ALLOWED_HOSTS with django.http.request.validate_host self.assertTrue(validate_host("192.168.1.2", self.fnmatch_ips)) self.assertFalse(validate_host("10.0.1.2", self.fnmatch_ips))
def proxy(request, url=None, response_callback=None, sec_chk_hosts=True, sec_chk_rules=True, timeout=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) 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, access_token = get_headers(request, url, raw_url) # Inject access_token if necessary 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)) response, content = http_client.request(_url, method=request.method, data=request.body, headers=headers, timeout=timeout, user=request.user) content = response.content or response.reason status = response.status_code content_type = response.headers.get('Content-Type') # 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': from StringIO import StringIO import gzip buf = StringIO(content) f = gzip.GzipFile(fileobj=buf) content = f.read() if response and 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 and 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: def _get_message(text): _s = text.decode("utf-8", "replace") try: found = re.search('<b>Message</b>(.+?)</p>', _s).group(1).strip() except BaseException: found = _s return found return HttpResponse( content=content, reason=_get_message(content) if status not in (200, 201) else None, status=status, content_type=content_type)
def valid_origin(self, origin): # None is not allowed if origin is None: return False # Check against our list return validate_host(origin, self.allowed_origins)
def assert_redirects(response, expected_url, status_code=302, target_status_code=200, fetch_redirect_response=True): ''' Assert that a response redirected to a specific URL and that the redirect URL can be loaded. Won't work for external links since it uses the test client to do a request (use fetch_redirect_response=False to check such links without fetching them). ''' __tracebackhide__ = True if hasattr(response, 'redirect_chain'): # The request was a followed redirect assert len(response.redirect_chain) > 0, _error( 'Response didn\'t redirect as expected: ' 'Response code was {0} (expected {1})', response.status_code, status_code ) assert response.redirect_chain[0][1] == status_code, _error( 'Initial response didn\'t redirect as expected: ' 'Response code was {0} (expected {1})', response.redirect_chain[0][1], status_code ) url, status_code = response.redirect_chain[-1] scheme, netloc, path, query, fragment = urlsplit(url) assert response.status_code == target_status_code, _error( 'Response didn\'t redirect as expected: ' 'Final Response code was {0} (expected {1})', response.status_code, target_status_code ) else: # Not a followed redirect assert response.status_code == status_code, _error( 'Response didn\'t redirect as expected: ' 'Response code was {0} (expected {1})', response.status_code, status_code ) url = response.url scheme, netloc, path, query, fragment = urlsplit(url) # Prepend the request path to handle relative path redirects. if not path.startswith('/'): url = urljoin(response.request['PATH_INFO'], url) path = urljoin(response.request['PATH_INFO'], path) if fetch_redirect_response: # netloc might be empty, or in cases where Django tests the # HTTP scheme, the convention is for netloc to be 'testserver'. # Trust both as "internal" URLs here. domain, port = split_domain_port(netloc) if domain and not validate_host(domain, settings.ALLOWED_HOSTS): raise ValueError( 'The test client is unable to fetch remote URLs (got {0}). ' 'If the host is served by Django, add "{1}" to ALLOWED_HOSTS. ' 'Otherwise, use assertRedirects(..., fetch_redirect_response=False).' ''.format(url, domain) ) redirect_response = response.client.get(path, QueryDict(query), secure=(scheme == 'https')) # Get the redirection page, using the same client that was used # to obtain the original response. assert redirect_response.status_code == target_status_code, _error( 'Couldn\'t retrieve redirection page "{0}": ' 'response code was {1} (expected {2})', path, redirect_response.status_code, target_status_code ) assert url == expected_url, 'Response redirected to "{0}", expected "{1}"'.foramt(url, expected_url)
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 access_token = None if request and 'access_token' in request.session: access_token = request.session['access_token'] # 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 if request and 'access_token' in request.session: access_token = request.session['access_token'] if access_token: # TODO: Bearer is currently cutted of by Djano / GeoServer if request.method in ("POST", "PUT"): headers['Authorization'] = 'Bearer %s' % access_token if access_token and 'access_token' not in locator: query_separator = '&' if '?' in locator else '?' locator = ('%s%saccess_token=%s' % (locator, query_separator, access_token)) elif 'HTTP_AUTHORIZATION' in request.META: auth = request.META.get( 'HTTP_AUTHORIZATION', request.META.get('HTTP_AUTHORIZATION2')) if auth: headers['Authorization'] = auth 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) conn.request(request.method, locator.encode('utf8'), 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)