Skip to content

Commit

Permalink
Merge pull request #1786 from nickstenning/saner-set-permissions
Browse files Browse the repository at this point in the history
datastore: Rationalise set-permissions command
  • Loading branch information
joetsoi committed Jun 26, 2014
2 parents ffdd089 + 16418ec commit 6b40b6d
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 241 deletions.
2 changes: 1 addition & 1 deletion bin/travis-install-dependencies
Expand Up @@ -45,7 +45,7 @@ paster db init -c test-core.ini
if [ $PGVERSION != '8.4' ]
then
sed -i -e 's/.*datastore.read_url.*/ckan.datastore.read_url = postgresql:\/\/datastore_default:pass@\/datastore_test/' test-core.ini
paster datastore set-permissions postgres -c test-core.ini
paster datastore -c test-core.ini set-permissions | sudo -u postgres psql
else
sed -i -e 's/.*datastore.read_url.*//' test-core.ini
fi
Expand Down
Empty file removed ckanext/datastore/bin/__init__.py
Empty file.
84 changes: 0 additions & 84 deletions ckanext/datastore/bin/datastore_setup.py

This file was deleted.

54 changes: 0 additions & 54 deletions ckanext/datastore/bin/set_permissions.sql

This file was deleted.

117 changes: 58 additions & 59 deletions ckanext/datastore/commands.py
@@ -1,74 +1,73 @@
import logging
from __future__ import print_function
import argparse
import os
import sys

import ckan.lib.cli as cli
import bin.datastore_setup as setup

log = logging.getLogger(__name__)

def _abort(message):
print(message, file=sys.stderr)
sys.exit(1)

class SetupDatastoreCommand(cli.CkanCommand):
'''Perform commands to set up the datastore.
Make sure that the datastore URLs are set properly before you run
these commands.

Usage::
def _set_permissions(args):
write_url = cli.parse_db_config('ckan.datastore.write_url')
read_url = cli.parse_db_config('ckan.datastore.read_url')
db_url = cli.parse_db_config('sqlalchemy.url')

# Basic validation that read and write URLs reference the same database.
# This obviously doesn't check they're the same database (the hosts/ports
# could be different), but it's better than nothing, I guess.
if write_url['db_name'] != read_url['db_name']:
_abort("The datastore write_url and read_url must refer to the same "
"database!")

context = {
'maindb': db_url['db_name'],
'datastoredb': write_url['db_name'],
'mainuser': db_url['db_user'],
'writeuser': write_url['db_user'],
'readuser': read_url['db_user'],
}

sql = _permissions_sql(context)

paster datastore set-permissions SQL_SUPER_USER
print(sql)

Where:
SQL_SUPER_USER is the name of a postgres user with sufficient
permissions to create new tables, users, and grant
and revoke new permissions. Typically, this would
be the "postgres" user.

'''
summary = __doc__.split('\n')[0]
usage = __doc__
def _permissions_sql(context):
template_filename = os.path.join(os.path.dirname(__file__),
'set_permissions.sql')
with open(template_filename) as fp:
template = fp.read()
return template.format(**context)

def __init__(self, name):

super(SetupDatastoreCommand, self).__init__(name)
parser = argparse.ArgumentParser(
prog='paster datastore',
description='Perform commands to set up the datastore',
epilog='Make sure that the datastore URLs are set properly before you run '
'these commands!')
subparsers = parser.add_subparsers(title='commands')

parser_set_perms = subparsers.add_parser(
'set-permissions',
description='Set the permissions on the datastore.',
help='This command will help ensure that the permissions for the '
'datastore users as configured in your configuration file are '
'correct at the database. It will emit an SQL script that '
'you can use to set these permissions.',
epilog='"The ships hung in the sky in much the same way that bricks '
'don\'t."')
parser_set_perms.set_defaults(func=_set_permissions)


class SetupDatastoreCommand(cli.CkanCommand):
summary = parser.description

def command(self):
'''
Parse command line arguments and call appropriate method.
'''
if not self.args or self.args[0] in ['--help', '-h', 'help']:
print SetupDatastoreCommand.__doc__
return

cmd = self.args[0]
self._load_config()

self.db_write_url_parts = cli.parse_db_config(
'ckan.datastore.write_url')
self.db_read_url_parts = cli.parse_db_config(
'ckan.datastore.read_url')
self.db_ckan_url_parts = cli.parse_db_config(
'sqlalchemy.url')

write_db = self.db_write_url_parts['db_name']
read_db = self.db_read_url_parts['db_name']
assert write_db == read_db,\
"write and read db have to be the same"

if len(self.args) != 2:
print self.usage
return

if cmd == 'set-permissions':
setup.set_permissions(
pguser=self.args[1],
pgport=self.db_ckan_url_parts['db_port'],
ckandb=self.db_ckan_url_parts['db_name'],
datastoredb=self.db_write_url_parts['db_name'],
ckanuser=self.db_ckan_url_parts['db_user'],
writeuser=self.db_write_url_parts['db_user'],
readonlyuser=self.db_read_url_parts['db_user']
)
if self.verbose:
print 'Set permissions for read-only user: SUCCESS'
else:
print self.usage
log.error('Command "%s" not recognized' % (cmd,))
return
args = parser.parse_args(self.args)
args.func(args)
50 changes: 50 additions & 0 deletions ckanext/datastore/set_permissions.sql
@@ -0,0 +1,50 @@
/*
This script configures the permissions for the datastore.
It ensures that the datastore read-only user will only be able to select from
the datastore database but has no create/write/edit permission or any
permissions on other databases. You must execute this script as a database
superuser on the PostgreSQL server that hosts your datastore database.
For example, if PostgreSQL is running locally and the "postgres" user has the
appropriate permissions (as in the default Ubuntu PostgreSQL install), you can
run:
paster datastore set-permissions | sudo -u postgres psql
Or, if your PostgreSQL server is remote, you can pipe the permissions script
over SSH:
paster datastore set-permissions | ssh dbserver sudo -u postgres psql
*/

-- Most of the following commands apply to an explicit database or to the whole
-- 'public' schema, and could be executed anywhere. But ALTER DEFAULT
-- PERMISSIONS applies to the current database, and so we must be connected to
-- the datastore DB:
\connect {datastoredb}

-- revoke permissions for the read-only user
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
REVOKE USAGE ON SCHEMA public FROM PUBLIC;

GRANT CREATE ON SCHEMA public TO "{mainuser}";
GRANT USAGE ON SCHEMA public TO "{mainuser}";

GRANT CREATE ON SCHEMA public TO "{writeuser}";
GRANT USAGE ON SCHEMA public TO "{writeuser}";

-- take connect permissions from main db
REVOKE CONNECT ON DATABASE "{maindb}" FROM "{readuser}";

-- grant select permissions for read-only user
GRANT CONNECT ON DATABASE "{datastoredb}" TO "{readuser}";
GRANT USAGE ON SCHEMA public TO "{readuser}";

-- grant access to current tables and views to read-only user
GRANT SELECT ON ALL TABLES IN SCHEMA public TO "{readuser}";

-- grant access to new tables and views by default
ALTER DEFAULT PRIVILEGES FOR USER "{writeuser}" IN SCHEMA public
GRANT SELECT ON TABLES TO "{readuser}";

0 comments on commit 6b40b6d

Please sign in to comment.