2014-09-02 17:52:55 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2017-04-26 16:52:53 +02:00
|
|
|
# Copyright (c) 2014 - 2017 CoNWeT Lab., Universidad Politécnica de Madrid
|
2014-09-02 17:52:55 +02:00
|
|
|
|
|
|
|
# This file is part of CKAN Private Dataset Extension.
|
|
|
|
|
|
|
|
# CKAN Private Dataset Extension is free software: you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU Affero General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
|
|
|
|
# CKAN Private Dataset Extension is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with CKAN Private Dataset Extension. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2014-07-17 13:25:03 +02:00
|
|
|
import ckan.lib.search as search
|
2015-01-29 18:08:18 +01:00
|
|
|
import ckan.model as model
|
2014-06-23 17:25:37 +02:00
|
|
|
import ckan.plugins as p
|
2017-11-15 19:09:36 +01:00
|
|
|
from ckan.lib.plugins import DefaultPermissionLabels
|
2014-06-23 17:25:37 +02:00
|
|
|
import ckan.plugins.toolkit as tk
|
2014-07-10 12:26:31 +02:00
|
|
|
import auth
|
2014-07-14 11:05:56 +02:00
|
|
|
import actions
|
2014-07-11 13:55:23 +02:00
|
|
|
import constants
|
2014-07-10 12:26:31 +02:00
|
|
|
import converters_validators as conv_val
|
|
|
|
import db
|
|
|
|
import helpers as helpers
|
2014-07-04 14:18:10 +02:00
|
|
|
|
|
|
|
|
2015-02-19 17:02:10 +01:00
|
|
|
HIDDEN_FIELDS = [constants.ALLOWED_USERS, constants.SEARCHABLE]
|
|
|
|
|
|
|
|
|
2017-11-15 19:09:36 +01:00
|
|
|
class PrivateDatasets(p.SingletonPlugin, tk.DefaultDatasetForm, DefaultPermissionLabels):
|
2014-06-24 17:21:06 +02:00
|
|
|
|
2014-06-23 17:25:37 +02:00
|
|
|
p.implements(p.IDatasetForm)
|
|
|
|
p.implements(p.IAuthFunctions)
|
|
|
|
p.implements(p.IConfigurer)
|
2014-06-24 17:21:06 +02:00
|
|
|
p.implements(p.IRoutes, inherit=True)
|
2014-07-14 11:05:56 +02:00
|
|
|
p.implements(p.IActions)
|
2014-07-28 10:02:16 +02:00
|
|
|
p.implements(p.IPackageController, inherit=True)
|
2014-07-04 14:18:10 +02:00
|
|
|
p.implements(p.ITemplateHelpers)
|
2017-11-15 19:09:36 +01:00
|
|
|
p.implements(p.IPermissionLabels)
|
2014-06-23 17:25:37 +02:00
|
|
|
|
|
|
|
######################################################################
|
|
|
|
############################ DATASET FORM ############################
|
|
|
|
######################################################################
|
|
|
|
|
2014-07-17 13:25:03 +02:00
|
|
|
def __init__(self, name=None):
|
|
|
|
self.indexer = search.PackageSearchIndex()
|
|
|
|
|
2014-06-23 17:25:37 +02:00
|
|
|
def _modify_package_schema(self):
|
|
|
|
return {
|
2014-06-26 16:37:27 +02:00
|
|
|
# remove datasets_with_no_organization_cannot_be_private validator
|
|
|
|
'private': [tk.get_validator('ignore_missing'),
|
|
|
|
tk.get_validator('boolean_validator')],
|
2014-07-11 13:55:23 +02:00
|
|
|
constants.ALLOWED_USERS_STR: [tk.get_validator('ignore_missing'),
|
|
|
|
conv_val.private_datasets_metadata_checker],
|
2014-07-16 13:29:53 +02:00
|
|
|
constants.ALLOWED_USERS: [conv_val.allowed_users_convert,
|
|
|
|
tk.get_validator('ignore_missing'),
|
2014-07-11 13:55:23 +02:00
|
|
|
conv_val.private_datasets_metadata_checker],
|
2014-08-28 15:19:34 +02:00
|
|
|
constants.ACQUIRE_URL: [tk.get_validator('ignore_missing'),
|
2014-07-11 13:55:23 +02:00
|
|
|
conv_val.private_datasets_metadata_checker,
|
2014-07-29 15:33:16 +02:00
|
|
|
conv_val.url_checker,
|
2014-07-11 13:55:23 +02:00
|
|
|
tk.get_converter('convert_to_extras')],
|
|
|
|
constants.SEARCHABLE: [tk.get_validator('ignore_missing'),
|
|
|
|
conv_val.private_datasets_metadata_checker,
|
|
|
|
tk.get_converter('convert_to_extras'),
|
|
|
|
tk.get_validator('boolean_validator')]
|
2014-06-23 17:25:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
def create_package_schema(self):
|
2014-06-24 11:04:14 +02:00
|
|
|
# grab the default schema in our plugin
|
2014-06-23 17:25:37 +02:00
|
|
|
schema = super(PrivateDatasets, self).create_package_schema()
|
|
|
|
schema.update(self._modify_package_schema())
|
|
|
|
return schema
|
|
|
|
|
|
|
|
def update_package_schema(self):
|
2014-06-24 11:04:14 +02:00
|
|
|
# grab the default schema in our plugin
|
2014-06-23 17:25:37 +02:00
|
|
|
schema = super(PrivateDatasets, self).update_package_schema()
|
|
|
|
schema.update(self._modify_package_schema())
|
|
|
|
return schema
|
|
|
|
|
|
|
|
def show_package_schema(self):
|
|
|
|
schema = super(PrivateDatasets, self).show_package_schema()
|
|
|
|
schema.update({
|
2014-07-11 13:55:23 +02:00
|
|
|
constants.ALLOWED_USERS: [conv_val.get_allowed_users,
|
|
|
|
tk.get_validator('ignore_missing')],
|
2014-08-28 15:19:34 +02:00
|
|
|
constants.ACQUIRE_URL: [tk.get_converter('convert_from_extras'),
|
2014-07-11 13:55:23 +02:00
|
|
|
tk.get_validator('ignore_missing')],
|
|
|
|
constants.SEARCHABLE: [tk.get_converter('convert_from_extras'),
|
|
|
|
tk.get_validator('ignore_missing')]
|
2014-06-23 17:25:37 +02:00
|
|
|
})
|
|
|
|
return schema
|
|
|
|
|
|
|
|
def is_fallback(self):
|
|
|
|
# Return True to register this plugin as the default handler for
|
|
|
|
# package types not handled by any other IDatasetForm plugin.
|
|
|
|
return True
|
|
|
|
|
|
|
|
def package_types(self):
|
|
|
|
# This plugin doesn't handle any special package types, it just
|
|
|
|
# registers itself as the default (above).
|
|
|
|
return []
|
|
|
|
|
|
|
|
######################################################################
|
|
|
|
########################### AUTH FUNCTIONS ###########################
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def get_auth_functions(self):
|
2015-04-08 14:34:35 +02:00
|
|
|
auth_functions = {'package_show': auth.package_show,
|
|
|
|
'package_update': auth.package_update,
|
|
|
|
# 'resource_show': auth.resource_show,
|
|
|
|
constants.PACKAGE_ACQUIRED: auth.package_acquired,
|
2016-06-23 12:28:09 +02:00
|
|
|
constants.ACQUISITIONS_LIST: auth.acquisitions_list,
|
2017-04-26 16:52:53 +02:00
|
|
|
constants.PACKAGE_DELETED: auth.revoke_access}
|
2015-04-08 14:34:35 +02:00
|
|
|
|
2015-04-09 10:34:44 +02:00
|
|
|
# resource_show is not required in CKAN 2.3 because it delegates to
|
2015-04-08 14:34:35 +02:00
|
|
|
# package_show
|
2015-04-09 10:34:44 +02:00
|
|
|
if not tk.check_ckan_version(min_version='2.3'):
|
2015-04-08 14:34:35 +02:00
|
|
|
auth_functions['resource_show'] = auth.resource_show
|
|
|
|
|
|
|
|
return auth_functions
|
2014-06-23 17:25:37 +02:00
|
|
|
|
|
|
|
######################################################################
|
|
|
|
############################ ICONFIGURER #############################
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def update_config(self, config):
|
|
|
|
# Add this plugin's templates dir to CKAN's extra_template_paths, so
|
|
|
|
# that CKAN will use this plugin's custom templates.
|
|
|
|
tk.add_template_directory(config, 'templates')
|
|
|
|
|
|
|
|
# Register this plugin's fanstatic directory with CKAN.
|
|
|
|
tk.add_resource('fanstatic', 'privatedatasets')
|
2014-06-24 17:21:06 +02:00
|
|
|
|
|
|
|
######################################################################
|
2014-07-14 11:05:56 +02:00
|
|
|
############################## IROUTES ###############################
|
2014-06-24 17:21:06 +02:00
|
|
|
######################################################################
|
|
|
|
|
2014-07-29 12:11:54 +02:00
|
|
|
def before_map(self, m):
|
2014-08-28 15:19:34 +02:00
|
|
|
# DataSet acquired notification
|
|
|
|
m.connect('user_acquired_datasets', '/dashboard/acquired', ckan_icon='shopping-cart',
|
|
|
|
controller='ckanext.privatedatasets.controllers.ui_controller:AcquiredDatasetsControllerUI',
|
|
|
|
action='user_acquired_datasets', conditions=dict(method=['GET']))
|
2014-06-24 17:21:06 +02:00
|
|
|
|
|
|
|
return m
|
2014-06-26 10:22:10 +02:00
|
|
|
|
2016-07-01 13:24:18 +02:00
|
|
|
|
|
|
|
|
2014-07-14 11:05:56 +02:00
|
|
|
######################################################################
|
|
|
|
############################## IACTIONS ##############################
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def get_actions(self):
|
2015-01-23 14:44:40 +01:00
|
|
|
return {
|
|
|
|
constants.PACKAGE_ACQUIRED: actions.package_acquired,
|
2016-06-23 12:28:09 +02:00
|
|
|
constants.ACQUISITIONS_LIST: actions.acquisitions_list,
|
2017-04-26 16:52:53 +02:00
|
|
|
constants.PACKAGE_DELETED: actions.revoke_access
|
2015-01-23 14:44:40 +01:00
|
|
|
}
|
2014-07-14 11:05:56 +02:00
|
|
|
|
2014-07-04 11:49:36 +02:00
|
|
|
######################################################################
|
|
|
|
######################### IPACKAGECONTROLLER #########################
|
|
|
|
######################################################################
|
|
|
|
|
2015-01-29 18:08:18 +01:00
|
|
|
def _delete_pkg_atts(self, pkg_dict, attrs):
|
|
|
|
for attr in attrs:
|
|
|
|
if attr in pkg_dict:
|
|
|
|
del pkg_dict[attr]
|
|
|
|
|
2014-07-04 11:49:36 +02:00
|
|
|
def before_index(self, pkg_dict):
|
2014-07-07 09:30:51 +02:00
|
|
|
|
2014-07-11 13:55:23 +02:00
|
|
|
if 'extras_' + constants.SEARCHABLE in pkg_dict:
|
2014-07-07 09:30:51 +02:00
|
|
|
if pkg_dict['extras_searchable'] == 'False':
|
|
|
|
pkg_dict['capacity'] = 'private'
|
|
|
|
else:
|
|
|
|
pkg_dict['capacity'] = 'public'
|
|
|
|
|
2014-07-04 11:49:36 +02:00
|
|
|
return pkg_dict
|
|
|
|
|
|
|
|
def after_create(self, context, pkg_dict):
|
2014-07-10 12:26:31 +02:00
|
|
|
session = context['session']
|
2014-07-17 13:25:03 +02:00
|
|
|
update_cache = False
|
2014-07-10 12:26:31 +02:00
|
|
|
|
2014-07-15 11:52:09 +02:00
|
|
|
db.init_db(context['model'])
|
2014-07-10 12:26:31 +02:00
|
|
|
|
|
|
|
# Get the users and the package ID
|
2014-07-11 13:55:23 +02:00
|
|
|
if constants.ALLOWED_USERS in pkg_dict:
|
2014-07-10 15:48:53 +02:00
|
|
|
|
2014-07-15 14:59:47 +02:00
|
|
|
allowed_users = pkg_dict[constants.ALLOWED_USERS]
|
2014-07-10 13:21:28 +02:00
|
|
|
package_id = pkg_dict['id']
|
|
|
|
|
|
|
|
# Get current users
|
|
|
|
users = db.AllowedUser.get(package_id=package_id)
|
|
|
|
|
|
|
|
# Delete users and save the list of current users
|
|
|
|
current_users = []
|
|
|
|
for user in users:
|
|
|
|
current_users.append(user.user_name)
|
2014-07-15 14:59:47 +02:00
|
|
|
if user.user_name not in allowed_users:
|
2014-07-10 13:21:28 +02:00
|
|
|
session.delete(user)
|
2014-07-17 13:25:03 +02:00
|
|
|
update_cache = True
|
2014-07-10 13:21:28 +02:00
|
|
|
|
|
|
|
# Add non existing users
|
2014-07-15 14:59:47 +02:00
|
|
|
for user_name in allowed_users:
|
2014-07-10 13:21:28 +02:00
|
|
|
if user_name not in current_users:
|
|
|
|
out = db.AllowedUser()
|
|
|
|
out.package_id = package_id
|
|
|
|
out.user_name = user_name
|
|
|
|
out.save()
|
|
|
|
session.add(out)
|
2014-07-17 13:25:03 +02:00
|
|
|
update_cache = True
|
|
|
|
|
|
|
|
session.commit()
|
|
|
|
|
|
|
|
# The cache should be updated. Otherwise, the system may return
|
|
|
|
# outdated information in future requests
|
|
|
|
if update_cache:
|
|
|
|
new_pkg_dict = tk.get_action('package_show')(
|
|
|
|
{'model': context['model'],
|
|
|
|
'ignore_auth': True,
|
|
|
|
'validate': False,
|
|
|
|
'use_cache': False},
|
|
|
|
{'id': package_id})
|
2015-06-30 13:13:31 +02:00
|
|
|
|
2014-10-21 17:39:13 +02:00
|
|
|
# Prevent acquired datasets jumping to the first position
|
2015-06-30 13:13:31 +02:00
|
|
|
revision = tk.get_action('revision_show')({'ignore_auth': True}, {'id': new_pkg_dict['revision_id']})
|
|
|
|
new_pkg_dict['metadata_modified'] = revision.get('timestamp', '')
|
2014-07-17 13:25:03 +02:00
|
|
|
self.indexer.update_dict(new_pkg_dict)
|
2014-07-10 12:26:31 +02:00
|
|
|
|
2014-07-04 11:49:36 +02:00
|
|
|
return pkg_dict
|
|
|
|
|
2014-07-11 13:55:23 +02:00
|
|
|
def after_update(self, context, pkg_dict):
|
|
|
|
return self.after_create(context, pkg_dict)
|
|
|
|
|
2014-07-04 11:49:36 +02:00
|
|
|
def after_show(self, context, pkg_dict):
|
2014-07-11 13:55:23 +02:00
|
|
|
|
2014-07-10 12:26:31 +02:00
|
|
|
user_obj = context.get('auth_user_obj')
|
2014-07-11 13:55:23 +02:00
|
|
|
updating_via_api = context.get(constants.CONTEXT_CALLBACK, False)
|
2014-07-10 12:26:31 +02:00
|
|
|
|
2015-02-19 17:02:10 +01:00
|
|
|
# allowed_users and searchable fileds can be only viewed by (and only if the dataset is private):
|
2014-07-15 17:51:13 +02:00
|
|
|
# * the dataset creator
|
|
|
|
# * the sysadmin
|
|
|
|
# * users allowed to update the allowed_users list via the notification API
|
2014-09-05 10:59:33 +02:00
|
|
|
if pkg_dict.get('private') is False or not updating_via_api and (not user_obj or (pkg_dict['creator_user_id'] != user_obj.id and not user_obj.sysadmin)):
|
2015-02-19 17:02:10 +01:00
|
|
|
# The original list cannot be modified
|
|
|
|
attrs = list(HIDDEN_FIELDS)
|
2015-01-29 18:08:18 +01:00
|
|
|
self._delete_pkg_atts(pkg_dict, attrs)
|
2014-07-10 12:26:31 +02:00
|
|
|
|
2014-07-04 11:49:36 +02:00
|
|
|
return pkg_dict
|
|
|
|
|
2014-07-28 15:50:45 +02:00
|
|
|
def after_delete(self, context, pkg_dict):
|
|
|
|
session = context['session']
|
|
|
|
package_id = pkg_dict['id']
|
|
|
|
|
|
|
|
# Get current users
|
2014-08-27 15:33:10 +02:00
|
|
|
db.init_db(context['model'])
|
2014-07-28 15:50:45 +02:00
|
|
|
users = db.AllowedUser.get(package_id=package_id)
|
|
|
|
|
|
|
|
# Delete all the users
|
|
|
|
for user in users:
|
|
|
|
session.delete(user)
|
|
|
|
session.commit()
|
|
|
|
|
|
|
|
return pkg_dict
|
|
|
|
|
2015-01-29 18:08:18 +01:00
|
|
|
def after_search(self, search_results, search_params):
|
|
|
|
for result in search_results['results']:
|
|
|
|
# Extra fields should not be returned
|
2015-02-19 17:02:10 +01:00
|
|
|
# The original list cannot be modified
|
|
|
|
attrs = list(HIDDEN_FIELDS)
|
|
|
|
|
2015-01-29 18:08:18 +01:00
|
|
|
# Additionally, resources should not be included if the user is not allowed
|
|
|
|
# to show the resource
|
|
|
|
context = {
|
|
|
|
'model': model,
|
|
|
|
'session': model.Session,
|
|
|
|
'user': tk.c.user,
|
|
|
|
'user_obj': tk.c.userobj
|
|
|
|
}
|
|
|
|
|
|
|
|
try:
|
|
|
|
tk.check_access('package_show', context, result)
|
|
|
|
except tk.NotAuthorized:
|
|
|
|
# NotAuthorized exception is risen when the user is not allowed
|
|
|
|
# to read the package.
|
|
|
|
attrs.append('resources')
|
|
|
|
|
2015-06-18 13:05:37 +02:00
|
|
|
# Delete
|
2015-01-29 18:08:18 +01:00
|
|
|
self._delete_pkg_atts(result, attrs)
|
|
|
|
|
|
|
|
return search_results
|
|
|
|
|
2017-11-15 19:09:36 +01:00
|
|
|
####
|
|
|
|
|
|
|
|
def get_dataset_labels(self, dataset_obj):
|
|
|
|
labels = super(PrivateDatasets, self).get_dataset_labels(
|
|
|
|
dataset_obj)
|
|
|
|
|
|
|
|
if getattr(dataset_obj, 'searchable', False):
|
|
|
|
labels.append('searchable')
|
|
|
|
|
|
|
|
return labels
|
|
|
|
|
|
|
|
def get_user_dataset_labels(self, user_obj):
|
|
|
|
labels = super(PrivateDatasets, self).get_user_dataset_labels(
|
|
|
|
user_obj)
|
|
|
|
|
|
|
|
labels.append('searchable')
|
|
|
|
return labels
|
|
|
|
|
2014-07-04 14:18:10 +02:00
|
|
|
######################################################################
|
2014-07-14 17:05:46 +02:00
|
|
|
######################### ITEMPLATESHELPER ###########################
|
2014-07-04 14:18:10 +02:00
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def get_helpers(self):
|
2014-08-28 15:19:34 +02:00
|
|
|
return {'is_dataset_acquired': helpers.is_dataset_acquired,
|
2014-07-10 12:26:31 +02:00
|
|
|
'get_allowed_users_str': helpers.get_allowed_users_str,
|
2014-07-24 15:38:44 +02:00
|
|
|
'is_owner': helpers.is_owner,
|
2014-10-09 14:44:47 +02:00
|
|
|
'can_read': helpers.can_read,
|
|
|
|
'show_acquire_url_on_create': helpers.show_acquire_url_on_create,
|
2014-12-12 13:00:57 +01:00
|
|
|
'show_acquire_url_on_edit': helpers.show_acquire_url_on_edit,
|
2015-06-18 13:05:37 +02:00
|
|
|
'acquire_button': helpers.acquire_button
|
2014-10-09 14:44:47 +02:00
|
|
|
}
|