Skip to content

Commit

Permalink
Add a security policy and protect create character action
Browse files Browse the repository at this point in the history
  • Loading branch information
davismr committed Jan 5, 2025
1 parent b0f49cd commit 4f70ca0
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/wfrp/character/forms/create/new_character.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from wfrp.character.models.character import Character


@view_defaults(route_name="new_character")
@view_defaults(route_name="new_character", permission="create_character")
class NewCharacterViews:
def __init__(self, request):
self.request = request
Expand Down
18 changes: 17 additions & 1 deletion src/wfrp/character/security.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import bcrypt
from pyramid.authentication import AuthTktCookieHelper
from pyramid.security import Allowed
from pyramid.security import Denied

from wfrp.character.models.user import User


def hash_password(pw):
Expand All @@ -21,7 +25,9 @@ def __init__(self, secret):

def identity(self, request):
identity = self.authtkt.identify(request)
if identity is not None and identity["userid"] in USERS:
if identity is not None:
# XXX needs to be wrapped in try/except
request.dbsession.query(User).filter(User.email == identity["userid"]).one()
return identity

def authenticated_userid(self, request):
Expand All @@ -34,3 +40,13 @@ def remember(self, request, userid, **kw):

def forget(self, request, **kw):
return self.authtkt.forget(request, **kw)

def permits(self, request, context, permission):
identity = self.identity(request)
if identity is None:
return Denied("User is not signed in.")
if identity and permission == "create_character":
return Allowed(
f"Access granted for user {identity} for {permission} permission."
)
return Denied(f"Access denied for user {identity} for {permission} permission.")
6 changes: 6 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from wfrp.character.application import DBSession
from wfrp.character.application import dbsession
from wfrp.character.models.character import Character
from wfrp.character.security import SecurityPolicy

register(CharacterFactory)

Expand All @@ -20,6 +21,11 @@ def testapp():
engine = engine_from_config({"sqlalchemy.url": "sqlite:///:memory:"}, "sqlalchemy.")
config = testing.setUp()
config.add_request_method(dbsession, reify=True)
config.set_security_policy(
SecurityPolicy(
secret="secret",
),
)
config.include("pyramid_chameleon")
config.include("wfrp.character.routes")
config.add_static_view("static", "wfrp.character:static")
Expand Down
20 changes: 11 additions & 9 deletions tests/test_register.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import pytest
from pyramid import testing
from pyramid.httpexceptions import HTTPFound
from pyramid.httpexceptions import HTTPForbidden

from wfrp.character.application import DBSession
from wfrp.character.models.character import Character
from wfrp.character.models.user import User
from wfrp.character.views.auth import AuthViews


@pytest.mark.register
Expand All @@ -15,9 +14,7 @@ def test_register(testapp):
"password": "a_password",
"form.submitted": "Register",
}
request = testing.DummyRequest(post=payload)
response = AuthViews(request).register()
assert isinstance(response, HTTPFound)
testapp.post("/register", payload, status=302)
users = DBSession.query(User).all()
assert len(users) == 1
user = users[0]
Expand All @@ -28,6 +25,11 @@ def test_register(testapp):
"password": "a_password",
"form.submitted": "Log In",
}
request = testing.DummyRequest(post=payload)
response = AuthViews(request).login()
assert isinstance(response, HTTPFound)
testapp.post("/login", payload, status=302)
character_count = DBSession.query(Character).count()
testapp.get("/character/new", status=302)
assert DBSession.query(Character).count() == character_count + 1
testapp.get("/logout", status=302)
with pytest.raises(HTTPForbidden) as error:
testapp.get("/character/new")
assert str(error.value) == "Unauthorized: NewCharacterViews failed permission check"

0 comments on commit 4f70ca0

Please sign in to comment.