+ pg_default_acls> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ defaclrole
+ oid
+ pg_authid.oid
+ The OID of the role associated with this entry
+
+
+
+ defaclnamespace
+ oid
+ pg_namespace.oid
+ The OID of the namespace associated with this entry
+
+
+
+ defaclgrantobjtype
+ char
+
+
+ r> = relation (table, view)
+ f> = function, S> = sequence
+
+
+
+
+ defacllist
+ aclitem[]
+
+
+ Access privileges that the object should have on creation.
+ This is NOT a mask, it's exactly what the object will get.
+ See
+ ,
+ and
+
+ for details.
+
+
+
+
+
+
+
+
+
pg_opclass
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 2ba2319..ab4cd0f 100644
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
*************** Complete list of usable sgml source file
*** 9,14 ****
--- 9,15 ----
+
diff --git a/doc/src/sgml/ref/alter_default_privileges.sgml b/doc/src/sgml/ref/alter_default_privileges.sgml
index ...72bde1b .
*** a/doc/src/sgml/ref/alter_default_privileges.sgml
--- b/doc/src/sgml/ref/alter_default_privileges.sgml
***************
*** 0 ****
--- 1,197 ----
+
+
+
+
+ ALTER DEFAULT PRIVILEGES
+ 7
+ SQL - Language Statements
+
+
+
+ ALTER DEFAULT PRIVILEGES
+ define default access privileges
+
+
+
+ ALTER DEFAULT PRIVILEGES
+
+
+
+
+ ALTER DEFAULT PRIVILEGES
+ [ FOR { ROLE | USER } role_name [, ...] ]
+ [ IN SCHEMA schema_name [, ...] ]
+ grant_statement
+
+ where grant_statement is:
+ GRANT { { { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON TABLE
+ TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ] } |
+ { { { USAGE | SELECT | UPDATE }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON SEQUENCE
+ TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ] } |
+ { { EXECUTE | ALL [ PRIVILEGES ] }
+ ON FUNCTION
+ TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ] } }
+
+ ALTER DEFAULT PRIVILEGES
+ [ FOR { ROLE | USER } role_name [, ...] ]
+ [ IN SCHEMA schema_name [, ...] ]
+ revoke_statement
+
+ where revoke_statement is:
+ REVOKE [ GRANT OPTION FOR ]
+ { { { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON TABLE
+ FROM { [ GROUP ] rolename | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ] } |
+ { { { USAGE | SELECT | UPDATE }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON SEQUENCE
+ FROM { [ GROUP ] rolename | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ] } |
+ { { EXECUTE | ALL [ PRIVILEGES ] }
+ ON FUNCTION
+ FROM { [ GROUP ] rolename | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ] } }
+
+
+
+
+ Description
+
+
+ ALTER DEFAULT PRIVILEGES command allows you to define privileges for newly
+ created objects.
+
+
+
+ You can change default privileges only for yourself or roles for which
+ you have ADMIN privilege.
+
+
+
+ If you ommit the schema_name parameter,
+ the specified default privileges will be used globaly for whole database.
+
+
+
+ Default privileges are not inherited in any way. So if you for example
+ define database wide default privileges and also the default privileges
+ for new objects in schema public, then upon creating
+ new objects in schema public only those default
+ privileges defined for the schema will apply.
+
+
+
+ See the description of the and
+ commands for more detailed
+ explanation of privilege system and the meaning of the privilege types.
+
+
+
+ Parameters
+
+
+
+ role_name
+
+
+ The name of an existing role for which you have ADMIN privilege.
+
+
+
+
+
+ schema_name
+
+
+ The name of an existing schema.
+
+
+
+
+
+
+
+
+ Examples
+
+
+ Grant select privilege for every tables and views you'll create in schema
+ public to everybody:
+
+
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLE TO public;
+
+
+
+
+ Grant admin> SELECT>, UPDATE>, INSERT> and DELETE>
+ privileges on all tables and views you'll create in the users> schema.
+
+
+ ALTER DEFAULT PRIVILEGES IN SCHEMA users GRANT SELECT, UPDATE, INSERT, DELETE TO admin;
+
+
+
+
+ Give user webuserSELECT> privilege on all new
+ tables, views, and sequences, and EXECUTE> on all new functions in
+ the public schema.
+ Allow the admin to not only select but also
+ change the contents of new tables and views in the public> schema, and grant those permissions
+ to other users. The admin> user will also be allowed to execute new functions,
+ and read new sequences like webuser.
+ Finally, allow admin> to update new sequences.
+ All above only for objects your role will create.
+
+
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLE TO webuser, admin;
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT UPDATE, INSERT, DELETE ON TABLE TO admin WITH GRANT OPTION;
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT EXECUTE ON FUNCTION TO webuser, admin;
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCE TO webuser;
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCE TO admin;
+
+
+
+
+ For new tables and views created by role admin in any
+ schema in current database give role webuser
+ SELECT> privilege (per schema default privileges can override it).
+ And for new functions created in schema public by role
+ admin give role webuser
+ EXECUTE> privilege.
+
+
+ ALTER DEFAULT PRIVILEGES FOR ROLE admin GRANT SELECT ON TABLE TO webuser;
+ ALTER DEFAULT PRIVILEGES FOR ROLE admin IN SCHEMA public GRANT EXECUTE ON FUNCTION TO webuser;
+
+
+
+
+
+ Compatibility
+
+
+ There is no ALTER DEFAULT PRIVILEGES statement in the SQL
+ standard.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index 8aaedb3..8facea9 100644
*** a/doc/src/sgml/ref/grant.sgml
--- b/doc/src/sgml/ref/grant.sgml
*************** PostgreSQL documentation
*** 22,28 ****
GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
! [,...] | ALL [ PRIVILEGES ] }
ON [ TABLE ] table_name [, ...]
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
--- 22,28 ----
GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
! [,...] | ALL [ PRIVILEGES ] | DEFAULT PRIVILEGES }
ON [ TABLE ] table_name [, ...]
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
*************** GRANT { { SELECT | INSERT | UPDATE | REF
*** 32,38 ****
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
GRANT { { USAGE | SELECT | UPDATE }
! [,...] | ALL [ PRIVILEGES ] }
ON SEQUENCE sequence_name [, ...]
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
--- 32,38 ----
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
GRANT { { USAGE | SELECT | UPDATE }
! [,...] | ALL [ PRIVILEGES ] | DEFAULT PRIVILEGES }
ON SEQUENCE sequence_name [, ...]
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
*************** GRANT { USAGE | ALL [ PRIVILEGES ] }
*** 48,54 ****
ON FOREIGN SERVER server_name [, ...]
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
! GRANT { EXECUTE | ALL [ PRIVILEGES ] }
ON FUNCTION function_name ( [ [ argmode ] [ arg_name ] arg_type [, ...] ] ) [, ...]
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
--- 48,54 ----
ON FOREIGN SERVER server_name [, ...]
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
! GRANT { EXECUTE | ALL [ PRIVILEGES ] | DEFAULT PRIVILEGES }
ON FUNCTION function_name ( [ [ argmode ] [ arg_name ] arg_type [, ...] ] ) [, ...]
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
*************** GRANT rol
*** 137,142 ****
--- 137,144 ----
include granting some privileges to PUBLIC.
The default is no public access for tables, columns, schemas, and
tablespaces;
+ For tables, views, functions and sequences the initial default privileges
+ can also be changed using ALTER DEFAULT PRIVILEGES command;
CONNECT> privilege and TEMP> table creation privilege
for databases;
EXECUTE> privilege for functions; and
*************** GRANT rol
*** 344,349 ****
--- 346,368 ----
+
+
+ DEFAULT PRIVILEGES
+
+
+ Replace current privileges with the ones specified using
+ .
+ The WITH GRANT OPTION parameter is not applicable
+ because it is copied from default privileges. You also can't specify
+ TO role_name for above reason.
+ Note: this can actually revoke some
+ privileges because it clears all existing privileges object has and
+ replaces them with the default ones for the schema in which this object
+ resides.
+
+
+
The privileges required by other commands are listed on the
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 8ce451c..48be3d2 100644
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 37,42 ****
--- 37,43 ----
&alterAggregate;
&alterConversion;
&alterDatabase;
+ &alterDefaultPrivileges;
&alterDomain;
&alterForeignDataWrapper;
&alterFunction;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 861cb1d..d80d72b 100644
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
*************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr
*** 37,42 ****
--- 37,43 ----
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
+ pg_default_acls.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ec4aaf0..9c169f3 100644
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
***************
*** 27,32 ****
--- 27,33 ----
#include "catalog/pg_authid.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+ #include "catalog/pg_default_acls.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
***************
*** 51,57 ****
--- 52,61 ----
#include "utils/tqual.h"
+ static void ExecGrantStmt_Defaults(InternalGrant *istmt);
static void ExecGrant_Relation(InternalGrant *grantStmt);
+ static void ExecGrantDefaults_Relation(InternalGrant *grantStmt);
+ static void ExecGrantDefaults_Function(InternalGrant *grantStmt);
static void ExecGrant_Database(InternalGrant *grantStmt);
static void ExecGrant_Fdw(InternalGrant *grantStmt);
static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
*************** static AclMode restrict_and_check_grant(
*** 79,84 ****
--- 83,90 ----
static AclMode pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mask, AclMaskHow how);
+ static Acl *get_default_acl(Oid roleId, Oid nsp_oid, char objtype);
+
#ifdef ACLDEBUG
static void
*************** ExecuteGrantStmt(GrantStmt *stmt)
*** 294,299 ****
--- 300,314 ----
istmt.grant_option = stmt->grant_option;
istmt.behavior = stmt->behavior;
+ /*
+ * GRANT DEFAULT PRIVILEGES
+ */
+ if (stmt->grantees == NIL)
+ {
+ ExecGrantStmt_Defaults(&istmt);
+ return;
+ }
+
/*
* Convert the PrivGrantee list into an Oid list. Note that at this point
* we insert an ACL_ID_PUBLIC into the list if an empty role name is
*************** objectNamesToOids(GrantObjectType objtyp
*** 610,615 ****
--- 625,1091 ----
}
/*
+ * Default ACLs
+ * Implements ALTER DEFAULT PRIVILEGES
+ */
+ void
+ ExecDefaultACLsStmt(DefaultACLsStmt *stmt)
+ {
+ InternalDefaultACLs iacls;
+ GrantStmt *action;
+ ListCell *cell;
+ ListCell *rolecell;
+ ListCell *nspcell;
+ List *rolenames = NIL;
+ List *roleids = NIL;
+ List *nspnames = NIL;
+ List *nspids = NIL;
+ DefElem *drolenames = NULL;
+ DefElem *dnspnames = NULL;
+ Relation rolerel;
+ Relation nsprel;
+ const char *errormsg;
+ AclMode all_privileges;
+
+ Assert(IsA(stmt->action, GrantStmt));
+
+ foreach(cell, stmt->options)
+ {
+ DefElem *defel = (DefElem *) lfirst(cell);
+
+ if (strcmp(defel->defname, "schemas") == 0)
+ {
+ if (dnspnames)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ dnspnames = defel;
+ }
+ else if (strcmp(defel->defname, "roles") == 0)
+ {
+ if (drolenames)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ drolenames = defel;
+ }
+ }
+
+ if (dnspnames)
+ nspnames = (List *) dnspnames->arg;
+ if (drolenames)
+ rolenames = (List *) drolenames->arg;
+
+ action = (GrantStmt *) stmt->action;
+ iacls.is_grant = action->is_grant;
+ iacls.objtype = action->objtype;
+ /* privileges to be filled below */
+ iacls.grantees = NIL; /* filled below */
+ iacls.grant_option = action->grant_option;
+ iacls.behavior = action->behavior;
+
+ /*
+ * Convert the PrivGrantee list into an Oid list. Note that at this point
+ * we insert an ACL_ID_PUBLIC into the list if an empty role name is
+ * detected (which is what the grammar uses if PUBLIC is found), so
+ * downstream there shouldn't be any additional work needed to support
+ * this case.
+ */
+ foreach(cell, action->grantees)
+ {
+ PrivGrantee *grantee = (PrivGrantee *) lfirst(cell);
+
+ if (grantee->rolname == NULL)
+ iacls.grantees = lappend_oid(iacls.grantees, ACL_ID_PUBLIC);
+ else
+ iacls.grantees =
+ lappend_oid(iacls.grantees,
+ get_roleid_checked(grantee->rolname));
+ }
+
+ /*
+ * Convert stmt->privileges, a list of AccessPriv nodes, into an AclMode
+ * bitmask. Note: objtype can't be ACL_OBJECT_COLUMN.
+ */
+ switch (action->objtype)
+ {
+ case ACL_OBJECT_RELATION:
+ all_privileges = ACL_ALL_RIGHTS_RELATION;
+ errormsg = gettext_noop("invalid privilege type %s for relation");
+ break;
+ case ACL_OBJECT_SEQUENCE:
+ all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+ errormsg = gettext_noop("invalid privilege type %s for sequence");
+ break;
+ case ACL_OBJECT_FUNCTION:
+ all_privileges = ACL_ALL_RIGHTS_FUNCTION;
+ errormsg = gettext_noop("invalid privilege type %s for function");
+ break;
+ default:
+ /* keep compiler quiet */
+ all_privileges = ACL_NO_RIGHTS;
+ errormsg = NULL;
+ elog(ERROR, "unrecognized GrantStmt.objtype: %d",
+ (int) action->objtype);
+ }
+
+ /*
+ * Convert stmt->action->privileges, a list of privilege strings,
+ * into an AclMode bitmask.
+ */
+ if (action->privileges == NIL)
+ {
+ iacls.all_privs = true;
+ iacls.privileges = ACL_NO_RIGHTS;
+ }
+ else
+ {
+ iacls.all_privs = false;
+ iacls.privileges = ACL_NO_RIGHTS;
+
+ foreach(cell, action->privileges)
+ {
+ AccessPriv *privnode = (AccessPriv *) lfirst(cell);
+ AclMode priv;
+
+ /*
+ * If it's a column-level specification, we just set it aside in
+ * col_privs for the moment; but insist it's for a relation.
+ */
+ if (privnode->cols)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("column privileges are not valid for default privileges")));
+ }
+
+ if (privnode->priv_name == NULL) /* parser mistake? */
+ elog(ERROR, "AccessPriv node must specify privilege");
+ priv = string_to_privilege(privnode->priv_name);
+
+ if (priv & ~((AclMode) all_privileges))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg(errormsg, privilege_to_string(priv))));
+
+ iacls.privileges |= priv;
+ }
+ }
+
+ /*
+ * Ensure roles and namespaces don't get deleted while
+ * we are adding default privileges for them.
+ */
+ rolerel = heap_open(AuthIdRelationId, ShareLock);
+ nsprel = heap_open(NamespaceRelationId, ShareLock);
+
+ /*
+ * Convert role and namespace names to oids
+ * and check permissions while we are at it.
+ */
+ foreach(rolecell, rolenames)
+ {
+ char *rolename = strVal(lfirst(rolecell));
+ Oid roleid = get_roleid_checked(rolename);
+
+
+ if (!have_createrole_privilege() &&
+ !is_admin_of_role(GetUserId(), roleid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must have admin option on role \"%s\"",
+ rolename)));
+
+ roleids = lappend_oid(roleids, roleid);
+ }
+
+ foreach(nspcell, nspnames)
+ {
+ char *nspname = strVal(lfirst(nspcell));
+ Oid nspid = LookupExplicitNamespace(nspname);
+
+ if (nspid == PG_CATALOG_NAMESPACE || nspid == PG_TOAST_NAMESPACE)
+ elog(ERROR, "can't set default permissions for system catalog");
+
+ nspids = lappend_oid(nspids, nspid);
+ }
+
+ /*
+ * We need to set default acls for current role if no roles were specified
+ * and database wide if no schema was specified.
+ */
+ if (roleids == NIL)
+ roleids = lappend_oid(roleids, GetUserId());
+ if (nspids == NIL)
+ nspids = lappend_oid(nspids, InvalidOid);
+
+ /*
+ * Go through roles and namespaces and update the catalog for them.
+ */
+ foreach(rolecell, roleids)
+ {
+ iacls.roleid = lfirst_oid(rolecell);
+ foreach(nspcell, nspids)
+ {
+ iacls.nspid = lfirst_oid(nspcell);
+ SetDefaultACLs(&iacls);
+ }
+ }
+
+ heap_close(nsprel, ShareLock);
+ heap_close(rolerel, ShareLock);
+ }
+
+
+ /*
+ * Create or update the DefaultACL entry
+ *
+ * Caller is responsible for checking privileges
+ */
+ void
+ SetDefaultACLs(InternalDefaultACLs *iacls)
+ {
+ Form_pg_default_acls pg_default_acls_tuple;
+ char objtype;
+ Datum aclDatum;
+ bool isNew;
+ bool isNull;
+ AclMode this_privileges = iacls->privileges;
+ ScanKeyData key[3];
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ Relation rel;
+ Acl *old_acl;
+ Acl *new_acl;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_default_acls];
+ bool nulls[Natts_pg_default_acls];
+ bool replaces[Natts_pg_default_acls];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ /*
+ * Convert ACL object type to DefaultACLs object type
+ * and handle all_privs option
+ */
+ switch (iacls->objtype)
+ {
+ case ACL_OBJECT_RELATION:
+ objtype = DEFACLOBJ_RELATION;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_RELATION;
+ break;
+
+ case ACL_OBJECT_FUNCTION:
+ objtype = DEFACLOBJ_FUNCTION;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_FUNCTION;
+ break;
+
+ case ACL_OBJECT_SEQUENCE:
+ objtype = DEFACLOBJ_SEQUENCE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+ break;
+
+ default:
+ elog(ERROR, "unrecognized objtype: %d",
+ (int) iacls->objtype);
+ objtype = 0; /* keep compiler quiet */
+ break;
+ }
+
+ /* Search for existing row for our object type in catalog */
+ ScanKeyInit(&key[0],
+ Anum_pg_default_acls_defaclrole,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(iacls->roleid));
+
+ ScanKeyInit(&key[1],
+ Anum_pg_default_acls_defaclnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(iacls->nspid));
+
+ ScanKeyInit(&key[2],
+ Anum_pg_default_acls_defaclgrantobjtype,
+ BTEqualStrategyNumber, F_CHAREQ,
+ CharGetDatum(objtype));
+
+ rel = heap_open(DefaultAclsRelationId, RowExclusiveLock);
+
+ scan = heap_beginscan(rel, SnapshotNow, 3, key);
+
+ tuple = heap_getnext(scan, ForwardScanDirection);
+ if (HeapTupleIsValid(tuple))
+ {
+ pg_default_acls_tuple = (Form_pg_default_acls) GETSTRUCT(tuple);
+ aclDatum = heap_getattr(tuple, Anum_pg_default_acls_defacldefacllist,
+ RelationGetDescr(rel), &isNull);
+ isNew = false;
+ }
+ else
+ {
+ isNull = true;
+ isNew = true;
+ }
+
+ if (isNull)
+ old_acl = acldefault(iacls->objtype, iacls->roleid);
+ else
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ /*
+ * Generate new ACL.
+ *
+ * We need the members of both old and new ACLs so we can correct
+ * the shared dependency information.
+ */
+ new_acl = merge_acl_with_grant(old_acl,
+ iacls->is_grant,
+ iacls->grant_option,
+ iacls->behavior,
+ iacls->grantees,
+ this_privileges,
+ GetUserId(),
+ iacls->roleid);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ if (isNew)
+ {
+ ObjectAddress myself,
+ referenced;
+
+ values[Anum_pg_default_acls_defaclrole - 1] = ObjectIdGetDatum(iacls->roleid);
+ values[Anum_pg_default_acls_defaclnamespace - 1] = ObjectIdGetDatum(iacls->nspid);
+ values[Anum_pg_default_acls_defaclgrantobjtype - 1] = CharGetDatum(objtype);
+ values[Anum_pg_default_acls_defacldefacllist - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_form_tuple(rel->rd_att, values, nulls);
+ simple_heap_insert(rel, newtuple);
+
+ myself.classId = DefaultAclsRelationId;
+ myself.objectId = HeapTupleGetOid(newtuple);
+ myself.objectSubId = 0;
+
+ referenced.classId = AuthIdRelationId;
+ referenced.objectId = iacls->roleid;
+ referenced.objectSubId = 0;
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on namespace */
+ if (OidIsValid(iacls->nspid))
+ {
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = iacls->nspid;
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+ }
+ else
+ {
+ values[Anum_pg_default_acls_defacldefacllist - 1] = PointerGetDatum(new_acl);
+ replaces[Anum_pg_default_acls_defacldefacllist - 1] = true;
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
+ values, nulls, replaces);
+ simple_heap_update(rel, &newtuple->t_self, newtuple);
+ }
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(rel, newtuple);
+
+ /*
+ * Update the shared dependency ACL info
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ updateAclDependencies(DefaultAclsRelationId, HeapTupleGetOid(newtuple), 0,
+ GetUserId(), iacls->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ pfree(new_acl);
+
+ heap_endscan(scan);
+ heap_close(rel, RowExclusiveLock);
+ }
+
+
+ /*
+ * Remove DefaultACLs tuple.
+ *
+ * Used for dependency removal.
+ */
+ void
+ RemoveDefaultACLsById(Oid nsdaOid)
+ {
+ Relation rel;
+ ScanKeyData key[1];
+ HeapScanDesc scan;
+ HeapTuple tuple;
+
+ rel = heap_open(DefaultAclsRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(nsdaOid));
+
+ scan = heap_beginscan(rel, SnapshotNow, 1, key);
+
+ tuple = heap_getnext(scan, ForwardScanDirection);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "could not find tuple for default acls %u",
+ nsdaOid);
+
+ simple_heap_delete(rel, &tuple->t_self);
+
+ heap_endscan(scan);
+ heap_close(rel, RowExclusiveLock);
+ }
+
+
+ /*
+ * Remove DefaultACLs tuples for given role.
+ *
+ * Used by DropRole.
+ */
+ void
+ RemoveDefaultACLsByRoleId(Oid roleid)
+ {
+ Relation rel;
+ ScanKeyData key[1];
+ HeapScanDesc scan;
+ HeapTuple tuple;
+
+ rel = heap_open(DefaultAclsRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_default_acls_defaclrole,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(roleid));
+
+ scan = heap_beginscan(rel, SnapshotNow, 1, key);
+
+ while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
+ simple_heap_delete(rel, &tuple->t_self);
+
+ heap_endscan(scan);
+ heap_close(rel, RowExclusiveLock);
+ }
+
+
+ /*
* expand_col_privileges
*
* OR the specified privilege(s) into per-column array entries for each
*************** ExecGrant_Tablespace(InternalGrant *istm
*** 1988,1993 ****
--- 2464,2701 ----
}
+ /*
+ * ExecGrantStmt_Defaults
+ *
+ * "Internal" entrypoint for reseting privileges to schema DEFAULT
+ */
+ static void
+ ExecGrantStmt_Defaults(InternalGrant *istmt)
+ {
+ switch (istmt->objtype)
+ {
+ case ACL_OBJECT_RELATION:
+ case ACL_OBJECT_SEQUENCE:
+ ExecGrantDefaults_Relation(istmt);
+ break;
+ case ACL_OBJECT_FUNCTION:
+ ExecGrantDefaults_Function(istmt);
+ break;
+ default:
+ elog(ERROR, "unrecognized GrantStmt.objtype: %d",
+ (int) istmt->objtype);
+ }
+ }
+
+ static void
+ ExecGrantDefaults_Relation(InternalGrant *grantStmt)
+ {
+ Relation relation;
+ ListCell *cell;
+
+ relation = heap_open(RelationRelationId, RowExclusiveLock);
+
+ foreach(cell, grantStmt->objects)
+ {
+ Oid relOid = lfirst_oid(cell);
+ Oid ownerId;
+ Datum aclDatum;
+ bool isNull;
+ Form_pg_class pg_class_tuple;
+ Acl *old_acl = NULL;
+ Acl *new_acl;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_class];
+ bool nulls[Natts_pg_class];
+ bool replaces[Natts_pg_class];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+ char objtype;
+
+ tuple = SearchSysCache(RELOID,
+ ObjectIdGetDatum(relOid),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relOid);
+ pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
+
+ /* Only owner can replace acls */
+ pg_class_ownercheck(relOid, GetUserId());
+
+ ownerId = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
+
+ aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
+ &isNull);
+ if (!isNull)
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ /* Check relkind and convert it to DefaultACLs object */
+ switch (pg_class_tuple->relkind)
+ {
+ case RELKIND_SEQUENCE:
+ objtype = DEFACLOBJ_SEQUENCE;
+ break;
+ case RELKIND_RELATION:
+ objtype = DEFACLOBJ_RELATION;
+ break;
+ default: /* parser error ? */
+ elog(ERROR, "relation can't have DEFAULT PRIVILEGES");
+ }
+
+ new_acl = pg_namespace_object_default_acl(pg_class_tuple->relnamespace,
+ objtype, ownerId);
+
+ if (new_acl == NULL)
+ elog(ERROR, "no DEFAULT PRIVILEGES for relation");
+
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* Replace ACLs */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_class_relacl - 1] = true;
+ values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
+ values, nulls, replaces);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(relation, newtuple);
+
+ /* Remove old ACL dependencies */
+ updateAclDependencies(RelationRelationId, relOid, 0,
+ pg_class_tuple->relowner, false,
+ noldmembers, oldmembers,
+ 0, NULL);
+
+ /* Add new ACL dependencies */
+ updateAclDependencies(RelationRelationId, relOid, 0,
+ pg_class_tuple->relowner, true,
+ 0, NULL,
+ nnewmembers, newmembers);
+
+ pfree(new_acl);
+
+ ReleaseSysCache(tuple);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+ }
+
+
+ static void
+ ExecGrantDefaults_Function(InternalGrant *grantStmt)
+ {
+ Relation relation;
+ ListCell *cell;
+
+ relation = heap_open(ProcedureRelationId, RowExclusiveLock);
+
+ foreach(cell, grantStmt->objects)
+ {
+ Oid funcId = lfirst_oid(cell);
+ Oid ownerId;
+ Form_pg_proc pg_proc_tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *old_acl = NULL;
+ Acl *new_acl;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_class];
+ bool nulls[Natts_pg_class];
+ bool replaces[Natts_pg_class];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(funcId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for function %u", funcId);
+
+ pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple);
+
+ /* Only owner can replace acls */
+ pg_proc_ownercheck(funcId, GetUserId());
+
+ ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
+
+ /* Get the default acls */
+ new_acl = pg_namespace_object_default_acl(pg_proc_tuple->pronamespace,
+ DEFACLOBJ_FUNCTION,
+ ownerId);
+
+ if (new_acl == NULL)
+ elog(ERROR, "no DEFAULT PRIVILEGES for function");
+
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* Get original acls */
+ aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
+ &isNull);
+
+ if (!isNull)
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+
+ /* Replace ACLs */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_proc_proacl - 1] = true;
+ values[Anum_pg_proc_proacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(relation, newtuple);
+
+ /* Remove old ACL dependencies */
+ updateAclDependencies(ProcedureRelationId, funcId, 0,
+ pg_proc_tuple->proowner, false,
+ noldmembers, oldmembers,
+ 0, NULL);
+
+ /* Add new ACL dependencies */
+ updateAclDependencies(ProcedureRelationId, funcId, 0,
+ pg_proc_tuple->proowner, true,
+ 0, NULL,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+ }
+
+
static AclMode
string_to_privilege(const char *privname)
{
*************** pg_conversion_ownercheck(Oid conv_oid, O
*** 3532,3534 ****
--- 4240,4342 ----
return has_privs_of_role(roleid, ownerId);
}
+
+ /*
+ * Search for default permissions for given role, namespace and objecttype
+ */
+ Acl *
+ get_default_acl(Oid roleId, Oid nsp_oid, char objtype)
+ {
+ ScanKeyData key[3];
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ Relation rel;
+ Datum aclDatum;
+ bool isNull;
+ Acl *result;
+
+ ScanKeyInit(&key[0],
+ Anum_pg_default_acls_defaclrole,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(roleId));
+
+ ScanKeyInit(&key[1],
+ Anum_pg_default_acls_defaclnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(nsp_oid));
+
+ ScanKeyInit(&key[2],
+ Anum_pg_default_acls_defaclgrantobjtype,
+ BTEqualStrategyNumber, F_CHAREQ,
+ CharGetDatum(objtype));
+
+ rel = heap_open(DefaultAclsRelationId, AccessShareLock);
+
+ scan = heap_beginscan(rel, SnapshotNow, 3, key);
+
+ tuple = heap_getnext(scan, ForwardScanDirection);
+
+ if (HeapTupleIsValid(tuple))
+ {
+ aclDatum = heap_getattr(tuple, Anum_pg_default_acls_defacldefacllist,
+ RelationGetDescr(rel), &isNull);
+
+ if (!isNull)
+ result = DatumGetAclPCopy(aclDatum);
+ }
+ else
+ {
+ isNull = true;
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+
+ if (isNull)
+ return NULL;
+
+ return result;
+ }
+
+ /*
+ * Get default permissions for newly created object inside given schema
+ */
+ Acl *
+ pg_namespace_object_default_acl(Oid nsp_oid, char objtype, Oid ownerId)
+ {
+ Acl *result;
+ GrantObjectType aclobjtype;
+
+ /*
+ * Use NULL value for object acls if it is created in system schema
+ * or if it does not have default permissions support.
+ */
+ if (nsp_oid == PG_CATALOG_NAMESPACE || nsp_oid == PG_TOAST_NAMESPACE)
+ return NULL;
+
+ switch (objtype)
+ {
+ case DEFACLOBJ_RELATION:
+ aclobjtype = ACL_OBJECT_RELATION;
+ break;
+
+ case DEFACLOBJ_FUNCTION:
+ aclobjtype = ACL_OBJECT_FUNCTION;
+ break;
+
+ case DEFACLOBJ_SEQUENCE:
+ aclobjtype = ACL_OBJECT_SEQUENCE;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ result = get_default_acl(ownerId, nsp_oid, objtype);
+
+ /* If nothing found for given namespace, try global default permissions */
+ if (result == NULL)
+ result = get_default_acl(ownerId, InvalidOid, objtype);
+
+ return result;
+ }
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index dd2b29a..6a73485 100644
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 32,37 ****
--- 32,38 ----
#include "catalog/pg_conversion.h"
#include "catalog/pg_conversion_fn.h"
#include "catalog/pg_database.h"
+ #include "catalog/pg_default_acls.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
***************
*** 70,75 ****
--- 71,77 ----
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "utils/acl.h"
/*
*************** static const Oid object_classes[MAX_OCLA
*** 146,152 ****
TableSpaceRelationId, /* OCLASS_TBLSPACE */
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
! UserMappingRelationId /* OCLASS_USER_MAPPING */
};
--- 148,155 ----
TableSpaceRelationId, /* OCLASS_TBLSPACE */
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
! UserMappingRelationId, /* OCLASS_USER_MAPPING */
! DefaultAclsRelationId /* OCLASS_DEFACLS */
};
*************** static bool object_address_present_add_f
*** 178,183 ****
--- 181,187 ----
ObjectAddresses *addrs);
static void getRelationDescription(StringInfo buffer, Oid relid);
static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
+ static void getDefaultACLDescription(StringInfo buffer, char objtype);
/*
*************** doDeletion(const ObjectAddress *object)
*** 1135,1140 ****
--- 1139,1148 ----
RemoveUserMappingById(object->objectId);
break;
+ case OCLASS_DEFACLS:
+ RemoveDefaultACLsById(object->objectId);
+ break;
+
default:
elog(ERROR, "unrecognized object class: %u",
object->classId);
*************** getObjectClass(const ObjectAddress *obje
*** 2054,2059 ****
--- 2062,2071 ----
case UserMappingRelationId:
Assert(object->objectSubId == 0);
return OCLASS_USER_MAPPING;
+
+ case DefaultAclsRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_DEFACLS;
}
/* shouldn't get here */
*************** getObjectDescription(const ObjectAddress
*** 2596,2601 ****
--- 2608,2656 ----
break;
}
+ case OCLASS_DEFACLS:
+ {
+ Relation rel;
+ ScanKeyData key[1];
+ HeapScanDesc scan;
+ HeapTuple tup;
+ Form_pg_default_acls defacls;
+
+ rel = heap_open(DefaultAclsRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ scan = heap_beginscan(rel, SnapshotNow, 1, key);
+
+ tup = heap_getnext(scan, ForwardScanDirection);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "could not find tuple for default acls %u",
+ object->objectId);
+
+ defacls = (Form_pg_default_acls) GETSTRUCT(tup);
+
+ appendStringInfo(&buffer,
+ _("default acls for role %s on new "),
+ GetUserNameFromId(defacls->defaclrole));
+
+ getDefaultACLDescription(&buffer, defacls->defaclobjtype);
+
+ if (OidIsValid(defacls->defaclnamespace))
+ {
+ appendStringInfo(&buffer,
+ _(" in namespace %s"),
+ get_namespace_name(defacls->defaclnamespace));
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+ break;
+ }
+
default:
appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId,
*************** getOpFamilyDescription(StringInfo buffer
*** 2714,2716 ****
--- 2769,2796 ----
ReleaseSysCache(amTup);
ReleaseSysCache(opfTup);
}
+
+
+ /*
+ * subroutine for getObjectDescription: describe DefaultACLs object type
+ */
+ static void
+ getDefaultACLDescription(StringInfo buffer, char objtype)
+ {
+ switch (objtype)
+ {
+ case DEFACLOBJ_RELATION:
+ appendStringInfo(buffer, _("relation"));
+ break;
+ case DEFACLOBJ_FUNCTION:
+ appendStringInfo(buffer, _("function"));
+ break;
+ case DEFACLOBJ_SEQUENCE:
+ appendStringInfo(buffer, _("sequence"));
+ break;
+ default:
+ /* shouldn't get here */
+ appendStringInfo(buffer, _("unknown"));
+ break;
+ }
+ }
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index ae406ae..812e287 100644
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 41,46 ****
--- 41,47 ----
#include "catalog/indexing.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_constraint.h"
+ #include "catalog/pg_default_acls.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_statistic.h"
***************
*** 67,72 ****
--- 68,74 ----
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
+ #include "utils/acl.h"
static void AddNewRelationTuple(Relation pg_class_desc,
*************** AddNewAttributeTuples(Oid new_rel_oid,
*** 635,642 ****
* Caller has already opened and locked pg_class.
* Tuple data is taken from new_rel_desc->rd_rel, except for the
* variable-width fields which are not present in a cached reldesc.
! * We always initialize relacl to NULL (i.e., default permissions),
! * and reloptions is set to the passed-in text array (if any).
* --------------------------------
*/
void
--- 637,644 ----
* Caller has already opened and locked pg_class.
* Tuple data is taken from new_rel_desc->rd_rel, except for the
* variable-width fields which are not present in a cached reldesc.
! * Initial permissions are obtained from pg_default_acls
! * (if applicable) and reloptions is set to the passed-in text array (if any).
* --------------------------------
*/
void
*************** InsertPgClassTuple(Relation pg_class_des
*** 648,653 ****
--- 650,658 ----
Form_pg_class rd_rel = new_rel_desc->rd_rel;
Datum values[Natts_pg_class];
bool nulls[Natts_pg_class];
+ bool isNull;
+ Acl *relacl;
+ char aclobjtype;
HeapTuple tup;
/* This is a tad tedious, but way cleaner than what we used to do... */
*************** InsertPgClassTuple(Relation pg_class_des
*** 677,684 ****
values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
! /* start out with empty permissions */
! nulls[Anum_pg_class_relacl - 1] = true;
if (reloptions != (Datum) 0)
values[Anum_pg_class_reloptions - 1] = reloptions;
else
--- 682,715 ----
values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
!
! /* get default permissions */
! switch (rd_rel->relkind)
! {
! case RELKIND_RELATION:
! case RELKIND_VIEW:
! isNull = false;
! aclobjtype = DEFACLOBJ_RELATION;
! break;
! case RELKIND_SEQUENCE:
! isNull = false;
! aclobjtype = DEFACLOBJ_SEQUENCE;
! break;
! default:
! isNull = true;
! break;
! }
!
! if (!isNull)
! relacl = pg_namespace_object_default_acl(rd_rel->relnamespace, aclobjtype, rd_rel->relowner);
! else
! relacl = NULL;
!
! if (relacl != NULL)
! values[Anum_pg_class_relacl - 1] = PointerGetDatum(relacl);
! else
! nulls[Anum_pg_class_relacl - 1] = true;
!
if (reloptions != (Datum) 0)
values[Anum_pg_class_reloptions - 1] = reloptions;
else
*************** InsertPgClassTuple(Relation pg_class_des
*** 697,702 ****
--- 728,748 ----
CatalogUpdateIndexes(pg_class_desc, tup);
+ if (relacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ /* Update the shared dependency ACL info */
+ nnewmembers = aclmembers(relacl, &newmembers);
+ updateAclDependencies(RelationRelationId, new_rel_oid, 0,
+ rd_rel->relowner, true,
+ 0, NULL,
+ nnewmembers, newmembers);
+
+ pfree(relacl);
+ }
+
heap_freetuple(tup);
}
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 1a06530..a307472 100644
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
***************
*** 18,23 ****
--- 18,24 ----
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+ #include "catalog/pg_default_acls.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
*************** ProcedureCreate(const char *procedureNam
*** 95,100 ****
--- 96,102 ----
bool nulls[Natts_pg_proc];
Datum values[Natts_pg_proc];
bool replaces[Natts_pg_proc];
+ Acl *proacl;
Oid relid;
NameData procname;
TupleDesc tupDesc;
*************** ProcedureCreate(const char *procedureNam
*** 330,337 ****
values[Anum_pg_proc_proconfig - 1] = proconfig;
else
nulls[Anum_pg_proc_proconfig - 1] = true;
! /* start out with empty permissions */
! nulls[Anum_pg_proc_proacl - 1] = true;
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
tupDesc = RelationGetDescr(rel);
--- 332,344 ----
values[Anum_pg_proc_proconfig - 1] = proconfig;
else
nulls[Anum_pg_proc_proconfig - 1] = true;
!
! /* get default permissions */
! proacl = pg_namespace_object_default_acl(procNamespace, DEFACLOBJ_FUNCTION, GetUserId());
! if (proacl != NULL)
! values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl);
! else
! nulls[Anum_pg_proc_proacl - 1] = true;
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
tupDesc = RelationGetDescr(rel);
*************** ProcedureCreate(const char *procedureNam
*** 539,544 ****
--- 546,566 ----
/* dependency on owner */
recordDependencyOnOwner(ProcedureRelationId, retval, GetUserId());
+ if (proacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ /* Update the shared dependency ACL info */
+ nnewmembers = aclmembers(proacl, &newmembers);
+ updateAclDependencies(ProcedureRelationId, retval, 0,
+ GetUserId(), true,
+ 0, NULL,
+ nnewmembers, newmembers);
+
+ pfree(proacl);
+ }
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index cd04053..a9c586e 100644
*** a/src/backend/catalog/pg_shdepend.c
--- b/src/backend/catalog/pg_shdepend.c
***************
*** 17,28 ****
--- 17,30 ----
#include "access/genam.h"
#include "access/heapam.h"
#include "access/xact.h"
+ #include "access/sysattr.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+ #include "catalog/pg_default_acls.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
***************
*** 40,45 ****
--- 42,48 ----
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
*************** static void storeObjectDescription(Strin
*** 74,79 ****
--- 77,84 ----
int count);
static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel);
+ static void shdepDropObjACLs(Oid classid, Oid objid, Oid roleid);
+ static void shdepDropDefaultACLs(Oid objid, Oid roleid);
/*
* recordSharedDependencyOn
*************** shdepDropOwned(List *roleids, DropBehavi
*** 1180,1186 ****
while ((tuple = systable_getnext(scan)) != NULL)
{
Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
- InternalGrant istmt;
ObjectAddress obj;
/* We only operate on objects in the current database */
--- 1185,1190 ----
*************** shdepDropOwned(List *roleids, DropBehavi
*** 1195,1236 ****
elog(ERROR, "unexpected dependency type");
break;
case SHARED_DEPENDENCY_ACL:
! switch (sdepForm->classid)
! {
! case RelationRelationId:
! /* it's OK to use RELATION for a sequence */
! istmt.objtype = ACL_OBJECT_RELATION;
! break;
! case DatabaseRelationId:
! istmt.objtype = ACL_OBJECT_DATABASE;
! break;
! case ProcedureRelationId:
! istmt.objtype = ACL_OBJECT_FUNCTION;
! break;
! case LanguageRelationId:
! istmt.objtype = ACL_OBJECT_LANGUAGE;
! break;
! case NamespaceRelationId:
! istmt.objtype = ACL_OBJECT_NAMESPACE;
! break;
! case TableSpaceRelationId:
! istmt.objtype = ACL_OBJECT_TABLESPACE;
! break;
! default:
! elog(ERROR, "unexpected object type %d",
! sdepForm->classid);
! break;
! }
! istmt.is_grant = false;
! istmt.objects = list_make1_oid(sdepForm->objid);
! istmt.all_privs = true;
! istmt.privileges = ACL_NO_RIGHTS;
! istmt.col_privs = NIL;
! istmt.grantees = list_make1_oid(roleid);
! istmt.grant_option = false;
! istmt.behavior = DROP_CASCADE;
!
! ExecGrantStmt_oids(&istmt);
break;
case SHARED_DEPENDENCY_OWNER:
/* Save it for deletion below */
--- 1199,1208 ----
elog(ERROR, "unexpected dependency type");
break;
case SHARED_DEPENDENCY_ACL:
! if (sdepForm->classid == DefaultAclsRelationId)
! shdepDropDefaultACLs(sdepForm->objid, roleid);
! else
! shdepDropObjACLs(sdepForm->classid, sdepForm->objid, roleid);
break;
case SHARED_DEPENDENCY_OWNER:
/* Save it for deletion below */
*************** shdepDropOwned(List *roleids, DropBehavi
*** 1253,1258 ****
--- 1225,1353 ----
free_object_addresses(deleteobjs);
}
+ /*
+ * shdepDropObjACLs
+ *
+ * Does the actual ACLs (Grant) removal for shdepDropOwned
+ */
+ void
+ shdepDropObjACLs(Oid classid, Oid objid, Oid roleid)
+ {
+ InternalGrant istmt;
+
+ switch (classid)
+ {
+ case RelationRelationId:
+ /* it's OK to use RELATION for a sequence */
+ istmt.objtype = ACL_OBJECT_RELATION;
+ break;
+ case DatabaseRelationId:
+ istmt.objtype = ACL_OBJECT_DATABASE;
+ break;
+ case ProcedureRelationId:
+ istmt.objtype = ACL_OBJECT_FUNCTION;
+ break;
+ case LanguageRelationId:
+ istmt.objtype = ACL_OBJECT_LANGUAGE;
+ break;
+ case NamespaceRelationId:
+ istmt.objtype = ACL_OBJECT_NAMESPACE;
+ break;
+ case TableSpaceRelationId:
+ istmt.objtype = ACL_OBJECT_TABLESPACE;
+ break;
+ default:
+ elog(ERROR, "unexpected object type %d", classid);
+ break;
+ }
+ istmt.is_grant = false;
+ istmt.objects = list_make1_oid(objid);
+ istmt.all_privs = true;
+ istmt.privileges = ACL_NO_RIGHTS;
+ istmt.col_privs = NIL;
+ istmt.grantees = list_make1_oid(roleid);
+ istmt.grant_option = false;
+ istmt.behavior = DROP_CASCADE;
+
+ ExecGrantStmt_oids(&istmt);
+ }
+
+
+ /*
+ * shdepDropDefaultACLs
+ *
+ * Drops roles ACLs from DefaultAcls tuple, used by shdepDropOwned
+ */
+ void
+ shdepDropDefaultACLs(Oid objid, Oid roleid)
+ {
+ InternalDefaultACLs iacls;
+ Form_pg_default_acls pg_default_acls_tuple;
+ Relation rel;
+ ScanKeyData key[1];
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ AclResult aclresult;
+
+ /* first fetch info needed by SetDefaultACLs */
+ rel = heap_open(DefaultAclsRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objid));
+
+ scan = heap_beginscan(rel, SnapshotNow, 1, key);
+
+ tuple = heap_getnext(scan, ForwardScanDirection);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "could not find tuple for default acls %u",
+ objid);
+
+ pg_default_acls_tuple = (Form_pg_default_acls) GETSTRUCT(tuple);
+
+ iacls.roleid = pg_default_acls_tuple->defaclrole;
+ iacls.nspid = pg_default_acls_tuple->defaclnamespace;
+
+ switch (pg_default_acls_tuple->defaclobjtype)
+ {
+ case DEFACLOBJ_RELATION:
+ iacls.objtype = ACL_OBJECT_RELATION;
+ break;
+ case DEFACLOBJ_FUNCTION:
+ iacls.objtype = ACL_OBJECT_FUNCTION;
+ break;
+ case ACL_OBJECT_SEQUENCE:
+ iacls.objtype = ACL_OBJECT_SEQUENCE;
+ break;
+ default:
+ /* Shouldn't get here */
+ elog(ERROR, "unexpected object type %d", pg_default_acls_tuple->defaclobjtype);
+ break;
+ }
+
+ heap_endscan(scan);
+ heap_close(rel, RowExclusiveLock);
+
+ /* Check privileges */
+ aclresult = pg_namespace_aclcheck(iacls.nspid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(iacls.nspid));
+
+ iacls.is_grant = false;
+ iacls.all_privs = true;
+ iacls.privileges = ACL_NO_RIGHTS;
+ iacls.grantees = list_make1_oid(roleid);
+ iacls.grant_option = false;
+ iacls.behavior = DROP_CASCADE;
+
+ /* Do it */
+ SetDefaultACLs(&iacls);
+ }
+
+
/*
* shdepReassignOwned
*
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index fa260c4..e43016e 100644
*** a/src/backend/commands/user.c
--- b/src/backend/commands/user.c
*************** static void DelRoleMems(const char *role
*** 44,72 ****
bool admin_opt);
- /* Check if current user has createrole privileges */
- static bool
- have_createrole_privilege(void)
- {
- bool result = false;
- HeapTuple utup;
-
- /* Superusers can always do everything */
- if (superuser())
- return true;
-
- utup = SearchSysCache(AUTHOID,
- ObjectIdGetDatum(GetUserId()),
- 0, 0, 0);
- if (HeapTupleIsValid(utup))
- {
- result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
- ReleaseSysCache(utup);
- }
- return result;
- }
-
-
/*
* CREATE ROLE
*/
--- 44,49 ----
*************** DropRole(DropRoleStmt *stmt)
*** 939,944 ****
--- 916,926 ----
systable_endscan(sscan);
/*
+ * Remove DefaultACLs for this role.
+ */
+ RemoveDefaultACLsByRoleId(roleid);
+
+ /*
* Remove any comments on this role.
*/
DeleteSharedComments(roleid, AuthIdRelationId);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 25961d1..ad4e444 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateSchemaStmt(CreateSchemaStmt *
*** 3217,3222 ****
--- 3217,3233 ----
return newnode;
}
+ static DefaultACLsStmt *
+ _copyDefaultACLsStmt(DefaultACLsStmt *from)
+ {
+ DefaultACLsStmt *newnode = makeNode(DefaultACLsStmt);
+
+ COPY_NODE_FIELD(options);
+ COPY_NODE_FIELD(action);
+
+ return newnode;
+ }
+
static CreateConversionStmt *
_copyCreateConversionStmt(CreateConversionStmt *from)
{
*************** copyObject(void *from)
*** 3959,3964 ****
--- 3970,3978 ----
case T_CreateSchemaStmt:
retval = _copyCreateSchemaStmt(from);
break;
+ case T_DefaultACLsStmt:
+ retval = _copyDefaultACLsStmt(from);
+ break;
case T_CreateConversionStmt:
retval = _copyCreateConversionStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 142aa8f..b6601b5 100644
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateSchemaStmt(CreateSchemaStmt
*** 1763,1768 ****
--- 1763,1777 ----
}
static bool
+ _equalDefaultACLsStmt(DefaultACLsStmt *a, DefaultACLsStmt *b)
+ {
+ COMPARE_NODE_FIELD(options);
+ COMPARE_NODE_FIELD(action);
+
+ return true;
+ }
+
+ static bool
_equalCreateConversionStmt(CreateConversionStmt *a, CreateConversionStmt *b)
{
COMPARE_NODE_FIELD(conversion_name);
*************** equal(void *a, void *b)
*** 2738,2743 ****
--- 2747,2755 ----
case T_CreateSchemaStmt:
retval = _equalCreateSchemaStmt(a, b);
break;
+ case T_DefaultACLsStmt:
+ retval = _equalDefaultACLsStmt(a, b);
+ break;
case T_CreateConversionStmt:
retval = _equalCreateConversionStmt(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 22570db..fe91a2e 100644
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static TypeName *TableFuncTypeName(List
*** 179,184 ****
--- 179,185 ----
ResTarget *target;
struct PrivTarget *privtarget;
AccessPriv *accesspriv;
+ GrantObjectType grantobjecttype;
InsertStmt *istmt;
VariableSetStmt *vsetstmt;
*************** static TypeName *TableFuncTypeName(List
*** 196,202 ****
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
! CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
--- 197,203 ----
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
! CreatedbStmt DeclareCursorStmt DefaultACLsStmt DefaultACLsAction DefineStmt DeleteStmt DiscardStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
*************** static TypeName *TableFuncTypeName(List
*** 268,273 ****
--- 269,275 ----
%type privilege
%type privileges privilege_list
%type privilege_target
+ %type defacl_privilege_target
%type function_with_argtypes
%type function_with_argtypes_list
*************** static TypeName *TableFuncTypeName(List
*** 424,429 ****
--- 426,433 ----
%type opt_existing_window_name
%type opt_frame_clause frame_extent frame_bound
+ %type OptDefACLOption
+ %type OptDefACLOptionList
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
*************** stmt :
*** 669,674 ****
--- 673,679 ----
| CreatedbStmt
| DeallocateStmt
| DeclareCursorStmt
+ | DefaultACLsStmt
| DefineStmt
| DeleteStmt
| DiscardStmt
*************** schema_stmt:
*** 1110,1115 ****
--- 1115,1204 ----
;
+
+ /*****************************************************************************
+ *
+ * DEFAULT PRIVILEGES for newly created objects in schema
+ *
+ *****************************************************************************/
+
+ DefaultACLsStmt:
+ ALTER DEFAULT PRIVILEGES OptDefACLOptionList DefaultACLsAction
+ {
+ DefaultACLsStmt *n = makeNode(DefaultACLsStmt);
+ n->options = $4;
+ n->action = $5;
+ $$ = (Node*)n;
+ }
+ ;
+
+ OptDefACLOptionList:
+ OptDefACLOptionList OptDefACLOption { $$ = lappend($1, $2); }
+ | /* EMPTY */ { $$ = NIL; }
+ ;
+
+ OptDefACLOption:
+ IN_P SCHEMA name_list
+ {
+ $$ = makeDefElem("schemas", (Node *)$3);
+ }
+ | FOR ROLE name_list
+ {
+ $$ = makeDefElem("roles", (Node *)$3);
+ }
+ | FOR USER name_list
+ {
+ $$ = makeDefElem("roles", (Node *)$3);
+ }
+ ;
+
+ DefaultACLsAction:
+ GRANT privileges ON defacl_privilege_target TO grantee_list
+ opt_grant_grant_option
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = true;
+ n->privileges = $2;
+ n->objtype = $4;
+ n->objects = NIL;
+ n->grantees = $6;
+ n->grant_option = $7;
+ $$ = (Node*)n;
+ }
+ | REVOKE privileges ON defacl_privilege_target
+ FROM grantee_list opt_drop_behavior
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = false;
+ n->grant_option = false;
+ n->privileges = $2;
+ n->objtype = $4;
+ n->objects = NIL;
+ n->grantees = $6;
+ n->behavior = $7;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR privileges ON defacl_privilege_target
+ FROM grantee_list opt_drop_behavior
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = false;
+ n->grant_option = true;
+ n->privileges = $5;
+ n->objtype = $7;
+ n->objects = NIL;
+ n->grantees = $9;
+ n->behavior = $10;
+ $$ = (Node *)n;
+ }
+ ;
+
+ defacl_privilege_target:
+ TABLE { $$ = ACL_OBJECT_RELATION; }
+ | FUNCTION { $$ = ACL_OBJECT_FUNCTION; }
+ | SEQUENCE { $$ = ACL_OBJECT_SEQUENCE; }
+ ;
+
/*****************************************************************************
*
* Set PG internal variable
*************** GrantStmt: GRANT privileges ON privilege
*** 4248,4253 ****
--- 4337,4353 ----
n->grant_option = $7;
$$ = (Node*)n;
}
+ | GRANT DEFAULT PRIVILEGES ON privilege_target
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = true;
+ n->privileges = NIL;
+ n->objtype = ($5)->objtype;
+ n->objects = ($5)->objs;
+ n->grantees = NIL;
+ n->grant_option = FALSE;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index ee4ed8b..209888a 100644
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** check_xact_readonly(Node *parsetree)
*** 180,185 ****
--- 180,186 ----
case T_AlterOpFamilyStmt:
case T_RuleStmt:
case T_CreateSchemaStmt:
+ case T_DefaultACLsStmt:
case T_CreateSeqStmt:
case T_CreateStmt:
case T_CreateTableSpaceStmt:
*************** ProcessUtility(Node *parsetree,
*** 406,411 ****
--- 407,416 ----
queryString);
break;
+ case T_DefaultACLsStmt:
+ ExecDefaultACLsStmt((DefaultACLsStmt *) parsetree);
+ break;
+
case T_CreateStmt:
{
List *stmts;
*************** CreateCommandTag(Node *parsetree)
*** 1365,1370 ****
--- 1370,1379 ----
tag = "CREATE SCHEMA";
break;
+ case T_DefaultACLsStmt:
+ tag = "ALTER DEFAULT PRIVILEGES";
+ break;
+
case T_CreateStmt:
tag = "CREATE TABLE";
break;
*************** GetCommandLogLevel(Node *parsetree)
*** 2134,2139 ****
--- 2143,2152 ----
lev = LOGSTMT_DDL;
break;
+ case T_DefaultACLsStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CreateStmt:
lev = LOGSTMT_DDL;
break;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index e19e96d..e7538db 100644
*** a/src/backend/utils/adt/acl.c
--- b/src/backend/utils/adt/acl.c
*************** is_admin_of_role(Oid member, Oid role)
*** 4499,4504 ****
--- 4499,4527 ----
}
+ /* Check if user has createrole privileges */
+ bool
+ have_createrole_privilege(void)
+ {
+ bool result = false;
+ HeapTuple utup;
+
+ /* Superusers can always do everything */
+ if (superuser())
+ return true;
+
+ utup = SearchSysCache(AUTHOID,
+ ObjectIdGetDatum(GetUserId()),
+ 0, 0, 0);
+ if (HeapTupleIsValid(utup))
+ {
+ result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
+ ReleaseSysCache(utup);
+ }
+ return result;
+ }
+
+
/* does what it says ... */
static int
count_one_bits(AclMode mask)
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index dbd3619..e00e2e2 100644
*** a/src/backend/utils/cache/syscache.c
--- b/src/backend/utils/cache/syscache.c
***************
*** 31,36 ****
--- 31,37 ----
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+ #include "catalog/pg_default_acls.h"
#include "catalog/pg_enum.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index a2f6761..ab37b16 100644
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 146,151 ****
--- 146,152 ----
OCLASS_FDW, /* pg_foreign_data_wrapper */
OCLASS_FOREIGN_SERVER, /* pg_foreign_server */
OCLASS_USER_MAPPING, /* pg_user_mapping */
+ OCLASS_DEFACLS, /* pg_default_acls */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 81e18a1..fcbe2f9 100644
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
*************** DECLARE_UNIQUE_INDEX(pg_user_mapping_oid
*** 267,272 ****
--- 267,277 ----
DECLARE_UNIQUE_INDEX(pg_user_mapping_user_server_index, 175, on pg_user_mapping using btree(umuser oid_ops, umserver oid_ops));
#define UserMappingUserServerIndexId 175
+ DECLARE_UNIQUE_INDEX(pg_default_acls_role_nsp_obj_index, 626, on pg_default_acls using btree(defaclrole oid_ops, defaclnamespace oid_ops, defaclobjtype char_ops));
+ #define DefaultAclsNspObjIndexId 626
+ DECLARE_UNIQUE_INDEX(pg_default_acls_oid_index, 627, on pg_default_acls using btree(oid oid_ops));
+ #define DefaultAclsOidIndexId 627
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
diff --git a/src/include/catalog/pg_default_acls.h b/src/include/catalog/pg_default_acls.h
index ...9b4019f .
*** a/src/include/catalog/pg_default_acls.h
--- b/src/include/catalog/pg_default_acls.h
***************
*** 0 ****
--- 1,79 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_default_acls.h
+ * definition of default ACLs for new objects
+ * Specified by schema and grantable object type
+ *
+ *
+ * Copyright (c) 2009, PostgreSQL Global Development Group
+ *
+ * $Id$
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef PG_DEFAULT_ACLS_H
+ #define PG_DEFAULT_ACLS_H
+
+ #include "catalog/genbki.h"
+
+ /* ----------------
+ * pg_default_acls definition. cpp turns this into
+ * typedef struct FormData_pg_default_acls
+ * ----------------
+ */
+ #define DefaultAclsRelationId 1248
+
+ CATALOG(pg_default_acls,1248) /*BKI_BOOTSTRAP*/
+ {
+ Oid defaclrole; /* OID of the role these ACLs apply to */
+ Oid defaclnamespace; /* OID of namespace these ACLs apply to */
+ char defaclobjtype; /* see DEFACL_OBJECT_xxx constants below */
+
+ /*
+ * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
+ */
+
+ aclitem defacllist[1]; /* Permissions to be applied at CREATE time */
+ } FormData_pg_default_acls;
+
+ /* Size of fixed part of pg_default_acls tuples, not counting var-length fields */
+ #define DEFAULT_ACLS_TUPLE_SIZE \
+ (offsetof(FormData_pg_default_acls,relfrozenxid) + sizeof(TransactionId))
+
+ /* ----------------
+ * Form_pg_default_acls corresponds to a pointer to a tuple with
+ * the format of pg_default_acls relation.
+ * ----------------
+ */
+ typedef FormData_pg_default_acls *Form_pg_default_acls;
+
+ /* ----------------
+ * compiler constants for pg_default_acls
+ * ----------------
+ */
+
+ #define Natts_pg_default_acls 4
+ #define Anum_pg_default_acls_defaclrole 1
+ #define Anum_pg_default_acls_defaclnamespace 2
+ #define Anum_pg_default_acls_defaclgrantobjtype 3
+ #define Anum_pg_default_acls_defacldefacllist 4
+
+ /* ----------------
+ * pg_default_acls has no initial contents
+ * ----------------
+ */
+
+ /*
+ * Types of objects which the user is allowed to specify default
+ * permissions on through pg_default_acls.
+ */
+
+ #define DEFACLOBJ_RELATION 'r' /* table, view */
+ #define DEFACLOBJ_FUNCTION 'f' /* function */
+ #define DEFACLOBJ_SEQUENCE 'S' /* sequence */
+
+ #endif /* PG_DEFAULT_ACLS_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 19e28e7..611db76 100644
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 338,343 ****
--- 338,344 ----
T_CreateUserMappingStmt,
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
+ T_DefaultACLsStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*************** typedef enum NodeTag
*** 376,381 ****
--- 377,384 ----
T_XmlSerialize,
T_WithClause,
T_CommonTableExpr,
+ T_DefaultACLsObject,
+ T_DefaultACLsPriv,
/*
* TAGS FOR RANDOM OTHER STUFF
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8d5c058..84e19d7 100644
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct GrantRoleStmt
*** 1260,1265 ****
--- 1260,1277 ----
} GrantRoleStmt;
/* ----------------------
+ * Alter DEFAULT PRIVILEGES
+ * ----------------------
+ */
+
+ typedef struct DefaultACLsStmt
+ {
+ NodeTag type;
+ List *options;
+ Node *action;
+ } DefaultACLsStmt;
+
+ /* ----------------------
* Copy Statement
*
* We support "COPY relation FROM file", "COPY relation TO file", and
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index bde8727..7cb5fb1 100644
*** a/src/include/utils/acl.h
--- b/src/include/utils/acl.h
*************** typedef struct
*** 219,224 ****
--- 219,243 ----
DropBehavior behavior;
} InternalGrant;
+
+ /*
+ * Internal format for DefaultACLs.
+ * Needs to be exported because it's needed for dropping owned objects.
+ */
+ typedef struct
+ {
+ Oid roleid; /* Role for which we are changing DefaultACLs */
+ Oid nspid; /* Namespace this DefaultACLs aplies to - can be invalidOid for database wide */
+ bool is_grant; /* True means we are granting, false means we are revoking */
+ GrantObjectType objtype; /* Object type affected by these DefaultACLs */
+ bool all_privs; /* See comment for InternalGrant */
+ AclMode privileges; /* See comment for InternalGrant */
+ List *grantees; /* Users to/from who we are Granting/Revoking privileges in these DefaultACLs */
+ bool grant_option; /* Indicates that WITH GRAN OPTION was specified */
+ DropBehavior behavior; /* CASCADE or RESTRICT */
+ } InternalDefaultACLs;
+
+
/*
* routines used internally
*/
*************** extern bool is_member_of_role(Oid member
*** 238,243 ****
--- 257,263 ----
extern bool is_member_of_role_nosuper(Oid member, Oid role);
extern bool is_admin_of_role(Oid member, Oid role);
extern void check_is_member_of_role(Oid member, Oid role);
+ extern bool have_createrole_privilege(void);
extern void select_best_grantor(Oid roleId, AclMode privileges,
const Acl *acl, Oid ownerId,
*************** extern Datum hash_aclitem(PG_FUNCTION_AR
*** 263,268 ****
--- 283,293 ----
extern void ExecuteGrantStmt(GrantStmt *stmt);
extern void ExecGrantStmt_oids(InternalGrant *istmt);
+ extern void ExecDefaultACLsStmt(DefaultACLsStmt *stmt);
+ extern void SetDefaultACLs(InternalDefaultACLs *iacls);
+ extern void RemoveDefaultACLsById(Oid nsdaOid);
+ extern void RemoveDefaultACLsByRoleId(Oid roleid);
+
extern AclMode pg_attribute_aclmask(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mask, AclMaskHow how);
extern AclMode pg_class_aclmask(Oid table_oid, Oid roleid,
*************** extern bool pg_ts_dict_ownercheck(Oid di
*** 317,320 ****
--- 342,347 ----
extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid);
extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid);
+ extern Acl *pg_namespace_object_default_acl(Oid nsp_oid, char objtype, Oid ownerId);
+
#endif /* ACL_H */
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 809b656..a068517 100644
*** a/src/test/regress/expected/privileges.out
--- b/src/test/regress/expected/privileges.out
*************** SELECT has_sequence_privilege('x_seq', '
*** 836,843 ****
--- 836,957 ----
t
(1 row)
+ -- Default ACLs
+ \c -
+ CREATE SCHEMA regressns;
+ GRANT USAGE ON SCHEMA regressns TO regressgroup1, regressuser2;
+ GRANT ALL ON SCHEMA regressns TO regressuser1;
+ -- bad input
+ ALTER DEFAULT PRIVILEGES IN SCHEMA regressns GRANT USAGE ON FUNCTION TO regressgroup1;
+ ERROR: invalid privilege type USAGE for function
+ -- try different possible syntaxes from simple ones to more complex ones
+ ALTER DEFAULT PRIVILEGES IN SCHEMA regressns GRANT SELECT ON TABLE TO public;
+ CREATE TABLE regressns.acltest1 (one int, two int, three int);
+ SELECT has_table_privilege('regressuser1', 'regressns.acltest1', 'INSERT'); -- false
+ has_table_privilege
+ ---------------------
+ f
+ (1 row)
+
+ SELECT has_table_privilege('regressuser1', 'regressns.acltest1', 'SELECT'); -- true
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
+ ALTER DEFAULT PRIVILEGES IN SCHEMA regressns GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE TO regressuser2;
+ CREATE TABLE regressns.acltest2 (one int, two int, three int);
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest2', 'DELETE'); -- true
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest2', 'INSERT'); -- false
+ has_table_privilege
+ ---------------------
+ f
+ (1 row)
+
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest2', 'SELECT'); -- true (as member of regressgroup1)
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
+ ALTER DEFAULT PRIVILEGES IN SCHEMA regressns GRANT INSERT ON TABLE TO regressgroup1;
+ CREATE TABLE regressns.acltest3 (foo text);
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest3', 'INSERT'); -- true
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
+ ALTER DEFAULT PRIVILEGES IN SCHEMA regressns REVOKE DELETE ON TABLE FROM regressuser2;
+ -- supress implicit sequence creation NOTICE
+ SET client_min_messages TO 'error';
+ CREATE TABLE regressns.acltest4 (bar serial);
+ RESET client_min_messages;
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest4', 'DELETE'); -- false
+ has_table_privilege
+ ---------------------
+ f
+ (1 row)
+
+ SET SESSION AUTHORIZATION regressuser2;
+ SELECT nextval(pg_get_serial_sequence('regressns.acltest4', 'bar')::regclass); -- error
+ ERROR: permission denied for sequence acltest4_bar_seq
+ RESET SESSION AUTHORIZATION;
+ -- set default privileges for other user
+ ALTER DEFAULT PRIVILEGES FOR USER regressuser1 IN SCHEMA regressns GRANT ALL ON SEQUENCE TO regressgroup1, regressuser2;
+ ALTER DEFAULT PRIVILEGES FOR ROLE regressuser1 IN SCHEMA regressns GRANT ALL PRIVILEGES ON TABLE TO regressuser2 WITH GRANT OPTION;
+ ALTER DEFAULT PRIVILEGES FOR ROLE regressuser1 IN SCHEMA regressns GRANT EXECUTE ON FUNCTION TO regressgroup1;
+ SET SESSION AUTHORIZATION regressuser1;
+ ALTER DEFAULT PRIVILEGES FOR USER regressuser2 IN SCHEMA regressns GRANT SELECT ON TABLE TO regressgroup1; -- error
+ ERROR: must have admin option on role "regressuser2"
+ -- supress implicit sequence creation NOTICE
+ SET client_min_messages TO 'error';
+ CREATE TABLE regressns.acltest5 (a serial, b text);
+ RESET client_min_messages;
+ SELECT has_table_privilege('regressgroup1', 'regressns.acltest5', 'INSERT'); -- false
+ has_table_privilege
+ ---------------------
+ f
+ (1 row)
+
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest5', 'INSERT'); -- true
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
+ CREATE FUNCTION regressns.testfunc1() RETURNS int AS 'select 1;' LANGUAGE sql;
+ SELECT has_function_privilege('regressgroup1', 'regressns.testfunc1()', 'EXECUTE'); -- true
+ has_function_privilege
+ ------------------------
+ t
+ (1 row)
+
+ CREATE VIEW regressns.acltest6 AS SELECT * FROM regressns.acltest4;
+ SET SESSION AUTHORIZATION regressuser2;
+ SELECT nextval(pg_get_serial_sequence('regressns.acltest5', 'a')::regclass); -- 1
+ nextval
+ ---------
+ 1
+ (1 row)
+
+ GRANT SELECT ON regressns.acltest6 TO regressuser3;
+ SELECT has_table_privilege('regressuser3', 'regressns.acltest6', 'SELECT'); -- true
+ has_table_privilege
+ ---------------------
+ t
+ (1 row)
+
-- clean up
\c
+ SET client_min_messages TO 'error';
+ DROP SCHEMA regressns CASCADE;
+ RESET client_min_messages;
drop sequence x_seq;
DROP FUNCTION testfunc2(int);
DROP FUNCTION testfunc4(boolean);
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 7213192..ec1e704 100644
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** SELECT relname, relhasindex
*** 95,100 ****
--- 95,101 ----
pg_constraint | t
pg_conversion | t
pg_database | t
+ pg_default_acls | t
pg_depend | t
pg_description | t
pg_enum | t
*************** SELECT relname, relhasindex
*** 151,157 ****
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (140 rows)
--
-- another sanity check: every system catalog that has OIDs should have
--- 152,158 ----
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (141 rows)
--
-- another sanity check: every system catalog that has OIDs should have
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 917e8e5..d96dc9c 100644
*** a/src/test/regress/sql/privileges.sql
--- b/src/test/regress/sql/privileges.sql
*************** SET SESSION AUTHORIZATION regressuser2;
*** 484,493 ****
--- 484,562 ----
SELECT has_sequence_privilege('x_seq', 'USAGE');
+
+ -- Default ACLs
+
+ \c -
+
+ CREATE SCHEMA regressns;
+ GRANT USAGE ON SCHEMA regressns TO regressgroup1, regressuser2;
+ GRANT ALL ON SCHEMA regressns TO regressuser1;
+
+ -- bad input
+
+ ALTER DEFAULT PRIVILEGES IN SCHEMA regressns GRANT USAGE ON FUNCTION TO regressgroup1;
+
+ -- try different possible syntaxes from simple ones to more complex ones
+
+ ALTER DEFAULT PRIVILEGES IN SCHEMA regressns GRANT SELECT ON TABLE TO public;
+ CREATE TABLE regressns.acltest1 (one int, two int, three int);
+ SELECT has_table_privilege('regressuser1', 'regressns.acltest1', 'INSERT'); -- false
+ SELECT has_table_privilege('regressuser1', 'regressns.acltest1', 'SELECT'); -- true
+
+ ALTER DEFAULT PRIVILEGES IN SCHEMA regressns GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE TO regressuser2;
+ CREATE TABLE regressns.acltest2 (one int, two int, three int);
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest2', 'DELETE'); -- true
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest2', 'INSERT'); -- false
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest2', 'SELECT'); -- true (as member of regressgroup1)
+
+ ALTER DEFAULT PRIVILEGES IN SCHEMA regressns GRANT INSERT ON TABLE TO regressgroup1;
+ CREATE TABLE regressns.acltest3 (foo text);
+ SELECT has_table_privilege('regressuser4', 'regressns.acltest3', 'INSERT'); -- true
+
+ ALTER DEFAULT PRIVILEGES IN SCHEMA regressns REVOKE DELETE ON TABLE FROM regressuser2;
+ -- supress implicit sequence creation NOTICE
+ SET client_min_messages TO 'error';
+ CREATE TABLE regressns.acltest4 (bar serial);
+ RESET client_min_messages;
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest4', 'DELETE'); -- false
+ SET SESSION AUTHORIZATION regressuser2;
+ SELECT nextval(pg_get_serial_sequence('regressns.acltest4', 'bar')::regclass); -- error
+ RESET SESSION AUTHORIZATION;
+
+ -- set default privileges for other user
+ ALTER DEFAULT PRIVILEGES FOR USER regressuser1 IN SCHEMA regressns GRANT ALL ON SEQUENCE TO regressgroup1, regressuser2;
+ ALTER DEFAULT PRIVILEGES FOR ROLE regressuser1 IN SCHEMA regressns GRANT ALL PRIVILEGES ON TABLE TO regressuser2 WITH GRANT OPTION;
+ ALTER DEFAULT PRIVILEGES FOR ROLE regressuser1 IN SCHEMA regressns GRANT EXECUTE ON FUNCTION TO regressgroup1;
+
+ SET SESSION AUTHORIZATION regressuser1;
+ ALTER DEFAULT PRIVILEGES FOR USER regressuser2 IN SCHEMA regressns GRANT SELECT ON TABLE TO regressgroup1; -- error
+
+ -- supress implicit sequence creation NOTICE
+ SET client_min_messages TO 'error';
+ CREATE TABLE regressns.acltest5 (a serial, b text);
+ RESET client_min_messages;
+ SELECT has_table_privilege('regressgroup1', 'regressns.acltest5', 'INSERT'); -- false
+ SELECT has_table_privilege('regressuser2', 'regressns.acltest5', 'INSERT'); -- true
+
+ CREATE FUNCTION regressns.testfunc1() RETURNS int AS 'select 1;' LANGUAGE sql;
+ SELECT has_function_privilege('regressgroup1', 'regressns.testfunc1()', 'EXECUTE'); -- true
+
+ CREATE VIEW regressns.acltest6 AS SELECT * FROM regressns.acltest4;
+ SET SESSION AUTHORIZATION regressuser2;
+ SELECT nextval(pg_get_serial_sequence('regressns.acltest5', 'a')::regclass); -- 1
+ GRANT SELECT ON regressns.acltest6 TO regressuser3;
+ SELECT has_table_privilege('regressuser3', 'regressns.acltest6', 'SELECT'); -- true
+
+
-- clean up
\c
+ SET client_min_messages TO 'error';
+ DROP SCHEMA regressns CASCADE;
+ RESET client_min_messages;
+
drop sequence x_seq;
DROP FUNCTION testfunc2(int);