Search code examples
pythonpyramid

Different login views in Pyramid


There are some URLs which are handled by my Pyramid application. When an unauthenticated user tries to open any URL then the user is redirected to login form:

def forbidden(request):
    if request.user.keyname == 'guest':
        return HTTPFound(location=request.route_url('auth.login',))

    request.response.status = 403
    return dict(subtitle=u"Access denied")

config.add_view(forbidden, context=HTTPForbidden, renderer='auth/forbidden.mako')

But for some urls (routes) I have to return not the login form, but a 401 Unauthorized status code with WWW-Authenticate header. How I can setup my routes to accomplish this? I am guessing that I have to use route_predicate.


Solution

  • You can wrap those two errors into your custom one, and raise that in place of both. Then you can handle your exception and run the desired scenario for each error. Here is an example:

    class YourError(HTTPException):
        def __init__(self, error):
            super(YourError, self).__init__()
    
            self.error = error
    
        def handle(self, request):
            if error is HTTPForbidden or is instance(error, HTTPForbidden):
                return self._handle403(request)
            elif error is HTTPUnauthorized or is instance(error, HTTPUnauthorized):
                return self._handle401(request)
    
        def _handle403(self, request):
            # do your stuff for 403
    
        def _handle401(self, request):
            # do your stuff for 401
    
    # ...modify your view
    def forbidden(context, request):
        return context.handle(request)
    

    Then edit your view configuration:

    config.add_view(forbidden, context=YourError, renderer='auth/forbidden.mako')
    

    And then in other views where you need to return 403 or 401, go this way:

    def another_view(request)
        ...
        raise YourError(HTTPForbidden)
        # or
        raise YourError(HTTPUnauthorized)
        ...
    

    Then you will only need to implement your handling logic inside the YourError class.