diff options
Diffstat (limited to 'src/backend/catalog')
| -rw-r--r-- | src/backend/catalog/Makefile | 3 | ||||
| -rw-r--r-- | src/backend/catalog/aclchk.c | 721 | ||||
| -rw-r--r-- | src/backend/catalog/dependency.c | 78 | ||||
| -rw-r--r-- | src/backend/catalog/heap.c | 87 | ||||
| -rw-r--r-- | src/backend/catalog/index.c | 3 | ||||
| -rw-r--r-- | src/backend/catalog/pg_proc.c | 28 | ||||
| -rw-r--r-- | src/backend/catalog/pg_shdepend.c | 52 | ||||
| -rw-r--r-- | src/backend/catalog/toasting.c | 3 |
8 files changed, 913 insertions, 62 deletions
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index e4414ed2bf5..53784e9c54b 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -2,7 +2,7 @@ # # Makefile for backend/catalog # -# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.71 2009/08/26 22:24:43 petere Exp $ +# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.72 2009/10/05 19:24:34 tgl Exp $ # #------------------------------------------------------------------------- @@ -37,6 +37,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ 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_acl.h \ toasting.h indexing.h \ ) diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index bb15e78d1c9..b06e587a1b1 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.154 2009/06/11 14:48:54 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.155 2009/10/05 19:24:35 tgl Exp $ * * NOTES * See acl.h. @@ -27,6 +27,7 @@ #include "catalog/pg_authid.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" +#include "catalog/pg_default_acl.h" #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_language.h" @@ -51,6 +52,51 @@ #include "utils/tqual.h" +/* + * The information about one Grant/Revoke statement, in internal format: object + * and grantees names have been turned into Oids, the privilege list is an + * AclMode bitmask. If 'privileges' is ACL_NO_RIGHTS (the 0 value) and + * all_privs is true, 'privileges' will be internally set to the right kind of + * ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the + * InternalGrant struct!) + * + * Note: 'all_privs' and 'privileges' represent object-level privileges only. + * There might also be column-level privilege specifications, which are + * represented in col_privs (this is a list of untransformed AccessPriv nodes). + * Column privileges are only valid for objtype ACL_OBJECT_RELATION. + */ +typedef struct +{ + bool is_grant; + GrantObjectType objtype; + List *objects; + bool all_privs; + AclMode privileges; + List *col_privs; + List *grantees; + bool grant_option; + DropBehavior behavior; +} InternalGrant; + +/* + * Internal format used by ALTER DEFAULT PRIVILEGES. + */ +typedef struct +{ + Oid roleid; /* owning role */ + Oid nspid; /* namespace, or InvalidOid if none */ + /* remaining fields are same as in InternalGrant: */ + bool is_grant; + GrantObjectType objtype; + bool all_privs; + AclMode privileges; + List *grantees; + bool grant_option; + DropBehavior behavior; +} InternalDefaultACL; + + +static void ExecGrantStmt_oids(InternalGrant *istmt); static void ExecGrant_Relation(InternalGrant *grantStmt); static void ExecGrant_Database(InternalGrant *grantStmt); static void ExecGrant_Fdw(InternalGrant *grantStmt); @@ -60,6 +106,9 @@ static void ExecGrant_Language(InternalGrant *grantStmt); static void ExecGrant_Namespace(InternalGrant *grantStmt); static void ExecGrant_Tablespace(InternalGrant *grantStmt); +static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames); +static void SetDefaultACL(InternalDefaultACL *iacls); + static List *objectNamesToOids(GrantObjectType objtype, List *objnames); static void expand_col_privileges(List *colnames, Oid table_oid, AclMode this_privileges, @@ -361,11 +410,11 @@ ExecuteGrantStmt(GrantStmt *stmt) errormsg = gettext_noop("invalid privilege type %s for foreign server"); break; default: + elog(ERROR, "unrecognized GrantStmt.objtype: %d", + (int) stmt->objtype); /* keep compiler quiet */ all_privileges = ACL_NO_RIGHTS; errormsg = NULL; - elog(ERROR, "unrecognized GrantStmt.objtype: %d", - (int) stmt->objtype); } if (stmt->privileges == NIL) @@ -421,11 +470,9 @@ ExecuteGrantStmt(GrantStmt *stmt) /* * ExecGrantStmt_oids * - * "Internal" entrypoint for granting and revoking privileges. This is - * exported for pg_shdepend.c to use in revoking privileges when dropping - * a role. + * Internal entry point for granting and revoking privileges. */ -void +static void ExecGrantStmt_oids(InternalGrant *istmt) { switch (istmt->objtype) @@ -610,6 +657,563 @@ objectNamesToOids(GrantObjectType objtype, List *objnames) } /* + * ALTER DEFAULT PRIVILEGES statement + */ +void +ExecAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *stmt) +{ + GrantStmt *action = stmt->action; + InternalDefaultACL iacls; + ListCell *cell; + List *rolenames = NIL; + List *nspnames = NIL; + DefElem *drolenames = NULL; + DefElem *dnspnames = NULL; + AclMode all_privileges; + const char *errormsg; + + /* Deconstruct the "options" part of the statement */ + 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; + } + else + elog(ERROR, "option \"%s\" not recognized", defel->defname); + } + + if (dnspnames) + nspnames = (List *) dnspnames->arg; + if (drolenames) + rolenames = (List *) drolenames->arg; + + /* Prepare the InternalDefaultACL representation of the statement */ + /* roleid to be filled below */ + /* nspid to be filled in SetDefaultACLsInSchemas */ + iacls.is_grant = action->is_grant; + iacls.objtype = action->objtype; + /* all_privs to be filled below */ + /* 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 action->privileges, a list of privilege strings, + * into an AclMode bitmask. + */ + 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: + elog(ERROR, "unrecognized GrantStmt.objtype: %d", + (int) action->objtype); + /* keep compiler quiet */ + all_privileges = ACL_NO_RIGHTS; + errormsg = NULL; + } + + if (action->privileges == NIL) + { + iacls.all_privs = true; + + /* + * will be turned into ACL_ALL_RIGHTS_* by the internal routines + * depending on the object type + */ + 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 (privnode->cols) + ereport(ERROR, + (errcode(ERRCODE_INVALID_GRANT_OPERATION), + errmsg("default privileges cannot be set for columns"))); + + 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; + } + } + + if (rolenames == NIL) + { + /* Set permissions for myself */ + iacls.roleid = GetUserId(); + + SetDefaultACLsInSchemas(&iacls, nspnames); + } + else + { + /* Look up the role OIDs and do permissions checks */ + ListCell *rolecell; + + foreach(rolecell, rolenames) + { + char *rolename = strVal(lfirst(rolecell)); + + iacls.roleid = get_roleid_checked(rolename); + + /* + * We insist that calling user be a member of each target role. + * If he has that, he could become that role anyway via SET ROLE, + * so FOR ROLE is just a syntactic convenience and doesn't give + * any special privileges. + */ + check_is_member_of_role(GetUserId(), iacls.roleid); + + SetDefaultACLsInSchemas(&iacls, nspnames); + } + } +} + +/* + * Process ALTER DEFAULT PRIVILEGES for a list of target schemas + * + * All fields of *iacls except nspid were filled already + */ +static void +SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames) +{ + if (nspnames == NIL) + { + /* Set database-wide permissions if no schema was specified */ + iacls->nspid = InvalidOid; + + SetDefaultACL(iacls); + } + else + { + /* Look up the schema OIDs and do permissions checks */ + ListCell *nspcell; + + foreach(nspcell, nspnames) + { + char *nspname = strVal(lfirst(nspcell)); + AclResult aclresult; + + /* + * Normally we'd use LookupCreationNamespace here, but it's + * important to do the permissions check against the target role + * not the calling user, so write it out in full. We require + * CREATE privileges, since without CREATE you won't be able to do + * anything using the default privs anyway. + */ + iacls->nspid = GetSysCacheOid(NAMESPACENAME, + CStringGetDatum(nspname), + 0, 0, 0); + if (!OidIsValid(iacls->nspid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_SCHEMA), + errmsg("schema \"%s\" does not exist", nspname))); + + aclresult = pg_namespace_aclcheck(iacls->nspid, iacls->roleid, + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + nspname); + + SetDefaultACL(iacls); + } + } +} + + +/* + * Create or update a pg_default_acl entry + */ +static void +SetDefaultACL(InternalDefaultACL *iacls) +{ + AclMode this_privileges = iacls->privileges; + char objtype; + Relation rel; + HeapTuple tuple; + bool isNew; + Acl *old_acl; + Acl *new_acl; + HeapTuple newtuple; + Datum values[Natts_pg_default_acl]; + bool nulls[Natts_pg_default_acl]; + bool replaces[Natts_pg_default_acl]; + int noldmembers; + int nnewmembers; + Oid *oldmembers; + Oid *newmembers; + + rel = heap_open(DefaultAclRelationId, RowExclusiveLock); + + /* + * Convert ACL object type to pg_default_acl 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_SEQUENCE: + objtype = DEFACLOBJ_SEQUENCE; + if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS) + this_privileges = ACL_ALL_RIGHTS_SEQUENCE; + break; + + case ACL_OBJECT_FUNCTION: + objtype = DEFACLOBJ_FUNCTION; + if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS) + this_privileges = ACL_ALL_RIGHTS_FUNCTION; + break; + + default: + elog(ERROR, "unrecognized objtype: %d", + (int) iacls->objtype); + objtype = 0; /* keep compiler quiet */ + break; + } + + /* Search for existing row for this object type in catalog */ + tuple = SearchSysCache(DEFACLROLENSPOBJ, + ObjectIdGetDatum(iacls->roleid), + ObjectIdGetDatum(iacls->nspid), + CharGetDatum(objtype), + 0); + + if (HeapTupleIsValid(tuple)) + { + Datum aclDatum; + bool isNull; + + aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple, + Anum_pg_default_acl_defaclacl, + &isNull); + if (!isNull) + old_acl = DatumGetAclPCopy(aclDatum); + else + old_acl = NULL; + isNew = false; + } + else + { + old_acl = NULL; + isNew = true; + } + + if (old_acl == NULL) + { + /* + * If we are creating a global entry, start with the hard-wired + * defaults and modify as per command. Otherwise, start with an empty + * ACL and modify that. This is needed because global entries + * replace the hard-wired defaults, while others do not. + */ + if (!OidIsValid(iacls->nspid)) + old_acl = acldefault(iacls->objtype, iacls->roleid); + else + old_acl = make_empty_acl(); + } + + /* + * We need the members of both old and new ACLs so we can correct the + * shared dependency information. Collect data before + * merge_acl_with_grant throws away old_acl. + */ + noldmembers = aclmembers(old_acl, &oldmembers); + + /* + * Generate new ACL. Grantor of rights is always the same as the + * target role. + */ + new_acl = merge_acl_with_grant(old_acl, + iacls->is_grant, + iacls->grant_option, + iacls->behavior, + iacls->grantees, + this_privileges, + iacls->roleid, + 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) + { + values[Anum_pg_default_acl_defaclrole - 1] = ObjectIdGetDatum(iacls->roleid); + values[Anum_pg_default_acl_defaclnamespace - 1] = ObjectIdGetDatum(iacls->nspid); + values[Anum_pg_default_acl_defaclobjtype - 1] = CharGetDatum(objtype); + values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl); + + newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); + simple_heap_insert(rel, newtuple); + } + else + { + values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl); + replaces[Anum_pg_default_acl_defaclacl - 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); + + /* these dependencies don't change in an update */ + if (isNew) + { + /* dependency on role */ + recordDependencyOnOwner(DefaultAclRelationId, + HeapTupleGetOid(newtuple), + iacls->roleid); + + /* dependency on namespace */ + if (OidIsValid(iacls->nspid)) + { + ObjectAddress myself, + referenced; + + myself.classId = DefaultAclRelationId; + myself.objectId = HeapTupleGetOid(newtuple); + myself.objectSubId = 0; + + referenced.classId = NamespaceRelationId; + referenced.objectId = iacls->nspid; + referenced.objectSubId = 0; + + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + } + } + + /* + * Update the shared dependency ACL info + */ + nnewmembers = aclmembers(new_acl, &newmembers); + + updateAclDependencies(DefaultAclRelationId, HeapTupleGetOid(newtuple), 0, + iacls->roleid, iacls->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + + pfree(new_acl); + + if (HeapTupleIsValid(tuple)) + ReleaseSysCache(tuple); + + heap_close(rel, RowExclusiveLock); +} + + +/* + * RemoveRoleFromObjectACL + * + * Used by shdepDropOwned to remove mentions of a role in ACLs + */ +void +RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid) +{ + if (classid == DefaultAclRelationId) + { + InternalDefaultACL iacls; + Form_pg_default_acl pg_default_acl_tuple; + Relation rel; + ScanKeyData skey[1]; + SysScanDesc scan; + HeapTuple tuple; + + /* first fetch info needed by SetDefaultACL */ + rel = heap_open(DefaultAclRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objid)); + + scan = systable_beginscan(rel, DefaultAclOidIndexId, true, + SnapshotNow, 1, skey); + + tuple = systable_getnext(scan); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "could not find tuple for default ACL %u", objid); + + pg_default_acl_tuple = (Form_pg_default_acl) GETSTRUCT(tuple); + + iacls.roleid = pg_default_acl_tuple->defaclrole; + iacls.nspid = pg_default_acl_tuple->defaclnamespace; + + switch (pg_default_acl_tuple->defaclobjtype) + { + case DEFACLOBJ_RELATION: + iacls.objtype = ACL_OBJECT_RELATION; + break; + case ACL_OBJECT_SEQUENCE: + iacls.objtype = ACL_OBJECT_SEQUENCE; + break; + case DEFACLOBJ_FUNCTION: + iacls.objtype = ACL_OBJECT_FUNCTION; + break; + default: + /* Shouldn't get here */ + elog(ERROR, "unexpected default ACL type %d", + pg_default_acl_tuple->defaclobjtype); + break; + } + + systable_endscan(scan); + heap_close(rel, AccessShareLock); + + 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 */ + SetDefaultACL(&iacls); + } + else + { + 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 class %u", 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); + } +} + + +/* + * Remove a pg_default_acl entry + */ +void +RemoveDefaultACLById(Oid defaclOid) +{ + Relation rel; + ScanKeyData skey[1]; + SysScanDesc scan; + HeapTuple tuple; + + rel = heap_open(DefaultAclRelationId, RowExclusiveLock); + + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(defaclOid)); + + scan = systable_beginscan(rel, DefaultAclOidIndexId, true, + SnapshotNow, 1, skey); + + tuple = systable_getnext(scan); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "could not find tuple for default ACL %u", defaclOid); + + simple_heap_delete(rel, &tuple->t_self); + + systable_endscan(scan); + heap_close(rel, RowExclusiveLock); +} + + +/* * expand_col_privileges * * OR the specified privilege(s) into per-column array entries for each @@ -3532,3 +4136,106 @@ pg_conversion_ownercheck(Oid conv_oid, Oid roleid) return has_privs_of_role(roleid, ownerId); } + +/* + * Fetch pg_default_acl entry for given role, namespace and object type + * (object type must be given in pg_default_acl's encoding). + * Returns NULL if no such entry. + */ +static Acl * +get_default_acl_internal(Oid roleId, Oid nsp_oid, char objtype) +{ + Acl *result = NULL; + HeapTuple tuple; + + tuple = SearchSysCache(DEFACLROLENSPOBJ, + ObjectIdGetDatum(roleId), + ObjectIdGetDatum(nsp_oid), + CharGetDatum(objtype), + 0); + + if (HeapTupleIsValid(tuple)) + { + Datum aclDatum; + bool isNull; + + aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple, + Anum_pg_default_acl_defaclacl, + &isNull); + if (!isNull) + result = DatumGetAclPCopy(aclDatum); + ReleaseSysCache(tuple); + } + + return result; +} + +/* + * Get default permissions for newly created object within given schema + * + * Returns NULL if built-in system defaults should be used + */ +Acl * +get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid) +{ + Acl *result; + Acl *glob_acl; + Acl *schema_acl; + Acl *def_acl; + char defaclobjtype; + + /* + * Use NULL during bootstrap, since pg_default_acl probably isn't there + * yet. + */ + if (IsBootstrapProcessingMode()) + return NULL; + + /* Check if object type is supported in pg_default_acl */ + switch (objtype) + { + case ACL_OBJECT_RELATION: + defaclobjtype = DEFACLOBJ_RELATION; + break; + + case ACL_OBJECT_SEQUENCE: + defaclobjtype = DEFACLOBJ_SEQUENCE; + break; + + case ACL_OBJECT_FUNCTION: + defaclobjtype = DEFACLOBJ_FUNCTION; + break; + + default: + return NULL; + } + + /* Look up the relevant pg_default_acl entries */ + glob_acl = get_default_acl_internal(ownerId, InvalidOid, defaclobjtype); + schema_acl = get_default_acl_internal(ownerId, nsp_oid, defaclobjtype); + + /* Quick out if neither entry exists */ + if (glob_acl == NULL && schema_acl == NULL) + return NULL; + + /* We need to know the hard-wired default value, too */ + def_acl = acldefault(objtype, ownerId); + + /* If there's no global entry, substitute the hard-wired default */ + if (glob_acl == NULL) + glob_acl = def_acl; + + /* Merge in any per-schema privileges */ + result = aclmerge(glob_acl, schema_acl, ownerId); + + /* + * For efficiency, we want to return NULL if the result equals default. + * This requires sorting both arrays to get an accurate comparison. + */ + aclitemsort(result); + aclitemsort(def_acl); + if (aclequal(result, def_acl)) + result = NULL; + + return result; +} diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index c3d87ef59eb..8a07c69c7e8 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.91 2009/09/22 15:46:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.92 2009/10/05 19:24:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,6 +32,7 @@ #include "catalog/pg_conversion.h" #include "catalog/pg_conversion_fn.h" #include "catalog/pg_database.h" +#include "catalog/pg_default_acl.h" #include "catalog/pg_depend.h" #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" @@ -64,6 +65,7 @@ #include "parser/parsetree.h" #include "rewrite/rewriteRemove.h" #include "storage/lmgr.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/guc.h" @@ -146,7 +148,8 @@ static const Oid object_classes[MAX_OCLASS] = { TableSpaceRelationId, /* OCLASS_TBLSPACE */ ForeignDataWrapperRelationId, /* OCLASS_FDW */ ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */ - UserMappingRelationId /* OCLASS_USER_MAPPING */ + UserMappingRelationId, /* OCLASS_USER_MAPPING */ + DefaultAclRelationId /* OCLASS_DEFACL */ }; @@ -1136,6 +1139,10 @@ doDeletion(const ObjectAddress *object) RemoveUserMappingById(object->objectId); break; + case OCLASS_DEFACL: + RemoveDefaultACLById(object->objectId); + break; + default: elog(ERROR, "unrecognized object class: %u", object->classId); @@ -2055,6 +2062,10 @@ getObjectClass(const ObjectAddress *object) case UserMappingRelationId: Assert(object->objectSubId == 0); return OCLASS_USER_MAPPING; + + case DefaultAclRelationId: + Assert(object->objectSubId == 0); + return OCLASS_DEFACL; } /* shouldn't get here */ @@ -2597,6 +2608,69 @@ getObjectDescription(const ObjectAddress *object) break; } + case OCLASS_DEFACL: + { + Relation defaclrel; + ScanKeyData skey[1]; + SysScanDesc rcscan; + HeapTuple tup; + Form_pg_default_acl defacl; + + defaclrel = heap_open(DefaultAclRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->objectId)); + + rcscan = systable_beginscan(defaclrel, DefaultAclOidIndexId, + true, SnapshotNow, 1, skey); + + tup = systable_getnext(rcscan); + + if (!HeapTupleIsValid(tup)) + elog(ERROR, "could not find tuple for default ACL %u", + object->objectId); + + defacl = (Form_pg_default_acl) GETSTRUCT(tup); + + switch (defacl->defaclobjtype) + { + case DEFACLOBJ_RELATION: + appendStringInfo(&buffer, + _("default privileges on new relations belonging to role %s"), + GetUserNameFromId(defacl->defaclrole)); + break; + case DEFACLOBJ_SEQUENCE: + appendStringInfo(&buffer, + _("default privileges on new sequences belonging to role %s"), + GetUserNameFromId(defacl->defaclrole)); + break; + case DEFACLOBJ_FUNCTION: + appendStringInfo(&buffer, + _("default privileges on new functions belonging to role %s"), + GetUserNameFromId(defacl->defaclrole)); + break; + default: + /* shouldn't get here */ + appendStringInfo(&buffer, + _("default privileges belonging to role %s"), + GetUserNameFromId(defacl->defaclrole)); + break; + } + + if (OidIsValid(defacl->defaclnamespace)) + { + appendStringInfo(&buffer, + _(" in schema %s"), + get_namespace_name(defacl->defaclnamespace)); + } + + systable_endscan(rcscan); + heap_close(defaclrel, AccessShareLock); + break; + } + default: appendStringInfo(&buffer, "unrecognized object %u %u %d", object->classId, diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index e9bdc882238..a0133062847 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.359 2009/09/26 22:42:01 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.360 2009/10/05 19:24:35 tgl Exp $ * * * INTERFACE ROUTINES @@ -59,6 +59,7 @@ #include "storage/bufmgr.h" #include "storage/freespace.h" #include "storage/smgr.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/inval.h" @@ -74,6 +75,7 @@ static void AddNewRelationTuple(Relation pg_class_desc, Oid new_rel_oid, Oid new_type_oid, Oid relowner, char relkind, + Datum relacl, Datum reloptions); static Oid AddNewRelationType(const char *typeName, Oid typeNamespace, @@ -636,14 +638,16 @@ AddNewAttributeTuples(Oid new_rel_oid, * 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). + * relacl and reloptions are passed in Datum form (to avoid having + * to reference the data types in heap.h). Pass (Datum) 0 to set them + * to NULL. * -------------------------------- */ void InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, + Datum relacl, Datum reloptions) { Form_pg_class rd_rel = new_rel_desc->rd_rel; @@ -678,8 +682,10 @@ InsertPgClassTuple(Relation pg_class_desc, 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 (relacl != (Datum) 0) + values[Anum_pg_class_relacl - 1] = relacl; + else + nulls[Anum_pg_class_relacl - 1] = true; if (reloptions != (Datum) 0) values[Anum_pg_class_reloptions - 1] = reloptions; else @@ -715,6 +721,7 @@ AddNewRelationTuple(Relation pg_class_desc, Oid new_type_oid, Oid relowner, char relkind, + Datum relacl, Datum reloptions) { Form_pg_class new_rel_reltup; @@ -775,7 +782,8 @@ AddNewRelationTuple(Relation pg_class_desc, new_rel_desc->rd_att->tdtypeid = new_type_oid; /* Now build and insert the tuple */ - InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions); + InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, + relacl, reloptions); } @@ -831,6 +839,27 @@ AddNewRelationType(const char *typeName, * heap_create_with_catalog * * creates a new cataloged relation. see comments above. + * + * Arguments: + * relname: name to give to new rel + * relnamespace: OID of namespace it goes in + * reltablespace: OID of tablespace it goes in + * relid: OID to assign to new rel, or InvalidOid to select a new OID + * reltypeid: OID to assign to rel's rowtype, or InvalidOid to select one + * ownerid: OID of new rel's owner + * tupdesc: tuple descriptor (source of column definitions) + * cooked_constraints: list of precooked check constraints and defaults + * relkind: relkind for new rel + * shared_relation: TRUE if it's to be a shared relation + * oidislocal: TRUE if oid column (if any) should be marked attislocal + * oidinhcount: attinhcount to assign to oid column (if any) + * oncommit: ON COMMIT marking (only relevant if it's a temp table) + * reloptions: reloptions in Datum form, or (Datum) 0 if none + * use_user_acl: TRUE if should look for user-defined default permissions; + * if FALSE, relacl is always set NULL + * allow_system_table_mods: TRUE to allow creation in system namespaces + * + * Returns the OID of the new relation * -------------------------------- */ Oid @@ -848,10 +877,12 @@ heap_create_with_catalog(const char *relname, int oidinhcount, OnCommitAction oncommit, Datum reloptions, + bool use_user_acl, bool allow_system_table_mods) { Relation pg_class_desc; Relation new_rel_desc; + Acl *relacl; Oid old_type_oid; Oid new_type_oid; Oid new_array_oid = InvalidOid; @@ -921,6 +952,30 @@ heap_create_with_catalog(const char *relname, pg_class_desc); /* + * Determine the relation's initial permissions. + */ + if (use_user_acl) + { + switch (relkind) + { + case RELKIND_RELATION: + case RELKIND_VIEW: + relacl = get_user_default_acl(ACL_OBJECT_RELATION, ownerid, + relnamespace); + break; + case RELKIND_SEQUENCE: + relacl = get_user_default_acl(ACL_OBJECT_SEQUENCE, ownerid, + relnamespace); + break; + default: + relacl = NULL; + break; + } + } + else + relacl = NULL; + + /* * Create the relcache entry (mostly dummy at this point) and the physical * disk file. (If we fail further down, it's the smgr's responsibility to * remove the disk file again.) @@ -1027,6 +1082,7 @@ heap_create_with_catalog(const char *relname, new_type_oid, ownerid, relkind, + PointerGetDatum(relacl), reloptions); /* @@ -1037,12 +1093,15 @@ heap_create_with_catalog(const char *relname, /* * Make a dependency link to force the relation to be deleted if its - * namespace is. Also make a dependency link to its owner. + * namespace is. Also make a dependency link to its owner, as well + * as dependencies for any roles mentioned in the default ACL. * * For composite types, these dependencies are tracked for the pg_type * entry, so we needn't record them here. Likewise, TOAST tables don't * need a namespace dependency (they live in a pinned namespace) nor an - * owner dependency (they depend indirectly through the parent table). + * owner dependency (they depend indirectly through the parent table), + * nor should they have any ACL entries. + * * Also, skip this in bootstrap mode, since we don't make dependencies * while bootstrapping. */ @@ -1062,6 +1121,18 @@ heap_create_with_catalog(const char *relname, recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOnOwner(RelationRelationId, relid, ownerid); + + if (relacl != NULL) + { + int nnewmembers; + Oid *newmembers; + + nnewmembers = aclmembers(relacl, &newmembers); + updateAclDependencies(RelationRelationId, relid, 0, + ownerid, true, + 0, NULL, + nnewmembers, newmembers); + } } /* diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 759950b2265..b43b2910e73 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.321 2009/08/02 22:14:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.322 2009/10/05 19:24:35 tgl Exp $ * * * INTERFACE ROUTINES @@ -664,6 +664,7 @@ index_create(Oid heapRelationId, */ InsertPgClassTuple(pg_class, indexRelation, RelationGetRelid(indexRelation), + (Datum) 0, reloptions); /* done with pg_class */ diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 1fda61cc160..db69c127ad4 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.166 2009/10/02 18:13:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.167 2009/10/05 19:24:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -90,6 +90,7 @@ ProcedureCreate(const char *procedureName, bool internalOutParam = false; Oid variadicType = InvalidOid; Oid proowner = GetUserId(); + Acl *proacl = NULL; Relation rel; HeapTuple tup; HeapTuple oldtup; @@ -331,8 +332,7 @@ ProcedureCreate(const char *procedureName, 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; + /* proacl will be determined later */ rel = heap_open(ProcedureRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); @@ -489,6 +489,15 @@ ProcedureCreate(const char *procedureName, else { /* Creating a new procedure */ + + /* First, get default permissions and set up proacl */ + proacl = get_user_default_acl(ACL_OBJECT_FUNCTION, proowner, + procNamespace); + if (proacl != NULL) + values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl); + else + nulls[Anum_pg_proc_proacl - 1] = true; + tup = heap_form_tuple(tupDesc, values, nulls); simple_heap_insert(rel, tup); is_update = false; @@ -543,6 +552,19 @@ ProcedureCreate(const char *procedureName, if (!is_update) recordDependencyOnOwner(ProcedureRelationId, retval, proowner); + /* dependency on any roles mentioned in ACL */ + if (!is_update && proacl != NULL) + { + int nnewmembers; + Oid *newmembers; + + nnewmembers = aclmembers(proacl, &newmembers); + updateAclDependencies(ProcedureRelationId, retval, 0, + proowner, true, + 0, NULL, + nnewmembers, newmembers); + } + heap_freetuple(tup); heap_close(rel, RowExclusiveLock); diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 08977c38633..869ec1fdd57 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.34 2009/06/11 14:48:55 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.35 2009/10/05 19:24:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -23,6 +23,7 @@ #include "catalog/pg_authid.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" +#include "catalog/pg_default_acl.h" #include "catalog/pg_language.h" #include "catalog/pg_namespace.h" #include "catalog/pg_operator.h" @@ -1180,7 +1181,6 @@ shdepDropOwned(List *roleids, DropBehavior behavior) 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 */ @@ -1195,42 +1195,9 @@ shdepDropOwned(List *roleids, DropBehavior behavior) 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); + RemoveRoleFromObjectACL(roleid, + sdepForm->classid, + sdepForm->objid); break; case SHARED_DEPENDENCY_OWNER: /* Save it for deletion below */ @@ -1365,8 +1332,15 @@ shdepReassignOwned(List *roleids, Oid newrole) AlterLanguageOwner_oid(sdepForm->objid, newrole); break; + case DefaultAclRelationId: + /* + * Ignore default ACLs; they should be handled by + * DROP OWNED, not REASSIGN OWNED. + */ + break; + default: - elog(ERROR, "unexpected classid %d", sdepForm->classid); + elog(ERROR, "unexpected classid %u", sdepForm->classid); break; } /* Make sure the next iteration will see my changes */ diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index ac7b52734dd..dfc178724ef 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.19 2009/09/26 22:42:01 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.20 2009/10/05 19:24:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -213,6 +213,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, 0, ONCOMMIT_NOOP, reloptions, + false, true); /* make the toast relation visible, else index creation will fail */ |
