CPI Ordering System (the old version)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

98 lines
3.5 KiB

''' User Authentication and Authorization '''
from passlib.context import CryptContext
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.security import Authenticated, Everyone
from pyramid.settings import aslist
from ordr.models.account import User
#: passlib context for hashing passwords
# at least one scheme must be set in advance, will be overridden by the
# settings in the .ini file.
password_context = CryptContext(schemes=['argon2'])
class AuthenticationPolicy(AuthTktAuthenticationPolicy):
''' How to authenticate users '''
def authenticated_userid(self, request):
''' returns the id of an authenticated user
heavy lifting done in get_user() attached to request
'''
user = request.user
if user is not None:
return user.id
def effective_principals(self, request):
''' returns a list of principals for the user '''
principals = [Everyone]
user = request.user
if user is not None:
principals.append(Authenticated)
principals.extend(user.principals)
return principals
def get_user(request):
''' retrieves the user object by the unauthenticated user id
:param pyramid.request.Request request:
the current request object
:rtype: :class:`ordr.models.account.User` or None
'''
user_id = request.unauthenticated_userid
if user_id is not None:
user = request.dbsession.query(User).filter_by(id=user_id).first()
if user and user.is_active:
return user
return None
def crypt_context_settings_to_string(settings, prefix='passlib.'):
''' returns a passlib context setting as a INI-formatted content
:param dict settings: settings for the crypt context
:param str prefix: prefix of the settings keys
:rtype: (str) config string in INI format for CryptContext.load()
This looks at first like a dump hack, but the parsing of all possible
context settings is quite a task. Since passlib has a context parser
included, this seems the most reliable way to do it.
'''
config_lines = ['[passlib]']
for ini_key, value in settings.items():
if ini_key.startswith(prefix):
context_key = ini_key.replace(prefix, '')
# the pyramid .ini format is different on lists
# than the .ini format used by passlib.
if context_key in {'schemes', 'deprecated'} and ',' not in value:
value = ','.join(aslist(value))
config_lines.append(f'{context_key} = {value}')
return '\n'.join(config_lines)
def includeme(config): # pragma: no cover
''' initializing authentication, authorization and password hash settings
Activate this setup using ``config.include('ordr.security')``.
'''
settings = config.get_settings()
# configure the passlib context manager for hashing user passwords
config_str = crypt_context_settings_to_string(settings, prefix='passlib.')
password_context.load(config_str)
# config for authentication and authorization
authn_policy = AuthenticationPolicy(
settings.get('auth.secret', ''),
hashalg='sha512',
)
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(ACLAuthorizationPolicy())
# attach the get_user function returned by get_user_closure()
config.add_request_method(get_user, 'user', reify=True)