@@ -133,6 +133,84 @@ impl TryFrom<ComputeSpec> for ParsedSpec {
133133 }
134134}
135135
136+ /// Create special neon_superuser role, that's a slightly nerfed version of a real superuser
137+ /// that we give to customers
138+ fn create_neon_superuser ( spec : & ComputeSpec , client : & mut Client ) -> Result < ( ) > {
139+ let roles = spec
140+ . cluster
141+ . roles
142+ . iter ( )
143+ . map ( |r| format ! ( "'{}'" , escape_literal( & r. name) ) )
144+ . collect :: < Vec < _ > > ( ) ;
145+
146+ let dbs = spec
147+ . cluster
148+ . databases
149+ . iter ( )
150+ . map ( |db| format ! ( "'{}'" , escape_literal( & db. name) ) )
151+ . collect :: < Vec < _ > > ( ) ;
152+
153+ let roles_decl = if roles. is_empty ( ) {
154+ String :: from ( "roles text[] := NULL;" )
155+ } else {
156+ format ! (
157+ r#"
158+ roles text[] := ARRAY(SELECT rolname
159+ FROM pg_catalog.pg_roles
160+ WHERE rolname IN ({}));"# ,
161+ roles. join( ", " )
162+ )
163+ } ;
164+
165+ let database_decl = if dbs. is_empty ( ) {
166+ String :: from ( "dbs text[] := NULL;" )
167+ } else {
168+ format ! (
169+ r#"
170+ dbs text[] := ARRAY(SELECT datname
171+ FROM pg_catalog.pg_database
172+ WHERE datname IN ({}));"# ,
173+ dbs. join( ", " )
174+ )
175+ } ;
176+
177+ // ALL PRIVILEGES grants CREATE, CONNECT, and TEMPORARY on all databases
178+ // (see https://www.postgresql.org/docs/current/ddl-priv.html)
179+ let query = format ! (
180+ r#"
181+ DO $$
182+ DECLARE
183+ r text;
184+ {}
185+ {}
186+ BEGIN
187+ IF NOT EXISTS (
188+ SELECT FROM pg_catalog.pg_roles WHERE rolname = 'neon_superuser')
189+ THEN
190+ CREATE ROLE neon_superuser CREATEDB CREATEROLE NOLOGIN IN ROLE pg_read_all_data, pg_write_all_data;
191+ IF array_length(roles, 1) IS NOT NULL THEN
192+ EXECUTE format('GRANT neon_superuser TO %s',
193+ array_to_string(ARRAY(SELECT quote_ident(x) FROM unnest(roles) as x), ', '));
194+ FOREACH r IN ARRAY roles LOOP
195+ EXECUTE format('ALTER ROLE %s CREATEROLE CREATEDB', quote_ident(r));
196+ END LOOP;
197+ END IF;
198+ IF array_length(dbs, 1) IS NOT NULL THEN
199+ EXECUTE format('GRANT ALL PRIVILEGES ON DATABASE %s TO neon_superuser',
200+ array_to_string(ARRAY(SELECT quote_ident(x) FROM unnest(dbs) as x), ', '));
201+ END IF;
202+ END IF;
203+ END
204+ $$;"# ,
205+ roles_decl, database_decl,
206+ ) ;
207+ info ! ( "Neon superuser created:\n {}" , & query) ;
208+ client
209+ . simple_query ( & query)
210+ . map_err ( |e| anyhow:: anyhow!( e) . context ( query) ) ?;
211+ Ok ( ( ) )
212+ }
213+
136214impl ComputeNode {
137215 pub fn set_status ( & self , status : ComputeStatus ) {
138216 let mut state = self . state . lock ( ) . unwrap ( ) ;
@@ -347,6 +425,8 @@ impl ComputeNode {
347425 . map_err ( |_| anyhow:: anyhow!( "invalid connstr" ) ) ?;
348426
349427 let mut client = Client :: connect ( zenith_admin_connstr. as_str ( ) , NoTls ) ?;
428+ // Disable forwarding so that users don't get a cloud_admin role
429+ client. simple_query ( "SET neon.forward_ddl = false" ) ?;
350430 client. simple_query ( "CREATE USER cloud_admin WITH SUPERUSER" ) ?;
351431 client. simple_query ( "GRANT zenith_admin TO cloud_admin" ) ?;
352432 drop ( client) ;
@@ -357,14 +437,16 @@ impl ComputeNode {
357437 Ok ( client) => client,
358438 } ;
359439
360- // Proceed with post-startup configuration. Note, that order of operations is important.
361440 // Disable DDL forwarding because control plane already knows about these roles/databases.
362441 client. simple_query ( "SET neon.forward_ddl = false" ) ?;
442+
443+ // Proceed with post-startup configuration. Note, that order of operations is important.
363444 let spec = & compute_state. pspec . as_ref ( ) . expect ( "spec must be set" ) . spec ;
445+ create_neon_superuser ( spec, & mut client) ?;
364446 handle_roles ( spec, & mut client) ?;
365447 handle_databases ( spec, & mut client) ?;
366448 handle_role_deletions ( spec, self . connstr . as_str ( ) , & mut client) ?;
367- handle_grants ( spec, self . connstr . as_str ( ) , & mut client ) ?;
449+ handle_grants ( spec, self . connstr . as_str ( ) ) ?;
368450 handle_extensions ( spec, & mut client) ?;
369451
370452 // 'Close' connection
@@ -402,7 +484,7 @@ impl ComputeNode {
402484 handle_roles ( & spec, & mut client) ?;
403485 handle_databases ( & spec, & mut client) ?;
404486 handle_role_deletions ( & spec, self . connstr . as_str ( ) , & mut client) ?;
405- handle_grants ( & spec, self . connstr . as_str ( ) , & mut client ) ?;
487+ handle_grants ( & spec, self . connstr . as_str ( ) ) ?;
406488 handle_extensions ( & spec, & mut client) ?;
407489 }
408490
0 commit comments