postgresql/src/backend/utils/adt/acl.c

5277 lines
134 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* acl.c
* Basic access control list data structures manipulation routines.
*
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
2010-09-20 22:08:53 +02:00
* src/backend/utils/adt/acl.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <ctype.h>
#include "access/hash.h"
#include "access/htup_details.h"
#include "catalog/catalog.h"
#include "catalog/namespace.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/proclang.h"
#include "commands/tablespace.h"
#include "foreign/foreign.h"
#include "funcapi.h"
#include "miscadmin.h"
1999-07-16 07:00:38 +02:00
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
1999-07-16 07:00:38 +02:00
#include "utils/syscache.h"
#include "utils/varlena.h"
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;
2005-10-15 04:49:52 +02:00
static List *cached_privs_roles = NIL;
static Oid cached_member_role = InvalidOid;
2005-10-15 04:49:52 +02:00
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);
2010-02-26 03:01:40 +01:00
static int aclitemComparator(const void *arg1, const void *arg2);
static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
Oid ownerId);
static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
Oid ownerId, DropBehavior behavior);
static AclMode convert_priv_string(text *priv_type_text);
static AclMode convert_any_priv_string(text *priv_type_text,
const priv_map *privileges);
2002-09-04 22:31:48 +02:00
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);
2002-09-04 22:31:48 +02:00
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);
2002-09-04 22:31:48 +02:00
static Oid convert_function_name(text *functionname);
static AclMode convert_function_priv_string(text *priv_type_text);
2002-09-04 22:31:48 +02:00
static Oid convert_language_name(text *languagename);
static AclMode convert_language_priv_string(text *priv_type_text);
2002-09-04 22:31:48 +02:00
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);
2004-08-29 07:07:03 +02:00
static Oid convert_tablespace_name(text *tablespacename);
static AclMode convert_tablespace_priv_string(text *priv_type_text);
static Oid convert_type_name(text *typename);
static AclMode convert_type_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, uint32 hashvalue);
/*
* getid
* Consumes the first alphanumeric string (identifier) found in string
* 's', ignoring any leading white space. If it finds a double quote
* it returns the word inside the quotes.
*
* RETURNS:
* the string position in 's' that points to the next non-space character
* in 's', after any quotes. Also:
* - loads the identifier into 'n'. (If no identifier is found, 'n'
* contains an empty string.) 'n' must be NAMEDATALEN bytes.
*/
static const char *
getid(const char *s, char *n)
{
int len = 0;
bool in_quotes = false;
Assert(s && n);
while (isspace((unsigned char) *s))
s++;
/* This code had better match what putid() does, below */
for (;
*s != '\0' &&
2003-08-04 02:43:34 +02:00
(isalnum((unsigned char) *s) ||
*s == '_' ||
*s == '"' ||
in_quotes);
s++)
{
if (*s == '"')
{
/* safe to look at next char (could be '\0' though) */
if (*(s + 1) != '"')
{
in_quotes = !in_quotes;
continue;
}
/* it's an escaped double quote; skip the escaping char */
s++;
}
/* Add the character to the string */
if (len >= NAMEDATALEN - 1)
ereport(ERROR,
(errcode(ERRCODE_NAME_TOO_LONG),
errmsg("identifier too long"),
2005-10-15 04:49:52 +02:00
errdetail("Identifier must be less than %d characters.",
NAMEDATALEN)));
n[len++] = *s;
}
n[len] = '\0';
while (isspace((unsigned char) *s))
s++;
1998-09-01 05:29:17 +02:00
return s;
}
/*
* 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
*/
static void
putid(char *p, const char *s)
{
const char *src;
2003-08-04 02:43:34 +02:00
bool safe = true;
for (src = s; *src; src++)
{
/* This test had better match what getid() does, above */
if (!isalnum((unsigned char) *src) && *src != '_')
{
safe = false;
break;
}
}
if (!safe)
*p++ = '"';
for (src = s; *src; src++)
{
/* A double quote character in a username is encoded as "" */
if (*src == '"')
*p++ = '"';
*p++ = *src;
}
if (!safe)
*p++ = '"';
*p = '\0';
}
/*
* aclparse
* Consumes and parses an ACL specification of the form:
* [group|user] [A-Za-z0-9]*=[rwaR]*
* from string 's', ignoring any leading white space or white space
* 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.
*
* RETURNS:
* the string position in 's' immediately following the ACL
* specification. Also:
* - loads the structure pointed to by 'aip' with the appropriate
* UID/GID, id type identifier and mode type values.
*/
static const char *
aclparse(const char *s, AclItem *aip)
{
2003-08-04 02:43:34 +02:00
AclMode privs,
goption,
read;
char name[NAMEDATALEN];
char name2[NAMEDATALEN];
Assert(s && aip);
#ifdef ACLDEBUG
elog(LOG, "aclparse: input = \"%s\"", s);
#endif
s = getid(s, name);
if (*s != '=')
{
/* we just read a keyword, not a name */
if (strcmp(name, "group") != 0 && strcmp(name, "user") != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("unrecognized key word: \"%s\"", name),
2005-10-15 04:49:52 +02:00
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,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("missing name"),
errhint("A name must follow the \"group\" or \"user\" key word.")));
}
if (*s != '=')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("missing \"=\" sign")));
privs = goption = ACL_NO_RIGHTS;
2003-08-04 02:43:34 +02:00
for (++s, read = 0; isalpha((unsigned char) *s) || *s == '*'; s++)
{
switch (*s)
{
case '*':
goption |= read;
break;
case ACL_INSERT_CHR:
read = ACL_INSERT;
break;
case ACL_SELECT_CHR:
read = ACL_SELECT;
break;
case ACL_UPDATE_CHR:
read = ACL_UPDATE;
break;
case ACL_DELETE_CHR:
read = ACL_DELETE;
break;
case ACL_TRUNCATE_CHR:
read = ACL_TRUNCATE;
break;
case ACL_REFERENCES_CHR:
read = ACL_REFERENCES;
break;
case ACL_TRIGGER_CHR:
read = ACL_TRIGGER;
break;
case ACL_EXECUTE_CHR:
read = ACL_EXECUTE;
break;
case ACL_USAGE_CHR:
read = ACL_USAGE;
break;
case ACL_CREATE_CHR:
read = ACL_CREATE;
break;
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)));
}
privs |= read;
}
if (name[0] == '\0')
aip->ai_grantee = ACL_ID_PUBLIC;
else
aip->ai_grantee = get_role_oid(name, false);
2003-08-04 02:43:34 +02:00
/*
2005-10-15 04:49:52 +02:00
* XXX Allow a degree of backward compatibility by defaulting the grantor
* to the superuser.
2003-08-04 02:43:34 +02:00
*/
if (*s == '/')
{
s = getid(s + 1, name2);
if (name2[0] == '\0')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("a name must follow the \"/\" sign")));
aip->ai_grantor = get_role_oid(name2, false);
}
else
{
aip->ai_grantor = BOOTSTRAP_SUPERUSERID;
ereport(WARNING,
(errcode(ERRCODE_INVALID_GRANTOR),
errmsg("defaulting grantor to user ID %u",
BOOTSTRAP_SUPERUSERID)));
}
ACLITEM_SET_PRIVS_GOPTIONS(*aip, privs, goption);
#ifdef ACLDEBUG
elog(LOG, "aclparse: correctly read [%u %x %x]",
aip->ai_grantee, privs, goption);
#endif
1998-09-01 05:29:17 +02:00
return s;
}
/*
* allocacl
* Allocates storage for a new Acl with 'n' entries.
*
* RETURNS:
* the new Acl
*/
static Acl *
allocacl(int n)
{
Acl *new_acl;
Size size;
if (n < 0)
elog(ERROR, "invalid size: %d", n);
size = ACL_N_SIZE(n);
new_acl = (Acl *) palloc0(size);
SET_VARSIZE(new_acl, size);
new_acl->ndim = 1;
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;
1998-09-01 05:29:17 +02:00
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++)
{
2010-02-26 03:01:40 +01:00
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),
2006-10-06 19:14:01 +02:00
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),
2006-10-06 19:14:01 +02:00
errmsg("ACL arrays must not contain null values")));
}
/*
* aclitemin
* Allocates storage for, and fills in, a new AclItem given a string
* 's' that contains an ACL specification. See aclparse for details.
*
* RETURNS:
* the new AclItem
*/
Datum
aclitemin(PG_FUNCTION_ARGS)
{
const char *s = PG_GETARG_CSTRING(0);
AclItem *aip;
aip = (AclItem *) palloc(sizeof(AclItem));
s = aclparse(s, aip);
while (isspace((unsigned char) *s))
++s;
if (*s)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("extra garbage at the end of the ACL specification")));
PG_RETURN_ACLITEM_P(aip);
}
/*
* aclitemout
* Allocates storage for, and fills in, a new null-delimited string
* containing a formatted ACL specification. See aclparse for details.
*
* RETURNS:
* the new string
*/
Datum
aclitemout(PG_FUNCTION_ARGS)
{
2001-03-22 05:01:46 +01:00
AclItem *aip = PG_GETARG_ACLITEM_P(0);
char *p;
char *out;
HeapTuple htup;
unsigned i;
out = palloc(strlen("=/") +
2 * N_ACL_RIGHTS +
2 * (2 * NAMEDATALEN + 2) +
1);
p = out;
*p = '\0';
if (aip->ai_grantee != ACL_ID_PUBLIC)
{
htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantee));
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++ = '=';
for (i = 0; i < N_ACL_RIGHTS; ++i)
{
if (ACLITEM_GET_PRIVS(*aip) & (1 << i))
*p++ = ACL_ALL_RIGHTS_STR[i];
if (ACLITEM_GET_GOPTIONS(*aip) & (1 << i))
*p++ = '*';
}
*p++ = '/';
*p = '\0';
htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantor));
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_grantor);
}
PG_RETURN_CSTRING(out);
}
/*
* aclitem_match
* Two AclItems are considered to match iff they have the same
* grantee and grantor; the privileges are ignored.
*/
static bool
aclitem_match(const AclItem *a1, const AclItem *a2)
{
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
*/
Datum
aclitem_eq(PG_FUNCTION_ARGS)
{
2003-08-04 02:43:34 +02:00
AclItem *a1 = PG_GETARG_ACLITEM_P(0);
AclItem *a2 = PG_GETARG_ACLITEM_P(1);
bool result;
result = a1->ai_privs == a2->ai_privs &&
a1->ai_grantee == a2->ai_grantee &&
a1->ai_grantor == a2->ai_grantor;
PG_RETURN_BOOL(result);
}
/*
* aclitem hash function
*
* We make aclitems hashable not so much because anyone is likely to hash
* them, as because we want array equality to work on aclitem arrays, and
* with the typcache mechanism we must have a hash or btree opclass.
*/
Datum
hash_aclitem(PG_FUNCTION_ARGS)
{
AclItem *a = PG_GETARG_ACLITEM_P(0);
/* not very bright, but avoids any issue of padding in struct */
PG_RETURN_UINT32((uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor));
}
/*
* 64-bit hash function for aclitem.
*
* Similar to hash_aclitem, but accepts a seed and returns a uint64 value.
*/
Datum
hash_aclitem_extended(PG_FUNCTION_ARGS)
{
AclItem *a = PG_GETARG_ACLITEM_P(0);
uint64 seed = PG_GETARG_INT64(1);
uint32 sum = (uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor);
return (seed == 0) ? UInt64GetDatum(sum) : hash_uint32_extended(sum, seed);
}
/*
* acldefault() --- create an ACL describing default access permissions
*
* Change this routine if you want to alter the default access policy for
* newly-created objects (or any object with a NULL acl entry). When
* you make a change here, don't forget to update the GRANT man page,
* which explains all the default permissions.
*
* Note that these are the hard-wired "defaults" that are used in the
* absence of any pg_default_acl entry.
*/
Acl *
acldefault(ObjectType objtype, Oid ownerId)
{
AclMode world_default;
AclMode owner_default;
int nacl;
Acl *acl;
AclItem *aip;
switch (objtype)
{
case OBJECT_COLUMN:
/* by default, columns have no extra privileges */
world_default = ACL_NO_RIGHTS;
owner_default = ACL_NO_RIGHTS;
break;
case OBJECT_TABLE:
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_RELATION;
break;
case OBJECT_SEQUENCE:
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_SEQUENCE;
break;
case OBJECT_DATABASE:
/* for backwards compatibility, grant some rights by default */
world_default = ACL_CREATE_TEMP | ACL_CONNECT;
owner_default = ACL_ALL_RIGHTS_DATABASE;
break;
case OBJECT_FUNCTION:
/* Grant EXECUTE by default, for now */
world_default = ACL_EXECUTE;
owner_default = ACL_ALL_RIGHTS_FUNCTION;
break;
case OBJECT_LANGUAGE:
/* Grant USAGE by default, for now */
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_LANGUAGE;
break;
case OBJECT_LARGEOBJECT:
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_LARGEOBJECT;
break;
case OBJECT_SCHEMA:
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_SCHEMA;
break;
case OBJECT_TABLESPACE:
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_TABLESPACE;
break;
case OBJECT_FDW:
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_FDW;
break;
case OBJECT_FOREIGN_SERVER:
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
break;
case OBJECT_DOMAIN:
case OBJECT_TYPE:
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
Phase 2 of pgindent updates. Change pg_bsd_indent to follow upstream rules for placement of comments to the right of code, and remove pgindent hack that caused comments following #endif to not obey the general rule. Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using the published version of pg_bsd_indent, but a hacked-up version that tried to minimize the amount of movement of comments to the right of code. The situation of interest is where such a comment has to be moved to the right of its default placement at column 33 because there's code there. BSD indent has always moved right in units of tab stops in such cases --- but in the previous incarnation, indent was working in 8-space tab stops, while now it knows we use 4-space tabs. So the net result is that in about half the cases, such comments are placed one tab stop left of before. This is better all around: it leaves more room on the line for comment text, and it means that in such cases the comment uniformly starts at the next 4-space tab stop after the code, rather than sometimes one and sometimes two tabs after. Also, ensure that comments following #endif are indented the same as comments following other preprocessor commands such as #else. That inconsistency turns out to have been self-inflicted damage from a poorly-thought-through post-indent "fixup" in pgindent. This patch is much less interesting than the first round of indent changes, but also bulkier, so I thought it best to separate the effects. Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02:00
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
owner_default = ACL_NO_RIGHTS;
break;
}
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_PUBLIC;
aip->ai_grantor = ownerId;
ACLITEM_SET_PRIVS_GOPTIONS(*aip, world_default, ACL_NO_RIGHTS);
aip++;
}
/*
2005-10-15 04:49:52 +02:00
* 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
* wherever we are testing grant options.
*/
if (owner_default != ACL_NO_RIGHTS)
{
aip->ai_grantee = ownerId;
aip->ai_grantor = ownerId;
ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS);
}
1998-09-01 05:29:17 +02:00
return acl;
}
/*
* SQL-accessible version of acldefault(). Hackish mapping from "char" type to
* OBJECT_* values, but it's only used in the information schema, not
* documented for general use.
*/
Datum
acldefault_sql(PG_FUNCTION_ARGS)
{
char objtypec = PG_GETARG_CHAR(0);
Oid owner = PG_GETARG_OID(1);
ObjectType objtype = 0;
switch (objtypec)
{
case 'c':
objtype = OBJECT_COLUMN;
break;
case 'r':
objtype = OBJECT_TABLE;
break;
case 's':
objtype = OBJECT_SEQUENCE;
break;
case 'd':
objtype = OBJECT_DATABASE;
break;
case 'f':
objtype = OBJECT_FUNCTION;
break;
case 'l':
objtype = OBJECT_LANGUAGE;
break;
case 'L':
objtype = OBJECT_LARGEOBJECT;
break;
case 'n':
objtype = OBJECT_SCHEMA;
break;
case 't':
objtype = OBJECT_TABLESPACE;
break;
case 'F':
objtype = OBJECT_FDW;
break;
case 'S':
objtype = OBJECT_FOREIGN_SERVER;
break;
case 'T':
objtype = OBJECT_TYPE;
break;
default:
elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
}
PG_RETURN_ACL_P(acldefault(objtype, owner));
}
/*
* Update an ACL array to add or remove specified privileges.
*
* 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: Oid of object owner
* behavior: RESTRICT or CASCADE behavior for recursive removal
*
* ownerid and behavior are only relevant when the update operation specifies
* deletion of grant options.
*
* 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 *
aclupdate(const Acl *old_acl, const AclItem *mod_aip,
int modechg, Oid ownerId, DropBehavior behavior)
{
2003-01-24 22:53:29 +01:00
Acl *new_acl = NULL;
AclItem *old_aip,
2003-01-24 22:53:29 +01:00
*new_aip = NULL;
AclMode old_rights,
old_goptions,
new_rights,
new_goptions;
int dst,
num;
/* 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);
num = ACL_NUM(old_acl);
old_aip = ACL_DAT(old_acl);
/*
2005-10-15 04:49:52 +02:00
* 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)
{
if (aclitem_match(mod_aip, old_aip + dst))
{
/* found a match, so modify existing item */
new_acl = allocacl(num);
new_aip = ACL_DAT(new_acl);
memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
break;
}
}
if (dst == num)
{
/* need to append a new item */
new_acl = allocacl(num + 1);
new_aip = ACL_DAT(new_acl);
memcpy(new_aip, old_aip, num * sizeof(AclItem));
/* 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_GOPTIONS(new_aip[dst],
ACL_NO_RIGHTS, ACL_NO_RIGHTS);
num++; /* set num to the size of new_acl */
}
old_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
old_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
/* apply the specified permissions change */
switch (modechg)
{
case ACL_MODECHG_ADD:
ACLITEM_SET_RIGHTS(new_aip[dst],
old_rights | ACLITEM_GET_RIGHTS(*mod_aip));
break;
case ACL_MODECHG_DEL:
ACLITEM_SET_RIGHTS(new_aip[dst],
2005-10-15 04:49:52 +02:00
old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip));
break;
case ACL_MODECHG_EQL:
ACLITEM_SET_RIGHTS(new_aip[dst],
ACLITEM_GET_RIGHTS(*mod_aip));
break;
}
new_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
new_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
/*
* If the adjusted entry has no permissions, delete it from the list.
*/
if (new_rights == ACL_NO_RIGHTS)
{
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;
SET_VARSIZE(new_acl, ACL_N_SIZE(num - 1));
}
/*
* Remove abandoned privileges (cascading revoke). Currently we can only
2005-10-15 04:49:52 +02:00
* handle this when the grantee is not PUBLIC.
*/
if ((old_goptions & ~new_goptions) != 0)
{
Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee,
(old_goptions & ~new_goptions),
ownerId, behavior);
}
1998-09-01 05:29:17 +02:00
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: 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, Oid oldOwnerId, Oid newOwnerId)
{
Acl *new_acl;
2004-08-29 07:07:03 +02:00
AclItem *new_aip;
AclItem *old_aip;
AclItem *dst_aip;
AclItem *src_aip;
AclItem *targ_aip;
bool newpresent = false;
int dst,
src,
targ,
num;
check_acl(old_acl);
/*
* Make a copy of the given ACL, substituting new owner ID for old
2005-10-15 04:49:52 +02:00
* 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);
new_acl = allocacl(num);
new_aip = ACL_DAT(new_acl);
memcpy(new_aip, old_aip, num * sizeof(AclItem));
for (dst = 0, dst_aip = new_aip; dst < num; dst++, dst_aip++)
{
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;
}
/*
2005-10-15 04:49:52 +02:00
* 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
2005-10-15 04:49:52 +02:00
* 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.)
*
2005-10-15 04:49:52 +02:00
* 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
2005-10-15 04:49:52 +02:00
* duplicate-free and need not be considered anymore.
*/
if (newpresent)
{
dst = 0;
for (targ = 0, targ_aip = new_aip; targ < num; targ++, targ_aip++)
{
/* ignore if deleted in an earlier pass */
if (ACLITEM_GET_RIGHTS(*targ_aip) == ACL_NO_RIGHTS)
continue;
/* find and merge any duplicates */
for (src = targ + 1, src_aip = targ_aip + 1; src < num;
src++, src_aip++)
{
if (ACLITEM_GET_RIGHTS(*src_aip) == ACL_NO_RIGHTS)
continue;
if (aclitem_match(targ_aip, src_aip))
{
ACLITEM_SET_RIGHTS(*targ_aip,
ACLITEM_GET_RIGHTS(*targ_aip) |
ACLITEM_GET_RIGHTS(*src_aip));
/* mark the duplicate deleted */
ACLITEM_SET_RIGHTS(*src_aip, ACL_NO_RIGHTS);
}
}
/* and emit to output */
new_aip[dst] = *targ_aip;
dst++;
}
/* Adjust array size to be 'dst' items */
ARR_DIMS(new_acl)[0] = dst;
SET_VARSIZE(new_acl, ACL_N_SIZE(dst));
}
return new_acl;
}
/*
* When granting grant options, we must disallow attempts to set up circular
* chains of grant options. Suppose A (the object owner) grants B some
* privileges with grant option, and B re-grants them to C. If C could
* grant the privileges to B as well, then A would be unable to effectively
* revoke the privileges from B, since recursive_revoke would consider that
* B still has 'em from C.
*
* We check for this by recursively deleting all grant options belonging to
* the target grantee, and then seeing if the would-be grantor still has the
* grant option or not.
*/
static void
check_circularity(const Acl *old_acl, const AclItem *mod_aip,
Oid ownerId)
{
Acl *acl;
AclItem *aip;
int i,
num;
AclMode own_privs;
check_acl(old_acl);
/*
* For now, grant options can only be granted to roles, not PUBLIC.
* Otherwise we'd have to work a bit harder here.
*/
Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
/* The owner always has grant options, no need to check */
if (mod_aip->ai_grantor == ownerId)
return;
/* Make a working copy */
acl = allocacl(ACL_NUM(old_acl));
memcpy(acl, old_acl, ACL_SIZE(old_acl));
/* Zap all grant options of target grantee, plus what depends on 'em */
cc_restart:
num = ACL_NUM(acl);
aip = ACL_DAT(acl);
for (i = 0; i < num; i++)
{
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);
pfree(acl);
acl = new_acl;
goto cc_restart;
}
}
/* Now we can compute grantor's independently-derived privileges */
own_privs = aclmask(acl,
mod_aip->ai_grantor,
ownerId,
2005-10-15 04:49:52 +02:00
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")));
pfree(acl);
}
/*
* Ensure that no privilege is "abandoned". A privilege is abandoned
* if the user that granted the privilege loses the grant option. (So
* the chain through which it was granted is broken.) Either the
* abandoned privileges are revoked as well, or an error message is
* printed, depending on the drop behavior option.
*
* acl: the input ACL list
* grantee: the user from whom some grant options have been revoked
* revoke_privs: the grant options being revoked
* 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,
Oid grantee,
AclMode revoke_privs,
Oid ownerId,
DropBehavior behavior)
{
AclMode still_has;
AclItem *aip;
int i,
num;
check_acl(acl);
/* The owner can never truly lose grant options, so short-circuit */
if (grantee == ownerId)
return acl;
/* The grantee might still have some grant options via another grantor */
still_has = aclmask(acl, grantee, ownerId,
ACL_GRANT_OPTION_FOR(revoke_privs),
ACLMASK_ALL);
revoke_privs &= ~ACL_OPTION_TO_PRIVS(still_has);
if (revoke_privs == ACL_NO_RIGHTS)
return acl;
restart:
num = ACL_NUM(acl);
aip = ACL_DAT(acl);
for (i = 0; i < num; i++)
{
if (aip[i].ai_grantor == grantee
&& (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0)
{
2003-08-04 02:43:34 +02:00
AclItem mod_acl;
Acl *new_acl;
if (behavior == DROP_RESTRICT)
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("dependent privileges exist"),
errhint("Use CASCADE to revoke them too.")));
mod_acl.ai_grantor = grantee;
mod_acl.ai_grantee = aip[i].ai_grantee;
ACLITEM_SET_PRIVS_GOPTIONS(mod_acl,
revoke_privs,
revoke_privs);
new_acl = aclupdate(acl, &mod_acl, ACL_MODECHG_DEL,
ownerId, behavior);
pfree(acl);
acl = new_acl;
goto restart;
}
}
return acl;
}
/*
* aclmask --- compute bitmask of all privileges held by roleid.
*
* When 'how' = ACLMASK_ALL, this simply returns the privilege bits
* 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.)
*
* When 'how' = ACLMASK_ANY, returns as soon as any bit in the mask
* is known true. (This lets us exit soonest in cases where the
* caller is only going to test for zero or nonzero result.)
*
* Usage patterns:
*
* To see if any of a set of privileges are held:
* if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ANY) != 0)
*
* To see if all of a set of privileges are held:
* if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL) == privs)
*
* To determine exactly which of a set of privileges are held:
* heldprivs = aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL);
*/
AclMode
aclmask(const Acl *acl, Oid roleid, Oid ownerId,
AclMode mask, AclMaskHow how)
{
AclMode result;
AclMode remaining;
AclItem *aidat;
int i,
num;
/*
* 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) &&
has_privs_of_role(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 or to public
*/
for (i = 0; i < num; i++)
{
2004-08-29 07:07:03 +02:00
AclItem *aidata = &aidat[i];
if (aidata->ai_grantee == ACL_ID_PUBLIC ||
aidata->ai_grantee == roleid)
{
result |= aidata->ai_privs & mask;
if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
return result;
}
}
/*
2005-10-15 04:49:52 +02:00
* 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;
for (i = 0; i < num; i++)
{
AclItem *aidata = &aidat[i];
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;
if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
return result;
remaining = mask & ~result;
}
}
return result;
}
/*
* 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 AclMode
aclmask_direct(const Acl *acl, Oid roleid, Oid ownerId,
AclMode mask, AclMaskHow how)
{
AclMode result;
AclItem *aidat;
int i,
num;
/*
* 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++)
{
AclItem *aidata = &aidat[i];
if (aidata->ai_grantee == roleid)
{
result |= aidata->ai_privs & mask;
if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
return result;
}
}
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)
{
2005-10-15 04:49:52 +02:00
Oid *list;
const AclItem *acldat;
2005-10-15 04:49:52 +02:00
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 */
2017-03-01 17:55:28 +01:00
qsort(list, j, sizeof(Oid), oid_cmp);
/* Remove duplicates from the array */
k = 0;
for (i = 1; i < j; i++)
{
if (list[k] != list[i])
list[++k] = list[i];
}
/*
2005-10-15 04:49:52 +02:00
* 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;
}
/*
* aclinsert (exported function)
*/
Datum
aclinsert(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("aclinsert is no longer supported")));
PG_RETURN_NULL(); /* keep compiler quiet */
}
Datum
aclremove(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("aclremove is no longer supported")));
PG_RETURN_NULL(); /* keep compiler quiet */
}
Datum
aclcontains(PG_FUNCTION_ARGS)
{
Acl *acl = PG_GETARG_ACL_P(0);
2001-03-22 05:01:46 +01:00
AclItem *aip = PG_GETARG_ACLITEM_P(1);
AclItem *aidat;
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 &&
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)
{
Oid grantee = PG_GETARG_OID(0);
2005-10-15 04:49:52 +02:00
Oid grantor = PG_GETARG_OID(1);
text *privtext = PG_GETARG_TEXT_PP(2);
bool goption = PG_GETARG_BOOL(3);
AclItem *result;
AclMode priv;
priv = convert_priv_string(privtext);
result = (AclItem *) palloc(sizeof(AclItem));
2003-08-04 02:43:34 +02:00
result->ai_grantee = grantee;
result->ai_grantor = grantor;
2003-08-04 02:43:34 +02:00
ACLITEM_SET_PRIVS_GOPTIONS(*result, priv,
(goption ? priv : ACL_NO_RIGHTS));
PG_RETURN_ACLITEM_P(result);
}
static AclMode
convert_priv_string(text *priv_type_text)
{
char *priv_type = text_to_cstring(priv_type_text);
if (pg_strcasecmp(priv_type, "SELECT") == 0)
return ACL_SELECT;
if (pg_strcasecmp(priv_type, "INSERT") == 0)
return ACL_INSERT;
if (pg_strcasecmp(priv_type, "UPDATE") == 0)
return ACL_UPDATE;
if (pg_strcasecmp(priv_type, "DELETE") == 0)
return ACL_DELETE;
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_TRIGGER;
if (pg_strcasecmp(priv_type, "EXECUTE") == 0)
return ACL_EXECUTE;
if (pg_strcasecmp(priv_type, "USAGE") == 0)
return ACL_USAGE;
if (pg_strcasecmp(priv_type, "CREATE") == 0)
return ACL_CREATE;
if (pg_strcasecmp(priv_type, "TEMP") == 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),
errmsg("unrecognized privilege type: \"%s\"", priv_type)));
return ACL_NO_RIGHTS; /* keep compiler quiet */
}
/*
* convert_any_priv_string: recognize privilege strings for has_foo_privilege
*
* 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;
/* 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 },
2010-02-26 03:01:40 +01:00
* { OID(joe), OID(foo), 'INSERT', true },
* { OID(joe), OID(foo), 'UPDATE', false }}
*----------
*/
Datum
aclexplode(PG_FUNCTION_ARGS)
{
Acl *acl = PG_GETARG_ACL_P(0);
2010-02-26 03:01:40 +01:00
FuncCallContext *funcctx;
int *idx;
2010-02-26 03:01:40 +01:00
AclItem *aidat;
if (SRF_IS_FIRSTCALL())
{
TupleDesc tupdesc;
MemoryContext oldcontext;
check_acl(acl);
funcctx = SRF_FIRSTCALL_INIT();
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/*
2010-02-26 03:01:40 +01:00
* 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);
/* need test here in case acl has no items */
while (idx[0] < ACL_NUM(acl))
{
AclItem *aidata;
AclMode priv_bit;
idx[1]++;
if (idx[1] == N_ACL_RIGHTS)
{
idx[1] = 0;
idx[0]++;
2010-02-26 03:01:40 +01:00
if (idx[0] >= ACL_NUM(acl)) /* done */
break;
}
aidata = &aidat[idx[0]];
priv_bit = 1 << idx[1];
if (ACLITEM_GET_PRIVS(*aidata) & priv_bit)
{
Datum result;
Datum values[4];
bool nulls[4];
HeapTuple tuple;
values[0] = ObjectIdGetDatum(aidata->ai_grantor);
values[1] = ObjectIdGetDatum(aidata->ai_grantee);
values[2] = CStringGetTextDatum(convert_aclright_to_string(priv_bit));
values[3] = BoolGetDatum((ACLITEM_GET_GOPTIONS(*aidata) & priv_bit) != 0);
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 rolename = PG_GETARG_NAME(0);
text *tablename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid tableoid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*rolename));
tableoid = convert_table_name(tablename);
mode = convert_table_priv_string(priv_type_text);
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_table_privilege_name
* Check user privileges on a table given
* text tablename and text priv name.
* current_user is assumed
*/
Datum
has_table_privilege_name(PG_FUNCTION_ARGS)
{
text *tablename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid tableoid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
tableoid = convert_table_name(tablename);
mode = convert_table_priv_string(priv_type_text);
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_table_privilege_name_id
* Check user privileges on a table given
* name usename, table oid, and text priv name.
*/
Datum
has_table_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid tableoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_table_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
PG_RETURN_NULL();
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_table_privilege_id
* Check user privileges on a table given
* table oid, and text priv name.
* current_user is assumed
*/
Datum
has_table_privilege_id(PG_FUNCTION_ARGS)
{
Oid tableoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
2005-10-15 04:49:52 +02:00
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
mode = convert_table_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
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
* roleid, text tablename, and text priv name.
*/
Datum
has_table_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *tablename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid tableoid;
AclMode mode;
AclResult aclresult;
tableoid = convert_table_name(tablename);
mode = convert_table_priv_string(priv_type_text);
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
* roleid, table oid, and text priv name.
*/
Datum
has_table_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid tableoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
mode = convert_table_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
PG_RETURN_NULL();
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* Support routines for has_table_privilege family.
*/
/*
* Given a table name expressed as a string, look it up and return Oid
*/
static Oid
convert_table_name(text *tablename)
{
RangeVar *relrv;
relrv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
/* We might not even have permissions on this relation; don't lock it. */
Improve table locking behavior in the face of current DDL. In the previous coding, callers were faced with an awkward choice: look up the name, do permissions checks, and then lock the table; or look up the name, lock the table, and then do permissions checks. The first choice was wrong because the results of the name lookup and permissions checks might be out-of-date by the time the table lock was acquired, while the second allowed a user with no privileges to interfere with access to a table by users who do have privileges (e.g. if a malicious backend queues up for an AccessExclusiveLock on a table on which AccessShareLock is already held, further attempts to access the table will be blocked until the AccessExclusiveLock is obtained and the malicious backend's transaction rolls back). To fix, allow callers of RangeVarGetRelid() to pass a callback which gets executed after performing the name lookup but before acquiring the relation lock. If the name lookup is retried (because invalidation messages are received), the callback will be re-executed as well, so we get the best of both worlds. RangeVarGetRelid() is renamed to RangeVarGetRelidExtended(); callers not wishing to supply a callback can continue to invoke it as RangeVarGetRelid(), which is now a macro. Since the only one caller that uses nowait = true now passes a callback anyway, the RangeVarGetRelid() macro defaults nowait as well. The callback can also be used for supplemental locking - for example, REINDEX INDEX needs to acquire the table lock before the index lock to reduce deadlock possibilities. There's a lot more work to be done here to fix all the cases where this can be a problem, but this commit provides the general infrastructure and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE, LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE. Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
return RangeVarGetRelid(relrv, NoLock, false);
}
/*
* convert_table_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_table_priv_string(text *priv_type_text)
{
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_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. The variants that take a relation OID
* return NULL if the OID doesn't exist.
*/
/*
* has_sequence_privilege_name_name
* Check user privileges on a sequence given
* name username, text sequencename, and text priv name.
*/
Datum
has_sequence_privilege_name_name(PG_FUNCTION_ARGS)
{
Name rolename = PG_GETARG_NAME(0);
text *sequencename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid sequenceoid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(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),
2010-02-26 03:01:40 +01:00
errmsg("\"%s\" is not a sequence",
text_to_cstring(sequencename))));
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_sequence_privilege_name
* Check user privileges on a sequence given
* text sequencename and text priv name.
* current_user is assumed
*/
Datum
has_sequence_privilege_name(PG_FUNCTION_ARGS)
{
text *sequencename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid sequenceoid;
AclMode mode;
AclResult aclresult;
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),
2010-02-26 03:01:40 +01:00
errmsg("\"%s\" is not a sequence",
text_to_cstring(sequencename))));
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_sequence_privilege_name_id
* Check user privileges on a sequence given
* name usename, sequence oid, and text priv name.
*/
Datum
has_sequence_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid sequenceoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
char relkind;
roleid = get_role_oid_or_public(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),
2010-02-26 03:01:40 +01:00
errmsg("\"%s\" is not a sequence",
get_rel_name(sequenceoid))));
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_sequence_privilege_id
* Check user privileges on a sequence given
* sequence oid, and text priv name.
* current_user is assumed
*/
Datum
has_sequence_privilege_id(PG_FUNCTION_ARGS)
{
Oid sequenceoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
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),
2010-02-26 03:01:40 +01:00
errmsg("\"%s\" is not a sequence",
get_rel_name(sequenceoid))));
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_sequence_privilege_id_name
* Check user privileges on a sequence given
* roleid, text sequencename, and text priv name.
*/
Datum
has_sequence_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *sequencename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid sequenceoid;
AclMode mode;
AclResult aclresult;
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),
2010-02-26 03:01:40 +01:00
errmsg("\"%s\" is not a sequence",
text_to_cstring(sequencename))));
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_sequence_privilege_id_id
* Check user privileges on a sequence given
* roleid, sequence oid, and text priv name.
*/
Datum
has_sequence_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid sequenceoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
char relkind;
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),
2010-02-26 03:01:40 +01:00
errmsg("\"%s\" is not a sequence",
get_rel_name(sequenceoid))));
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* convert_sequence_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_sequence_priv_string(text *priv_type_text)
{
static const priv_map sequence_priv_map[] = {
2010-02-26 03:01:40 +01:00
{"USAGE", ACL_USAGE},
{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
2010-02-26 03:01:40 +01:00
{"SELECT", ACL_SELECT},
{"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
2010-02-26 03:01:40 +01:00
{"UPDATE", ACL_UPDATE},
{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
2010-02-26 03:01:40 +01:00
{NULL, 0}
};
return convert_any_priv_string(priv_type_text, sequence_priv_map);
}
/*
* 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 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_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_any_column_privilege_name_name(PG_FUNCTION_ARGS)
{
Name rolename = PG_GETARG_NAME(0);
text *tablename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid tableoid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*rolename));
tableoid = convert_table_name(tablename);
mode = convert_column_priv_string(priv_type_text);
/* 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_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_any_column_privilege_name(PG_FUNCTION_ARGS)
{
text *tablename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid tableoid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
tableoid = convert_table_name(tablename);
mode = convert_column_priv_string(priv_type_text);
/* 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_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_any_column_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid tableoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_column_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
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_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_any_column_privilege_id(PG_FUNCTION_ARGS)
{
Oid tableoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
mode = convert_column_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
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_any_column_privilege_id_name
* Check user privileges on any column of a table given
* roleid, text tablename, and text priv name.
*/
Datum
has_any_column_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *tablename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid tableoid;
AclMode mode;
AclResult aclresult;
tableoid = convert_table_name(tablename);
mode = convert_column_priv_string(priv_type_text);
/* 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_any_column_privilege_id_id
* Check user privileges on any column of a table given
* roleid, table oid, and text priv name.
*/
Datum
has_any_column_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid tableoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
mode = convert_column_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
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_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.
*/
/*
* 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 int
column_privilege_check(Oid tableoid, AttrNumber attnum,
Oid roleid, AclMode mode)
{
AclResult aclresult;
HeapTuple attTuple;
Form_pg_attribute attributeForm;
/*
* 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 (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
return -1;
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
if (aclresult == ACLCHECK_OK)
return true;
/*
* 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 = SearchSysCache2(ATTNUM,
ObjectIdGetDatum(tableoid),
Int16GetDatum(attnum));
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);
}
/*
* has_column_privilege_name_name_name
* Check user privileges on a column given
* name username, text tablename, text colname, and text priv name.
*/
Datum
has_column_privilege_name_name_name(PG_FUNCTION_ARGS)
{
Name rolename = PG_GETARG_NAME(0);
text *tablename = PG_GETARG_TEXT_PP(1);
text *column = PG_GETARG_TEXT_PP(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
Oid roleid;
Oid tableoid;
AttrNumber colattnum;
AclMode mode;
int privresult;
roleid = get_role_oid_or_public(NameStr(*rolename));
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_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_PP(1);
AttrNumber colattnum = PG_GETARG_INT16(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
Oid roleid;
Oid tableoid;
AclMode mode;
int privresult;
roleid = get_role_oid_or_public(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_column_privilege_name_id_name
* Check user privileges on a column given
* name username, table oid, text colname, and text priv name.
*/
Datum
has_column_privilege_name_id_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid tableoid = PG_GETARG_OID(1);
text *column = PG_GETARG_TEXT_PP(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
Oid roleid;
AttrNumber colattnum;
AclMode mode;
int privresult;
roleid = get_role_oid_or_public(NameStr(*username));
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_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_PP(3);
Oid roleid;
AclMode mode;
int privresult;
roleid = get_role_oid_or_public(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_column_privilege_id_name_name
* Check user privileges on a column given
* oid roleid, text tablename, text colname, and text priv name.
*/
Datum
has_column_privilege_id_name_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *tablename = PG_GETARG_TEXT_PP(1);
text *column = PG_GETARG_TEXT_PP(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
Oid tableoid;
AttrNumber colattnum;
AclMode mode;
int privresult;
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_PP(1);
AttrNumber colattnum = PG_GETARG_INT16(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
Oid tableoid;
AclMode mode;
int privresult;
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_column_privilege_id_id_name
* Check user privileges on a column given
* oid roleid, table oid, text colname, and text priv name.
*/
Datum
has_column_privilege_id_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid tableoid = PG_GETARG_OID(1);
text *column = PG_GETARG_TEXT_PP(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
AttrNumber colattnum;
AclMode mode;
int privresult;
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_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_PP(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_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_column_privilege_name_name(PG_FUNCTION_ARGS)
{
text *tablename = PG_GETARG_TEXT_PP(0);
text *column = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid tableoid;
AttrNumber colattnum;
AclMode mode;
int privresult;
roleid = GetUserId();
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_name_attnum
* Check user privileges on a column given
* text tablename, int attnum, and text priv name.
* current_user is assumed
*/
Datum
has_column_privilege_name_attnum(PG_FUNCTION_ARGS)
{
text *tablename = PG_GETARG_TEXT_PP(0);
AttrNumber colattnum = PG_GETARG_INT16(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid tableoid;
AclMode mode;
int privresult;
roleid = GetUserId();
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_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_column_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid tableoid = PG_GETARG_OID(0);
text *column = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AttrNumber colattnum;
AclMode mode;
int privresult;
roleid = GetUserId();
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_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_PP(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_column_privilege family.
*/
/*
* Given a table OID and a column name expressed as a string, look it up
* and return the column number
*/
static AttrNumber
convert_column_name(Oid tableoid, text *column)
{
AttrNumber attnum;
char *colname;
colname = text_to_cstring(column);
attnum = get_attnum(tableoid, colname);
if (attnum == InvalidAttrNumber)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" of relation \"%s\" does not exist",
colname, get_rel_name(tableoid))));
pfree(colname);
return attnum;
}
/*
* convert_column_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_column_priv_string(text *priv_type_text)
{
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_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, or NULL if object doesn't exist.
*/
/*
* has_database_privilege_name_name
* Check user privileges on a database given
* name username, text databasename, and text priv name.
*/
Datum
has_database_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
text *databasename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid databaseoid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*username));
databaseoid = convert_database_name(databasename);
mode = convert_database_priv_string(priv_type_text);
aclresult = pg_database_aclcheck(databaseoid, 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.
* current_user is assumed
*/
Datum
has_database_privilege_name(PG_FUNCTION_ARGS)
{
text *databasename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid databaseoid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
databaseoid = convert_database_name(databasename);
mode = convert_database_priv_string(priv_type_text);
aclresult = pg_database_aclcheck(databaseoid, 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.
*/
Datum
has_database_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid databaseoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_database_priv_string(priv_type_text);
if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
PG_RETURN_NULL();
aclresult = pg_database_aclcheck(databaseoid, 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.
* current_user is assumed
*/
Datum
has_database_privilege_id(PG_FUNCTION_ARGS)
{
Oid databaseoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
mode = convert_database_priv_string(priv_type_text);
if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
PG_RETURN_NULL();
aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_database_privilege_id_name
* Check user privileges on a database given
* roleid, text databasename, and text priv name.
*/
Datum
has_database_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *databasename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid databaseoid;
AclMode mode;
AclResult aclresult;
databaseoid = convert_database_name(databasename);
mode = convert_database_priv_string(priv_type_text);
aclresult = pg_database_aclcheck(databaseoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_database_privilege_id_id
* Check user privileges on a database given
* roleid, database oid, and text priv name.
*/
Datum
has_database_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid databaseoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
mode = convert_database_priv_string(priv_type_text);
if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
PG_RETURN_NULL();
aclresult = pg_database_aclcheck(databaseoid, 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 = text_to_cstring(databasename);
return get_database_oid(dbname, false);
}
/*
* convert_database_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_database_priv_string(text *priv_type_text)
{
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);
}
/*
* 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_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_foreign_data_wrapper_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
text *fdwname = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid fdwid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*username));
fdwid = convert_foreign_data_wrapper_name(fdwname);
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* 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_foreign_data_wrapper_privilege_name(PG_FUNCTION_ARGS)
{
text *fdwname = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid fdwid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
fdwid = convert_foreign_data_wrapper_name(fdwname);
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* 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_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid fdwid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* 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_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS)
{
Oid fdwid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* 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_foreign_data_wrapper_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *fdwname = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid fdwid;
AclMode mode;
AclResult aclresult;
fdwid = convert_foreign_data_wrapper_name(fdwname);
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* 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_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid fdwid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* Support routines for has_foreign_data_wrapper_privilege family.
*/
/*
* Given a FDW name expressed as a string, look it up and return Oid
*/
static Oid
convert_foreign_data_wrapper_name(text *fdwname)
{
char *fdwstr = text_to_cstring(fdwname);
return get_foreign_data_wrapper_oid(fdwstr, false);
}
/*
* convert_foreign_data_wrapper_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_foreign_data_wrapper_priv_string(text *priv_type_text)
{
static const priv_map foreign_data_wrapper_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, foreign_data_wrapper_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 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_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid functionoid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(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_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(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_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_function_priv_string(priv_type_text);
if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
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_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
mode = convert_function_priv_string(priv_type_text);
if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
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_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(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_PP(2);
AclMode mode;
AclResult aclresult;
mode = convert_function_priv_string(priv_type_text);
if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
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,
2005-10-15 04:49:52 +02:00
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_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid languageoid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(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_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(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_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_language_priv_string(priv_type_text);
if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
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_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
mode = convert_language_priv_string(priv_type_text);
if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
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_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(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_PP(2);
AclMode mode;
AclResult aclresult;
mode = convert_language_priv_string(priv_type_text);
if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
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);
return get_language_oid(langname, false);
}
/*
* 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_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid schemaoid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(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_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(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_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_schema_priv_string(priv_type_text);
if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
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_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
mode = convert_schema_priv_string(priv_type_text);
if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
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_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(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_PP(2);
AclMode mode;
AclResult aclresult;
mode = convert_schema_priv_string(priv_type_text);
if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
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);
return get_namespace_oid(nspname, false);
}
/*
* 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_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid serverid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(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_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(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_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(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_PP(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_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(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_PP(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 get_foreign_server_oid(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_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid tablespaceoid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(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_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(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_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(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_PP(1);
2005-10-15 04:49:52 +02:00
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_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(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_PP(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);
return get_tablespace_oid(spcname, false);
}
/*
* 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);
}
/*
* has_type_privilege variants
* These are all named "has_type_privilege" at the SQL level.
* They take various combinations of type name, type 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_type_privilege_name_name
* Check user privileges on a type given
* name username, text typename, and text priv name.
*/
Datum
has_type_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
text *typename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid typeoid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*username));
typeoid = convert_type_name(typename);
mode = convert_type_priv_string(priv_type_text);
aclresult = pg_type_aclcheck(typeoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_type_privilege_name
* Check user privileges on a type given
* text typename and text priv name.
* current_user is assumed
*/
Datum
has_type_privilege_name(PG_FUNCTION_ARGS)
{
text *typename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid typeoid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
typeoid = convert_type_name(typename);
mode = convert_type_priv_string(priv_type_text);
aclresult = pg_type_aclcheck(typeoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_type_privilege_name_id
* Check user privileges on a type given
* name usename, type oid, and text priv name.
*/
Datum
has_type_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid typeoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_type_priv_string(priv_type_text);
if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
PG_RETURN_NULL();
aclresult = pg_type_aclcheck(typeoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_type_privilege_id
* Check user privileges on a type given
* type oid, and text priv name.
* current_user is assumed
*/
Datum
has_type_privilege_id(PG_FUNCTION_ARGS)
{
Oid typeoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
mode = convert_type_priv_string(priv_type_text);
if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
PG_RETURN_NULL();
aclresult = pg_type_aclcheck(typeoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_type_privilege_id_name
* Check user privileges on a type given
* roleid, text typename, and text priv name.
*/
Datum
has_type_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *typename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid typeoid;
AclMode mode;
AclResult aclresult;
typeoid = convert_type_name(typename);
mode = convert_type_priv_string(priv_type_text);
aclresult = pg_type_aclcheck(typeoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_type_privilege_id_id
* Check user privileges on a type given
* roleid, type oid, and text priv name.
*/
Datum
has_type_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid typeoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
mode = convert_type_priv_string(priv_type_text);
if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
PG_RETURN_NULL();
aclresult = pg_type_aclcheck(typeoid, roleid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* Support routines for has_type_privilege family.
*/
/*
* Given a type name expressed as a string, look it up and return Oid
*/
static Oid
convert_type_name(text *typename)
{
char *typname = text_to_cstring(typename);
Oid oid;
oid = DatumGetObjectId(DirectFunctionCall1(regtypein,
CStringGetDatum(typname)));
if (!OidIsValid(oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" does not exist", typname)));
return oid;
}
/*
* convert_type_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_type_priv_string(text *priv_type_text)
{
static const priv_map type_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, type_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_PP(2);
Oid roleid;
Oid roleoid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid(NameStr(*username), false);
roleoid = get_role_oid(NameStr(*rolename), false);
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_PP(1);
Oid roleid;
Oid roleoid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
roleoid = get_role_oid(NameStr(*rolename), false);
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_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid(NameStr(*username), false);
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_PP(1);
2005-10-15 04:49:52 +02:00
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_PP(2);
Oid roleoid;
AclMode mode;
AclResult aclresult;
roleoid = get_role_oid(NameStr(*rolename), false);
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_PP(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))
{
2014-02-17 15:33:31 +01:00
/*
* XXX For roleid == role_oid, is_admin_of_role() also examines the
* session and call stack. That suits two-argument pg_has_role(), but
* it gives the three-argument version a lamentable whimsy.
*/
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())
{
/*
2005-10-15 04:49:52 +02:00
* In normal mode, set a callback on any syscache invalidation of
* pg_auth_members rows
*/
CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
RoleMembershipCacheCallback,
(Datum) 0);
}
}
/*
* RoleMembershipCacheCallback
2005-10-15 04:49:52 +02:00
* Syscache inval callback function
*/
static void
RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
{
/* 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 = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
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)
{
2005-10-15 04:49:52 +02:00
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;
2005-10-15 04:49:52 +02:00
/*
* 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)
{
2005-10-15 04:49:52 +02:00
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 = SearchSysCacheList1(AUTHMEMMEMROLE,
ObjectIdGetDatum(memberid));
for (i = 0; i < memlist->n_members; i++)
{
HeapTuple tup = &memlist->members[i]->tuple;
2005-10-15 04:49:52 +02:00
Oid otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
/*
* Even though there shouldn't be any loops in the membership
2005-10-15 04:49:52 +02:00
* 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
*/
Phase 2 of pgindent updates. Change pg_bsd_indent to follow upstream rules for placement of comments to the right of code, and remove pgindent hack that caused comments following #endif to not obey the general rule. Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using the published version of pg_bsd_indent, but a hacked-up version that tried to minimize the amount of movement of comments to the right of code. The situation of interest is where such a comment has to be moved to the right of its default placement at column 33 because there's code there. BSD indent has always moved right in units of tab stops in such cases --- but in the previous incarnation, indent was working in 8-space tab stops, while now it knows we use 4-space tabs. So the net result is that in about half the cases, such comments are placed one tab stop left of before. This is better all around: it leaves more room on the line for comment text, and it means that in such cases the comment uniformly starts at the next 4-space tab stop after the code, rather than sometimes one and sometimes two tabs after. Also, ensure that comments following #endif are indented the same as comments following other preprocessor commands such as #else. That inconsistency turns out to have been self-inflicted damage from a poorly-thought-through post-indent "fixup" in pgindent. This patch is much less interesting than the first round of indent changes, but also bulkier, so I thought it best to separate the effects. Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02:00
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)
{
2005-10-15 04:49:52 +02:00
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;
2005-10-15 04:49:52 +02:00
/*
* 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)
{
2005-10-15 04:49:52 +02:00
Oid memberid = lfirst_oid(l);
CatCList *memlist;
int i;
/* Find roles that memberid is directly a member of */
memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
ObjectIdGetDatum(memberid));
for (i = 0; i < memlist->n_members; i++)
{
HeapTuple tup = &memlist->members[i]->tuple;
2005-10-15 04:49:52 +02:00
Oid otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
/*
* Even though there shouldn't be any loops in the membership
2005-10-15 04:49:52 +02:00
* 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;
2005-10-15 04:49:52 +02:00
/*
* 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;
2005-10-15 04:49:52 +02:00
/*
* 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, false))));
}
/*
* 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? That is, is member the role itself (subject to
2014-02-17 15:33:31 +01:00
* restrictions below), a member (directly or indirectly) WITH ADMIN OPTION,
* or a superuser?
*/
bool
is_admin_of_role(Oid member, Oid role)
{
bool result = false;
2005-10-15 04:49:52 +02:00
List *roles_list;
ListCell *l;
if (superuser_arg(member))
return true;
2014-02-17 15:33:31 +01:00
if (member == role)
2014-02-17 15:33:31 +01:00
/*
* A role can admin itself when it matches the session user and we're
* outside any security-restricted operation, SECURITY DEFINER or
* similar context. SQL-standard roles cannot self-admin. However,
* SQL-standard users are distinct from roles, and they are not
* grantable like roles: PostgreSQL's role-user duality extends the
* standard. Checking for a session user match has the effect of
* letting a role self-admin only when it's conspicuously behaving
* like a user. Note that allowing self-admin under a mere SET ROLE
* would make WITH ADMIN OPTION largely irrelevant; any member could
* SET ROLE to issue the otherwise-forbidden command.
*
* Withholding self-admin in a security-restricted operation prevents
* object owners from harnessing the session user identity during
* administrative maintenance. Suppose Alice owns a database, has
* issued "GRANT alice TO bob", and runs a daily ANALYZE. Bob creates
* an alice-owned SECURITY DEFINER function that issues "REVOKE alice
* FROM carol". If he creates an expression index calling that
* function, Alice will attempt the REVOKE during each ANALYZE.
* Checking InSecurityRestrictedOperation() thwarts that attack.
*
* Withholding self-admin in SECURITY DEFINER functions makes their
* behavior independent of the calling user. There's no security or
* SQL-standard-conformance need for that restriction, though.
*
* A role cannot have actual WITH ADMIN OPTION on itself, because that
* would imply a membership loop. Therefore, we're done either way.
*/
return member == GetSessionUserId() &&
!InLocalUserIdChange() && !InSecurityRestrictedOperation();
2005-10-15 04:49:52 +02:00
/*
* 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)
{
2005-10-15 04:49:52 +02:00
Oid memberid = lfirst_oid(l);
CatCList *memlist;
int i;
/* Find roles that memberid is directly a member of */
memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
ObjectIdGetDatum(memberid));
for (i = 0; i < memlist->n_members; i++)
{
HeapTuple tup = &memlist->members[i]->tuple;
2005-10-15 04:49:52 +02:00
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)
{
2005-10-15 04:49:52 +02:00
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);
2005-10-15 04:49:52 +02:00
List *roles_list;
int nrights;
ListCell *l;
/*
2005-10-15 04:49:52 +02:00
* 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
2005-10-15 04:49:52 +02:00
* 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)
{
2005-10-15 04:49:52 +02:00
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;
}
2005-10-15 04:49:52 +02:00
/*
* If it has just some of the needed privileges, remember best
* candidate.
*/
if (otherprivs != ACL_NO_RIGHTS)
{
2005-10-15 04:49:52 +02:00
int nnewrights = count_one_bits(otherprivs);
if (nnewrights > nrights)
{
*grantorId = otherrole;
*grantOptions = otherprivs;
nrights = nnewrights;
}
}
}
}
/*
* get_role_oid - Given a role name, look up the role's OID.
*
* If missing_ok is false, throw an error if role name not found. If
* true, just return InvalidOid.
*/
Oid
get_role_oid(const char *rolname, bool missing_ok)
{
Oid oid;
oid = GetSysCacheOid1(AUTHNAME, CStringGetDatum(rolname));
if (!OidIsValid(oid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role \"%s\" does not exist", rolname)));
return oid;
}
/*
* get_role_oid_or_public - As above, but return ACL_ID_PUBLIC if the
2011-04-10 17:42:00 +02:00
* role name is "public".
*/
Row-Level Security Policies (RLS) Building on the updatable security-barrier views work, add the ability to define policies on tables to limit the set of rows which are returned from a query and which are allowed to be added to a table. Expressions defined by the policy for filtering are added to the security barrier quals of the query, while expressions defined to check records being added to a table are added to the with-check options of the query. New top-level commands are CREATE/ALTER/DROP POLICY and are controlled by the table owner. Row Security is able to be enabled and disabled by the owner on a per-table basis using ALTER TABLE .. ENABLE/DISABLE ROW SECURITY. Per discussion, ROW SECURITY is disabled on tables by default and must be enabled for policies on the table to be used. If no policies exist on a table with ROW SECURITY enabled, a default-deny policy is used and no records will be visible. By default, row security is applied at all times except for the table owner and the superuser. A new GUC, row_security, is added which can be set to ON, OFF, or FORCE. When set to FORCE, row security will be applied even for the table owner and superusers. When set to OFF, row security will be disabled when allowed and an error will be thrown if the user does not have rights to bypass row security. Per discussion, pg_dump sets row_security = OFF by default to ensure that exports and backups will have all data in the table or will error if there are insufficient privileges to bypass row security. A new option has been added to pg_dump, --enable-row-security, to ask pg_dump to export with row security enabled. A new role capability, BYPASSRLS, which can only be set by the superuser, is added to allow other users to be able to bypass row security using row_security = OFF. Many thanks to the various individuals who have helped with the design, particularly Robert Haas for his feedback. Authors include Craig Ringer, KaiGai Kohei, Adam Brightwell, Dean Rasheed, with additional changes and rework by me. Reviewers have included all of the above, Greg Smith, Jeff McCormick, and Robert Haas.
2014-09-19 17:18:35 +02:00
Oid
get_role_oid_or_public(const char *rolname)
{
if (strcmp(rolname, "public") == 0)
return ACL_ID_PUBLIC;
return get_role_oid(rolname, false);
}
/*
* Given a RoleSpec node, return the OID it corresponds to. If missing_ok is
* true, return InvalidOid if the role does not exist.
*
* PUBLIC is always disallowed here. Routines wanting to handle the PUBLIC
* case must check the case separately.
*/
Oid
get_rolespec_oid(const RoleSpec *role, bool missing_ok)
{
Oid oid;
switch (role->roletype)
{
case ROLESPEC_CSTRING:
Assert(role->rolename);
oid = get_role_oid(role->rolename, missing_ok);
break;
case ROLESPEC_CURRENT_USER:
oid = GetUserId();
break;
case ROLESPEC_SESSION_USER:
oid = GetSessionUserId();
break;
case ROLESPEC_PUBLIC:
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role \"%s\" does not exist", "public")));
oid = InvalidOid; /* make compiler happy */
break;
default:
elog(ERROR, "unexpected role type %d", role->roletype);
}
return oid;
}
/*
* Given a RoleSpec node, return the pg_authid HeapTuple it corresponds to.
* Caller must ReleaseSysCache when done with the result tuple.
*/
HeapTuple
get_rolespec_tuple(const RoleSpec *role)
{
HeapTuple tuple;
switch (role->roletype)
{
case ROLESPEC_CSTRING:
Assert(role->rolename);
tuple = SearchSysCache1(AUTHNAME, CStringGetDatum(role->rolename));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role \"%s\" does not exist", role->rolename)));
break;
case ROLESPEC_CURRENT_USER:
tuple = SearchSysCache1(AUTHOID, GetUserId());
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for role %u", GetUserId());
break;
case ROLESPEC_SESSION_USER:
tuple = SearchSysCache1(AUTHOID, GetSessionUserId());
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for role %u", GetSessionUserId());
break;
case ROLESPEC_PUBLIC:
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role \"%s\" does not exist", "public")));
2015-05-24 03:35:49 +02:00
tuple = NULL; /* make compiler happy */
break;
default:
elog(ERROR, "unexpected role type %d", role->roletype);
}
return tuple;
}
/*
* Given a RoleSpec, returns a palloc'ed copy of the corresponding role's name.
*/
char *
get_rolespec_name(const RoleSpec *role)
{
HeapTuple tp;
Form_pg_authid authForm;
char *rolename;
tp = get_rolespec_tuple(role);
authForm = (Form_pg_authid) GETSTRUCT(tp);
rolename = pstrdup(NameStr(authForm->rolname));
ReleaseSysCache(tp);
return rolename;
}
/*
* Given a RoleSpec, throw an error if the name is reserved, using detail_msg,
* if provided.
*
* If node is NULL, no error is thrown. If detail_msg is NULL then no detail
* message is provided.
*/
void
check_rolespec_name(const RoleSpec *role, const char *detail_msg)
{
if (!role)
return;
if (role->roletype != ROLESPEC_CSTRING)
return;
if (IsReservedName(role->rolename))
{
if (detail_msg)
ereport(ERROR,
(errcode(ERRCODE_RESERVED_NAME),
2016-07-26 04:07:44 +02:00
errmsg("role name \"%s\" is reserved",
2016-06-10 00:02:36 +02:00
role->rolename),
errdetail("%s", detail_msg)));
else
ereport(ERROR,
(errcode(ERRCODE_RESERVED_NAME),
2016-07-26 04:07:44 +02:00
errmsg("role name \"%s\" is reserved",
2016-06-10 00:02:36 +02:00
role->rolename)));
}
}