def test_request_renewal(self, mock_notify_renewal_request): user = factories.ExternalUser() dataset = factories.Dataset() activity = core_factories.Activity( user_id=user["id"], object_id=dataset["id"], activity_type="new package", data={ "package": dataset, "actor": "Mr Someone" }, ) mock_notify_renewal_request.return_value = [] created, reason = request_renewal(user, activity) assert created assert reason is None mock_notify_renewal_request.assert_called_once() call = mock_notify_renewal_request.call_args_list[0] args, kwargs = call user_id = kwargs['user_id'] message = kwargs['message'] assert user_id == user['id'] assert 'User {} will expire on'.format(user['name']) in message assert 'Last activity: "{}" registered on'.format( activity['activity_type']) in message created, reason = request_renewal(user, activity) assert not created assert reason == 'The request already exists'
def test_requesting_user_is_not_admin_of_required_container(self): target_user = factories.ExternalUser(state=model.State.PENDING) requesting_user = core_factories.User() container2 = factories.DataContainer( users=[{ "name": requesting_user["name"], "capacity": "admin" }]) access_request_data_dict = { 'object_id': target_user['id'], 'object_type': 'user', 'message': 'asdf', 'role': 'member', 'data': { 'default_containers': [self.container1['id']] } } toolkit.get_action(u'access_request_create')({ 'user': target_user['id'], 'ignore_auth': True }, access_request_data_dict) action = toolkit.get_action("external_user_update_state") with pytest.raises(toolkit.NotAuthorized): action({"user": requesting_user["name"]}, { 'id': target_user['id'], 'state': model.State.ACTIVE })
def test_success(self): target_user = factories.ExternalUser(state=model.State.PENDING) access_request_data_dict = { 'object_id': target_user['id'], 'object_type': 'user', 'message': 'asdf', 'role': 'member', 'data': { 'default_containers': [self.container1['id']] } } toolkit.get_action(u'access_request_create')({ 'user': target_user['id'], 'ignore_auth': True }, access_request_data_dict) action = toolkit.get_action("external_user_update_state") action({"user": self.container1_admin["name"]}, { 'id': target_user['id'], 'state': model.State.ACTIVE }) user = toolkit.get_action("user_show")({ "ignore_auth": True }, { "id": target_user['id'] }) assert model.State.ACTIVE == user['state']
def test_user_autocomplete(self): sysadmin = core_factories.Sysadmin(name='sysadmin', id='sysadmin') factories.ExternalUser( fullname='Alice External', email='*****@*****.**', ) core_factories.User(fullname='Bob Internal') core_factories.User(fullname='Carlos Internal') core_factories.User(fullname='David Internal') action = toolkit.get_action('user_autocomplete') context = {'user': sysadmin['name']} result = action(context, {'q': 'alic'}) assert 0 == len(result) result = action(context, {'q': 'alic', 'include_external': True}) assert 'Alice External' == result[0]['fullname'] result = action(context, {'q': 'nal'}) fullnames = [r['fullname'] for r in result] assert 'Bob Internal' in fullnames assert 'Carlos Internal' in fullnames assert 'David Internal' in fullnames result = action(context, {'q': 'foobar'}) assert 0 == len(result)
def test_renewal_not_allowed(self): target_user = factories.ExternalUser() access_request_data_dict = { 'object_id': target_user['id'], 'object_type': 'user', 'message': 'asdf', 'role': 'member', 'data': { 'user_request_type': USER_REQUEST_TYPE_RENEWAL, 'users_who_can_approve': [self.container1_admin['id']] } } toolkit.get_action(u'access_request_create')({ 'user': target_user['id'], 'ignore_auth': True }, access_request_data_dict) requesting_user = core_factories.User() action = toolkit.get_action("external_user_update_state") with pytest.raises(toolkit.NotAuthorized): action({"user": requesting_user["name"]}, { 'id': target_user['id'], 'state': model.State.ACTIVE, 'renew_expiry_date': True })
def test_user_request_access_body(self): container1 = factories.DataContainer( name='brasil-container', title='Brazil', ) container2 = factories.DataContainer( name='arg-container', title='Argentina', ) user1 = factories.ExternalUser( name='user1', id='user1', email='*****@*****.**', focal_point='Maria', default_containers=[container1['id'], container2['id']]) user_message = 'I can haz access?\nkthxbye' email_body = mailer.compose_request_access_email_body( 'user', self.sysadmin, user1, user1, user_message) regularised_body = regularise_html(email_body) expected = regularise_html( 'External user <a href="{user_link}">Mr. Test User</a> has requested access to deposit datasets.' .format(user_link=toolkit.url_for('user.read', id=user1['id'], qualified=True), )) assert expected in regularised_body assert '<p>I can haz access?<br> kthxbye</p>' in regularised_body assert 'Data container(s): Brazil, Argentina' in regularised_body assert 'User\'s email address: [email protected]' in regularised_body assert 'Focal point: Maria' in regularised_body
def test_no_access_request(self): target_user = factories.ExternalUser(state=model.State.PENDING) action = toolkit.get_action("external_user_update_state") with pytest.raises(toolkit.NotAuthorized): action({"user": self.container1_admin["name"]}, { 'id': target_user['id'], 'state': model.State.ACTIVE })
def test_target_user_is_not_pending(self): target_user = factories.ExternalUser() action = toolkit.get_action("external_user_update_state") with pytest.raises(toolkit.NotAuthorized): action({"user": self.container1_admin["name"]}, { 'id': target_user['id'], 'state': model.State.ACTIVE })
def test_external_users_plugin_actions(self): external_user = factories.ExternalUser() for plugin in plugins.PluginImplementations(plugins.IAuthFunctions): for action in plugin.get_auth_functions().keys(): context = {'user': external_user['name']} if action not in ALLOWED_ACTIONS: with pytest.raises(toolkit.NotAuthorized): toolkit.check_access(action, context=context)
def test_user_show(self): sysadmin = core_factories.Sysadmin() external_user = factories.ExternalUser() internal_user = core_factories.User() action = toolkit.get_action('user_show') context = {'user': sysadmin['name']} assert action(context, {'id': external_user['id']})['external'] assert not action(context, {'id': internal_user['id']})['external']
def test_external_user_approved_deposit(self): sysadmin = core_factories.Sysadmin() external_user = factories.ExternalUser() target = factories.DataContainer( id='data-target', name='data-target', ) deposit = factories.DataContainer(id='data-deposit', name='data-deposit') deposited_dataset = factories.DepositedDataset( name='dataset1', owner_org='data-deposit', owner_org_dest='data-target', user=external_user) tmp = deposited_dataset.copy() tmp.update({ 'unit_of_measurement': 'individual', 'keywords': ['3', '4'], 'archived': 'False', 'data_collector': ['acf'], 'data_collection_technique': 'f2f', 'external_access_level': 'open_access', 'geographies': [DEFAULT_GEOGRAPHY_CODE] }) deposited_dataset = helpers.call_action('package_update', {'user': sysadmin['name']}, **tmp) # While the dataset is in deposited state, external_user can view it assert (True == toolkit.check_access( 'package_show', {'user': external_user['name']}, {'id': deposited_dataset['id']}, )) # Approve the dataset approved_dataset = convert_deposited_dataset_to_regular_dataset( deposited_dataset) approved_dataset = helpers.call_action('package_update', context={ 'user': sysadmin['name'], 'type': approved_dataset['type'] }, **approved_dataset) # Now that the dataset has moved out of the data-deposit, # external_user can not view it anymore with pytest.raises(toolkit.NotAuthorized): toolkit.check_access( 'package_show', context={'user': external_user['name']}, data_dict={'id': approved_dataset['id']}, )
def test_user_list_query(self): sysadmin = core_factories.Sysadmin() external_user = factories.ExternalUser() internal_user = core_factories.User() default_user = toolkit.get_action('get_site_user')({ 'ignore_auth': True }) action = toolkit.get_action('user_list') context = {'user': sysadmin['name'], 'return_query': True} users = action(context, {}) assert users.count() == 4
def test_external_user(self): sysadmin = core_factories.Sysadmin(name='sysadmin', id='sysadmin') external_user = factories.ExternalUser() dataset = factories.Dataset() action = toolkit.get_action("package_collaborator_create") with pytest.raises(toolkit.ValidationError): action({'user': sysadmin['name']}, { 'id': dataset['id'], 'user_id': external_user['id'], 'capacity': 'member', })
def test_about_expire_user_list(self): not_expired_date = datetime.date.today() + datetime.timedelta(days=35) about_expire_date = datetime.date.today() + datetime.timedelta(days=25) expired_date = datetime.date.today() - datetime.timedelta(days=5) user1 = factories.ExternalUser(name='not-expired-user', email='*****@*****.**', expiry_date=not_expired_date) user2 = factories.ExternalUser(name='expired-user', email='*****@*****.**', expiry_date=expired_date) user3 = factories.ExternalUser(name='about-expire-user', email='*****@*****.**', expiry_date=about_expire_date) dataset = factories.Dataset() activity = core_factories.Activity( user_id=user3["id"], object_id=dataset["id"], activity_type="new package", data={ "package": dataset, "actor": "Mr Someone" }, ) expired_users = expired_users_list(before_expire_days=30, include_activities=True) assert len(expired_users) == 1 ids = [u['id'] for u in expired_users] assert user1['id'] not in ids assert user2['id'] not in ids assert user3['id'] in ids expired_user = expired_users[0] activities = expired_user['activities'] assert len(activities) == 2 assert activities[0]['activity_type'] == activity['activity_type'] assert activities[1]['activity_type'] == 'new user'
def test_user_delete(self, mock_hook, mail_user_by_id): sysadmin = core_factories.Sysadmin() external_user = factories.ExternalUser(name='to-delete', id='to-delete') # User will leave this container orphaned after being deleted container = factories.DataContainer(user=sysadmin, users=[ { 'name': external_user['name'], 'capacity': 'admin' }, ]) # the container creatopr will be added as admin toolkit.get_action('member_delete')({ 'ignore_auth': True }, { 'id': container['id'], 'object_type': 'user', 'object': sysadmin['id'] }) toolkit.get_action('user_delete')({ 'ignore_auth': True }, { 'id': external_user['id'] }) mock_hook.assert_called_once() assert mock_hook.call_args_list[0][0][ 0].__name__ == 'process_last_admin_on_delete' assert mock_hook.call_args_list[0][0][1][0] == container['id'] # run the job process_last_admin_on_delete(container['id']) # and validate we have a new admin container = toolkit.get_action('organization_show')( { 'ignore_auth': True }, { 'id': container['id'] }) assert container['users'][0]['capacity'] == 'admin' assert container['users'][0]['name'] == sysadmin['name'] # Test the extra comment in the email mail_user_by_id.assert_called_once() args_list = mail_user_by_id.call_args_list mail_body = args_list[0][0][2] assert 'You have been assigned as admin' in mail_body
def test_user_list_query_empty(self): sysadmin = core_factories.Sysadmin() external_user = factories.ExternalUser() internal_user = core_factories.User() default_user = toolkit.get_action('get_site_user')({ 'ignore_auth': True }) action = toolkit.get_action('user_list') context = {'user': sysadmin['name'], 'return_query': True} # add a filter to get 0 results users = action(context, {'email': '*****@*****.**'}) assert users.count() == 0
def test_package_search_permissions(self): internal_user = core_factories.User() external_user = factories.ExternalUser() dataset = factories.Dataset(private=True) action = toolkit.get_action("package_search") internal_user_search_result = action({'user': internal_user["name"]}, {}) external_user_search_result = action({'user': external_user["name"]}, {}) assert 1 == internal_user_search_result[ 'count'] # internal_user can see this assert 0 == external_user_search_result['count'] # external_user can't
def test_deposited_dataset_owner_org_dest_not_visible_external(self): deposit = factories.DataContainer(id='data-deposit') target = factories.DataContainer(id='data-target', visible_external=False) internal_user = core_factories.User() external_user = factories.ExternalUser() with pytest.raises(toolkit.Invalid): validators.deposited_dataset_owner_org_dest( 'data-target', {'user': external_user['name']}) assert (validators.deposited_dataset_owner_org_dest( "data-target", {"user": internal_user["name"]}, ) == "data-target")
def setup(self): self.external_user = factories.ExternalUser() user = core_factories.User() target = factories.DataContainer( id='data-target', name='data-target', ) deposit = factories.DataContainer(id='data-deposit', name='data-deposit') self.external_user_dataset = factories.DepositedDataset( description='deposited dataset created by external user', owner_org='data-deposit', owner_org_dest='data-target', user=self.external_user) self.internal_user_dataset = factories.DepositedDataset( description='deposited dataset created by internal user', owner_org='data-deposit', owner_org_dest='data-target', user=user) self.dataset = factories.Dataset(owner_org=target['id'], ) self.external_dataset_resources = [ factories.Resource( description= 'resource created by external_user attached to deposited dataset created by external_user', package_id=self.external_user_dataset['id'], url_type='upload', user=self.external_user, visibility='restricted'), factories.Resource( description= 'resource created by someone else attached to deposited dataset created by external_user', package_id=self.external_user_dataset['id'], url_type='upload', user=user, visibility='restricted'), ] self.internal_dataset_resources = [ factories.Resource(package_id=self.internal_user_dataset['id'], url_type='upload', user=user), ] self.arbitrary_resource = factories.Resource( package_id=self.dataset['id'], url_type='upload', )
def test_external_users_core_actions(self): external_user = factories.ExternalUser() core_auth_modules = [ 'ckan.logic.auth.create', 'ckan.logic.auth.delete', 'ckan.logic.auth.get', 'ckan.logic.auth.patch', 'ckan.logic.auth.update', ] for module_path in core_auth_modules: actions = get_module_functions(module_path) for action in actions: context = {'user': external_user['name']} if action not in ALLOWED_ACTIONS: with pytest.raises(toolkit.NotAuthorized): toolkit.check_access(action, context=context)
def test_external_users_endpoints(self, app): external_user = factories.ExternalUser() dataset = factories.Dataset() container = factories.DataContainer() deposit = factories.DataContainer(id='data-deposit', name='data-deposit') env = {'REMOTE_USER': external_user['name'].encode('ascii')} endpoints_403 = [ '/about', '/ckan-admin', '/dashboard', '/metrics', '/dataset', '/data-container', '/organization', '/group', '/user', ] for endpoint in endpoints_403: resp = app.get(endpoint, extra_environ=env, status=403) endpoints_404 = [ '/dataset/{}'.format(dataset['name']), '/data-container/{}'.format(container['name']), '/organization/{}'.format(container['name']), '/group/{}'.format(container['name']), ] for endpoint in endpoints_404: # these throw a 404 rather than a 403 with app.flask_app.test_request_context(): resp = app.get(endpoint, extra_environ=env, status=404) endpoints_200 = [ '/', '/feeds/dataset.atom', '/data-container/data-deposit', '/api/2/util/resource/format_autocomplete?incomplete=a', '/api/2/util/tag/autocomplete?incomplete=a', '/user/{}'.format(external_user['name']), '/api/2/util/geography/autocomplete?q=a', ] for endpoint in endpoints_200: resp = app.get(endpoint, extra_environ=env, status=200)
def test_users_who_can_approve(self, mock_notify_renewal_request): """ Check what users can approve a renewal """ user = factories.ExternalUser() container_admin = core_factories.User() container_member = core_factories.User() curator = core_factories.User() container = factories.DataContainer(users=[ { "name": container_admin["name"], "capacity": "admin" }, { "name": container_member["name"], "capacity": "member" }, ]) dataset = factories.Dataset(user=user, owner_org=container['id']) activity = create_curation_activity('dataset_approved', dataset['id'], dataset['name'], curator['id'], message='x') mock_notify_renewal_request.return_value = [] created, reason = request_renewal(user, activity) assert created assert reason is None mock_notify_renewal_request.assert_called_once() access_requests = get_existing_access_request( user_id=user['id'], object_id=user['id'], status='requested', ) access_request = access_requests[0] users_who_can_approve = access_request.data.get( 'users_who_can_approve') assert user['id'] not in users_who_can_approve assert container_member['id'] not in users_who_can_approve assert container_admin['id'] in users_who_can_approve assert curator['id'] in users_who_can_approve
def test_user_list(self): sysadmin = core_factories.Sysadmin() external_user = factories.ExternalUser() internal_user = core_factories.User() default_user = toolkit.get_action('get_site_user')({ 'ignore_auth': True }) action = toolkit.get_action('user_list') context = {'user': sysadmin['name']} users = action(context, {}) assert (1 == len([ u for u in users if u['external'] and u['name'] != default_user['name'] ])) assert (2 == len([ u for u in users if not u['external'] and u['name'] != default_user['name'] ]))
def test_external_users_always_allowed_actions(self): external_user = factories.ExternalUser() # these actions should always return True, # regardless of the content of data_dict actions = [ 'format_autocomplete', 'package_search', 'group_list_authz', 'organization_list_for_user', 'tag_autocomplete', 'geography_autocomplete', 'geography_show', 'tag_list', ] context = {'user': external_user['name']} for action in actions: assert True == toolkit.check_access(action, context)
def setup(self): self.requesting_user = core_factories.User() self.standard_user = core_factories.User() self.pending_user = factories.ExternalUser(state=model.State.PENDING) self.container1_admin = core_factories.User() self.container1_curator = core_factories.User() self.container1 = factories.DataContainer( users=[{ "name": self.container1_admin["name"], "capacity": "admin" }, { "name": self.container1_curator["name"], "capacity": "editor" }]) self.dataset1 = factories.Dataset(owner_org=self.container1["id"], visibility="restricted") self.container_request = AccessRequest( user_id=self.requesting_user["id"], object_id=self.container1["id"], object_type="organization", message="", role="member", ) self.dataset_request = AccessRequest( user_id=self.requesting_user["id"], object_id=self.dataset1["id"], object_type="package", message="", role="member", ) self.user_request = AccessRequest( user_id=self.pending_user["id"], object_id=self.pending_user["id"], object_type="user", message="", role="member", data={'default_containers': [self.container1["id"]]}, ) model.Session.add(self.container_request) model.Session.add(self.dataset_request) model.Session.add(self.user_request) model.Session.commit()
def test_integration_new_deposit(self, app): # everyone can create datasets in the data-deposit external_user = factories.ExternalUser() with app.flask_app.test_request_context(): resp = app.get( url='/deposited-dataset/new', extra_environ={ 'REMOTE_USER': external_user['name'].encode('ascii') }, status=200, ) internal_user = core_factories.User() with app.flask_app.test_request_context(): resp = app.get( url='/deposited-dataset/new', extra_environ={ 'REMOTE_USER': internal_user['name'].encode('ascii') }, status=200, )
def setup(self): # Users self.internal_user = factories.InternalUser() self.internal_editor_user = factories.InternalUser() self.kobo_user = factories.InternalKoBoUser() self.internal_admin_user = factories.InternalUser() self.external_user = factories.ExternalUser() self.data_container = factories.DataContainer(users=[ { 'name': self.internal_admin_user['name'], 'capacity': 'admin' }, { 'name': self.internal_editor_user['name'], 'capacity': 'editor' }, { 'name': self.kobo_user['name'], 'capacity': 'editor' }, ])
def test_invalid_state(self): target_user = factories.ExternalUser(state=model.State.PENDING) access_request_data_dict = { 'object_id': target_user['id'], 'object_type': 'user', 'message': 'asdf', 'role': 'member', 'data': { 'default_containers': [self.container1['id']] } } toolkit.get_action(u'access_request_create')({ 'user': target_user['id'], 'ignore_auth': True }, access_request_data_dict) action = toolkit.get_action("external_user_update_state") with pytest.raises(toolkit.ValidationError): action({"user": self.container1_admin["name"]}, { 'id': target_user['id'], 'state': 'foobar' })
def test_integration_new_dataset(self, app): external_user = factories.ExternalUser() # external_user can't create a new dataset with app.flask_app.test_request_context(): resp = app.get( url='/dataset/new', extra_environ={ 'REMOTE_USER': external_user['name'].encode('ascii') }, status=403, ) internal_user = core_factories.User() # internal_user can't create a dataset # because they aren't an admin of any containers with app.flask_app.test_request_context(): resp = app.get( url='/dataset/new', extra_environ={ 'REMOTE_USER': internal_user['name'].encode('ascii') }, status=403, ) factories.DataContainer(users=[{ 'name': internal_user['name'], 'capacity': 'admin' }]) # now that internal_user is a container admin # they can create a dataset with app.flask_app.test_request_context(): resp = app.get( url='/dataset/new', extra_environ={ 'REMOTE_USER': internal_user['name'].encode('ascii') }, status=200, )
def test_organization_show(self): external_user = factories.ExternalUser() internal_user = core_factories.User() container = factories.DataContainer() deposit = factories.DataContainer(id='data-deposit', name='data-deposit') # Everyone can see the data deposit assert (True == toolkit.check_access('organization_show', {'user': internal_user['name']}, {'id': deposit['id']})) assert (True == toolkit.check_access('organization_show', {'user': external_user['name']}, {'id': deposit['id']})) # Only internal users can see other containers assert (True == toolkit.check_access('organization_show', {'user': internal_user['name']}, {'id': container['id']})) with pytest.raises(toolkit.NotAuthorized): toolkit.check_access('organization_show', context={'user': external_user['name']}, data_dict={'id': container['id']})