mirror of
https://github.com/gravitational/teleport.git
synced 2026-11-02 23:31:08 +08:00
* 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.
99 lines
3.9 KiB
PL/PgSQL
99 lines
3.9 KiB
PL/PgSQL
CREATE OR REPLACE PROCEDURE pg_temp.teleport_update_permissions(username VARCHAR, permissions_ JSONB)
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
DECLARE
|
|
grant_data JSONB;
|
|
row_data RECORD;
|
|
diff_removed INTEGER;
|
|
diff_added INTEGER;
|
|
BEGIN
|
|
grant_data = COALESCE(NULLIF(permissions_->'tables', 'null'), '[]'::JSONB);
|
|
-- 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 new_perms ON COMMIT DROP AS (
|
|
SELECT
|
|
item->>'schema' as table_schema,
|
|
item->>'table' as table_name,
|
|
item->>'privilege' as privilege_type
|
|
FROM
|
|
jsonb_array_elements(grant_data) as item
|
|
);
|
|
|
|
-- If the user has active connections to current database, verify that permissions haven't changed.
|
|
IF EXISTS (SELECT usename FROM pg_stat_activity WHERE usename = username AND datname = current_database()) THEN
|
|
WITH
|
|
cur_perms AS (
|
|
SELECT DISTINCT
|
|
pg_namespace.nspname::information_schema.sql_identifier AS table_schema,
|
|
obj.relname::information_schema.sql_identifier AS table_name,
|
|
acl.privilege_type::information_schema.character_data AS privilege_type
|
|
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 we are checking permissions for.
|
|
AND grantee.rolname = username
|
|
),
|
|
removed AS (
|
|
SELECT * FROM cur_perms
|
|
EXCEPT
|
|
SELECT * FROM new_perms
|
|
),
|
|
added AS (
|
|
SELECT * FROM new_perms
|
|
EXCEPT
|
|
SELECT * FROM cur_perms
|
|
)
|
|
SELECT
|
|
(SELECT COUNT(*) FROM removed),
|
|
(SELECT COUNT(*) FROM added)
|
|
INTO
|
|
diff_removed,
|
|
diff_added;
|
|
|
|
IF (diff_removed > 0) OR (diff_added > 0) THEN
|
|
RAISE WARNING 'Permission changes: removed=%, added=%', diff_removed, diff_added;
|
|
RAISE EXCEPTION SQLSTATE 'TP005' USING MESSAGE = 'TP005: User has active connections and permissions have changed';
|
|
END IF;
|
|
ELSE
|
|
CALL pg_temp.teleport_remove_permissions(username);
|
|
|
|
-- Assign all privileges to the created/activated user if grants are provided.
|
|
FOR row_data IN SELECT * FROM new_perms
|
|
LOOP
|
|
EXECUTE format('GRANT %s ON %I.%I TO %I',
|
|
row_data.privilege_type,
|
|
row_data.table_schema,
|
|
row_data.table_name,
|
|
username
|
|
);
|
|
END LOOP;
|
|
|
|
-- Grant USAGE on any schemas for which we made table privilege grants
|
|
-- See: https://github.com/gravitational/teleport/issues/51851
|
|
FOR row_data IN (
|
|
SELECT DISTINCT
|
|
table_schema
|
|
FROM
|
|
new_perms
|
|
)
|
|
LOOP
|
|
EXECUTE format('GRANT USAGE ON SCHEMA %I TO %I',
|
|
row_data.table_schema,
|
|
username
|
|
);
|
|
END LOOP;
|
|
END IF;
|
|
END;
|
|
$$;
|