Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cb4e744
Switch back to invenio-communities but still disable header link
spilth Nov 11, 2025
c05835e
Policy and generators in place
spilth Nov 17, 2025
14c0252
Add missing Pipfile entry:
spilth Nov 17, 2025
5e420d2
Hardcoding ultraviolet Pipfile.lock entry
spilth Nov 18, 2025
387135c
Fix dependencies finally
spilth Nov 18, 2025
7c7924e
Remove commented out function
spilth Nov 18, 2025
d405b3e
Some generator tests passing
spilth Nov 19, 2025
b2d0512
wip
spilth Nov 19, 2025
dd71e89
Some tests passing, some not
spilth Nov 24, 2025
d4146cb
Merge branch 'integrate-ultraviolet-permissions-module' into v13_update
spilth Dec 3, 2025
4165cdb
V13 dependencies satisfied
spilth Dec 3, 2025
5e9fbd0
Update template and adjust config variable name
spilth Dec 3, 2025
d75770d
wip
spilth Dec 4, 2025
e72c252
Cleanup permissions (#253)
ekate Dec 5, 2025
06100a6
Merge branch 'integrate-ultraviolet-permissions-module' into v13_upda…
spilth Dec 5, 2025
5a8b9a8
wip - still no luck
spilth Dec 5, 2025
d6bed81
Community permission tests added
spilth Dec 10, 2025
bd4317c
Extract some fixtures for less repetition
spilth Dec 11, 2025
d628454
See if putting sqlalchemy dependency back helps CI
spilth Dec 11, 2025
18f93fe
Try an earlier version of PostgreSQL when running tests
spilth Dec 11, 2025
8f00d1d
Remove hardcoded PostgreSQL version
spilth Dec 11, 2025
5d9fa2e
Trying a different PostgreSQL version in docker compose
spilth Dec 11, 2025
bd6b309
Remove unused test configurations
spilth Dec 12, 2025
b901bd4
Remove database test that was used for debugging
spilth Dec 12, 2025
5ff0d1b
Try hardcoding SQLALCHEMY_DATABASE_URI for CI
spilth Dec 12, 2025
b7e1018
Try the same version of Selenium we previously used
spilth Dec 12, 2025
a4472c6
Revert Selenium version since it didn't help
spilth Dec 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ name: ci:test

on:
push:
branches:
- main
paths-ignore:
- "docs/**"
- "**.md"
Expand Down Expand Up @@ -66,7 +64,7 @@ jobs:
- name: "Run tests"
run: |
pipenv install --dev
E2E=yes pipenv run pytest -p no:cacheprovider
E2E=yes SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://ultraviolet:ultraviolet@localhost/ultraviolet pipenv run pytest -p no:cacheprovider
- name: "Upload screenshots"
uses: actions/upload-artifact@v4
if: failure()
Expand Down
5 changes: 2 additions & 3 deletions .invenio
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ description = NYU Data Repository InvenioRDM Instance
author_name = NYU
author_email = [email protected]
year = 2020
python_version = 3.8
python_version = 3.9
database = postgresql
opensearch = 2
search = opensearch2
file_storage = local
_template = https://github.com/inveniosoftware/cookiecutter-invenio-rdm.git
_project_dir = /Users/katepechekhonova/PycharmProjects/ultraviolet

[files]
app_data = {'README.md': 'd58978488626bdeddc791a15a39e341a4ed06f202b84d0c0d4aa9ef49481c6d9'}
Expand Down
18 changes: 8 additions & 10 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,21 @@ verify_ssl = true

[dev-packages]
check-manifest = ">=0.25"
pytest = "==6.2.5"
selenium = "*"
pytest-invenio = "==2.1.1"
flask-pytest = "*"
pytest = ">=6,<9.0.0"
pytest-invenio = "==3.4.2"
selenium = "*"

[packages]
ultraviolet-saml = {git = "https://github.com/nyudlts/ultraviolet-saml.git"}
ultraviolet-permissions = {git = "https://github.com/nyudlts/ultraviolet-permissions-module.git", ref="align-v12-update"}
invenio-communities = {git = "https://github.com/nyudlts/ultraviolet-communities.git", ref="sync_release_13.1.1"}
sqlalchemy = {version = "*", extras = ["asyncio"], markers = "platform_machine == 'x86_64' or platform_machine == 'arm64'"}
xmlsec = "==1.3.16"
python-dotenv = "*"
invenio-app-rdm = {extras = ["opensearch2"], version = "~=12.0.0"}
ultraviolet = {editable = true, path="./site"}
uwsgi = ">=2.0"
uwsgitop = ">=0.11"
uwsgi-tools = ">=1.1.1"
ultraviolet-saml = {git = "https://github.com/nyudlts/ultraviolet-saml.git", ref="v13_update"}
python-dotenv = "*"
typing-extensions = "*"
invenio-app-rdm = {extras = ["opensearch2"], version = "~=13.0.0"}
sqlalchemy = {version = "*", extras = ["asyncio"], markers = "platform_machine == 'x86_64' or platform_machine == 'arm64'"}

[requires]
python_version = "3.9"
Expand Down
4,905 changes: 2,707 additions & 2,198 deletions Pipfile.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions docker-compose.full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ services:
- web-ui
- web-api
ports:
- "80:80"
- "443:443"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:80:80"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:443:443"
# UI Application
web-ui:
extends:
Expand All @@ -65,7 +65,7 @@ services:
command: ["uwsgi /opt/invenio/var/instance/uwsgi_ui.ini"]
image: ultraviolet:latest
ports:
- "5000"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5000:5000"
volumes:
- static_data:/opt/invenio/var/instance/static
- uploaded_data:/opt/invenio/var/instance/data
Expand All @@ -79,7 +79,7 @@ services:
command: ["uwsgi /opt/invenio/var/instance/uwsgi_rest.ini"]
image: ultraviolet:latest
ports:
- "5000"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5001:5000"
volumes:
- uploaded_data:/opt/invenio/var/instance/data
- archived_data:/opt/invenio/var/instance/archive
Expand Down
28 changes: 14 additions & 14 deletions docker-services.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version: '2.2'
services:
app:
build:
Expand All @@ -25,28 +24,28 @@ services:
image: ultraviolet-frontend
restart: "unless-stopped"
ports:
- "80"
- "443"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:80:80"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:443:443"
cache:
image: redis:7
restart: "unless-stopped"
read_only: true
ports:
- "6379:6379"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:6379:6379"
db:
image: postgres:12.4
image: postgres:15.15
restart: "unless-stopped"
environment:
- "POSTGRES_USER=ultraviolet"
- "POSTGRES_PASSWORD=ultraviolet"
- "POSTGRES_DB=ultraviolet"
ports:
- "5432:5432"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5432:5432"
pgadmin:
image: dpage/pgadmin4:6
restart: "unless-stopped"
ports:
- "5050:80"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5050:80"
environment:
PGADMIN_DEFAULT_EMAIL: "[email protected]"
PGADMIN_DEFAULT_PASSWORD: "ultraviolet"
Expand All @@ -56,15 +55,16 @@ services:
image: rabbitmq:3-management
restart: "unless-stopped"
ports:
- "15672:15672"
- "5672:5672"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:15672:15672"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5672:5672"
search:
image: opensearchproject/opensearch:2.3.0
image: opensearchproject/opensearch:2.12.0
restart: "unless-stopped"
environment:
# settings only for development. DO NOT use in production!
- bootstrap.memory_lock=true
- "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
- "OPENSEARCH_INITIAL_ADMIN_PASSWORD=guekxe3mvqieke1!%&ieIADE"
- "DISABLE_INSTALL_DEMO_CONFIG=true"
- "DISABLE_SECURITY_PLUGIN=true"
- "discovery.type=single-node"
Expand All @@ -77,12 +77,12 @@ services:
hard: 65536
mem_limit: 2g
ports:
- "9200:9200"
- "9600:9600"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:9200:9200"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:9600:9600"
opensearch-dashboards:
image: opensearchproject/opensearch-dashboards:2.3.0
image: opensearchproject/opensearch-dashboards:2.12.0
ports:
- "5601:5601"
- "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5601:5601"
expose:
- "5601"
environment:
Expand Down
18 changes: 13 additions & 5 deletions invenio.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,22 @@ https://inveniordm.docs.cern.ch/reference/configuration/.
"""
#required to use custom permissions
from ultraviolet_saml.handlers import acs_handler_factory
from ultraviolet_permissions.policies import UltraVioletPermissionPolicy

#requires to load config variables
import os
from dotenv import load_dotenv
from datetime import timedelta
from datetime import datetime
from invenio_i18n import lazy_gettext as _

# requires to load config variables
import os
from datetime import datetime
from datetime import timedelta

from dotenv import load_dotenv
from ultraviolet.policies import UltraVioletPermissionPolicy
# required to use custom permissions
from ultraviolet_saml.handlers import acs_handler_factory


def _(x): # needed to avoid start time failure with lazy strings
Expand Down Expand Up @@ -45,7 +53,7 @@ PERMANENT_SESSION_LIFETIME = timedelta(days=1)
# provided, the allowed hosts variable is set to localhost. In production it
# should be set to the correct host and it is strongly recommended to only
# route correct hosts to the application.
APP_ALLOWED_HOSTS = os.getenv("APP_ALLOWED_HOSTS",['0.0.0.0','localhost','127.0.0.1'])
TRUSTED_HOSTS = os.getenv("TRUSTED_HOSTS",['0.0.0.0','localhost','127.0.0.1'])

#Search hosts
#SEARCH_HOSTS = os.getenv("SEARCH_HOSTS",'localhostxx','port':9200,'timeout':160}])
Expand Down Expand Up @@ -87,7 +95,7 @@ APP_DEFAULT_SECURE_HEADERS = {
'strict_transport_security_preload': False,
}

COMMUNITIES_GROUPS_ENABLED = True
USERS_RESOURCES_GROUPS_ENABLED = True

# Flask-Babel
# ===========
Expand Down Expand Up @@ -362,7 +370,7 @@ RDM_ARCHIVE_DOWNLOAD_ENABLED = True

# Allow access to old admin panel
ADMIN_ROLE = "admin"
ADMIN_PERMISSION_FACTORY = "ultraviolet_permissions.policies.ultraviolet_admin_permission_factory"
ADMIN_PERMISSION_FACTORY = "ultraviolet.policies.ultraviolet_admin_permission_factory"


# Invenio-Accounts
Expand Down
2 changes: 2 additions & 0 deletions site/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ invenio_base.blueprints =
ultraviolet_views = ultraviolet.views:create_blueprint
invenio_assets.webpack =
ultraviolet_theme = ultraviolet.webpack:theme
invenio_base.finalize_app =
ultraviolet_customizations = ultraviolet.customizations:customize_menus
6 changes: 6 additions & 0 deletions site/ultraviolet/customizations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from flask_menu import current_menu


def customize_menus(app):
"""Customize menus."""
current_menu.submenu("main.communities").hide()
61 changes: 61 additions & 0 deletions site/ultraviolet/generators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from flask_login import current_user
from flask_principal import RoleNeed
from invenio_access.permissions import authenticated_user, superuser_access, system_identity
from invenio_records_permissions.generators import Generator
from invenio_search.engine import dsl



class AdminSuperUser(Generator):
"""Allows admin superusers"""

def __init__(self):
"""Constructor."""
super(AdminSuperUser, self).__init__()

def needs(self, **kwargs):
"""Enabling Needs."""
return [superuser_access]

def query_filter(self, identity=None, **kwargs):
"""Filters for current identity as super user."""
if superuser_access in identity.provides:
return dsl.Q('match_all')
else:
return []


class Depositor(Generator):
"""Allows users with the "depositor" role."""

def __init__(self):
"""Constructor."""
super(Depositor, self).__init__()

def needs(self, record=None, **kwargs):
"""Enabling Needs."""
return [RoleNeed("depositor")]


class Viewer(Generator):
"""Allow NYU Viewers for files restricted to NYU"""

def __init__(self):
"""Constructor."""
super(Viewer, self).__init__()

def needs(self, record=None, **kwargs):
"""Enabling Needs."""
return [RoleNeed("viewer")]


class Curator(Generator):
"""Allow Curator"""

def __init__(self):
"""Constructor."""
super(Curator, self).__init__()

def needs(self, record=None, **kwargs):
"""Enabling Needs."""
return [RoleNeed("curator")]
96 changes: 96 additions & 0 deletions site/ultraviolet/policies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from flask import current_app
from flask_principal import Permission, RoleNeed
from invenio_rdm_records.services import RDMRecordPermissionPolicy
from invenio_rdm_records.services.generators import SecretLinks, RecordCommunitiesAction, RecordOwners, AccessGrant, IfRestricted, ResourceAccessToken
from invenio_records_permissions.generators import SystemProcess, AuthenticatedUser, AnyUser, Disable

from .generators import AdminSuperUser, Depositor, Curator, Viewer


class UltraVioletPermissionPolicy(RDMRecordPermissionPolicy):
"""Access control configuration for records.

Note that even if the array is empty, the invenio_access Permission class
always adds the ``superuser-access``, so admins will always be allowed.
"""

NEED_LABEL_TO_ACTION = {
'bucket-update': 'update_files',
'bucket-read': 'read_files',
'object-read': 'read_files',
}

#
# High-level permissions (used by low-level)
#
can_manage = [ RecordOwners(),Viewer(), SystemProcess(), AccessGrant("manage"), AdminSuperUser(), Depositor(), RecordCommunitiesAction("manage")]
can_curate = can_manage + [SecretLinks("edit"), AccessGrant("edit"),Curator(),RecordCommunitiesAction("curate")]
can_preview = can_manage + [SecretLinks("preview"), AccessGrant("view"),Curator()]
can_view = can_manage + [SecretLinks("view"),AccessGrant("view"), RecordCommunitiesAction("view"), Viewer()]

can_authenticated = [AuthenticatedUser(), SystemProcess()]
can_all = [AnyUser(), SystemProcess()]

#
# Records
#
# Allow submitting new record
can_create = can_manage
# Allow reading metadata of a record
can_read = [
IfRestricted("record", then_=can_view, else_=can_all),
]
can_read_files = [
IfRestricted("files", then_=can_view, else_=can_all),
ResourceAccessToken("read"),
]

#
# Drafts
#
# Allow ability to search drafts
can_search_drafts = can_authenticated
# Allow reading metadata of a draft
can_read_draft = can_preview
# Allow reading files of a draft
can_draft_read_files = can_preview
# Allow updating metadata of a draft
can_update_draft = can_curate
# Allow uploading, updating and deleting files in drafts
can_draft_create_files = can_curate
can_draft_update_files = can_curate
can_draft_delete_files = can_curate

#
# PIDs
#
can_pid_reserve = can_curate
can_pid_delete = can_curate

#
# Actions
#
# Allow to put a record in edit mode (create a draft from record)
can_edit = can_curate
# Allow deleting/discarding a draft and all associated files
can_delete_draft = can_curate
# Allow creating a new version of an existing published record.
can_new_version = can_curate
# Allow publishing a new record or changes to an existing record.
can_publish = can_curate
# Allow lifting a record or draft.
can_lift_embargo = can_manage
# Allow deleting of records by admins
can_delete = can_manage
can_delete_files = can_manage

#
# Disabled actions (these should not be used or changed)
#
can_update = [Disable()]
can_create_files = [Disable()]
can_update_files = [Disable()]


def ultraviolet_admin_permission_factory(_admin_view):
return Permission(RoleNeed(current_app.config["ADMIN_ROLE"]))
Loading
Loading