Holger Frey
7 years ago
3 changed files with 233 additions and 0 deletions
@ -0,0 +1,55 @@
@@ -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 @@
@@ -1 +1,74 @@
|
||||
''' 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 @@
@@ -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