mirror of
https://github.com/postgres/pgweb.git
synced 2025-08-13 13:12:42 +00:00
Require explicit tagging on views taking query parameters
Require each view to declare which query parameters it wants, and filter out any other parameters. We have very few views that actually take query parameters, and random additional query patterns will have no effect on the view. However, they will break frontend caching (in making them look like different pages). This will be extended into an implementation in the caching frontends as well, btu it's needed in the backend to ensure that local testing will have tbe same effect as the caches.
This commit is contained in:
@ -4,7 +4,7 @@ import django.contrib.auth.views as authviews
|
||||
from django.http import HttpResponseRedirect, Http404, HttpResponse
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.shortcuts import get_object_or_404
|
||||
from pgweb.util.decorators import login_required, script_sources, frame_sources, content_sources
|
||||
from pgweb.util.decorators import login_required, script_sources, frame_sources, content_sources, queryparams
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.http import urlsafe_base64_encode
|
||||
@ -535,6 +535,7 @@ def signup_complete(request):
|
||||
@script_sources('https://www.gstatic.com/recaptcha/')
|
||||
@frame_sources('https://www.google.com/')
|
||||
@transaction.atomic
|
||||
@queryparams('do_abort')
|
||||
def signup_oauth(request):
|
||||
if 'oauth_email' not in request.session \
|
||||
or 'oauth_firstname' not in request.session \
|
||||
@ -618,6 +619,7 @@ def signup_oauth(request):
|
||||
####
|
||||
# Community authentication endpoint
|
||||
####
|
||||
@queryparams('d', 'su')
|
||||
def communityauth(request, siteid):
|
||||
# Get whatever site the user is trying to log in to.
|
||||
site = get_object_or_404(CommunityAuthSite, pk=siteid)
|
||||
@ -742,6 +744,7 @@ def communityauth_logout(request, siteid):
|
||||
|
||||
|
||||
@login_required
|
||||
@queryparams('next')
|
||||
def communityauth_consent(request, siteid):
|
||||
org = get_object_or_404(CommunityAuthSite, id=siteid).org
|
||||
if request.method == 'POST':
|
||||
@ -776,6 +779,7 @@ def _encrypt_site_response(site, s):
|
||||
)
|
||||
|
||||
|
||||
@queryparams('s', 'e', 'n', 'u')
|
||||
def communityauth_search(request, siteid):
|
||||
# Perform a search for users. The response will be encrypted with the site
|
||||
# key to prevent abuse, therefor we need the site.
|
||||
|
@ -3,7 +3,7 @@ from django.http import HttpResponseRedirect, Http404
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.conf import settings
|
||||
|
||||
from pgweb.util.decorators import cache
|
||||
from pgweb.util.decorators import cache, queryparams
|
||||
|
||||
import urllib.parse
|
||||
import requests
|
||||
@ -47,6 +47,7 @@ def generate_pagelinks(pagenum, totalpages, querystring):
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@queryparams('a', 'd', 'l', 'ln', 'm', 'p', 'q', 's', 'u')
|
||||
@cache(minutes=30)
|
||||
def search(request):
|
||||
# Perform a general web search
|
||||
|
@ -24,6 +24,17 @@ def cache(days=0, hours=0, minutes=0, seconds=0):
|
||||
return _cache
|
||||
|
||||
|
||||
def queryparams(*args):
|
||||
"""
|
||||
Allow specified query parameters when calling function.
|
||||
NOTE! Must be the "outermost" decorator!!!
|
||||
"""
|
||||
def _queryparams(fn):
|
||||
fn.queryparams = args
|
||||
return fn
|
||||
return _queryparams
|
||||
|
||||
|
||||
def allow_frames(fn):
|
||||
def _allow_frames(request, *_args, **_kwargs):
|
||||
resp = fn(request, *_args, **_kwargs)
|
||||
|
@ -1,4 +1,5 @@
|
||||
from django.conf import settings
|
||||
from django.http import QueryDict
|
||||
|
||||
from pgweb.util.templateloader import initialize_template_collection, get_all_templates
|
||||
|
||||
@ -76,3 +77,29 @@ class PgMiddleware(object):
|
||||
|
||||
response['X-XSS-Protection'] = "1; mode=block"
|
||||
return response
|
||||
|
||||
def process_view(self, request, view_func, view_args, view_kwargs):
|
||||
# Filter out any query parameters that are not explicitly allowed. We do the same thing in Varnish,
|
||||
# and it's better to also do it in django if they show up here, so issues because of it are caught
|
||||
# in local testing where there is no Varnish.
|
||||
if not request.GET:
|
||||
# If there are no parameters, just skip this whole process
|
||||
return None
|
||||
|
||||
if request.path.startswith('/admin/'):
|
||||
# django-admin uses it a lot and it's not for us to change
|
||||
return None
|
||||
|
||||
allowed = getattr(view_func, 'queryparams', None)
|
||||
|
||||
if allowed:
|
||||
# Filter the QueryDict for only the allowed parameters
|
||||
result = request.GET.copy()
|
||||
for k in request.GET.keys():
|
||||
if k not in allowed:
|
||||
del result[k]
|
||||
result.mutable = False
|
||||
request.GET = result
|
||||
else:
|
||||
request.GET = QueryDict()
|
||||
return None
|
||||
|
Reference in New Issue
Block a user