]> granicus.if.org Git - postgresql/commitdiff
Support default ACLs in get_object_address
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 11 Mar 2015 22:23:47 +0000 (19:23 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 11 Mar 2015 22:23:47 +0000 (19:23 -0300)
In the spirit of 890192e99af, this time add support for the things
living in the pg_default_acl catalog.  These are not really "objects",
but they show up as such in event triggers.

There is no "DROP DEFAULT PRIVILEGES" or similar command, so it doesn't
look like the new representation given would be useful anywhere else, so
I didn't try to use it outside objectaddress.c.  (That might be a bug in
itself, but that would be material for another commit.)

Reviewed by Stephen Frost.

src/backend/catalog/objectaddress.c
src/backend/commands/event_trigger.c
src/include/nodes/parsenodes.h
src/test/regress/expected/event_trigger.out
src/test/regress/expected/object_address.out
src/test/regress/sql/event_trigger.sql
src/test/regress/sql/object_address.sql

index 67c14020e5c9e1736f1a5d1715555aa78bd3de4b..142bc689e95cce0350e23e556b546df7daf0a054 100644 (file)
@@ -522,7 +522,7 @@ ObjectTypeMap[] =
        /* OCLASS_USER_MAPPING */
        { "user mapping", OBJECT_USER_MAPPING },
        /* OCLASS_DEFACL */
-       { "default acl", -1 },          /* unmapped */
+       { "default acl", OBJECT_DEFACL },
        /* OCLASS_EXTENSION */
        { "extension", OBJECT_EXTENSION },
        /* OCLASS_EVENT_TRIGGER */
@@ -557,6 +557,8 @@ static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname,
                                                List *objargs, bool missing_ok);
 static ObjectAddress get_object_address_usermapping(List *objname,
                                                           List *objargs, bool missing_ok);
+static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
+                                                 bool missing_ok);
 static const ObjectPropertyType *get_object_property_data(Oid class_id);
 
 static void getRelationDescription(StringInfo buffer, Oid relid);
@@ -775,6 +777,10 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
                                address = get_object_address_usermapping(objname, objargs,
                                                                                                                 missing_ok);
                                break;
+                       case OBJECT_DEFACL:
+                               address = get_object_address_defacl(objname, objargs,
+                                                                                                       missing_ok);
+                               break;
                        default:
                                elog(ERROR, "unrecognized objtype: %d", (int) objtype);
                                /* placate compiler, in case it thinks elog might return */
@@ -1447,6 +1453,113 @@ get_object_address_usermapping(List *objname, List *objargs, bool missing_ok)
        return address;
 }
 
