]> granicus.if.org Git - postgresql/commitdiff
Altering default privileges on schemas
authorTeodor Sigaev <teodor@sigaev.ru>
Tue, 28 Mar 2017 15:58:55 +0000 (18:58 +0300)
committerTeodor Sigaev <teodor@sigaev.ru>
Tue, 28 Mar 2017 15:58:55 +0000 (18:58 +0300)
Extend ALTER DEFAULT PRIVILEGES command to schemas.

Author: Matheus Oliveira
Reviewed-by: Petr JelĂ­nek, Ashutosh Sharma
https://commitfest.postgresql.org/13/887/

13 files changed:
doc/src/sgml/ref/alter_default_privileges.sgml
src/backend/catalog/aclchk.c
src/backend/catalog/objectaddress.c
src/backend/catalog/pg_namespace.c
src/backend/parser/gram.y
src/bin/pg_dump/dumputils.c
src/bin/pg_dump/pg_dump.c
src/bin/psql/describe.c
src/bin/psql/tab-complete.c
src/include/catalog/pg_default_acl.h
src/include/parser/kwlist.h
src/test/regress/expected/privileges.out
src/test/regress/sql/privileges.sql

index 04064d399cb312a30b915d7c14867891fd1221bc..e3363f868a4604a26ad7d7c1913d42ef6c8bbcf3 100644 (file)
@@ -46,6 +46,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
     ON TYPES
     TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
 
+GRANT { USAGE | CREATE | ALL [ PRIVILEGES ] }
+    ON SCHEMAS
+    TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
 REVOKE [ GRANT OPTION FOR ]
     { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
     [, ...] | ALL [ PRIVILEGES ] }
@@ -71,6 +75,12 @@ REVOKE [ GRANT OPTION FOR ]
     ON TYPES
     FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
     [ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+    { USAGE | CREATE | ALL [ PRIVILEGES ] }
+    ON SCHEMAS
+    FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
+    [ CASCADE | RESTRICT ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -81,8 +91,9 @@ REVOKE [ GRANT OPTION FOR ]
    <command>ALTER DEFAULT PRIVILEGES</> allows you to set the privileges
    that will be applied to objects created in the future.  (It does not
    affect privileges assigned to already-existing objects.)  Currently,
-   only the privileges for tables (including views and foreign tables),
-   sequences, functions, and types (including domains) can be altered.
+   only the privileges for schemas, tables (including views and foreign
+   tables), sequences, functions, and types (including domains) can be
+   altered.
   </para>
 
   <para>
@@ -125,6 +136,8 @@ REVOKE [ GRANT OPTION FOR ]
       are altered for objects later created in that schema.
       If <literal>IN SCHEMA</> is omitted, the global default privileges
       are altered.
+      <literal>IN SCHEMA</> is not allowed when using <literal>ON SCHEMAS</>
+      as schemas can't be nested.
      </para>
     </listitem>
    </varlistentry>
index d01930f4a80d0fdb8da8e98baaa1d0d764160881..2d535c2aada6951c0a6a9aa8d06ce4a849758a5c 100644 (file)
@@ -959,6 +959,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
                        all_privileges = ACL_ALL_RIGHTS_TYPE;
                        errormsg = gettext_noop("invalid privilege type %s for type");
                        break;
+               case ACL_OBJECT_NAMESPACE:
+                       all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
+                       errormsg = gettext_noop("invalid privilege type %s for schema");
+                       break;
                default:
                        elog(ERROR, "unrecognized GrantStmt.objtype: %d",
                                 (int) action->objtype);
@@ -1146,6 +1150,16 @@ SetDefaultACL(InternalDefaultACL *iacls)
                                this_privileges = ACL_ALL_RIGHTS_TYPE;
                        break;
 
+               case ACL_OBJECT_NAMESPACE:
+                       if (OidIsValid(iacls->nspid))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+                                                errmsg("cannot use IN SCHEMA clause when using GRANT/REVOKE ON SCHEMAS")));
+                       objtype = DEFACLOBJ_NAMESPACE;
+                       if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+                               this_privileges = ACL_ALL_RIGHTS_NAMESPACE;
+                       break;
+
                default:
                        elog(ERROR, "unrecognized objtype: %d",
                                 (int) iacls->objtype);
