Allow to access datasets description page when private (#51)

This commit is contained in:
SSladarov 2019-05-30 15:38:30 +02:00 committed by Francisco de la Vega
parent bdb1e3ff57
commit 5341906767
10 changed files with 348 additions and 165 deletions

5
.gitignore vendored
View File

@ -20,7 +20,7 @@ var/
*.egg-info/ *.egg-info/
.installed.cfg .installed.cfg
*.egg *.egg
.eggs
# Installer logs # Installer logs
pip-log.txt pip-log.txt
pip-delete-this-directory.txt pip-delete-this-directory.txt
@ -50,3 +50,6 @@ coverage.xml
# Sphinx documentation # Sphinx documentation
docs/_build/ docs/_build/
.idea
venv

View File

@ -41,41 +41,37 @@ def package_show(context, data_dict):
# Not active packages can only be seen by its owners # Not active packages can only be seen by its owners
if package.state == 'active': if package.state == 'active':
# anyone can see a public package # anyone can see a public package
if not package.private: if package.private:
return {'success': True}
# if the user has rights to read in the organization or in the group acquired = False
if package.owner_org:
authorized = authz.has_user_permission_for_group_or_org(
package.owner_org, user, 'read')
else:
authorized = False
# if the user is not authorized yet, we should check if the if package.owner_org:
# user is in the allowed_users object acquired = authz.has_user_permission_for_group_or_org(
if not authorized: package.owner_org, user, 'read')
# Init the model
db.init_db(context['model'])
# Branch not executed if the database return an empty list if not acquired:
if db.AllowedUser.get(package_id=package.id, user_name=user): # Init the model
authorized = True db.init_db(context['model'])
if not authorized: # Branch not executed if the database return an empty list
# Show a flash message with the URL to acquire the dataset if db.AllowedUser.get(package_id=package.id, user_name=user):
# This message only can be shown when the user tries to access the dataset via its URL (/dataset/...) acquired = True
# The message cannot be displayed in other pages that uses the package_show function such as
# the user profile page
if hasattr(package, 'extras') and 'acquire_url' in package.extras and request.path.startswith('/dataset/')\ if not acquired:
and package.extras['acquire_url'] != '':
helpers.flash_notice(_('This private dataset can be acquired. To do so, please click ' +
'<a target="_blank" href="%s">here</a>') % package.extras['acquire_url'],
allow_html=True)
return {'success': False, 'msg': _('User %s not authorized to read package %s') % (user, package.id)} # Show a flash message with the URL to acquire the dataset
else: # This message only can be shown when the user tries to access the dataset via its URL (/dataset/...)
return {'success': True} # The message cannot be displayed in other pages that uses the package_show function such as
# the user profile page
if hasattr(package, 'extras') and 'acquire_url' in package.extras and request.path.startswith(
'/dataset/') \
and package.extras['acquire_url'] != '':
helpers.flash_notice(_('This private dataset can be acquired. To do so, please click ' +
'<a target="_blank" href="%s">here</a>') % package.extras['acquire_url'],
allow_html=True)
return {'success': True}
else: else:
return {'success': False, 'msg': _('User %s not authorized to read package %s') % (user, package.id)} return {'success': False, 'msg': _('User %s not authorized to read package %s') % (user, package.id)}
@ -104,32 +100,49 @@ def package_update(context, data_dict):
@tk.auth_allow_anonymous_access @tk.auth_allow_anonymous_access
def resource_show(context, data_dict): def resource_show(context, data_dict):
# This function is needed since CKAN resource_show function uses the default package_show
# function instead of the one defined in the plugin.
# A bug is openend in order to be able to remove this function
# https://github.com/ckan/ckan/issues/1818
# It's fixed now, so this function can be deleted when the new version is released.
_model = context['model']
user = context.get('user')
resource = logic_auth.get_resource_object(context, data_dict)
user = context.get('user')
user_obj = context.get('auth_user_obj')
resource = logic_auth.get_resource_object(context, data_dict)
# check authentication against package # check authentication against package
query = _model.Session.query(_model.Package)\ package_dict = {'id': resource.package_id}
.join(_model.ResourceGroup)\ package = logic_auth.get_package_object(context, package_dict)
.join(_model.Resource)\ if not package:
.filter(_model.ResourceGroup.id == resource.resource_group_id)
pkg = query.first()
if not pkg:
raise tk.ObjectNotFound(_('No package found for this resource, cannot check auth.')) raise tk.ObjectNotFound(_('No package found for this resource, cannot check auth.'))
pkg_dict = {'id': pkg.id} if package and user_obj and package.creator_user_id == user_obj.id:
authorized = package_show(context, pkg_dict).get('success')
if not authorized:
return {'success': False, 'msg': _('User %s not authorized to read resource %s') % (user, resource.id)}
else:
return {'success': True} return {'success': True}
# active packages can only be seen by its owners
if package.state == 'active':
# anyone can see a public package
if not package.private:
return {'success': True}
# if the user has rights to read in the organization or in the group
if package.owner_org:
authorized = authz.has_user_permission_for_group_or_org(
package.owner_org, user, 'read')
else:
authorized = False
if not authorized:
# Init the model
db.init_db(context['model'])
# Branch not executed if the database return an empty list
if db.AllowedUser.get(package_id=package.id, user_name=user):
authorized = True
if not authorized:
return {'success': False, 'msg': _('User %s not authorized to read resource %s') % (user, resource.id)}
else:
return {'success': True}
else:
return {'success': False, 'msg': _('User %s not authorized to read resource %s') % (user, resource.id)}
@tk.auth_allow_anonymous_access @tk.auth_allow_anonymous_access
def package_acquired(context, data_dict): def package_acquired(context, data_dict):

