Files
teleport/lib/srv/db/postgres/sql/remove-permissions.sql
T
Gavin Frazar 0bcabee6b4 Fix database user auto-provisioning (#51945)
* Use a custom query to find user db privileges on tables to avoid the
  "grantor" filter condition in the information_schema.tables_privileges
  view. This fixes the cases where the grantor for a privilege is set to
  the table owner rather than the user (teleport) who issued the grant.
  Most notably, this happens when a superuser grants privileges on a
  table they do not own to a user.
* Grant USAGE on schemas that contain tables where we intend to grant
  table privileges. This is necessary to use the table privileges we
  grant.
* Wrap all remaining plpgsql procedure creation/calls with retries.
* Add a db permissions e2e test for RDS
* Expand e2e tests to test with and without a superuser db admin
* Significantly speed up the RDS e2e tests by wrapping EventuallyWithT
  in a helper func that tries the condition func immediately rather than
  waiting for the first tick duration.
2025-02-12 02:51:33 +00:00

69 lines
2.7 KiB
PL/PgSQL

CREATE OR REPLACE PROCEDURE pg_temp.teleport_remove_permissions(username VARCHAR)
LANGUAGE plpgsql
AS $$
DECLARE
row_data RECORD;
BEGIN
-- Check active connections.
IF EXISTS (SELECT usename FROM pg_stat_activity WHERE usename = username AND datname = current_database()) THEN
RAISE NOTICE 'User has active connections to current database';
ELSE
-- create temp table with ON COMMIT DROP so that this procedure can be
-- retried within a single session without getting an error that the
-- table already exists.
CREATE TEMPORARY TABLE cur_perms ON COMMIT DROP AS (
SELECT DISTINCT
pg_namespace.nspname::information_schema.sql_identifier AS table_schema,
obj.relname::information_schema.sql_identifier AS table_name
FROM
pg_class as obj
INNER JOIN
pg_namespace ON obj.relnamespace = pg_namespace.oid
INNER JOIN LATERAL
aclexplode(COALESCE(obj.relacl, acldefault('r'::"char", obj.relowner))) AS acl ON true
INNER JOIN
pg_roles AS grantee ON acl.grantee = grantee.oid
WHERE
-- only objects that are one of r=ordinary table, v=view, f=foreign table, p=partitioned table.
(obj.relkind = ANY (ARRAY['r', 'v', 'f', 'p']))
-- only privileges we support provisioning.
AND (acl.privilege_type = ANY (ARRAY['DELETE'::text, 'INSERT'::text, 'REFERENCES'::text, 'SELECT'::text, 'TRUNCATE'::text, 'TRIGGER'::text, 'UPDATE'::text]))
-- only the user in we are revoking permissions from.
AND grantee.rolname = username
);
-- Loop through and revoke all table permissions
FOR row_data IN (
SELECT
table_schema,
table_name
FROM
cur_perms
)
LOOP
EXECUTE format('REVOKE ALL PRIVILEGES ON %I.%I FROM %I',
row_data.table_schema,
row_data.table_name,
username
);
END LOOP;
-- We implicitly grant USAGE on any schema for which we make table privilege grants.
-- Loop through and revoke all schema USAGE.
-- See: https://github.com/gravitational/teleport/issues/51851
FOR row_data IN (
SELECT DISTINCT
table_schema
FROM
cur_perms
)
LOOP
EXECUTE format('REVOKE USAGE ON SCHEMA %I FROM %I',
row_data.table_schema,
username
);
END LOOP;
END IF;
END;
$$;