Commits

Andriy Kornatskyy committed 835a619

Introduced secure decorator (to handle https permanent redirects).

Comments (0)

Files changed (3)

doc/userguide.rst

 :py:class:`~wheezy.web.middleware.errors.HTTPErrorMiddleware` to route 401
 status code to signin page. Read more in :ref:`httperrormiddleware` section.
 
+@secure
+~~~~~~~
+
+Decorator :py:class:`~wheezy.web.authorization.secure` accepts only secure
+requests (those that are communication via SSL) and if incoming
+request is not secure, issue permanent redirect to HTTPS location::
+
+    class MyHandler(BaseHandler):
+        @secure
+        def get(self):
+            ...
+            return response
+
+The behavior can be controlled via ``enabled`` (in case it is
+``False`` no checks performed, defaults to ``True``).
+
+
 XSRF/Resubmission
 ^^^^^^^^^^^^^^^^^
 

src/wheezy/web/authorization.py

 """
 """
 
+from wheezy.core.url import UrlParts
+from wheezy.http import permanent_redirect
 from wheezy.http import unauthorized
 
 
 def authorize(wrapped=None, roles=None):
-    """ Checks if user accessing protected resource is
+    """ Checks if user is accessing protected resource is
         authenticated and optionally in one of allowed ``roles``.
 
         ``roles`` - a list of authorized roles.
         return decorate
     else:
         return decorate(wrapped)
+
+
+def secure(wrapped=None, enabled=True):
+    """ Checks if user is accessing protected resource via SSL and if
+        not, issue permanent redirect to HTTPS location.
+
+        ``enabled`` - whenever to do any checks (defaults to ``True``).
+
+        Example::
+
+            class MyHandler(BaseHandler):
+                @secure
+                def get(self):
+                    ...
+                    return response
+
+        Using ``enabled``::
+
+            class MyHandler(BaseHandler):
+                @secure(enabled=False)
+                def get(self):
+                    ...
+                    return response
+    """
+    def decorate(method):
+        if not enabled:
+            return method
+
+        def check(handler, *args, **kwargs):
+            if not handler.request.secure:
+                parts = handler.request.urlparts
+                parts = UrlParts(('https',  # scheme
+                                  parts[1],  # netloc
+                                  parts[2],  # path
+                                  parts[3],  # query
+                                  None,  # fragment
+                                  ))
+                return permanent_redirect(parts.geturl())
+            return method(handler, *args, **kwargs)
+        return check
+    if wrapped is None:
+        return decorate
+    else:
+        return decorate(wrapped)

src/wheezy/web/tests/test_authorization.py

         assert 'response' == handler(mock_handler)
 
     def test_wrapped(self):
-        """ Check decorator.
+        """ Check decorators
         """
         from wheezy.web.authorization import authorize
         mock_handler = Mock()
         def get(self):
             return 'response'
         assert 'response' == get(mock_handler)
+
+
+class SecureTestCase(unittest.TestCase):
+    """ Test the ``secure``.
+    """
+
+    def test_check_not_secure(self):
+        """ Check if request is not secure.
+
+            @secure
+            def get(self):
+                ...
+        """
+        from wheezy.web.authorization import secure
+        mock_handler = Mock()
+        mock_handler.request.secure = False
+        mock_handler.request.urlparts = ('http', 'localhost:8080',
+                                         '/en/signin', None, None)
+        mock_handler_method = Mock()
+        handler = secure()(mock_handler_method)
+        response = handler(mock_handler)
+        assert 301 == response.status_code
+        location = dict(response.headers)['Location']
+        assert 'https://localhost:8080/en/signin' == location
+
+    def test_check_secure(self):
+        """ Check if request is secure.
+        """
+        from wheezy.web.authorization import secure
+        mock_handler = Mock()
+        mock_handler.request.secure = True
+        mock_handler_method = Mock(return_value='response')
+        handler = secure()(mock_handler_method)
+        assert 'response' == handler(mock_handler)
+
+    def test_check_not_enabled(self):
+        """ Check if request is secure.
+        """
+        from wheezy.web.authorization import secure
+        mock_handler = Mock()
+        mock_handler_method = Mock(return_value='response')
+        handler = secure(enabled=False)(mock_handler_method)
+        assert 'response' == handler(mock_handler)
+
+    def test_wrapped(self):
+        """ Check decorators
+        """
+        from wheezy.web.authorization import secure
+        mock_handler = Mock()
+        mock_handler.request.secure = True
+
+        @secure
+        def get(self):
+            return 'response'
+        assert 'response' == get(mock_handler)