diff options
-rw-r--r-- | mediagoblin/app.py | 13 | ||||
-rw-r--r-- | mediagoblin/edit/forms.py | 2 | ||||
-rw-r--r-- | mediagoblin/meddleware/__init__.py | 2 | ||||
-rw-r--r-- | mediagoblin/meddleware/csrf.py | 17 | ||||
-rw-r--r-- | mediagoblin/meddleware/noop.py | 3 | ||||
-rw-r--r-- | mediagoblin/submit/forms.py | 2 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/auth/change_fp.html | 2 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/utils/pagination.html | 2 | ||||
-rw-r--r-- | mediagoblin/tests/test_csrf_middleware.py | 21 |
9 files changed, 47 insertions, 17 deletions
diff --git a/mediagoblin/app.py b/mediagoblin/app.py index aafadd97..7f087ed9 100644 --- a/mediagoblin/app.py +++ b/mediagoblin/app.py @@ -107,12 +107,6 @@ class MediaGoblinApp(object): def __call__(self, environ, start_response): request = Request(environ) - # pass the request through our meddleware classes - for m in self.meddleware: - response = m.process_request(request) - if response is not None: - return response(environ, start_response) - ## Routing / controller loading stuff path_info = request.path_info route_match = self.routing.match(path_info) @@ -164,6 +158,13 @@ class MediaGoblinApp(object): return render_404(request)(environ, start_response) controller = common.import_component(route_match['controller']) + + # pass the request through our meddleware classes + for m in self.meddleware: + response = m.process_request(request, controller) + if response is not None: + return response(environ, start_response) + request.start_response = start_response # get the response from the controller diff --git a/mediagoblin/edit/forms.py b/mediagoblin/edit/forms.py index 93934be7..dd339e08 100644 --- a/mediagoblin/edit/forms.py +++ b/mediagoblin/edit/forms.py @@ -28,7 +28,7 @@ class EditForm(wtforms.Form): _('Tags'), [tag_length_validator], description=_( - "Seperate tags by commas or spaces.")) + "Seperate tags by commas.")) slug = wtforms.TextField( _('Slug'), [wtforms.validators.Required(message=_("The slug can't be empty"))], diff --git a/mediagoblin/meddleware/__init__.py b/mediagoblin/meddleware/__init__.py index 729a020d..7ba70d87 100644 --- a/mediagoblin/meddleware/__init__.py +++ b/mediagoblin/meddleware/__init__.py @@ -25,7 +25,7 @@ class BaseMeddleware(object): def __init__(self, mg_app): self.app = mg_app - def process_request(self, request): + def process_request(self, request, controller): pass def process_response(self, request, response): diff --git a/mediagoblin/meddleware/csrf.py b/mediagoblin/meddleware/csrf.py index ca2eca5f..16541bee 100644 --- a/mediagoblin/meddleware/csrf.py +++ b/mediagoblin/meddleware/csrf.py @@ -31,6 +31,13 @@ else: getrandbits = random.getrandbits +def csrf_exempt(func): + """Decorate a Controller to exempt it from CSRF protection.""" + + func.csrf_enabled = False + return func + + class CsrfForm(Form): """Simple form to handle rendering a CSRF token and confirming it is included in the POST.""" @@ -58,7 +65,7 @@ class CsrfMeddleware(BaseMeddleware): CSRF_KEYLEN = 64 SAFE_HTTP_METHODS = ("GET", "HEAD", "OPTIONS", "TRACE") - def process_request(self, request): + def process_request(self, request, controller): """For non-safe requests, confirm that the tokens are present and match. """ @@ -75,9 +82,11 @@ class CsrfMeddleware(BaseMeddleware): # if this is a non-"safe" request (ie, one that could have # side effects), confirm that the CSRF tokens are present and # valid - if request.method not in self.SAFE_HTTP_METHODS \ - and ('gmg.verify_csrf' in request.environ or - 'paste.testing' not in request.environ): + if (getattr(controller, 'csrf_enabled', True) and + request.method not in self.SAFE_HTTP_METHODS and + ('gmg.verify_csrf' in request.environ or + 'paste.testing' not in request.environ) + ): return self.verify_tokens(request) diff --git a/mediagoblin/meddleware/noop.py b/mediagoblin/meddleware/noop.py index b43053de..f5376494 100644 --- a/mediagoblin/meddleware/noop.py +++ b/mediagoblin/meddleware/noop.py @@ -19,7 +19,8 @@ from mediagoblin.meddleware import BaseMeddleware class NoOpMeddleware(BaseMeddleware): - def process_request(self, request): + + def process_request(self, request, controller): pass def process_response(self, request, response): diff --git a/mediagoblin/submit/forms.py b/mediagoblin/submit/forms.py index 48a21f02..ad420771 100644 --- a/mediagoblin/submit/forms.py +++ b/mediagoblin/submit/forms.py @@ -32,4 +32,4 @@ class SubmitStartForm(wtforms.Form): _('Tags'), [tag_length_validator], description=_( - "Seperate tags by commas or spaces.")) + "Seperate tags by commas.")) diff --git a/mediagoblin/templates/mediagoblin/auth/change_fp.html b/mediagoblin/templates/mediagoblin/auth/change_fp.html index fa972085..5677949c 100644 --- a/mediagoblin/templates/mediagoblin/auth/change_fp.html +++ b/mediagoblin/templates/mediagoblin/auth/change_fp.html @@ -30,7 +30,7 @@ {{ wtforms_util.render_divs(cp_form) }} <div class="form_submit_buttons"> - <input type="submit" value="submit" class="button_form"/> + <input type="submit" value="{% trans %}Submit{% endtrans %}" class="button_form"/> </div> </div> diff --git a/mediagoblin/templates/mediagoblin/utils/pagination.html b/mediagoblin/templates/mediagoblin/utils/pagination.html index 84336103..3c12f93c 100644 --- a/mediagoblin/templates/mediagoblin/utils/pagination.html +++ b/mediagoblin/templates/mediagoblin/utils/pagination.html @@ -47,7 +47,7 @@ <a href="{{ next_url }}"><img class="pagination_arrow" src="{{ request.staticdirect('/images/pagination_right.png') }}" alt="Next page" /></a> {% endif %} <br /> - Go to page: + {% trans %}Go to page:{% endtrans %} {%- for page in pagination.iter_pages() %} {% if page %} {% if page != pagination.page %} diff --git a/mediagoblin/tests/test_csrf_middleware.py b/mediagoblin/tests/test_csrf_middleware.py index 691f10b9..c8fca23a 100644 --- a/mediagoblin/tests/test_csrf_middleware.py +++ b/mediagoblin/tests/test_csrf_middleware.py @@ -27,7 +27,7 @@ from mediagoblin import mg_globals def test_csrf_cookie_set(test_app): cookie_name = mg_globals.app_config['csrf_cookie_name'] - + # get login page response = test_app.get('/auth/login/') @@ -69,3 +69,22 @@ def test_csrf_token_must_match(test_app): mg_globals.app_config['csrf_cookie_name'])}, extra_environ={'gmg.verify_csrf': True}).\ status_int == 200 + +@setup_fresh_app +def test_csrf_exempt(test_app): + + # monkey with the views to decorate a known endpoint + import mediagoblin.auth.views + from mediagoblin.meddleware.csrf import csrf_exempt + + mediagoblin.auth.views.login = csrf_exempt( + mediagoblin.auth.views.login + ) + + # construct a request with no cookie or form token + assert test_app.post('/auth/login/', + extra_environ={'gmg.verify_csrf': True}, + expect_errors=False).status_int == 200 + + # restore the CSRF protection in case other tests expect it + mediagoblin.auth.views.login.csrf_enabled = True |