Skip to content

Commit

Permalink
[#652][wip] Add actions to make datastore resources private or public
Browse files Browse the repository at this point in the history
  • Loading branch information
domoritz committed Apr 22, 2013
1 parent e8287b3 commit 04cb3f2
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 2 deletions.
37 changes: 37 additions & 0 deletions ckanext/datastore/db.py
Expand Up @@ -13,6 +13,7 @@
import sqlalchemy
from sqlalchemy.exc import ProgrammingError, IntegrityError, DBAPIError
import psycopg2.extras
import ckan.lib.cli as cli

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -1129,3 +1130,39 @@ def search_sql(context, data_dict):
raise
finally:
context['connection'].close()


def _get_read_only_user(data_dict):
parsed = cli.parse_db_config('ckan.datastore.read_url')
return parsed['db_user']


def _change_privilege(context, data_dict, what):
engine = _get_engine(context, data_dict)
context['connection'] = engine.connect()
read_only_user = _get_read_only_user(data_dict)
assert(what in ['REVOKE', 'GRANT'])
if what == 'REVOKE':
sql = u'REVOKE SELECT ON TABLE "{0}" FROM "{1}"'.format(
data_dict['resource_id'],
read_only_user)
elif what == 'GRANT':
sql = u'GRANT SELECT ON TABLE "{0}" TO "{1}"'.format(
data_dict['resource_id'],
read_only_user)
try:
context['connection'].execute(sql)
except ProgrammingError, e:
log.critical("Error making resource private. {0}".format(e.message))
raise ValidationError({
'privileges': [u'cannot make "{0}" private'.format(
data_dict['resource_id'])],
})


def make_private(context, data_dict):
_change_privilege(context, data_dict, 'REVOKE')


def make_public(context, data_dict):
_change_privilege(context, data_dict, 'GRANT')
34 changes: 34 additions & 0 deletions ckanext/datastore/logic/action.py
Expand Up @@ -287,3 +287,37 @@ def datastore_search_sql(context, data_dict):
result.pop('id', None)
result.pop('connection_url')
return result


def datastore_make_private(context, data_dict):
model = _get_or_bust(context, 'model')
if 'id' in data_dict:
data_dict['resource_id'] = data_dict['id']
res_id = _get_or_bust(data_dict, 'resource_id')

if not model.Resource.get(res_id):
raise p.toolkit.ObjectNotFound(p.toolkit._(
'Resource "{0}" was not found.'.format(res_id)
))

p.toolkit.check_access('datastore_change_permissions', context, data_dict)

data_dict['connection_url'] = pylons.config.get('ckan.datastore.write_url')
db.make_private(context, data_dict)


def datastore_make_public(context, data_dict):
model = _get_or_bust(context, 'model')
if 'id' in data_dict:
data_dict['resource_id'] = data_dict['id']
res_id = _get_or_bust(data_dict, 'resource_id')

if not model.Resource.get(res_id):
raise p.toolkit.ObjectNotFound(p.toolkit._(
'Resource "{0}" was not found.'.format(res_id)
))

p.toolkit.check_access('datastore_change_permissions', context, data_dict)

data_dict['connection_url'] = pylons.config.get('ckan.datastore.write_url')
db.make_public(context, data_dict)
4 changes: 4 additions & 0 deletions ckanext/datastore/logic/auth.py
Expand Up @@ -32,3 +32,7 @@ def datastore_delete(context, data_dict):

def datastore_search(context, data_dict):
return _datastore_auth(context, data_dict, 'resource_show')


def datastore_change_permissions(context, data_dict):
return _datastore_auth(context, data_dict)
7 changes: 5 additions & 2 deletions ckanext/datastore/plugin.py
Expand Up @@ -201,7 +201,9 @@ def get_actions(self):
actions = {'datastore_create': action.datastore_create,
'datastore_upsert': action.datastore_upsert,
'datastore_delete': action.datastore_delete,
'datastore_search': action.datastore_search}
'datastore_search': action.datastore_search,
'datastore_make_private': action.datastore_make_private,
'datastore_make_public': action.datastore_make_public}
if not self.legacy_mode:
actions['datastore_search_sql'] = action.datastore_search_sql
return actions
Expand All @@ -210,4 +212,5 @@ def get_auth_functions(self):
return {'datastore_create': auth.datastore_create,
'datastore_upsert': auth.datastore_upsert,
'datastore_delete': auth.datastore_delete,
'datastore_search': auth.datastore_search}
'datastore_search': auth.datastore_search,
'datastore_change_permissions': auth.datastore_change_permissions}
27 changes: 27 additions & 0 deletions ckanext/datastore/tests/test_search.py
Expand Up @@ -561,3 +561,30 @@ def test_self_join(self):
assert res_dict['success'] is True
result = res_dict['result']
assert result['records'] == self.expected_join_results

def test_read_private(self):
from pylons import config
context = {
'user': self.sysadmin_user.name,
'model': model}
data_dict = {
'resource_id': self.data['resource_id'],
'connection_url': config['ckan.datastore.write_url']}
p.toolkit.get_action('datastore_make_private')(context, data_dict)
'''
data = {'resource_id': self.data['resource_id']}
postparams = json.dumps(data)
auth = {'Authorization': str(self.sysadmin_user.apikey)}
res = self.app.post('/api/action/datastore_make_private', params=postparams,
extra_environ=auth)
res_dict = json.loads(res.body)
assert res_dict['success'] is True
'''
query = 'SELECT * FROM "{0}"'.format(self.data['resource_id'])
data = {'sql': query}
postparams = json.dumps(data)
auth = {'Authorization': str(self.normal_user.apikey)}
res = self.app.post('/api/action/datastore_search_sql', params=postparams,
extra_environ=auth)
res_dict = json.loads(res.body)
assert res_dict['success'] is False

0 comments on commit 04cb3f2

Please sign in to comment.