@@ -1369,6 +1383,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
                        case DEFACLOBJ_TYPE:
                                iacls.objtype = ACL_OBJECT_TYPE;
                                break;
+                       case DEFACLOBJ_NAMESPACE:
+                               iacls.objtype = ACL_OBJECT_NAMESPACE;
+                               break;
                        default:
                                /* Shouldn't get here */
                                elog(ERROR, "unexpected default ACL type: %d",
@@ -5259,6 +5276,10 @@ get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid)
                        defaclobjtype = DEFACLOBJ_TYPE;
                        break;
 
+               case ACL_OBJECT_NAMESPACE:
+                       defaclobjtype = DEFACLOBJ_NAMESPACE;
+                       break;
+
                default:
                        return NULL;
        }
index 2948d64fa73a6ab7108e8d4b3730669d98777c0e..1eb7930901974959afec077c4f645a772a259746 100644 (file)
@@ -1843,11 +1843,14 @@ get_object_address_defacl(List *object, bool missing_ok)
                case DEFACLOBJ_TYPE:
                        objtype_str = "types";
                        break;
+               case DEFACLOBJ_NAMESPACE:
+                       objtype_str = "schemas";
+                       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\".")));
+                                        errhint("Valid object types are \"r\", \"S\", \"f\", \"T\" and \"s\".")));
        }
 
        /*
@@ -3255,6 +3258,11 @@ getObjectDescription(const ObjectAddress *object)
                                                                                 _("default privileges on new types belonging to role %s"),
                                                           GetUserNameFromId(defacl->defaclrole, false));
                                                break;
+                                       case DEFACLOBJ_NAMESPACE:
+                                               appendStringInfo(&buffer,
+                                                                                _("default privileges on new schemas belonging to role %s"),
+                                                          GetUserNameFromId(defacl->defaclrole, false));
+                                               break;
                                        default:
                                                /* shouldn't get here */
                                                appendStringInfo(&buffer,
@@ -4762,6 +4770,10 @@ getObjectIdentityParts(const ObjectAddress *object,
                                                appendStringInfoString(&buffer,
                                                                                           " on types");
                                                break;
+                                       case DEFACLOBJ_NAMESPACE:
+                                               appendStringInfoString(&buffer,
+                                                                                          " on schemas");
+                                               break;
                                }
 
                                if (objname)
index 5672536d31c4bc6b968770936dee81503ea65936..613b963683d01e7682a421082a955e87f6258cf6 100644 (file)
  * Create a namespace (schema) with the given name and owner OID.
  *
  * If isTemp is true, this schema is a per-backend schema for holding
- * temporary tables.  Currently, the only effect of that is to prevent it
- * from being linked as a member of any active extension.  (If someone
- * does CREATE TEMP TABLE in an extension script, we don't want the temp
- * schema to become part of the extension.)
+ * temporary tables.  Currently, it is used to prevent it from being
+ * linked as a member of any active extension.  (If someone does CREATE
+ * TEMP TABLE in an extension script, we don't want the temp schema to
+ * become part of the extension). And to avoid checking for default ACL
+ * for temp namespace (as it is not necessary).
  * ---------------
  */
 Oid
@@ -49,6 +50,7 @@ NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
        TupleDesc       tupDesc;
        ObjectAddress myself;
        int                     i;
+       Acl                     *nspacl;
 
        /* sanity checks */
        if (!nspName)
@@ -60,6 +62,12 @@ NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
                                (errcode(ERRCODE_DUPLICATE_SCHEMA),
                                 errmsg("schema \"%s\" already exists", nspName)));
 
