Skip to content

Commit

Permalink
community records pt2
Browse files Browse the repository at this point in the history
  • Loading branch information
Ronald Krist committed Oct 24, 2023
1 parent 1ce6780 commit c8d153b
Show file tree
Hide file tree
Showing 28 changed files with 450 additions and 210 deletions.
6 changes: 3 additions & 3 deletions format.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
black oarepo_requests tests --target-version py310
autoflake --in-place --remove-all-unused-imports --recursive oarepo_requests tests
isort oarepo_requests tests --profile black
black oarepo_communities tests --target-version py310
autoflake --in-place --remove-all-unused-imports --recursive oarepo_communities tests
isort oarepo_communities tests --profile black
16 changes: 16 additions & 0 deletions oarepo_communities/cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from cachetools import TTLCache, cached
from invenio_access.permissions import system_identity
from invenio_communities import current_communities


@cached(cache=TTLCache(maxsize=1028, ttl=600))
def permissions_cache(community_id):
return current_communities.service.read(system_identity, community_id).data[
"custom_fields"
]["permissions"]


def usermap(community_id):
return current_communities.service.read(system_identity, community_id).data[
"custom_fields"
]["usermap"]
11 changes: 8 additions & 3 deletions oarepo_communities/cf/loader.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from flask import current_app


def get_field(record_class):
if str(record_class).find("invenio_communities.communities.records.api.Community") > 0:
return "custom_fields", current_app.config["COMMUNITIES_CUSTOM_FIELDS"]
return None
if (
str(record_class).find("invenio_communities.communities.records.api.Community")
> 0
and "COMMUNITIES_CUSTOM_FIELDS" in current_app.config
):
return current_app.config["COMMUNITIES_CUSTOM_FIELDS"]
return None
17 changes: 15 additions & 2 deletions oarepo_communities/cf/permissions.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
from invenio_records_resources.services.custom_fields import BaseCF
from marshmallow import fields as ma_fields


class PermissionsCF(BaseCF):
""""""

@property
def mapping(self):
return {"type": "object", "dynamic": True}
return {
"custom_fields": {
"properties": {"permissions": {"type": "object", "dynamic": True}}
}
}

@property
def mapping_settings(self):
return {}

@property
def field(self):
return ma_fields.Dict(keys=ma_fields.String, values=ma_fields.Dict(keys=ma_fields.String, values=ma_fields.Boolean))
return ma_fields.Dict(
keys=ma_fields.String,
values=ma_fields.Dict(keys=ma_fields.String, values=ma_fields.Boolean),
)
# example {
# "owner": {"can_create": true , "can_read": true, "can_update": true , "can_delete":true },
# "manager": {"can_create": true , "can_read": true, "can_update": true , "can_delete":true },
Expand Down
4 changes: 3 additions & 1 deletion oarepo_communities/cf/user_map.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from invenio_records_resources.services.custom_fields import BaseCF
from marshmallow import fields as ma_fields


class UserMapCF(BaseCF):
""""""

@property
def mapping(self):
return {"type": "object", "dynamic": True}
Expand All @@ -15,4 +17,4 @@ def field(self):
# "manager": {"can_create": true , "can_read": true, "can_update": true , "can_delete":true },
# "curator": {"can_create": true , "can_read": true, "can_update": true , "can_delete":false},
# "reader": {"can_create": false, "can_read": true, "can_update": false, "can_delete":false},
# }
# }
22 changes: 13 additions & 9 deletions oarepo_communities/components/actions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from collections import defaultdict

from invenio_records_resources.services.records.components import ServiceComponent
from oarepo_runtime.communities.proxies import current_communities_permissions
from ..proxies import current_communities_permissions


