1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* acl.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Basic access control list data structures manipulation routines.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2020-01-01 18:21:45 +01:00
|
|
|
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/utils/adt/acl.c
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
1996-11-06 11:32:10 +01:00
|
|
|
|
2001-06-05 21:34:56 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2016-04-08 22:56:27 +02:00
|
|
|
#include "catalog/catalog.h"
|
2002-03-26 20:17:02 +01:00
|
|
|
#include "catalog/namespace.h"
|
2005-06-28 07:09:14 +02:00
|
|
|
#include "catalog/pg_auth_members.h"
|
2019-11-12 04:00:16 +01:00
|
|
|
#include "catalog/pg_authid.h"
|
2009-08-03 23:11:40 +02:00
|
|
|
#include "catalog/pg_class.h"
|
2019-11-12 04:00:16 +01:00
|
|
|
#include "catalog/pg_type.h"
|
2002-08-09 18:45:16 +02:00
|
|
|
#include "commands/dbcommands.h"
|
2010-08-05 16:45:09 +02:00
|
|
|
#include "commands/proclang.h"
|
2004-07-12 22:23:59 +02:00
|
|
|
#include "commands/tablespace.h"
|
2020-02-27 04:55:41 +01:00
|
|
|
#include "common/hashfn.h"
|
2008-12-19 17:25:19 +01:00
|
|
|
#include "foreign/foreign.h"
|
2009-12-05 22:43:36 +01:00
|
|
|
#include "funcapi.h"
|
2019-11-07 04:51:04 +01:00
|
|
|
#include "lib/qunique.h"
|
2000-10-07 02:58:23 +02:00
|
|
|
#include "miscadmin.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/acl.h"
|
2019-08-16 19:33:30 +02:00
|
|
|
#include "utils/array.h"
|
2000-06-05 09:29:25 +02:00
|
|
|
#include "utils/builtins.h"
|
2012-08-29 00:26:24 +02:00
|
|
|
#include "utils/catcache.h"
|
2005-06-28 07:09:14 +02:00
|
|
|
#include "utils/inval.h"
|
2001-06-14 03:09:22 +02:00
|
|
|
#include "utils/lsyscache.h"
|
2005-06-28 07:09:14 +02:00
|
|
|
#include "utils/memutils.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/syscache.h"
|
2017-01-21 02:29:53 +01:00
|
|
|
#include "utils/varlena.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
AclMode value;
|
|
|
|
} priv_map;
|
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
2005-07-26 18:38:29 +02:00
|
|
|
* 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).
|
|
|
|
*
|
2005-06-29 22:34:15 +02:00
|
|
|
* Possibly this mechanism should be generalized to allow caching membership
|
2005-07-26 18:38:29 +02:00
|
|
|
* 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.
|
2005-06-29 22:34:15 +02:00
|
|
|
*
|
2005-07-26 18:38:29 +02:00
|
|
|
* 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.
|
2005-06-28 07:09:14 +02:00
|
|
|
*/
|
2005-07-26 18:38:29 +02:00
|
|
|
static Oid cached_privs_role = InvalidOid;
|
2005-10-15 04:49:52 +02:00
|
|
|
static List *cached_privs_roles = NIL;
|
2005-07-26 18:38:29 +02:00
|
|
|
static Oid cached_member_role = InvalidOid;
|
2005-10-15 04:49:52 +02:00
|
|
|
static List *cached_membership_roles = NIL;
|
2005-06-28 07:09:14 +02:00
|
|
|
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2001-06-10 01:21:55 +02:00
|
|
|
static const char *getid(const char *s, char *n);
|
2003-06-02 21:00:29 +02:00
|
|
|
static void putid(char *p, const char *s);
|
2003-06-11 11:23:55 +02:00
|
|
|
static Acl *allocacl(int n);
|
2005-11-18 03:38:24 +01:00
|
|
|
static void check_acl(const Acl *acl);
|
2003-01-24 00:39:07 +01:00
|
|
|
static const char *aclparse(const char *s, AclItem *aip);
|
2003-06-27 02:33:26 +02:00
|
|
|
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);
|
2004-06-01 23:49:23 +02:00
|
|
|
static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
|
2019-05-22 19:04:48 +02:00
|
|
|
Oid ownerId);
|
2005-06-28 07:09:14 +02:00
|
|
|
static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
|
2019-05-22 19:04:48 +02:00
|
|
|
Oid ownerId, DropBehavior behavior);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2003-06-11 11:23:55 +02:00
|
|
|
static AclMode convert_priv_string(text *priv_type_text);
|
2009-02-06 22:15:12 +01:00
|
|
|
static AclMode convert_any_priv_string(text *priv_type_text,
|
2019-05-22 19:04:48 +02:00
|
|
|
const priv_map *privileges);
|
2003-06-11 11:23:55 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
static Oid convert_table_name(text *tablename);
|
2002-08-09 18:45:16 +02:00
|
|
|
static AclMode convert_table_priv_string(text *priv_type_text);
|
2009-08-03 23:11:40 +02:00
|
|
|
static AclMode convert_sequence_priv_string(text *priv_type_text);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2002-08-09 18:45:16 +02:00
|
|
|
static AclMode convert_database_priv_string(text *priv_type_text);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2002-08-09 18:45:16 +02:00
|
|
|
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);
|
2002-08-09 18:45:16 +02:00
|
|
|
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);
|
2002-08-09 18:45:16 +02:00
|
|
|
static AclMode convert_schema_priv_string(text *priv_type_text);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2004-07-12 22:23:59 +02:00
|
|
|
static AclMode convert_tablespace_priv_string(text *priv_type_text);
|
2011-12-19 23:05:19 +01:00
|
|
|
static Oid convert_type_name(text *typename);
|
|
|
|
static AclMode convert_type_priv_string(text *priv_type_text);
|
2005-07-26 02:04:19 +02:00
|
|
|
static AclMode convert_role_priv_string(text *priv_type_text);
|
|
|
|
static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2011-08-17 01:27:46 +02:00
|
|
|
static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
|
2005-06-28 07:09:14 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* getid
|
1997-09-07 07:04:48 +02:00
|
|
|
* Consumes the first alphanumeric string (identifier) found in string
|
2014-05-06 18:12:18 +02:00
|
|
|
* 's', ignoring any leading white space. If it finds a double quote
|
1999-03-21 07:31:59 +01:00
|
|
|
* it returns the word inside the quotes.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* the string position in 's' that points to the next non-space character
|
1999-03-21 07:31:59 +01:00
|
|
|
* in 's', after any quotes. Also:
|
2003-08-14 16:19:11 +02:00
|
|
|
* - loads the identifier into 'n'. (If no identifier is found, 'n'
|
|
|
|
* contains an empty string.) 'n' must be NAMEDATALEN bytes.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2001-06-10 01:21:55 +02:00
|
|
|
static const char *
|
|
|
|
getid(const char *s, char *n)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2003-06-02 21:00:29 +02:00
|
|
|
int len = 0;
|
|
|
|
bool in_quotes = false;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
Assert(s && n);
|
|
|
|
|
2000-12-03 21:45:40 +01:00
|
|
|
while (isspace((unsigned char) *s))
|
1999-03-21 07:31:59 +01:00
|
|
|
s++;
|
2003-08-14 16:19:11 +02:00
|
|
|
/* This code had better match what putid() does, below */
|
2003-06-02 21:00:29 +02:00
|
|
|
for (;
|
|
|
|
*s != '\0' &&
|
2003-08-04 02:43:34 +02:00
|
|
|
(isalnum((unsigned char) *s) ||
|
|
|
|
*s == '_' ||
|
|
|
|
*s == '"' ||
|
|
|
|
in_quotes);
|
2003-06-02 21:00:29 +02:00
|
|
|
s++)
|
1999-03-21 07:31:59 +01:00
|
|
|
{
|
2003-06-02 21:00:29 +02:00
|
|
|
if (*s == '"')
|
1999-03-21 07:31:59 +01:00
|
|
|
{
|
2003-08-14 16:19:11 +02:00
|
|
|
/* 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++;
|
1999-03-21 07:31:59 +01:00
|
|
|
}
|
2003-08-14 16:19:11 +02:00
|
|
|
|
|
|
|
/* 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)));
|
2003-08-14 16:19:11 +02:00
|
|
|
|
|
|
|
n[len++] = *s;
|
1999-03-21 07:31:59 +01:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
n[len] = '\0';
|
2000-12-03 21:45:40 +01:00
|
|
|
while (isspace((unsigned char) *s))
|
2003-06-02 21:00:29 +02:00
|
|
|
s++;
|
1998-09-01 05:29:17 +02:00
|
|
|
return s;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2003-06-02 21:00:29 +02:00
|
|
|
/*
|
2005-06-29 22:34:15 +02:00
|
|
|
* Write a role name at *p, adding double quotes if needed.
|
2003-08-14 16:19:11 +02:00
|
|
|
* 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
|
2003-06-02 21:00:29 +02:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
putid(char *p, const char *s)
|
|
|
|
{
|
|
|
|
const char *src;
|
2003-08-04 02:43:34 +02:00
|
|
|
bool safe = true;
|
2003-06-02 21:00:29 +02:00
|
|
|
|
|
|
|
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++)
|
2003-08-14 16:19:11 +02:00
|
|
|
{
|
|
|
|
/* A double quote character in a username is encoded as "" */
|
|
|
|
if (*src == '"')
|
|
|
|
*p++ = '"';
|
2003-06-02 21:00:29 +02:00
|
|
|
*p++ = *src;
|
2003-08-14 16:19:11 +02:00
|
|
|
}
|
2003-06-02 21:00:29 +02:00
|
|
|
if (!safe)
|
|
|
|
*p++ = '"';
|
|
|
|
*p = '\0';
|
|
|
|
}
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
|
|
|
* aclparse
|
1997-09-07 07:04:48 +02:00
|
|
|
* Consumes and parses an ACL specification of the form:
|
2003-01-24 00:39:07 +01:00
|
|
|
* [group|user] [A-Za-z0-9]*=[rwaR]*
|
1997-09-07 07:04:48 +02:00
|
|
|
* from string 's', ignoring any leading white space or white space
|
|
|
|
* between the optional id type keyword (group|user) and the actual
|
|
|
|
* ACL specification.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2005-06-29 22:34:15 +02:00
|
|
|
* The group|user decoration is unnecessary in the roles world,
|
|
|
|
* but we still accept it for backward compatibility.
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This routine is called by the parser as well as aclitemin(), hence
|
|
|
|
* the added generality.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* the string position in 's' immediately following the ACL
|
2014-05-06 18:12:18 +02:00
|
|
|
* specification. Also:
|
1997-09-07 07:04:48 +02:00
|
|
|
* - loads the structure pointed to by 'aip' with the appropriate
|
|
|
|
* UID/GID, id type identifier and mode type values.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2002-04-21 02:26:44 +02:00
|
|
|
static const char *
|
2003-01-24 00:39:07 +01:00
|
|
|
aclparse(const char *s, AclItem *aip)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
AclMode privs,
|
|
|
|
goption,
|
|
|
|
read;
|
1997-09-08 04:41:22 +02:00
|
|
|
char name[NAMEDATALEN];
|
2003-01-24 00:39:07 +01:00
|
|
|
char name2[NAMEDATALEN];
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-01-24 00:39:07 +01:00
|
|
|
Assert(s && aip);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
s = getid(s, name);
|
2003-01-24 00:39:07 +01:00
|
|
|
if (*s != '=')
|
1999-03-21 07:31:59 +01:00
|
|
|
{
|
|
|
|
/* we just read a keyword, not a name */
|
2005-06-29 22:34:15 +02:00
|
|
|
if (strcmp(name, "group") != 0 && strcmp(name, "user") != 0)
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("unrecognized key word: \"%s\"", name),
|
2005-10-15 04:49:52 +02:00
|
|
|
errhint("ACL key word must be \"group\" or \"user\".")));
|
1997-09-07 07:04:48 +02:00
|
|
|
s = getid(s, name); /* move s to the name beyond the keyword */
|
|
|
|
if (name[0] == '\0')
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
|
|
errmsg("missing name"),
|
2005-06-29 22:34:15 +02:00
|
|
|
errhint("A name must follow the \"group\" or \"user\" key word.")));
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-01-24 00:39:07 +01:00
|
|
|
if (*s != '=')
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
|
|
errmsg("missing \"=\" sign")));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-01-24 00:39:07 +01:00
|
|
|
privs = goption = ACL_NO_RIGHTS;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
for (++s, read = 0; isalpha((unsigned char) *s) || *s == '*'; s++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
switch (*s)
|
|
|
|
{
|
2003-01-24 00:39:07 +01:00
|
|
|
case '*':
|
|
|
|
goption |= read;
|
|
|
|
break;
|
2002-04-21 02:26:44 +02:00
|
|
|
case ACL_INSERT_CHR:
|
2003-01-24 00:39:07 +01:00
|
|
|
read = ACL_INSERT;
|
2002-04-21 02:26:44 +02:00
|
|
|
break;
|
|
|
|
case ACL_SELECT_CHR:
|
2003-01-24 00:39:07 +01:00
|
|
|
read = ACL_SELECT;
|
2002-04-21 02:26:44 +02:00
|
|
|
break;
|
|
|
|
case ACL_UPDATE_CHR:
|
2003-01-24 00:39:07 +01:00
|
|
|
read = ACL_UPDATE;
|
2002-04-21 02:26:44 +02:00
|
|
|
break;
|
|
|
|
case ACL_DELETE_CHR:
|
2003-01-24 00:39:07 +01:00
|
|
|
read = ACL_DELETE;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
2008-09-08 02:47:41 +02:00
|
|
|
case ACL_TRUNCATE_CHR:
|
|
|
|
read = ACL_TRUNCATE;
|
|
|
|
break;
|
2002-04-21 02:26:44 +02:00
|
|
|
case ACL_REFERENCES_CHR:
|
2003-01-24 00:39:07 +01:00
|
|
|
read = ACL_REFERENCES;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
2002-04-21 02:26:44 +02:00
|
|
|
case ACL_TRIGGER_CHR:
|
2003-01-24 00:39:07 +01:00
|
|
|
read = ACL_TRIGGER;
|
2001-05-27 11:59:30 +02:00
|
|
|
break;
|
2002-04-21 02:26:44 +02:00
|
|
|
case ACL_EXECUTE_CHR:
|
2003-01-24 00:39:07 +01:00
|
|
|
read = ACL_EXECUTE;
|
2001-05-27 11:59:30 +02:00
|
|
|
break;
|
2002-04-21 02:26:44 +02:00
|
|
|
case ACL_USAGE_CHR:
|
2003-01-24 00:39:07 +01:00
|
|
|
read = ACL_USAGE;
|
2001-05-27 11:59:30 +02:00
|
|
|
break;
|
2002-04-21 02:26:44 +02:00
|
|
|
case ACL_CREATE_CHR:
|
2003-01-24 00:39:07 +01:00
|
|
|
read = ACL_CREATE;
|
2002-04-21 02:26:44 +02:00
|
|
|
break;
|
|
|
|
case ACL_CREATE_TEMP_CHR:
|
2003-01-24 00:39:07 +01:00
|
|
|
read = ACL_CREATE_TEMP;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
2006-04-30 04:09:07 +02:00
|
|
|
case ACL_CONNECT_CHR:
|
|
|
|
read = ACL_CONNECT;
|
|
|
|
break;
|
2006-09-05 23:08:36 +02:00
|
|
|
case 'R': /* ignore old RULE privileges */
|
|
|
|
read = 0;
|
|
|
|
break;
|
1997-09-08 04:41:22 +02:00
|
|
|
default:
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
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:35:54 +02:00
|
|
|
errmsg("invalid mode character: must be one of \"%s\"",
|
|
|
|
ACL_ALL_RIGHTS_STR)));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2003-01-24 00:39:07 +01:00
|
|
|
|
|
|
|
privs |= read;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
if (name[0] == '\0')
|
|
|
|
aip->ai_grantee = ACL_ID_PUBLIC;
|
|
|
|
else
|
2010-08-05 16:45:09 +02:00
|
|
|
aip->ai_grantee = get_role_oid(name, false);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
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
|
|
|
*/
|
2003-01-24 00:39:07 +01:00
|
|
|
if (*s == '/')
|
|
|
|
{
|
|
|
|
s = getid(s + 1, name2);
|
|
|
|
if (name2[0] == '\0')
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
|
|
errmsg("a name must follow the \"/\" sign")));
|
2010-08-05 16:45:09 +02:00
|
|
|
aip->ai_grantor = get_role_oid(name2, false);
|
2003-01-24 00:39:07 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
aip->ai_grantor = BOOTSTRAP_SUPERUSERID;
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANTOR),
|
2005-06-28 07:09:14 +02:00
|
|
|
errmsg("defaulting grantor to user ID %u",
|
|
|
|
BOOTSTRAP_SUPERUSERID)));
|
2003-01-24 00:39:07 +01:00
|
|
|
}
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
ACLITEM_SET_PRIVS_GOPTIONS(*aip, privs, goption);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return s;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-06-11 11:23:55 +02:00
|
|
|
* allocacl
|
1997-09-07 07:04:48 +02:00
|
|
|
* Allocates storage for a new Acl with 'n' entries.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* the new Acl
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2002-04-21 02:26:44 +02:00
|
|
|
static Acl *
|
2003-06-11 11:23:55 +02:00
|
|
|
allocacl(int n)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Acl *new_acl;
|
|
|
|
Size size;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (n < 0)
|
2003-07-27 06:53:12 +02:00
|
|
|
elog(ERROR, "invalid size: %d", n);
|
1997-09-07 07:04:48 +02:00
|
|
|
size = ACL_N_SIZE(n);
|
2002-11-13 01:39:48 +01:00
|
|
|
new_acl = (Acl *) palloc0(size);
|
2007-02-28 00:48:10 +01:00
|
|
|
SET_VARSIZE(new_acl, size);
|
1997-09-07 07:04:48 +02:00
|
|
|
new_acl->ndim = 1;
|
2005-11-17 23:14:56 +01:00
|
|
|
new_acl->dataoffset = 0; /* we never put in any nulls */
|
2002-08-26 19:54:02 +02:00
|
|
|
new_acl->elemtype = ACLITEMOID;
|
2004-08-06 20:05:49 +02:00
|
|
|
ARR_LBOUND(new_acl)[0] = 1;
|
1997-09-07 07:04:48 +02:00
|
|
|
ARR_DIMS(new_acl)[0] = n;
|
1998-09-01 05:29:17 +02:00
|
|
|
return new_acl;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
/*
|
|
|
|
* Create a zero-entry ACL
|
|
|
|
*/
|
|
|
|
Acl *
|
|
|
|
make_empty_acl(void)
|
|
|
|
{
|
|
|
|
return allocacl(0);
|
|
|
|
}
|
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
/*
|
|
|
|
* Copy an ACL
|
|
|
|
*/
|
|
|
|
Acl *
|
|
|
|
aclcopy(const Acl *orig_acl)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
Acl *result_acl;
|
2009-01-22 21:16:10 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
Acl *result_acl;
|
2009-01-22 21:16:10 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2005-11-18 03:38:24 +01:00
|
|
|
/*
|
|
|
|
* 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")));
|
2005-11-18 03:38:24 +01:00
|
|
|
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")));
|
2005-11-18 03:38:24 +01:00
|
|
|
}
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
|
|
|
* aclitemin
|
1997-09-07 07:04:48 +02:00
|
|
|
* Allocates storage for, and fills in, a new AclItem given a string
|
|
|
|
* 's' that contains an ACL specification. See aclparse for details.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* the new AclItem
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2000-08-01 00:39:17 +02:00
|
|
|
Datum
|
|
|
|
aclitemin(PG_FUNCTION_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2001-06-10 01:21:55 +02:00
|
|
|
const char *s = PG_GETARG_CSTRING(0);
|
1997-09-08 04:41:22 +02:00
|
|
|
AclItem *aip;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
aip = (AclItem *) palloc(sizeof(AclItem));
|
2003-01-24 00:39:07 +01:00
|
|
|
s = aclparse(s, aip);
|
2000-12-03 21:45:40 +01:00
|
|
|
while (isspace((unsigned char) *s))
|
1997-09-07 07:04:48 +02:00
|
|
|
++s;
|
|
|
|
if (*s)
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
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:35:54 +02:00
|
|
|
errmsg("extra garbage at the end of the ACL specification")));
|
2003-07-27 06:53:12 +02:00
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
PG_RETURN_ACLITEM_P(aip);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* aclitemout
|
1997-09-07 07:04:48 +02:00
|
|
|
* Allocates storage for, and fills in, a new null-delimited string
|
|
|
|
* containing a formatted ACL specification. See aclparse for details.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* the new string
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2000-08-01 00:39:17 +02:00
|
|
|
Datum
|
|
|
|
aclitemout(PG_FUNCTION_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
AclItem *aip = PG_GETARG_ACLITEM_P(0);
|
1998-02-26 05:46:47 +01:00
|
|
|
char *p;
|
1997-09-08 04:41:22 +02:00
|
|
|
char *out;
|
1998-08-19 04:04:17 +02:00
|
|
|
HeapTuple htup;
|
1997-09-08 04:41:22 +02:00
|
|
|
unsigned i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
out = palloc(strlen("=/") +
|
2003-06-02 21:00:29 +02:00
|
|
|
2 * N_ACL_RIGHTS +
|
2003-08-14 16:19:11 +02:00
|
|
|
2 * (2 * NAMEDATALEN + 2) +
|
2003-06-02 21:00:29 +02:00
|
|
|
1);
|
|
|
|
|
|
|
|
p = out;
|
1997-09-07 07:04:48 +02:00
|
|
|
*p = '\0';
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
if (aip->ai_grantee != ACL_ID_PUBLIC)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2010-02-14 19:42:19 +01:00
|
|
|
htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantee));
|
2005-06-28 07:09:14 +02:00
|
|
|
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);
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
while (*p)
|
|
|
|
++p;
|
2003-01-24 00:39:07 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
*p++ = '=';
|
2003-01-24 00:39:07 +01:00
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
for (i = 0; i < N_ACL_RIGHTS; ++i)
|
2003-01-24 00:39:07 +01:00
|
|
|
{
|
|
|
|
if (ACLITEM_GET_PRIVS(*aip) & (1 << i))
|
2002-04-21 02:26:44 +02:00
|
|
|
*p++ = ACL_ALL_RIGHTS_STR[i];
|
2003-01-24 00:39:07 +01:00
|
|
|
if (ACLITEM_GET_GOPTIONS(*aip) & (1 << i))
|
|
|
|
*p++ = '*';
|
|
|
|
}
|
|
|
|
|
|
|
|
*p++ = '/';
|
|
|
|
*p = '\0';
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantor));
|
2003-01-24 00:39:07 +01:00
|
|
|
if (HeapTupleIsValid(htup))
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname));
|
2003-01-24 00:39:07 +01:00
|
|
|
ReleaseSysCache(htup);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
/* Generate numeric OID if we don't find an entry */
|
|
|
|
sprintf(p, "%u", aip->ai_grantor);
|
2003-01-24 00:39:07 +01:00
|
|
|
}
|
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
PG_RETURN_CSTRING(out);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-06-27 02:33:26 +02:00
|
|
|
* aclitem_match
|
|
|
|
* Two AclItems are considered to match iff they have the same
|
2003-01-24 00:39:07 +01:00
|
|
|
* grantee and grantor; the privileges are ignored.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2000-08-01 00:39:17 +02:00
|
|
|
static bool
|
2003-06-27 02:33:26 +02:00
|
|
|
aclitem_match(const AclItem *a1, const AclItem *a2)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
return a1->ai_grantee == a2->ai_grantee &&
|
2003-01-24 00:39:07 +01:00
|
|
|
a1->ai_grantor == a2->ai_grantor;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2003-06-27 02:33:26 +02:00
|
|
|
/*
|
|
|
|
* 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);
|
2003-06-27 02:33:26 +02:00
|
|
|
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);
|
|
|
|
}
|
2000-10-02 06:49:28 +02:00
|
|
|
|
2003-08-17 21:58:06 +02:00
|
|
|
/*
|
|
|
|
* 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));
|
|
|
|
}
|
|
|
|
|
2017-09-01 04:21:21 +02:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
2003-08-17 21:58:06 +02:00
|
|
|
|
2000-10-02 06:49:28 +02:00
|
|
|
/*
|
|
|
|
* acldefault() --- create an ACL describing default access permissions
|
|
|
|
*
|
|
|
|
* Change this routine if you want to alter the default access policy for
|
2017-10-11 22:56:23 +02:00
|
|
|
* 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.
|
2009-10-05 21:24:49 +02:00
|
|
|
*
|
|
|
|
* Note that these are the hard-wired "defaults" that are used in the
|
|
|
|
* absence of any pg_default_acl entry.
|
2000-10-02 06:49:28 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
Acl *
|
2017-10-12 00:35:19 +02:00
|
|
|
acldefault(ObjectType objtype, Oid ownerId)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2002-04-21 02:26:44 +02:00
|
|
|
AclMode world_default;
|
|
|
|
AclMode owner_default;
|
2009-01-22 21:16:10 +01:00
|
|
|
int nacl;
|
1997-09-08 04:41:22 +02:00
|
|
|
Acl *acl;
|
|
|
|
AclItem *aip;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
switch (objtype)
|
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_COLUMN:
|
2009-01-22 21:16:10 +01:00
|
|
|
/* by default, columns have no extra privileges */
|
|
|
|
world_default = ACL_NO_RIGHTS;
|
|
|
|
owner_default = ACL_NO_RIGHTS;
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TABLE:
|
2002-04-21 02:26:44 +02:00
|
|
|
world_default = ACL_NO_RIGHTS;
|
|
|
|
owner_default = ACL_ALL_RIGHTS_RELATION;
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SEQUENCE:
|
2006-01-21 03:16:21 +01:00
|
|
|
world_default = ACL_NO_RIGHTS;
|
|
|
|
owner_default = ACL_ALL_RIGHTS_SEQUENCE;
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_DATABASE:
|
2006-04-30 23:15:33 +02:00
|
|
|
/* for backwards compatibility, grant some rights by default */
|
|
|
|
world_default = ACL_CREATE_TEMP | ACL_CONNECT;
|
2002-04-21 02:26:44 +02:00
|
|
|
owner_default = ACL_ALL_RIGHTS_DATABASE;
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FUNCTION:
|
2002-09-25 01:14:25 +02:00
|
|
|
/* Grant EXECUTE by default, for now */
|
|
|
|
world_default = ACL_EXECUTE;
|
2002-04-21 02:26:44 +02:00
|
|
|
owner_default = ACL_ALL_RIGHTS_FUNCTION;
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_LANGUAGE:
|
2002-09-25 01:14:25 +02:00
|
|
|
/* Grant USAGE by default, for now */
|
|
|
|
world_default = ACL_USAGE;
|
2002-04-21 02:26:44 +02:00
|
|
|
owner_default = ACL_ALL_RIGHTS_LANGUAGE;
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_LARGEOBJECT:
|
2009-12-11 04:34:57 +01:00
|
|
|
world_default = ACL_NO_RIGHTS;
|
|
|
|
owner_default = ACL_ALL_RIGHTS_LARGEOBJECT;
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SCHEMA:
|
2002-04-21 02:26:44 +02:00
|
|
|
world_default = ACL_NO_RIGHTS;
|
2017-10-12 00:35:19 +02:00
|
|
|
owner_default = ACL_ALL_RIGHTS_SCHEMA;
|
2002-04-21 02:26:44 +02:00
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TABLESPACE:
|
2004-06-18 08:14:31 +02:00
|
|
|
world_default = ACL_NO_RIGHTS;
|
|
|
|
owner_default = ACL_ALL_RIGHTS_TABLESPACE;
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FDW:
|
2008-12-19 17:25:19 +01:00
|
|
|
world_default = ACL_NO_RIGHTS;
|
|
|
|
owner_default = ACL_ALL_RIGHTS_FDW;
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FOREIGN_SERVER:
|
2008-12-19 17:25:19 +01:00
|
|
|
world_default = ACL_NO_RIGHTS;
|
|
|
|
owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_DOMAIN:
|
|
|
|
case OBJECT_TYPE:
|
2011-12-19 23:05:19 +01:00
|
|
|
world_default = ACL_USAGE;
|
|
|
|
owner_default = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
break;
|
2002-04-21 02:26:44 +02:00
|
|
|
default:
|
2003-07-27 06:53:12 +02:00
|
|
|
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 */
|
2002-04-21 02:26:44 +02:00
|
|
|
owner_default = ACL_NO_RIGHTS;
|
|
|
|
break;
|
|
|
|
}
|
2000-10-02 06:49:28 +02:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
nacl = 0;
|
|
|
|
if (world_default != ACL_NO_RIGHTS)
|
|
|
|
nacl++;
|
|
|
|
if (owner_default != ACL_NO_RIGHTS)
|
|
|
|
nacl++;
|
|
|
|
|
|
|
|
acl = allocacl(nacl);
|
1997-09-07 07:04:48 +02:00
|
|
|
aip = ACL_DAT(acl);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2003-01-24 00:39:07 +01:00
|
|
|
if (world_default != ACL_NO_RIGHTS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
aip->ai_grantee = ACL_ID_PUBLIC;
|
|
|
|
aip->ai_grantor = ownerId;
|
|
|
|
ACLITEM_SET_PRIVS_GOPTIONS(*aip, world_default, ACL_NO_RIGHTS);
|
2003-10-29 23:20:54 +01:00
|
|
|
aip++;
|
2003-01-24 00:39:07 +01:00
|
|
|
}
|
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/*
|
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
|
2012-04-24 04:43:09 +02:00
|
|
|
* wherever we are testing grant options.
|
2004-06-01 23:49:23 +02:00
|
|
|
*/
|
2009-01-22 21:16:10 +01:00
|
|
|
if (owner_default != ACL_NO_RIGHTS)
|
|
|
|
{
|
|
|
|
aip->ai_grantee = ownerId;
|
|
|
|
aip->ai_grantor = ownerId;
|
|
|
|
ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS);
|
|
|
|
}
|
2002-04-21 02:26:44 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return acl;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-27 20:58:51 +01:00
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* SQL-accessible version of acldefault(). Hackish mapping from "char" type to
|
2018-09-24 16:14:57 +02:00
|
|
|
* OBJECT_* values.
|
2012-01-27 20:58:51 +01:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
acldefault_sql(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
char objtypec = PG_GETARG_CHAR(0);
|
|
|
|
Oid owner = PG_GETARG_OID(1);
|
2017-10-12 00:35:19 +02:00
|
|
|
ObjectType objtype = 0;
|
2012-01-27 20:58:51 +01:00
|
|
|
|
|
|
|
switch (objtypec)
|
|
|
|
{
|
|
|
|
case 'c':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_COLUMN;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
case 'r':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_TABLE;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
case 's':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_SEQUENCE;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
case 'd':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_DATABASE;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
case 'f':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_FUNCTION;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
case 'l':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_LANGUAGE;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
case 'L':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_LARGEOBJECT;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
case 'n':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_SCHEMA;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
case 't':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_TABLESPACE;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
case 'F':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_FDW;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
case 'S':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_FOREIGN_SERVER;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
case 'T':
|
2017-10-12 00:35:19 +02:00
|
|
|
objtype = OBJECT_TYPE;
|
2012-01-27 20:58:51 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
|
|
|
|
}
|
|
|
|
|
|
|
|
PG_RETURN_ACL_P(acldefault(objtype, owner));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
/*
|
2004-06-01 23:49:23 +02:00
|
|
|
* 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
|
2005-06-28 07:09:14 +02:00
|
|
|
* ownerId: Oid of object owner
|
2004-06-01 23:49:23 +02:00
|
|
|
* 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.
|
2000-08-01 00:39:17 +02:00
|
|
|
*
|
|
|
|
* NB: caller is responsible for having detoasted the input ACL, if needed.
|
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
Acl *
|
2004-06-01 23:49:23 +02:00
|
|
|
aclupdate(const Acl *old_acl, const AclItem *mod_aip,
|
2005-06-28 07:09:14 +02:00
|
|
|
int modechg, Oid ownerId, DropBehavior behavior)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2003-01-24 22:53:29 +01:00
|
|
|
Acl *new_acl = NULL;
|
1997-09-08 04:41:22 +02:00
|
|
|
AclItem *old_aip,
|
2003-01-24 22:53:29 +01:00
|
|
|
*new_aip = NULL;
|
2004-06-01 23:49:23 +02:00
|
|
|
AclMode old_rights,
|
2003-10-29 23:20:54 +01:00
|
|
|
old_goptions,
|
2004-06-01 23:49:23 +02:00
|
|
|
new_rights,
|
2003-10-29 23:20:54 +01:00
|
|
|
new_goptions;
|
2001-06-05 21:34:56 +02:00
|
|
|
int dst,
|
1997-09-08 04:41:22 +02:00
|
|
|
num;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-11-18 03:38:24 +01:00
|
|
|
/* Caller probably already checked old_acl, but be safe */
|
|
|
|
check_acl(old_acl);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/* If granting grant options, check for circularity */
|
|
|
|
if (modechg != ACL_MODECHG_DEL &&
|
|
|
|
ACLITEM_GET_GOPTIONS(*mod_aip) != ACL_NO_RIGHTS)
|
2005-06-28 07:09:14 +02:00
|
|
|
check_circularity(old_acl, mod_aip, ownerId);
|
2004-06-01 23:49:23 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
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.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-11-03 20:01:36 +01:00
|
|
|
|
2003-01-24 00:39:07 +01:00
|
|
|
for (dst = 0; dst < num; ++dst)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2003-06-27 02:33:26 +02:00
|
|
|
if (aclitem_match(mod_aip, old_aip + dst))
|
2003-01-24 00:39:07 +01:00
|
|
|
{
|
|
|
|
/* found a match, so modify existing item */
|
2003-06-11 11:23:55 +02:00
|
|
|
new_acl = allocacl(num);
|
2003-01-24 00:39:07 +01:00
|
|
|
new_aip = ACL_DAT(new_acl);
|
|
|
|
memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
|
|
|
|
break;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2003-01-24 00:39:07 +01:00
|
|
|
|
|
|
|
if (dst == num)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2003-01-24 00:39:07 +01:00
|
|
|
/* need to append a new item */
|
2003-06-11 11:23:55 +02:00
|
|
|
new_acl = allocacl(num + 1);
|
1997-09-07 07:04:48 +02:00
|
|
|
new_aip = ACL_DAT(new_acl);
|
2003-01-24 00:39:07 +01:00
|
|
|
memcpy(new_aip, old_aip, num * sizeof(AclItem));
|
|
|
|
|
2001-06-05 21:34:56 +02:00
|
|
|
/* initialize the new entry with no permissions */
|
2003-01-24 00:39:07 +01:00
|
|
|
new_aip[dst].ai_grantee = mod_aip->ai_grantee;
|
|
|
|
new_aip[dst].ai_grantor = mod_aip->ai_grantor;
|
2005-06-28 07:09:14 +02:00
|
|
|
ACLITEM_SET_PRIVS_GOPTIONS(new_aip[dst],
|
|
|
|
ACL_NO_RIGHTS, ACL_NO_RIGHTS);
|
1997-09-07 07:04:48 +02:00
|
|
|
num++; /* set num to the size of new_acl */
|
|
|
|
}
|
2000-11-03 20:01:36 +01:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
old_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
|
2003-10-29 23:20:54 +01:00
|
|
|
old_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
|
|
|
|
|
|
|
|
/* apply the specified permissions change */
|
1997-09-07 07:04:48 +02:00
|
|
|
switch (modechg)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
case ACL_MODECHG_ADD:
|
2004-06-01 23:49:23 +02:00
|
|
|
ACLITEM_SET_RIGHTS(new_aip[dst],
|
|
|
|
old_rights | ACLITEM_GET_RIGHTS(*mod_aip));
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case ACL_MODECHG_DEL:
|
2004-06-01 23:49:23 +02:00
|
|
|
ACLITEM_SET_RIGHTS(new_aip[dst],
|
2005-10-15 04:49:52 +02:00
|
|
|
old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip));
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case ACL_MODECHG_EQL:
|
2004-06-01 23:49:23 +02:00
|
|
|
ACLITEM_SET_RIGHTS(new_aip[dst],
|
|
|
|
ACLITEM_GET_RIGHTS(*mod_aip));
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
new_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
|
2003-10-29 23:20:54 +01:00
|
|
|
new_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2003-01-24 00:39:07 +01:00
|
|
|
* If the adjusted entry has no permissions, delete it from the list.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2004-06-01 23:49:23 +02:00
|
|
|
if (new_rights == ACL_NO_RIGHTS)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2003-01-24 00:39:07 +01:00
|
|
|
memmove(new_aip + dst,
|
|
|
|
new_aip + dst + 1,
|
2001-06-05 21:34:56 +02:00
|
|
|
(num - dst - 1) * sizeof(AclItem));
|
2007-02-28 00:48:10 +01:00
|
|
|
/* Adjust array size to be 'num - 1' items */
|
2000-08-01 00:39:17 +02:00
|
|
|
ARR_DIMS(new_acl)[0] = num - 1;
|
2007-02-28 00:48:10 +01:00
|
|
|
SET_VARSIZE(new_acl, ACL_N_SIZE(num - 1));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2003-01-24 00:39:07 +01:00
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* 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.
|
2003-01-24 00:39:07 +01:00
|
|
|
*/
|
2004-06-01 23:49:23 +02:00
|
|
|
if ((old_goptions & ~new_goptions) != 0)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
|
2003-10-29 23:20:54 +01:00
|
|
|
new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee,
|
|
|
|
(old_goptions & ~new_goptions),
|
2005-06-28 07:09:14 +02:00
|
|
|
ownerId, behavior);
|
2004-06-01 23:49:23 +02:00
|
|
|
}
|
2003-01-24 00:39:07 +01:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return new_acl;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2004-08-01 22:30:49 +02:00
|
|
|
/*
|
|
|
|
* Update an ACL array to reflect a change of owner to the parent object
|
|
|
|
*
|
|
|
|
* old_acl: the input ACL array (must not be NULL)
|
2005-06-28 07:09:14 +02:00
|
|
|
* oldOwnerId: Oid of the old object owner
|
|
|
|
* newOwnerId: Oid of the new object owner
|
2004-08-01 22:30:49 +02:00
|
|
|
*
|
|
|
|
* 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 *
|
2005-06-28 07:09:14 +02:00
|
|
|
aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
|
2004-08-01 22:30:49 +02:00
|
|
|
{
|
|
|
|
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;
|
2004-08-01 22:30:49 +02:00
|
|
|
bool newpresent = false;
|
|
|
|
int dst,
|
|
|
|
src,
|
|
|
|
targ,
|
|
|
|
num;
|
|
|
|
|
2005-11-18 03:38:24 +01:00
|
|
|
check_acl(old_acl);
|
|
|
|
|
2004-08-01 22:30:49 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
2004-08-01 22:30:49 +02:00
|
|
|
*/
|
|
|
|
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++)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
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)
|
2004-08-01 22:30:49 +02:00
|
|
|
newpresent = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If the old ACL contained any references to the new owner, then we may
|
2014-05-06 18:12:18 +02:00
|
|
|
* 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.)
|
2004-08-01 22:30:49 +02:00
|
|
|
*
|
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
|
2014-05-06 18:12:18 +02:00
|
|
|
* 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.
|
2004-08-01 22:30:49 +02:00
|
|
|
*/
|
|
|
|
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;
|
2007-02-28 00:48:10 +01:00
|
|
|
SET_VARSIZE(new_acl, ACL_N_SIZE(dst));
|
2004-08-01 22:30:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return new_acl;
|
|
|
|
}
|
|
|
|
|
2003-01-24 00:39:07 +01:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/*
|
|
|
|
* 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,
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid ownerId)
|
2004-06-01 23:49:23 +02:00
|
|
|
{
|
|
|
|
Acl *acl;
|
|
|
|
AclItem *aip;
|
|
|
|
int i,
|
|
|
|
num;
|
|
|
|
AclMode own_privs;
|
|
|
|
|
2005-11-18 03:38:24 +01:00
|
|
|
check_acl(old_acl);
|
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/*
|
2005-06-28 07:09:14 +02:00
|
|
|
* For now, grant options can only be granted to roles, not PUBLIC.
|
|
|
|
* Otherwise we'd have to work a bit harder here.
|
2004-06-01 23:49:23 +02:00
|
|
|
*/
|
2005-06-28 07:09:14 +02:00
|
|
|
Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC);
|
2004-06-01 23:49:23 +02:00
|
|
|
|
|
|
|
/* The owner always has grant options, no need to check */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (mod_aip->ai_grantor == ownerId)
|
2004-06-01 23:49:23 +02:00
|
|
|
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++)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
if (aip[i].ai_grantee == mod_aip->ai_grantee &&
|
2004-06-01 23:49:23 +02:00
|
|
|
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,
|
2005-06-28 07:09:14 +02:00
|
|
|
ownerId, DROP_CASCADE);
|
2004-06-01 23:49:23 +02:00
|
|
|
|
|
|
|
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,
|
2005-06-28 07:09:14 +02:00
|
|
|
ownerId,
|
2005-10-15 04:49:52 +02:00
|
|
|
ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)),
|
2004-06-01 23:49:23 +02:00
|
|
|
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),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
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:35:54 +02:00
|
|
|
errmsg("grant options cannot be granted back to your own grantor")));
|
2004-06-01 23:49:23 +02:00
|
|
|
|
|
|
|
pfree(acl);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-24 00:39:07 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
2004-06-01 23:49:23 +02:00
|
|
|
*
|
|
|
|
* acl: the input ACL list
|
|
|
|
* grantee: the user from whom some grant options have been revoked
|
|
|
|
* revoke_privs: the grant options being revoked
|
2005-06-28 07:09:14 +02:00
|
|
|
* ownerId: Oid of object owner
|
2004-06-01 23:49:23 +02:00
|
|
|
* behavior: RESTRICT or CASCADE behavior for recursive removal
|
|
|
|
*
|
|
|
|
* The input Acl object is pfree'd if replaced.
|
2003-01-24 00:39:07 +01:00
|
|
|
*/
|
|
|
|
static Acl *
|
|
|
|
recursive_revoke(Acl *acl,
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid grantee,
|
2003-01-24 00:39:07 +01:00
|
|
|
AclMode revoke_privs,
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid ownerId,
|
2003-01-24 00:39:07 +01:00
|
|
|
DropBehavior behavior)
|
|
|
|
{
|
2004-06-01 23:49:23 +02:00
|
|
|
AclMode still_has;
|
|
|
|
AclItem *aip;
|
|
|
|
int i,
|
|
|
|
num;
|
|
|
|
|
2005-11-18 03:38:24 +01:00
|
|
|
check_acl(acl);
|
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/* The owner can never truly lose grant options, so short-circuit */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (grantee == ownerId)
|
2004-06-01 23:49:23 +02:00
|
|
|
return acl;
|
|
|
|
|
2012-08-23 23:25:10 +02:00
|
|
|
/* The grantee might still have some grant options via another grantor */
|
2005-06-28 07:09:14 +02:00
|
|
|
still_has = aclmask(acl, grantee, ownerId,
|
2004-06-01 23:49:23 +02:00
|
|
|
ACL_GRANT_OPTION_FOR(revoke_privs),
|
|
|
|
ACLMASK_ALL);
|
2012-08-23 23:25:10 +02:00
|
|
|
revoke_privs &= ~ACL_OPTION_TO_PRIVS(still_has);
|
2004-06-01 23:49:23 +02:00
|
|
|
if (revoke_privs == ACL_NO_RIGHTS)
|
|
|
|
return acl;
|
2003-01-24 00:39:07 +01:00
|
|
|
|
|
|
|
restart:
|
2004-06-01 23:49:23 +02:00
|
|
|
num = ACL_NUM(acl);
|
|
|
|
aip = ACL_DAT(acl);
|
|
|
|
for (i = 0; i < num; i++)
|
2003-01-24 00:39:07 +01:00
|
|
|
{
|
|
|
|
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;
|
2004-06-01 23:49:23 +02:00
|
|
|
Acl *new_acl;
|
2003-01-24 00:39:07 +01:00
|
|
|
|
|
|
|
if (behavior == DROP_RESTRICT)
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
|
|
|
|
errmsg("dependent privileges exist"),
|
|
|
|
errhint("Use CASCADE to revoke them too.")));
|
2003-01-24 00:39:07 +01:00
|
|
|
|
|
|
|
mod_acl.ai_grantor = grantee;
|
|
|
|
mod_acl.ai_grantee = aip[i].ai_grantee;
|
2005-06-28 07:09:14 +02:00
|
|
|
ACLITEM_SET_PRIVS_GOPTIONS(mod_acl,
|
|
|
|
revoke_privs,
|
|
|
|
revoke_privs);
|
2003-01-24 00:39:07 +01:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
new_acl = aclupdate(acl, &mod_acl, ACL_MODECHG_DEL,
|
2005-06-28 07:09:14 +02:00
|
|
|
ownerId, behavior);
|
2004-06-01 23:49:23 +02:00
|
|
|
|
|
|
|
pfree(acl);
|
|
|
|
acl = new_acl;
|
|
|
|
|
2003-01-24 00:39:07 +01:00
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return acl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
2005-06-28 07:09:14 +02:00
|
|
|
* aclmask --- compute bitmask of all privileges held by roleid.
|
2004-06-01 23:49:23 +02:00
|
|
|
*
|
|
|
|
* When 'how' = ACLMASK_ALL, this simply returns the privilege bits
|
2005-06-28 07:09:14 +02:00
|
|
|
* held by the given roleid according to the given ACL list, ANDed
|
2004-06-01 23:49:23 +02:00
|
|
|
* 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:
|
2005-06-28 07:09:14 +02:00
|
|
|
* if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ANY) != 0)
|
2004-06-01 23:49:23 +02:00
|
|
|
*
|
|
|
|
* To see if all of a set of privileges are held:
|
2005-06-28 07:09:14 +02:00
|
|
|
* if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL) == privs)
|
2004-06-01 23:49:23 +02:00
|
|
|
*
|
|
|
|
* To determine exactly which of a set of privileges are held:
|
2005-06-28 07:09:14 +02:00
|
|
|
* heldprivs = aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL);
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2004-06-01 23:49:23 +02:00
|
|
|
AclMode
|
2005-06-28 07:09:14 +02:00
|
|
|
aclmask(const Acl *acl, Oid roleid, Oid ownerId,
|
2004-06-01 23:49:23 +02:00
|
|
|
AclMode mask, AclMaskHow how)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-06-01 23:49:23 +02:00
|
|
|
AclMode result;
|
2005-06-29 22:34:15 +02:00
|
|
|
AclMode remaining;
|
2004-06-01 23:49:23 +02:00
|
|
|
AclItem *aidat;
|
|
|
|
int i,
|
|
|
|
num;
|
2000-08-01 00:39:17 +02:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/*
|
|
|
|
* Null ACL should not happen, since caller should have inserted
|
|
|
|
* appropriate default
|
|
|
|
*/
|
|
|
|
if (acl == NULL)
|
|
|
|
elog(ERROR, "null ACL");
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2005-11-18 03:38:24 +01:00
|
|
|
check_acl(acl);
|
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/* Quick exit for mask == 0 */
|
|
|
|
if (mask == 0)
|
|
|
|
return 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
result = 0;
|
|
|
|
|
|
|
|
/* Owner always implicitly has all grant options */
|
2005-10-07 21:59:34 +02:00
|
|
|
if ((mask & ACLITEM_ALL_GOPTION_BITS) &&
|
|
|
|
has_privs_of_role(roleid, ownerId))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-06-01 23:49:23 +02:00
|
|
|
result = mask & ACLITEM_ALL_GOPTION_BITS;
|
2005-10-07 21:59:34 +02:00
|
|
|
if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
|
2004-06-01 23:49:23 +02:00
|
|
|
return result;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
num = ACL_NUM(acl);
|
|
|
|
aidat = ACL_DAT(acl);
|
|
|
|
|
|
|
|
/*
|
2005-10-07 21:59:34 +02:00
|
|
|
* Check privileges granted directly to roleid or to public
|
2004-06-01 23:49:23 +02:00
|
|
|
*/
|
|
|
|
for (i = 0; i < num; i++)
|
2000-11-03 20:01:36 +01:00
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
AclItem *aidata = &aidat[i];
|
2004-06-01 23:49:23 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
if (aidata->ai_grantee == ACL_ID_PUBLIC ||
|
2005-06-29 22:34:15 +02:00
|
|
|
aidata->ai_grantee == roleid)
|
2004-06-01 23:49:23 +02:00
|
|
|
{
|
2005-07-21 06:41:43 +02:00
|
|
|
result |= aidata->ai_privs & mask;
|
2004-06-01 23:49:23 +02:00
|
|
|
if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
|
|
|
|
return result;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2004-06-01 23:49:23 +02:00
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
/*
|
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.
|
2005-06-29 22:34:15 +02:00
|
|
|
*/
|
2005-07-21 06:41:43 +02:00
|
|
|
remaining = mask & ~result;
|
2005-06-29 22:34:15 +02:00
|
|
|
for (i = 0; i < num; i++)
|
|
|
|
{
|
|
|
|
AclItem *aidata = &aidat[i];
|
2004-06-01 23:49:23 +02:00
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
if (aidata->ai_grantee == ACL_ID_PUBLIC ||
|
|
|
|
aidata->ai_grantee == roleid)
|
|
|
|
continue; /* already checked it */
|
2004-06-01 23:49:23 +02:00
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
if ((aidata->ai_privs & remaining) &&
|
2005-07-26 18:38:29 +02:00
|
|
|
has_privs_of_role(roleid, aidata->ai_grantee))
|
2005-06-29 22:34:15 +02:00
|
|
|
{
|
2005-07-21 06:41:43 +02:00
|
|
|
result |= aidata->ai_privs & mask;
|
2005-06-29 22:34:15 +02:00
|
|
|
if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
|
|
|
|
return result;
|
2005-07-21 06:41:43 +02:00
|
|
|
remaining = mask & ~result;
|
2005-06-29 22:34:15 +02:00
|
|
|
}
|
|
|
|
}
|
2005-06-28 07:09:14 +02:00
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
return result;
|
2004-06-01 23:49:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-10-10 20:49:04 +02:00
|
|
|
/*
|
|
|
|
* 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");
|
|
|
|
|
2005-11-18 03:38:24 +01:00
|
|
|
check_acl(acl);
|
|
|
|
|
2005-10-10 20:49:04 +02:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-07-07 22:40:02 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
2005-07-07 22:40:02 +02:00
|
|
|
const AclItem *acldat;
|
2005-10-15 04:49:52 +02:00
|
|
|
int i,
|
2019-11-07 04:51:04 +01:00
|
|
|
j;
|
2005-07-07 22:40:02 +02:00
|
|
|
|
|
|
|
if (acl == NULL || ACL_NUM(acl) == 0)
|
|
|
|
{
|
|
|
|
*roleids = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-18 03:38:24 +01:00
|
|
|
check_acl(acl);
|
|
|
|
|
2005-07-07 22:40:02 +02:00
|
|
|
/* 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);
|
2005-07-07 22:40:02 +02:00
|
|
|
|
|
|
|
/*
|
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.
|
2005-07-07 22:40:02 +02:00
|
|
|
*/
|
|
|
|
*roleids = list;
|
|
|
|
|
2019-11-07 04:51:04 +01:00
|
|
|
/* Remove duplicates from the array */
|
|
|
|
return qunique(list, j, sizeof(Oid), oid_cmp);
|
2005-07-07 22:40:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/*
|
|
|
|
* 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")));
|
2000-11-03 20:01:36 +01:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
PG_RETURN_NULL(); /* keep compiler quiet */
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
Datum
|
|
|
|
aclcontains(PG_FUNCTION_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-08-01 00:39:17 +02:00
|
|
|
Acl *acl = PG_GETARG_ACL_P(0);
|
2001-03-22 05:01:46 +01:00
|
|
|
AclItem *aip = PG_GETARG_ACLITEM_P(1);
|
1997-09-08 04:41:22 +02:00
|
|
|
AclItem *aidat;
|
2000-08-01 00:39:17 +02:00
|
|
|
int i,
|
|
|
|
num;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-11-18 03:38:24 +01:00
|
|
|
check_acl(acl);
|
2000-08-01 00:39:17 +02:00
|
|
|
num = ACL_NUM(acl);
|
1997-09-07 07:04:48 +02:00
|
|
|
aidat = ACL_DAT(acl);
|
|
|
|
for (i = 0; i < num; ++i)
|
2001-01-14 20:23:27 +01:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
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))
|
2000-08-01 00:39:17 +02:00
|
|
|
PG_RETURN_BOOL(true);
|
2001-01-14 20:23:27 +01:00
|
|
|
}
|
2000-08-01 00:39:17 +02:00
|
|
|
PG_RETURN_BOOL(false);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2003-06-11 11:23:55 +02:00
|
|
|
Datum
|
|
|
|
makeaclitem(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid grantee = PG_GETARG_OID(0);
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid grantor = PG_GETARG_OID(1);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *privtext = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
bool goption = PG_GETARG_BOOL(3);
|
2005-06-29 22:34:15 +02:00
|
|
|
AclItem *result;
|
2003-06-11 11:23:55 +02:00
|
|
|
AclMode priv;
|
|
|
|
|
|
|
|
priv = convert_priv_string(privtext);
|
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
result = (AclItem *) palloc(sizeof(AclItem));
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
result->ai_grantee = grantee;
|
|
|
|
result->ai_grantor = grantor;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
ACLITEM_SET_PRIVS_GOPTIONS(*result, priv,
|
2005-06-28 07:09:14 +02:00
|
|
|
(goption ? priv : ACL_NO_RIGHTS));
|
2003-06-11 11:23:55 +02:00
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
PG_RETURN_ACLITEM_P(result);
|
2003-06-11 11:23:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static AclMode
|
|
|
|
convert_priv_string(text *priv_type_text)
|
|
|
|
{
|
2008-03-25 23:42:46 +01:00
|
|
|
char *priv_type = text_to_cstring(priv_type_text);
|
2003-06-11 11:23:55 +02:00
|
|
|
|
2004-05-07 02:24:59 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "SELECT") == 0)
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_SELECT;
|
2004-05-07 02:24:59 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "INSERT") == 0)
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_INSERT;
|
2004-05-07 02:24:59 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "UPDATE") == 0)
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_UPDATE;
|
2004-05-07 02:24:59 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "DELETE") == 0)
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_DELETE;
|
2008-09-08 02:47:41 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "TRUNCATE") == 0)
|
|
|
|
return ACL_TRUNCATE;
|
2004-05-07 02:24:59 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "REFERENCES") == 0)
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_REFERENCES;
|
2004-05-07 02:24:59 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "TRIGGER") == 0)
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_TRIGGER;
|
2004-05-07 02:24:59 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "EXECUTE") == 0)
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_EXECUTE;
|
2004-05-07 02:24:59 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "USAGE") == 0)
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_USAGE;
|
2004-05-07 02:24:59 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "CREATE") == 0)
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_CREATE;
|
2004-05-07 02:24:59 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "TEMP") == 0)
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_CREATE_TEMP;
|
2004-05-07 02:24:59 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "TEMPORARY") == 0)
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_CREATE_TEMP;
|
2006-04-30 23:15:33 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "CONNECT") == 0)
|
|
|
|
return ACL_CONNECT;
|
2006-09-05 23:08:36 +02:00
|
|
|
if (pg_strcasecmp(priv_type, "RULE") == 0)
|
|
|
|
return 0; /* ignore old RULE privileges */
|
2003-06-11 11:23:55 +02:00
|
|
|
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("unrecognized privilege type: \"%s\"", priv_type)));
|
2003-06-11 11:23:55 +02:00
|
|
|
return ACL_NO_RIGHTS; /* keep compiler quiet */
|
|
|
|
}
|
|
|
|
|
2000-10-07 02:58:23 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-05 22:43:36 +01:00
|
|
|
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 }}
|
2009-12-05 22:43:36 +01:00
|
|
|
*----------
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
aclexplode(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2010-01-12 03:39:22 +01:00
|
|
|
Acl *acl = PG_GETARG_ACL_P(0);
|
2010-02-26 03:01:40 +01:00
|
|
|
FuncCallContext *funcctx;
|
2009-12-05 22:43:36 +01:00
|
|
|
int *idx;
|
2010-02-26 03:01:40 +01:00
|
|
|
AclItem *aidat;
|
2009-12-05 22:43:36 +01:00
|
|
|
|
|
|
|
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)
|
2009-12-05 22:43:36 +01:00
|
|
|
*/
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
tupdesc = CreateTemplateTupleDesc(4);
|
2009-12-05 22:43:36 +01:00
|
|
|
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);
|
2010-01-12 03:39:22 +01:00
|
|
|
|
|
|
|
/* need test here in case acl has no items */
|
|
|
|
while (idx[0] < ACL_NUM(acl))
|
2009-12-05 22:43:36 +01:00
|
|
|
{
|
2010-01-12 03:39:22 +01:00
|
|
|
AclItem *aidata;
|
|
|
|
AclMode priv_bit;
|
|
|
|
|
2009-12-05 22:43:36 +01:00
|
|
|
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 */
|
2009-12-05 22:43:36 +01:00
|
|
|
break;
|
|
|
|
}
|
2010-01-12 03:39:22 +01:00
|
|
|
aidata = &aidat[idx[0]];
|
|
|
|
priv_bit = 1 << idx[1];
|
2009-12-05 22:43:36 +01:00
|
|
|
|
2010-01-12 03:39:22 +01:00
|
|
|
if (ACLITEM_GET_PRIVS(*aidata) & priv_bit)
|
2009-12-05 22:43:36 +01:00
|
|
|
{
|
|
|
|
Datum result;
|
|
|
|
Datum values[4];
|
|
|
|
bool nulls[4];
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
2010-01-12 03:39:22 +01:00
|
|
|
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);
|
2009-12-05 22:43:36 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-06-14 03:09:22 +02:00
|
|
|
/*
|
2002-08-09 18:45:16 +02:00
|
|
|
* has_table_privilege variants
|
|
|
|
* These are all named "has_table_privilege" at the SQL level.
|
|
|
|
* They take various combinations of relation name, relation OID,
|
2005-06-28 07:09:14 +02:00
|
|
|
* user name, user OID, or implicit user = current_user.
|
2001-06-14 03:09:22 +02:00
|
|
|
*
|
2002-08-09 18:45:16 +02:00
|
|
|
* The result is a boolean value: true if user has the indicated
|
2008-12-15 19:09:41 +01:00
|
|
|
* 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).
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_name_name
|
|
|
|
* Check user privileges on a table given
|
|
|
|
* name username, text tablename, and text priv name.
|
2001-06-14 03:09:22 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_name_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Name rolename = PG_GETARG_NAME(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid tableoid;
|
2002-04-26 03:24:08 +02:00
|
|
|
AclMode mode;
|
2002-04-27 05:45:03 +02:00
|
|
|
AclResult aclresult;
|
2002-04-26 03:24:08 +02:00
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*rolename));
|
2002-08-09 18:45:16 +02:00
|
|
|
tableoid = convert_table_name(tablename);
|
|
|
|
mode = convert_table_priv_string(priv_type_text);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
|
2002-04-26 03:24:08 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2001-06-14 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_name
|
2002-08-09 18:45:16 +02:00
|
|
|
* Check user privileges on a table given
|
|
|
|
* text tablename and text priv name.
|
2001-06-14 03:09:22 +02:00
|
|
|
* current_user is assumed
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid tableoid;
|
2002-04-26 03:24:08 +02:00
|
|
|
AclMode mode;
|
2002-04-27 05:45:03 +02:00
|
|
|
AclResult aclresult;
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2002-08-09 18:45:16 +02:00
|
|
|
tableoid = convert_table_name(tablename);
|
|
|
|
mode = convert_table_priv_string(priv_type_text);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2002-04-26 03:24:08 +02:00
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2001-06-14 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_name_id
|
2002-08-09 18:45:16 +02:00
|
|
|
* Check user privileges on a table given
|
|
|
|
* name usename, table oid, and text priv name.
|
2001-06-14 03:09:22 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_name_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Name username = PG_GETARG_NAME(0);
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid tableoid = PG_GETARG_OID(1);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-04-26 03:24:08 +02:00
|
|
|
AclMode mode;
|
2002-04-27 05:45:03 +02:00
|
|
|
AclResult aclresult;
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2002-08-09 18:45:16 +02:00
|
|
|
mode = convert_table_priv_string(priv_type_text);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
|
2002-04-26 03:24:08 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2001-06-14 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_id
|
2002-08-09 18:45:16 +02:00
|
|
|
* Check user privileges on a table given
|
|
|
|
* table oid, and text priv name.
|
2001-06-14 03:09:22 +02:00
|
|
|
* current_user is assumed
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid tableoid = PG_GETARG_OID(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid roleid;
|
2001-06-14 03:09:22 +02:00
|
|
|
AclMode mode;
|
2002-04-27 05:45:03 +02:00
|
|
|
AclResult aclresult;
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2002-08-09 18:45:16 +02:00
|
|
|
mode = convert_table_priv_string(priv_type_text);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2002-04-26 03:24:08 +02:00
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2001-06-14 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_id_name
|
2002-08-09 18:45:16 +02:00
|
|
|
* Check user privileges on a table given
|
2005-06-28 07:09:14 +02:00
|
|
|
* roleid, text tablename, and text priv name.
|
2001-06-14 03:09:22 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_id_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid tableoid;
|
2002-04-26 03:24:08 +02:00
|
|
|
AclMode mode;
|
2002-04-27 05:45:03 +02:00
|
|
|
AclResult aclresult;
|
2002-04-26 03:24:08 +02:00
|
|
|
|
2002-08-09 18:45:16 +02:00
|
|
|
tableoid = convert_table_name(tablename);
|
|
|
|
mode = convert_table_priv_string(priv_type_text);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2002-04-26 03:24:08 +02:00
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2001-06-14 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_id_id
|
2002-08-09 18:45:16 +02:00
|
|
|
* Check user privileges on a table given
|
2005-06-28 07:09:14 +02:00
|
|
|
* roleid, table oid, and text priv name.
|
2001-06-14 03:09:22 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_id_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid tableoid = PG_GETARG_OID(1);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2001-06-14 03:09:22 +02:00
|
|
|
AclMode mode;
|
2002-04-27 05:45:03 +02:00
|
|
|
AclResult aclresult;
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2002-08-09 18:45:16 +02:00
|
|
|
mode = convert_table_priv_string(priv_type_text);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
2002-04-26 03:24:08 +02:00
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2001-06-14 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2002-08-09 18:45:16 +02:00
|
|
|
* Support routines for has_table_privilege family.
|
2001-06-14 03:09:22 +02:00
|
|
|
*/
|
|
|
|
|
2002-04-26 03:24:08 +02:00
|
|
|
/*
|
2002-08-09 18:45:16 +02:00
|
|
|
* Given a table name expressed as a string, look it up and return Oid
|
2002-04-26 03:24:08 +02:00
|
|
|
*/
|
|
|
|
static Oid
|
2002-08-09 18:45:16 +02:00
|
|
|
convert_table_name(text *tablename)
|
2002-04-26 03:24:08 +02:00
|
|
|
{
|
|
|
|
RangeVar *relrv;
|
|
|
|
|
2005-05-27 02:57:49 +02:00
|
|
|
relrv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
|
2002-04-26 03:24:08 +02:00
|
|
|
|
2011-07-09 04:19:30 +02:00
|
|
|
/* 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);
|
2002-04-26 03:24:08 +02:00
|
|
|
}
|
|
|
|
|
2001-06-14 03:09:22 +02:00
|
|
|
/*
|
2002-08-09 18:45:16 +02:00
|
|
|
* convert_table_priv_string
|
|
|
|
* Convert text string to AclMode value.
|
2001-06-14 03:09:22 +02:00
|
|
|
*/
|
|
|
|
static AclMode
|
2002-08-09 18:45:16 +02:00
|
|
|
convert_table_priv_string(text *priv_type_text)
|
2001-06-14 03:09:22 +02:00
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
static const priv_map table_priv_map[] = {
|
2009-06-11 16:49:15 +02:00
|
|
|
{"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}
|
2009-02-06 22:15:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
return convert_any_priv_string(priv_type_text, table_priv_map);
|
|
|
|
}
|
|
|
|
|
2009-08-03 23:11:40 +02:00
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *sequencename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-08-03 23:11:40 +02:00
|
|
|
Oid roleid;
|
|
|
|
Oid sequenceoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*rolename));
|
2009-08-03 23:11:40 +02:00
|
|
|
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))));
|
2009-08-03 23:11:40 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *sequencename = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2009-08-03 23:11:40 +02:00
|
|
|
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))));
|
2009-08-03 23:11:40 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-08-03 23:11:40 +02:00
|
|
|
Oid roleid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
char relkind;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2009-08-03 23:11:40 +02:00
|
|
|
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))));
|
2009-08-03 23:11:40 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2009-08-03 23:11:40 +02:00
|
|
|
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))));
|
2009-08-03 23:11:40 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *sequencename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-08-03 23:11:40 +02:00
|
|
|
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))));
|
2009-08-03 23:11:40 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-08-03 23:11:40 +02:00
|
|
|
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))));
|
2009-08-03 23:11:40 +02:00
|
|
|
|
|
|
|
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},
|
2017-11-26 18:49:40 +01:00
|
|
|
{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
|
2010-02-26 03:01:40 +01:00
|
|
|
{"SELECT", ACL_SELECT},
|
2017-11-26 18:49:40 +01:00
|
|
|
{"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
|
2010-02-26 03:01:40 +01:00
|
|
|
{"UPDATE", ACL_UPDATE},
|
2017-11-26 18:49:40 +01:00
|
|
|
{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
|
2010-02-26 03:01:40 +01:00
|
|
|
{NULL, 0}
|
2009-08-03 23:11:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
return convert_any_priv_string(priv_type_text, sequence_priv_map);
|
|
|
|
}
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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,
|
2005-06-28 07:09:14 +02:00
|
|
|
* user name, user OID, or implicit user = current_user.
|
2002-08-09 18:45:16 +02:00
|
|
|
*
|
|
|
|
* The result is a boolean value: true if user has the indicated
|
2009-02-06 22:15:12 +01:00
|
|
|
* 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.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* has_any_column_privilege_name_name
|
|
|
|
* Check user privileges on any column of a table given
|
|
|
|
* name username, text tablename, and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
Datum
|
2009-02-06 22:15:12 +01:00
|
|
|
has_any_column_privilege_name_name(PG_FUNCTION_ARGS)
|
2002-08-09 18:45:16 +02:00
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
Name rolename = PG_GETARG_NAME(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid tableoid;
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*rolename));
|
2009-02-06 22:15:12 +01:00
|
|
|
tableoid = convert_table_name(tablename);
|
|
|
|
mode = convert_column_priv_string(priv_type_text);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
/* 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);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* has_any_column_privilege_name
|
|
|
|
* Check user privileges on any column of a table given
|
|
|
|
* text tablename and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
* current_user is assumed
|
|
|
|
*/
|
|
|
|
Datum
|
2009-02-06 22:15:12 +01:00
|
|
|
has_any_column_privilege_name(PG_FUNCTION_ARGS)
|
2002-08-09 18:45:16 +02:00
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid tableoid;
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2009-02-06 22:15:12 +01:00
|
|
|
tableoid = convert_table_name(tablename);
|
|
|
|
mode = convert_column_priv_string(priv_type_text);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
/* 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);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* has_any_column_privilege_name_id
|
|
|
|
* Check user privileges on any column of a table given
|
|
|
|
* name usename, table oid, and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
Datum
|
2009-02-06 22:15:12 +01:00
|
|
|
has_any_column_privilege_name_id(PG_FUNCTION_ARGS)
|
2002-08-09 18:45:16 +02:00
|
|
|
{
|
|
|
|
Name username = PG_GETARG_NAME(0);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid tableoid = PG_GETARG_OID(1);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2009-02-06 22:15:12 +01:00
|
|
|
mode = convert_column_priv_string(priv_type_text);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
/* 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);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* has_any_column_privilege_id
|
|
|
|
* Check user privileges on any column of a table given
|
|
|
|
* table oid, and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
* current_user is assumed
|
|
|
|
*/
|
|
|
|
Datum
|
2009-02-06 22:15:12 +01:00
|
|
|
has_any_column_privilege_id(PG_FUNCTION_ARGS)
|
2002-08-09 18:45:16 +02:00
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid tableoid = PG_GETARG_OID(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2009-02-06 22:15:12 +01:00
|
|
|
mode = convert_column_priv_string(priv_type_text);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
/* 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);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* has_any_column_privilege_id_name
|
|
|
|
* Check user privileges on any column of a table given
|
|
|
|
* roleid, text tablename, and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
Datum
|
2009-02-06 22:15:12 +01:00
|
|
|
has_any_column_privilege_id_name(PG_FUNCTION_ARGS)
|
2002-08-09 18:45:16 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid tableoid;
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
tableoid = convert_table_name(tablename);
|
|
|
|
mode = convert_column_priv_string(priv_type_text);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
/* 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);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* has_any_column_privilege_id_id
|
|
|
|
* Check user privileges on any column of a table given
|
|
|
|
* roleid, table oid, and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
Datum
|
2009-02-06 22:15:12 +01:00
|
|
|
has_any_column_privilege_id_id(PG_FUNCTION_ARGS)
|
2002-08-09 18:45:16 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid tableoid = PG_GETARG_OID(1);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
mode = convert_column_priv_string(priv_type_text);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
/* 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);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* 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
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
* return NULL (rather than throwing an error) if that relation OID
|
|
|
|
* doesn't exist. Likewise, the variants that take an integer attnum
|
|
|
|
* return NULL (rather than throwing an error) if there is no such
|
|
|
|
* pg_attribute entry. All variants return NULL if an attisdropped
|
|
|
|
* column is selected. These rules are meant to avoid unnecessary
|
|
|
|
* failures in queries that scan pg_attribute.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* 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.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
2009-02-06 22:15:12 +01:00
|
|
|
static int
|
|
|
|
column_privilege_check(Oid tableoid, AttrNumber attnum,
|
|
|
|
Oid roleid, AclMode mode)
|
2002-08-09 18:45:16 +02:00
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
AclResult aclresult;
|
|
|
|
HeapTuple attTuple;
|
|
|
|
Form_pg_attribute attributeForm;
|
2001-10-25 07:50:21 +02:00
|
|
|
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
/*
|
|
|
|
* If convert_column_name failed, we can just return -1 immediately.
|
|
|
|
*/
|
|
|
|
if (attnum == InvalidAttrNumber)
|
|
|
|
return -1;
|
|
|
|
|
2001-06-14 03:09:22 +02:00
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* 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
|
2014-05-06 18:12:18 +02:00
|
|
|
* here and there. So if we see the row in the syscache, so will
|
2009-02-06 22:15:12 +01:00
|
|
|
* pg_class_aclcheck.
|
2001-06-14 03:09:22 +02:00
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
|
2009-02-06 22:15:12 +01:00
|
|
|
return -1;
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
if (aclresult == ACLCHECK_OK)
|
|
|
|
return true;
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
attTuple = SearchSysCache2(ATTNUM,
|
|
|
|
ObjectIdGetDatum(tableoid),
|
|
|
|
Int16GetDatum(attnum));
|
2009-02-06 22:15:12 +01:00
|
|
|
if (!HeapTupleIsValid(attTuple))
|
|
|
|
return -1;
|
|
|
|
attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
|
|
|
|
if (attributeForm->attisdropped)
|
|
|
|
{
|
|
|
|
ReleaseSysCache(attTuple);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ReleaseSysCache(attTuple);
|
2006-04-30 23:15:33 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
aclresult = pg_attribute_aclcheck(tableoid, attnum, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
return (aclresult == ACLCHECK_OK);
|
|
|
|
}
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* has_column_privilege_name_name_name
|
|
|
|
* Check user privileges on a column given
|
|
|
|
* name username, text tablename, text colname, and text priv name.
|
2008-12-19 17:25:19 +01:00
|
|
|
*/
|
2009-02-06 22:15:12 +01:00
|
|
|
Datum
|
|
|
|
has_column_privilege_name_name_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Name rolename = PG_GETARG_NAME(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *column = PG_GETARG_TEXT_PP(2);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(3);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
Oid tableoid;
|
|
|
|
AttrNumber colattnum;
|
|
|
|
AclMode mode;
|
|
|
|
int privresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*rolename));
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
|
|
|
}
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* has_column_privilege_name_name_attnum
|
|
|
|
* Check user privileges on a column given
|
|
|
|
* name username, text tablename, int attnum, and text priv name.
|
2008-12-19 17:25:19 +01:00
|
|
|
*/
|
2009-02-06 22:15:12 +01:00
|
|
|
Datum
|
|
|
|
has_column_privilege_name_name_attnum(PG_FUNCTION_ARGS)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
Name rolename = PG_GETARG_NAME(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(1);
|
2009-02-06 22:15:12 +01:00
|
|
|
AttrNumber colattnum = PG_GETARG_INT16(2);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(3);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
Oid tableoid;
|
|
|
|
AclMode mode;
|
|
|
|
int privresult;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*rolename));
|
2009-02-06 22:15:12 +01:00
|
|
|
tableoid = convert_table_name(tablename);
|
|
|
|
mode = convert_column_priv_string(priv_type_text);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
privresult = column_privilege_check(tableoid, colattnum, roleid, mode);
|
|
|
|
if (privresult < 0)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_BOOL(privresult);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* has_column_privilege_name_id_name
|
|
|
|
* Check user privileges on a column given
|
|
|
|
* name username, table oid, text colname, and text priv name.
|
2008-12-19 17:25:19 +01:00
|
|
|
*/
|
|
|
|
Datum
|
2009-02-06 22:15:12 +01:00
|
|
|
has_column_privilege_name_id_name(PG_FUNCTION_ARGS)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
|
|
|
Name username = PG_GETARG_NAME(0);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid tableoid = PG_GETARG_OID(1);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *column = PG_GETARG_TEXT_PP(2);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(3);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
AttrNumber colattnum;
|
|
|
|
AclMode mode;
|
|
|
|
int privresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2009-02-06 22:15:12 +01:00
|
|
|
colattnum = convert_column_name(tableoid, column);
|
|
|
|
mode = convert_column_priv_string(priv_type_text);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(3);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
AclMode mode;
|
|
|
|
int privresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *column = PG_GETARG_TEXT_PP(2);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(3);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(1);
|
2009-02-06 22:15:12 +01:00
|
|
|
AttrNumber colattnum = PG_GETARG_INT16(2);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(3);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *column = PG_GETARG_TEXT_PP(2);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(3);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(3);
|
2009-02-06 22:15:12 +01:00
|
|
|
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)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *column = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
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)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablename = PG_GETARG_TEXT_PP(0);
|
2009-02-06 22:15:12 +01:00
|
|
|
AttrNumber colattnum = PG_GETARG_INT16(1);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *column = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
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
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
* and return the column number. Returns InvalidAttrNumber in cases
|
|
|
|
* where caller should return NULL instead of failing.
|
2009-02-06 22:15:12 +01:00
|
|
|
*/
|
|
|
|
static AttrNumber
|
|
|
|
convert_column_name(Oid tableoid, text *column)
|
|
|
|
{
|
|
|
|
char *colname;
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
HeapTuple attTuple;
|
|
|
|
AttrNumber attnum;
|
2009-02-06 22:15:12 +01:00
|
|
|
|
|
|
|
colname = text_to_cstring(column);
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't use get_attnum() here because it will report that dropped
|
|
|
|
* columns don't exist. We need to treat dropped columns differently from
|
|
|
|
* nonexistent columns.
|
|
|
|
*/
|
|
|
|
attTuple = SearchSysCache2(ATTNAME,
|
|
|
|
ObjectIdGetDatum(tableoid),
|
|
|
|
CStringGetDatum(colname));
|
|
|
|
if (HeapTupleIsValid(attTuple))
|
|
|
|
{
|
|
|
|
Form_pg_attribute attributeForm;
|
|
|
|
|
|
|
|
attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
|
|
|
|
/* We want to return NULL for dropped columns */
|
|
|
|
if (attributeForm->attisdropped)
|
|
|
|
attnum = InvalidAttrNumber;
|
|
|
|
else
|
|
|
|
attnum = attributeForm->attnum;
|
|
|
|
ReleaseSysCache(attTuple);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *tablename = get_rel_name(tableoid);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the table OID is bogus, or it's just been dropped, we'll get
|
|
|
|
* NULL back. In such cases we want has_column_privilege to return
|
|
|
|
* NULL too, so just return InvalidAttrNumber.
|
|
|
|
*/
|
|
|
|
if (tablename != NULL)
|
|
|
|
{
|
|
|
|
/* tableoid exists, colname does not, so throw error */
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
|
|
|
errmsg("column \"%s\" of relation \"%s\" does not exist",
|
|
|
|
colname, tablename)));
|
|
|
|
}
|
|
|
|
/* tableoid doesn't exist, so act like attisdropped case */
|
|
|
|
attnum = InvalidAttrNumber;
|
|
|
|
}
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
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[] = {
|
2009-06-11 16:49:15 +02:00
|
|
|
{"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}
|
2009-02-06 22:15:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *databasename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
Oid databaseoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2009-02-06 22:15:12 +01:00
|
|
|
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)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *databasename = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2009-02-06 22:15:12 +01:00
|
|
|
mode = convert_database_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
roleid = GetUserId();
|
|
|
|
mode = convert_database_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *databasename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
mode = convert_database_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
return get_database_oid(dbname, false);
|
2009-02-06 22:15:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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[] = {
|
2009-06-11 16:49:15 +02:00
|
|
|
{"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}
|
2009-02-06 22:15:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *fdwname = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
Oid fdwid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *fdwname = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2009-02-06 22:15:12 +01:00
|
|
|
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid)))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
roleid = GetUserId();
|
|
|
|
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
|
|
|
|
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid)))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *fdwname = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
|
|
|
|
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid)))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
|
|
|
|
2011-04-01 17:28:28 +02:00
|
|
|
return get_foreign_data_wrapper_oid(fdwstr, false);
|
2009-02-06 22:15:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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[] = {
|
2009-06-11 16:49:15 +02:00
|
|
|
{"USAGE", ACL_USAGE},
|
|
|
|
{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
|
|
|
|
{NULL, 0}
|
2009-02-06 22:15:12 +01:00
|
|
|
};
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
return convert_any_priv_string(priv_type_text, foreign_data_wrapper_priv_map);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-08-09 18:45:16 +02:00
|
|
|
/*
|
|
|
|
* has_function_privilege variants
|
|
|
|
* These are all named "has_function_privilege" at the SQL level.
|
|
|
|
* They take various combinations of function name, function OID,
|
2005-06-28 07:09:14 +02:00
|
|
|
* user name, user OID, or implicit user = current_user.
|
2002-08-09 18:45:16 +02:00
|
|
|
*
|
|
|
|
* The result is a boolean value: true if user has the indicated
|
2008-12-15 19:09:41 +01:00
|
|
|
* privilege, false if not, or NULL if object doesn't exist.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *functionname = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid functionoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2002-08-09 18:45:16 +02:00
|
|
|
functionoid = convert_function_name(functionname);
|
|
|
|
mode = convert_function_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *functionname = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid functionoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2002-08-09 18:45:16 +02:00
|
|
|
functionoid = convert_function_name(functionname);
|
|
|
|
mode = convert_function_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2002-08-09 18:45:16 +02:00
|
|
|
mode = convert_function_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2002-08-09 18:45:16 +02:00
|
|
|
mode = convert_function_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_function_privilege_id_name
|
|
|
|
* Check user privileges on a function given
|
2005-06-28 07:09:14 +02:00
|
|
|
* roleid, text functionname, and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_function_privilege_id_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *functionname = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid functionoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
functionoid = convert_function_name(functionname);
|
|
|
|
mode = convert_function_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_function_privilege_id_id
|
|
|
|
* Check user privileges on a function given
|
2005-06-28 07:09:14 +02:00
|
|
|
* roleid, function oid, and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_function_privilege_id_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid functionoid = PG_GETARG_OID(1);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
mode = convert_function_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_proc_aclcheck(functionoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2008-03-25 23:42:46 +01:00
|
|
|
char *funcname = text_to_cstring(functionname);
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid oid;
|
|
|
|
|
|
|
|
oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein,
|
2005-10-15 04:49:52 +02:00
|
|
|
CStringGetDatum(funcname)));
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
if (!OidIsValid(oid))
|
2003-07-27 06:53:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
|
|
errmsg("function \"%s\" does not exist", funcname)));
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
return oid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* convert_function_priv_string
|
|
|
|
* Convert text string to AclMode value.
|
|
|
|
*/
|
|
|
|
static AclMode
|
|
|
|
convert_function_priv_string(text *priv_type_text)
|
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
static const priv_map function_priv_map[] = {
|
2009-06-11 16:49:15 +02:00
|
|
|
{"EXECUTE", ACL_EXECUTE},
|
|
|
|
{"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
|
|
|
|
{NULL, 0}
|
2009-02-06 22:15:12 +01:00
|
|
|
};
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
return convert_any_priv_string(priv_type_text, function_priv_map);
|
2002-08-09 18:45:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_language_privilege variants
|
|
|
|
* These are all named "has_language_privilege" at the SQL level.
|
|
|
|
* They take various combinations of language name, language OID,
|
2005-06-28 07:09:14 +02:00
|
|
|
* user name, user OID, or implicit user = current_user.
|
2002-08-09 18:45:16 +02:00
|
|
|
*
|
|
|
|
* The result is a boolean value: true if user has the indicated
|
2008-12-15 19:09:41 +01:00
|
|
|
* privilege, false if not, or NULL if object doesn't exist.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *languagename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid languageoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2002-08-09 18:45:16 +02:00
|
|
|
languageoid = convert_language_name(languagename);
|
|
|
|
mode = convert_language_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_language_aclcheck(languageoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *languagename = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid languageoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2002-08-09 18:45:16 +02:00
|
|
|
languageoid = convert_language_name(languagename);
|
|
|
|
mode = convert_language_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_language_aclcheck(languageoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2002-08-09 18:45:16 +02:00
|
|
|
mode = convert_language_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_language_aclcheck(languageoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2002-08-09 18:45:16 +02:00
|
|
|
mode = convert_language_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_language_aclcheck(languageoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_language_privilege_id_name
|
|
|
|
* Check user privileges on a language given
|
2005-06-28 07:09:14 +02:00
|
|
|
* roleid, text languagename, and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_language_privilege_id_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *languagename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid languageoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
languageoid = convert_language_name(languagename);
|
|
|
|
mode = convert_language_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_language_aclcheck(languageoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_language_privilege_id_id
|
|
|
|
* Check user privileges on a language given
|
2005-06-28 07:09:14 +02:00
|
|
|
* roleid, language oid, and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_language_privilege_id_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid languageoid = PG_GETARG_OID(1);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
mode = convert_language_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_language_aclcheck(languageoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2008-03-25 23:42:46 +01:00
|
|
|
char *langname = text_to_cstring(languagename);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
return get_language_oid(langname, false);
|
2002-08-09 18:45:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* convert_language_priv_string
|
|
|
|
* Convert text string to AclMode value.
|
|
|
|
*/
|
|
|
|
static AclMode
|
|
|
|
convert_language_priv_string(text *priv_type_text)
|
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
static const priv_map language_priv_map[] = {
|
2009-06-11 16:49:15 +02:00
|
|
|
{"USAGE", ACL_USAGE},
|
|
|
|
{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
|
|
|
|
{NULL, 0}
|
2009-02-06 22:15:12 +01:00
|
|
|
};
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
return convert_any_priv_string(priv_type_text, language_priv_map);
|
2002-08-09 18:45:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_schema_privilege variants
|
|
|
|
* These are all named "has_schema_privilege" at the SQL level.
|
|
|
|
* They take various combinations of schema name, schema OID,
|
2005-06-28 07:09:14 +02:00
|
|
|
* user name, user OID, or implicit user = current_user.
|
2002-08-09 18:45:16 +02:00
|
|
|
*
|
|
|
|
* The result is a boolean value: true if user has the indicated
|
2008-12-15 19:09:41 +01:00
|
|
|
* privilege, false if not, or NULL if object doesn't exist.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *schemaname = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid schemaoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2002-08-09 18:45:16 +02:00
|
|
|
schemaoid = convert_schema_name(schemaname);
|
|
|
|
mode = convert_schema_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *schemaname = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid schemaoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2002-08-09 18:45:16 +02:00
|
|
|
schemaoid = convert_schema_name(schemaname);
|
|
|
|
mode = convert_schema_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2002-08-09 18:45:16 +02:00
|
|
|
mode = convert_schema_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2002-08-09 18:45:16 +02:00
|
|
|
mode = convert_schema_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_schema_privilege_id_name
|
|
|
|
* Check user privileges on a schema given
|
2005-06-28 07:09:14 +02:00
|
|
|
* roleid, text schemaname, and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_schema_privilege_id_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *schemaname = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid schemaoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
schemaoid = convert_schema_name(schemaname);
|
|
|
|
mode = convert_schema_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_schema_privilege_id_id
|
|
|
|
* Check user privileges on a schema given
|
2005-06-28 07:09:14 +02:00
|
|
|
* roleid, schema oid, and text priv name.
|
2002-08-09 18:45:16 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_schema_privilege_id_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2002-08-09 18:45:16 +02:00
|
|
|
Oid schemaoid = PG_GETARG_OID(1);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2002-08-09 18:45:16 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
mode = convert_schema_priv_string(priv_type_text);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
|
2008-12-15 19:09:41 +01:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2008-03-25 23:42:46 +01:00
|
|
|
char *nspname = text_to_cstring(schemaname);
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
return get_namespace_oid(nspname, false);
|
2002-08-09 18:45:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* convert_schema_priv_string
|
|
|
|
* Convert text string to AclMode value.
|
|
|
|
*/
|
|
|
|
static AclMode
|
|
|
|
convert_schema_priv_string(text *priv_type_text)
|
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
static const priv_map schema_priv_map[] = {
|
2009-06-11 16:49:15 +02:00
|
|
|
{"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}
|
2009-02-06 22:15:12 +01:00
|
|
|
};
|
2002-08-09 18:45:16 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
return convert_any_priv_string(priv_type_text, schema_priv_map);
|
2001-06-14 03:09:22 +02:00
|
|
|
}
|
2004-07-12 22:23:59 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *servername = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
Oid serverid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2009-02-06 22:15:12 +01:00
|
|
|
serverid = convert_server_name(servername);
|
|
|
|
mode = convert_server_priv_string(priv_type_text);
|
|
|
|
|
|
|
|
aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *servername = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2009-02-06 22:15:12 +01:00
|
|
|
mode = convert_server_priv_string(priv_type_text);
|
|
|
|
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid)))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2009-02-06 22:15:12 +01:00
|
|
|
Oid roleid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
roleid = GetUserId();
|
|
|
|
mode = convert_server_priv_string(priv_type_text);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid)))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *servername = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2009-02-06 22:15:12 +01:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
mode = convert_server_priv_string(priv_type_text);
|
|
|
|
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid)))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
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);
|
|
|
|
|
2011-04-01 17:28:28 +02:00
|
|
|
return get_foreign_server_oid(serverstr, false);
|
2009-02-06 22:15:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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[] = {
|
2009-06-11 16:49:15 +02:00
|
|
|
{"USAGE", ACL_USAGE},
|
|
|
|
{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
|
|
|
|
{NULL, 0}
|
2009-02-06 22:15:12 +01:00
|
|
|
};
|
2008-12-19 17:25:19 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
return convert_any_priv_string(priv_type_text, server_priv_map);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-07-12 22:23:59 +02:00
|
|
|
/*
|
|
|
|
* has_tablespace_privilege variants
|
|
|
|
* These are all named "has_tablespace_privilege" at the SQL level.
|
|
|
|
* They take various combinations of tablespace name, tablespace OID,
|
2005-06-28 07:09:14 +02:00
|
|
|
* user name, user OID, or implicit user = current_user.
|
2004-07-12 22:23:59 +02:00
|
|
|
*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablespacename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2004-07-12 22:23:59 +02:00
|
|
|
Oid tablespaceoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2004-07-12 22:23:59 +02:00
|
|
|
tablespaceoid = convert_tablespace_name(tablespacename);
|
|
|
|
mode = convert_tablespace_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
|
2004-07-12 22:23:59 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablespacename = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2004-07-12 22:23:59 +02:00
|
|
|
Oid tablespaceoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2004-07-12 22:23:59 +02:00
|
|
|
tablespaceoid = convert_tablespace_name(tablespacename);
|
|
|
|
mode = convert_tablespace_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
|
2004-07-12 22:23:59 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid;
|
2004-07-12 22:23:59 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-10-13 07:37:23 +02:00
|
|
|
roleid = get_role_oid_or_public(NameStr(*username));
|
2004-07-12 22:23:59 +02:00
|
|
|
mode = convert_tablespace_priv_string(priv_type_text);
|
|
|
|
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid)))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
|
2004-07-12 22:23:59 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid roleid;
|
2004-07-12 22:23:59 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
roleid = GetUserId();
|
2004-07-12 22:23:59 +02:00
|
|
|
mode = convert_tablespace_priv_string(priv_type_text);
|
|
|
|
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid)))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
|
2004-07-12 22:23:59 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_tablespace_privilege_id_name
|
|
|
|
* Check user privileges on a tablespace given
|
2005-06-28 07:09:14 +02:00
|
|
|
* roleid, text tablespacename, and text priv name.
|
2004-07-12 22:23:59 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_tablespace_privilege_id_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *tablespacename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2004-07-12 22:23:59 +02:00
|
|
|
Oid tablespaceoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
tablespaceoid = convert_tablespace_name(tablespacename);
|
|
|
|
mode = convert_tablespace_priv_string(priv_type_text);
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
|
2004-07-12 22:23:59 +02:00
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_tablespace_privilege_id_id
|
|
|
|
* Check user privileges on a tablespace given
|
2005-06-28 07:09:14 +02:00
|
|
|
* roleid, tablespace oid, and text priv name.
|
2004-07-12 22:23:59 +02:00
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
2004-07-12 22:23:59 +02:00
|
|
|
Oid tablespaceoid = PG_GETARG_OID(1);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2004-07-12 22:23:59 +02:00
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
mode = convert_tablespace_priv_string(priv_type_text);
|
|
|
|
|
Fix corner-case failures in has_foo_privilege() family of functions.
The variants of these functions that take numeric inputs (OIDs or
column numbers) are supposed to return NULL rather than failing
on bad input; this rule reduces problems with snapshot skew when
queries apply the functions to all rows of a catalog.
has_column_privilege() had careless handling of the case where the
table OID didn't exist. You might get something like this:
select has_column_privilege(9999,'nosuchcol','select');
ERROR: column "nosuchcol" of relation "(null)" does not exist
or you might get a crash, depending on the platform's printf's response
to a null string pointer.
In addition, while applying the column-number variant to a dropped
column returned NULL as desired, applying the column-name variant
did not:
select has_column_privilege('mytable','........pg.dropped.2........','select');
ERROR: column "........pg.dropped.2........" of relation "mytable" does not exist
It seems better to make this case return NULL as well.
Also, the OID-accepting variants of has_foreign_data_wrapper_privilege,
has_server_privilege, and has_tablespace_privilege didn't follow the
principle of returning NULL for nonexistent OIDs. Superusers got TRUE,
everybody else got an error.
Per investigation of Jaime Casanova's report of a new crash in HEAD.
These behaviors have been like this for a long time, so back-patch to
all supported branches.
Patch by me; thanks to Stephen Frost for discussion and review
Discussion: https://postgr.es/m/CAJGNTeP=-6Gyqq5TN9OvYEydi7Fv1oGyYj650LGTnW44oAzYCg@mail.gmail.com
2018-10-02 17:54:12 +02:00
|
|
|
if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid)))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode);
|
2004-07-12 22:23:59 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2008-03-25 23:42:46 +01:00
|
|
|
char *spcname = text_to_cstring(tablespacename);
|
2004-08-01 22:30:49 +02:00
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
return get_tablespace_oid(spcname, false);
|
2004-07-12 22:23:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* convert_tablespace_priv_string
|
|
|
|
* Convert text string to AclMode value.
|
|
|
|
*/
|
|
|
|
static AclMode
|
|
|
|
convert_tablespace_priv_string(text *priv_type_text)
|
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
static const priv_map tablespace_priv_map[] = {
|
2009-06-11 16:49:15 +02:00
|
|
|
{"CREATE", ACL_CREATE},
|
|
|
|
{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
|
|
|
|
{NULL, 0}
|
2009-02-06 22:15:12 +01:00
|
|
|
};
|
2004-07-12 22:23:59 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
return convert_any_priv_string(priv_type_text, tablespace_priv_map);
|
2004-07-12 22:23:59 +02:00
|
|
|
}
|
2005-06-28 07:09:14 +02:00
|
|
|
|
2011-12-19 23:05:19 +01:00
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *typename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2011-12-19 23:05:19 +01:00
|
|
|
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)
|
|
|
|
{
|
2017-03-13 00:35:34 +01:00
|
|
|
text *typename = PG_GETARG_TEXT_PP(0);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2011-12-19 23:05:19 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2011-12-19 23:05:19 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2011-12-19 23:05:19 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *typename = PG_GETARG_TEXT_PP(1);
|
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2011-12-19 23:05:19 +01:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2011-12-19 23:05:19 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-07-26 02:04:19 +02:00
|
|
|
/*
|
|
|
|
* 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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-07-26 02:04:19 +02:00
|
|
|
Oid roleid;
|
|
|
|
Oid roleoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
roleid = get_role_oid(NameStr(*username), false);
|
|
|
|
roleoid = get_role_oid(NameStr(*rolename), false);
|
2005-07-26 02:04:19 +02:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-07-26 02:04:19 +02:00
|
|
|
Oid roleid;
|
|
|
|
Oid roleoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
roleid = GetUserId();
|
2010-08-05 16:45:09 +02:00
|
|
|
roleoid = get_role_oid(NameStr(*rolename), false);
|
2005-07-26 02:04:19 +02:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-07-26 02:04:19 +02:00
|
|
|
Oid roleid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
roleid = get_role_oid(NameStr(*username), false);
|
2005-07-26 02:04:19 +02:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(1);
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid roleid;
|
2005-07-26 02:04:19 +02:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-07-26 02:04:19 +02:00
|
|
|
Oid roleoid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
roleoid = get_role_oid(NameStr(*rolename), false);
|
2005-07-26 02:04:19 +02:00
|
|
|
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);
|
2017-03-13 00:35:34 +01:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_PP(2);
|
2005-07-26 02:04:19 +02:00
|
|
|
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.
|
|
|
|
*
|
2005-07-26 18:38:29 +02:00
|
|
|
* 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
|
2005-07-26 02:04:19 +02:00
|
|
|
* is shared only with pg_role_aclcheck, below.
|
|
|
|
*/
|
|
|
|
static AclMode
|
|
|
|
convert_role_priv_string(text *priv_type_text)
|
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
static const priv_map role_priv_map[] = {
|
2009-06-11 16:49:15 +02:00
|
|
|
{"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}
|
2009-02-06 22:15:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
return convert_any_priv_string(priv_type_text, role_priv_map);
|
2005-07-26 02:04:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pg_role_aclcheck
|
|
|
|
* Quick-and-dirty support for pg_has_role
|
|
|
|
*/
|
|
|
|
static AclResult
|
|
|
|
pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode)
|
|
|
|
{
|
2005-07-26 18:38:29 +02:00
|
|
|
if (mode & ACL_GRANT_OPTION_FOR(ACL_CREATE))
|
2005-07-26 02:04:19 +02:00
|
|
|
{
|
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.
|
|
|
|
*/
|
2005-07-26 02:04:19 +02:00
|
|
|
if (is_admin_of_role(roleid, role_oid))
|
|
|
|
return ACLCHECK_OK;
|
|
|
|
}
|
2005-07-26 18:38:29 +02:00
|
|
|
if (mode & ACL_CREATE)
|
2005-07-26 02:04:19 +02:00
|
|
|
{
|
|
|
|
if (is_member_of_role(roleid, role_oid))
|
|
|
|
return ACLCHECK_OK;
|
|
|
|
}
|
2005-07-26 18:38:29 +02:00
|
|
|
if (mode & ACL_USAGE)
|
|
|
|
{
|
|
|
|
if (has_privs_of_role(roleid, role_oid))
|
|
|
|
return ACLCHECK_OK;
|
|
|
|
}
|
|
|
|
return ACLCHECK_NO_PRIV;
|
2005-07-26 02:04:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
/*
|
|
|
|
* initialization function (called by InitPostgres)
|
|
|
|
*/
|
2005-06-28 07:09:14 +02:00
|
|
|
void
|
2005-06-28 21:51:26 +02:00
|
|
|
initialize_acl(void)
|
2005-06-28 07:09:14 +02:00
|
|
|
{
|
|
|
|
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
|
2005-06-28 07:09:14 +02:00
|
|
|
*/
|
|
|
|
CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
|
2005-06-29 22:34:15 +02:00
|
|
|
RoleMembershipCacheCallback,
|
2005-06-28 07:09:14 +02:00
|
|
|
(Datum) 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-06-29 22:34:15 +02:00
|
|
|
* RoleMembershipCacheCallback
|
2005-10-15 04:49:52 +02:00
|
|
|
* Syscache inval callback function
|
2005-06-28 07:09:14 +02:00
|
|
|
*/
|
|
|
|
static void
|
2011-08-17 01:27:46 +02:00
|
|
|
RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
|
2005-06-28 07:09:14 +02:00
|
|
|
{
|
2005-07-26 18:38:29 +02:00
|
|
|
/* Force membership caches to be recomputed on next use */
|
|
|
|
cached_privs_role = InvalidOid;
|
|
|
|
cached_member_role = InvalidOid;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-23 19:35:49 +01:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
/*
|
2005-10-10 20:49:04 +02:00
|
|
|
* Get a list of roles that the specified roleid has the privileges of
|
2005-07-26 18:38:29 +02:00
|
|
|
*
|
|
|
|
* 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
|
2005-10-10 20:49:04 +02:00
|
|
|
* 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.
|
2005-07-26 18:38:29 +02:00
|
|
|
*/
|
2005-10-10 20:49:04 +02:00
|
|
|
static List *
|
|
|
|
roles_has_privs_of(Oid roleid)
|
2005-07-26 18:38:29 +02:00
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
List *roles_list;
|
|
|
|
ListCell *l;
|
|
|
|
List *new_cached_privs_roles;
|
|
|
|
MemoryContext oldctx;
|
2005-07-26 18:38:29 +02:00
|
|
|
|
2005-10-10 20:49:04 +02:00
|
|
|
/* If cache is already valid, just return the list */
|
|
|
|
if (OidIsValid(cached_privs_role) && cached_privs_role == roleid)
|
|
|
|
return cached_privs_roles;
|
2005-07-26 18:38:29 +02:00
|
|
|
|
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.
|
2005-07-26 18:38:29 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-10-10 20:49:04 +02:00
|
|
|
roles_list = list_make1_oid(roleid);
|
2005-07-26 18:38:29 +02:00
|
|
|
|
|
|
|
foreach(l, roles_list)
|
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid memberid = lfirst_oid(l);
|
|
|
|
CatCList *memlist;
|
|
|
|
int i;
|
2005-07-26 18:38:29 +02:00
|
|
|
|
|
|
|
/* Ignore non-inheriting roles */
|
2014-12-23 19:35:49 +01:00
|
|
|
if (!has_rolinherit(memberid))
|
2005-07-26 18:38:29 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Find roles that memberid is directly a member of */
|
2010-02-14 19:42:19 +01:00
|
|
|
memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
|
|
|
|
ObjectIdGetDatum(memberid));
|
2005-07-26 18:38:29 +02:00
|
|
|
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;
|
2005-07-26 18:38:29 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
2005-07-26 18:38:29 +02:00
|
|
|
*/
|
2005-07-29 00:27:02 +02:00
|
|
|
roles_list = list_append_unique_oid(roles_list, otherid);
|
2005-07-26 18:38:29 +02:00
|
|
|
}
|
|
|
|
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 */
|
2005-07-26 18:38:29 +02:00
|
|
|
list_free(cached_privs_roles);
|
|
|
|
cached_privs_roles = new_cached_privs_roles;
|
2005-10-10 20:49:04 +02:00
|
|
|
cached_privs_role = roleid;
|
2005-07-26 18:38:29 +02:00
|
|
|
|
|
|
|
/* And now we can return the answer */
|
2005-10-10 20:49:04 +02:00
|
|
|
return cached_privs_roles;
|
2005-06-28 07:09:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
/*
|
2005-10-10 20:49:04 +02:00
|
|
|
* Get a list of roles that the specified roleid is a member of
|
2005-06-29 22:34:15 +02:00
|
|
|
*
|
2005-07-26 18:38:29 +02:00
|
|
|
* This is defined to recurse through roles regardless of rolinherit.
|
|
|
|
*
|
2005-06-29 22:34:15 +02:00
|
|
|
* Since indirect membership testing is relatively expensive, we cache
|
2005-10-10 20:49:04 +02:00
|
|
|
* a list of memberships. Hence, the result is only guaranteed good until
|
|
|
|
* the next call of roles_is_member_of()!
|
2005-06-28 07:09:14 +02:00
|
|
|
*/
|
2005-10-10 20:49:04 +02:00
|
|
|
static List *
|
|
|
|
roles_is_member_of(Oid roleid)
|
2005-06-29 22:34:15 +02:00
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
List *roles_list;
|
|
|
|
ListCell *l;
|
|
|
|
List *new_cached_membership_roles;
|
|
|
|
MemoryContext oldctx;
|
2005-06-28 07:09:14 +02:00
|
|
|
|
2005-10-10 20:49:04 +02:00
|
|
|
/* If cache is already valid, just return the list */
|
|
|
|
if (OidIsValid(cached_member_role) && cached_member_role == roleid)
|
|
|
|
return cached_membership_roles;
|
2005-06-28 07:09:14 +02:00
|
|
|
|
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.
|
2005-06-29 22:34:15 +02:00
|
|
|
*
|
|
|
|
* 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.
|
2005-06-28 07:09:14 +02:00
|
|
|
*/
|
2005-10-10 20:49:04 +02:00
|
|
|
roles_list = list_make1_oid(roleid);
|
2005-06-28 07:09:14 +02:00
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
foreach(l, roles_list)
|
2005-06-28 07:09:14 +02:00
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid memberid = lfirst_oid(l);
|
|
|
|
CatCList *memlist;
|
|
|
|
int i;
|
2005-06-29 22:34:15 +02:00
|
|
|
|
|
|
|
/* Find roles that memberid is directly a member of */
|
2010-02-14 19:42:19 +01:00
|
|
|
memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
|
|
|
|
ObjectIdGetDatum(memberid));
|
2005-06-29 22:34:15 +02:00
|
|
|
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;
|
2005-06-29 22:34:15 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
2005-06-29 22:34:15 +02:00
|
|
|
*/
|
2005-07-29 00:27:02 +02:00
|
|
|
roles_list = list_append_unique_oid(roles_list, otherid);
|
2005-06-28 07:09:14 +02:00
|
|
|
}
|
|
|
|
ReleaseSysCacheList(memlist);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-06-29 22:34:15 +02:00
|
|
|
* Copy the completed list into TopMemoryContext so it will persist.
|
2005-06-28 07:09:14 +02:00
|
|
|
*/
|
|
|
|
oldctx = MemoryContextSwitchTo(TopMemoryContext);
|
2005-07-26 18:38:29 +02:00
|
|
|
new_cached_membership_roles = list_copy(roles_list);
|
2005-06-28 07:09:14 +02:00
|
|
|
MemoryContextSwitchTo(oldctx);
|
2005-06-29 22:34:15 +02:00
|
|
|
list_free(roles_list);
|
2005-06-28 07:09:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now safe to assign to state variable
|
|
|
|
*/
|
2005-07-26 18:38:29 +02:00
|
|
|
cached_member_role = InvalidOid; /* just paranoia */
|
|
|
|
list_free(cached_membership_roles);
|
|
|
|
cached_membership_roles = new_cached_membership_roles;
|
2005-10-10 20:49:04 +02:00
|
|
|
cached_member_role = roleid;
|
2005-06-28 07:09:14 +02:00
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
/* And now we can return the answer */
|
2005-10-10 20:49:04 +02:00
|
|
|
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
|
|
|
/*
|
2005-10-10 20:49:04 +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
|
|
|
/*
|
2005-10-10 20:49:04 +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);
|
2005-06-29 22:34:15 +02:00
|
|
|
}
|
|
|
|
|
2005-07-14 23:46:30 +02:00
|
|
|
/*
|
|
|
|
* 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\"",
|
2015-05-09 19:06:49 +02:00
|
|
|
GetUserNameFromId(role, false))));
|
2005-07-14 23:46:30 +02:00
|
|
|
}
|
|
|
|
|
2005-11-04 18:25:15 +01:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* 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?
|
2005-06-29 22:34:15 +02:00
|
|
|
*/
|
|
|
|
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;
|
2005-06-29 22:34:15 +02:00
|
|
|
|
2005-07-26 02:04:19 +02:00
|
|
|
if (superuser_arg(member))
|
|
|
|
return true;
|
|
|
|
|
2014-02-17 15:33:31 +01:00
|
|
|
if (member == role)
|
2014-05-06 18:12:18 +02:00
|
|
|
|
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.
|
2005-06-28 07:09:14 +02:00
|
|
|
*/
|
2005-06-29 22:34:15 +02:00
|
|
|
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;
|
2005-06-29 22:34:15 +02:00
|
|
|
|
|
|
|
/* Find roles that memberid is directly a member of */
|
2010-02-14 19:42:19 +01:00
|
|
|
memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
|
|
|
|
ObjectIdGetDatum(memberid));
|
2005-06-29 22:34:15 +02:00
|
|
|
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;
|
2005-06-29 22:34:15 +02:00
|
|
|
|
|
|
|
if (otherid == role &&
|
|
|
|
((Form_pg_auth_members) GETSTRUCT(tup))->admin_option)
|
|
|
|
{
|
|
|
|
/* Found what we came for, so can stop searching */
|
|
|
|
result = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-07-29 00:27:02 +02:00
|
|
|
roles_list = list_append_unique_oid(roles_list, otherid);
|
2005-06-29 22:34:15 +02:00
|
|
|
}
|
|
|
|
ReleaseSysCacheList(memlist);
|
|
|
|
if (result)
|
|
|
|
break;
|
|
|
|
}
|
2005-06-28 07:09:14 +02:00
|
|
|
|
|
|
|
list_free(roles_list);
|
2005-06-29 22:34:15 +02:00
|
|
|
|
|
|
|
return result;
|
2005-06-28 07:09:14 +02:00
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* does what it says ... */
|
|
|
|
static int
|
|
|
|
count_one_bits(AclMode mask)
|
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
int nbits = 0;
|
2005-10-10 20:49:04 +02:00
|
|
|
|
|
|
|
/* 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
|
2014-05-06 18:12:18 +02:00
|
|
|
* multiple "original sources" of a privilege. Therefore, if the would-be
|
2005-10-10 20:49:04 +02:00
|
|
|
* 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
|
2014-05-06 18:12:18 +02:00
|
|
|
* of desired options. Ties are broken in favor of closer ancestors.
|
2005-10-10 20:49:04 +02:00
|
|
|
*
|
|
|
|
* 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;
|
2005-10-10 20:49:04 +02:00
|
|
|
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.
|
2005-10-10 20:49:04 +02:00
|
|
|
*/
|
|
|
|
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()
|
2005-10-10 20:49:04 +02:00
|
|
|
* 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;
|
2005-10-10 20:49:04 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
2005-10-10 20:49:04 +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);
|
2005-10-10 20:49:04 +02:00
|
|
|
|
|
|
|
if (nnewrights > nrights)
|
|
|
|
{
|
|
|
|
*grantorId = otherrole;
|
|
|
|
*grantOptions = otherprivs;
|
|
|
|
nrights = nnewrights;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-08-05 16:45:09 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* get_role_oid - Given a role name, look up the role's OID.
|
|
|
|
*
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
* If missing_ok is false, throw an error if role name not found. If
|
2010-08-05 16:45:09 +02:00
|
|
|
* true, just return InvalidOid.
|
|
|
|
*/
|
|
|
|
Oid
|
|
|
|
get_role_oid(const char *rolname, bool missing_ok)
|
|
|
|
{
|
|
|
|
Oid oid;
|
|
|
|
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
oid = GetSysCacheOid1(AUTHNAME, Anum_pg_authid_oid,
|
|
|
|
CStringGetDatum(rolname));
|
2010-08-05 16:45:09 +02:00
|
|
|
if (!OidIsValid(oid) && !missing_ok)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("role \"%s\" does not exist", rolname)));
|
|
|
|
return oid;
|
|
|
|
}
|
2010-10-13 07:37:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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".
|
2010-10-13 07:37:23 +02:00
|
|
|
*/
|
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
|
2010-10-13 07:37:23 +02:00
|
|
|
get_role_oid_or_public(const char *rolname)
|
|
|
|
{
|
|
|
|
if (strcmp(rolname, "public") == 0)
|
|
|
|
return ACL_ID_PUBLIC;
|
|
|
|
|
|
|
|
return get_role_oid(rolname, false);
|
|
|
|
}
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2016-12-28 18:00:00 +01:00
|
|
|
get_rolespec_oid(const RoleSpec *role, bool missing_ok)
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
{
|
|
|
|
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
|
2016-12-28 18:00:00 +01:00
|
|
|
get_rolespec_tuple(const RoleSpec *role)
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
{
|
|
|
|
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),
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
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:35:54 +02:00
|
|
|
errmsg("role \"%s\" does not exist", role->rolename)));
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
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 */
|
2018-05-02 01:35:08 +02:00
|
|
|
break;
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
|
|
|
|
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 *
|
2016-12-28 18:00:00 +01:00
|
|
|
get_rolespec_name(const RoleSpec *role)
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
{
|
|
|
|
HeapTuple tp;
|
|
|
|
Form_pg_authid authForm;
|
|
|
|
char *rolename;
|
|
|
|
|
2016-12-28 18:00:00 +01:00
|
|
|
tp = get_rolespec_tuple(role);
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
authForm = (Form_pg_authid) GETSTRUCT(tp);
|
|
|
|
rolename = pstrdup(NameStr(authForm->rolname));
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
|
|
|
|
return rolename;
|
|
|
|
}
|
2016-04-08 22:56:27 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2016-12-28 18:00:00 +01:00
|
|
|
check_rolespec_name(const RoleSpec *role, const char *detail_msg)
|
2016-04-08 22:56:27 +02:00
|
|
|
{
|
2016-12-28 18:00:00 +01:00
|
|
|
if (!role)
|
2016-04-08 22:56:27 +02:00
|
|
|
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),
|
2016-04-08 22:56:27 +02:00
|
|
|
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)));
|
2016-04-08 22:56:27 +02:00
|
|
|
}
|
|
|
|
}
|