Search code examples
pythonauthenticationpyramid

How to have two logins/Authentication policies within same app?


I have a standard Pyramid application. In the __init__.py main method, I have a config.add_request_method(AuthHelper.get_user, 'user', reify=True) to set up a globally accessible user object.

I store all users in a user table, and I access the current, logged-in user via the self.request.user. My problem is that I would like to create an admin portal, completely separate from the rest of the application, and I would like those "admins" to be stored in a table called admin. These admins are not users – they have a separate login page, and should have no relation to the user table.

I am having trouble setting up Authorization for admins. For instance, in the remember(self.request, admin.id, max_age=... there is no way to differentiate between a successfully logged in user vs. a successfully logged in admin, as far as the authentication goes. A User.id = 1 could access the admin portal for an Admin.id == 1, even though they logged in with different credentials on different views.

I thought about using an Admin.username in the remember method, but it still clashes with the config.add_request_method(AuthHelper.get_user...) (understandably so), which takes a User.id.

How can I set up an Authorization policy such that if you logged in successfully from the Admin portal login, your authorization is separate from a regular user who logged in from the main index page, and vice versa?

I feel like this is a relatively basic need, so I must be missing a simple way to implement this. Thank you.


Solution

  • For an "admin portal, completely separate from the rest of the application" one option would be to set up a separate WSGI application, which would have a separate request object, authentication policy etc.

    Then you can assemble two applications together with a WSGI "composite" so your "normal" application is mounted on / and your admin is on, say, /admin.

    You can still import the same code (models and even views) from your admin app - basically it's a matter of adding another "startup" file (the one which contains return config.make_wsgi_app()) and configuring the .ini file.

    If you change the cookie name in the admin app you'll be even able to be logged in as a User in your "main" app and as an Admin in your "admin portal":

    authn_policy = AuthTktAuthenticationPolicy(
        ...
        cookie_name='admin_auth_tkt',
        ...)
    authz_policy = ACLAuthorizationPolicy()
    config.set_authentication_policy(authn_policy)
    config.set_authorization_policy(authz_policy)
    

    You'll need to define an entry point in your setup.py file:

      entry_points="""\
          [paste.app_factory]
          main = MyApp:main_app
          admin = MyApp:admin_app
      """
    

    ( main_app and admin_app should be functions importable from myapp, which return respective WSGI applications).

    Then you can assemble a composite:

    [composite:main]
    use = egg:Paste#urlmap
    / = mainapp
    /files = adminapp
    
    [app:mainapp]
    use = egg:MyApp#main
    
    [app:adminapp]
    use = egg:MyApp#admin