Compare commits
No commits in common. "184aa0630d43804bdda19cfbee4c02ec4405bca5" and "09161ff072b74e5eb4b22bed21ff9703fbd7eb38" have entirely different histories.
184aa0630d
...
09161ff072
5 changed files with 2 additions and 236 deletions
|
|
@ -5,12 +5,6 @@ All notable changes to wuttaweb will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## v0.30.2 (2026-03-22)
|
||||
|
||||
### Fix
|
||||
|
||||
- add support for merging 2 roles
|
||||
|
||||
## v0.30.1 (2026-03-21)
|
||||
|
||||
### Fix
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|||
|
||||
[project]
|
||||
name = "WuttaWeb"
|
||||
version = "0.30.2"
|
||||
version = "0.30.1"
|
||||
description = "Web App for Wutta Framework"
|
||||
readme = "README.md"
|
||||
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
|
||||
|
|
|
|||
|
|
@ -3300,7 +3300,6 @@ class MasterView(View): # pylint: disable=too-many-public-methods
|
|||
removing_data,
|
||||
keeping_data,
|
||||
self.merge_get_final_data(removing_data, keeping_data),
|
||||
fields=self.merge_get_all_fields(),
|
||||
)
|
||||
|
||||
context = {"removing": removing, "keeping": keeping, "diff": diff}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# wuttaweb -- Web App for Wutta Framework
|
||||
# Copyright © 2024-2026 Lance Edgar
|
||||
# Copyright © 2024-2025 Lance Edgar
|
||||
#
|
||||
# This file is part of Wutta Framework.
|
||||
#
|
||||
|
|
@ -59,9 +59,6 @@ class RoleView(MasterView): # pylint: disable=abstract-method
|
|||
}
|
||||
sort_defaults = "name"
|
||||
|
||||
mergeable = True
|
||||
merge_additive_fields = ["permission_count", "user_count"]
|
||||
|
||||
wutta_permissions = None
|
||||
|
||||
# TODO: master should handle this, possibly via configure_form()
|
||||
|
|
@ -287,83 +284,6 @@ class RoleView(MasterView): # pylint: disable=abstract-method
|
|||
else:
|
||||
auth.revoke_permission(role, pkey)
|
||||
|
||||
def merge_get_data(self, obj): # pylint: disable=empty-docstring
|
||||
""" """
|
||||
data = super().merge_get_data(obj)
|
||||
role = obj
|
||||
|
||||
data["permissions"] = role.permissions
|
||||
data["permission_count"] = len(data["permissions"])
|
||||
|
||||
data["usernames"] = [user.username for user in role.users]
|
||||
data["user_count"] = len(data["usernames"])
|
||||
|
||||
return data
|
||||
|
||||
def merge_get_final_data(
|
||||
self, removing, keeping
|
||||
): # pylint: disable=empty-docstring
|
||||
""" """
|
||||
final = super().merge_get_final_data(removing, keeping)
|
||||
|
||||
permissions = set(removing["permissions"] + keeping["permissions"])
|
||||
final["permission_count"] = len(permissions)
|
||||
|
||||
usernames = set(removing["usernames"] + keeping["usernames"])
|
||||
final["user_count"] = len(usernames)
|
||||
|
||||
return final
|
||||
|
||||
def merge_why_not(self, removing, keeping):
|
||||
"""
|
||||
This checks to ensure the "removing" role is not one of the
|
||||
special built-in roles (Administrator, Authenticated,
|
||||
Anonymous).
|
||||
|
||||
See also parent method:
|
||||
:meth:`~wuttaweb.views.master.MasterView.merge_why_not()`
|
||||
"""
|
||||
auth = self.app.get_auth_handler()
|
||||
session = self.Session()
|
||||
|
||||
if removing is auth.get_role_administrator(session):
|
||||
return "Cannot remove the Administrator role."
|
||||
|
||||
if removing is auth.get_role_anonymous(session):
|
||||
return "Cannot remove the Anonymous role."
|
||||
|
||||
if removing is auth.get_role_authenticated(session):
|
||||
return "Cannot remove the Authenticated role."
|
||||
|
||||
return None
|
||||
|
||||
def merge_execute(self, removing, keeping):
|
||||
"""
|
||||
The logic to merge 2 roles is extended as follows:
|
||||
|
||||
Any users belonging to the "removing" role will be added to
|
||||
the "keeping" role (if not already present).
|
||||
|
||||
Any permissions belonging to the "removing" role will be added
|
||||
to the "keeping" role (if not already present).
|
||||
|
||||
See also parent method:
|
||||
:meth:`~wuttaweb.views.master.MasterView.merge_execute()`
|
||||
"""
|
||||
|
||||
# transfer permissions
|
||||
for perm in list(removing.permissions):
|
||||
if perm not in keeping.permissions:
|
||||
keeping.permissions.append(perm)
|
||||
|
||||
# transfer users
|
||||
for user in list(removing.users):
|
||||
if user not in keeping.users:
|
||||
keeping.users.append(user)
|
||||
|
||||
# continue default merge
|
||||
super().merge_execute(removing, keeping)
|
||||
|
||||
@classmethod
|
||||
def defaults(cls, config): # pylint: disable=empty-docstring
|
||||
""" """
|
||||
|
|
|
|||
|
|
@ -284,153 +284,6 @@ class TestRoleView(WebTestCase):
|
|||
self.assertIs(role, blokes)
|
||||
self.assertEqual(blokes.permissions, ["widgets.polish", "widgets.view"])
|
||||
|
||||
def test_merge_get_data(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
|
||||
role = model.Role(name="whatever")
|
||||
auth.grant_permission(role, "people.list")
|
||||
auth.grant_permission(role, "people.view")
|
||||
auth.grant_permission(role, "people.edit")
|
||||
self.session.add(role)
|
||||
|
||||
user1 = model.User(username="user1")
|
||||
user1.roles.append(role)
|
||||
self.session.add(user1)
|
||||
|
||||
user2 = model.User(username="user2")
|
||||
user2.roles.append(role)
|
||||
self.session.add(user2)
|
||||
|
||||
self.session.commit()
|
||||
self.assertEqual(len(role.permissions), 3)
|
||||
self.assertEqual(len(role.users), 2)
|
||||
|
||||
view = self.make_view()
|
||||
with patch.object(view, "Session", return_value=self.session):
|
||||
data = view.merge_get_data(role)
|
||||
self.assertEqual(
|
||||
sorted(data["permissions"]),
|
||||
["people.edit", "people.list", "people.view"],
|
||||
)
|
||||
self.assertEqual(data["permission_count"], 3)
|
||||
self.assertEqual(data["usernames"], ["user1", "user2"])
|
||||
self.assertEqual(data["user_count"], 2)
|
||||
|
||||
def test_merge_get_final_data(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
|
||||
role1 = model.Role(name="whatever1")
|
||||
auth.grant_permission(role1, "people.list")
|
||||
auth.grant_permission(role1, "people.view")
|
||||
auth.grant_permission(role1, "people.edit")
|
||||
self.session.add(role1)
|
||||
|
||||
role2 = model.Role(name="whatever2")
|
||||
auth.grant_permission(role2, "people.list")
|
||||
auth.grant_permission(role2, "people.view")
|
||||
self.session.add(role2)
|
||||
|
||||
user1 = model.User(username="user1")
|
||||
user1.roles.append(role1)
|
||||
self.session.add(user1)
|
||||
|
||||
user2 = model.User(username="user2")
|
||||
user2.roles.append(role1)
|
||||
user2.roles.append(role2)
|
||||
self.session.add(user2)
|
||||
|
||||
self.session.commit()
|
||||
self.assertEqual(len(role1.permissions), 3)
|
||||
self.assertEqual(len(role1.users), 2)
|
||||
self.assertEqual(len(role2.permissions), 2)
|
||||
self.assertEqual(len(role2.users), 1)
|
||||
|
||||
view = self.make_view()
|
||||
with patch.object(view, "Session", return_value=self.session):
|
||||
removing = view.merge_get_data(role1)
|
||||
keeping = view.merge_get_data(role2)
|
||||
final = view.merge_get_final_data(removing, keeping)
|
||||
self.assertEqual(final["permission_count"], 3)
|
||||
self.assertEqual(final["user_count"], 2)
|
||||
|
||||
def test_merge_why_not(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
|
||||
role1 = model.Role(name="whatever1")
|
||||
self.session.add(role1)
|
||||
role2 = model.Role(name="whatever2")
|
||||
self.session.add(role2)
|
||||
self.session.commit()
|
||||
|
||||
view = self.make_view()
|
||||
with patch.object(view, "Session", return_value=self.session):
|
||||
|
||||
# normal merge is allowed
|
||||
self.assertIsNone(view.merge_why_not(role1, role2))
|
||||
|
||||
# special roles can be part of a merge if they are being "kept"
|
||||
# but not if being "removed"
|
||||
|
||||
admin = auth.get_role_administrator(self.session)
|
||||
self.assertIsNone(view.merge_why_not(role1, admin))
|
||||
reason = view.merge_why_not(admin, role1)
|
||||
self.assertEqual(reason, "Cannot remove the Administrator role.")
|
||||
|
||||
authed = auth.get_role_authenticated(self.session)
|
||||
self.assertIsNone(view.merge_why_not(role1, authed))
|
||||
reason = view.merge_why_not(authed, role1)
|
||||
self.assertEqual(reason, "Cannot remove the Authenticated role.")
|
||||
|
||||
anon = auth.get_role_anonymous(self.session)
|
||||
self.assertIsNone(view.merge_why_not(role1, anon))
|
||||
reason = view.merge_why_not(anon, role1)
|
||||
self.assertEqual(reason, "Cannot remove the Anonymous role.")
|
||||
|
||||
def test_merge_execute(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
|
||||
role1 = model.Role(name="whatever1")
|
||||
auth.grant_permission(role1, "people.list")
|
||||
auth.grant_permission(role1, "people.view")
|
||||
auth.grant_permission(role1, "people.edit")
|
||||
self.session.add(role1)
|
||||
|
||||
role2 = model.Role(name="whatever2")
|
||||
auth.grant_permission(role2, "people.list")
|
||||
auth.grant_permission(role2, "people.view")
|
||||
self.session.add(role2)
|
||||
|
||||
user1 = model.User(username="user1")
|
||||
user1.roles.append(role1)
|
||||
self.session.add(user1)
|
||||
|
||||
user2 = model.User(username="user2")
|
||||
user2.roles.append(role1)
|
||||
user2.roles.append(role2)
|
||||
self.session.add(user2)
|
||||
|
||||
self.session.commit()
|
||||
self.assertEqual(self.session.query(model.Role).count(), 2)
|
||||
self.assertEqual(len(role1.permissions), 3)
|
||||
self.assertEqual(len(role1.users), 2)
|
||||
self.assertEqual(len(role2.permissions), 2)
|
||||
self.assertEqual(len(role2.users), 1)
|
||||
|
||||
view = self.make_view()
|
||||
with patch.object(view, "Session", return_value=self.session):
|
||||
view.merge_execute(role1, role2)
|
||||
self.session.commit()
|
||||
self.assertEqual(self.session.query(model.Role).count(), 1)
|
||||
self.assertNotIn(role1, self.session)
|
||||
self.assertIn(role2, self.session)
|
||||
self.assertIs(role2, self.session.query(model.Role).one())
|
||||
self.assertEqual(len(role2.permissions), 3)
|
||||
self.assertEqual(len(role2.users), 2)
|
||||
|
||||
|
||||
class TestPermissionView(WebTestCase):
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue