diff schema/manage_users.sql @ 307:750a9c9cd965

Use SQL UPDATE to update users This implies it's not a database error anymore to try to update a non-existent user. Thus, handle this as a HTTP-404 in the backend, which is in line with what GET does. Using UPDATE here will allow to GRANT column-wise privileges. The password has become part of the view to be updatable as well.
author Tom Gottfried <tom@intevation.de>
date Wed, 01 Aug 2018 15:49:38 +0200
parents 0b2d9f96ddb8
children 0745b4d336c4
line wrap: on
line diff
--- a/schema/manage_users.sql	Wed Aug 01 15:18:26 2018 +0200
+++ b/schema/manage_users.sql	Wed Aug 01 15:49:38 2018 +0200
@@ -38,7 +38,13 @@
 
 
 CREATE OR REPLACE VIEW users.list_users WITH (security_barrier) AS
-    SELECT r.rolname, p.*
+    SELECT
+            r.rolname,
+            p.username,
+            CAST('' AS varchar) AS pw,
+            p.country,
+            p.map_extent,
+            p.email_address
         FROM internal.user_profiles p
             JOIN pg_roles u ON p.username = u.rolname
             JOIN pg_auth_members a ON u.oid = a.member
@@ -79,61 +85,52 @@
     SECURITY DEFINER;
 
 
-CREATE OR REPLACE FUNCTION sys_admin.update_user(
-       username internal.user_profiles.username%TYPE,
-       new_userrole varchar,
-       new_username internal.user_profiles.username%TYPE,
-       new_pw varchar,
-       new_country internal.user_profiles.country%TYPE,
-       new_map_extent internal.user_profiles.map_extent%TYPE,
-       new_email_address internal.user_profiles.email_address%TYPE
-    )
-    RETURNS void
+CREATE OR REPLACE FUNCTION internal.update_user() RETURNS trigger
 AS $$
 DECLARE
-    cur_username name;
-    cur_userrole name;
+    cur_username varchar;
 BEGIN
-    cur_username = username;
+    cur_username = OLD.username;
 
     UPDATE internal.user_profiles p
         SET (username, country, map_extent, email_address)
-        = (new_username, new_country, new_map_extent, new_email_address)
+        = (NEW.username, NEW.country, NEW.map_extent, NEW.email_address)
         WHERE p.username = cur_username;
 
-    IF new_username <> cur_username
+    IF NEW.username <> cur_username
     THEN
         EXECUTE format(
-            'ALTER ROLE %I RENAME TO %I', username, new_username);
-        cur_username = new_username;
+            'ALTER ROLE %I RENAME TO %I', cur_username, NEW.username);
+        cur_username = NEW.username;
     END IF;
 
-    cur_userrole = rolname FROM pg_roles r
-        JOIN pg_auth_members a ON r.oid = a.roleid
-        WHERE member = (
-            SELECT oid FROM pg_roles WHERE rolname = cur_username);
-    IF new_userrole <> cur_userrole
+    IF NEW.rolname <> OLD.rolname
     THEN
         EXECUTE format(
-            'REVOKE %I FROM %I', cur_userrole, cur_username);
+            'REVOKE %I FROM %I', OLD.rolname, cur_username);
+        EXECUTE format(
+            'GRANT %I TO %I', NEW.rolname, cur_username);
     END IF;
-    -- GRANT new_userrole unconditionally to ensure it's an error to upgrade
-    -- a non-existent cur_username (GRANTing a role twice is not an error)
-    EXECUTE format(
-        'GRANT %I TO %I', new_userrole, cur_username);
 
-    IF new_pw IS NOT NULL AND new_pw <> ''
+    IF NEW.pw IS NOT NULL AND NEW.pw <> ''
     THEN
         EXECUTE format(
             'ALTER ROLE %I PASSWORD %L',
             cur_username,
-            users.check_password(new_pw));
+            users.check_password(NEW.pw));
     END IF;
+
+    -- Do not leak new password
+    NEW.pw = '';
+    RETURN NEW;
 END;
 $$
     LANGUAGE plpgsql
     SECURITY DEFINER;
 
+CREATE TRIGGER update_user INSTEAD OF UPDATE ON users.list_users FOR EACH ROW
+    EXECUTE PROCEDURE internal.update_user();
+
 
 CREATE OR REPLACE FUNCTION sys_admin.delete_user(
        username internal.user_profiles.username%TYPE