+/*
+ * Find the ObjectAddress for a default ACL.
+ */
+static ObjectAddress
+get_object_address_defacl(List *objname, List *objargs, bool missing_ok)
+{
+       HeapTuple       tp;
+       Oid                     userid;
+       Oid                     schemaid;
+       char       *username;
+       char       *schema;
+       char            objtype;
+       char       *objtype_str;
+       ObjectAddress address;
+
+       ObjectAddressSet(address, DefaultAclRelationId, InvalidOid);
+
+       /*
+        * First figure out the textual attributes so that they can be used for
+        * error reporting.
+        */
+       username = strVal(linitial(objname));
+       if (list_length(objname) >= 2)
+               schema = (char *) strVal(lsecond(objname));
+       else
+               schema = NULL;
+
+       /*
+        * Decode defaclobjtype.  Only first char is considered; the rest of the
+        * string, if any, is blissfully ignored.
+        */
+       objtype = ((char *) strVal(linitial(objargs)))[0];
+       switch (objtype)
+       {
+               case DEFACLOBJ_RELATION:
+                       objtype_str = "tables";
+                       break;
+               case DEFACLOBJ_SEQUENCE:
+                       objtype_str = "sequences";
+                       break;
+               case DEFACLOBJ_FUNCTION:
+                       objtype_str = "functions";
+                       break;
+               case DEFACLOBJ_TYPE:
+                       objtype_str = "types";
+                       break;
+               default:
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("unrecognized default ACL object type %c", objtype),
+                                        errhint("Valid object types are 'r', 'S', 'f', and 'T'.")));
+       }
+
+       /*
+        * Look up user ID.  Behave as "default ACL not found" if the user doesn't
+        * exist.
+        */
+       tp = SearchSysCache1(AUTHNAME,
+                                                CStringGetDatum(username));
+       if (!HeapTupleIsValid(tp))
+               goto not_found;
+       userid = HeapTupleGetOid(tp);
+       ReleaseSysCache(tp);
+
+       /*
+        * If a schema name was given, look up its OID.  If it doesn't exist,
+        * behave as "default ACL not found".
+        */
+       if (schema)
+       {
+               schemaid = get_namespace_oid(schema, true);
+               if (schemaid == InvalidOid)
+                       goto not_found;
+       }
+       else
+               schemaid = InvalidOid;
+
+       /* Finally, look up the pg_default_acl object */
+       tp = SearchSysCache3(DEFACLROLENSPOBJ,
+                                                ObjectIdGetDatum(userid),
+                                                ObjectIdGetDatum(schemaid),
+                                                CharGetDatum(objtype));
+       if (!HeapTupleIsValid(tp))
+               goto not_found;
+
+       address.objectId = HeapTupleGetOid(tp);
+       ReleaseSysCache(tp);
+
+       return address;
+
+not_found:
+       if (!missing_ok)
+       {
+               if (schema)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                        errmsg("default ACL for user \"%s\" in schema \"%s\" on %s does not exist",
+                                                       username, schema, objtype_str)));
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                        errmsg("default ACL for user \"%s\" on %s does not exist",
+                                                       username, objtype_str)));
+       }
+       return address;
+}
+
 /*
  * Convert an array of TEXT into a List of string Values, as emitted by the
  * parser, which is what get_object_address uses as input.
@@ -1599,6 +1712,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
                case OBJECT_OPFAMILY:
                case OBJECT_CAST:
                case OBJECT_USER_MAPPING:
+               case OBJECT_DEFACL:
                        if (list_length(args) != 1)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -4024,10 +4138,8 @@ getObjectIdentityParts(const ObjectAddress *object,
                                SysScanDesc rcscan;
                                HeapTuple       tup;
                                Form_pg_default_acl defacl;
-
-                               /* no objname support */
-                               if (objname)
-                                       *objname = NIL;
+                               char       *schema;
+                               char       *username;
 
                                defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
 
@@ -4047,19 +4159,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 
                                defacl = (Form_pg_default_acl) GETSTRUCT(tup);
 
+                               username = GetUserNameFromId(defacl->defaclrole);
                                appendStringInfo(&buffer,
                                                                 "for role %s",
-                                       quote_identifier(GetUserNameFromId(defacl->defaclrole)));
+                                                                quote_identifier(username));
 
                                if (OidIsValid(defacl->defaclnamespace))
                                {
-                                       char       *schema;
-
                                        schema = get_namespace_name(defacl->defaclnamespace);
                                        appendStringInfo(&buffer,
                                                                         " in schema %s",
                                                                         quote_identifier(schema));
                                }