+       if (!isTemp)
+               nspacl = get_user_default_acl(ACL_OBJECT_NAMESPACE, ownerId,
+                                                                         InvalidOid);
+       else
+               nspacl = NULL;
+
        /* initialize nulls and values */
        for (i = 0; i < Natts_pg_namespace; i++)
        {
@@ -69,7 +77,10 @@ NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
        namestrcpy(&nname, nspName);
        values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname);
        values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId);
-       nulls[Anum_pg_namespace_nspacl - 1] = true;
+       if (nspacl != NULL)
+               values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(nspacl);
+       else
+               nulls[Anum_pg_namespace_nspacl - 1] = true;
 
        nspdesc = heap_open(NamespaceRelationId, RowExclusiveLock);
        tupDesc = nspdesc->rd_att;
index 19dd77d7877e90606026aa02a98c1bd1a846b922..20865c0ee000064e992ba6219e4e81d2e7e9253a 100644 (file)
@@ -668,7 +668,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
        RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
        ROW ROWS RULE
 
-       SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
+       SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
        SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
        SIMILAR SIMPLE SKIP SLOT SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
        START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P
@@ -7035,6 +7035,7 @@ defacl_privilege_target:
                        | FUNCTIONS             { $$ = ACL_OBJECT_FUNCTION; }
                        | SEQUENCES             { $$ = ACL_OBJECT_SEQUENCE; }
                        | TYPES_P               { $$ = ACL_OBJECT_TYPE; }
+                       | SCHEMAS               { $$ = ACL_OBJECT_NAMESPACE; }
                ;
 
 
@@ -14713,6 +14714,7 @@ unreserved_keyword:
                        | RULE
                        | SAVEPOINT
                        | SCHEMA
+                       | SCHEMAS
                        | SCROLL
                        | SEARCH
                        | SECOND_P
index b41f2b91258f13944a52f9803e626f1d03789a1e..c74153acce209b2c9384a07c676a8d64e3e1fd9e 100644 (file)
@@ -520,7 +520,9 @@ do { \
                CONVERT_PRIV('X', "EXECUTE");
        else if (strcmp(type, "LANGUAGE") == 0)
                CONVERT_PRIV('U', "USAGE");
-       else if (strcmp(type, "SCHEMA") == 0)
+       else if (strcmp(type, "SCHEMA") == 0 ||
+                        strcmp(type, "SCHEMAS") == 0
+                       )
        {
                CONVERT_PRIV('C', "CREATE");
                CONVERT_PRIV('U', "USAGE");
index ba34cc163e97a1534499c5864a50289c88c97c68..262f5539bc6d3d45ff65c080a18eb50f6b82519a 100644 (file)
@@ -14295,6 +14295,9 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo)
                case DEFACLOBJ_TYPE:
                        type = "TYPES";
                        break;
+               case DEFACLOBJ_NAMESPACE:
+                       type = "SCHEMAS";
+                       break;
                default:
                        /* shouldn't get here */
                        exit_horribly(NULL,
index bcf675208b472a8a30d30f921e60796722b6e950..b0f3e5e34709ea212eeeeb156276ccd7207b62d9 100644 (file)
@@ -1028,7 +1028,7 @@ listDefaultACLs(const char *pattern)
        printfPQExpBuffer(&buf,
                           "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n"
                                          "  n.nspname AS \"%s\",\n"
-                                         "  CASE d.defaclobjtype WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' END AS \"%s\",\n"
+                                         "  CASE d.defaclobjtype WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' END AS \"%s\",\n"
                                          "  ",
                                          gettext_noop("Owner"),
                                          gettext_noop("Schema"),
@@ -1040,6 +1040,8 @@ listDefaultACLs(const char *pattern)
                                          gettext_noop("function"),
                                          DEFACLOBJ_TYPE,
                                          gettext_noop("type"),
+                                         DEFACLOBJ_NAMESPACE,
+                                         gettext_noop("schema"),
                                          gettext_noop("Type"));
 
        printACLColumn(&buf, "d.defaclacl");
index f7494065de81cd180545157b5cab874ad2ea98cb..dc2794d48a6998393ade02540f100485e0352186 100644 (file)
@@ -2796,7 +2796,7 @@ psql_completion(const char *text, int start, int end)
                 * to the kinds of objects supported.
                 */
                if (HeadMatches3("ALTER","DEFAULT","PRIVILEGES"))
-                       COMPLETE_WITH_LIST4("TABLES", "SEQUENCES", "FUNCTIONS", "TYPES");
+                       COMPLETE_WITH_LIST5("TABLES", "SEQUENCES", "FUNCTIONS", "TYPES", "SCHEMAS");
                else
                        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf,
                                                                   " UNION SELECT 'ALL FUNCTIONS IN SCHEMA'"
index 42fb224f9d3ba4a52184086bde9eca4d4f235f7d..78bbeb64feb2686f9366e42368d38bf8a38498d6 100644 (file)
@@ -70,5 +70,6 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
 #define DEFACLOBJ_SEQUENCE             'S'             /* sequence */
 #define DEFACLOBJ_FUNCTION             'f'             /* function */
 #define DEFACLOBJ_TYPE                 'T'             /* type */
+#define DEFACLOBJ_NAMESPACE            'n'             /* namespace */
 
 #endif   /* PG_DEFAULT_ACL_H */
index 6cd36c7fe3014a63ca479768239fc81c8735679b..cd21a789d57293e1c1f9d4092e4e0a9df777653d 100644 (file)
@@ -344,6 +344,7 @@ PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD)
 PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD)
 PG_KEYWORD("savepoint", SAVEPOINT, UNRESERVED_KEYWORD)
 PG_KEYWORD("schema", SCHEMA, UNRESERVED_KEYWORD)
