* acl.c
* Basic access control list data structures manipulation routines.
*
- * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.114 2005/05/27 00:57:49 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.154 2010/01/02 16:57:53 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <ctype.h>
#include "catalog/namespace.h"
-#include "catalog/pg_group.h"
-#include "catalog/pg_shadow.h"
+#include "catalog/pg_authid.h"
+#include "catalog/pg_auth_members.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_class.h"
#include "commands/dbcommands.h"
#include "commands/tablespace.h"
+#include "foreign/foreign.h"
+#include "funcapi.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/inval.h"
#include "utils/lsyscache.h"
+#include "utils/memutils.h"
#include "utils/syscache.h"
-#define ACL_IDTYPE_GID_KEYWORD "group"
-#define ACL_IDTYPE_UID_KEYWORD "user"
+typedef struct
+{
+ const char *name;
+ AclMode value;
+} priv_map;
+
+/*
+ * We frequently need to test whether a given role is a member of some other
+ * role. In most of these tests the "given role" is the same, namely the
+ * active current user. So we can optimize it by keeping a cached list of
+ * all the roles the "given role" is a member of, directly or indirectly.
+ * The cache is flushed whenever we detect a change in pg_auth_members.
+ *
+ * There are actually two caches, one computed under "has_privs" rules
+ * (do not recurse where rolinherit isn't true) and one computed under
+ * "is_member" rules (recurse regardless of rolinherit).
+ *
+ * Possibly this mechanism should be generalized to allow caching membership
+ * info for multiple roles?
+ *
+ * The has_privs cache is:
+ * cached_privs_role is the role OID the cache is for.
+ * cached_privs_roles is an OID list of roles that cached_privs_role
+ * has the privileges of (always including itself).
+ * The cache is valid if cached_privs_role is not InvalidOid.
+ *
+ * The is_member cache is similarly:
+ * cached_member_role is the role OID the cache is for.
+ * cached_membership_roles is an OID list of roles that cached_member_role
+ * is a member of (always including itself).
+ * The cache is valid if cached_member_role is not InvalidOid.
+ */
+static Oid cached_privs_role = InvalidOid;
+static List *cached_privs_roles = NIL;
+static Oid cached_member_role = InvalidOid;
+static List *cached_membership_roles = NIL;
+
static const char *getid(const char *s, char *n);
static void putid(char *p, const char *s);
static Acl *allocacl(int n);
+static void check_acl(const Acl *acl);
static const char *aclparse(const char *s, AclItem *aip);
static bool aclitem_match(const AclItem *a1, const AclItem *a2);
+static int aclitemComparator(const void *arg1, const void *arg2);
static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
- AclId ownerid);
-static Acl *recursive_revoke(Acl *acl, AclId grantee, AclMode revoke_privs,
- AclId ownerid, DropBehavior behavior);
-static bool in_group(AclId uid, AclId gid);
+ Oid ownerId);
+static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
+ Oid ownerId, DropBehavior behavior);
+static int oidComparator(const void *arg1, const void *arg2);
static AclMode convert_priv_string(text *priv_type_text);
+static AclMode convert_any_priv_string(text *priv_type_text,
+ const priv_map *privileges);
static Oid convert_table_name(text *tablename);
static AclMode convert_table_priv_string(text *priv_type_text);
+static AclMode convert_sequence_priv_string(text *priv_type_text);
+static AttrNumber convert_column_name(Oid tableoid, text *column);
+static AclMode convert_column_priv_string(text *priv_type_text);
static Oid convert_database_name(text *databasename);
static AclMode convert_database_priv_string(text *priv_type_text);
+static Oid convert_foreign_data_wrapper_name(text *fdwname);
+static AclMode convert_foreign_data_wrapper_priv_string(text *priv_type_text);
static Oid convert_function_name(text *functionname);
static AclMode convert_function_priv_string(text *priv_type_text);
static Oid convert_language_name(text *languagename);
static AclMode convert_language_priv_string(text *priv_type_text);
static Oid convert_schema_name(text *schemaname);
static AclMode convert_schema_priv_string(text *priv_type_text);
+static Oid convert_server_name(text *servername);
+static AclMode convert_server_priv_string(text *priv_type_text);
static Oid convert_tablespace_name(text *tablespacename);
static AclMode convert_tablespace_priv_string(text *priv_type_text);
+static AclMode convert_role_priv_string(text *priv_type_text);
+static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
+
+static void RoleMembershipCacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr);
/*
ereport(ERROR,
(errcode(ERRCODE_NAME_TOO_LONG),
errmsg("identifier too long"),
- errdetail("Identifier must be less than %d characters.",
- NAMEDATALEN)));
+ errdetail("Identifier must be less than %d characters.",
+ NAMEDATALEN)));
n[len++] = *s;
}
}
/*
- * Write a user or group Name at *p, adding double quotes if needed.
+ * Write a role name at *p, adding double quotes if needed.
* There must be at least (2*NAMEDATALEN)+2 bytes available at *p.
* This needs to be kept in sync with copyAclUserName in pg_dump/dumputils.c
*/
* between the optional id type keyword (group|user) and the actual
* ACL specification.
*
+ * The group|user decoration is unnecessary in the roles world,
+ * but we still accept it for backward compatibility.
+ *
* This routine is called by the parser as well as aclitemin(), hence
* the added generality.
*
AclMode privs,
goption,
read;
- uint32 idtype;
char name[NAMEDATALEN];
char name2[NAMEDATALEN];
#ifdef ACLDEBUG
elog(LOG, "aclparse: input = \"%s\"", s);
#endif
- idtype = ACL_IDTYPE_UID;
s = getid(s, name);
if (*s != '=')
{
/* we just read a keyword, not a name */
- if (strcmp(name, ACL_IDTYPE_GID_KEYWORD) == 0)
- idtype = ACL_IDTYPE_GID;
- else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD) != 0)
+ if (strcmp(name, "group") != 0 && strcmp(name, "user") != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("unrecognized key word: \"%s\"", name),
- errhint("ACL key word must be \"group\" or \"user\".")));
+ errhint("ACL key word must be \"group\" or \"user\".")));
s = getid(s, name); /* move s to the name beyond the keyword */
if (name[0] == '\0')
ereport(ERROR,
errmsg("missing name"),
errhint("A name must follow the \"group\" or \"user\" key word.")));
}
- if (name[0] == '\0')
- idtype = ACL_IDTYPE_WORLD;
if (*s != '=')
ereport(ERROR,
case ACL_DELETE_CHR:
read = ACL_DELETE;
break;
- case ACL_RULE_CHR:
- read = ACL_RULE;
+ case ACL_TRUNCATE_CHR:
+ read = ACL_TRUNCATE;
break;
case ACL_REFERENCES_CHR:
read = ACL_REFERENCES;
case ACL_CREATE_TEMP_CHR:
read = ACL_CREATE_TEMP;
break;
+ case ACL_CONNECT_CHR:
+ read = ACL_CONNECT;
+ break;
+ case 'R': /* ignore old RULE privileges */
+ read = 0;
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid mode character: must be one of \"%s\"",
- ACL_ALL_RIGHTS_STR)));
+ errmsg("invalid mode character: must be one of \"%s\"",
+ ACL_ALL_RIGHTS_STR)));
}
privs |= read;
}
- switch (idtype)
- {
- case ACL_IDTYPE_UID:
- aip->ai_grantee = get_usesysid(name);
- break;
- case ACL_IDTYPE_GID:
- aip->ai_grantee = get_grosysid(name);
- break;
- case ACL_IDTYPE_WORLD:
- aip->ai_grantee = ACL_ID_WORLD;
- break;
- }
+ if (name[0] == '\0')
+ aip->ai_grantee = ACL_ID_PUBLIC;
+ else
+ aip->ai_grantee = get_roleid_checked(name);
/*
- * XXX Allow a degree of backward compatibility by defaulting the
- * grantor to the superuser.
+ * XXX Allow a degree of backward compatibility by defaulting the grantor
+ * to the superuser.
*/
if (*s == '/')
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("a name must follow the \"/\" sign")));
-
- aip->ai_grantor = get_usesysid(name2);
+ aip->ai_grantor = get_roleid_checked(name2);
}
else
{
- aip->ai_grantor = BOOTSTRAP_USESYSID;
+ aip->ai_grantor = BOOTSTRAP_SUPERUSERID;
ereport(WARNING,
(errcode(ERRCODE_INVALID_GRANTOR),
- errmsg("defaulting grantor to user ID %u", BOOTSTRAP_USESYSID)));
+ errmsg("defaulting grantor to user ID %u",
+ BOOTSTRAP_SUPERUSERID)));
}
- ACLITEM_SET_PRIVS_IDTYPE(*aip, privs, goption, idtype);
+ ACLITEM_SET_PRIVS_GOPTIONS(*aip, privs, goption);
#ifdef ACLDEBUG
- elog(LOG, "aclparse: correctly read [%x %d %x]",
- idtype, aip->ai_grantee, privs);
+ elog(LOG, "aclparse: correctly read [%u %x %x]",
+ aip->ai_grantee, privs, goption);
#endif
+
return s;
}
elog(ERROR, "invalid size: %d", n);
size = ACL_N_SIZE(n);
new_acl = (Acl *) palloc0(size);
- new_acl->size = size;
+ SET_VARSIZE(new_acl, size);
new_acl->ndim = 1;
- new_acl->flags = 0;
+ new_acl->dataoffset = 0; /* we never put in any nulls */
new_acl->elemtype = ACLITEMOID;
ARR_LBOUND(new_acl)[0] = 1;
ARR_DIMS(new_acl)[0] = n;
return new_acl;
}
+/*
+ * Create a zero-entry ACL
+ */
+Acl *
+make_empty_acl(void)
+{
+ return allocacl(0);
+}
+
+/*
+ * Copy an ACL
+ */
+Acl *
+aclcopy(const Acl *orig_acl)
+{
+ Acl *result_acl;
+
+ result_acl = allocacl(ACL_NUM(orig_acl));
+
+ memcpy(ACL_DAT(result_acl),
+ ACL_DAT(orig_acl),
+ ACL_NUM(orig_acl) * sizeof(AclItem));
+
+ return result_acl;
+}
+
+/*
+ * Concatenate two ACLs
+ *
+ * This is a bit cheesy, since we may produce an ACL with redundant entries.
+ * Be careful what the result is used for!
+ */
+Acl *
+aclconcat(const Acl *left_acl, const Acl *right_acl)
+{
+ Acl *result_acl;
+
+ result_acl = allocacl(ACL_NUM(left_acl) + ACL_NUM(right_acl));
+
+ memcpy(ACL_DAT(result_acl),
+ ACL_DAT(left_acl),
+ ACL_NUM(left_acl) * sizeof(AclItem));
+
+ memcpy(ACL_DAT(result_acl) + ACL_NUM(left_acl),
+ ACL_DAT(right_acl),
+ ACL_NUM(right_acl) * sizeof(AclItem));
+
+ return result_acl;
+}
+
+/*
+ * Merge two ACLs
+ *
+ * This produces a properly merged ACL with no redundant entries.
+ * Returns NULL on NULL input.
+ */
+Acl *
+aclmerge(const Acl *left_acl, const Acl *right_acl, Oid ownerId)
+{
+ Acl *result_acl;
+ AclItem *aip;
+ int i,
+ num;
+
+ /* Check for cases where one or both are empty/null */
+ if (left_acl == NULL || ACL_NUM(left_acl) == 0)
+ {
+ if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+ return NULL;
+ else
+ return aclcopy(right_acl);
+ }
+ else
+ {
+ if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+ return aclcopy(left_acl);
+ }
+
+ /* Merge them the hard way, one item at a time */
+ result_acl = aclcopy(left_acl);
+
+ aip = ACL_DAT(right_acl);
+ num = ACL_NUM(right_acl);
+
+ for (i = 0; i < num; i++, aip++)
+ {
+ Acl *tmp_acl;
+
+ tmp_acl = aclupdate(result_acl, aip, ACL_MODECHG_ADD,
+ ownerId, DROP_RESTRICT);
+ pfree(result_acl);
+ result_acl = tmp_acl;
+ }
+
+ return result_acl;
+}
+
+/*
+ * Sort the items in an ACL (into an arbitrary but consistent order)
+ */
+void
+aclitemsort(Acl *acl)
+{
+ if (acl != NULL && ACL_NUM(acl) > 1)
+ qsort(ACL_DAT(acl), ACL_NUM(acl), sizeof(AclItem), aclitemComparator);
+}
+
+/*
+ * Check if two ACLs are exactly equal
+ *
+ * This will not detect equality if the two arrays contain the same items
+ * in different orders. To handle that case, sort both inputs first,
+ * using aclitemsort().
+ */
+bool
+aclequal(const Acl *left_acl, const Acl *right_acl)
+{
+ /* Check for cases where one or both are empty/null */
+ if (left_acl == NULL || ACL_NUM(left_acl) == 0)
+ {
+ if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+ return true;
+ else
+ return false;
+ }
+ else
+ {
+ if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+ return false;
+ }
+
+ if (ACL_NUM(left_acl) != ACL_NUM(right_acl))
+ return false;
+
+ if (memcmp(ACL_DAT(left_acl),
+ ACL_DAT(right_acl),
+ ACL_NUM(left_acl) * sizeof(AclItem)) == 0)
+ return true;
+
+ return false;
+}
+
+/*
+ * Verify that an ACL array is acceptable (one-dimensional and has no nulls)
+ */
+static void
+check_acl(const Acl *acl)
+{
+ if (ARR_ELEMTYPE(acl) != ACLITEMOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("ACL array contains wrong data type")));
+ if (ARR_NDIM(acl) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("ACL arrays must be one-dimensional")));
+ if (ARR_HASNULL(acl))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("ACL arrays must not contain null values")));
+}
+
/*
* aclitemin
* Allocates storage for, and fills in, a new AclItem given a string
if (*s)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("extra garbage at the end of the ACL specification")));
+ errmsg("extra garbage at the end of the ACL specification")));
PG_RETURN_ACLITEM_P(aip);
}
char *out;
HeapTuple htup;
unsigned i;
- char *tmpname;
- out = palloc(strlen("group =/") +
+ out = palloc(strlen("=/") +
2 * N_ACL_RIGHTS +
2 * (2 * NAMEDATALEN + 2) +
1);
p = out;
*p = '\0';
- switch (ACLITEM_GET_IDTYPE(*aip))
+ if (aip->ai_grantee != ACL_ID_PUBLIC)
{
- case ACL_IDTYPE_UID:
- htup = SearchSysCache(SHADOWSYSID,
- ObjectIdGetDatum(aip->ai_grantee),
- 0, 0, 0);
- if (HeapTupleIsValid(htup))
- {
- putid(p, NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename));
- ReleaseSysCache(htup);
- }
- else
- {
- /* Generate numeric UID if we don't find an entry */
- sprintf(p, "%d", aip->ai_grantee);
- }
- break;
- case ACL_IDTYPE_GID:
- strcpy(p, "group ");
- p += strlen(p);
- tmpname = get_groname(aip->ai_grantee);
- if (tmpname != NULL)
- putid(p, tmpname);
- else
- {
- /* Generate numeric GID if we don't find an entry */
- sprintf(p, "%d", aip->ai_grantee);
- }
- break;
- case ACL_IDTYPE_WORLD:
- break;
- default:
- elog(ERROR, "unrecognized idtype: %d",
- (int) ACLITEM_GET_IDTYPE(*aip));
- break;
+ htup = SearchSysCache(AUTHOID,
+ ObjectIdGetDatum(aip->ai_grantee),
+ 0, 0, 0);
+ if (HeapTupleIsValid(htup))
+ {
+ putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));
+ ReleaseSysCache(htup);
+ }
+ else
+ {
+ /* Generate numeric OID if we don't find an entry */
+ sprintf(p, "%u", aip->ai_grantee);
+ }
}
while (*p)
++p;
*p++ = '/';
*p = '\0';
- htup = SearchSysCache(SHADOWSYSID,
+ htup = SearchSysCache(AUTHOID,
ObjectIdGetDatum(aip->ai_grantor),
0, 0, 0);
if (HeapTupleIsValid(htup))
{
- putid(p, NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename));
+ putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));
ReleaseSysCache(htup);
}
else
{
- /* Generate numeric UID if we don't find an entry */
- sprintf(p, "%d", aip->ai_grantor);
+ /* Generate numeric OID if we don't find an entry */
+ sprintf(p, "%u", aip->ai_grantor);
}
- while (*p)
- ++p;
- *p = '\0';
-
PG_RETURN_CSTRING(out);
}
static bool
aclitem_match(const AclItem *a1, const AclItem *a2)
{
- return ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) &&
- a1->ai_grantee == a2->ai_grantee &&
+ return a1->ai_grantee == a2->ai_grantee &&
a1->ai_grantor == a2->ai_grantor;
}
+/*
+ * aclitemComparator
+ * qsort comparison function for AclItems
+ */
+static int
+aclitemComparator(const void *arg1, const void *arg2)
+{
+ const AclItem *a1 = (const AclItem *) arg1;
+ const AclItem *a2 = (const AclItem *) arg2;
+
+ if (a1->ai_grantee > a2->ai_grantee)
+ return 1;
+ if (a1->ai_grantee < a2->ai_grantee)
+ return -1;
+ if (a1->ai_grantor > a2->ai_grantor)
+ return 1;
+ if (a1->ai_grantor < a2->ai_grantor)
+ return -1;
+ if (a1->ai_privs > a2->ai_privs)
+ return 1;
+ if (a1->ai_privs < a2->ai_privs)
+ return -1;
+ return 0;
+}
+
/*
* aclitem equality operator
*/
*
* Change this routine if you want to alter the default access policy for
* newly-created objects (or any object with a NULL acl entry).
+ *
+ * Note that these are the hard-wired "defaults" that are used in the
+ * absence of any pg_default_acl entry.
*/
Acl *
-acldefault(GrantObjectType objtype, AclId ownerid)
+acldefault(GrantObjectType objtype, Oid ownerId)
{
AclMode world_default;
AclMode owner_default;
+ int nacl;
Acl *acl;
AclItem *aip;
switch (objtype)
{
+ case ACL_OBJECT_COLUMN:
+ /* by default, columns have no extra privileges */
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_NO_RIGHTS;
+ break;
case ACL_OBJECT_RELATION:
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_RELATION;
break;
+ case ACL_OBJECT_SEQUENCE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_SEQUENCE;
+ break;
case ACL_OBJECT_DATABASE:
- world_default = ACL_CREATE_TEMP; /* not NO_RIGHTS! */
+ /* for backwards compatibility, grant some rights by default */
+ world_default = ACL_CREATE_TEMP | ACL_CONNECT;
owner_default = ACL_ALL_RIGHTS_DATABASE;
break;
case ACL_OBJECT_FUNCTION:
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_LANGUAGE;
break;
+ case ACL_OBJECT_LARGEOBJECT:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_LARGEOBJECT;
+ break;
case ACL_OBJECT_NAMESPACE:
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_NAMESPACE;
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_TABLESPACE;
break;
+ case ACL_OBJECT_FDW:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_FDW;
+ break;
+ case ACL_OBJECT_FOREIGN_SERVER:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
break;
}
- acl = allocacl((world_default != ACL_NO_RIGHTS) ? 2 : 1);
+ nacl = 0;
+ if (world_default != ACL_NO_RIGHTS)
+ nacl++;
+ if (owner_default != ACL_NO_RIGHTS)
+ nacl++;
+
+ acl = allocacl(nacl);
aip = ACL_DAT(acl);
if (world_default != ACL_NO_RIGHTS)
{
- aip->ai_grantee = ACL_ID_WORLD;
- aip->ai_grantor = ownerid;
- ACLITEM_SET_PRIVS_IDTYPE(*aip, world_default, ACL_NO_RIGHTS,
- ACL_IDTYPE_WORLD);
+ aip->ai_grantee = ACL_ID_PUBLIC;
+ aip->ai_grantor = ownerId;
+ ACLITEM_SET_PRIVS_GOPTIONS(*aip, world_default, ACL_NO_RIGHTS);
aip++;
}
/*
- * Note that the owner's entry shows all ordinary privileges but no
- * grant options. This is because his grant options come "from the
- * system" and not from his own efforts. (The SQL spec says that the
- * owner's rights come from a "_SYSTEM" authid.) However, we do
- * consider that the owner's ordinary privileges are self-granted;
- * this lets him revoke them. We implement the owner's grant options
- * without any explicit "_SYSTEM"-like ACL entry, by internally
- * special-casing the owner whereever we are testing grant options.
+ * Note that the owner's entry shows all ordinary privileges but no grant
+ * options. This is because his grant options come "from the system" and
+ * not from his own efforts. (The SQL spec says that the owner's rights
+ * come from a "_SYSTEM" authid.) However, we do consider that the
+ * owner's ordinary privileges are self-granted; this lets him revoke
+ * them. We implement the owner's grant options without any explicit
+ * "_SYSTEM"-like ACL entry, by internally special-casing the owner
+ * whereever we are testing grant options.
*/
- aip->ai_grantee = ownerid;
- aip->ai_grantor = ownerid;
- ACLITEM_SET_PRIVS_IDTYPE(*aip, owner_default, ACL_NO_RIGHTS,
- ACL_IDTYPE_UID);
+ if (owner_default != ACL_NO_RIGHTS)
+ {
+ aip->ai_grantee = ownerId;
+ aip->ai_grantor = ownerId;
+ ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS);
+ }
return acl;
}
* old_acl: the input ACL array
* mod_aip: defines the privileges to be added, removed, or substituted
* modechg: ACL_MODECHG_ADD, ACL_MODECHG_DEL, or ACL_MODECHG_EQL
- * ownerid: AclId of object owner
+ * ownerId: Oid of object owner
* behavior: RESTRICT or CASCADE behavior for recursive removal
*
* ownerid and behavior are only relevant when the update operation specifies
*/
Acl *
aclupdate(const Acl *old_acl, const AclItem *mod_aip,
- int modechg, AclId ownerid, DropBehavior behavior)
+ int modechg, Oid ownerId, DropBehavior behavior)
{
Acl *new_acl = NULL;
AclItem *old_aip,
int dst,
num;
- /* These checks for null input are probably dead code, but... */
- if (!old_acl || ACL_NUM(old_acl) < 0)
- old_acl = allocacl(0);
- if (!mod_aip)
- {
- new_acl = allocacl(ACL_NUM(old_acl));
- memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
- return new_acl;
- }
+ /* Caller probably already checked old_acl, but be safe */
+ check_acl(old_acl);
/* If granting grant options, check for circularity */
if (modechg != ACL_MODECHG_DEL &&
ACLITEM_GET_GOPTIONS(*mod_aip) != ACL_NO_RIGHTS)
- check_circularity(old_acl, mod_aip, ownerid);
+ check_circularity(old_acl, mod_aip, ownerId);
num = ACL_NUM(old_acl);
old_aip = ACL_DAT(old_acl);
/*
- * Search the ACL for an existing entry for this grantee and grantor.
- * If one exists, just modify the entry in-place (well, in the same
- * position, since we actually return a copy); otherwise, insert the
- * new entry at the end.
+ * Search the ACL for an existing entry for this grantee and grantor. If
+ * one exists, just modify the entry in-place (well, in the same position,
+ * since we actually return a copy); otherwise, insert the new entry at
+ * the end.
*/
for (dst = 0; dst < num; ++dst)
/* initialize the new entry with no permissions */
new_aip[dst].ai_grantee = mod_aip->ai_grantee;
new_aip[dst].ai_grantor = mod_aip->ai_grantor;
- ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst],
- ACL_NO_RIGHTS, ACL_NO_RIGHTS,
- ACLITEM_GET_IDTYPE(*mod_aip));
+ ACLITEM_SET_PRIVS_GOPTIONS(new_aip[dst],
+ ACL_NO_RIGHTS, ACL_NO_RIGHTS);
num++; /* set num to the size of new_acl */
}
break;
case ACL_MODECHG_DEL:
ACLITEM_SET_RIGHTS(new_aip[dst],
- old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip));
+ old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip));
break;
case ACL_MODECHG_EQL:
ACLITEM_SET_RIGHTS(new_aip[dst],
memmove(new_aip + dst,
new_aip + dst + 1,
(num - dst - 1) * sizeof(AclItem));
+ /* Adjust array size to be 'num - 1' items */
ARR_DIMS(new_acl)[0] = num - 1;
- ARR_SIZE(new_acl) -= sizeof(AclItem);
+ SET_VARSIZE(new_acl, ACL_N_SIZE(num - 1));
}
/*
- * Remove abandoned privileges (cascading revoke). Currently we can
- * only handle this when the grantee is a user.
+ * Remove abandoned privileges (cascading revoke). Currently we can only
+ * handle this when the grantee is not PUBLIC.
*/
if ((old_goptions & ~new_goptions) != 0)
{
- Assert(ACLITEM_GET_IDTYPE(*mod_aip) == ACL_IDTYPE_UID);
+ Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee,
(old_goptions & ~new_goptions),
- ownerid, behavior);
+ ownerId, behavior);
}
return new_acl;
* Update an ACL array to reflect a change of owner to the parent object
*
* old_acl: the input ACL array (must not be NULL)
- * oldownerid: AclId of the old object owner
- * newownerid: AclId of the new object owner
+ * oldOwnerId: Oid of the old object owner
+ * newOwnerId: Oid of the new object owner
*
* The result is a modified copy; the input object is not changed.
*
* NB: caller is responsible for having detoasted the input ACL, if needed.
*/
Acl *
-aclnewowner(const Acl *old_acl, AclId oldownerid, AclId newownerid)
+aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
{
Acl *new_acl;
AclItem *new_aip;
targ,
num;
+ check_acl(old_acl);
+
/*
* Make a copy of the given ACL, substituting new owner ID for old
- * wherever it appears as either grantor or grantee. Also note if the
- * new owner ID is already present.
+ * wherever it appears as either grantor or grantee. Also note if the new
+ * owner ID is already present.
*/
num = ACL_NUM(old_acl);
old_aip = ACL_DAT(old_acl);
memcpy(new_aip, old_aip, num * sizeof(AclItem));
for (dst = 0, dst_aip = new_aip; dst < num; dst++, dst_aip++)
{
- /* grantor is always a UID, but grantee might not be */
- if (dst_aip->ai_grantor == oldownerid)
- dst_aip->ai_grantor = newownerid;
- else if (dst_aip->ai_grantor == newownerid)
+ if (dst_aip->ai_grantor == oldOwnerId)
+ dst_aip->ai_grantor = newOwnerId;
+ else if (dst_aip->ai_grantor == newOwnerId)
+ newpresent = true;
+ if (dst_aip->ai_grantee == oldOwnerId)
+ dst_aip->ai_grantee = newOwnerId;
+ else if (dst_aip->ai_grantee == newOwnerId)
newpresent = true;
- if (ACLITEM_GET_IDTYPE(*dst_aip) == ACL_IDTYPE_UID)
- {
- if (dst_aip->ai_grantee == oldownerid)
- dst_aip->ai_grantee = newownerid;
- else if (dst_aip->ai_grantee == newownerid)
- newpresent = true;
- }
}
/*
- * If the old ACL contained any references to the new owner, then we
- * may now have generated an ACL containing duplicate entries. Find
- * them and merge them so that there are not duplicates. (This is
- * relatively expensive since we use a stupid O(N^2) algorithm, but
- * it's unlikely to be the normal case.)
+ * If the old ACL contained any references to the new owner, then we may
+ * now have generated an ACL containing duplicate entries. Find them and
+ * merge them so that there are not duplicates. (This is relatively
+ * expensive since we use a stupid O(N^2) algorithm, but it's unlikely to
+ * be the normal case.)
*
- * To simplify deletion of duplicate entries, we temporarily leave them
- * in the array but set their privilege masks to zero; when we reach
- * such an entry it's just skipped. (Thus, a side effect of this code
- * will be to remove privilege-free entries, should there be any in
- * the input.) dst is the next output slot, targ is the currently
- * considered input slot (always >= dst), and src scans entries to the
- * right of targ looking for duplicates. Once an entry has been
- * emitted to dst it is known duplicate-free and need not be
- * considered anymore.
+ * To simplify deletion of duplicate entries, we temporarily leave them in
+ * the array but set their privilege masks to zero; when we reach such an
+ * entry it's just skipped. (Thus, a side effect of this code will be to
+ * remove privilege-free entries, should there be any in the input.) dst
+ * is the next output slot, targ is the currently considered input slot
+ * (always >= dst), and src scans entries to the right of targ looking for
+ * duplicates. Once an entry has been emitted to dst it is known
+ * duplicate-free and need not be considered anymore.
*/
if (newpresent)
{
}
/* Adjust array size to be 'dst' items */
ARR_DIMS(new_acl)[0] = dst;
- ARR_SIZE(new_acl) = ACL_N_SIZE(dst);
+ SET_VARSIZE(new_acl, ACL_N_SIZE(dst));
}
return new_acl;
*/
static void
check_circularity(const Acl *old_acl, const AclItem *mod_aip,
- AclId ownerid)
+ Oid ownerId)
{
Acl *acl;
AclItem *aip;
num;
AclMode own_privs;
+ check_acl(old_acl);
+
/*
- * For now, grant options can only be granted to users, not groups or
- * PUBLIC. Otherwise we'd have to work a bit harder here.
+ * For now, grant options can only be granted to roles, not PUBLIC.
+ * Otherwise we'd have to work a bit harder here.
*/
- Assert(ACLITEM_GET_IDTYPE(*mod_aip) == ACL_IDTYPE_UID);
+ Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
/* The owner always has grant options, no need to check */
- if (mod_aip->ai_grantor == ownerid)
+ if (mod_aip->ai_grantor == ownerId)
return;
/* Make a working copy */
aip = ACL_DAT(acl);
for (i = 0; i < num; i++)
{
- if (ACLITEM_GET_IDTYPE(aip[i]) == ACL_IDTYPE_UID &&
- aip[i].ai_grantee == mod_aip->ai_grantee &&
+ if (aip[i].ai_grantee == mod_aip->ai_grantee &&
ACLITEM_GET_GOPTIONS(aip[i]) != ACL_NO_RIGHTS)
{
Acl *new_acl;
/* We'll actually zap ordinary privs too, but no matter */
new_acl = aclupdate(acl, &aip[i], ACL_MODECHG_DEL,
- ownerid, DROP_CASCADE);
+ ownerId, DROP_CASCADE);
pfree(acl);
acl = new_acl;
/* Now we can compute grantor's independently-derived privileges */
own_privs = aclmask(acl,
mod_aip->ai_grantor,
- ownerid,
- ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)),
+ ownerId,
+ ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)),
ACLMASK_ALL);
own_privs = ACL_OPTION_TO_PRIVS(own_privs);
if ((ACLITEM_GET_GOPTIONS(*mod_aip) & ~own_privs) != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
- errmsg("grant options cannot be granted back to your own grantor")));
+ errmsg("grant options cannot be granted back to your own grantor")));
pfree(acl);
}
* acl: the input ACL list
* grantee: the user from whom some grant options have been revoked
* revoke_privs: the grant options being revoked
- * ownerid: AclId of object owner
+ * ownerId: Oid of object owner
* behavior: RESTRICT or CASCADE behavior for recursive removal
*
* The input Acl object is pfree'd if replaced.
*/
static Acl *
recursive_revoke(Acl *acl,
- AclId grantee,
+ Oid grantee,
AclMode revoke_privs,
- AclId ownerid,
+ Oid ownerId,
DropBehavior behavior)
{
AclMode still_has;
int i,
num;
+ check_acl(acl);
+
/* The owner can never truly lose grant options, so short-circuit */
- if (grantee == ownerid)
+ if (grantee == ownerId)
return acl;
/* The grantee might still have the privileges via another grantor */
- still_has = aclmask(acl, grantee, ownerid,
+ still_has = aclmask(acl, grantee, ownerId,
ACL_GRANT_OPTION_FOR(revoke_privs),
ACLMASK_ALL);
revoke_privs &= ~still_has;
mod_acl.ai_grantor = grantee;
mod_acl.ai_grantee = aip[i].ai_grantee;
- ACLITEM_SET_PRIVS_IDTYPE(mod_acl,
- revoke_privs,
- revoke_privs,
- ACLITEM_GET_IDTYPE(aip[i]));
+ ACLITEM_SET_PRIVS_GOPTIONS(mod_acl,
+ revoke_privs,
+ revoke_privs);
new_acl = aclupdate(acl, &mod_acl, ACL_MODECHG_DEL,
- ownerid, behavior);
+ ownerId, behavior);
pfree(acl);
acl = new_acl;
/*
- * aclmask --- compute bitmask of all privileges held by userid.
+ * aclmask --- compute bitmask of all privileges held by roleid.
*
* When 'how' = ACLMASK_ALL, this simply returns the privilege bits
- * held by the given userid according to the given ACL list, ANDed
+ * held by the given roleid according to the given ACL list, ANDed
* with 'mask'. (The point of passing 'mask' is to let the routine
* exit early if all privileges of interest have been found.)
*
* Usage patterns:
*
* To see if any of a set of privileges are held:
- * if (aclmask(acl, userid, ownerid, privs, ACLMASK_ANY) != 0)
+ * if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ANY) != 0)
*
* To see if all of a set of privileges are held:
- * if (aclmask(acl, userid, ownerid, privs, ACLMASK_ALL) == privs)
+ * if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL) == privs)
*
* To determine exactly which of a set of privileges are held:
- * heldprivs = aclmask(acl, userid, ownerid, privs, ACLMASK_ALL);
+ * heldprivs = aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL);
*/
AclMode
-aclmask(const Acl *acl, AclId userid, AclId ownerid,
+aclmask(const Acl *acl, Oid roleid, Oid ownerId,
AclMode mask, AclMaskHow how)
{
AclMode result;
if (acl == NULL)
elog(ERROR, "null ACL");
+ check_acl(acl);
+
/* Quick exit for mask == 0 */
if (mask == 0)
return 0;
result = 0;
/* Owner always implicitly has all grant options */
- if (userid == ownerid)
+ if ((mask & ACLITEM_ALL_GOPTION_BITS) &&
+ has_privs_of_role(roleid, ownerId))
{
result = mask & ACLITEM_ALL_GOPTION_BITS;
- if (result == mask)
+ if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
return result;
}
aidat = ACL_DAT(acl);
/*
- * Check privileges granted directly to user or to public
+ * Check privileges granted directly to roleid or to public
*/
for (i = 0; i < num; i++)
{
AclItem *aidata = &aidat[i];
- if (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_WORLD
- || (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_UID
- && aidata->ai_grantee == userid))
+ if (aidata->ai_grantee == ACL_ID_PUBLIC ||
+ aidata->ai_grantee == roleid)
{
- result |= (aidata->ai_privs & mask);
+ result |= aidata->ai_privs & mask;
if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
return result;
}
}
/*
- * Check privileges granted via groups. We do this in a separate pass
- * to minimize expensive lookups in pg_group.
+ * Check privileges granted indirectly via role memberships. We do this in
+ * a separate pass to minimize expensive indirect membership tests. In
+ * particular, it's worth testing whether a given ACL entry grants any
+ * privileges still of interest before we perform the has_privs_of_role
+ * test.
*/
- remaining = (mask & ~result);
+ remaining = mask & ~result;
for (i = 0; i < num; i++)
{
AclItem *aidata = &aidat[i];
- if (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_GID
- && (aidata->ai_privs & remaining)
- && in_group(userid, aidata->ai_grantee))
+ if (aidata->ai_grantee == ACL_ID_PUBLIC ||
+ aidata->ai_grantee == roleid)
+ continue; /* already checked it */
+
+ if ((aidata->ai_privs & remaining) &&
+ has_privs_of_role(roleid, aidata->ai_grantee))
{
- result |= (aidata->ai_privs & mask);
+ result |= aidata->ai_privs & mask;
if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
return result;
- remaining = (mask & ~result);
+ remaining = mask & ~result;
}
}
/*
- * Is user a member of group?
+ * aclmask_direct --- compute bitmask of all privileges held by roleid.
+ *
+ * This is exactly like aclmask() except that we consider only privileges
+ * held *directly* by roleid, not those inherited via role membership.
*/
-static bool
-in_group(AclId uid, AclId gid)
+static AclMode
+aclmask_direct(const Acl *acl, Oid roleid, Oid ownerId,
+ AclMode mask, AclMaskHow how)
{
- bool result = false;
- HeapTuple tuple;
- Datum att;
- bool isNull;
- IdList *glist;
- AclId *aidp;
+ AclMode result;
+ AclItem *aidat;
int i,
num;
- tuple = SearchSysCache(GROSYSID,
- ObjectIdGetDatum(gid),
- 0, 0, 0);
- if (HeapTupleIsValid(tuple))
+ /*
+ * Null ACL should not happen, since caller should have inserted
+ * appropriate default
+ */
+ if (acl == NULL)
+ elog(ERROR, "null ACL");
+
+ check_acl(acl);
+
+ /* Quick exit for mask == 0 */
+ if (mask == 0)
+ return 0;
+
+ result = 0;
+
+ /* Owner always implicitly has all grant options */
+ if ((mask & ACLITEM_ALL_GOPTION_BITS) &&
+ roleid == ownerId)
+ {
+ result = mask & ACLITEM_ALL_GOPTION_BITS;
+ if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
+ return result;
+ }
+
+ num = ACL_NUM(acl);
+ aidat = ACL_DAT(acl);
+
+ /*
+ * Check privileges granted directly to roleid (and not to public)
+ */
+ for (i = 0; i < num; i++)
{
- att = SysCacheGetAttr(GROSYSID,
- tuple,
- Anum_pg_group_grolist,
- &isNull);
- if (!isNull)
+ AclItem *aidata = &aidat[i];
+
+ if (aidata->ai_grantee == roleid)
{
- /* be sure the IdList is not toasted */
- glist = DatumGetIdListP(att);
- /* scan it */
- num = IDLIST_NUM(glist);
- aidp = IDLIST_DAT(glist);
- for (i = 0; i < num; ++i)
- {
- if (aidp[i] == uid)
- {
- result = true;
- break;
- }
- }
- /* if IdList was toasted, free detoasted copy */
- if ((Pointer) glist != DatumGetPointer(att))
- pfree(glist);
+ result |= aidata->ai_privs & mask;
+ if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
+ return result;
}
- ReleaseSysCache(tuple);
}
- else
- ereport(WARNING,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("group with ID %u does not exist", gid)));
+
return result;
}
+/*
+ * aclmembers
+ * Find out all the roleids mentioned in an Acl.
+ * Note that we do not distinguish grantors from grantees.
+ *
+ * *roleids is set to point to a palloc'd array containing distinct OIDs
+ * in sorted order. The length of the array is the function result.
+ */
+int
+aclmembers(const Acl *acl, Oid **roleids)
+{
+ Oid *list;
+ const AclItem *acldat;
+ int i,
+ j,
+ k;
+
+ if (acl == NULL || ACL_NUM(acl) == 0)
+ {
+ *roleids = NULL;
+ return 0;
+ }
+
+ check_acl(acl);
+
+ /* Allocate the worst-case space requirement */
+ list = palloc(ACL_NUM(acl) * 2 * sizeof(Oid));
+ acldat = ACL_DAT(acl);
+
+ /*
+ * Walk the ACL collecting mentioned RoleIds.
+ */
+ j = 0;
+ for (i = 0; i < ACL_NUM(acl); i++)
+ {
+ const AclItem *ai = &acldat[i];
+
+ if (ai->ai_grantee != ACL_ID_PUBLIC)
+ list[j++] = ai->ai_grantee;
+ /* grantor is currently never PUBLIC, but let's check anyway */
+ if (ai->ai_grantor != ACL_ID_PUBLIC)
+ list[j++] = ai->ai_grantor;
+ }
+
+ /* Sort the array */
+ qsort(list, j, sizeof(Oid), oidComparator);
+
+ /* Remove duplicates from the array */
+ k = 0;
+ for (i = 1; i < j; i++)
+ {
+ if (list[k] != list[i])
+ list[++k] = list[i];
+ }
+
+ /*
+ * We could repalloc the array down to minimum size, but it's hardly worth
+ * it since it's only transient memory.
+ */
+ *roleids = list;
+
+ return k + 1;
+}
+
+/*
+ * oidComparator
+ * qsort comparison function for Oids
+ */
+static int
+oidComparator(const void *arg1, const void *arg2)
+{
+ Oid oid1 = *(const Oid *) arg1;
+ Oid oid2 = *(const Oid *) arg2;
+
+ if (oid1 > oid2)
+ return 1;
+ if (oid1 < oid2)
+ return -1;
+ return 0;
+}
+
+
/*
* aclinsert (exported function)
*/
int i,
num;
+ check_acl(acl);
num = ACL_NUM(acl);
aidat = ACL_DAT(acl);
for (i = 0; i < num; ++i)
{
- if (aip->ai_grantee == aidat[i].ai_grantee
- && ACLITEM_GET_IDTYPE(*aip) == ACLITEM_GET_IDTYPE(aidat[i])
- && aip->ai_grantor == aidat[i].ai_grantor
- && (ACLITEM_GET_RIGHTS(*aip) & ACLITEM_GET_RIGHTS(aidat[i])) == ACLITEM_GET_RIGHTS(*aip))
+ if (aip->ai_grantee == aidat[i].ai_grantee &&
+ aip->ai_grantor == aidat[i].ai_grantor &&
+ (ACLITEM_GET_RIGHTS(*aip) & ACLITEM_GET_RIGHTS(aidat[i])) == ACLITEM_GET_RIGHTS(*aip))
PG_RETURN_BOOL(true);
}
PG_RETURN_BOOL(false);
Datum
makeaclitem(PG_FUNCTION_ARGS)
{
- int32 u_grantee = PG_GETARG_INT32(0);
- int32 g_grantee = PG_GETARG_INT32(1);
- int32 grantor = PG_GETARG_INT32(2);
- text *privtext = PG_GETARG_TEXT_P(3);
- bool goption = PG_GETARG_BOOL(4);
- AclItem *aclitem;
+ Oid grantee = PG_GETARG_OID(0);
+ Oid grantor = PG_GETARG_OID(1);
+ text *privtext = PG_GETARG_TEXT_P(2);
+ bool goption = PG_GETARG_BOOL(3);
+ AclItem *result;
AclMode priv;
priv = convert_priv_string(privtext);
- aclitem = (AclItem *) palloc(sizeof(*aclitem));
-
- if (u_grantee == 0 && g_grantee == 0)
- {
- aclitem ->ai_grantee = ACL_ID_WORLD;
-
- ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_WORLD);
- }
- else if (u_grantee != 0 && g_grantee != 0)
- {
- ereport(ERROR,
- (errcode(ERRCODE_DATA_EXCEPTION),
- errmsg("cannot specify both user and group")));
- }
- else if (u_grantee != 0)
- {
- aclitem ->ai_grantee = u_grantee;
-
- ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_UID);
- }
- else
- /* (g_grantee != 0) */
- {
- aclitem ->ai_grantee = g_grantee;
-
- ACLITEM_SET_IDTYPE(*aclitem, ACL_IDTYPE_GID);
- }
+ result = (AclItem *) palloc(sizeof(AclItem));
- aclitem ->ai_grantor = grantor;
+ result->ai_grantee = grantee;
+ result->ai_grantor = grantor;
- ACLITEM_SET_PRIVS(*aclitem, priv);
- if (goption)
- ACLITEM_SET_GOPTIONS(*aclitem, priv);
- else
- ACLITEM_SET_GOPTIONS(*aclitem, ACL_NO_RIGHTS);
+ ACLITEM_SET_PRIVS_GOPTIONS(*result, priv,
+ (goption ? priv : ACL_NO_RIGHTS));
- PG_RETURN_ACLITEM_P(aclitem);
+ PG_RETURN_ACLITEM_P(result);
}
static AclMode
convert_priv_string(text *priv_type_text)
{
- char *priv_type;
-
- priv_type = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(priv_type_text)));
+ char *priv_type = text_to_cstring(priv_type_text);
if (pg_strcasecmp(priv_type, "SELECT") == 0)
return ACL_SELECT;
return ACL_UPDATE;
if (pg_strcasecmp(priv_type, "DELETE") == 0)
return ACL_DELETE;
- if (pg_strcasecmp(priv_type, "RULE") == 0)
- return ACL_RULE;
+ if (pg_strcasecmp(priv_type, "TRUNCATE") == 0)
+ return ACL_TRUNCATE;
if (pg_strcasecmp(priv_type, "REFERENCES") == 0)
return ACL_REFERENCES;
if (pg_strcasecmp(priv_type, "TRIGGER") == 0)
return ACL_CREATE_TEMP;
if (pg_strcasecmp(priv_type, "TEMPORARY") == 0)
return ACL_CREATE_TEMP;
+ if (pg_strcasecmp(priv_type, "CONNECT") == 0)
+ return ACL_CONNECT;
+ if (pg_strcasecmp(priv_type, "RULE") == 0)
+ return 0; /* ignore old RULE privileges */
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
/*
- * has_table_privilege variants
- * These are all named "has_table_privilege" at the SQL level.
- * They take various combinations of relation name, relation OID,
- * user name, user sysid, or implicit user = current_user.
+ * convert_any_priv_string: recognize privilege strings for has_foo_privilege
*
- * The result is a boolean value: true if user has the indicated
- * privilege, false if not.
+ * We accept a comma-separated list of case-insensitive privilege names,
+ * producing a bitmask of the OR'd privilege bits. We are liberal about
+ * whitespace between items, not so much about whitespace within items.
+ * The allowed privilege names are given as an array of priv_map structs,
+ * terminated by one with a NULL name pointer.
*/
+static AclMode
+convert_any_priv_string(text *priv_type_text,
+ const priv_map *privileges)
+{
+ AclMode result = 0;
+ char *priv_type = text_to_cstring(priv_type_text);
+ char *chunk;
+ char *next_chunk;
-/*
- * has_table_privilege_name_name
- * Check user privileges on a table given
+ /* We rely on priv_type being a private, modifiable string */
+ for (chunk = priv_type; chunk; chunk = next_chunk)
+ {
+ int chunk_len;
+ const priv_map *this_priv;
+
+ /* Split string at commas */
+ next_chunk = strchr(chunk, ',');
+ if (next_chunk)
+ *next_chunk++ = '\0';
+
+ /* Drop leading/trailing whitespace in this chunk */
+ while (*chunk && isspace((unsigned char) *chunk))
+ chunk++;
+ chunk_len = strlen(chunk);
+ while (chunk_len > 0 && isspace((unsigned char) chunk[chunk_len - 1]))
+ chunk_len--;
+ chunk[chunk_len] = '\0';
+
+ /* Match to the privileges list */
+ for (this_priv = privileges; this_priv->name; this_priv++)
+ {
+ if (pg_strcasecmp(this_priv->name, chunk) == 0)
+ {
+ result |= this_priv->value;
+ break;
+ }
+ }
+ if (!this_priv->name)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized privilege type: \"%s\"", chunk)));
+ }
+
+ pfree(priv_type);
+ return result;
+}
+
+
+static const char *
+convert_aclright_to_string(int aclright)
+{
+ switch (aclright)
+ {
+ case ACL_INSERT:
+ return "INSERT";
+ case ACL_SELECT:
+ return "SELECT";
+ case ACL_UPDATE:
+ return "UPDATE";
+ case ACL_DELETE:
+ return "DELETE";
+ case ACL_TRUNCATE:
+ return "TRUNCATE";
+ case ACL_REFERENCES:
+ return "REFERENCES";
+ case ACL_TRIGGER:
+ return "TRIGGER";
+ case ACL_EXECUTE:
+ return "EXECUTE";
+ case ACL_USAGE:
+ return "USAGE";
+ case ACL_CREATE:
+ return "CREATE";
+ case ACL_CREATE_TEMP:
+ return "TEMPORARY";
+ case ACL_CONNECT:
+ return "CONNECT";
+ default:
+ elog(ERROR, "unrecognized aclright: %d", aclright);
+ return NULL;
+ }
+}
+
+
+/*----------
+ * Convert an aclitem[] to a table.
+ *
+ * Example:
+ *
+ * aclexplode('{=r/joe,foo=a*w/joe}'::aclitem[])
+ *
+ * returns the table
+ *
+ * {{ OID(joe), 0::OID, 'SELECT', false },
+ * { OID(joe), OID(foo), 'INSERT', true },
+ * { OID(joe), OID(foo), 'UPDATE', false }}
+ *----------
+ */
+Datum
+aclexplode(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ int *idx;
+ Acl *acl = PG_GETARG_ACL_P(0);
+ AclItem *aidat;
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ TupleDesc tupdesc;
+ MemoryContext oldcontext;
+
+ check_acl(acl);
+
+ funcctx = SRF_FIRSTCALL_INIT();
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+ /*
+ * build tupdesc for result tuples (matches out parameters in
+ * pg_proc entry)
+ */
+ tupdesc = CreateTemplateTupleDesc(4, false);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "grantor",
+ OIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee",
+ OIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 3, "privilege_type",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_grantable",
+ BOOLOID, -1, 0);
+
+ funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+
+ /* allocate memory for user context */
+ idx = (int *) palloc(sizeof(int[2]));
+ idx[0] = 0; /* ACL array item index */
+ idx[1] = -1; /* privilege type counter */
+ funcctx->user_fctx = (void *) idx;
+
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ idx = (int *) funcctx->user_fctx;
+
+ aidat = ACL_DAT(acl);
+ while (1)
+ {
+ idx[1]++;
+ if (idx[1] == N_ACL_RIGHTS)
+ {
+ idx[1] = 0;
+ idx[0]++;
+ if (idx[0] == ACL_NUM(acl))
+ /* done */
+ break;
+ }
+
+ Assert(idx[0] < ACL_NUM(acl));
+ Assert(idx[1] < N_ACL_RIGHTS);
+
+ if (ACLITEM_GET_PRIVS(aidat[idx[0]]) & (1 << idx[1]))
+ {
+ Datum result;
+ Datum values[4];
+ bool nulls[4];
+ HeapTuple tuple;
+
+ values[0] = ObjectIdGetDatum(aidat[idx[0]].ai_grantor);
+ values[1] = ObjectIdGetDatum(aidat[idx[0]].ai_grantee);
+ values[2] = CStringGetTextDatum(convert_aclright_to_string(1 << idx[1]));
+ values[3] = BoolGetDatum(ACLITEM_GET_GOPTIONS(aidat[idx[0]]) & (1 << idx[1]));
+
+ MemSet(nulls, 0, sizeof(nulls));
+
+ tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+ result = HeapTupleGetDatum(tuple);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ }
+
+ SRF_RETURN_DONE(funcctx);
+}
+
+
+/*
+ * has_table_privilege variants
+ * These are all named "has_table_privilege" at the SQL level.
+ * They take various combinations of relation name, relation OID,
+ * user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has the indicated
+ * privilege, false if not. The variants that take a relation OID
+ * return NULL if the OID doesn't exist (rather than failing, as
+ * they did before Postgres 8.4).
+ */
+
+/*
+ * has_table_privilege_name_name
+ * Check user privileges on a table given
* name username, text tablename, and text priv name.
*/
Datum
has_table_privilege_name_name(PG_FUNCTION_ARGS)
{
- Name username = PG_GETARG_NAME(0);
+ Name rolename = PG_GETARG_NAME(0);
text *tablename = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
+ Oid roleid;
Oid tableoid;
AclMode mode;
AclResult aclresult;
- usesysid = get_usesysid(NameStr(*username));
+ roleid = get_roleid_checked(NameStr(*rolename));
tableoid = convert_table_name(tablename);
mode = convert_table_priv_string(priv_type_text);
- aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
{
text *tablename = PG_GETARG_TEXT_P(0);
text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
+ Oid roleid;
Oid tableoid;
AclMode mode;
AclResult aclresult;
- usesysid = GetUserId();
+ roleid = GetUserId();
tableoid = convert_table_name(tablename);
mode = convert_table_priv_string(priv_type_text);
- aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
Name username = PG_GETARG_NAME(0);
Oid tableoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
+ Oid roleid;
AclMode mode;
AclResult aclresult;
- usesysid = get_usesysid(NameStr(*username));
+ roleid = get_roleid_checked(NameStr(*username));
mode = convert_table_priv_string(priv_type_text);
- aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
+ if (!SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(tableoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
{
Oid tableoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
+ Oid roleid;
AclMode mode;
AclResult aclresult;
- usesysid = GetUserId();
+ roleid = GetUserId();
mode = convert_table_priv_string(priv_type_text);
- aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
+ if (!SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(tableoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_table_privilege_id_name
* Check user privileges on a table given
- * usesysid, text tablename, and text priv name.
+ * roleid, text tablename, and text priv name.
*/
Datum
has_table_privilege_id_name(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
+ Oid roleid = PG_GETARG_OID(0);
text *tablename = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
Oid tableoid;
tableoid = convert_table_name(tablename);
mode = convert_table_priv_string(priv_type_text);
- aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_table_privilege_id_id
* Check user privileges on a table given
- * usesysid, table oid, and text priv name.
+ * roleid, table oid, and text priv name.
*/
Datum
has_table_privilege_id_id(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
+ Oid roleid = PG_GETARG_OID(0);
Oid tableoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
AclMode mode;
mode = convert_table_priv_string(priv_type_text);
- aclresult = pg_class_aclcheck(tableoid, usesysid, mode);
+ if (!SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(tableoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
static AclMode
convert_table_priv_string(text *priv_type_text)
{
- char *priv_type;
-
- priv_type = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(priv_type_text)));
-
- /*
- * Return mode from priv_type string
- */
- if (pg_strcasecmp(priv_type, "SELECT") == 0)
- return ACL_SELECT;
- if (pg_strcasecmp(priv_type, "SELECT WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_SELECT);
-
- if (pg_strcasecmp(priv_type, "INSERT") == 0)
- return ACL_INSERT;
- if (pg_strcasecmp(priv_type, "INSERT WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_INSERT);
-
- if (pg_strcasecmp(priv_type, "UPDATE") == 0)
- return ACL_UPDATE;
- if (pg_strcasecmp(priv_type, "UPDATE WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_UPDATE);
-
- if (pg_strcasecmp(priv_type, "DELETE") == 0)
- return ACL_DELETE;
- if (pg_strcasecmp(priv_type, "DELETE WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_DELETE);
-
- if (pg_strcasecmp(priv_type, "RULE") == 0)
- return ACL_RULE;
- if (pg_strcasecmp(priv_type, "RULE WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_RULE);
-
- if (pg_strcasecmp(priv_type, "REFERENCES") == 0)
- return ACL_REFERENCES;
- if (pg_strcasecmp(priv_type, "REFERENCES WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_REFERENCES);
-
- if (pg_strcasecmp(priv_type, "TRIGGER") == 0)
- return ACL_TRIGGER;
- if (pg_strcasecmp(priv_type, "TRIGGER WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_TRIGGER);
-
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized privilege type: \"%s\"", priv_type)));
- return ACL_NO_RIGHTS; /* keep compiler quiet */
+ static const priv_map table_priv_map[] = {
+ {"SELECT", ACL_SELECT},
+ {"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
+ {"INSERT", ACL_INSERT},
+ {"INSERT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_INSERT)},
+ {"UPDATE", ACL_UPDATE},
+ {"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
+ {"DELETE", ACL_DELETE},
+ {"DELETE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_DELETE)},
+ {"TRUNCATE", ACL_TRUNCATE},
+ {"TRUNCATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRUNCATE)},
+ {"REFERENCES", ACL_REFERENCES},
+ {"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+ {"TRIGGER", ACL_TRIGGER},
+ {"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
+ {"RULE", 0}, /* ignore old RULE privileges */
+ {"RULE WITH GRANT OPTION", 0},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_type_text, table_priv_map);
}
-
/*
- * has_database_privilege variants
- * These are all named "has_database_privilege" at the SQL level.
- * They take various combinations of database name, database OID,
- * user name, user sysid, or implicit user = current_user.
+ * has_sequence_privilege variants
+ * These are all named "has_sequence_privilege" at the SQL level.
+ * They take various combinations of relation name, relation OID,
+ * user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
- * privilege, false if not.
+ * privilege, false if not. The variants that take a relation OID
+ * return NULL if the OID doesn't exist.
*/
/*
- * has_database_privilege_name_name
- * Check user privileges on a database given
- * name username, text databasename, and text priv name.
+ * has_sequence_privilege_name_name
+ * Check user privileges on a sequence given
+ * name username, text sequencename, and text priv name.
*/
Datum
-has_database_privilege_name_name(PG_FUNCTION_ARGS)
+has_sequence_privilege_name_name(PG_FUNCTION_ARGS)
{
- Name username = PG_GETARG_NAME(0);
- text *databasename = PG_GETARG_TEXT_P(1);
+ Name rolename = PG_GETARG_NAME(0);
+ text *sequencename = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
- Oid databaseoid;
+ Oid roleid;
+ Oid sequenceoid;
AclMode mode;
AclResult aclresult;
- usesysid = get_usesysid(NameStr(*username));
- databaseoid = convert_database_name(databasename);
- mode = convert_database_priv_string(priv_type_text);
+ roleid = get_roleid_checked(NameStr(*rolename));
+ mode = convert_sequence_priv_string(priv_type_text);
+ sequenceoid = convert_table_name(sequencename);
+ if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ text_to_cstring(sequencename))));
- aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
+ aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_database_privilege_name
- * Check user privileges on a database given
- * text databasename and text priv name.
+ * has_sequence_privilege_name
+ * Check user privileges on a sequence given
+ * text sequencename and text priv name.
* current_user is assumed
*/
Datum
-has_database_privilege_name(PG_FUNCTION_ARGS)
+has_sequence_privilege_name(PG_FUNCTION_ARGS)
{
- text *databasename = PG_GETARG_TEXT_P(0);
+ text *sequencename = PG_GETARG_TEXT_P(0);
text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
- Oid databaseoid;
+ Oid roleid;
+ Oid sequenceoid;
AclMode mode;
AclResult aclresult;
- usesysid = GetUserId();
- databaseoid = convert_database_name(databasename);
- mode = convert_database_priv_string(priv_type_text);
+ roleid = GetUserId();
+ mode = convert_sequence_priv_string(priv_type_text);
+ sequenceoid = convert_table_name(sequencename);
+ if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ text_to_cstring(sequencename))));
- aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
+ aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_database_privilege_name_id
- * Check user privileges on a database given
- * name usename, database oid, and text priv name.
+ * has_sequence_privilege_name_id
+ * Check user privileges on a sequence given
+ * name usename, sequence oid, and text priv name.
*/
Datum
-has_database_privilege_name_id(PG_FUNCTION_ARGS)
+has_sequence_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
- Oid databaseoid = PG_GETARG_OID(1);
+ Oid sequenceoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
+ Oid roleid;
AclMode mode;
AclResult aclresult;
+ char relkind;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ mode = convert_sequence_priv_string(priv_type_text);
+ relkind = get_rel_relkind(sequenceoid);
+ if (relkind == '\0')
+ PG_RETURN_NULL();
+ else if (relkind != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ get_rel_name(sequenceoid))));
- usesysid = get_usesysid(NameStr(*username));
- mode = convert_database_priv_string(priv_type_text);
-
- aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
+ aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_database_privilege_id
- * Check user privileges on a database given
- * database oid, and text priv name.
+ * has_sequence_privilege_id
+ * Check user privileges on a sequence given
+ * sequence oid, and text priv name.
* current_user is assumed
*/
Datum
-has_database_privilege_id(PG_FUNCTION_ARGS)
+has_sequence_privilege_id(PG_FUNCTION_ARGS)
{
- Oid databaseoid = PG_GETARG_OID(0);
+ Oid sequenceoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
+ Oid roleid;
AclMode mode;
AclResult aclresult;
+ char relkind;
+
+ roleid = GetUserId();
+ mode = convert_sequence_priv_string(priv_type_text);
+ relkind = get_rel_relkind(sequenceoid);
+ if (relkind == '\0')
+ PG_RETURN_NULL();
+ else if (relkind != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ get_rel_name(sequenceoid))));
- usesysid = GetUserId();
- mode = convert_database_priv_string(priv_type_text);
-
- aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
+ aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_database_privilege_id_name
- * Check user privileges on a database given
- * usesysid, text databasename, and text priv name.
+ * has_sequence_privilege_id_name
+ * Check user privileges on a sequence given
+ * roleid, text sequencename, and text priv name.
*/
Datum
-has_database_privilege_id_name(PG_FUNCTION_ARGS)
+has_sequence_privilege_id_name(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
- text *databasename = PG_GETARG_TEXT_P(1);
+ Oid roleid = PG_GETARG_OID(0);
+ text *sequencename = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- Oid databaseoid;
+ Oid sequenceoid;
AclMode mode;
AclResult aclresult;
- databaseoid = convert_database_name(databasename);
- mode = convert_database_priv_string(priv_type_text);
+ mode = convert_sequence_priv_string(priv_type_text);
+ sequenceoid = convert_table_name(sequencename);
+ if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ text_to_cstring(sequencename))));
- aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
+ aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_database_privilege_id_id
- * Check user privileges on a database given
- * usesysid, database oid, and text priv name.
+ * has_sequence_privilege_id_id
+ * Check user privileges on a sequence given
+ * roleid, sequence oid, and text priv name.
*/
Datum
-has_database_privilege_id_id(PG_FUNCTION_ARGS)
+has_sequence_privilege_id_id(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
- Oid databaseoid = PG_GETARG_OID(1);
+ Oid roleid = PG_GETARG_OID(0);
+ Oid sequenceoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
AclMode mode;
AclResult aclresult;
+ char relkind;
- mode = convert_database_priv_string(priv_type_text);
+ mode = convert_sequence_priv_string(priv_type_text);
+ relkind = get_rel_relkind(sequenceoid);
+ if (relkind == '\0')
+ PG_RETURN_NULL();
+ else if (relkind != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ get_rel_name(sequenceoid))));
- aclresult = pg_database_aclcheck(databaseoid, usesysid, mode);
+ aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * Support routines for has_database_privilege family.
- */
-
-/*
- * Given a database name expressed as a string, look it up and return Oid
- */
-static Oid
-convert_database_name(text *databasename)
-{
- char *dbname;
- Oid oid;
-
- dbname = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(databasename)));
-
- oid = get_database_oid(dbname);
- if (!OidIsValid(oid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_DATABASE),
- errmsg("database \"%s\" does not exist", dbname)));
-
- return oid;
-}
-
-/*
- * convert_database_priv_string
+ * convert_sequence_priv_string
* Convert text string to AclMode value.
*/
static AclMode
-convert_database_priv_string(text *priv_type_text)
+convert_sequence_priv_string(text *priv_type_text)
{
- char *priv_type;
-
- priv_type = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(priv_type_text)));
-
- /*
- * Return mode from priv_type string
- */
- if (pg_strcasecmp(priv_type, "CREATE") == 0)
- return ACL_CREATE;
- if (pg_strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_CREATE);
-
- if (pg_strcasecmp(priv_type, "TEMPORARY") == 0)
- return ACL_CREATE_TEMP;
- if (pg_strcasecmp(priv_type, "TEMPORARY WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
-
- if (pg_strcasecmp(priv_type, "TEMP") == 0)
- return ACL_CREATE_TEMP;
- if (pg_strcasecmp(priv_type, "TEMP WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
-
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized privilege type: \"%s\"", priv_type)));
- return ACL_NO_RIGHTS; /* keep compiler quiet */
+ static const priv_map sequence_priv_map[] = {
+ { "USAGE", ACL_USAGE },
+ { "SELECT", ACL_SELECT },
+ { "UPDATE", ACL_UPDATE },
+ { NULL, 0 }
+ };
+
+ return convert_any_priv_string(priv_type_text, sequence_priv_map);
}
/*
- * has_function_privilege variants
- * These are all named "has_function_privilege" at the SQL level.
- * They take various combinations of function name, function OID,
- * user name, user sysid, or implicit user = current_user.
+ * has_any_column_privilege variants
+ * These are all named "has_any_column_privilege" at the SQL level.
+ * They take various combinations of relation name, relation OID,
+ * user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
- * privilege, false if not.
+ * privilege for any column of the table, false if not. The variants
+ * that take a relation OID return NULL if the OID doesn't exist.
*/
/*
- * has_function_privilege_name_name
- * Check user privileges on a function given
- * name username, text functionname, and text priv name.
+ * has_any_column_privilege_name_name
+ * Check user privileges on any column of a table given
+ * name username, text tablename, and text priv name.
*/
Datum
-has_function_privilege_name_name(PG_FUNCTION_ARGS)
+has_any_column_privilege_name_name(PG_FUNCTION_ARGS)
{
- Name username = PG_GETARG_NAME(0);
- text *functionname = PG_GETARG_TEXT_P(1);
+ Name rolename = PG_GETARG_NAME(0);
+ text *tablename = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
- Oid functionoid;
+ Oid roleid;
+ Oid tableoid;
AclMode mode;
AclResult aclresult;
- usesysid = get_usesysid(NameStr(*username));
- functionoid = convert_function_name(functionname);
- mode = convert_function_priv_string(priv_type_text);
+ roleid = get_roleid_checked(NameStr(*rolename));
+ tableoid = convert_table_name(tablename);
+ mode = convert_column_priv_string(priv_type_text);
- aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
+ /* First check at table level, then examine each column if needed */
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+ if (aclresult != ACLCHECK_OK)
+ aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
+ ACLMASK_ANY);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_function_privilege_name
- * Check user privileges on a function given
- * text functionname and text priv name.
+ * has_any_column_privilege_name
+ * Check user privileges on any column of a table given
+ * text tablename and text priv name.
* current_user is assumed
*/
Datum
-has_function_privilege_name(PG_FUNCTION_ARGS)
+has_any_column_privilege_name(PG_FUNCTION_ARGS)
{
- text *functionname = PG_GETARG_TEXT_P(0);
+ text *tablename = PG_GETARG_TEXT_P(0);
text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
- Oid functionoid;
+ Oid roleid;
+ Oid tableoid;
AclMode mode;
AclResult aclresult;
- usesysid = GetUserId();
- functionoid = convert_function_name(functionname);
- mode = convert_function_priv_string(priv_type_text);
+ roleid = GetUserId();
+ tableoid = convert_table_name(tablename);
+ mode = convert_column_priv_string(priv_type_text);
- aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
+ /* First check at table level, then examine each column if needed */
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+ if (aclresult != ACLCHECK_OK)
+ aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
+ ACLMASK_ANY);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_function_privilege_name_id
- * Check user privileges on a function given
- * name usename, function oid, and text priv name.
+ * has_any_column_privilege_name_id
+ * Check user privileges on any column of a table given
+ * name usename, table oid, and text priv name.
*/
Datum
-has_function_privilege_name_id(PG_FUNCTION_ARGS)
+has_any_column_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
- Oid functionoid = PG_GETARG_OID(1);
+ Oid tableoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
+ Oid roleid;
AclMode mode;
AclResult aclresult;
- usesysid = get_usesysid(NameStr(*username));
- mode = convert_function_priv_string(priv_type_text);
+ roleid = get_roleid_checked(NameStr(*username));
+ mode = convert_column_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(tableoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
- aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
+ /* First check at table level, then examine each column if needed */
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+ if (aclresult != ACLCHECK_OK)
+ aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
+ ACLMASK_ANY);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_function_privilege_id
- * Check user privileges on a function given
- * function oid, and text priv name.
+ * has_any_column_privilege_id
+ * Check user privileges on any column of a table given
+ * table oid, and text priv name.
* current_user is assumed
*/
Datum
-has_function_privilege_id(PG_FUNCTION_ARGS)
+has_any_column_privilege_id(PG_FUNCTION_ARGS)
{
- Oid functionoid = PG_GETARG_OID(0);
+ Oid tableoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
+ Oid roleid;
AclMode mode;
AclResult aclresult;
- usesysid = GetUserId();
- mode = convert_function_priv_string(priv_type_text);
+ roleid = GetUserId();
+ mode = convert_column_priv_string(priv_type_text);
- aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
+ if (!SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(tableoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ /* First check at table level, then examine each column if needed */
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+ if (aclresult != ACLCHECK_OK)
+ aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
+ ACLMASK_ANY);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_function_privilege_id_name
- * Check user privileges on a function given
- * usesysid, text functionname, and text priv name.
+ * has_any_column_privilege_id_name
+ * Check user privileges on any column of a table given
+ * roleid, text tablename, and text priv name.
*/
Datum
-has_function_privilege_id_name(PG_FUNCTION_ARGS)
+has_any_column_privilege_id_name(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
- text *functionname = PG_GETARG_TEXT_P(1);
+ Oid roleid = PG_GETARG_OID(0);
+ text *tablename = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- Oid functionoid;
+ Oid tableoid;
AclMode mode;
AclResult aclresult;
- functionoid = convert_function_name(functionname);
- mode = convert_function_priv_string(priv_type_text);
+ tableoid = convert_table_name(tablename);
+ mode = convert_column_priv_string(priv_type_text);
- aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
+ /* First check at table level, then examine each column if needed */
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+ if (aclresult != ACLCHECK_OK)
+ aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
+ ACLMASK_ANY);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_function_privilege_id_id
- * Check user privileges on a function given
- * usesysid, function oid, and text priv name.
+ * has_any_column_privilege_id_id
+ * Check user privileges on any column of a table given
+ * roleid, table oid, and text priv name.
*/
Datum
-has_function_privilege_id_id(PG_FUNCTION_ARGS)
+has_any_column_privilege_id_id(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
- Oid functionoid = PG_GETARG_OID(1);
+ Oid roleid = PG_GETARG_OID(0);
+ Oid tableoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
AclMode mode;
AclResult aclresult;
- mode = convert_function_priv_string(priv_type_text);
+ mode = convert_column_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(tableoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
- aclresult = pg_proc_aclcheck(functionoid, usesysid, mode);
+ /* First check at table level, then examine each column if needed */
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+ if (aclresult != ACLCHECK_OK)
+ aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
+ ACLMASK_ANY);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
+
/*
- * Support routines for has_function_privilege family.
+ * has_column_privilege variants
+ * These are all named "has_column_privilege" at the SQL level.
+ * They take various combinations of relation name, relation OID,
+ * column name, column attnum, user name, user OID, or
+ * implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has the indicated
+ * privilege, false if not. The variants that take a relation OID
+ * and an integer attnum return NULL (rather than throwing an error)
+ * if the column doesn't exist or is dropped.
*/
/*
- * Given a function name expressed as a string, look it up and return Oid
+ * column_privilege_check: check column privileges, but don't throw an error
+ * for dropped column or table
+ *
+ * Returns 1 if have the privilege, 0 if not, -1 if dropped column/table.
*/
-static Oid
-convert_function_name(text *functionname)
+static int
+column_privilege_check(Oid tableoid, AttrNumber attnum,
+ Oid roleid, AclMode mode)
{
- char *funcname;
- Oid oid;
+ AclResult aclresult;
+ HeapTuple attTuple;
+ Form_pg_attribute attributeForm;
- funcname = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(functionname)));
+ /*
+ * First check if we have the privilege at the table level. We check
+ * existence of the pg_class row before risking calling pg_class_aclcheck.
+ * Note: it might seem there's a race condition against concurrent DROP,
+ * but really it's safe because there will be no syscache flush between
+ * here and there. So if we see the row in the syscache, so will
+ * pg_class_aclcheck.
+ */
+ if (!SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(tableoid),
+ 0, 0, 0))
+ return -1;
- oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein,
- CStringGetDatum(funcname)));
+ aclresult = pg_class_aclcheck(tableoid, roleid, mode);
- if (!OidIsValid(oid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("function \"%s\" does not exist", funcname)));
+ if (aclresult == ACLCHECK_OK)
+ return true;
- return oid;
+ /*
+ * No table privilege, so try per-column privileges. Again, we have to
+ * check for dropped attribute first, and we rely on the syscache not to
+ * notice a concurrent drop before pg_attribute_aclcheck fetches the row.
+ */
+ attTuple = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(tableoid),
+ Int16GetDatum(attnum),
+ 0, 0);
+ if (!HeapTupleIsValid(attTuple))
+ return -1;
+ attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
+ if (attributeForm->attisdropped)
+ {
+ ReleaseSysCache(attTuple);
+ return -1;
+ }
+ ReleaseSysCache(attTuple);
+
+ aclresult = pg_attribute_aclcheck(tableoid, attnum, roleid, mode);
+
+ return (aclresult == ACLCHECK_OK);
}
/*
- * convert_function_priv_string
- * Convert text string to AclMode value.
+ * has_column_privilege_name_name_name
+ * Check user privileges on a column given
+ * name username, text tablename, text colname, and text priv name.
*/
-static AclMode
-convert_function_priv_string(text *priv_type_text)
+Datum
+has_column_privilege_name_name_name(PG_FUNCTION_ARGS)
{
- char *priv_type;
-
- priv_type = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(priv_type_text)));
+ Name rolename = PG_GETARG_NAME(0);
+ text *tablename = PG_GETARG_TEXT_P(1);
+ text *column = PG_GETARG_TEXT_P(2);
+ text *priv_type_text = PG_GETARG_TEXT_P(3);
+ Oid roleid;
+ Oid tableoid;
+ AttrNumber colattnum;
+ AclMode mode;
+ int privresult;
- /*
- * Return mode from priv_type string
- */
- if (pg_strcasecmp(priv_type, "EXECUTE") == 0)
- return ACL_EXECUTE;
- if (pg_strcasecmp(priv_type, "EXECUTE WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_EXECUTE);
+ roleid = get_roleid_checked(NameStr(*rolename));
+ tableoid = convert_table_name(tablename);
+ colattnum = convert_column_name(tableoid, column);
+ mode = convert_column_priv_string(priv_type_text);
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized privilege type: \"%s\"", priv_type)));
- return ACL_NO_RIGHTS; /* keep compiler quiet */
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
}
-
/*
- * has_language_privilege variants
- * These are all named "has_language_privilege" at the SQL level.
- * They take various combinations of language name, language OID,
- * user name, user sysid, or implicit user = current_user.
- *
- * The result is a boolean value: true if user has the indicated
- * privilege, false if not.
+ * has_column_privilege_name_name_attnum
+ * Check user privileges on a column given
+ * name username, text tablename, int attnum, and text priv name.
*/
+Datum
+has_column_privilege_name_name_attnum(PG_FUNCTION_ARGS)
+{
+ Name rolename = PG_GETARG_NAME(0);
+ text *tablename = PG_GETARG_TEXT_P(1);
+ AttrNumber colattnum = PG_GETARG_INT16(2);
+ text *priv_type_text = PG_GETARG_TEXT_P(3);
+ Oid roleid;
+ Oid tableoid;
+ AclMode mode;
+ int privresult;
+
+ roleid = get_roleid_checked(NameStr(*rolename));
+ tableoid = convert_table_name(tablename);
+ mode = convert_column_priv_string(priv_type_text);
+
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
+}
/*
- * has_language_privilege_name_name
- * Check user privileges on a language given
- * name username, text languagename, and text priv name.
+ * has_column_privilege_name_id_name
+ * Check user privileges on a column given
+ * name username, table oid, text colname, and text priv name.
*/
Datum
-has_language_privilege_name_name(PG_FUNCTION_ARGS)
+has_column_privilege_name_id_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
- text *languagename = PG_GETARG_TEXT_P(1);
- text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
- Oid languageoid;
+ Oid tableoid = PG_GETARG_OID(1);
+ text *column = PG_GETARG_TEXT_P(2);
+ text *priv_type_text = PG_GETARG_TEXT_P(3);
+ Oid roleid;
+ AttrNumber colattnum;
AclMode mode;
- AclResult aclresult;
+ int privresult;
- usesysid = get_usesysid(NameStr(*username));
- languageoid = convert_language_name(languagename);
- mode = convert_language_priv_string(priv_type_text);
+ roleid = get_roleid_checked(NameStr(*username));
+ colattnum = convert_column_name(tableoid, column);
+ mode = convert_column_priv_string(priv_type_text);
- aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
+}
- PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+/*
+ * has_column_privilege_name_id_attnum
+ * Check user privileges on a column given
+ * name username, table oid, int attnum, and text priv name.
+ */
+Datum
+has_column_privilege_name_id_attnum(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid tableoid = PG_GETARG_OID(1);
+ AttrNumber colattnum = PG_GETARG_INT16(2);
+ text *priv_type_text = PG_GETARG_TEXT_P(3);
+ Oid roleid;
+ AclMode mode;
+ int privresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ mode = convert_column_priv_string(priv_type_text);
+
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
}
/*
- * has_language_privilege_name
- * Check user privileges on a language given
- * text languagename and text priv name.
- * current_user is assumed
+ * has_column_privilege_id_name_name
+ * Check user privileges on a column given
+ * oid roleid, text tablename, text colname, and text priv name.
*/
Datum
-has_language_privilege_name(PG_FUNCTION_ARGS)
+has_column_privilege_id_name_name(PG_FUNCTION_ARGS)
{
- text *languagename = PG_GETARG_TEXT_P(0);
- text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
- Oid languageoid;
+ Oid roleid = PG_GETARG_OID(0);
+ text *tablename = PG_GETARG_TEXT_P(1);
+ text *column = PG_GETARG_TEXT_P(2);
+ text *priv_type_text = PG_GETARG_TEXT_P(3);
+ Oid tableoid;
+ AttrNumber colattnum;
AclMode mode;
- AclResult aclresult;
+ int privresult;
- usesysid = GetUserId();
- languageoid = convert_language_name(languagename);
- mode = convert_language_priv_string(priv_type_text);
+ tableoid = convert_table_name(tablename);
+ colattnum = convert_column_name(tableoid, column);
+ mode = convert_column_priv_string(priv_type_text);
+
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
+}
+
+/*
+ * has_column_privilege_id_name_attnum
+ * Check user privileges on a column given
+ * oid roleid, text tablename, int attnum, and text priv name.
+ */
+Datum
+has_column_privilege_id_name_attnum(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *tablename = PG_GETARG_TEXT_P(1);
+ AttrNumber colattnum = PG_GETARG_INT16(2);
+ text *priv_type_text = PG_GETARG_TEXT_P(3);
+ Oid tableoid;
+ AclMode mode;
+ int privresult;
- aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
+ tableoid = convert_table_name(tablename);
+ mode = convert_column_priv_string(priv_type_text);
- PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
}
/*
- * has_language_privilege_name_id
- * Check user privileges on a language given
- * name usename, language oid, and text priv name.
+ * has_column_privilege_id_id_name
+ * Check user privileges on a column given
+ * oid roleid, table oid, text colname, and text priv name.
*/
Datum
-has_language_privilege_name_id(PG_FUNCTION_ARGS)
+has_column_privilege_id_id_name(PG_FUNCTION_ARGS)
{
- Name username = PG_GETARG_NAME(0);
- Oid languageoid = PG_GETARG_OID(1);
- text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
+ Oid roleid = PG_GETARG_OID(0);
+ Oid tableoid = PG_GETARG_OID(1);
+ text *column = PG_GETARG_TEXT_P(2);
+ text *priv_type_text = PG_GETARG_TEXT_P(3);
+ AttrNumber colattnum;
AclMode mode;
- AclResult aclresult;
+ int privresult;
- usesysid = get_usesysid(NameStr(*username));
- mode = convert_language_priv_string(priv_type_text);
+ colattnum = convert_column_name(tableoid, column);
+ mode = convert_column_priv_string(priv_type_text);
- aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
+}
- PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+/*
+ * has_column_privilege_id_id_attnum
+ * Check user privileges on a column given
+ * oid roleid, table oid, int attnum, and text priv name.
+ */
+Datum
+has_column_privilege_id_id_attnum(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid tableoid = PG_GETARG_OID(1);
+ AttrNumber colattnum = PG_GETARG_INT16(2);
+ text *priv_type_text = PG_GETARG_TEXT_P(3);
+ AclMode mode;
+ int privresult;
+
+ mode = convert_column_priv_string(priv_type_text);
+
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
}
/*
- * has_language_privilege_id
- * Check user privileges on a language given
- * language oid, and text priv name.
+ * has_column_privilege_name_name
+ * Check user privileges on a column given
+ * text tablename, text colname, and text priv name.
* current_user is assumed
*/
Datum
-has_language_privilege_id(PG_FUNCTION_ARGS)
+has_column_privilege_name_name(PG_FUNCTION_ARGS)
{
- Oid languageoid = PG_GETARG_OID(0);
- text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
+ text *tablename = PG_GETARG_TEXT_P(0);
+ text *column = PG_GETARG_TEXT_P(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ Oid tableoid;
+ AttrNumber colattnum;
AclMode mode;
- AclResult aclresult;
-
- usesysid = GetUserId();
- mode = convert_language_priv_string(priv_type_text);
+ int privresult;
- aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
+ roleid = GetUserId();
+ tableoid = convert_table_name(tablename);
+ colattnum = convert_column_name(tableoid, column);
+ mode = convert_column_priv_string(priv_type_text);
- PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
}
/*
- * has_language_privilege_id_name
- * Check user privileges on a language given
- * usesysid, text languagename, and text priv name.
+ * has_column_privilege_name_attnum
+ * Check user privileges on a column given
+ * text tablename, int attnum, and text priv name.
+ * current_user is assumed
*/
Datum
-has_language_privilege_id_name(PG_FUNCTION_ARGS)
+has_column_privilege_name_attnum(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
- text *languagename = PG_GETARG_TEXT_P(1);
+ text *tablename = PG_GETARG_TEXT_P(0);
+ AttrNumber colattnum = PG_GETARG_INT16(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- Oid languageoid;
+ Oid roleid;
+ Oid tableoid;
AclMode mode;
- AclResult aclresult;
-
- languageoid = convert_language_name(languagename);
- mode = convert_language_priv_string(priv_type_text);
+ int privresult;
- aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
+ roleid = GetUserId();
+ tableoid = convert_table_name(tablename);
+ mode = convert_column_priv_string(priv_type_text);
- PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
}
/*
- * has_language_privilege_id_id
- * Check user privileges on a language given
- * usesysid, language oid, and text priv name.
+ * has_column_privilege_id_name
+ * Check user privileges on a column given
+ * table oid, text colname, and text priv name.
+ * current_user is assumed
*/
Datum
-has_language_privilege_id_id(PG_FUNCTION_ARGS)
+has_column_privilege_id_name(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
- Oid languageoid = PG_GETARG_OID(1);
+ Oid tableoid = PG_GETARG_OID(0);
+ text *column = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ AttrNumber colattnum;
AclMode mode;
- AclResult aclresult;
+ int privresult;
- mode = convert_language_priv_string(priv_type_text);
+ roleid = GetUserId();
+ colattnum = convert_column_name(tableoid, column);
+ mode = convert_column_priv_string(priv_type_text);
- aclresult = pg_language_aclcheck(languageoid, usesysid, mode);
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
+}
- PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+/*
+ * has_column_privilege_id_attnum
+ * Check user privileges on a column given
+ * table oid, int attnum, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_column_privilege_id_attnum(PG_FUNCTION_ARGS)
+{
+ Oid tableoid = PG_GETARG_OID(0);
+ AttrNumber colattnum = PG_GETARG_INT16(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ AclMode mode;
+ int privresult;
+
+ roleid = GetUserId();
+ mode = convert_column_priv_string(priv_type_text);
+
+ privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
+ if (privresult < 0)
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(privresult);
}
/*
- * Support routines for has_language_privilege family.
+ * Support routines for has_column_privilege family.
*/
/*
- * Given a language name expressed as a string, look it up and return Oid
+ * Given a table OID and a column name expressed as a string, look it up
+ * and return the column number
*/
-static Oid
-convert_language_name(text *languagename)
+static AttrNumber
+convert_column_name(Oid tableoid, text *column)
{
- char *langname;
- Oid oid;
+ AttrNumber attnum;
+ char *colname;
- langname = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(languagename)));
-
- oid = GetSysCacheOid(LANGNAME,
- CStringGetDatum(langname),
- 0, 0, 0);
- if (!OidIsValid(oid))
+ colname = text_to_cstring(column);
+ attnum = get_attnum(tableoid, colname);
+ if (attnum == InvalidAttrNumber)
ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("language \"%s\" does not exist", langname)));
-
- return oid;
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ colname, get_rel_name(tableoid))));
+ pfree(colname);
+ return attnum;
}
/*
- * convert_language_priv_string
+ * convert_column_priv_string
* Convert text string to AclMode value.
*/
static AclMode
-convert_language_priv_string(text *priv_type_text)
+convert_column_priv_string(text *priv_type_text)
{
- char *priv_type;
-
- priv_type = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(priv_type_text)));
-
- /*
- * Return mode from priv_type string
- */
- if (pg_strcasecmp(priv_type, "USAGE") == 0)
- return ACL_USAGE;
- if (pg_strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_USAGE);
-
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized privilege type: \"%s\"", priv_type)));
- return ACL_NO_RIGHTS; /* keep compiler quiet */
+ static const priv_map column_priv_map[] = {
+ {"SELECT", ACL_SELECT},
+ {"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
+ {"INSERT", ACL_INSERT},
+ {"INSERT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_INSERT)},
+ {"UPDATE", ACL_UPDATE},
+ {"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
+ {"REFERENCES", ACL_REFERENCES},
+ {"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_type_text, column_priv_map);
}
/*
- * has_schema_privilege variants
- * These are all named "has_schema_privilege" at the SQL level.
- * They take various combinations of schema name, schema OID,
- * user name, user sysid, or implicit user = current_user.
+ * has_database_privilege variants
+ * These are all named "has_database_privilege" at the SQL level.
+ * They take various combinations of database name, database OID,
+ * user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
- * privilege, false if not.
+ * privilege, false if not, or NULL if object doesn't exist.
*/
/*
- * has_schema_privilege_name_name
- * Check user privileges on a schema given
- * name username, text schemaname, and text priv name.
+ * has_database_privilege_name_name
+ * Check user privileges on a database given
+ * name username, text databasename, and text priv name.
*/
Datum
-has_schema_privilege_name_name(PG_FUNCTION_ARGS)
+has_database_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
- text *schemaname = PG_GETARG_TEXT_P(1);
+ text *databasename = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
- Oid schemaoid;
+ Oid roleid;
+ Oid databaseoid;
AclMode mode;
AclResult aclresult;
- usesysid = get_usesysid(NameStr(*username));
- schemaoid = convert_schema_name(schemaname);
- mode = convert_schema_priv_string(priv_type_text);
+ roleid = get_roleid_checked(NameStr(*username));
+ databaseoid = convert_database_name(databasename);
+ mode = convert_database_priv_string(priv_type_text);
- aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
+ aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_schema_privilege_name
- * Check user privileges on a schema given
- * text schemaname and text priv name.
+ * has_database_privilege_name
+ * Check user privileges on a database given
+ * text databasename and text priv name.
* current_user is assumed
*/
Datum
-has_schema_privilege_name(PG_FUNCTION_ARGS)
+has_database_privilege_name(PG_FUNCTION_ARGS)
{
- text *schemaname = PG_GETARG_TEXT_P(0);
+ text *databasename = PG_GETARG_TEXT_P(0);
text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
- Oid schemaoid;
+ Oid roleid;
+ Oid databaseoid;
AclMode mode;
AclResult aclresult;
- usesysid = GetUserId();
- schemaoid = convert_schema_name(schemaname);
- mode = convert_schema_priv_string(priv_type_text);
+ roleid = GetUserId();
+ databaseoid = convert_database_name(databasename);
+ mode = convert_database_priv_string(priv_type_text);
- aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
+ aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_schema_privilege_name_id
- * Check user privileges on a schema given
- * name usename, schema oid, and text priv name.
+ * has_database_privilege_name_id
+ * Check user privileges on a database given
+ * name usename, database oid, and text priv name.
*/
Datum
-has_schema_privilege_name_id(PG_FUNCTION_ARGS)
+has_database_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
- Oid schemaoid = PG_GETARG_OID(1);
+ Oid databaseoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
+ Oid roleid;
AclMode mode;
AclResult aclresult;
- usesysid = get_usesysid(NameStr(*username));
- mode = convert_schema_priv_string(priv_type_text);
+ roleid = get_roleid_checked(NameStr(*username));
+ mode = convert_database_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(DATABASEOID,
+ ObjectIdGetDatum(databaseoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
- aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
+ aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_schema_privilege_id
- * Check user privileges on a schema given
- * schema oid, and text priv name.
+ * has_database_privilege_id
+ * Check user privileges on a database given
+ * database oid, and text priv name.
* current_user is assumed
*/
Datum
-has_schema_privilege_id(PG_FUNCTION_ARGS)
+has_database_privilege_id(PG_FUNCTION_ARGS)
{
- Oid schemaoid = PG_GETARG_OID(0);
+ Oid databaseoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
+ Oid roleid;
AclMode mode;
AclResult aclresult;
- usesysid = GetUserId();
- mode = convert_schema_priv_string(priv_type_text);
+ roleid = GetUserId();
+ mode = convert_database_priv_string(priv_type_text);
- aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
+ if (!SearchSysCacheExists(DATABASEOID,
+ ObjectIdGetDatum(databaseoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_schema_privilege_id_name
- * Check user privileges on a schema given
- * usesysid, text schemaname, and text priv name.
+ * has_database_privilege_id_name
+ * Check user privileges on a database given
+ * roleid, text databasename, and text priv name.
*/
Datum
-has_schema_privilege_id_name(PG_FUNCTION_ARGS)
+has_database_privilege_id_name(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
- text *schemaname = PG_GETARG_TEXT_P(1);
+ Oid roleid = PG_GETARG_OID(0);
+ text *databasename = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- Oid schemaoid;
+ Oid databaseoid;
AclMode mode;
AclResult aclresult;
- schemaoid = convert_schema_name(schemaname);
- mode = convert_schema_priv_string(priv_type_text);
+ databaseoid = convert_database_name(databasename);
+ mode = convert_database_priv_string(priv_type_text);
- aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
+ aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_schema_privilege_id_id
- * Check user privileges on a schema given
- * usesysid, schema oid, and text priv name.
+ * has_database_privilege_id_id
+ * Check user privileges on a database given
+ * roleid, database oid, and text priv name.
*/
Datum
-has_schema_privilege_id_id(PG_FUNCTION_ARGS)
+has_database_privilege_id_id(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
- Oid schemaoid = PG_GETARG_OID(1);
+ Oid roleid = PG_GETARG_OID(0);
+ Oid databaseoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
AclMode mode;
AclResult aclresult;
- mode = convert_schema_priv_string(priv_type_text);
+ mode = convert_database_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(DATABASEOID,
+ ObjectIdGetDatum(databaseoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
- aclresult = pg_namespace_aclcheck(schemaoid, usesysid, mode);
+ aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * Support routines for has_schema_privilege family.
+ * Support routines for has_database_privilege family.
*/
/*
- * Given a schema name expressed as a string, look it up and return Oid
+ * Given a database name expressed as a string, look it up and return Oid
*/
static Oid
-convert_schema_name(text *schemaname)
+convert_database_name(text *databasename)
{
- char *nspname;
+ char *dbname = text_to_cstring(databasename);
Oid oid;
- nspname = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(schemaname)));
-
- oid = GetSysCacheOid(NAMESPACENAME,
- CStringGetDatum(nspname),
- 0, 0, 0);
+ oid = get_database_oid(dbname);
if (!OidIsValid(oid))
ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_SCHEMA),
- errmsg("schema \"%s\" does not exist", nspname)));
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("database \"%s\" does not exist", dbname)));
return oid;
}
/*
- * convert_schema_priv_string
+ * convert_database_priv_string
* Convert text string to AclMode value.
*/
static AclMode
-convert_schema_priv_string(text *priv_type_text)
+convert_database_priv_string(text *priv_type_text)
{
- char *priv_type;
-
- priv_type = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(priv_type_text)));
-
- /*
- * Return mode from priv_type string
- */
- if (pg_strcasecmp(priv_type, "CREATE") == 0)
- return ACL_CREATE;
- if (pg_strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_CREATE);
-
- if (pg_strcasecmp(priv_type, "USAGE") == 0)
- return ACL_USAGE;
- if (pg_strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_USAGE);
+ static const priv_map database_priv_map[] = {
+ {"CREATE", ACL_CREATE},
+ {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ {"TEMPORARY", ACL_CREATE_TEMP},
+ {"TEMPORARY WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
+ {"TEMP", ACL_CREATE_TEMP},
+ {"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
+ {"CONNECT", ACL_CONNECT},
+ {"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_type_text, database_priv_map);
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized privilege type: \"%s\"", priv_type)));
- return ACL_NO_RIGHTS; /* keep compiler quiet */
}
+
/*
- * has_tablespace_privilege variants
- * These are all named "has_tablespace_privilege" at the SQL level.
- * They take various combinations of tablespace name, tablespace OID,
- * user name, user sysid, or implicit user = current_user.
+ * has_foreign_data_wrapper_privilege variants
+ * These are all named "has_foreign_data_wrapper_privilege" at the SQL level.
+ * They take various combinations of foreign-data wrapper name,
+ * fdw OID, user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
* privilege, false if not.
*/
/*
- * has_tablespace_privilege_name_name
- * Check user privileges on a tablespace given
- * name username, text tablespacename, and text priv name.
+ * has_foreign_data_wrapper_privilege_name_name
+ * Check user privileges on a foreign-data wrapper given
+ * name username, text fdwname, and text priv name.
*/
Datum
-has_tablespace_privilege_name_name(PG_FUNCTION_ARGS)
+has_foreign_data_wrapper_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
- text *tablespacename = PG_GETARG_TEXT_P(1);
+ text *fdwname = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
- Oid tablespaceoid;
+ Oid roleid;
+ Oid fdwid;
AclMode mode;
AclResult aclresult;
- usesysid = get_usesysid(NameStr(*username));
- tablespaceoid = convert_tablespace_name(tablespacename);
- mode = convert_tablespace_priv_string(priv_type_text);
+ roleid = get_roleid_checked(NameStr(*username));
+ fdwid = convert_foreign_data_wrapper_name(fdwname);
+ mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
- aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+ aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_tablespace_privilege_name
- * Check user privileges on a tablespace given
- * text tablespacename and text priv name.
+ * has_foreign_data_wrapper_privilege_name
+ * Check user privileges on a foreign-data wrapper given
+ * text fdwname and text priv name.
* current_user is assumed
*/
Datum
-has_tablespace_privilege_name(PG_FUNCTION_ARGS)
+has_foreign_data_wrapper_privilege_name(PG_FUNCTION_ARGS)
{
- text *tablespacename = PG_GETARG_TEXT_P(0);
+ text *fdwname = PG_GETARG_TEXT_P(0);
text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
- Oid tablespaceoid;
+ Oid roleid;
+ Oid fdwid;
AclMode mode;
AclResult aclresult;
- usesysid = GetUserId();
- tablespaceoid = convert_tablespace_name(tablespacename);
- mode = convert_tablespace_priv_string(priv_type_text);
+ roleid = GetUserId();
+ fdwid = convert_foreign_data_wrapper_name(fdwname);
+ mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
- aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+ aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_tablespace_privilege_name_id
- * Check user privileges on a tablespace given
- * name usename, tablespace oid, and text priv name.
+ * has_foreign_data_wrapper_privilege_name_id
+ * Check user privileges on a foreign-data wrapper given
+ * name usename, foreign-data wrapper oid, and text priv name.
*/
Datum
-has_tablespace_privilege_name_id(PG_FUNCTION_ARGS)
+has_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
- Oid tablespaceoid = PG_GETARG_OID(1);
+ Oid fdwid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- int32 usesysid;
+ Oid roleid;
AclMode mode;
AclResult aclresult;
- usesysid = get_usesysid(NameStr(*username));
- mode = convert_tablespace_priv_string(priv_type_text);
+ roleid = get_roleid_checked(NameStr(*username));
+ mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
- aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+ aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_tablespace_privilege_id
- * Check user privileges on a tablespace given
- * tablespace oid, and text priv name.
+ * has_foreign_data_wrapper_privilege_id
+ * Check user privileges on a foreign-data wrapper given
+ * foreign-data wrapper oid, and text priv name.
* current_user is assumed
*/
Datum
-has_tablespace_privilege_id(PG_FUNCTION_ARGS)
+has_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS)
{
- Oid tablespaceoid = PG_GETARG_OID(0);
+ Oid fdwid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_P(1);
- AclId usesysid;
+ Oid roleid;
AclMode mode;
AclResult aclresult;
- usesysid = GetUserId();
- mode = convert_tablespace_priv_string(priv_type_text);
+ roleid = GetUserId();
+ mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
- aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+ aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_tablespace_privilege_id_name
- * Check user privileges on a tablespace given
- * usesysid, text tablespacename, and text priv name.
+ * has_foreign_data_wrapper_privilege_id_name
+ * Check user privileges on a foreign-data wrapper given
+ * roleid, text fdwname, and text priv name.
*/
Datum
-has_tablespace_privilege_id_name(PG_FUNCTION_ARGS)
+has_foreign_data_wrapper_privilege_id_name(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
- text *tablespacename = PG_GETARG_TEXT_P(1);
+ Oid roleid = PG_GETARG_OID(0);
+ text *fdwname = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
- Oid tablespaceoid;
+ Oid fdwid;
AclMode mode;
AclResult aclresult;
- tablespaceoid = convert_tablespace_name(tablespacename);
- mode = convert_tablespace_priv_string(priv_type_text);
+ fdwid = convert_foreign_data_wrapper_name(fdwname);
+ mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
- aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+ aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * has_tablespace_privilege_id_id
- * Check user privileges on a tablespace given
- * usesysid, tablespace oid, and text priv name.
+ * has_foreign_data_wrapper_privilege_id_id
+ * Check user privileges on a foreign-data wrapper given
+ * roleid, fdw oid, and text priv name.
*/
Datum
-has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
+has_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS)
{
- int32 usesysid = PG_GETARG_INT32(0);
- Oid tablespaceoid = PG_GETARG_OID(1);
+ Oid roleid = PG_GETARG_OID(0);
+ Oid fdwid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
AclMode mode;
AclResult aclresult;
- mode = convert_tablespace_priv_string(priv_type_text);
+ mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
- aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+ aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
- * Support routines for has_tablespace_privilege family.
+ * Support routines for has_foreign_data_wrapper_privilege family.
*/
/*
- * Given a tablespace name expressed as a string, look it up and return Oid
+ * Given a FDW name expressed as a string, look it up and return Oid
*/
static Oid
-convert_tablespace_name(text *tablespacename)
+convert_foreign_data_wrapper_name(text *fdwname)
{
- char *spcname;
- Oid oid;
-
- spcname = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(tablespacename)));
- oid = get_tablespace_oid(spcname);
-
- if (!OidIsValid(oid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("tablespace \"%s\" does not exist", spcname)));
+ char *fdwstr = text_to_cstring(fdwname);
- return oid;
+ return GetForeignDataWrapperOidByName(fdwstr, false);
}
/*
- * convert_tablespace_priv_string
+ * convert_foreign_data_wrapper_priv_string
* Convert text string to AclMode value.
*/
static AclMode
-convert_tablespace_priv_string(text *priv_type_text)
+convert_foreign_data_wrapper_priv_string(text *priv_type_text)
{
- char *priv_type;
+ static const priv_map foreign_data_wrapper_priv_map[] = {
+ {"USAGE", ACL_USAGE},
+ {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ {NULL, 0}
+ };
- priv_type = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(priv_type_text)));
+ return convert_any_priv_string(priv_type_text, foreign_data_wrapper_priv_map);
+}
- /*
- * Return mode from priv_type string
- */
- if (pg_strcasecmp(priv_type, "CREATE") == 0)
- return ACL_CREATE;
- if (pg_strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
- return ACL_GRANT_OPTION_FOR(ACL_CREATE);
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized privilege type: \"%s\"", priv_type)));
- return ACL_NO_RIGHTS; /* keep compiler quiet */
+/*
+ * has_function_privilege variants
+ * These are all named "has_function_privilege" at the SQL level.
+ * They take various combinations of function name, function OID,
+ * user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has the indicated
+ * privilege, false if not, or NULL if object doesn't exist.
+ */
+
+/*
+ * has_function_privilege_name_name
+ * Check user privileges on a function given
+ * name username, text functionname, and text priv name.
+ */
+Datum
+has_function_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *functionname = PG_GETARG_TEXT_P(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ Oid functionoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ functionoid = convert_function_name(functionname);
+ mode = convert_function_priv_string(priv_type_text);
+
+ aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_function_privilege_name
+ * Check user privileges on a function given
+ * text functionname and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_function_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *functionname = PG_GETARG_TEXT_P(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ Oid functionoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ functionoid = convert_function_name(functionname);
+ mode = convert_function_priv_string(priv_type_text);
+
+ aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_function_privilege_name_id
+ * Check user privileges on a function given
+ * name usename, function oid, and text priv name.
+ */
+Datum
+has_function_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid functionoid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ mode = convert_function_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(PROCOID,
+ ObjectIdGetDatum(functionoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_function_privilege_id
+ * Check user privileges on a function given
+ * function oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_function_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid functionoid = PG_GETARG_OID(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_function_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(PROCOID,
+ ObjectIdGetDatum(functionoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_function_privilege_id_name
+ * Check user privileges on a function given
+ * roleid, text functionname, and text priv name.
+ */
+Datum
+has_function_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *functionname = PG_GETARG_TEXT_P(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid functionoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ functionoid = convert_function_name(functionname);
+ mode = convert_function_priv_string(priv_type_text);
+
+ aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_function_privilege_id_id
+ * Check user privileges on a function given
+ * roleid, function oid, and text priv name.
+ */
+Datum
+has_function_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid functionoid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_function_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(PROCOID,
+ ObjectIdGetDatum(functionoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_function_privilege family.
+ */
+
+/*
+ * Given a function name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_function_name(text *functionname)
+{
+ char *funcname = text_to_cstring(functionname);
+ Oid oid;
+
+ oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein,
+ CStringGetDatum(funcname)));
+
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function \"%s\" does not exist", funcname)));
+
+ return oid;
+}
+
+/*
+ * convert_function_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_function_priv_string(text *priv_type_text)
+{
+ static const priv_map function_priv_map[] = {
+ {"EXECUTE", ACL_EXECUTE},
+ {"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_type_text, function_priv_map);
+}
+
+
+/*
+ * has_language_privilege variants
+ * These are all named "has_language_privilege" at the SQL level.
+ * They take various combinations of language name, language OID,
+ * user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has the indicated
+ * privilege, false if not, or NULL if object doesn't exist.
+ */
+
+/*
+ * has_language_privilege_name_name
+ * Check user privileges on a language given
+ * name username, text languagename, and text priv name.
+ */
+Datum
+has_language_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *languagename = PG_GETARG_TEXT_P(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ Oid languageoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ languageoid = convert_language_name(languagename);
+ mode = convert_language_priv_string(priv_type_text);
+
+ aclresult = pg_language_aclcheck(languageoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_language_privilege_name
+ * Check user privileges on a language given
+ * text languagename and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_language_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *languagename = PG_GETARG_TEXT_P(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ Oid languageoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ languageoid = convert_language_name(languagename);
+ mode = convert_language_priv_string(priv_type_text);
+
+ aclresult = pg_language_aclcheck(languageoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_language_privilege_name_id
+ * Check user privileges on a language given
+ * name usename, language oid, and text priv name.
+ */
+Datum
+has_language_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid languageoid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ mode = convert_language_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(LANGOID,
+ ObjectIdGetDatum(languageoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_language_aclcheck(languageoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_language_privilege_id
+ * Check user privileges on a language given
+ * language oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_language_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid languageoid = PG_GETARG_OID(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_language_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(LANGOID,
+ ObjectIdGetDatum(languageoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_language_aclcheck(languageoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_language_privilege_id_name
+ * Check user privileges on a language given
+ * roleid, text languagename, and text priv name.
+ */
+Datum
+has_language_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *languagename = PG_GETARG_TEXT_P(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid languageoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ languageoid = convert_language_name(languagename);
+ mode = convert_language_priv_string(priv_type_text);
+
+ aclresult = pg_language_aclcheck(languageoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_language_privilege_id_id
+ * Check user privileges on a language given
+ * roleid, language oid, and text priv name.
+ */
+Datum
+has_language_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid languageoid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_language_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(LANGOID,
+ ObjectIdGetDatum(languageoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_language_aclcheck(languageoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_language_privilege family.
+ */
+
+/*
+ * Given a language name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_language_name(text *languagename)
+{
+ char *langname = text_to_cstring(languagename);
+ Oid oid;
+
+ oid = GetSysCacheOid(LANGNAME,
+ CStringGetDatum(langname),
+ 0, 0, 0);
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("language \"%s\" does not exist", langname)));
+
+ return oid;
+}
+
+/*
+ * convert_language_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_language_priv_string(text *priv_type_text)
+{
+ static const priv_map language_priv_map[] = {
+ {"USAGE", ACL_USAGE},
+ {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_type_text, language_priv_map);
+}
+
+
+/*
+ * has_schema_privilege variants
+ * These are all named "has_schema_privilege" at the SQL level.
+ * They take various combinations of schema name, schema OID,
+ * user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has the indicated
+ * privilege, false if not, or NULL if object doesn't exist.
+ */
+
+/*
+ * has_schema_privilege_name_name
+ * Check user privileges on a schema given
+ * name username, text schemaname, and text priv name.
+ */
+Datum
+has_schema_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *schemaname = PG_GETARG_TEXT_P(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ Oid schemaoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ schemaoid = convert_schema_name(schemaname);
+ mode = convert_schema_priv_string(priv_type_text);
+
+ aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_schema_privilege_name
+ * Check user privileges on a schema given
+ * text schemaname and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_schema_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *schemaname = PG_GETARG_TEXT_P(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ Oid schemaoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ schemaoid = convert_schema_name(schemaname);
+ mode = convert_schema_priv_string(priv_type_text);
+
+ aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_schema_privilege_name_id
+ * Check user privileges on a schema given
+ * name usename, schema oid, and text priv name.
+ */
+Datum
+has_schema_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid schemaoid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ mode = convert_schema_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(NAMESPACEOID,
+ ObjectIdGetDatum(schemaoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_schema_privilege_id
+ * Check user privileges on a schema given
+ * schema oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_schema_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid schemaoid = PG_GETARG_OID(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_schema_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(NAMESPACEOID,
+ ObjectIdGetDatum(schemaoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_schema_privilege_id_name
+ * Check user privileges on a schema given
+ * roleid, text schemaname, and text priv name.
+ */
+Datum
+has_schema_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *schemaname = PG_GETARG_TEXT_P(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid schemaoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ schemaoid = convert_schema_name(schemaname);
+ mode = convert_schema_priv_string(priv_type_text);
+
+ aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_schema_privilege_id_id
+ * Check user privileges on a schema given
+ * roleid, schema oid, and text priv name.
+ */
+Datum
+has_schema_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid schemaoid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_schema_priv_string(priv_type_text);
+
+ if (!SearchSysCacheExists(NAMESPACEOID,
+ ObjectIdGetDatum(schemaoid),
+ 0, 0, 0))
+ PG_RETURN_NULL();
+
+ aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_schema_privilege family.
+ */
+
+/*
+ * Given a schema name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_schema_name(text *schemaname)
+{
+ char *nspname = text_to_cstring(schemaname);
+ Oid oid;
+
+ oid = GetSysCacheOid(NAMESPACENAME,
+ CStringGetDatum(nspname),
+ 0, 0, 0);
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_SCHEMA),
+ errmsg("schema \"%s\" does not exist", nspname)));
+
+ return oid;
+}
+
+/*
+ * convert_schema_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_schema_priv_string(text *priv_type_text)
+{
+ static const priv_map schema_priv_map[] = {
+ {"CREATE", ACL_CREATE},
+ {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ {"USAGE", ACL_USAGE},
+ {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_type_text, schema_priv_map);
+}
+
+
+/*
+ * has_server_privilege variants
+ * These are all named "has_server_privilege" at the SQL level.
+ * They take various combinations of foreign server name,
+ * server OID, user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has the indicated
+ * privilege, false if not.
+ */
+
+/*
+ * has_server_privilege_name_name
+ * Check user privileges on a foreign server given
+ * name username, text servername, and text priv name.
+ */
+Datum
+has_server_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *servername = PG_GETARG_TEXT_P(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ Oid serverid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ serverid = convert_server_name(servername);
+ mode = convert_server_priv_string(priv_type_text);
+
+ aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_server_privilege_name
+ * Check user privileges on a foreign server given
+ * text servername and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_server_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *servername = PG_GETARG_TEXT_P(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ Oid serverid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ serverid = convert_server_name(servername);
+ mode = convert_server_priv_string(priv_type_text);
+
+ aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_server_privilege_name_id
+ * Check user privileges on a foreign server given
+ * name usename, foreign server oid, and text priv name.
+ */
+Datum
+has_server_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid serverid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ mode = convert_server_priv_string(priv_type_text);
+
+ aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_server_privilege_id
+ * Check user privileges on a foreign server given
+ * server oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_server_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid serverid = PG_GETARG_OID(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_server_priv_string(priv_type_text);
+
+ aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_server_privilege_id_name
+ * Check user privileges on a foreign server given
+ * roleid, text servername, and text priv name.
+ */
+Datum
+has_server_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *servername = PG_GETARG_TEXT_P(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid serverid;
+ AclMode mode;
+ AclResult aclresult;
+
+ serverid = convert_server_name(servername);
+ mode = convert_server_priv_string(priv_type_text);
+
+ aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_server_privilege_id_id
+ * Check user privileges on a foreign server given
+ * roleid, server oid, and text priv name.
+ */
+Datum
+has_server_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid serverid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_server_priv_string(priv_type_text);
+
+ aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_server_privilege family.
+ */
+
+/*
+ * Given a server name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_server_name(text *servername)
+{
+ char *serverstr = text_to_cstring(servername);
+
+ return GetForeignServerOidByName(serverstr, false);
+}
+
+/*
+ * convert_server_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_server_priv_string(text *priv_type_text)
+{
+ static const priv_map server_priv_map[] = {
+ {"USAGE", ACL_USAGE},
+ {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_type_text, server_priv_map);
+}
+
+
+/*
+ * has_tablespace_privilege variants
+ * These are all named "has_tablespace_privilege" at the SQL level.
+ * They take various combinations of tablespace name, tablespace OID,
+ * user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has the indicated
+ * privilege, false if not.
+ */
+
+/*
+ * has_tablespace_privilege_name_name
+ * Check user privileges on a tablespace given
+ * name username, text tablespacename, and text priv name.
+ */
+Datum
+has_tablespace_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *tablespacename = PG_GETARG_TEXT_P(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ Oid tablespaceoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ tablespaceoid = convert_tablespace_name(tablespacename);
+ mode = convert_tablespace_priv_string(priv_type_text);
+
+ aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_tablespace_privilege_name
+ * Check user privileges on a tablespace given
+ * text tablespacename and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_tablespace_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *tablespacename = PG_GETARG_TEXT_P(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ Oid tablespaceoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ tablespaceoid = convert_tablespace_name(tablespacename);
+ mode = convert_tablespace_priv_string(priv_type_text);
+
+ aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_tablespace_privilege_name_id
+ * Check user privileges on a tablespace given
+ * name usename, tablespace oid, and text priv name.
+ */
+Datum
+has_tablespace_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid tablespaceoid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ mode = convert_tablespace_priv_string(priv_type_text);
+
+ aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_tablespace_privilege_id
+ * Check user privileges on a tablespace given
+ * tablespace oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_tablespace_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid tablespaceoid = PG_GETARG_OID(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_tablespace_priv_string(priv_type_text);
+
+ aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_tablespace_privilege_id_name
+ * Check user privileges on a tablespace given
+ * roleid, text tablespacename, and text priv name.
+ */
+Datum
+has_tablespace_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *tablespacename = PG_GETARG_TEXT_P(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid tablespaceoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ tablespaceoid = convert_tablespace_name(tablespacename);
+ mode = convert_tablespace_priv_string(priv_type_text);
+
+ aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_tablespace_privilege_id_id
+ * Check user privileges on a tablespace given
+ * roleid, tablespace oid, and text priv name.
+ */
+Datum
+has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid tablespaceoid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_tablespace_priv_string(priv_type_text);
+
+ aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_tablespace_privilege family.
+ */
+
+/*
+ * Given a tablespace name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_tablespace_name(text *tablespacename)
+{
+ char *spcname = text_to_cstring(tablespacename);
+ Oid oid;
+
+ oid = get_tablespace_oid(spcname);
+
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace \"%s\" does not exist", spcname)));
+
+ return oid;
+}
+
+/*
+ * convert_tablespace_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_tablespace_priv_string(text *priv_type_text)
+{
+ static const priv_map tablespace_priv_map[] = {
+ {"CREATE", ACL_CREATE},
+ {"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_type_text, tablespace_priv_map);
+}
+
+/*
+ * pg_has_role variants
+ * These are all named "pg_has_role" at the SQL level.
+ * They take various combinations of role name, role OID,
+ * user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has the indicated
+ * privilege, false if not.
+ */
+
+/*
+ * pg_has_role_name_name
+ * Check user privileges on a role given
+ * name username, name rolename, and text priv name.
+ */
+Datum
+pg_has_role_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Name rolename = PG_GETARG_NAME(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ Oid roleoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ roleoid = get_roleid_checked(NameStr(*rolename));
+ mode = convert_role_priv_string(priv_type_text);
+
+ aclresult = pg_role_aclcheck(roleoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * pg_has_role_name
+ * Check user privileges on a role given
+ * name rolename and text priv name.
+ * current_user is assumed
+ */
+Datum
+pg_has_role_name(PG_FUNCTION_ARGS)
+{
+ Name rolename = PG_GETARG_NAME(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ Oid roleoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ roleoid = get_roleid_checked(NameStr(*rolename));
+ mode = convert_role_priv_string(priv_type_text);
+
+ aclresult = pg_role_aclcheck(roleoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * pg_has_role_name_id
+ * Check user privileges on a role given
+ * name usename, role oid, and text priv name.
+ */
+Datum
+pg_has_role_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid roleoid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_roleid_checked(NameStr(*username));
+ mode = convert_role_priv_string(priv_type_text);
+
+ aclresult = pg_role_aclcheck(roleoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * pg_has_role_id
+ * Check user privileges on a role given
+ * role oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+pg_has_role_id(PG_FUNCTION_ARGS)
+{
+ Oid roleoid = PG_GETARG_OID(0);
+ text *priv_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_role_priv_string(priv_type_text);
+
+ aclresult = pg_role_aclcheck(roleoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * pg_has_role_id_name
+ * Check user privileges on a role given
+ * roleid, name rolename, and text priv name.
+ */
+Datum
+pg_has_role_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Name rolename = PG_GETARG_NAME(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ Oid roleoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleoid = get_roleid_checked(NameStr(*rolename));
+ mode = convert_role_priv_string(priv_type_text);
+
+ aclresult = pg_role_aclcheck(roleoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * pg_has_role_id_id
+ * Check user privileges on a role given
+ * roleid, role oid, and text priv name.
+ */
+Datum
+pg_has_role_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid roleoid = PG_GETARG_OID(1);
+ text *priv_type_text = PG_GETARG_TEXT_P(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_role_priv_string(priv_type_text);
+
+ aclresult = pg_role_aclcheck(roleoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for pg_has_role family.
+ */
+
+/*
+ * convert_role_priv_string
+ * Convert text string to AclMode value.
+ *
+ * We use USAGE to denote whether the privileges of the role are accessible
+ * (has_privs), MEMBER to denote is_member, and MEMBER WITH GRANT OPTION
+ * (or ADMIN OPTION) to denote is_admin. There is no ACL bit corresponding
+ * to MEMBER so we cheat and use ACL_CREATE for that. This convention
+ * is shared only with pg_role_aclcheck, below.
+ */
+static AclMode
+convert_role_priv_string(text *priv_type_text)
+{
+ static const priv_map role_priv_map[] = {
+ {"USAGE", ACL_USAGE},
+ {"MEMBER", ACL_CREATE},
+ {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ {"USAGE WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ {"MEMBER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ {"MEMBER WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_type_text, role_priv_map);
+}
+
+/*
+ * pg_role_aclcheck
+ * Quick-and-dirty support for pg_has_role
+ */
+static AclResult
+pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode)
+{
+ if (mode & ACL_GRANT_OPTION_FOR(ACL_CREATE))
+ {
+ if (is_admin_of_role(roleid, role_oid))
+ return ACLCHECK_OK;
+ }
+ if (mode & ACL_CREATE)
+ {
+ if (is_member_of_role(roleid, role_oid))
+ return ACLCHECK_OK;
+ }
+ if (mode & ACL_USAGE)
+ {
+ if (has_privs_of_role(roleid, role_oid))
+ return ACLCHECK_OK;
+ }
+ return ACLCHECK_NO_PRIV;
+}
+
+
+/*
+ * initialization function (called by InitPostgres)
+ */
+void
+initialize_acl(void)
+{
+ if (!IsBootstrapProcessingMode())
+ {
+ /*
+ * In normal mode, set a callback on any syscache invalidation of
+ * pg_auth_members rows
+ */
+ CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
+ RoleMembershipCacheCallback,
+ (Datum) 0);
+ }
+}
+
+/*
+ * RoleMembershipCacheCallback
+ * Syscache inval callback function
+ */
+static void
+RoleMembershipCacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
+{
+ /* Force membership caches to be recomputed on next use */
+ cached_privs_role = InvalidOid;
+ cached_member_role = InvalidOid;
+}
+
+
+/* Check if specified role has rolinherit set */
+static bool
+has_rolinherit(Oid roleid)
+{
+ bool result = false;
+ HeapTuple utup;
+
+ utup = SearchSysCache(AUTHOID,
+ ObjectIdGetDatum(roleid),
+ 0, 0, 0);
+ if (HeapTupleIsValid(utup))
+ {
+ result = ((Form_pg_authid) GETSTRUCT(utup))->rolinherit;
+ ReleaseSysCache(utup);
+ }
+ return result;
+}
+
+
+/*
+ * Get a list of roles that the specified roleid has the privileges of
+ *
+ * This is defined not to recurse through roles that don't have rolinherit
+ * set; for such roles, membership implies the ability to do SET ROLE, but
+ * the privileges are not available until you've done so.
+ *
+ * Since indirect membership testing is relatively expensive, we cache
+ * a list of memberships. Hence, the result is only guaranteed good until
+ * the next call of roles_has_privs_of()!
+ *
+ * For the benefit of select_best_grantor, the result is defined to be
+ * in breadth-first order, ie, closer relationships earlier.
+ */
+static List *
+roles_has_privs_of(Oid roleid)
+{
+ List *roles_list;
+ ListCell *l;
+ List *new_cached_privs_roles;
+ MemoryContext oldctx;
+
+ /* If cache is already valid, just return the list */
+ if (OidIsValid(cached_privs_role) && cached_privs_role == roleid)
+ return cached_privs_roles;
+
+ /*
+ * Find all the roles that roleid is a member of, including multi-level
+ * recursion. The role itself will always be the first element of the
+ * resulting list.
+ *
+ * Each element of the list is scanned to see if it adds any indirect
+ * memberships. We can use a single list as both the record of
+ * already-found memberships and the agenda of roles yet to be scanned.
+ * This is a bit tricky but works because the foreach() macro doesn't
+ * fetch the next list element until the bottom of the loop.
+ */
+ roles_list = list_make1_oid(roleid);
+
+ foreach(l, roles_list)
+ {
+ Oid memberid = lfirst_oid(l);
+ CatCList *memlist;
+ int i;
+
+ /* Ignore non-inheriting roles */
+ if (!has_rolinherit(memberid))
+ continue;
+
+ /* Find roles that memberid is directly a member of */
+ memlist = SearchSysCacheList(AUTHMEMMEMROLE, 1,
+ ObjectIdGetDatum(memberid),
+ 0, 0, 0);
+ for (i = 0; i < memlist->n_members; i++)
+ {
+ HeapTuple tup = &memlist->members[i]->tuple;
+ Oid otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
+
+ /*
+ * Even though there shouldn't be any loops in the membership
+ * graph, we must test for having already seen this role. It is
+ * legal for instance to have both A->B and A->C->B.
+ */
+ roles_list = list_append_unique_oid(roles_list, otherid);
+ }
+ ReleaseSysCacheList(memlist);
+ }
+
+ /*
+ * Copy the completed list into TopMemoryContext so it will persist.
+ */
+ oldctx = MemoryContextSwitchTo(TopMemoryContext);
+ new_cached_privs_roles = list_copy(roles_list);
+ MemoryContextSwitchTo(oldctx);
+ list_free(roles_list);
+
+ /*
+ * Now safe to assign to state variable
+ */
+ cached_privs_role = InvalidOid; /* just paranoia */
+ list_free(cached_privs_roles);
+ cached_privs_roles = new_cached_privs_roles;
+ cached_privs_role = roleid;
+
+ /* And now we can return the answer */
+ return cached_privs_roles;
+}
+
+
+/*
+ * Get a list of roles that the specified roleid is a member of
+ *
+ * This is defined to recurse through roles regardless of rolinherit.
+ *
+ * Since indirect membership testing is relatively expensive, we cache
+ * a list of memberships. Hence, the result is only guaranteed good until
+ * the next call of roles_is_member_of()!
+ */
+static List *
+roles_is_member_of(Oid roleid)
+{
+ List *roles_list;
+ ListCell *l;
+ List *new_cached_membership_roles;
+ MemoryContext oldctx;
+
+ /* If cache is already valid, just return the list */
+ if (OidIsValid(cached_member_role) && cached_member_role == roleid)
+ return cached_membership_roles;
+
+ /*
+ * Find all the roles that roleid is a member of, including multi-level
+ * recursion. The role itself will always be the first element of the
+ * resulting list.
+ *
+ * Each element of the list is scanned to see if it adds any indirect
+ * memberships. We can use a single list as both the record of
+ * already-found memberships and the agenda of roles yet to be scanned.
+ * This is a bit tricky but works because the foreach() macro doesn't
+ * fetch the next list element until the bottom of the loop.
+ */
+ roles_list = list_make1_oid(roleid);
+
+ foreach(l, roles_list)
+ {
+ Oid memberid = lfirst_oid(l);
+ CatCList *memlist;
+ int i;
+
+ /* Find roles that memberid is directly a member of */
+ memlist = SearchSysCacheList(AUTHMEMMEMROLE, 1,
+ ObjectIdGetDatum(memberid),
+ 0, 0, 0);
+ for (i = 0; i < memlist->n_members; i++)
+ {
+ HeapTuple tup = &memlist->members[i]->tuple;
+ Oid otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
+
+ /*
+ * Even though there shouldn't be any loops in the membership
+ * graph, we must test for having already seen this role. It is
+ * legal for instance to have both A->B and A->C->B.
+ */
+ roles_list = list_append_unique_oid(roles_list, otherid);
+ }
+ ReleaseSysCacheList(memlist);
+ }
+
+ /*
+ * Copy the completed list into TopMemoryContext so it will persist.
+ */
+ oldctx = MemoryContextSwitchTo(TopMemoryContext);
+ new_cached_membership_roles = list_copy(roles_list);
+ MemoryContextSwitchTo(oldctx);
+ list_free(roles_list);
+
+ /*
+ * Now safe to assign to state variable
+ */
+ cached_member_role = InvalidOid; /* just paranoia */
+ list_free(cached_membership_roles);
+ cached_membership_roles = new_cached_membership_roles;
+ cached_member_role = roleid;
+
+ /* And now we can return the answer */
+ return cached_membership_roles;
+}
+
+
+/*
+ * Does member have the privileges of role (directly or indirectly)?
+ *
+ * This is defined not to recurse through roles that don't have rolinherit
+ * set; for such roles, membership implies the ability to do SET ROLE, but
+ * the privileges are not available until you've done so.
+ */
+bool
+has_privs_of_role(Oid member, Oid role)
+{
+ /* Fast path for simple case */
+ if (member == role)
+ return true;
+
+ /* Superusers have every privilege, so are part of every role */
+ if (superuser_arg(member))
+ return true;
+
+ /*
+ * Find all the roles that member has the privileges of, including
+ * multi-level recursion, then see if target role is any one of them.
+ */
+ return list_member_oid(roles_has_privs_of(member), role);
+}
+
+
+/*
+ * Is member a member of role (directly or indirectly)?
+ *
+ * This is defined to recurse through roles regardless of rolinherit.
+ */
+bool
+is_member_of_role(Oid member, Oid role)
+{
+ /* Fast path for simple case */
+ if (member == role)
+ return true;
+
+ /* Superusers have every privilege, so are part of every role */
+ if (superuser_arg(member))
+ return true;
+
+ /*
+ * Find all the roles that member is a member of, including multi-level
+ * recursion, then see if target role is any one of them.
+ */
+ return list_member_oid(roles_is_member_of(member), role);
+}
+
+/*
+ * check_is_member_of_role
+ * is_member_of_role with a standard permission-violation error if not
+ */
+void
+check_is_member_of_role(Oid member, Oid role)
+{
+ if (!is_member_of_role(member, role))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be member of role \"%s\"",
+ GetUserNameFromId(role))));
+}
+
+/*
+ * Is member a member of role, not considering superuserness?
+ *
+ * This is identical to is_member_of_role except we ignore superuser
+ * status.
+ */
+bool
+is_member_of_role_nosuper(Oid member, Oid role)
+{
+ /* Fast path for simple case */
+ if (member == role)
+ return true;
+
+ /*
+ * Find all the roles that member is a member of, including multi-level
+ * recursion, then see if target role is any one of them.
+ */
+ return list_member_oid(roles_is_member_of(member), role);
+}
+
+
+/*
+ * Is member an admin of role (directly or indirectly)? That is, is it
+ * a member WITH ADMIN OPTION?
+ *
+ * We could cache the result as for is_member_of_role, but currently this
+ * is not used in any performance-critical paths, so we don't.
+ */
+bool
+is_admin_of_role(Oid member, Oid role)
+{
+ bool result = false;
+ List *roles_list;
+ ListCell *l;
+
+ /* Fast path for simple case */
+ if (member == role)
+ return true;
+
+ /* Superusers have every privilege, so are part of every role */
+ if (superuser_arg(member))
+ return true;
+
+ /*
+ * Find all the roles that member is a member of, including multi-level
+ * recursion. We build a list in the same way that is_member_of_role does
+ * to track visited and unvisited roles.
+ */
+ roles_list = list_make1_oid(member);
+
+ foreach(l, roles_list)
+ {
+ Oid memberid = lfirst_oid(l);
+ CatCList *memlist;
+ int i;
+
+ /* Find roles that memberid is directly a member of */
+ memlist = SearchSysCacheList(AUTHMEMMEMROLE, 1,
+ ObjectIdGetDatum(memberid),
+ 0, 0, 0);
+ for (i = 0; i < memlist->n_members; i++)
+ {
+ HeapTuple tup = &memlist->members[i]->tuple;
+ Oid otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
+
+ if (otherid == role &&
+ ((Form_pg_auth_members) GETSTRUCT(tup))->admin_option)
+ {
+ /* Found what we came for, so can stop searching */
+ result = true;
+ break;
+ }
+
+ roles_list = list_append_unique_oid(roles_list, otherid);
+ }
+ ReleaseSysCacheList(memlist);
+ if (result)
+ break;
+ }
+
+ list_free(roles_list);
+
+ return result;
+}
+
+
+/* does what it says ... */
+static int
+count_one_bits(AclMode mask)
+{
+ int nbits = 0;
+
+ /* this code relies on AclMode being an unsigned type */
+ while (mask)
+ {
+ if (mask & 1)
+ nbits++;
+ mask >>= 1;
+ }
+ return nbits;
+}
+
+
+/*
+ * Select the effective grantor ID for a GRANT or REVOKE operation.
+ *
+ * The grantor must always be either the object owner or some role that has
+ * been explicitly granted grant options. This ensures that all granted
+ * privileges appear to flow from the object owner, and there are never
+ * multiple "original sources" of a privilege. Therefore, if the would-be
+ * grantor is a member of a role that has the needed grant options, we have
+ * to do the grant as that role instead.
+ *
+ * It is possible that the would-be grantor is a member of several roles
+ * that have different subsets of the desired grant options, but no one
+ * role has 'em all. In this case we pick a role with the largest number
+ * of desired options. Ties are broken in favor of closer ancestors.
+ *
+ * roleId: the role attempting to do the GRANT/REVOKE
+ * privileges: the privileges to be granted/revoked
+ * acl: the ACL of the object in question
+ * ownerId: the role owning the object in question
+ * *grantorId: receives the OID of the role to do the grant as
+ * *grantOptions: receives the grant options actually held by grantorId
+ *
+ * If no grant options exist, we set grantorId to roleId, grantOptions to 0.
+ */
+void
+select_best_grantor(Oid roleId, AclMode privileges,
+ const Acl *acl, Oid ownerId,
+ Oid *grantorId, AclMode *grantOptions)
+{
+ AclMode needed_goptions = ACL_GRANT_OPTION_FOR(privileges);
+ List *roles_list;
+ int nrights;
+ ListCell *l;
+
+ /*
+ * The object owner is always treated as having all grant options, so if
+ * roleId is the owner it's easy. Also, if roleId is a superuser it's
+ * easy: superusers are implicitly members of every role, so they act as
+ * the object owner.
+ */
+ if (roleId == ownerId || superuser_arg(roleId))
+ {
+ *grantorId = ownerId;
+ *grantOptions = needed_goptions;
+ return;
+ }
+
+ /*
+ * Otherwise we have to do a careful search to see if roleId has the
+ * privileges of any suitable role. Note: we can hang onto the result of
+ * roles_has_privs_of() throughout this loop, because aclmask_direct()
+ * doesn't query any role memberships.
+ */
+ roles_list = roles_has_privs_of(roleId);
+
+ /* initialize candidate result as default */
+ *grantorId = roleId;
+ *grantOptions = ACL_NO_RIGHTS;
+ nrights = 0;
+
+ foreach(l, roles_list)
+ {
+ Oid otherrole = lfirst_oid(l);
+ AclMode otherprivs;
+
+ otherprivs = aclmask_direct(acl, otherrole, ownerId,
+ needed_goptions, ACLMASK_ALL);
+ if (otherprivs == needed_goptions)
+ {
+ /* Found a suitable grantor */
+ *grantorId = otherrole;
+ *grantOptions = otherprivs;
+ return;
+ }
+
+ /*
+ * If it has just some of the needed privileges, remember best
+ * candidate.
+ */
+ if (otherprivs != ACL_NO_RIGHTS)
+ {
+ int nnewrights = count_one_bits(otherprivs);
+
+ if (nnewrights > nrights)
+ {
+ *grantorId = otherrole;
+ *grantOptions = otherprivs;
+ nrights = nnewrights;
+ }
+ }
+ }
}