diff --git a/.gitignore b/.gitignore index faee6bab08..32927ea4c9 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,9 @@ scripts/demo/postgresql/ docker-compose.dev.yml *.bak +# Test output files +test_output.txt + #PDF cover page-related files header-icons/* combined-pdfs/* diff --git a/modules/invenio-files-rest/invenio_files_rest/admin.py b/modules/invenio-files-rest/invenio_files_rest/admin.py index 9ca274993c..b0e6075c7a 100644 --- a/modules/invenio-files-rest/invenio_files_rest/admin.py +++ b/modules/invenio-files-rest/invenio_files_rest/admin.py @@ -148,6 +148,23 @@ class LocationModelView(ModelView): 'System Administrator') _repoadmin_role = os.environ.get('INVENIO_ROLE_REPOSITORY', 'Repository Administrator') + + def get_query(self): + """Override get_query to filter locations based on user roles.""" + query = super(LocationModelView, self).get_query() + user_role_names = {role.name for role in current_user.roles} + if not self._system_role in user_role_names: + # Non-system admins should not see default locations. + query = query.filter_by(default=False) + return query + + def get_count_query(self): + """Override get_count_query to apply the same role-based filters.""" + query = super(LocationModelView, self).get_count_query() + user_role_names = {role.name for role in current_user.roles} + if not self._system_role in user_role_names: + query = query.filter_by(default=False) + return query @expose('/') def index_view(self): """Override index view to add custom logic. diff --git a/modules/invenio-files-rest/tests/test_admin.py b/modules/invenio-files-rest/tests/test_admin.py index 8c546b61f7..6dfefc9b4c 100644 --- a/modules/invenio-files-rest/tests/test_admin.py +++ b/modules/invenio-files-rest/tests/test_admin.py @@ -343,3 +343,140 @@ def test_can_delete(self, app, db, monkeypatch): with patch('invenio_files_rest.admin.current_user', mock_user): view = LocationModelView(Location, db.session) assert not view.can_delete + + + def test_get_query(self, app, db, monkeypatch): + """Test get_query filters locations based on user roles.""" + monkeypatch.setenv('INVENIO_ROLE_SYSTEM', 'System Administrator') + monkeypatch.setenv('INVENIO_ROLE_REPOSITORY', 'Repository Administrator') + + # Create test locations + default_loc = Location(name='default-loc', uri='/tmp/default', default=True) + non_default_loc = Location(name='non-default-loc', uri='/tmp/non-default', default=False) + db.session.add(default_loc) + db.session.add(non_default_loc) + db.session.commit() + + try: + mock_user = MagicMock() + + # Test Case: System Administrator sees all locations (including default) + mock_role_sysad = MagicMock() + mock_role_sysad.name = 'System Administrator' + mock_user.roles = [mock_role_sysad] + with patch('invenio_files_rest.admin.current_user', mock_user): + view = LocationModelView(Location, db.session) + query = view.get_query() + locations = query.all() + location_names = {loc.name for loc in locations} + assert 'default-loc' in location_names + assert 'non-default-loc' in location_names + + # Test Case: Repository Administrator does not see default locations + mock_role_repoad = MagicMock() + mock_role_repoad.name = 'Repository Administrator' + mock_user.roles = [mock_role_repoad] + with patch('invenio_files_rest.admin.current_user', mock_user): + view = LocationModelView(Location, db.session) + query = view.get_query() + locations = query.all() + location_names = {loc.name for loc in locations} + assert 'default-loc' not in location_names + assert 'non-default-loc' in location_names + + # Test Case: Community Administrator does not see default locations + mock_role_commad = MagicMock() + mock_role_commad.name = 'Community Administrator' + mock_user.roles = [mock_role_commad] + with patch('invenio_files_rest.admin.current_user', mock_user): + view = LocationModelView(Location, db.session) + query = view.get_query() + locations = query.all() + location_names = {loc.name for loc in locations} + assert 'default-loc' not in location_names + assert 'non-default-loc' in location_names + + # Test Case: User without role does not see default locations + mock_user.roles = [] + with patch('invenio_files_rest.admin.current_user', mock_user): + view = LocationModelView(Location, db.session) + query = view.get_query() + locations = query.all() + location_names = {loc.name for loc in locations} + assert 'default-loc' not in location_names + assert 'non-default-loc' in location_names + finally: + # Clean up test locations + db.session.delete(default_loc) + db.session.delete(non_default_loc) + db.session.commit() + + + def test_get_count_query(self, app, db, monkeypatch): + """Test get_count_query filters locations based on user roles.""" + monkeypatch.setenv('INVENIO_ROLE_SYSTEM', 'System Administrator') + monkeypatch.setenv('INVENIO_ROLE_REPOSITORY', 'Repository Administrator') + + # Create test locations + default_loc = Location(name='default-loc-count', uri='/tmp/default-count', default=True) + non_default_loc1 = Location(name='non-default-loc-1', uri='/tmp/non-default-1', default=False) + non_default_loc2 = Location(name='non-default-loc-2', uri='/tmp/non-default-2', default=False) + db.session.add(default_loc) + db.session.add(non_default_loc1) + db.session.add(non_default_loc2) + db.session.commit() + + try: + mock_user = MagicMock() + + # Test Case: System Administrator sees count of all locations (including default) + mock_role_sysad = MagicMock() + mock_role_sysad.name = 'System Administrator' + mock_user.roles = [mock_role_sysad] + with patch('invenio_files_rest.admin.current_user', mock_user): + view = LocationModelView(Location, db.session) + query = view.get_count_query() + count = query.count() + # Should see all locations in the database + total_locations = db.session.query(Location).count() + assert count == total_locations + + # Test Case: Repository Administrator sees count excluding default locations + mock_role_repoad = MagicMock() + mock_role_repoad.name = 'Repository Administrator' + mock_user.roles = [mock_role_repoad] + with patch('invenio_files_rest.admin.current_user', mock_user): + view = LocationModelView(Location, db.session) + query = view.get_count_query() + count = query.count() + # Should only see non-default locations + non_default_count = db.session.query(Location).filter_by(default=False).count() + assert count == non_default_count + + # Test Case: Community Administrator sees count excluding default locations + mock_role_commad = MagicMock() + mock_role_commad.name = 'Community Administrator' + mock_user.roles = [mock_role_commad] + with patch('invenio_files_rest.admin.current_user', mock_user): + view = LocationModelView(Location, db.session) + query = view.get_count_query() + count = query.count() + # Should only see non-default locations + non_default_count = db.session.query(Location).filter_by(default=False).count() + assert count == non_default_count + + # Test Case: User without role sees count excluding default locations + mock_user.roles = [] + with patch('invenio_files_rest.admin.current_user', mock_user): + view = LocationModelView(Location, db.session) + query = view.get_count_query() + count = query.count() + # Should only see non-default locations + non_default_count = db.session.query(Location).filter_by(default=False).count() + assert count == non_default_count + finally: + # Clean up test locations + db.session.delete(default_loc) + db.session.delete(non_default_loc1) + db.session.delete(non_default_loc2) + db.session.commit() diff --git a/modules/weko-records-ui/weko_records_ui/config.py b/modules/weko-records-ui/weko_records_ui/config.py index ac792124e2..9637e6e5bd 100644 --- a/modules/weko-records-ui/weko_records_ui/config.py +++ b/modules/weko-records-ui/weko_records_ui/config.py @@ -827,3 +827,6 @@ class FILE_OPEN_STATUS(Enum): WEKO_RECORDS_UI_GAKUNIN_RDM_URL = "https://rdm.nii.ac.jp" """URL of GakuNin RDM.""" + +WEKO_RECORDS_UI_USER_STORAGE_MODIFICATION_ENABLED = False +"""Enable user storage modification feature.""" diff --git a/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/file_details_contents.html b/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/file_details_contents.html index fa92b69213..e3c269181d 100644 --- a/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/file_details_contents.html +++ b/modules/weko-records-ui/weko_records_ui/templates/weko_records_ui/file_details_contents.html @@ -125,13 +125,15 @@