Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1786 from nickstenning/saner-set-permissions
datastore: Rationalise set-permissions command
- Loading branch information
Showing
7 changed files
with
130 additions
and
241 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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}"; |
Oops, something went wrong.