@@ -84,6 +84,8 @@ func CreateUser(c *resources.AppConfig, user resources.EphemeralUser) error {
8484 query = superuserQuery (user .Name , user .Password )
8585 }
8686
87+ log .Msg (query )
88+
8789 out , err := runSimpleSQL (query , getPgConnStr (c .Host , dbName , c .DB .Username , c .Port ))
8890 if err != nil {
8991 return errors .Wrap (err , "failed to run psql" )
@@ -98,50 +100,125 @@ func superuserQuery(username, password string) string {
98100 return fmt .Sprintf (`create user "%s" with password '%s' login superuser;` , username , password )
99101}
100102
101- const restrictedTemplate = `
102- -- create new user
103- create user %[1]s with password '%s' createdb ;
103+ const restrictionTemplate = `
104+ -- create a new user
105+ create user %[1]s with password '%s' login ;
104106
105- -- grant all privileges in the database
106- grant all privileges on database %s to %[1]s;
107+ -- change a database owner
108+ alter database %s owner to %[1]s;
107109
108- -- grant all on all objects in all schemas in the database
109110do $$
111+ declare
112+ new_owner text;
113+ object_type record;
114+ r record;
110115begin
111- -- grant usage on all schemas in the database
112- execute (
113- select string_agg(format('grant usage on schema %%I to %[1]s', nspname), '; ')
114- from pg_namespace
115- where nspname <> 'information_schema'
116- and nspname not like 'pg\_%%'
117- );
118-
119- -- grant all on all tables in all schemas in database
120- execute (
121- select string_agg(format('grant all on all tables in schema %%I to %[1]s', nspname), '; ')
122- from pg_namespace
123- where nspname <> 'information_schema'
124- and nspname not like 'pg\_%%'
125- );
126-
127- -- grant all on all sequences in all custom schemas in the database
128- execute (
129- select string_agg(format('grant all on all sequences in schema %%I to %[1]s', nspname), '; ')
130- from pg_namespace
131- where nspname <> 'information_schema'
132- and nspname not like 'pg\_%%'
133- );
134-
135- -- grant all on all functions in all schemas in the database
136- execute (
137- select string_agg(format('grant all on all functions in schema %%I to %[1]s', nspname), '; ')
138- from pg_namespace
139- where nspname <> 'information_schema'
140- and nspname not like 'pg\_%%'
141- );
142- end $$;
116+ new_owner := '%[1]s';
117+
118+ -- c: composite type
119+ -- t: type (TOAST)
120+ -- S: sequence
121+ -- i: index
122+ -- r: table
123+ -- v: view
124+ -- m: materialized view
125+ for object_type in
126+ select
127+ unnest('{type,table,sequence,table,view,materialized view}'::text[]) type_name,
128+ unnest('{c,t,S,r,v,m}'::text[]) code
129+ loop
130+ for r in
131+ execute format(
132+ $sql$
133+ select n.nspname, c.relname
134+ from pg_class c
135+ join pg_namespace n on
136+ n.oid = c.relnamespace
137+ and not n.nspname in ('pg_catalog', 'information_schema')
138+ and c.relkind = %%L
139+ left join pg_class cc on
140+ c.relkind = 't' /*leave 't' hardcoded!*/
141+ and cc.oid = nullif(regexp_replace(c.relname, '^pg_toast_(.*)', '\1'), c.relname)::int8
142+ left join pg_namespace nn on
143+ nn.oid = cc.relnamespace
144+ where
145+ c.relkind <> 't' /*leave 't' hardcoded!*/
146+ or not nn.nspname in ('pg_catalog', 'information_schema')
147+ $sql$,
148+ object_type.code
149+ )
150+ loop
151+ raise debug 'Changing ownership of %% %%.%% to %%',
152+ object_type.type_name, r.nspname, r.relname, new_owner;
153+ execute format(
154+ 'alter %%s %%I.%%I owner to %%I;',
155+ object_type.type_name,
156+ r.nspname,
157+ r.relname,
158+ new_owner
159+ );
160+ end loop;
161+ end loop;
162+
163+ -- Functions,
164+ for r in
165+ select
166+ p.proname,
167+ n.nspname,
168+ pg_catalog.pg_get_function_identity_arguments(p.oid) as args
169+ from pg_catalog.pg_namespace as n
170+ join pg_catalog.pg_proc as p on p.pronamespace = n.oid
171+ where not n.nspname in ('pg_catalog', 'information_schema')
172+ loop
173+ raise debug 'Changing ownership of function %%.%%(%%) to %%',
174+ r.nspname, r.proname, r.args, new_owner;
175+ execute format(
176+ 'alter function %%I.%%I(%%s) owner to %%I', -- todo: check support CamelStyle r.args
177+ r.nspname,
178+ r.proname,
179+ r.args,
180+ new_owner
181+ );
182+ end loop;
183+
184+ -- full text search dictionary
185+ -- TODO: text search configuration
186+ for r in
187+ select *
188+ from pg_catalog.pg_namespace n
189+ join pg_catalog.pg_ts_dict d on d.dictnamespace = n.oid
190+ where not n.nspname in ('pg_catalog', 'information_schema')
191+ loop
192+ raise debug 'Changing ownership of text search dictionary %%.%% to %%',
193+ r.nspname, r.dictname, new_owner;
194+ execute format(
195+ 'alter text search dictionary %%I.%%I owner to %%I',
196+ r.nspname,
197+ r.dictname,
198+ new_owner
199+ );
200+ end loop;
201+
202+ -- domain
203+ for r in
204+ select typname, nspname
205+ from pg_catalog.pg_type
206+ join pg_catalog.pg_namespace on pg_namespace.oid = pg_type.typnamespace
207+ where typtype = 'd' and not nspname in ('pg_catalog', 'information_schema')
208+ loop
209+ raise debug 'Changing ownership of domain %%.%% to %%',
210+ r.nspname, r.typname, new_owner;
211+ execute format(
212+ 'alter domain %%I.%%I owner to %%I',
213+ r.nspname,
214+ r.typname,
215+ new_owner
216+ );
217+ end loop;
218+ end
219+ $$;
143220`
144221
145222func restrictedUserQuery (username , password , database string ) string {
146- return fmt .Sprintf (restrictedTemplate , username , password , database )
223+ return fmt .Sprintf (restrictionTemplate , username , password , database )
147224}
0 commit comments