class AllowedActionsComponent(ServiceComponent):
Expand All @@ -13,21 +13,25 @@ def _get_available_actions(self, identity, record, dict_to_save_result, **kwargs
for need in identity.provides:
if need.method == "community" and need.value in record_communities:
usercommunities2roles[need.value].append(need.role)
communities_permissions[need.value] = \
current_communities_permissions(need.value)

communities_permissions[need.value] = current_communities_permissions(
need.value
)

allowed_actions_for_record_and_user = set()
for user_community, user_roles_community in usercommunities2roles.items():
if user_community in record_communities:
for user_role_community in user_roles_community:
permissions = communities_permissions[user_community][user_role_community]
allowed_actions_for_record_and_user |= {permission for permission, allowed in permissions.items() if
allowed}
permissions = communities_permissions[user_community][
user_role_community
]
allowed_actions_for_record_and_user |= {
permission
for permission, allowed in permissions.items()
if allowed
}

dict_to_save_result = kwargs[dict_to_save_result]
dict_to_save_result["allowed_actions"] = allowed_actions_for_record_and_user


def before_ui_detail(self, identity, data=None, record=None, errors=None, **kwargs):
self._get_available_actions(identity, record, "extra_context", **kwargs)
self._get_available_actions(identity, record, "extra_context", **kwargs)
30 changes: 30 additions & 0 deletions oarepo_communities/ext.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from .cache import permissions_cache


class OARepoCommunities(object):
"""OARepo extension of Invenio-Vocabularies."""

def __init__(self, app=None):
"""Extension initialization."""
if app:
self.init_app(app)

def init_app(self, app):
"""Flask application initialization."""
self.init_config(app)
app.extensions["oarepo-communities"] = self

def init_config(self, app):
"""Initialize configuration."""
from . import ext_config

if "OAREPO_PERMISSIONS_PRESETS" not in app.config:
app.config["OAREPO_PERMISSIONS_PRESETS"] = {}

for k in ext_config.OAREPO_PERMISSIONS_PRESETS:
if k not in app.config["OAREPO_PERMISSIONS_PRESETS"]:
app.config["OAREPO_PERMISSIONS_PRESETS"][
k
] = ext_config.OAREPO_PERMISSIONS_PRESETS[k]

self.permissions_cache = permissions_cache
5 changes: 5 additions & 0 deletions oarepo_communities/ext_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from oarepo_communities.permissions.presets import CommunityPermissionPolicy

OAREPO_PERMISSIONS_PRESETS = {
"community": CommunityPermissionPolicy,
}
Empty file.
42 changes: 42 additions & 0 deletions oarepo_communities/permissions/presets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from invenio_records_permissions import RecordPermissionPolicy
from invenio_records_permissions.generators import (
AnyUser,
AuthenticatedUser,
SystemProcess,
)

from .record import RecordCommunitiesGenerator


class CommunityPermissionPolicy(RecordPermissionPolicy):
can_search = [SystemProcess(), AnyUser()]
can_read = [SystemProcess(), RecordCommunitiesGenerator("can_read")]
can_create = [SystemProcess(), AuthenticatedUser()]
can_update = [SystemProcess(), RecordCommunitiesGenerator("can_update")]
can_delete = [SystemProcess(), RecordCommunitiesGenerator("can_delete")]
can_manage = [SystemProcess()]

can_create_files = [SystemProcess()]
can_set_content_files = [SystemProcess()]
can_get_content_files = [AnyUser(), SystemProcess()]
can_commit_files = [SystemProcess()]
can_read_files = [AnyUser(), SystemProcess()]
can_update_files = [SystemProcess()]
can_delete_files = [SystemProcess()]

can_edit = [SystemProcess()]
can_new_version = [SystemProcess()]
can_search_drafts = [SystemProcess()]
can_read_draft = [SystemProcess()]
can_update_draft = [SystemProcess()]
can_delete_draft = [SystemProcess()]
can_publish = [SystemProcess(), RecordCommunitiesGenerator("can_publish")]
can_draft_create_files = [SystemProcess()]
can_draft_set_content_files = [SystemProcess()]
can_draft_get_content_files = [SystemProcess()]
can_draft_commit_files = [SystemProcess()]
can_draft_read_files = [SystemProcess()]
can_draft_update_files = [SystemProcess()]

can_add_community = [SystemProcess(), AuthenticatedUser()]
can_remove_community = [SystemProcess(), AuthenticatedUser()]
65 changes: 65 additions & 0 deletions oarepo_communities/permissions/record.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from collections import defaultdict

from cachetools import TTLCache, cached
from invenio_communities.generators import CommunityRoleNeed
from invenio_records_permissions.generators import Generator

from ..proxies import current_communities_permissions


class RecordCommunitiesGenerator(Generator):
"""Allows system_process role."""

def __init__(self, action):
self.action = action

def needs(self, **kwargs):
_needs = set()
if "record" in kwargs and hasattr(kwargs["record"], "parent"):
record = kwargs["record"]
try:
community_ids = record.parent["communities"]["ids"]
except KeyError:
return []
by_actions = record_community_permissions(frozenset(community_ids))
if self.action in by_actions:
community2role_list = by_actions[self.action]
for community_id, roles in community2role_list.items():
for role in roles:
_needs.add(CommunityRoleNeed(community_id, role))
return _needs
return []

"""
def communities(self, identity):
roles = self.roles()
community_ids = set()
for n in identity.provides:
if n.method == "community" and n.role in roles:
community_ids.add(n.value)
return list(community_ids)
def query_filter(self, identity=None, **kwargs):
return dsl.Q("terms", **{"parent.communities.ids": self.communities(identity)})
"""


@cached(cache=TTLCache(maxsize=1028, ttl=600))
def record_community_permissions(record_communities):
communities_permissions = {}

for record_community_id in record_communities:
communities_permissions[record_community_id] = current_communities_permissions(
record_community_id
)

by_actions = defaultdict(lambda: defaultdict(list))
for community_id, role_permissions_dct in communities_permissions.items():
for role, role_permissions in role_permissions_dct.items():
for action, val in role_permissions.items():
if val:
by_actions[action][community_id].append(role)
return by_actions
6 changes: 6 additions & 0 deletions oarepo_communities/proxies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from flask import current_app
from werkzeug.local import LocalProxy

current_communities_permissions = LocalProxy(
lambda: current_app.extensions["oarepo-communities"].permissions_cache
)
9 changes: 4 additions & 5 deletions oarepo_communities/resources/community_records/config.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from invenio_drafts_resources.resources import RecordResourceConfig

from invenio_records_resources.services.base.config import ConfiguratorMixin, FromConfig
from invenio_records_resources.services.base.config import ConfiguratorMixin


class CommunityRecordsResourceConfig(RecordResourceConfig, ConfiguratorMixin):
"""Community's records resource config."""

#blueprint_name = "community-records"
#url_prefix = "/communities"
routes = {"list": "/<pid_value>/records"}
# blueprint_name = "community-records"
# url_prefix = "/communities"
routes = {"list": "/<pid_value>/records"}
11 changes: 3 additions & 8 deletions oarepo_communities/resources/community_records/resource.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@

from flask import g

from flask_resources import (
resource_requestctx,
response_handler,
route,
)
from flask_resources import resource_requestctx, response_handler, route
from invenio_drafts_resources.resources import RecordResource
from invenio_records_resources.resources.records.resource import (
request_data,
Expand All @@ -14,6 +8,7 @@
)
from invenio_records_resources.resources.records.utils import search_preference


class CommunityRecordsResource(RecordResource):
"""RDM community's records resource."""

Expand Down Expand Up @@ -61,4 +56,4 @@ def delete(self):
response = {}
if errors:
response["errors"] = errors
return response, 200
return response, 200
7 changes: 3 additions & 4 deletions oarepo_communities/resources/record_communities/config.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import marshmallow as ma
from invenio_communities.communities.resources import CommunityResourceConfig
from invenio_communities.communities.resources.config import community_error_handlers

from invenio_records_resources.services.base.config import ConfiguratorMixin


class RecordCommunitiesResourceConfig(CommunityResourceConfig, ConfiguratorMixin):
"""Record communities resource config."""

#blueprint_name = "records-community"
#url_prefix = "/nr-documents/"
# blueprint_name = "records-community"
# url_prefix = "/nr-documents/"
routes = {
"list": "/<pid_value>/communities",
"suggestions": "/<pid_value>/communities-suggestions",
Expand All @@ -21,4 +20,4 @@ class RecordCommunitiesResourceConfig(CommunityResourceConfig, ConfiguratorMixin
"membership": ma.fields.Boolean(),
}

error_handlers = community_error_handlers
error_handlers = community_error_handlers
22 changes: 3 additions & 19 deletions oarepo_communities/resources/record_communities/resource.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,8 @@

from flask import abort, current_app, g, send_file
from flask_cors import cross_origin
from flask_resources import (
HTTPJSONException,
Resource,
ResponseHandler,
from_conf,
request_parser,
resource_requestctx,
response_handler,
route,
with_content_negotiation,
)

from flask import g
from flask_resources import Resource, resource_requestctx, response_handler, route
from invenio_records_resources.resources.errors import ErrorHandlersMixin
from invenio_records_resources.resources.records.resource import (
request_data,
request_extra_args,
request_headers,
request_read_args,
request_search_args,
request_view_args,
)
Expand Down Expand Up @@ -113,4 +97,4 @@ def remove(self):
if errors:
response["errors"] = errors

return response, 200 if len(processed) > 0 else 400
return response, 200 if len(processed) > 0 else 400
Loading

0 comments on commit c8d153b

Please sign in to comment.