Holger Frey
7 years ago
3 changed files with 233 additions and 0 deletions
@ -0,0 +1,55 @@ |
|||||||
|
''' User Authentication and Authorization ''' |
||||||
|
|
||||||
|
from pyramid.authentication import AuthTktAuthenticationPolicy |
||||||
|
from pyramid.authorization import ACLAuthorizationPolicy |
||||||
|
from pyramid.security import Authenticated, Everyone |
||||||
|
|
||||||
|
from .models import User |
||||||
|
|
||||||
|
|
||||||
|
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.append(user.principal) |
||||||
|
principals.extend(user.role_principals) |
||||||
|
return principals |
||||||
|
|
||||||
|
|
||||||
|
def get_user(request): |
||||||
|
''' retrieves the user object by the unauthenticated user id ''' |
||||||
|
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 includeme(config): |
||||||
|
''' initializing authentication and authorization for the Pyramid app |
||||||
|
|
||||||
|
Activate this setup using ``config.include('ordr2.security')``. |
||||||
|
''' |
||||||
|
settings = config.get_settings() |
||||||
|
authn_policy = AuthenticationPolicy( |
||||||
|
settings['auth.secret'], |
||||||
|
hashalg='sha512', |
||||||
|
) |
||||||
|
config.set_authentication_policy(authn_policy) |
||||||
|
config.set_authorization_policy(ACLAuthorizationPolicy()) |
||||||
|
config.add_request_method(get_user, 'user', reify=True) |
@ -1 +1,74 @@ |
|||||||
''' Test package for ordr2. ''' |
''' Test package for ordr2. ''' |
||||||
|
|
||||||
|
import pytest |
||||||
|
import transaction |
||||||
|
|
||||||
|
from pyramid import testing |
||||||
|
|
||||||
|
|
||||||
|
APP_SETTINGS = { |
||||||
|
'sqlalchemy.url': 'sqlite:///:memory:', |
||||||
|
'auth.secret': 'not-very-secure', |
||||||
|
'session.secret': 'not-very-secure', |
||||||
|
'session.auto_csrf': True |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
# helpers |
||||||
|
|
||||||
|
def create_users(db): |
||||||
|
''' set up some well known example users ''' |
||||||
|
from ordr2.models import Role, User |
||||||
|
stubs = [ |
||||||
|
('Graham', 'Chapman', Role.UNVALIDATED), |
||||||
|
('John', 'Cleese', Role.NEW), |
||||||
|
('Terry', 'Gilliam', Role.USER), |
||||||
|
('Eric', 'Idle', Role.PURCHASER), |
||||||
|
('Terry', 'Jones', Role.ADMIN), |
||||||
|
('Michael', 'Palin', Role.INACTIVE) |
||||||
|
] |
||||||
|
for i, stub in enumerate(stubs): |
||||||
|
first_name, last_name, role = stub |
||||||
|
user = User( |
||||||
|
id=i+1, |
||||||
|
username=first_name + last_name, |
||||||
|
first_name = first_name, |
||||||
|
last_name = last_name, |
||||||
|
email = last_name.lower() + '@example.com', |
||||||
|
role=role, |
||||||
|
password_hash = first_name.lower() |
||||||
|
) |
||||||
|
db.add(user) |
||||||
|
|
||||||
|
|
||||||
|
# fixtures |
||||||
|
|
||||||
|
@pytest.fixture(scope='session') |
||||||
|
def app_config(): |
||||||
|
''' fixture for tests requiring a pyramid.testing setup ''' |
||||||
|
with testing.testConfig(settings=APP_SETTINGS) as config: |
||||||
|
config.include('pyramid_jinja2') |
||||||
|
#config.include('pyramid_mailer.testing') |
||||||
|
yield config |
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function') |
||||||
|
def dbsession(app_config): |
||||||
|
''' fixture for testing with database connection ''' |
||||||
|
from ordr2.models.meta import Base |
||||||
|
from ordr2.models import ( |
||||||
|
get_engine, |
||||||
|
get_session_factory, |
||||||
|
get_tm_session |
||||||
|
) |
||||||
|
|
||||||
|
settings = app_config.get_settings() |
||||||
|
engine = get_engine(settings) |
||||||
|
session_factory = get_session_factory(engine) |
||||||
|
session = get_tm_session(session_factory, transaction.manager) |
||||||
|
Base.metadata.create_all(engine) |
||||||
|
|
||||||
|
yield session |
||||||
|
|
||||||
|
transaction.abort() |
||||||
|
Base.metadata.drop_all(engine) |
||||||
|
@ -0,0 +1,105 @@ |
|||||||
|
''' Tests for ordr2.security ''' |
||||||
|
|
||||||
|
import pytest |
||||||
|
|
||||||
|
from . import app_config, dbsession, create_users |
||||||
|
|
||||||
|
|
||||||
|
# tests for ordr2.security.AuthenticationPolicy |
||||||
|
|
||||||
|
def test_authenticated_userid_no_user(): |
||||||
|
''' test if authenticated user id is None if no active user present ''' |
||||||
|
from pyramid.testing import DummyRequest |
||||||
|
from ordr2.security import AuthenticationPolicy |
||||||
|
|
||||||
|
request = DummyRequest(user=None) |
||||||
|
policy = AuthenticationPolicy(secret='') |
||||||
|
|
||||||
|
assert policy.authenticated_userid(request) is None |
||||||
|
|
||||||
|
|
||||||
|
def test_authenticated_userid_with_user(): |
||||||
|
''' test if authenticated user id is the id of the user ''' |
||||||
|
from pyramid.testing import DummyRequest |
||||||
|
from ordr2.models import User |
||||||
|
from ordr2.security import AuthenticationPolicy |
||||||
|
|
||||||
|
user = User(id=3) |
||||||
|
request = DummyRequest(user=user) |
||||||
|
policy = AuthenticationPolicy(secret='') |
||||||
|
|
||||||
|
assert policy.authenticated_userid(request) == 3 |
||||||
|
|
||||||
|
|
||||||
|
def test_effective_principals_no_user(): |
||||||
|
''' test the effective principals if no user is authenticated ''' |
||||||
|
from pyramid.testing import DummyRequest |
||||||
|
from pyramid.security import Everyone |
||||||
|
from ordr2.security import AuthenticationPolicy |
||||||
|
|
||||||
|
request = DummyRequest(user=None) |
||||||
|
policy = AuthenticationPolicy(secret='') |
||||||
|
|
||||||
|
assert policy.effective_principals(request) == [Everyone] |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize( |
||||||
|
'role_name, role_principals', [ |
||||||
|
('UNVALIDATED', ['role:unvalidated']), |
||||||
|
('NEW', ['role:new']), |
||||||
|
('USER', ['role:user']), |
||||||
|
('PURCHASER', ['role:purchaser', 'role:user']), |
||||||
|
('ADMIN', ['role:admin', 'role:purchaser', 'role:user']), |
||||||
|
('INACTIVE', ['role:inactive']) |
||||||
|
] |
||||||
|
) |
||||||
|
def test_effective_principals_with_user(role_name, role_principals): |
||||||
|
''' test the effective principals if a user is authenticated ''' |
||||||
|
from pyramid.testing import DummyRequest |
||||||
|
from pyramid.security import Authenticated, Everyone |
||||||
|
from ordr2.models import User, Role |
||||||
|
from ordr2.security import AuthenticationPolicy |
||||||
|
|
||||||
|
role = Role[role_name] |
||||||
|
user = User(id=3, role=role) |
||||||
|
request = DummyRequest(user=user) |
||||||
|
policy = AuthenticationPolicy(secret='') |
||||||
|
|
||||||
|
expected = [Everyone, Authenticated, 'user:3'] |
||||||
|
expected.extend(role_principals) |
||||||
|
assert policy.effective_principals(request) == expected |
||||||
|
|
||||||
|
|
||||||
|
# tests for the get_user function |
||||||
|
|
||||||
|
def test_get_user_no_unauthenticated_user_id(): |
||||||
|
''' get_user() should return None if unauthenticated_userid is None ''' |
||||||
|
from pyramid.testing import DummyRequest |
||||||
|
from ordr2.security import get_user |
||||||
|
|
||||||
|
request = DummyRequest(unauthenticated_userid=None) |
||||||
|
|
||||||
|
assert get_user(request) is None |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize( |
||||||
|
'user_id', [ |
||||||
|
3, # active user, must work |
||||||
|
pytest.mark.xfail(1), # inactive user, must fail |
||||||
|
pytest.mark.xfail(1969), # unknown user id, must fail |
||||||
|
] |
||||||
|
) |
||||||
|
def test_get_user_no_unauthenticated_user_id(user_id, dbsession): |
||||||
|
''' get_user() should return None if unauthenticated_userid is None ''' |
||||||
|
from collections import namedtuple |
||||||
|
from ordr2.models import User, Role |
||||||
|
from ordr2.security import get_user |
||||||
|
|
||||||
|
create_users(dbsession) |
||||||
|
# pyramid.testing.DummyRequest can't be used, since the parameter |
||||||
|
# unauthenticated_userid cannot be set. A named tuple is used instead |
||||||
|
Request = namedtuple('Request', 'dbsession, unauthenticated_userid') |
||||||
|
request = Request(dbsession=dbsession, unauthenticated_userid=user_id) |
||||||
|
user = get_user(request) |
||||||
|
|
||||||
|
assert isinstance(user, User) |
Reference in new issue