3
0
Fork 0

feat: add custom security policy, login/logout for pyramid

aka. the `wuttaweb.auth` module
This commit is contained in:
Lance Edgar 2024-08-04 21:54:46 -05:00
parent c6f0007908
commit e296b50aa4
5 changed files with 296 additions and 0 deletions

View file

@ -32,6 +32,7 @@ from wuttjamaican.conf import make_config
from pyramid.config import Configurator
import wuttaweb.db
from wuttaweb.auth import WuttaSecurityPolicy
class WebAppProvider(AppProvider):
@ -115,6 +116,9 @@ def make_pyramid_config(settings):
pyramid_config = Configurator(settings=settings)
# configure user authorization / authentication
pyramid_config.set_security_policy(WuttaSecurityPolicy())
pyramid_config.include('pyramid_beaker')
pyramid_config.include('pyramid_deform')
pyramid_config.include('pyramid_mako')

146
src/wuttaweb/auth.py Normal file
View file

@ -0,0 +1,146 @@
# -*- coding: utf-8; -*-
################################################################################
#
# wuttaweb -- Web App for Wutta Framework
# Copyright © 2024 Lance Edgar
#
# This file is part of Wutta Framework.
#
# Wutta Framework is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# Wutta Framework is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Auth Utility Logic
"""
import re
from pyramid.authentication import SessionAuthenticationHelper
from pyramid.request import RequestLocalCache
from pyramid.security import remember, forget
from wuttaweb.db import Session
def login_user(request, user):
"""
Perform the steps necessary to "login" the given user. This
returns a ``headers`` dict which you should pass to the final
redirect, like so::
from pyramid.httpexceptions import HTTPFound
headers = login_user(request, user)
return HTTPFound(location='/', headers=headers)
.. warning::
This logic does not "authenticate" the user! It assumes caller
has already authenticated the user and they are safe to login.
See also :func:`logout_user()`.
"""
headers = remember(request, user.uuid)
return headers
def logout_user(request):
"""
Perform the logout action for the given request. This returns a
``headers`` dict which you should pass to the final redirect, like
so::
from pyramid.httpexceptions import HTTPFound
headers = logout_user(request)
return HTTPFound(location='/', headers=headers)
See also :func:`login_user()`.
"""
request.session.delete()
request.session.invalidate()
headers = forget(request)
return headers
class WuttaSecurityPolicy:
"""
Pyramid :term:`security policy` for WuttaWeb.
For more on the Pyramid details, see :doc:`pyramid:narr/security`.
But the idea here is that you should be able to just use this,
without thinking too hard::
from pyramid.config import Configurator
from wuttaweb.auth import WuttaSecurityPolicy
pyramid_config = Configurator()
pyramid_config.set_security_policy(WuttaSecurityPolicy())
This security policy will then do the following:
* use the request "web session" for auth storage (e.g. current
``user.uuid``)
* check permissions as needed, by calling
:meth:`~wuttjamaican:wuttjamaican.auth.AuthHandler.has_permission()`
for current user
:param db_session: Optional :term:`db session` to use, instead of
:class:`wuttaweb.db.Session`. Probably only useful for tests.
"""
def __init__(self, db_session=None):
self.session_helper = SessionAuthenticationHelper()
self.identity_cache = RequestLocalCache(self.load_identity)
self.db_session = db_session or Session()
def load_identity(self, request):
config = request.registry.settings['wutta_config']
app = config.get_app()
model = app.model
# fetch user uuid from current session
uuid = self.session_helper.authenticated_userid(request)
if not uuid:
return
# fetch user object from db
user = self.db_session.get(model.User, uuid)
if not user:
return
return user
def identity(self, request):
return self.identity_cache.get_or_create(request)
def authenticated_userid(self, request):
user = self.identity(request)
if user is not None:
return user.uuid
def remember(self, request, userid, **kw):
return self.session_helper.remember(request, userid, **kw)
def forget(self, request, **kw):
return self.session_helper.forget(request, **kw)
def permits(self, request, context, permission):
config = request.registry.settings['wutta_config']
app = config.get_app()
auth = app.get_auth_handler()
user = self.identity(request)
return auth.has_permission(self.db_session, user, permission)