-
Notifications
You must be signed in to change notification settings - Fork 2k
/
commands.py
166 lines (134 loc) · 5.84 KB
/
commands.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import re
import sys
import logging
import ckan.lib.cli as cli
log = logging.getLogger(__name__)
read_only_user_sql = '''
-- revoke permissions for the new user
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
REVOKE USAGE ON SCHEMA public FROM PUBLIC;
GRANT CREATE ON SCHEMA public TO "{ckanuser}";
GRANT USAGE ON SCHEMA public TO "{ckanuser}";
GRANT CREATE ON SCHEMA public TO "{writeuser}";
GRANT USAGE ON SCHEMA public TO "{writeuser}";
-- create new read only user
CREATE USER "{readonlyuser}" {with_password} NOSUPERUSER NOCREATEDB NOCREATEROLE LOGIN;
-- take connect permissions from main db
REVOKE CONNECT ON DATABASE "{maindb}" FROM "{readonlyuser}";
-- grant select permissions for read-only user
GRANT CONNECT ON DATABASE "{datastore}" TO "{readonlyuser}";
GRANT USAGE ON SCHEMA public TO "{readonlyuser}";
-- grant access to current tables and views
GRANT SELECT ON ALL TABLES IN SCHEMA public TO "{readonlyuser}";
-- grant access to new tables and views by default
ALTER DEFAULT PRIVILEGES FOR USER "{writeuser}" IN SCHEMA public
GRANT SELECT ON TABLES TO "{readonlyuser}";
'''
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.
`create-all` will run create-write-user, create-db and create-read-only-user.
Usage::
paster datastore create-write-user SQL_SUPER_USER
paster datastore create-db SQL_SUPER_USER
paster datastore create-read-only-user SQL_SUPER_USER
paster datastore create-all SQL_SUPER_USER
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 __init__(self, name):
super(SetupDatastoreCommand, self).__init__(name)
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')
assert self.db_write_url_parts['db_name'] == self.db_read_url_parts['db_name'], "write and read db should be the same"
if len(self.args) != 2:
print self.usage
return
self.sql_superuser = self.args[1]
if cmd == 'create-db':
self.create_db()
if self.verbose:
print 'Creating DB: SUCCESS'
elif cmd == 'create-read-only-user':
self.create_read_only_user()
if self.verbose:
print 'Creating read-only user: SUCCESS'
elif cmd == 'create-write-user':
self.create_write_user()
if self.verbose:
print 'Creating write user: SUCCESS'
elif cmd == 'create-all':
self.create_db()
self.create_write_user()
self.create_read_only_user()
if self.verbose:
print 'Creating db and users for datastore: SUCCESS'
else:
print self.usage
log.error('Command "%s" not recognized' % (cmd,))
return
def _run_cmd(self, command_line, inputstring=''):
if self.verbose:
print "Running:", command_line
import subprocess
if not self.simulate:
p = subprocess.Popen(
command_line, shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout_value, stderr_value = p.communicate(input=inputstring)
if stderr_value:
print '\nAn error occured: {0}'.format(stderr_value)
sys.exit(1)
def _run_sql(self, sql, as_sql_user, database='postgres'):
if self.verbose:
print "Executing: \n#####\n", sql, "\n####\nOn database:", database
if not self.simulate:
self._run_cmd("sudo -u '{username}' psql --dbname='{database}' -W".format(
username=as_sql_user,
database=database
), inputstring=sql)
def create_db(self):
cmd = "sudo -u {pguser} createdb -O '{ckanuser}' '{db}'".format(
pguser=self.sql_superuser,
db=self.db_write_url_parts['db_name'],
ckanuser=self.db_ckan_url_parts['db_user'])
self._run_cmd(cmd)
def create_write_user(self):
cmd = "sudo -u {pguser} createuser \
--no-createdb --no-createrole --no-superuser '{writeuser}'".format(
pguser=self.sql_superuser,
writeuser=self.db_write_url_parts['db_user'])
self._run_cmd(cmd)
def create_read_only_user(self):
password = self.db_read_url_parts['db_pass']
self.validate_password(password)
sql = read_only_user_sql.format(
maindb=self.db_ckan_url_parts['db_name'],
datastore=self.db_write_url_parts['db_name'],
ckanuser=self.db_ckan_url_parts['db_user'],
readonlyuser=self.db_read_url_parts['db_user'],
with_password="WITH PASSWORD '{0}'".format(password) if password else "",
writeuser=self.db_write_url_parts['db_user'])
self._run_sql(sql,
as_sql_user=self.sql_superuser,
database=self.db_write_url_parts['db_name'])
def validate_password(self, password):
if "'" in password:
raise ValueError("Passwords cannot contain '")