ckanext-dcor-schemas
Advanced tools
+4
-0
@@ -0,1 +1,5 @@ | ||
| 1.0.2 | ||
| - fix: only group admins and editors may add or remove datasets | ||
| - fix: only group admins may add or remove users | ||
| - fix: do not allow listing users details for `group_list` | ||
| 1.0.1 | ||
@@ -2,0 +6,0 @@ - fix: deny user listing for non-admins |
| Metadata-Version: 2.4 | ||
| Name: ckanext-dcor_schemas | ||
| Version: 1.0.1 | ||
| Version: 1.0.2 | ||
| Summary: Introduces or lifts restrictions (authorization) for managing data and metadata on DCOR | ||
@@ -5,0 +5,0 @@ Author: Paul Müller |
@@ -31,5 +31,5 @@ # file generated by setuptools-scm | ||
| __version__ = version = '1.0.1' | ||
| __version_tuple__ = version_tuple = (1, 0, 1) | ||
| __version__ = version = '1.0.2' | ||
| __version_tuple__ = version_tuple = (1, 0, 2) | ||
| __commit_id__ = commit_id = 'gd5d8ace35' | ||
| __commit_id__ = commit_id = 'geba8d4429' |
@@ -67,2 +67,39 @@ from email.utils import parseaddr | ||
| @logic.auth_allow_anonymous_access | ||
| def group_list(context, data_dict): | ||
| """Check whether access to individual group details is authorised""" | ||
| if data_dict.get('include_users', False): | ||
| return {'success': False, | ||
| 'msg': "Fetching user info via 'group_list' is not allowed."} | ||
| return content_listing(context, data_dict) | ||
| def member_create(context, data_dict): | ||
| """In contrast to CKAN defaults, only editors of groups may add datasets""" | ||
| group = logic.auth.get_group_object(context, data_dict) | ||
| user = context["user"] | ||
| if not group.is_organization: | ||
| if data_dict.get("object_type") == "package": | ||
| permission = "create_dataset" | ||
| elif data_dict.get("object_type") == "user": | ||
| permission = "membership" | ||
| else: | ||
| raise ValueError(f"`object_type` should be 'package' or 'user'," | ||
| f"got '{data_dict.get('object_type')}'. " | ||
| f"The application logic did not check this case.") | ||
| authorized = authz.has_user_permission_for_group_or_org(group.id, | ||
| user, | ||
| permission) | ||
| if not authorized: | ||
| return {"success": False, | ||
| "msg": f'User {user} not authorized to ' | ||
| f'edit collection {group.id}' | ||
| } | ||
| # Run the original auth function | ||
| return logic.auth.create.member_create(context, data_dict) | ||
| def package_create(context, data_dict): | ||
@@ -583,22 +620,1 @@ # Note that we did not decorate this function with | ||
| 'msg': "Users may only view their own details"} | ||
| @logic.auth_allow_anonymous_access | ||
| def group_show(context, data_dict): | ||
| """Check whether access to a group is authorised. | ||
| If it's just the group metadata, this requires no privileges, | ||
| but if user details have been requested, it requires a group admin. | ||
| """ | ||
| user = context.get('user') | ||
| group = logic.auth.get_group_object(context, data_dict) | ||
| if group.state == 'active' and \ | ||
| not asbool(data_dict.get('include_users', False)) and \ | ||
| data_dict.get('object_type', None) != 'user': | ||
| return {'success': True} | ||
| authorized = authz.has_user_permission_for_group_or_org( | ||
| group.id, user, 'update') | ||
| if authorized: | ||
| return {'success': True} | ||
| else: | ||
| return {'success': False, | ||
| 'msg': f'User {user} not authorized to read group {group.id}'} |
@@ -9,3 +9,3 @@ import logging | ||
| import ckan.lib.signals | ||
| from ckan import config, common, logic | ||
| from ckan import authz, config, common, logic, model | ||
| import ckan.plugins as plugins | ||
@@ -45,2 +45,9 @@ import ckan.plugins.toolkit as toolkit | ||
| # Monkey-patch away the `manage_group` permission for group members. | ||
| # We need this to prevent simple group members (non-editors and non-admins) | ||
| # from adding or removing datasets to a group. | ||
| if "manage_group" in authz.ROLE_PERMISSIONS["member"]: | ||
| authz.ROLE_PERMISSIONS["member"].remove("manage_group") | ||
| class DCORDatasetFormPlugin(plugins.SingletonPlugin, | ||
@@ -85,4 +92,5 @@ toolkit.DefaultDatasetForm, | ||
| 'dataset_purge': dcor_auth.dataset_purge, | ||
| 'group_show': dcor_auth.group_show, | ||
| 'group_list': dcor_auth.content_listing, | ||
| 'group_list': dcor_auth.group_list, | ||
| 'group_show': dcor_auth.content_listing, | ||
| 'member_create': dcor_auth.member_create, | ||
| 'member_roles_list': dcor_auth.content_listing, | ||
@@ -427,5 +435,15 @@ 'organization_list': dcor_auth.content_listing, | ||
| if user_obj and not user_obj.is_anonymous and hasattr(user_obj, "id"): | ||
| grps = logic.get_action("group_list_authz")( | ||
| {u'user': user_obj.id}, {}) | ||
| labels.extend(u'group-%s' % o['id'] for o in grps) | ||
| # I initially meant to use `group_authz_list` for this, but | ||
| # this one checks for `manage_group` while regular members only | ||
| # have the `read` permission. Thus, directly ask the DB: | ||
| q = (model.Session.query(model.Member.group_id) | ||
| .filter(model.Member.table_name == 'user') | ||
| .filter(model.Member.capacity.in_(["member"])) | ||
| .filter(model.Member.table_id == user_obj.id) | ||
| .filter(model.Member.state == 'active') | ||
| ) | ||
| grps = [] | ||
| for row in q: | ||
| grps.append(row.group_id) | ||
| labels.extend(u'group-%s' % g for g in grps) | ||
| return labels | ||
@@ -432,0 +450,0 @@ |
@@ -17,2 +17,75 @@ import pathlib | ||
| @pytest.mark.usefixtures('clean_db', 'with_plugins', 'with_request_context') | ||
| def test_group_dataset_create(): | ||
| """Only admins and editors of a group are allowed to add datasets""" | ||
| user1 = factories.User() | ||
| user2 = factories.User() | ||
| user3 = factories.User() | ||
| user4 = factories.User() | ||
| owner_org = factories.Organization(users=[ | ||
| {'name': user1['id'], 'capacity': 'editor'}, | ||
| {'name': user2['id'], 'capacity': 'member'}, | ||
| ]) | ||
| # create a datasets | ||
| create_context1 = {'ignore_auth': False, | ||
| 'user': user1['name'], | ||
| 'api_version': 3} | ||
| ds_dict_1, _ = make_dataset_via_s3( | ||
| create_context=create_context1, | ||
| owner_org=owner_org, | ||
| resource_path=data_path / "calibration_beads_47.rtdc", | ||
| activate=True) | ||
| # create group for user1 | ||
| group_dict = helpers.call_action( | ||
| "group_create", | ||
| name=f"test_group-{uuid.uuid4()}", | ||
| title="Tests for group permissions", | ||
| packages=[ds_dict_1], | ||
| context=create_context1, | ||
| ) | ||
| # add users 2 and 3 to the group in different capacities | ||
| for user, capacity in [ | ||
| [user2, "editor"], | ||
| [user3, "member"], | ||
| ]: | ||
| helpers.call_action("member_create", | ||
| id=group_dict["id"], | ||
| object=user["id"], | ||
| object_type='user', | ||
| capacity=capacity) | ||
| # admin and editor of the group should be able to create a dataset | ||
| for user in [user1, user2]: | ||
| context = {'ignore_auth': False, | ||
| 'user': user['name'], | ||
| 'api_version': 3} | ||
| helpers.call_auth("member_create", | ||
| context, | ||
| object_type="package", | ||
| id=group_dict["id"]) | ||
| # This must also be reflected in group_list_authz | ||
| groups = helpers.call_action("group_list_authz", context) | ||
| assert len(groups) == 1 | ||
| assert groups[0]["id"] == group_dict["id"] | ||
| # A member, random user, or anon may not add dataset | ||
| for user in [user3, user4, {"name": None}]: | ||
| context = {'ignore_auth': False, | ||
| 'user': user['name'], | ||
| 'api_version': 3} | ||
| with pytest.raises(logic.NotAuthorized): | ||
| helpers.call_auth("member_create", | ||
| context, | ||
| object_type="package", | ||
| id=group_dict["id"]) | ||
| # This must also be reflected in group_list_authz | ||
| groups = helpers.call_action("group_list_authz", context) | ||
| assert len(groups) == 0 | ||
| @pytest.mark.ckan_config('ckan.plugins', 'dcor_schemas') | ||
| @pytest.mark.usefixtures('clean_db', 'with_plugins', 'with_request_context') | ||
| def test_group_delete(): | ||
@@ -132,1 +205,159 @@ """Make sure users can delete their groups""" | ||
| id=group_dict["id"]) | ||
| @pytest.mark.ckan_config('ckan.plugins', 'dcor_schemas') | ||
| @pytest.mark.ckan_config('ckanext.dcor_schemas.allow_content_listing_for_anon', | ||
| 'false') | ||
| @pytest.mark.usefixtures('clean_db', 'with_plugins', 'with_request_context') | ||
| def test_group_show_by_user(): | ||
| """ | ||
| Only members of a group are allowed to show it | ||
| """ | ||
| user1 = factories.User() | ||
| user2 = factories.User() | ||
| user3 = factories.User() | ||
| user4 = factories.User() | ||
| owner_org = factories.Organization(users=[ | ||
| {'name': user1['id'], 'capacity': 'editor'}, | ||
| {'name': user2['id'], 'capacity': 'member'}, | ||
| ]) | ||
| # create a datasets | ||
| create_context1 = {'ignore_auth': False, | ||
| 'user': user1['name'], | ||
| 'api_version': 3} | ||
| ds_dict_1, _ = make_dataset_via_s3( | ||
| create_context=create_context1, | ||
| owner_org=owner_org, | ||
| resource_path=data_path / "calibration_beads_47.rtdc", | ||
| activate=True) | ||
| # create group for user1 | ||
| group_dict = helpers.call_action( | ||
| "group_create", | ||
| name=f"test_group-{uuid.uuid4()}", | ||
| title="Tests for group permissions", | ||
| packages=[ds_dict_1], | ||
| context=create_context1, | ||
| ) | ||
| # add users 2 and 3 to the group in different capacities | ||
| for user, capacity in [ | ||
| [user2, "editor"], | ||
| [user3, "member"], | ||
| ]: | ||
| helpers.call_action("member_create", | ||
| id=group_dict["id"], | ||
| object=user["id"], | ||
| object_type='user', | ||
| capacity=capacity) | ||
| # The admin, editor, and member should be able to view the group | ||
| for user in [user1, user2, user3]: | ||
| helpers.call_auth("group_show", | ||
| {'ignore_auth': False, | ||
| 'user': user['name'], | ||
| 'api_version': 3}, | ||
| id=group_dict["id"]) | ||
| # A random user is allowed to view it. But we need this | ||
| # functionality so that users can list all groups! | ||
| helpers.call_auth("group_show", | ||
| {'ignore_auth': False, | ||
| 'user': user4['name'], | ||
| 'api_version': 3}, | ||
| id=group_dict["id"]) | ||
| # An anonymous user is also not allowed | ||
| with pytest.raises(logic.NotAuthorized): | ||
| helpers.call_auth("group_show", | ||
| {'ignore_auth': False, | ||
| 'user': None, | ||
| 'api_version': 3}, | ||
| id=group_dict["id"]) | ||
| @pytest.mark.ckan_config('ckan.plugins', 'dcor_schemas') | ||
| @pytest.mark.usefixtures('clean_db', 'with_plugins', 'with_request_context') | ||
| def test_group_user_add(): | ||
| """Only admins of a group are allowed to add users to it""" | ||
| user1 = factories.User() | ||
| user2 = factories.User() | ||
| user3 = factories.User() | ||
| user4 = factories.User() | ||
| owner_org = factories.Organization(users=[ | ||
| {'name': user1['id'], 'capacity': 'editor'}, | ||
| {'name': user2['id'], 'capacity': 'member'}, | ||
| ]) | ||
| # create a datasets | ||
| create_context1 = {'ignore_auth': False, | ||
| 'user': user1['name'], | ||
| 'api_version': 3} | ||
| ds_dict_1, _ = make_dataset_via_s3( | ||
| create_context=create_context1, | ||
| owner_org=owner_org, | ||
| resource_path=data_path / "calibration_beads_47.rtdc", | ||
| activate=True) | ||
| # create group for user1 | ||
| group_dict = helpers.call_action( | ||
| "group_create", | ||
| name=f"test_group-{uuid.uuid4()}", | ||
| title="Tests for group permissions", | ||
| packages=[ds_dict_1], | ||
| context=create_context1, | ||
| ) | ||
| # add users 2 and 3 to the group in different capacities | ||
| for user, capacity in [ | ||
| [user2, "editor"], | ||
| [user3, "member"], | ||
| ]: | ||
| helpers.call_action("member_create", | ||
| id=group_dict["id"], | ||
| object=user["id"], | ||
| object_type='user', | ||
| capacity=capacity) | ||
| # The admin of the group should be able to add a user | ||
| helpers.call_auth("member_create", | ||
| {'ignore_auth': False, | ||
| 'user': user1['name'], | ||
| 'api_version': 3}, | ||
| object_type="user", | ||
| id=group_dict["id"]) | ||
| # An editor of a group should not be able to add a user | ||
| with pytest.raises(logic.NotAuthorized): | ||
| helpers.call_auth("member_create", | ||
| {'ignore_auth': False, | ||
| 'user': user2['name'], | ||
| 'api_version': 3}, | ||
| object_type="user", | ||
| id=group_dict["id"]) | ||
| # A simple member of a group is not allowed to add another member | ||
| with pytest.raises(logic.NotAuthorized): | ||
| helpers.call_auth("member_create", | ||
| {'ignore_auth': False, | ||
| 'user': user3['name'], | ||
| 'api_version': 3}, | ||
| object_type="user", | ||
| id=group_dict["id"]) | ||
| # A random user is not allowed to add another member | ||
| with pytest.raises(logic.NotAuthorized): | ||
| helpers.call_auth("member_create", | ||
| {'ignore_auth': False, | ||
| 'user': user4['name'], | ||
| 'api_version': 3}, | ||
| object_type="user", | ||
| id=group_dict["id"]) | ||
| # An anonymous user is also not allowed | ||
| with pytest.raises(logic.NotAuthorized): | ||
| helpers.call_auth("member_create", | ||
| {'ignore_auth': False, | ||
| 'user': None, | ||
| 'api_version': 3}, | ||
| object_type="package", | ||
| id=group_dict["id"]) |
@@ -11,2 +11,4 @@ import pytest | ||
| @pytest.mark.usefixtures('clean_db', 'with_plugins', 'with_request_context') | ||
| @pytest.mark.ckan_config('ckanext.dcor_schemas.allow_content_listing_for_anon', | ||
| 'false') | ||
| def test_auth_group_show(): | ||
@@ -13,0 +15,0 @@ """Anonymous user not allowed to list group with users""" |
+1
-1
| Metadata-Version: 2.4 | ||
| Name: ckanext-dcor_schemas | ||
| Version: 1.0.1 | ||
| Version: 1.0.2 | ||
| Summary: Introduces or lifts restrictions (authorization) for managing data and metadata on DCOR | ||
@@ -5,0 +5,0 @@ Author: Paul Müller |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
1258194
0.85%6579
3.72%