Skip to content

Commit 008cb9f

Browse files
[DPE-7785] Backport roles fixes from PgBouncer (#1048)
* Backport roles fixes from PgBouncer Signed-off-by: Marcelo Henrique Neppel <[email protected]> * Grant connect to charmed_backup role to new databases Signed-off-by: Marcelo Henrique Neppel <[email protected]> * Remove unused parameter Signed-off-by: Marcelo Henrique Neppel <[email protected]> * Allow event trigger function update Signed-off-by: Marcelo Henrique Neppel <[email protected]> * Avoid trying to create the event triggers when they already exists Signed-off-by: Marcelo Henrique Neppel <[email protected]> --------- Signed-off-by: Marcelo Henrique Neppel <[email protected]>
1 parent c349890 commit 008cb9f

File tree

2 files changed

+22
-16
lines changed

2 files changed

+22
-16
lines changed

lib/charms/postgresql_k8s/v1/postgresql.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ def create_database(
275275
)
276276
)
277277
with self._connect_to_database(database=database) as conn, conn.cursor() as curs:
278+
curs.execute(SQL("SET ROLE {};").format(Identifier(ROLE_DATABASES_OWNER)))
278279
curs.execute(SQL("SELECT set_up_predefined_catalog_roles();"))
279280
except psycopg2.Error as e:
280281
logger.error(f"Failed to create database: {e}")
@@ -347,13 +348,13 @@ def create_user(
347348
)
348349
connect_statements = []
349350
if database:
350-
if not any(True for role in roles if role in [ROLE_STATS, ROLE_READ, ROLE_DML, ROLE_BACKUP, ROLE_DBA]):
351+
if roles is not None and not any(True for role in roles if role in [ROLE_STATS, ROLE_READ, ROLE_DML, ROLE_BACKUP, ROLE_DBA]):
351352
user_definition += f' IN ROLE "charmed_{database}_admin", "charmed_{database}_dml"'
352353
else:
353354
connect_statements.append(SQL("GRANT CONNECT ON DATABASE {} TO {};").format(
354355
Identifier(database), Identifier(user)
355356
))
356-
if any(True for role in roles if role in [ROLE_STATS, ROLE_READ, ROLE_DML, ROLE_BACKUP, ROLE_DBA, ROLE_ADMIN, ROLE_DATABASES_OWNER]):
357+
if roles is not None and any(True for role in roles if role in [ROLE_STATS, ROLE_READ, ROLE_DML, ROLE_BACKUP, ROLE_DBA, ROLE_ADMIN, ROLE_DATABASES_OWNER]):
357358
for system_database in ["postgres", "template1"]:
358359
connect_statements.append(SQL("GRANT CONNECT ON DATABASE {} TO {};").format(
359360
Identifier(system_database), Identifier(user)
@@ -898,16 +899,13 @@ def set_up_database(self, temp_location: Optional[str] = None) -> None:
898899
self.set_up_predefined_catalog_roles_function()
899900

900901
# Create database function and event trigger to identify users created by PgBouncer.
901-
cursor.execute(
902-
"SELECT TRUE FROM pg_event_trigger WHERE evtname = 'update_pg_hba_on_create_schema';"
903-
)
904-
if cursor.fetchone() is None:
905-
cursor.execute("""
902+
cursor.execute("""
906903
CREATE OR REPLACE FUNCTION update_pg_hba()
907904
RETURNS event_trigger
908905
LANGUAGE plpgsql
909906
AS $$
910907
DECLARE
908+
temp_schema TEXT;
911909
hba_file TEXT;
912910
copy_command TEXT;
913911
connection_type TEXT;
@@ -918,20 +916,23 @@ def set_up_database(self, temp_location: Optional[str] = None) -> None:
918916
-- Don't execute on replicas.
919917
IF NOT pg_is_in_recovery() THEN
920918
-- Load the current authorisation rules.
921-
PERFORM TRUE FROM pg_tables WHERE schemaname LIKE 'pg_temp_%' AND tablename = 'pg_hba';
922-
IF FOUND THEN
923-
DROP TABLE pg_hba;
919+
SELECT nspname INTO temp_schema FROM pg_namespace WHERE oid = pg_my_temp_schema();
920+
IF temp_schema != '' THEN
921+
PERFORM TRUE FROM pg_tables WHERE schemaname = temp_schema AND tablename = 'pg_hba';
922+
IF FOUND THEN
923+
DROP TABLE pg_hba;
924+
END IF;
925+
PERFORM TRUE FROM pg_tables WHERE schemaname = temp_schema AND tablename = 'relation_users';
926+
IF FOUND THEN
927+
DROP TABLE relation_users;
928+
END IF;
924929
END IF;
925930
CREATE TEMPORARY TABLE pg_hba (lines TEXT);
926931
SELECT setting INTO hba_file FROM pg_settings WHERE name = 'hba_file';
927932
IF hba_file IS NOT NULL THEN
928933
copy_command='COPY pg_hba FROM ''' || hba_file || '''' ;
929934
EXECUTE copy_command;
930935
-- Build a list of the relation users and the databases they can access.
931-
PERFORM TRUE FROM pg_tables WHERE schemaname LIKE 'pg_temp_%' AND tablename = 'relation_users';
932-
IF FOUND THEN
933-
DROP TABLE relation_users;
934-
END IF;
935936
CREATE TEMPORARY TABLE relation_users AS
936937
SELECT t.user, STRING_AGG(DISTINCT t.database, ',') AS databases FROM( SELECT u.usename AS user, CASE WHEN u.usesuper THEN 'all' ELSE d.datname END AS database FROM ( SELECT usename, usesuper FROM pg_catalog.pg_user WHERE usename NOT IN ('backup', 'monitoring', 'operator', 'postgres', 'replication', 'rewind')) AS u JOIN ( SELECT datname FROM pg_catalog.pg_database WHERE NOT datistemplate ) AS d ON has_database_privilege(u.usename, d.datname, 'CONNECT') ) AS t GROUP BY 1;
937938
IF (SELECT COUNT(lines) FROM pg_hba WHERE lines LIKE 'hostssl %') > 0 THEN
@@ -964,7 +965,11 @@ def set_up_database(self, temp_location: Optional[str] = None) -> None:
964965
END IF;
965966
END;
966967
$$ SECURITY DEFINER;
967-
""")
968+
""")
969+
cursor.execute(
970+
"SELECT TRUE FROM pg_event_trigger WHERE evtname = 'update_pg_hba_on_create_schema';"
971+
)
972+
if cursor.fetchone() is None:
968973
cursor.execute("""
969974
CREATE EVENT TRIGGER update_pg_hba_on_create_schema
970975
ON ddl_command_end
@@ -1102,6 +1107,7 @@ def set_up_predefined_catalog_roles_function(self) -> None:
11021107
'GRANT CONNECT ON DATABASE ' || database || ' TO {ROLE_STATS};',
11031108
'GRANT CONNECT ON DATABASE ' || database || ' TO {ROLE_READ};',
11041109
'GRANT CONNECT ON DATABASE ' || database || ' TO {ROLE_DML};',
1110+
'GRANT CONNECT ON DATABASE ' || database || ' TO {ROLE_BACKUP};',
11051111
'GRANT CONNECT ON DATABASE ' || database || ' TO {ROLE_DBA};',
11061112
'GRANT CONNECT ON DATABASE ' || database || ' TO {ROLE_ADMIN};',
11071113
'GRANT ' || admin_user || ' TO {ROLE_ADMIN} WITH INHERIT FALSE;',

tests/integration/test_invalid_database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def test_deploy(juju: jubilant.Juju, charm) -> None:
6262
juju.wait(lambda status: data_integrator_blocked(status), timeout=TIMEOUT)
6363

6464

65-
def test_database(juju: jubilant.Juju, predefined_roles, predefined_roles_combinations) -> None:
65+
def test_database(juju: jubilant.Juju, predefined_roles) -> None:
6666
"""Check that an invalid database name makes the database charm block."""
6767
del predefined_roles[""]
6868
invalid_database_names = [

0 commit comments

Comments
 (0)