From d13885db0659e87191b57f7249567312420a39e3 Mon Sep 17 00:00:00 2001 From: Guillaume Viger Date: Wed, 1 May 2024 09:36:45 -0400 Subject: [PATCH] permissions: add can_request_membership --- invenio_communities/generators.py | 22 ++++++- invenio_communities/permissions.py | 19 +++++- .../invenio_communities/details/header.html | 4 +- invenio_communities/views/communities.py | 1 + tests/communities/test_permissions.py | 63 +++++++++++++++++++ 5 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 tests/communities/test_permissions.py diff --git a/invenio_communities/generators.py b/invenio_communities/generators.py index e18a6320f..2a42056e2 100644 --- a/invenio_communities/generators.py +++ b/invenio_communities/generators.py @@ -17,7 +17,7 @@ from itertools import chain from flask_principal import UserNeed -from invenio_access.permissions import any_user, system_process +from invenio_access.permissions import any_user, authenticated_user, system_process from invenio_records_permissions.generators import Generator from invenio_search.engine import dsl @@ -199,6 +199,26 @@ def query_filter(self, **kwargs): # # Community membership generators # + +class AuthenticatedButNotCommunityMembers(Generator): + """Authenticated user not part of community.""" + + def needs(self, record=None, **kwargs): + """Required needs.""" + return [authenticated_user] + + def excludes(self, record=None, **kwargs): + """Exluding needs. + + Excludes identities with a role in the community. This assumes all roles at + this point mean valid memberships. This is the same assumption as + `CommunityMembers` below. + """ + if not record: + return [] + community_id = str(record.id) + return [CommunityRoleNeed(community_id, r.name) for r in current_roles] + class CommunityRoles(Generator): """Base class for community roles generators.""" diff --git a/invenio_communities/permissions.py b/invenio_communities/permissions.py index 196912233..34e78c50e 100644 --- a/invenio_communities/permissions.py +++ b/invenio_communities/permissions.py @@ -25,6 +25,7 @@ from .generators import ( AllowedMemberTypes, + AuthenticatedButNotCommunityMembers, CommunityCurators, CommunityManagers, CommunityManagersForRole, @@ -179,9 +180,25 @@ class CommunityPermissionPolicy(BasePermissionPolicy): # Permissions to set if communities can have children can_manage_children = [SystemProcess()] - # Permission for assinging a parent community + # Permission for assigning a parent community can_manage_parent = [Administration(), SystemProcess()] + # request_membership permission is based on configuration, community settings and + # identity. Other factors (e.g., previous membership requests) are not under + # its purview and are dealt with elsewhere. + can_request_membership = [ + IfConfig( + "COMMUNITIES_ALLOW_MEMBERSHIP_REQUESTS", + then_=[ + IfPolicyClosed( + "member_policy", + then_=[Disable()], + else_=[AuthenticatedButNotCommunityMembers()] + ) + ], + else_=[Disable()] + ), + ] def can_perform_action(community, context): """Check if the given action is available on the request.""" diff --git a/invenio_communities/templates/semantic-ui/invenio_communities/details/header.html b/invenio_communities/templates/semantic-ui/invenio_communities/details/header.html index ea74b4fad..0f5f9ebbf 100644 --- a/invenio_communities/templates/semantic-ui/invenio_communities/details/header.html +++ b/invenio_communities/templates/semantic-ui/invenio_communities/details/header.html @@ -12,8 +12,8 @@ {%- from "invenio_communities/details/macros/access-status-label.html" import access_status_label -%} {% macro button_to_request_membership(community) %} - {# TODO: replace by permission check when permissions implemented #} - {% if config.COMMUNITIES_ALLOW_MEMBERSHIP_REQUESTS %} + {% if permissions.can_request_membership %} + {# TODO: Add relation_to_community for other flows #}