+                               else
+                                       schema = NULL;
 
                                switch (defacl->defaclobjtype)
                                {
@@ -4081,6 +4194,14 @@ getObjectIdentityParts(const ObjectAddress *object,
                                                break;
                                }
 
+                               if (objname)
+                               {
+                                       *objname = list_make1(username);
+                                       if (schema)
+                                               *objname = lappend(*objname, schema);
+                                       *objargs = list_make1(psprintf("%c", defacl->defaclobjtype));
+                               }
+
                                systable_endscan(rcscan);
                                heap_close(defaclrel, AccessShareLock);
                                break;
index 4e446bd25cdbedfa4d067d56de6a4e55b60ccfc6..3fec57ea2370b7691201d0a75c849fb78b27011d 100644 (file)
@@ -1065,6 +1065,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
                case OBJECT_COLUMN:
                case OBJECT_COLLATION:
                case OBJECT_CONVERSION:
+               case OBJECT_DEFACL:
                case OBJECT_DEFAULT:
                case OBJECT_DOMAIN:
                case OBJECT_DOMCONSTRAINT:
index 1279aca882cf422ccaeb0078f80ebbbd879445f7..38ed661122d6e1fbb1bce6441f8d4172757c0cb6 100644 (file)
@@ -1239,6 +1239,7 @@ typedef enum ObjectType
        OBJECT_CONVERSION,
        OBJECT_DATABASE,
        OBJECT_DEFAULT,
+       OBJECT_DEFACL,
        OBJECT_DOMAIN,
        OBJECT_DOMCONSTRAINT,
        OBJECT_EVENT_TRIGGER,
index b87d5034369ece2d47c687bcf247442985ff0563..1dace02782fd98c26dcd37780b4cb16650c25510 100644 (file)
@@ -116,6 +116,9 @@ create server useless_server foreign data wrapper useless;
 NOTICE:  test_event_trigger: ddl_command_end CREATE SERVER
 create user mapping for regression_bob server useless_server;
 NOTICE:  test_event_trigger: ddl_command_end CREATE USER MAPPING
+alter default privileges for role regression_bob
+ revoke delete on tables from regression_bob;
+NOTICE:  test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
 -- alter owner to non-superuser should fail
 alter event trigger regress_event_trigger owner to regression_bob;
 ERROR:  permission denied to change owner of event trigger "regress_event_trigger"
@@ -135,6 +138,7 @@ ERROR:  event trigger "regress_event_trigger" does not exist
 drop role regression_bob;
 ERROR:  role "regression_bob" cannot be dropped because some objects depend on it
 DETAIL:  owner of event trigger regress_event_trigger3
+owner of default privileges on new relations belonging to role regression_bob
 owner of user mapping for regression_bob on server useless_server
 -- cleanup before next test
 -- these are all OK; the second one should emit a NOTICE
index e72abda90aa1cac73e148ff7ba9dd7d57c89703b..3bcbcd8b65f00e8919cf6f918d5aac7dd3a0b24e 100644 (file)
@@ -30,6 +30,8 @@ CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDU
 CREATE POLICY genpol ON addr_nsp.gentable;
 CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
 CREATE USER MAPPING FOR regtest_addr_user SERVER "integer";
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regtest_addr_user;
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user REVOKE DELETE ON TABLES FROM regtest_addr_user;
 -- test some error cases
 SELECT pg_get_object_address('stone', '{}', '{}');
 ERROR:  unrecognized object type "stone"
@@ -77,7 +79,7 @@ BEGIN
                ('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
                ('text search parser'), ('text search dictionary'),
                ('text search template'), ('text search configuration'),
-               ('policy'), ('user mapping')
+               ('policy'), ('user mapping'), ('default acl')
        LOOP
                FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
                LOOP
@@ -255,6 +257,12 @@ WARNING:  error for user mapping,{addr_nsp,zwei},{}: argument list length must b
 WARNING:  error for user mapping,{addr_nsp,zwei},{integer}: user mapping for user "addr_nsp" in server "integer" does not exist
 WARNING:  error for user mapping,{eins,zwei,drei},{}: argument list length must be exactly 1
 WARNING:  error for user mapping,{eins,zwei,drei},{integer}: user mapping for user "eins" in server "integer" does not exist
+WARNING:  error for default acl,{eins},{}: argument list length must be exactly 1
+WARNING:  error for default acl,{eins},{integer}: unrecognized default ACL object type i
+WARNING:  error for default acl,{addr_nsp,zwei},{}: argument list length must be exactly 1
+WARNING:  error for default acl,{addr_nsp,zwei},{integer}: unrecognized default ACL object type i
+WARNING:  error for default acl,{eins,zwei,drei},{}: argument list length must be exactly 1
+WARNING:  error for default acl,{eins,zwei,drei},{integer}: unrecognized default ACL object type i
 -- these object types cannot be qualified names
 SELECT pg_get_object_address('language', '{one}', '{}');
 ERROR:  language "one" does not exist
@@ -341,6 +349,8 @@ WITH objects (type, name, args) AS (VALUES
                                ('foreign-data wrapper', '{addr_fdw}', '{}'),
                                ('server', '{addr_fserv}', '{}'),
                                ('user mapping', '{regtest_addr_user}', '{integer}'),
+                               ('default acl', '{regtest_addr_user,public}', '{r}'),
+                               ('default acl', '{regtest_addr_user}', '{r}'),
                                -- extension
                                -- event trigger
                                ('policy', '{addr_nsp, gentable, genpol}', '{}')
@@ -355,6 +365,8 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
        ORDER BY addr1.classid, addr1.objid;
            type            |   schema   |       name        |                               identity                               | ?column? 
 ---------------------------+------------+-------------------+----------------------------------------------------------------------+----------
+ default acl               |            |                   | for role regtest_addr_user in schema public on tables                | t
+ default acl               |            |                   | for role regtest_addr_user on tables                                 | t
  type                      | pg_catalog | _int4             | integer[]                                                            | t
  type                      | addr_nsp   | gencomptype       | addr_nsp.gencomptype                                                 | t
  type                      | addr_nsp   | genenum           | addr_nsp.genenum                                                     | t
@@ -391,11 +403,12 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
  text search parser        | addr_nsp   | addr_ts_prs       | addr_nsp.addr_ts_prs                                                 | t
  text search configuration | addr_nsp   | addr_ts_conf      | addr_nsp.addr_ts_conf                                                | t
  text search template      | addr_nsp   | addr_ts_temp      | addr_nsp.addr_ts_temp                                                | t
-(36 rows)
+(38 rows)
 
 ---
 --- Cleanup resources
 ---
 DROP FOREIGN DATA WRAPPER addr_fdw CASCADE;
 DROP SCHEMA addr_nsp CASCADE;
+DROP OWNED BY regtest_addr_user;
 DROP USER regtest_addr_user;
index bcfeb3a869311770b62bb306a7451472a13ad361..1b7346409c8a124fe773aa6eab0bf56f2c6b50db 100644 (file)
@@ -110,6 +110,8 @@ drop table event_trigger_fire1;
 create foreign data wrapper useless;
 create server useless_server foreign data wrapper useless;
 create user mapping for regression_bob server useless_server;
+alter default privileges for role regression_bob
+ revoke delete on tables from regression_bob;
 
 -- alter owner to non-superuser should fail
 alter event trigger regress_event_trigger owner to regression_bob;
index b714b529c8de91361be38ec97e2cb44b582718d2..a49f03fdf7dc3859fa0c8233b74d33613d9302a5 100644 (file)
@@ -34,6 +34,8 @@ CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDU
 CREATE POLICY genpol ON addr_nsp.gentable;
 CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
 CREATE USER MAPPING FOR regtest_addr_user SERVER "integer";
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regtest_addr_user;
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user REVOKE DELETE ON TABLES FROM regtest_addr_user;
 
 -- test some error cases
 SELECT pg_get_object_address('stone', '{}', '{}');
@@ -73,7 +75,7 @@ BEGIN
                ('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
                ('text search parser'), ('text search dictionary'),
                ('text search template'), ('text search configuration'),
-               ('policy'), ('user mapping')
+               ('policy'), ('user mapping'), ('default acl')
        LOOP
                FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
                LOOP
@@ -156,6 +158,8 @@ WITH objects (type, name, args) AS (VALUES
                                ('foreign-data wrapper', '{addr_fdw}', '{}'),
                                ('server', '{addr_fserv}', '{}'),
                                ('user mapping', '{regtest_addr_user}', '{integer}'),
+                               ('default acl', '{regtest_addr_user,public}', '{r}'),
+                               ('default acl', '{regtest_addr_user}', '{r}'),
                                -- extension
                                -- event trigger
                                ('policy', '{addr_nsp, gentable, genpol}', '{}')
@@ -176,4 +180,5 @@ DROP FOREIGN DATA WRAPPER addr_fdw CASCADE;
 
 DROP SCHEMA addr_nsp CASCADE;
 
+DROP OWNED BY regtest_addr_user;
 DROP USER regtest_addr_user;