View File

@ -29,6 +29,7 @@ from flask import Blueprint
from ckanext.privatedatasets import auth, actions, constants, converters_validators as conv_val, db, helpers from ckanext.privatedatasets import auth, actions, constants, converters_validators as conv_val, db, helpers
from ckanext.privatedatasets.views import acquired_datasets from ckanext.privatedatasets.views import acquired_datasets
HIDDEN_FIELDS = [constants.ALLOWED_USERS, constants.SEARCHABLE] HIDDEN_FIELDS = [constants.ALLOWED_USERS, constants.SEARCHABLE]
@ -43,6 +44,7 @@ class PrivateDatasets(p.SingletonPlugin, tk.DefaultDatasetForm, DefaultPermissio
p.implements(p.IPackageController, inherit=True) p.implements(p.IPackageController, inherit=True)
p.implements(p.ITemplateHelpers) p.implements(p.ITemplateHelpers)
p.implements(p.IPermissionLabels) p.implements(p.IPermissionLabels)
p.implements(p.IResourceController)
###################################################################### ######################################################################
############################ DATASET FORM ############################ ############################ DATASET FORM ############################
@ -112,16 +114,11 @@ class PrivateDatasets(p.SingletonPlugin, tk.DefaultDatasetForm, DefaultPermissio
def get_auth_functions(self): def get_auth_functions(self):
auth_functions = {'package_show': auth.package_show, auth_functions = {'package_show': auth.package_show,
'package_update': auth.package_update, 'package_update': auth.package_update,
# 'resource_show': auth.resource_show, 'resource_show': auth.resource_show,
constants.PACKAGE_ACQUIRED: auth.package_acquired, constants.PACKAGE_ACQUIRED: auth.package_acquired,
constants.ACQUISITIONS_LIST: auth.acquisitions_list, constants.ACQUISITIONS_LIST: auth.acquisitions_list,
constants.PACKAGE_DELETED: auth.revoke_access} constants.PACKAGE_DELETED: auth.revoke_access}
# resource_show is not required in CKAN 2.3 because it delegates to
# package_show
if not tk.check_ckan_version(min_version='2.3'):
auth_functions['resource_show'] = auth.resource_show
return auth_functions return auth_functions
###################################################################### ######################################################################
@ -162,11 +159,11 @@ class PrivateDatasets(p.SingletonPlugin, tk.DefaultDatasetForm, DefaultPermissio
###################################################################### ######################################################################
def get_actions(self): def get_actions(self):
return { action_functions = {constants.PACKAGE_ACQUIRED: actions.package_acquired,
constants.PACKAGE_ACQUIRED: actions.package_acquired, constants.ACQUISITIONS_LIST: actions.acquisitions_list,
constants.ACQUISITIONS_LIST: actions.acquisitions_list, constants.PACKAGE_DELETED: actions.revoke_access}
constants.PACKAGE_DELETED: actions.revoke_access
} return action_functions
###################################################################### ######################################################################
######################### IPACKAGECONTROLLER ######################### ######################### IPACKAGECONTROLLER #########################
@ -244,6 +241,16 @@ class PrivateDatasets(p.SingletonPlugin, tk.DefaultDatasetForm, DefaultPermissio
def after_show(self, context, pkg_dict): def after_show(self, context, pkg_dict):
void = False;
for resource in pkg_dict['resources']:
if resource == {}:
void = True
if void:
del pkg_dict['resources']
del pkg_dict['num_resources']
user_obj = context.get('auth_user_obj') user_obj = context.get('auth_user_obj')
updating_via_api = context.get(constants.CONTEXT_CALLBACK, False) updating_via_api = context.get(constants.CONTEXT_CALLBACK, False)
@ -294,13 +301,29 @@ class PrivateDatasets(p.SingletonPlugin, tk.DefaultDatasetForm, DefaultPermissio
# NotAuthorized exception is risen when the user is not allowed # NotAuthorized exception is risen when the user is not allowed
# to read the package. # to read the package.
attrs.append('resources') attrs.append('resources')
# Delete # Delete
self._delete_pkg_atts(result, attrs) self._delete_pkg_atts(result, attrs)
return search_results return search_results
#### ####
def before_view(self, pkg_dict):
for resource in pkg_dict['resources']:
context = {
'model': model,
'session': model.Session,
'user': tk.c.user,
'user_obj': tk.c.userobj
}
try:
tk.check_access('resource_show', context, resource)
except tk.NotAuthorized:
pkg_dict['resources'].remove(resource)
pkg_dict = self.before_view(pkg_dict)
return pkg_dict
def get_dataset_labels(self, dataset_obj): def get_dataset_labels(self, dataset_obj):
labels = super(PrivateDatasets, self).get_dataset_labels( labels = super(PrivateDatasets, self).get_dataset_labels(
@ -318,6 +341,34 @@ class PrivateDatasets(p.SingletonPlugin, tk.DefaultDatasetForm, DefaultPermissio
labels.append('searchable') labels.append('searchable')
return labels return labels
######################################################################
######################### IRESOURCECONTROLLER ########################
######################################################################
def before_create(self, context, resource):
pass
def before_update(self, context, current, resource):
pass
def before_delete(self, context, resource, resources):
pass
def before_show(self, resource_dict):
context = {
'model': model,
'session': model.Session,
'user': tk.c.user,
'user_obj': tk.c.userobj
}
try:
tk.check_access('resource_show', context, resource_dict)
except tk.NotAuthorized:
resource_dict.clear()
return resource_dict
###################################################################### ######################################################################
######################### ITEMPLATESHELPER ########################### ######################### ITEMPLATESHELPER ###########################
###################################################################### ######################################################################

View File

@ -26,7 +26,7 @@ Example:
{% block package_item_content %} {% block package_item_content %}
<div class="dataset-content"> <div class="dataset-content">
<h3 class="dataset-heading"> <h3 class="dataset-heading">
{% if package.private and not h.can_read(package) %} {% if package.private and not owner and not acquired %}
<span class="dataset-private label label-inverse"> <span class="dataset-private label label-inverse">
<i class="icon-lock fa fa-lock"></i> <i class="icon-lock fa fa-lock"></i>
{{ _('Private') }} {{ _('Private') }}
@ -46,8 +46,8 @@ Example:
{% endif %} {% endif %}
<!-- Customizations Acquire Button --> <!-- Customizations Acquire Button -->
{% if package.private and not h.can_read(package) %} {% if package.private and not owner and not acquired %}
{{ _(h.truncate(title, truncate_title)) }} {{ h.link_to(h.truncate(title, truncate_title), h.url_for(controller='package', action='read', id=package.name)) }}
<div class="divider"/> <div class="divider"/>
{{ h.acquire_button(package) }} {{ h.acquire_button(package) }}
{% else %} {% else %}

View File

@ -26,7 +26,7 @@ Example:
{% block package_item_content %} {% block package_item_content %}
<div class="dataset-content"> <div class="dataset-content">
<h3 class="dataset-heading"> <h3 class="dataset-heading">
{% if package.private and not h.can_read(package) %} {% if package.private and not owner and not acquired%}
<span class="dataset-private label label-inverse"> <span class="dataset-private label label-inverse">
<i class="icon-lock fa fa-lock"></i> <i class="icon-lock fa fa-lock"></i>
{{ _('Private') }} {{ _('Private') }}
@ -46,8 +46,8 @@ Example:
{% endif %} {% endif %}
<!-- Customizations Acquire Button --> <!-- Customizations Acquire Button -->
{% if package.private and not h.can_read(package) %} {% if package.private and not owner and not acquired %}
{{ _(h.truncate(title, truncate_title)) }} {{ h.link_to(h.truncate(title, truncate_title), h.url_for(controller='package', action='read', id=package.name)) }}
<div class="divider"/> <div class="divider"/>
{{ h.acquire_button(package) }} {{ h.acquire_button(package) }}
{% else %} {% else %}

View File

@ -53,7 +53,6 @@ class AuthTest(unittest.TestCase):
auth.authz = self._authz auth.authz = self._authz
auth.tk = self._tk auth.tk = self._tk
auth.db = self._db auth.db = self._db
if hasattr(self, '_package_show'): if hasattr(self, '_package_show'):
auth.package_show = self._package_show auth.package_show = self._package_show
@ -66,12 +65,12 @@ class AuthTest(unittest.TestCase):
# Anonymous user (public) # Anonymous user (public)
(None, None, None, False, 'active', None, None, None, None, None, True), (None, None, None, False, 'active', None, None, None, None, None, True),
# Anonymous user (private) # Anonymous user (private)
(None, None, None, True, 'active', None, None, None, None, '/', False), (None, None, None, True, 'active', None, None, None, None, '/', True),
(None, None, '', True, 'active', None, None, '', None, '/', False), (None, None, '', True, 'active', None, None, '', None, '/', True),
# Anonymous user (private). Buy URL not shown # Anonymous user (private). Buy URL not shown
(None, None, None, True, 'active', None, None, None, 'google.es', '/', False), (None, None, None, True, 'active', None, None, None, 'google.es', '/', True),
# Anonymous user (private). Buy URL show # Anonymous user (private). Buy URL show
(None, None, None, True, 'active', None, None, None, 'google.es', '/dataset/testds', False), (None, None, None, True, 'active', None, None, None, 'google.es', '/dataset/testds', True),
# The creator can always see the dataset # The creator can always see the dataset
(1, 1, None, False, 'active', None, None, None, None, None, True), (1, 1, None, False, 'active', None, None, None, None, None, True),
(1, 1, None, True, 'active', 'conwet', None, None, None, None, True), (1, 1, None, True, 'active', 'conwet', None, None, None, None, True),
@ -79,25 +78,26 @@ class AuthTest(unittest.TestCase):
(1, 1, None, False, 'draft', None, None, None, None, None, True), (1, 1, None, False, 'draft', None, None, None, None, None, True),
# Other user (no organizations) # Other user (no organizations)
(1, 2, 'test', False, 'active', None, None, None, None, None, True), (1, 2, 'test', False, 'active', None, None, None, None, None, True),
(1, 2, 'test', True, 'active', None, None, None, 'google.es', '/', False), # Buy MSG not shown (1, 2, 'test', True, 'active', None, None, None, 'google.es', '/', True), # Buy MSG not shown
(1, 2, 'test', True, 'active', None, None, None, None, '/dataset/testds', False), # Buy MSG not shown (1, 2, 'test', True, 'active', None, None, None, None, '/dataset/testds', True), # Buy MSG not shown
(1, 2, 'test', True, 'active', None, None, None, 'google.es', '/dataset/testds', False), # Buy MSG shown (1, 2, 'test', True, 'active', None, None, None, 'google.es', '/dataset/testds', True), # Buy MSG shown
(1, 2, 'test', False, 'draft', None, None, None, None, None, False), (1, 2, 'test', False, 'draft', None, None, None, None, None, False),
# Other user but authorized in the list of authorized users # Other user but authorized in the list of authorized users
(1, 2, 'test', True, 'active', None, None, True, None, None, True), (1, 2, 'test', True, 'active', None, None, True, None, None, True),
# Other user and not authorized in the list of authorized users # Other user and not authorized in the list of authorized users
(1, 2, 'test', True, 'active', None, None, False, 'google.es', '/', False), (1, 2, 'test', True, 'active', None, None, False, 'google.es', '/', True),
(1, 2, 'test', True, 'active', None, None, False, 'google.es', '/dataset/testds', False), (1, 2, 'test', True, 'active', None, None, False, 'google.es', '/dataset/testds', True),
# Other user with organizations # Other user with organizations
(1, 2, 'test', False, 'active', 'conwet', False, None, None, None, True), (1, 2, 'test', False, 'active', 'conwet', False, None, None, None, True),
(1, 2, 'test', True, 'active', 'conwet', False, None, None, None, False), (1, 2, 'test', True, 'active', 'conwet', False, None, None, None, True),
(1, 2, 'test', True, 'active', 'conwet', True, None, None, None, True), (1, 2, 'test', True, 'active', 'conwet', True, None, None, None, True),
(1, 2, 'test', True, 'draft', 'conwet', True, None, None, None, False), (1, 2, 'test', True, 'draft', 'conwet', True, None, None, None, False),
# Other user with organizations (user is not in the organization) # Other user with organizations (user is not in the organization)
(1, 2, 'test', True, 'active', 'conwet', False, True, None, None, True), (1, 2, 'test', True, 'active', 'conwet', False, True, None, None, True),
(1, 2, 'test', True, 'active', 'conwet', False, False, None, None, False), (1, 2, 'test', True, 'active', 'conwet', False, False, None, None, True),
(1, 2, 'test', True, 'active', 'conwet', False, False, 'google.es', '/dataset/testds', False), (1, 2, 'test', True, 'active', 'conwet', False, False, 'google.es', '/dataset/testds', True),
(1, 2, 'test', True, 'active', 'conwet', False, False, 'google.es', '/', False) ]) (1, 2, 'test', True, 'active', 'conwet', False, False, 'google.es', '/', True)
])
def test_auth_package_show(self, creator_user_id, user_obj_id, user, private, state, owner_org, def test_auth_package_show(self, creator_user_id, user_obj_id, user, private, state, owner_org,
owner_member, db_auth, acquire_url, request_path, authorized): owner_member, db_auth, acquire_url, request_path, authorized):
@ -140,14 +140,14 @@ class AuthTest(unittest.TestCase):
# Check the result # Check the result
self.assertEquals(authorized, result['success']) self.assertEquals(authorized, result['success'])
# Premissions for organization are checked when the dataset is private, it belongs to an organization # Permissions for organization are checked when the dataset is private, it belongs to an organization
# and when the dataset has not been created by the user who is asking for it # and when the dataset has not been created by the user who is asking for it
if private and owner_org and state == 'active' and creator_user_id != user_obj_id: if private and owner_org and state == 'active' and creator_user_id != user_obj_id:
auth.authz.has_user_permission_for_group_or_org.assert_called_once_with(owner_org, user, 'read') auth.authz.has_user_permission_for_group_or_org.assert_called_once_with(owner_org, user, 'read')
else: else:
self.assertEquals(0, auth.authz.has_user_permission_for_group_or_org.call_count) self.assertEquals(0, auth.authz.has_user_permission_for_group_or_org.call_count)
# The databse is only initialized when: # The database is only initialized when:
# * the dataset is private AND # * the dataset is private AND
# * the dataset is active AND # * the dataset is active AND
# * the dataset has no organization OR the user does not belong to that organization AND # * the dataset has no organization OR the user does not belong to that organization AND
@ -159,7 +159,7 @@ class AuthTest(unittest.TestCase):
self.assertEquals(0, auth.db.init_db.call_count) self.assertEquals(0, auth.db.init_db.call_count)
# Conditions to buy a dataset; It should be private, active and should not belong to any organization # Conditions to buy a dataset; It should be private, active and should not belong to any organization
if not authorized and state == 'active' and request_path and request_path.startswith('/dataset/') and acquire_url: if authorized and state == 'active' and request_path and request_path.startswith('/dataset/') and acquire_url:
auth.helpers.flash_notice.assert_called_once() auth.helpers.flash_notice.assert_called_once()
else: else:
self.assertEquals(0, auth.helpers.flash_notice.call_count) self.assertEquals(0, auth.helpers.flash_notice.call_count)
@ -203,56 +203,101 @@ class AuthTest(unittest.TestCase):
self.assertEquals(0, auth.authz.has_user_permission_for_group_or_org.call_count) self.assertEquals(0, auth.authz.has_user_permission_for_group_or_org.call_count)
@parameterized.expand([ @parameterized.expand([
(True, True), # if package dont exist
(True, False), (False, None, None, None, False, 'active', None, None, None, False),
(False, False), # Anonymous user only can view resources of a public and active package
(False, False) (True, None, None, None, False, 'active', None, None, None, True),
(True, None, None, None, True, 'active', None, None, None, False),
(True, None, None, '', True, 'active', None, None, None, False),
# The creator can always see the resource
(True, 1, 1, None, False, 'active', None, None, None, True),
(True, 1, 1, None, True, 'active', None, None, None, True),
(True, 1, 1, None, True, 'draft', None, None, None, True),
(True, 1, 1, None, False, 'draft', None, None, None, True),
# Other user (no organizations)
(True, 1, 2, 'test', False, 'active', None, None, None, True),
(True, 1, 2, 'test', True, 'active', None, None, None, False),
(True, 1, 2, 'test', True, 'draft', None, None, None, False),
# Other user but authorized in the list of authorized users
(True, 1, 2, 'test', True, 'active', None, None, True, True),
# Other user and not authorized in the list of authorized users
(True, 1, 2, 'test', True, 'active', None, None, False, False),
# Other user with organizations
(True, 1, 2, 'test', True, 'active', 'conwet', False, None, False),
(True, 1, 2, 'test', True, 'active', 'conwet', True, None, True),
]) ])
def test_auth_resource_show(self, exist_pkg=True, authorized_pkg=True): def test_auth_resource_show(self, exist_pkg, creator_user_id, user_obj_id, user, private, state, owner_org,
owner_member, db_auth, authorized):
#Recover the exception #Recover the exception
auth.tk.ObjectNotFound = self._tk.ObjectNotFound auth.tk.ObjectNotFound = self._tk.ObjectNotFound
# Mock the calls # Configure the mocks
package = MagicMock() if exist_pkg:
package.id = '1' returned_package = MagicMock()
returned_package.creator_user_id = creator_user_id
returned_package.private = private
returned_package.state = state
returned_package.owner_org = owner_org
returned_package.extras = {}
else:
returned_package = None
final_query = MagicMock() returned_resource = MagicMock()
final_query.first = MagicMock(return_value=package if exist_pkg else None) returned_resource.package_id = 1
second_join = MagicMock() # Configure the database
second_join.filter = MagicMock(return_value=final_query) db_response = []
if db_auth is True:
out = auth.db.AllowedUser()
out.package_id = 'package_id'
out.user_name = user
db_response.append(out)
first_join = MagicMock() # Prepare the context
first_join.join = MagicMock(return_value=second_join) context = {'model': MagicMock()}
if user is not None:
context['user'] = user
if user_obj_id is not None:
context['auth_user_obj'] = MagicMock()
context['auth_user_obj'].id = user_obj_id
query = MagicMock() auth.db.AllowedUser.get = MagicMock(return_value=db_response)
query.join = MagicMock(return_value=first_join) auth.logic_auth.get_resource_object = MagicMock(return_value=returned_resource)
auth.logic_auth.get_package_object = MagicMock(return_value=returned_package)
auth.authz.has_user_permission_for_group_or_org = MagicMock(return_value=owner_member)
model = MagicMock() # Prepare the context
session = MagicMock() context = {'model': MagicMock()}
session.query = MagicMock(return_value=query) if user is not None:
model.Session = session context['user'] = user
if user_obj_id is not None:
# Create the context context['auth_user_obj'] = MagicMock()
context = {} context['auth_user_obj'].id = user_obj_id
context['model'] = model
# Mock the package_show function
self._package_show = auth.package_show
success = True if authorized_pkg else False
auth.package_show = MagicMock(return_value={'success': success})
if not exist_pkg: if not exist_pkg:
self.assertRaises(self._tk.ObjectNotFound, auth.resource_show, context, {}) self.assertRaises(self._tk.ObjectNotFound, auth.resource_show, context, {})
else: else:
result = auth.resource_show(context, {}) result = auth.resource_show(context, {})
self.assertEquals(authorized_pkg, result['success']) self.assertEquals(authorized, result['success'])
if private and owner_org and state == 'active' and creator_user_id != user_obj_id:
auth.authz.has_user_permission_for_group_or_org.assert_called_once_with(owner_org, user, 'read')
else:
self.assertEquals(0, auth.authz.has_user_permission_for_group_or_org.call_count)
if private and state == 'active' and (not owner_org or not owner_member) and (creator_user_id != user_obj_id or user_obj_id is None):
# Check that the database has been initialized properly
auth.db.init_db.assert_called_once_with(context['model'])
else:
self.assertEquals(0, auth.db.init_db.call_count)
def test_package_acquired(self): def test_package_acquired(self):
self.assertTrue(auth.package_acquired({}, {})['success']) self.assertTrue(auth.package_acquired({}, {})['success'])
def test_package_deleted(self): def test_package_deleted(self):
self.assertTrue(auth.revoke_access({},{})['success']) self.assertTrue(auth.revoke_access({}, {})['success'])
@parameterized.expand([ @parameterized.expand([
({'user': 'user_1'}, {'user': 'user_1'}, True), ({'user': 'user_1'}, {'user': 'user_1'}, True),

View File

@ -39,7 +39,6 @@ class HelpersTest(unittest.TestCase):
self._db = helpers.db self._db = helpers.db
helpers.db = MagicMock() helpers.db = MagicMock()
self._request = helpers.request self._request = helpers.request
helpers.request = MagicMock() helpers.request = MagicMock()

View File

@ -65,19 +65,13 @@ class PluginTest(unittest.TestCase):
('package_show', plugin.auth.package_show), ('package_show', plugin.auth.package_show),
('package_update', plugin.auth.package_update), ('package_update', plugin.auth.package_update),
('resource_show', plugin.auth.resource_show), ('resource_show', plugin.auth.resource_show),
('resource_show', plugin.auth.resource_show, True, False),
('package_acquired', plugin.auth.package_acquired), ('package_acquired', plugin.auth.package_acquired),
('acquisitions_list', plugin.auth.acquisitions_list), ('acquisitions_list', plugin.auth.acquisitions_list),
('revoke_access', plugin.auth.revoke_access) ('revoke_access', plugin.auth.revoke_access)
]) ])
def test_auth_function(self, function_name, expected_function, is_ckan_23=False, expected=True): def test_auth_function(self, function_name, expected_function):
plugin.tk.check_ckan_version = MagicMock(return_value=is_ckan_23)
auth_functions = self.privateDatasets.get_auth_functions() auth_functions = self.privateDatasets.get_auth_functions()
self.assertEquals(auth_functions[function_name], expected_function)
if expected:
self.assertEquals(auth_functions[function_name], expected_function)
else:
self.assertNotIn(function_name, auth_functions)
def test_update_config(self): def test_update_config(self):
# Call the method # Call the method
@ -215,41 +209,73 @@ class PluginTest(unittest.TestCase):
self.assertTrue(found) self.assertTrue(found)
@parameterized.expand([ @parameterized.expand([
(True, 1, 1, False, True, True), (True, 1, 1, False, True, True, [{'id': 1}, {'id': 2}], True),
(True, 1, 2, False, True, True), (True, 1, 2, False, True, True, [{'id': 1}, {'id': 2}], True),
(True, 1, 1, True, True, True), (True, 1, 1, True, True, True, [{'id': 1}, {'id': 2}], True),
(True, 1, 2, True, True, True), (True, 1, 2, True, True, True, [{'id': 1}, {'id': 2}], True),
(True, 1, None, None, True, True), (True, 1, None, None, True, True, [{'id': 1}, {'id': 2}], True),
(True, 1, 1, None, True, True), (True, 1, 1, None, True, True, [{'id': 1}, {'id': 2}], True),
(True, 1, None, True, True, True), (True, 1, None, True, True, True, [{'id': 1}, {'id': 2}], True),
(True, 1, None, False, True, True), (True, 1, None, False, True, True, [{'id': 1}, {'id': 2}], True),
(False, 1, 1, False, True, True), (False, 1, 1, False, True, True, [{'id': 1}, {'id': 2}], True),
(False, 1, 2, False, True, False), (False, 1, 2, False, True, False, [{'id': 1}, {'id': 2}], True),
(False, 1, 1, True, True, True), (False, 1, 1, True, True, True, [{'id': 1}, {'id': 2}], True),
(False, 1, 2, True, True, True), (False, 1, 2, True, True, True, [{'id': 1}, {'id': 2}], True),
(False, 1, None, None, True, False), (False, 1, None, None, True, False, [{'id': 1}, {'id': 2}], True),
(False, 1, 1, None, True, True), (False, 1, 1, None, True, True, [{'id': 1}, {'id': 2}], True),
(False, 1, None, True, True, True), (False, 1, None, True, True, True, [{'id': 1}, {'id': 2}], True),
(False, 1, None, False, True, False), (False, 1, None, False, True, False, [{'id': 1}, {'id': 2}], True),
(True, 1, 1, False, False, False), (True, 1, 1, False, False, False, [{'id': 1}, {'id': 2}], True),
(True, 1, 2, False, False, False), (True, 1, 2, False, False, False, [{'id': 1}, {'id': 2}], True),
(True, 1, 1, True, False, False), (True, 1, 1, True, False, False, [{'id': 1}, {'id': 2}], True),
(True, 1, 2, True, False, False), (True, 1, 2, True, False, False, [{'id': 1}, {'id': 2}], True),
(True, 1, None, None, False, False), (True, 1, None, None, False, False, [{'id': 1}, {'id': 2}], True),
(True, 1, 1, None, False, False), (True, 1, 1, None, False, False, [{'id': 1}, {'id': 2}], True),
(True, 1, None, True, False, False), (True, 1, None, True, False, False, [{'id': 1}, {'id': 2}], True),
(True, 1, None, False, False, False), (True, 1, None, False, False, False, [{'id': 1}, {'id': 2}], True),
(False, 1, 1, False, False, False), (False, 1, 1, False, False, False, [{'id': 1}, {'id': 2}], True),
(False, 1, 2, False, False, False), (False, 1, 2, False, False, False, [{'id': 1}, {'id': 2}], True),
(False, 1, 1, True, False, False), (False, 1, 1, True, False, False, [{'id': 1}, {'id': 2}], True),
(False, 1, 2, True, False, False), (False, 1, 2, True, False, False, [{'id': 1}, {'id': 2}], True),
(False, 1, None, None, False, False), (False, 1, None, None, False, False, [{'id': 1}, {'id': 2}], True),
(False, 1, 1, None, False, False), (False, 1, 1, None, False, False, [{'id': 1}, {'id': 2}], True),
(False, 1, None, True, False, False), (False, 1, None, True, False, False, [{'id': 1}, {'id': 2}], True),
(False, 1, None, False, False, False), (False, 1, None, False, False, False, [{'id': 1}, {'id': 2}], True),
(True, 1, 1, False, True, True, [{}, {}], False),
(True, 1, 2, False, True, True, [{}, {}], False),
(True, 1, 1, True, True, True, [{}, {}], False),
(True, 1, 2, True, True, True, [{}, {}], False),
(True, 1, None, None, True, True, [{}, {}], False),
(True, 1, 1, None, True, True, [{}, {}], False),
(True, 1, None, True, True, True, [{}, {}], False),
(True, 1, None, False, True, True, [{}, {}], False),
(False, 1, 1, False, True, True, [{}, {}], False),
(False, 1, 2, False, True, False, [{}, {}], False),
(False, 1, 1, True, True, True, [{}, {}], False),
(False, 1, 2, True, True, True, [{}, {}], False),
(False, 1, None, None, True, False, [{}, {}], False),
(False, 1, 1, None, True, True, [{}, {}], False),
(False, 1, None, True, True, True, [{}, {}], False),
(False, 1, None, False, True, False, [{}, {}], False),
(True, 1, 1, False, False, False, [{}, {}], False),
(True, 1, 2, False, False, False, [{}, {}], False),
(True, 1, 1, True, False, False, [{}, {}], False),
(True, 1, 2, True, False, False, [{}, {}], False),
(True, 1, None, None, False, False, [{}, {}], False),
(True, 1, 1, None, False, False, [{}, {}], False),
(True, 1, None, True, False, False, [{}, {}], False),
(True, 1, None, False, False, False, [{}, {}], False),
(False, 1, 1, False, False, False, [{}, {}], False),
(False, 1, 2, False, False, False, [{}, {}], False),
(False, 1, 1, True, False, False, [{}, {}], False),
(False, 1, 2, True, False, False, [{}, {}], False),
(False, 1, None, None, False, False, [{}, {}], False),
(False, 1, 1, None, False, False, [{}, {}], False),
(False, 1, None, True, False, False, [{}, {}], False),
(False, 1, None, False, False, False, [{}, {}], False),
]) ])
def test_packagecontroller_after_show(self, update_via_api, creator_id, user_id, sysadmin, private, fields_expected): def test_packagecontroller_after_show(self, update_via_api, creator_id, user_id, sysadmin, private, fields_expected, resources, resources_fields):
context = {'updating_via_cb': update_via_api} context = {'updating_via_cb': update_via_api}
if creator_id is not None or sysadmin is not None: if creator_id is not None or sysadmin is not None:
@ -258,7 +284,7 @@ class PluginTest(unittest.TestCase):
user.sysadmin = sysadmin user.sysadmin = sysadmin
context['auth_user_obj'] = user context['auth_user_obj'] = user
pkg_dict = {'creator_user_id': creator_id, 'allowed_users': ['a', 'b', 'c'], 'searchable': True, 'acquire_url': 'http://google.es', 'private': private} pkg_dict = {'creator_user_id': creator_id, 'allowed_users': ['a', 'b', 'c'], 'searchable': True, 'acquire_url': 'http://google.es', 'private': private, 'resources': resources, 'num_resources': 2}
# Call the function # Call the function
result = self.privateDatasets.after_show(context, pkg_dict) # Call the function result = self.privateDatasets.after_show(context, pkg_dict) # Call the function
@ -271,6 +297,14 @@ class PluginTest(unittest.TestCase):
else: else:
self.assertFalse(field in result) self.assertFalse(field in result)
fields = ['resources', 'num_resources']
for field in fields:
if resources_fields:
self.assertTrue(field in result)
else:
self.assertFalse(field in result)
@parameterized.expand([ @parameterized.expand([
('public', None, 'public'), ('public', None, 'public'),
('public', 'False', 'private'), ('public', 'False', 'private'),
@ -449,3 +483,40 @@ class PluginTest(unittest.TestCase):
self.assertEquals(final_search_results['facets'], search_results['facets']) self.assertEquals(final_search_results['facets'], search_results['facets'])
self.assertEquals(final_search_results['elements'], search_results['elements']) self.assertEquals(final_search_results['elements'], search_results['elements'])
@parameterized.expand([
(True,),
(False,)
])
def test_package_controller_before_view(self, user_allowed):
pkg_dict = {'resources': [{'id': 1}, {'id': 2}, {'id': 3}]}
pkg_dict_not_allowed = {'resources': []}
plugin.tk.check_access.side_effect = None if user_allowed else plugin.tk.NotAuthorized
result = self.privateDatasets.before_view(pkg_dict)
if user_allowed:
self.assertEquals(result['resources'], pkg_dict['resources'])
else:
self.assertEquals(result['resources'], pkg_dict_not_allowed['resources'])
@parameterized.expand([
(True,),
(False,)
])
def test_resource_controller_before_show(self, user_allowed):
resource_dict = {'id': 1, 'resource_name': 'resource_test'}
plugin.tk.check_access.side_effect = None if user_allowed else plugin.tk.NotAuthorized
result = self.privateDatasets.before_show(resource_dict)
if user_allowed:
self.assertEquals(result['id'], resource_dict['id'])
self.assertEquals(result['resource_name'], resource_dict['resource_name'])
else:
self.assertNotIn('id', result)
self.assertNotIn('resource_name', result)

View File

@ -270,13 +270,13 @@ class TestSelenium(unittest.TestCase):
driver.get(self.base_url + 'dataset/' + dataset_url) driver.get(self.base_url + 'dataset/' + dataset_url)
if not acquired and private and not in_org: if not acquired and private and not in_org:
# If the user has not access to the dataset the 404 error page is displayed # If the dataset is private and the user hasnt access to the resources, the field resources dont appear
xpath = '//*[@id="content"]/div[2]/article/div/h1'
msg = '404 Not Found'
self.assertEqual(driver.find_element_by_xpath(xpath).text, msg) self.assertEquals('empty', driver.find_element_by_class_name('empty').get_attribute('class'))
self.assertEqual(self.base_url + 'dataset/%s' % dataset_url, driver.current_url)
else: else:
self.assertEquals('resource-list', driver.find_element_by_class_name('resource-list').get_attribute('class'))
self.assertEqual(self.base_url + 'dataset/%s' % dataset_url, driver.current_url) self.assertEqual(self.base_url + 'dataset/%s' % dataset_url, driver.current_url)
def check_acquired(self, dataset, dataset_url, acquired, private): def check_acquired(self, dataset, dataset_url, acquired, private):

View File

@ -4,6 +4,7 @@ host = 0.0.0.0
port = 5000 port = 5000
[app:main] [app:main]
# use = config:/usr/lib/ckan/default/src/ckan/test-core.ini
use = config:./ckan/test-core.ini use = config:./ckan/test-core.ini
ckan.site_id = ckanext.privatedatasets.test ckan.site_id = ckanext.privatedatasets.test