Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Ronald Krist committed Sep 26, 2023
1 parent ceebc4d commit 6df4aa7
Show file tree
Hide file tree
Showing 21 changed files with 331 additions and 0 deletions.
91 changes: 91 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# Idea software family
.idea/

# C extensions
*.so

# Distribution / packaging
.Python
env/
venv/
.venv/
build/
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover

# Translations
*.mo

# Django stuff:
*.log

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Vim swapfiles
.*.sw?

tests/test.db

.venv
.direnv

docs/migration/data

.env
.envrc
/.python-version
/poetry.lock
example/data
.DS_Store

test-model

# Testing
sample/

tests/test-sample-app
tests/test-sample-site

example_document/
dist/

.model_venv/
.vscode
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (C) 2021 CESNET.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# OARepo requests
3 changes: 3 additions & 0 deletions format.sh
Original file line number Diff line number Diff line change
@@ -0,0 +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
Empty file added oarepo_communities/__init__.py
Empty file.
Empty file.
8 changes: 8 additions & 0 deletions oarepo_communities/cf/loader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from invenio_communities.communities.records.api import Community as InvenioCommunityRecord
from flask import current_app

def get_field(record_class):
if str(record_class).find("invenio_communities.communities.records.api.Community") > 0:
custom_field = getattr(record_class, "custom_fields")
return None, current_app.config[custom_field.config_key]
return None
18 changes: 18 additions & 0 deletions oarepo_communities/cf/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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}

@property
def field(self):
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 },
# "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},
# }
18 changes: 18 additions & 0 deletions oarepo_communities/cf/roles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from invenio_records_resources.services.custom_fields import BaseCF
from marshmallow import fields as ma_fields

class CommunityRolesToGroupsCF(BaseCF):
""""""
@property
def mapping(self):
return {"type": "object", "dynamic": True}

@property
def field(self):
return ma_fields.Dict(keys=ma_fields.String, values=ma_fields.Dict(keys=ma_fields.String, values=ma_fields.String))
# 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 },
# "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},
# }
Empty file.
35 changes: 35 additions & 0 deletions oarepo_communities/components/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from collections import defaultdict

from invenio_records_resources.services.records.components import ServiceComponent
from invenio_communities.proxies import current_communities

from oarepo_communities.proxies import current_communities_permissions


class AllowedActionsComponent(ServiceComponent):
def _get_available_actions(self, identity, record, dict_to_save_result, **kwargs):
record_communities = set(record["parent"]["communities"]["ids"])

usercommunities2roles = defaultdict(list)
communities_permissions = {}
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)


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}

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)
20 changes: 20 additions & 0 deletions oarepo_communities/ext.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from .permissions import permissions_cache


class OArepoCommunities(object):
"""Invenio extension."""

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):
self.permissions_cache = permissions_cache

Empty file.
48 changes: 48 additions & 0 deletions oarepo_communities/generators/record.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from collections import defaultdict

from cachetools import TTLCache, cached
from invenio_records_permissions.generators import (
Generator,
)
from invenio_communities.generators import CommunityRoleNeed
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 c, roles in community2role_list.items():
for role in roles:
_needs.add(CommunityRoleNeed(c, role))
return _needs
return []


@cached(cache=TTLCache(maxsize=1028, ttl=360))
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 c, 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][c].append(role)
return by_actions
9 changes: 9 additions & 0 deletions oarepo_communities/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from functools import lru_cache

from invenio_access.permissions import system_identity
from invenio_communities import current_communities
import cachetools

@cachetools.cached(cache=cachetools.TTLCache(maxsize=1028, ttl=360))
def permissions_cache(community_id):
return current_communities.service.read(system_identity, community_id).data["custom_fields"]["permissions"]
4 changes: 4 additions & 0 deletions oarepo_communities/proxies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from flask import current_app
from werkzeug.local import LocalProxy

current_communities_permissions = LocalProxy(lambda: current_app.extensions["oarepo-communities"].permissions_cache)
Empty file.
23 changes: 23 additions & 0 deletions oarepo_communities/utils/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
def get_allowed_actions(record, identity=None):
record_communities = set(record["parent"]["communities"]["ids"])
user_community_roles = defaultdict(list)
communities_permissions = {}
for record_community_id in record_communities:
if need.method == "community" and need.value in record_communities:
for need in identity.provides:
user_community_roles[need.value].append(need.role)
communities_permissions[need.value] = \
current_communities_permissions(need.value)
allowed_actions_for_record_and_user = set()
for user_community, user_roles_community in user_community_roles.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}
"""
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build_system]
requires = ["setuptools", "wheel", "babel>2.8"]
build-backend = "setuptools.build_meta"
26 changes: 26 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[metadata]
name = oarepo-communities
version = 1.0.0
description =
authors = Ronald Krist <[email protected]>
readme = README.md
long_description = file:README.md
long_description_content_type = text/markdown


[options]
python = >=3.9
install_requires =
oarepo>=11,<12
#packages = find:

[options.package_data]
* = *.json, *.rst, *.md, *.json5, *.jinja2

[options.entry_points]
oarepo.custom_fields = record_field = oarepo_communities.cf.loader:get_field
invenio_base.apps =
oarepo_vocabularies = oarepo_communities.ext:OArepoCommunities
invenio_base.api_apps =
oarepo_vocabularies = oarepo_communities.ext:OArepoCommunities

3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from setuptools import setup

setup()

0 comments on commit 6df4aa7

Please sign in to comment.