+PG_KEYWORD("schemas", SCHEMAS, UNRESERVED_KEYWORD)
 PG_KEYWORD("scroll", SCROLL, UNRESERVED_KEYWORD)
 PG_KEYWORD("search", SEARCH, UNRESERVED_KEYWORD)
 PG_KEYWORD("second", SECOND_P, UNRESERVED_KEYWORD)
index f3499807596aee12babd30c2aa3907a0076929f1..c6e7031beff34f761b0127be9eefb5ff9e5ee1c3 100644 (file)
@@ -1356,6 +1356,64 @@ SELECT has_table_privilege('regress_user1', 'testns.acltest1', 'INSERT'); -- no
 (1 row)
 
 ALTER DEFAULT PRIVILEGES FOR ROLE regress_user1 REVOKE EXECUTE ON FUNCTIONS FROM public;
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON SCHEMAS TO regress_user2; -- error
+ERROR:  cannot use IN SCHEMA clause when using GRANT/REVOKE ON SCHEMAS
+ALTER DEFAULT PRIVILEGES GRANT USAGE ON SCHEMAS TO regress_user2;
+CREATE SCHEMA testns2;
+SELECT has_schema_privilege('regress_user2', 'testns2', 'USAGE'); -- yes
+ has_schema_privilege 
+----------------------
+ t
+(1 row)
+
+SELECT has_schema_privilege('regress_user2', 'testns2', 'CREATE'); -- no
+ has_schema_privilege 
+----------------------
+ f
+(1 row)
+
+ALTER DEFAULT PRIVILEGES REVOKE USAGE ON SCHEMAS FROM regress_user2;
+CREATE SCHEMA testns3;
+SELECT has_schema_privilege('regress_user2', 'testns3', 'USAGE'); -- no
+ has_schema_privilege 
+----------------------
+ f
+(1 row)
+
+SELECT has_schema_privilege('regress_user2', 'testns3', 'CREATE'); -- no
+ has_schema_privilege 
+----------------------
+ f
+(1 row)
+
+ALTER DEFAULT PRIVILEGES GRANT ALL ON SCHEMAS TO regress_user2;
+CREATE SCHEMA testns4;
+SELECT has_schema_privilege('regress_user2', 'testns4', 'USAGE'); -- yes
+ has_schema_privilege 
+----------------------
+ t
+(1 row)
+
+SELECT has_schema_privilege('regress_user2', 'testns4', 'CREATE'); -- yes
+ has_schema_privilege 
+----------------------
+ t
+(1 row)
+
+ALTER DEFAULT PRIVILEGES REVOKE ALL ON SCHEMAS FROM regress_user2;
+CREATE SCHEMA testns5;
+SELECT has_schema_privilege('regress_user2', 'testns5', 'USAGE'); -- no
+ has_schema_privilege 
+----------------------
+ f
+(1 row)
+
+SELECT has_schema_privilege('regress_user2', 'testns5', 'CREATE'); -- no
+ has_schema_privilege 
+----------------------
+ f
+(1 row)
+
 SET ROLE regress_user1;
 CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
 SELECT has_function_privilege('regress_user2', 'testns.foo()', 'EXECUTE'); -- no
@@ -1403,6 +1461,10 @@ SELECT count(*)
 
 DROP SCHEMA testns CASCADE;
 NOTICE:  drop cascades to table testns.acltest1
+DROP SCHEMA testns2 CASCADE;
+DROP SCHEMA testns3 CASCADE;
+DROP SCHEMA testns4 CASCADE;
+DROP SCHEMA testns5 CASCADE;
 SELECT d.*     -- check that entries went away
   FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid
   WHERE nspname IS NULL AND defaclnamespace != 0;
index 166e903012b2a71a367e0ee1c65f040840e95188..38215954dadd9389d1d738b951ad1171f61e9035 100644 (file)
@@ -816,6 +816,36 @@ SELECT has_table_privilege('regress_user1', 'testns.acltest1', 'INSERT'); -- no
 
 ALTER DEFAULT PRIVILEGES FOR ROLE regress_user1 REVOKE EXECUTE ON FUNCTIONS FROM public;
 
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT USAGE ON SCHEMAS TO regress_user2; -- error
+
+ALTER DEFAULT PRIVILEGES GRANT USAGE ON SCHEMAS TO regress_user2;
+
+CREATE SCHEMA testns2;
+
+SELECT has_schema_privilege('regress_user2', 'testns2', 'USAGE'); -- yes
+SELECT has_schema_privilege('regress_user2', 'testns2', 'CREATE'); -- no
+
+ALTER DEFAULT PRIVILEGES REVOKE USAGE ON SCHEMAS FROM regress_user2;
+
+CREATE SCHEMA testns3;
+
+SELECT has_schema_privilege('regress_user2', 'testns3', 'USAGE'); -- no
+SELECT has_schema_privilege('regress_user2', 'testns3', 'CREATE'); -- no
+
+ALTER DEFAULT PRIVILEGES GRANT ALL ON SCHEMAS TO regress_user2;
+
+CREATE SCHEMA testns4;
+
+SELECT has_schema_privilege('regress_user2', 'testns4', 'USAGE'); -- yes
+SELECT has_schema_privilege('regress_user2', 'testns4', 'CREATE'); -- yes
+
+ALTER DEFAULT PRIVILEGES REVOKE ALL ON SCHEMAS FROM regress_user2;
+
+CREATE SCHEMA testns5;
+
+SELECT has_schema_privilege('regress_user2', 'testns5', 'USAGE'); -- no
+SELECT has_schema_privilege('regress_user2', 'testns5', 'CREATE'); -- no
+
 SET ROLE regress_user1;
 
 CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
@@ -853,6 +883,10 @@ SELECT count(*)
   WHERE nspname = 'testns';
 
 DROP SCHEMA testns CASCADE;
+DROP SCHEMA testns2 CASCADE;
+DROP SCHEMA testns3 CASCADE;
+DROP SCHEMA testns4 CASCADE;
+DROP SCHEMA testns5 CASCADE;
 
 SELECT d.*     -- check that entries went away
   FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid