1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* acl.c
|
1996-07-09 08:22:35 +02:00
|
|
|
* Basic access control list data structures manipulation routines.
|
|
|
|
*
|
2023-01-02 21:00:37 +01:00
|
|
|
* Portions Copyright (c) 1996-2023, 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"
|
2021-03-26 18:42:17 +01:00
|
|
|
#include "catalog/pg_database.h"
|
2022-11-13 08:11:17 +01:00
|
|
|
#include "catalog/pg_foreign_data_wrapper.h"
|
|
|
|
#include "catalog/pg_foreign_server.h"
|
|
|
|
#include "catalog/pg_language.h"
|
|
|
|
#include "catalog/pg_namespace.h"
|
2022-04-06 19:24:33 +02:00
|
|
|
#include "catalog/pg_parameter_acl.h"
|
2022-11-13 08:11:17 +01:00
|
|
|
#include "catalog/pg_proc.h"
|
|
|
|
#include "catalog/pg_tablespace.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"
|
2022-04-06 19:24:33 +02:00
|
|
|
#include "utils/guc.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
|
2021-03-26 18:42:16 +01:00
|
|
|
* active current user. So we can optimize it by keeping cached lists of all
|
|
|
|
* the roles the "given role" is a member of, directly or indirectly.
|
2005-07-26 18:38:29 +02:00
|
|
|
*
|
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?
|
|
|
|
*
|
2021-03-26 18:42:16 +01:00
|
|
|
* Each element of cached_roles is an OID list of constituent roles for the
|
|
|
|
* corresponding element of cached_role (always including the cached_role
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
* itself). There's a separate cache for each RoleRecurseType, with the
|
|
|
|
* corresponding semantics.
|
2005-06-28 07:09:14 +02:00
|
|
|
*/
|
2021-03-26 18:42:16 +01:00
|
|
|
enum RoleRecurseType
|
|
|
|
{
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
ROLERECURSE_MEMBERS = 0, /* recurse unconditionally */
|
|
|
|
ROLERECURSE_PRIVS = 1, /* recurse through inheritable grants */
|
|
|
|
ROLERECURSE_SETROLE = 2 /* recurse through grants with set_option */
|
2021-03-26 18:42:16 +01:00
|
|
|
};
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
static Oid cached_role[] = {InvalidOid, InvalidOid, InvalidOid};
|
|
|
|
static List *cached_roles[] = {NIL, NIL, NIL};
|
2021-03-26 18:42:17 +01:00
|
|
|
static uint32 cached_db_hash;
|
2005-06-28 07:09:14 +02:00
|
|
|
|
2001-06-14 03:09:22 +02:00
|
|
|
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
static const char *getid(const char *s, char *n, Node *escontext);
|
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);
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
static const char *aclparse(const char *s, AclItem *aip, Node *escontext);
|
2003-06-27 02:33:26 +02:00
|
|
|
static bool aclitem_match(const AclItem *a1, const AclItem *a2);
|
2009-10-05 21:24:49 +02: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,
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid ownerId);
|
|
|
|
static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
|
|
|
|
Oid ownerId, DropBehavior behavior);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
static AclMode convert_any_priv_string(text *priv_type_text,
|
|
|
|
const priv_map *privileges);
|
2003-06-11 11:23:55 +02:00
|
|
|
|
2002-08-09 18:45:16 +02:00
|
|
|
static Oid convert_table_name(text *tablename);
|
|
|
|
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-08-09 18:45:16 +02:00
|
|
|
static Oid convert_database_name(text *databasename);
|
|
|
|
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-08-09 18:45:16 +02:00
|
|
|
static Oid convert_function_name(text *functionname);
|
|
|
|
static AclMode convert_function_priv_string(text *priv_type_text);
|
|
|
|
static Oid convert_language_name(text *languagename);
|
|
|
|
static AclMode convert_language_priv_string(text *priv_type_text);
|
|
|
|
static Oid convert_schema_name(text *schemaname);
|
|
|
|
static AclMode convert_schema_priv_string(text *priv_type_text);
|
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-07-12 22:23:59 +02:00
|
|
|
static Oid convert_tablespace_name(text *tablespacename);
|
|
|
|
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);
|
2022-04-06 19:24:33 +02:00
|
|
|
static AclMode convert_parameter_priv_string(text *priv_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
|
|
|
|
* Consumes the first alphanumeric string (identifier) found in string
|
1999-03-21 07:31:59 +01:00
|
|
|
* 's', ignoring any leading white space. If it finds a double quote
|
|
|
|
* it returns the word inside the quotes.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
|
|
|
* 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.
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
*
|
|
|
|
* Errors are reported via ereport, unless escontext is an ErrorSaveData node,
|
|
|
|
* in which case we log the error there and return NULL.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2001-06-10 01:21:55 +02:00
|
|
|
static const char *
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
getid(const char *s, char *n, Node *escontext)
|
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
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
Assert(s && n);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
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' &&
|
|
|
|
(isalnum((unsigned char) *s) ||
|
|
|
|
*s == '_' ||
|
|
|
|
*s == '"' ||
|
|
|
|
in_quotes);
|
|
|
|
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)
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
ereturn(escontext, NULL,
|
2003-08-14 16:19:11 +02:00
|
|
|
(errcode(ERRCODE_NAME_TOO_LONG),
|
|
|
|
errmsg("identifier too long"),
|
|
|
|
errdetail("Identifier must be less than %d characters.",
|
|
|
|
NAMEDATALEN)));
|
|
|
|
|
|
|
|
n[len++] = *s;
|
1999-03-21 07:31:59 +01:00
|
|
|
}
|
1996-07-09 08:22:35 +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++;
|
1996-07-09 08:22:35 +02:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
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.
|
2023-04-21 00:46:08 +02:00
|
|
|
* This needs to be kept in sync with dequoteAclUserName in pg_dump/dumputils.c
|
2003-06-02 21:00:29 +02:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
putid(char *p, const char *s)
|
|
|
|
{
|
|
|
|
const char *src;
|
|
|
|
bool safe = true;
|
|
|
|
|
|
|
|
for (src = s; *src; src++)
|
|
|
|
{
|
|
|
|
/* This test had better match what getid() does, above */
|
|
|
|
if (!isalnum((unsigned char) *src) && *src != '_')
|
|
|
|
{
|
|
|
|
safe = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!safe)
|
|
|
|
*p++ = '"';
|
|
|
|
for (src = s; *src; src++)
|
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
|
|
|
|
* Consumes and parses an ACL specification of the form:
|
2003-01-24 00:39:07 +01:00
|
|
|
* [group|user] [A-Za-z0-9]*=[rwaR]*
|
1996-07-09 08:22:35 +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.
|
|
|
|
*
|
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.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* This routine is called by the parser as well as aclitemin(), hence
|
|
|
|
* the added generality.
|
|
|
|
*
|
|
|
|
* RETURNS:
|
|
|
|
* the string position in 's' immediately following the ACL
|
|
|
|
* specification. Also:
|
|
|
|
* - loads the structure pointed to by 'aip' with the appropriate
|
|
|
|
* UID/GID, id type identifier and mode type values.
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
*
|
|
|
|
* Errors are reported via ereport, unless escontext is an ErrorSaveData node,
|
|
|
|
* in which case we log the error there and return NULL.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2002-04-21 02:26:44 +02:00
|
|
|
static const char *
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
aclparse(const char *s, AclItem *aip, Node *escontext)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2003-01-24 00:39:07 +01:00
|
|
|
AclMode privs,
|
|
|
|
goption,
|
|
|
|
read;
|
1997-08-03 04:38:47 +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
|
|
|
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
s = getid(s, name, escontext);
|
|
|
|
if (s == NULL)
|
|
|
|
return NULL;
|
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)
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
ereturn(escontext, NULL,
|
2003-07-27 06:53:12 +02:00
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("unrecognized key word: \"%s\"", name),
|
2005-06-29 22:34:15 +02:00
|
|
|
errhint("ACL key word must be \"group\" or \"user\".")));
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
/* move s to the name beyond the keyword */
|
|
|
|
s = getid(s, name, escontext);
|
|
|
|
if (s == NULL)
|
|
|
|
return NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
if (name[0] == '\0')
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
ereturn(escontext, NULL,
|
2003-07-27 06:53:12 +02:00
|
|
|
(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 != '=')
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
ereturn(escontext, NULL,
|
2003-07-27 06:53:12 +02:00
|
|
|
(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-01-24 00:39:07 +01:00
|
|
|
for (++s, read = 0; isalpha((unsigned char) *s) || *s == '*'; s++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1996-07-09 08:22:35 +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-07 07:04:48 +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-07 07:04:48 +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-07 07:04:48 +02:00
|
|
|
break;
|
2006-04-30 04:09:07 +02:00
|
|
|
case ACL_CONNECT_CHR:
|
|
|
|
read = ACL_CONNECT;
|
|
|
|
break;
|
2022-04-06 19:24:33 +02:00
|
|
|
case ACL_SET_CHR:
|
|
|
|
read = ACL_SET;
|
|
|
|
break;
|
|
|
|
case ACL_ALTER_SYSTEM_CHR:
|
|
|
|
read = ACL_ALTER_SYSTEM;
|
|
|
|
break;
|
2022-12-14 02:33:28 +01:00
|
|
|
case ACL_MAINTAIN_CHR:
|
|
|
|
read = ACL_MAINTAIN;
|
2022-11-28 16:08:42 +01:00
|
|
|
break;
|
2006-09-05 23:08:36 +02:00
|
|
|
case 'R': /* ignore old RULE privileges */
|
|
|
|
read = 0;
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
default:
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
ereturn(escontext, NULL,
|
2003-07-27 06:53:12 +02:00
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
|
|
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
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
{
|
|
|
|
aip->ai_grantee = get_role_oid(name, true);
|
|
|
|
if (!OidIsValid(aip->ai_grantee))
|
|
|
|
ereturn(escontext, NULL,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("role \"%s\" does not exist", name)));
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-01-24 00:39:07 +01:00
|
|
|
/*
|
|
|
|
* XXX Allow a degree of backward compatibility by defaulting the grantor
|
|
|
|
* to the superuser.
|
|
|
|
*/
|
|
|
|
if (*s == '/')
|
|
|
|
{
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
s = getid(s + 1, name2, escontext);
|
|
|
|
if (s == NULL)
|
|
|
|
return NULL;
|
2003-01-24 00:39:07 +01:00
|
|
|
if (name2[0] == '\0')
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
ereturn(escontext, NULL,
|
2003-07-27 06:53:12 +02:00
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
|
|
errmsg("a name must follow the \"/\" sign")));
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
aip->ai_grantor = get_role_oid(name2, true);
|
|
|
|
if (!OidIsValid(aip->ai_grantor))
|
|
|
|
ereturn(escontext, NULL,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("role \"%s\" does not exist", name2)));
|
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
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-06-11 11:23:55 +02:00
|
|
|
* allocacl
|
1996-07-09 08:22:35 +02:00
|
|
|
* Allocates storage for a new Acl with 'n' entries.
|
|
|
|
*
|
|
|
|
* RETURNS:
|
|
|
|
* the new Acl
|
|
|
|
*/
|
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
|
|
|
{
|
|
|
|
Acl *new_acl;
|
|
|
|
Size size;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
if (n < 0)
|
2003-07-27 06:53:12 +02:00
|
|
|
elog(ERROR, "invalid size: %d", n);
|
1996-07-09 08:22:35 +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);
|
1996-07-09 08:22:35 +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;
|
1996-07-09 08:22:35 +02:00
|
|
|
ARR_DIMS(new_acl)[0] = n;
|
|
|
|
return new_acl;
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
Acl *result_acl;
|
|
|
|
|
|
|
|
result_acl = allocacl(ACL_NUM(orig_acl));
|
|
|
|
|
|
|
|
memcpy(ACL_DAT(result_acl),
|
|
|
|
ACL_DAT(orig_acl),
|
|
|
|
ACL_NUM(orig_acl) * sizeof(AclItem));
|
|
|
|
|
|
|
|
return result_acl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Concatenate two ACLs
|
|
|
|
*
|
|
|
|
* This is a bit cheesy, since we may produce an ACL with redundant entries.
|
|
|
|
* Be careful what the result is used for!
|
|
|
|
*/
|
|
|
|
Acl *
|
|
|
|
aclconcat(const Acl *left_acl, const Acl *right_acl)
|
|
|
|
{
|
|
|
|
Acl *result_acl;
|
|
|
|
|
|
|
|
result_acl = allocacl(ACL_NUM(left_acl) + ACL_NUM(right_acl));
|
|
|
|
|
|
|
|
memcpy(ACL_DAT(result_acl),
|
|
|
|
ACL_DAT(left_acl),
|
|
|
|
ACL_NUM(left_acl) * sizeof(AclItem));
|
|
|
|
|
|
|
|
memcpy(ACL_DAT(result_acl) + ACL_NUM(left_acl),
|
|
|
|
ACL_DAT(right_acl),
|
|
|
|
ACL_NUM(right_acl) * sizeof(AclItem));
|
|
|
|
|
|
|
|
return result_acl;
|
|
|
|
}
|
|
|
|
|
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++)
|
|
|
|
{
|
|
|
|
Acl *tmp_acl;
|
|
|
|
|
|
|
|
tmp_acl = aclupdate(result_acl, aip, ACL_MODECHG_ADD,
|
|
|
|
ownerId, DROP_RESTRICT);
|
|
|
|
pfree(result_acl);
|
|
|
|
result_acl = tmp_acl;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result_acl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sort the items in an ACL (into an arbitrary but consistent order)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
aclitemsort(Acl *acl)
|
|
|
|
{
|
|
|
|
if (acl != NULL && ACL_NUM(acl) > 1)
|
|
|
|
qsort(ACL_DAT(acl), ACL_NUM(acl), sizeof(AclItem), aclitemComparator);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if two ACLs are exactly equal
|
|
|
|
*
|
|
|
|
* This will not detect equality if the two arrays contain the same items
|
|
|
|
* in different orders. To handle that case, sort both inputs first,
|
|
|
|
* using aclitemsort().
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
aclequal(const Acl *left_acl, const Acl *right_acl)
|
|
|
|
{
|
|
|
|
/* Check for cases where one or both are empty/null */
|
|
|
|
if (left_acl == NULL || ACL_NUM(left_acl) == 0)
|
|
|
|
{
|
|
|
|
if (right_acl == NULL || ACL_NUM(right_acl) == 0)
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (right_acl == NULL || ACL_NUM(right_acl) == 0)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ACL_NUM(left_acl) != ACL_NUM(right_acl))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (memcmp(ACL_DAT(left_acl),
|
|
|
|
ACL_DAT(right_acl),
|
|
|
|
ACL_NUM(left_acl) * sizeof(AclItem)) == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
* Allocates storage for, and fills in, a new AclItem given a string
|
|
|
|
* 's' that contains an ACL specification. See aclparse for details.
|
|
|
|
*
|
|
|
|
* RETURNS:
|
|
|
|
* the new AclItem
|
|
|
|
*/
|
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);
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
Node *escontext = fcinfo->context;
|
1996-07-09 08:22:35 +02:00
|
|
|
AclItem *aip;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
aip = (AclItem *) palloc(sizeof(AclItem));
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
|
|
|
|
s = aclparse(s, aip, escontext);
|
|
|
|
if (s == NULL)
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
2000-12-03 21:45:40 +01:00
|
|
|
while (isspace((unsigned char) *s))
|
1996-07-09 08:22:35 +02:00
|
|
|
++s;
|
|
|
|
if (*s)
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
2022-12-14 23:50:24 +01:00
|
|
|
ereturn(escontext, (Datum) 0,
|
2003-07-27 06:53:12 +02:00
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
|
|
errmsg("extra garbage at the end of the ACL specification")));
|
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
PG_RETURN_ACLITEM_P(aip);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* aclitemout
|
|
|
|
* Allocates storage for, and fills in, a new null-delimited string
|
|
|
|
* containing a formatted ACL specification. See aclparse for details.
|
|
|
|
*
|
|
|
|
* RETURNS:
|
|
|
|
* the new string
|
|
|
|
*/
|
2000-08-01 00:39:17 +02:00
|
|
|
Datum
|
|
|
|
aclitemout(PG_FUNCTION_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-08-01 00:39:17 +02:00
|
|
|
AclItem *aip = PG_GETARG_ACLITEM_P(0);
|
1998-02-11 20:14:04 +01:00
|
|
|
char *p;
|
1996-07-09 08:22:35 +02:00
|
|
|
char *out;
|
1998-08-19 04:04:17 +02:00
|
|
|
HeapTuple htup;
|
1996-07-09 08:22:35 +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;
|
1996-07-09 08:22:35 +02:00
|
|
|
*p = '\0';
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
if (aip->ai_grantee != ACL_ID_PUBLIC)
|
1996-07-09 08:22:35 +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);
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
while (*p)
|
|
|
|
++p;
|
2003-01-24 00:39:07 +01:00
|
|
|
|
1996-07-09 08:22:35 +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
|
|
|
{
|
2022-11-24 23:05:22 +01:00
|
|
|
if (ACLITEM_GET_PRIVS(*aip) & (UINT64CONST(1) << i))
|
2002-04-21 02:26:44 +02:00
|
|
|
*p++ = ACL_ALL_RIGHTS_STR[i];
|
2022-11-24 23:05:22 +01:00
|
|
|
if (ACLITEM_GET_GOPTIONS(*aip) & (UINT64CONST(1) << i))
|
2003-01-24 00:39:07 +01:00
|
|
|
*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)
|
|
|
|
{
|
|
|
|
AclItem *a1 = PG_GETARG_ACLITEM_P(0);
|
|
|
|
AclItem *a2 = PG_GETARG_ACLITEM_P(1);
|
|
|
|
bool result;
|
|
|
|
|
|
|
|
result = a1->ai_privs == a2->ai_privs &&
|
|
|
|
a1->ai_grantee == a2->ai_grantee &&
|
|
|
|
a1->ai_grantor == a2->ai_grantor;
|
|
|
|
PG_RETURN_BOOL(result);
|
|
|
|
}
|
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
|
|
|
*/
|
1996-07-09 08:22:35 +02: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;
|
1996-07-09 08:22:35 +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;
|
2022-04-06 19:24:33 +02:00
|
|
|
case OBJECT_PARAMETER_ACL:
|
|
|
|
world_default = ACL_NO_RIGHTS;
|
|
|
|
owner_default = ACL_ALL_RIGHTS_PARAMETER_ACL;
|
|
|
|
break;
|
2002-04-21 02:26:44 +02:00
|
|
|
default:
|
2022-11-08 18:45:29 +01:00
|
|
|
elog(ERROR, "unrecognized object type: %d", (int) objtype);
|
2002-04-21 02:26:44 +02:00
|
|
|
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
|
|
|
|
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);
|
1996-07-09 08:22:35 +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
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
return acl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-27 20:58:51 +01: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)
|
|
|
|
{
|
|
|
|
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;
|
2022-04-06 19:24:33 +02:00
|
|
|
case 'p':
|
|
|
|
objtype = OBJECT_PARAMETER_ACL;
|
|
|
|
break;
|
2012-01-27 20:58:51 +01:00
|
|
|
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:
|
2022-11-08 18:45:29 +01:00
|
|
|
elog(ERROR, "unrecognized object type abbreviation: %c", objtypec);
|
2012-01-27 20:58:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
1996-07-09 08:22:35 +02: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;
|
1996-07-09 08:22:35 +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,
|
1996-07-09 08:22:35 +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
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
num = ACL_NUM(old_acl);
|
|
|
|
old_aip = ACL_DAT(old_acl);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2003-01-24 00:39:07 +01: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);
|
1996-07-09 08:22:35 +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);
|
1996-07-09 08:22:35 +02:00
|
|
|
num++; /* set num to the size of new_acl */
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
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 */
|
1996-07-09 08:22:35 +02:00
|
|
|
switch (modechg)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1996-07-09 08:22:35 +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-07 07:04:48 +02:00
|
|
|
break;
|
1996-07-09 08:22:35 +02:00
|
|
|
case ACL_MODECHG_DEL:
|
2004-06-01 23:49:23 +02:00
|
|
|
ACLITEM_SET_RIGHTS(new_aip[dst],
|
|
|
|
old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip));
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
1996-07-09 08:22:35 +02:00
|
|
|
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-07 07:04:48 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
/*
|
2003-10-29 23:20:54 +01:00
|
|
|
* Remove abandoned privileges (cascading revoke). Currently we can only
|
2005-06-28 07:09:14 +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
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
return new_acl;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
AclItem *new_aip;
|
|
|
|
AclItem *old_aip;
|
|
|
|
AclItem *dst_aip;
|
|
|
|
AclItem *src_aip;
|
|
|
|
AclItem *targ_aip;
|
|
|
|
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
|
|
|
|
* wherever it appears as either grantor or grantee. Also note if the new
|
|
|
|
* owner ID is already present.
|
|
|
|
*/
|
|
|
|
num = ACL_NUM(old_acl);
|
|
|
|
old_aip = ACL_DAT(old_acl);
|
|
|
|
new_acl = allocacl(num);
|
|
|
|
new_aip = ACL_DAT(new_acl);
|
|
|
|
memcpy(new_aip, old_aip, num * sizeof(AclItem));
|
|
|
|
for (dst = 0, dst_aip = new_aip; dst < num; dst++, dst_aip++)
|
|
|
|
{
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the old ACL contained any references to the new owner, then we may
|
|
|
|
* now have generated an ACL containing duplicate entries. Find them and
|
|
|
|
* merge them so that there are not duplicates. (This is relatively
|
|
|
|
* expensive since we use a stupid O(N^2) algorithm, but it's unlikely to
|
|
|
|
* be the normal case.)
|
|
|
|
*
|
|
|
|
* To simplify deletion of duplicate entries, we temporarily leave them in
|
|
|
|
* the array but set their privilege masks to zero; when we reach such an
|
|
|
|
* entry it's just skipped. (Thus, a side effect of this code will be to
|
|
|
|
* remove privilege-free entries, should there be any in the input.) dst
|
|
|
|
* is the next output slot, targ is the currently considered input slot
|
|
|
|
* (always >= dst), and src scans entries to the right of targ looking for
|
|
|
|
* duplicates. Once an entry has been emitted to dst it is known
|
|
|
|
* duplicate-free and need not be considered anymore.
|
|
|
|
*/
|
|
|
|
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,
|
2004-06-01 23:49:23 +02:00
|
|
|
ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)),
|
|
|
|
ACLMASK_ALL);
|
|
|
|
own_privs = ACL_OPTION_TO_PRIVS(own_privs);
|
|
|
|
|
|
|
|
if ((ACLITEM_GET_GOPTIONS(*mod_aip) & ~own_privs) != 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
|
|
|
errmsg("grant options cannot be granted back to your own grantor")));
|
|
|
|
|
|
|
|
pfree(acl);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
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;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
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-06-01 23:49:23 +02:00
|
|
|
AclItem *aidata = &aidat[i];
|
|
|
|
|
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-07 21:59:34 +02:00
|
|
|
* Check privileges granted indirectly via role memberships. We do this in
|
2005-06-29 22:34:15 +02:00
|
|
|
* 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
|
2005-10-07 21:59:34 +02:00
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
Oid *list;
|
|
|
|
const AclItem *acldat;
|
|
|
|
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
|
|
|
|
|
|
|
/*
|
|
|
|
* We could repalloc the array down to minimum size, but it's hardly worth
|
|
|
|
* it since it's only transient memory.
|
|
|
|
*/
|
|
|
|
*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);
|
|
|
|
AclItem *aip = PG_GETARG_ACLITEM_P(1);
|
1996-07-09 08:22:35 +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);
|
1996-07-09 08:22:35 +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);
|
|
|
|
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;
|
2022-07-03 22:49:12 +02:00
|
|
|
static const priv_map any_priv_map[] = {
|
|
|
|
{"SELECT", ACL_SELECT},
|
|
|
|
{"INSERT", ACL_INSERT},
|
|
|
|
{"UPDATE", ACL_UPDATE},
|
|
|
|
{"DELETE", ACL_DELETE},
|
|
|
|
{"TRUNCATE", ACL_TRUNCATE},
|
|
|
|
{"REFERENCES", ACL_REFERENCES},
|
|
|
|
{"TRIGGER", ACL_TRIGGER},
|
|
|
|
{"EXECUTE", ACL_EXECUTE},
|
|
|
|
{"USAGE", ACL_USAGE},
|
|
|
|
{"CREATE", ACL_CREATE},
|
|
|
|
{"TEMP", ACL_CREATE_TEMP},
|
|
|
|
{"TEMPORARY", ACL_CREATE_TEMP},
|
|
|
|
{"CONNECT", ACL_CONNECT},
|
|
|
|
{"SET", ACL_SET},
|
|
|
|
{"ALTER SYSTEM", ACL_ALTER_SYSTEM},
|
2022-12-14 02:33:28 +01:00
|
|
|
{"MAINTAIN", ACL_MAINTAIN},
|
2022-07-03 22:49:12 +02:00
|
|
|
{"RULE", 0}, /* ignore old RULE privileges */
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
priv = convert_any_priv_string(privtext, any_priv_map);
|
2003-06-11 11:23:55 +02:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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";
|
2022-04-06 19:24:33 +02:00
|
|
|
case ACL_SET:
|
|
|
|
return "SET";
|
|
|
|
case ACL_ALTER_SYSTEM:
|
|
|
|
return "ALTER SYSTEM";
|
2022-12-14 02:33:28 +01:00
|
|
|
case ACL_MAINTAIN:
|
|
|
|
return "MAINTAIN";
|
2009-12-05 22:43:36 +01:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized aclright: %d", aclright);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------
|
|
|
|
* Convert an aclitem[] to a table.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* aclexplode('{=r/joe,foo=a*w/joe}'::aclitem[])
|
|
|
|
*
|
|
|
|
* returns the table
|
|
|
|
*
|
|
|
|
* {{ OID(joe), 0::OID, 'SELECT', false },
|
|
|
|
* { OID(joe), OID(foo), 'INSERT', true },
|
|
|
|
* { OID(joe), OID(foo), 'UPDATE', false }}
|
|
|
|
*----------
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
aclexplode(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2010-01-12 03:39:22 +01:00
|
|
|
Acl *acl = PG_GETARG_ACL_P(0);
|
2009-12-05 22:43:36 +01:00
|
|
|
FuncCallContext *funcctx;
|
|
|
|
int *idx;
|
|
|
|
AclItem *aidat;
|
|
|
|
|
|
|
|
if (SRF_IS_FIRSTCALL())
|
|
|
|
{
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
MemoryContext oldcontext;
|
|
|
|
|
|
|
|
check_acl(acl);
|
|
|
|
|
|
|
|
funcctx = SRF_FIRSTCALL_INIT();
|
|
|
|
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* build tupdesc for result tuples (matches out parameters in pg_proc
|
|
|
|
* entry)
|
|
|
|
*/
|
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-01-12 03:39:22 +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]];
|
2022-11-24 23:05:22 +01:00
|
|
|
priv_bit = UINT64CONST(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];
|
2022-07-16 08:42:15 +02:00
|
|
|
bool nulls[4] = {0};
|
2009-12-05 22:43:36 +01:00
|
|
|
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
|
|
|
|
|
|
|
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-06-28 07:09:14 +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[] = {
|
|
|
|
{"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)},
|
2022-12-14 02:33:28 +01:00
|
|
|
{"MAINTAIN", ACL_MAINTAIN},
|
|
|
|
{"MAINTAIN WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_MAINTAIN)},
|
2009-02-06 22:15:12 +01:00
|
|
|
{"RULE", 0}, /* ignore old RULE privileges */
|
|
|
|
{"RULE WITH GRANT OPTION", 0},
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
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),
|
|
|
|
errmsg("\"%s\" is not a sequence",
|
|
|
|
text_to_cstring(sequencename))));
|
|
|
|
|
|
|
|
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_sequence_privilege_name
|
|
|
|
* Check user privileges on a sequence given
|
|
|
|
* text sequencename and text priv name.
|
|
|
|
* current_user is assumed
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_sequence_privilege_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
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),
|
|
|
|
errmsg("\"%s\" is not a sequence",
|
|
|
|
text_to_cstring(sequencename))));
|
|
|
|
|
|
|
|
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_sequence_privilege_name_id
|
|
|
|
* Check user privileges on a sequence given
|
|
|
|
* name usename, sequence oid, and text priv name.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_sequence_privilege_name_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Name username = PG_GETARG_NAME(0);
|
|
|
|
Oid sequenceoid = PG_GETARG_OID(1);
|
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),
|
|
|
|
errmsg("\"%s\" is not a sequence",
|
|
|
|
get_rel_name(sequenceoid))));
|
|
|
|
|
|
|
|
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_sequence_privilege_id
|
|
|
|
* Check user privileges on a sequence given
|
|
|
|
* sequence oid, and text priv name.
|
|
|
|
* current_user is assumed
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_sequence_privilege_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid sequenceoid = PG_GETARG_OID(0);
|
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),
|
|
|
|
errmsg("\"%s\" is not a sequence",
|
|
|
|
get_rel_name(sequenceoid))));
|
|
|
|
|
|
|
|
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_sequence_privilege_id_name
|
|
|
|
* Check user privileges on a sequence given
|
|
|
|
* roleid, text sequencename, and text priv name.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_sequence_privilege_id_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
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),
|
|
|
|
errmsg("\"%s\" is not a sequence",
|
|
|
|
text_to_cstring(sequencename))));
|
|
|
|
|
|
|
|
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_sequence_privilege_id_id
|
|
|
|
* Check user privileges on a sequence given
|
|
|
|
* roleid, sequence oid, and text priv name.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_sequence_privilege_id_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
|
|
|
Oid sequenceoid = PG_GETARG_OID(1);
|
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),
|
|
|
|
errmsg("\"%s\" is not a sequence",
|
|
|
|
get_rel_name(sequenceoid))));
|
|
|
|
|
|
|
|
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* convert_sequence_priv_string
|
|
|
|
* Convert text string to AclMode value.
|
|
|
|
*/
|
|
|
|
static AclMode
|
|
|
|
convert_sequence_priv_string(text *priv_type_text)
|
|
|
|
{
|
|
|
|
static const priv_map sequence_priv_map[] = {
|
|
|
|
{"USAGE", ACL_USAGE},
|
2017-11-26 18:49:40 +01:00
|
|
|
{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
|
2009-08-03 23:11:40 +02:00
|
|
|
{"SELECT", ACL_SELECT},
|
2017-11-26 18:49:40 +01:00
|
|
|
{"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
|
2009-08-03 23:11:40 +02:00
|
|
|
{"UPDATE", ACL_UPDATE},
|
2017-11-26 18:49:40 +01:00
|
|
|
{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
|
2009-08-03 23:11:40 +02:00
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
bool is_missing = false;
|
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
|
|
|
/*
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
* Check for column-level privileges first. This serves in part as a check
|
|
|
|
* on whether the column even exists, so we need to do it before checking
|
|
|
|
* table-level privilege.
|
2001-06-14 03:09:22 +02:00
|
|
|
*/
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
aclresult = pg_attribute_aclcheck_ext(tableoid, attnum, roleid,
|
|
|
|
mode, &is_missing);
|
|
|
|
if (aclresult == ACLCHECK_OK)
|
|
|
|
return 1;
|
|
|
|
else if (is_missing)
|
2009-02-06 22:15:12 +01:00
|
|
|
return -1;
|
2002-08-09 18:45:16 +02:00
|
|
|
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
/* Next check if we have the privilege at the table level */
|
|
|
|
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
|
2009-02-06 22:15:12 +01:00
|
|
|
if (aclresult == ACLCHECK_OK)
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
return 1;
|
|
|
|
else if (is_missing)
|
2009-02-06 22:15:12 +01:00
|
|
|
return -1;
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
else
|
|
|
|
return 0;
|
2009-02-06 22:15:12 +01:00
|
|
|
}
|
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[] = {
|
|
|
|
{"SELECT", ACL_SELECT},
|
|
|
|
{"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
|
|
|
|
{"INSERT", ACL_INSERT},
|
|
|
|
{"INSERT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_INSERT)},
|
|
|
|
{"UPDATE", ACL_UPDATE},
|
|
|
|
{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
|
|
|
|
{"REFERENCES", ACL_REFERENCES},
|
|
|
|
{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
return convert_any_priv_string(priv_type_text, column_priv_map);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_database_privilege variants
|
|
|
|
* These are all named "has_database_privilege" at the SQL level.
|
|
|
|
* They take various combinations of database name, database OID,
|
|
|
|
* user name, user OID, or implicit user = current_user.
|
|
|
|
*
|
|
|
|
* The result is a boolean value: true if user has the indicated
|
|
|
|
* privilege, false if not, or NULL if object doesn't exist.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_database_privilege_name_name
|
|
|
|
* Check user privileges on a database given
|
|
|
|
* name username, text databasename, and text priv name.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_database_privilege_name_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Name username = PG_GETARG_NAME(0);
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
|
2009-02-06 22:15:12 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
|
2009-02-06 22:15:12 +01:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
|
2009-02-06 22:15:12 +01:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
|
2009-02-06 22:15:12 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
|
2009-02-06 22:15:12 +01:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
|
2009-02-06 22:15:12 +01:00
|
|
|
|
|
|
|
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[] = {
|
|
|
|
{"CREATE", ACL_CREATE},
|
|
|
|
{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
|
|
|
|
{"TEMPORARY", ACL_CREATE_TEMP},
|
|
|
|
{"TEMPORARY WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
|
|
|
|
{"TEMP", ACL_CREATE_TEMP},
|
|
|
|
{"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
|
|
|
|
{"CONNECT", ACL_CONNECT},
|
|
|
|
{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
return convert_any_priv_string(priv_type_text, database_priv_map);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_foreign_data_wrapper_privilege variants
|
|
|
|
* These are all named "has_foreign_data_wrapper_privilege" at the SQL level.
|
|
|
|
* They take various combinations of foreign-data wrapper name,
|
|
|
|
* fdw OID, user name, user OID, or implicit user = current_user.
|
|
|
|
*
|
|
|
|
* The result is a boolean value: true if user has the indicated
|
|
|
|
* privilege, false if not.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_foreign_data_wrapper_privilege_name_name
|
|
|
|
* Check user privileges on a foreign-data wrapper given
|
|
|
|
* name username, text fdwname, and text priv name.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_foreign_data_wrapper_privilege_name_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Name username = PG_GETARG_NAME(0);
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
|
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
|
|
|
|
* 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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignDataWrapperRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
|
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
|
|
|
|
* 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignDataWrapperRelationId, 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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignDataWrapperRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
|
2009-02-06 22:15:12 +01:00
|
|
|
|
|
|
|
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[] = {
|
|
|
|
{"USAGE", ACL_USAGE},
|
|
|
|
{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ProcedureRelationId, 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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ProcedureRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ProcedureRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ProcedureRelationId, 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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ProcedureRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ProcedureRelationId, 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,
|
|
|
|
CStringGetDatum(funcname)));
|
|
|
|
|
|
|
|
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[] = {
|
|
|
|
{"EXECUTE", ACL_EXECUTE},
|
|
|
|
{"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(LanguageRelationId, 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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(LanguageRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(LanguageRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(LanguageRelationId, 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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(LanguageRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(LanguageRelationId, 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[] = {
|
|
|
|
{"USAGE", ACL_USAGE},
|
|
|
|
{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(NamespaceRelationId, 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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(NamespaceRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(NamespaceRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(NamespaceRelationId, 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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(NamespaceRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(NamespaceRelationId, 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[] = {
|
|
|
|
{"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}
|
|
|
|
};
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignServerRelationId, 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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignServerRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignServerRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignServerRelationId, serverid, roleid, mode);
|
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_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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignServerRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(ForeignServerRelationId, serverid, roleid, mode);
|
2009-02-06 22:15:12 +01:00
|
|
|
|
|
|
|
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[] = {
|
|
|
|
{"USAGE", ACL_USAGE},
|
|
|
|
{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TableSpaceRelationId, 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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TableSpaceRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TableSpaceRelationId, 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-06-28 07:09:14 +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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TableSpaceRelationId, 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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TableSpaceRelationId, 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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TableSpaceRelationId, 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[] = {
|
|
|
|
{"CREATE", ACL_CREATE},
|
|
|
|
{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
|
2011-12-19 23:05:19 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
|
2011-12-19 23:05:19 +01:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
|
2011-12-19 23:05:19 +01:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
|
2011-12-19 23:05:19 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
|
2011-12-19 23:05:19 +01:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2022-11-13 08:11:17 +01:00
|
|
|
aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
|
2011-12-19 23:05:19 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-04-06 19:24:33 +02:00
|
|
|
/*
|
|
|
|
* has_parameter_privilege variants
|
|
|
|
* These are all named "has_parameter_privilege" at the SQL level.
|
|
|
|
* They take various combinations of parameter name with
|
|
|
|
* user name, user OID, or implicit user = current_user.
|
|
|
|
*
|
|
|
|
* The result is a boolean value: true if user has been granted
|
|
|
|
* the indicated privilege or false if not.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_param_priv_byname
|
|
|
|
*
|
|
|
|
* Helper function to check user privileges on a parameter given the
|
|
|
|
* role by Oid, parameter by text name, and privileges as AclMode.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
has_param_priv_byname(Oid roleid, const text *parameter, AclMode priv)
|
|
|
|
{
|
|
|
|
char *paramstr = text_to_cstring(parameter);
|
|
|
|
|
|
|
|
return pg_parameter_aclcheck(paramstr, roleid, priv) == ACLCHECK_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_parameter_privilege_name_name
|
|
|
|
* Check user privileges on a parameter given name username, text
|
|
|
|
* parameter, and text priv name.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_parameter_privilege_name_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Name username = PG_GETARG_NAME(0);
|
|
|
|
text *parameter = PG_GETARG_TEXT_PP(1);
|
|
|
|
AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
|
|
|
|
Oid roleid = get_role_oid_or_public(NameStr(*username));
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_parameter_privilege_name
|
|
|
|
* Check user privileges on a parameter given text parameter and text priv
|
|
|
|
* name. current_user is assumed
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_parameter_privilege_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
text *parameter = PG_GETARG_TEXT_PP(0);
|
|
|
|
AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(1));
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(has_param_priv_byname(GetUserId(), parameter, priv));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_parameter_privilege_id_name
|
|
|
|
* Check user privileges on a parameter given roleid, text parameter, and
|
|
|
|
* text priv name.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_parameter_privilege_id_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
|
|
|
text *parameter = PG_GETARG_TEXT_PP(1);
|
|
|
|
AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Support routines for has_parameter_privilege family.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* convert_parameter_priv_string
|
|
|
|
* Convert text string to AclMode value.
|
|
|
|
*/
|
|
|
|
static AclMode
|
|
|
|
convert_parameter_priv_string(text *priv_text)
|
|
|
|
{
|
|
|
|
static const priv_map parameter_priv_map[] = {
|
|
|
|
{"SET", ACL_SET},
|
|
|
|
{"SET WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET)},
|
|
|
|
{"ALTER SYSTEM", ACL_ALTER_SYSTEM},
|
|
|
|
{"ALTER SYSTEM WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_ALTER_SYSTEM)},
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
return convert_any_priv_string(priv_text, parameter_priv_map);
|
|
|
|
}
|
2011-12-19 23:05:19 +01:00
|
|
|
|
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-07-26 02:04:19 +02:00
|
|
|
Oid roleid;
|
|
|
|
AclMode mode;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
roleid = GetUserId();
|
|
|
|
mode = convert_role_priv_string(priv_type_text);
|
|
|
|
|
|
|
|
aclresult = pg_role_aclcheck(roleoid, roleid, mode);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pg_has_role_id_name
|
|
|
|
* Check user privileges on a role given
|
|
|
|
* roleid, name rolename, and text priv name.
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
pg_has_role_id_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid roleid = PG_GETARG_OID(0);
|
|
|
|
Name rolename = PG_GETARG_NAME(1);
|
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
|
2023-05-02 05:23:08 +02:00
|
|
|
* (has_privs_of_role), MEMBER to denote is_member, and MEMBER WITH GRANT
|
|
|
|
* (or ADMIN) OPTION to denote is_admin. There is no ACL bit corresponding
|
2005-07-26 18:38:29 +02:00
|
|
|
* 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[] = {
|
|
|
|
{"USAGE", ACL_USAGE},
|
|
|
|
{"MEMBER", ACL_CREATE},
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
{"SET", ACL_SET},
|
2009-02-06 22:15:12 +01:00
|
|
|
{"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)},
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
{"SET WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
|
|
|
|
{"SET WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
|
2009-02-06 22:15:12 +01:00
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
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
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
if (mode & ACL_SET)
|
|
|
|
{
|
|
|
|
if (member_can_set_role(roleid, role_oid))
|
|
|
|
return ACLCHECK_OK;
|
|
|
|
}
|
2005-07-26 18:38:29 +02:00
|
|
|
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())
|
|
|
|
{
|
2021-03-26 18:42:17 +01:00
|
|
|
cached_db_hash =
|
|
|
|
GetSysCacheHashValue1(DATABASEOID,
|
|
|
|
ObjectIdGetDatum(MyDatabaseId));
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
/*
|
2020-12-25 19:41:59 +01:00
|
|
|
* In normal mode, set a callback on any syscache invalidation of rows
|
2022-08-25 16:06:02 +02:00
|
|
|
* of pg_auth_members (for roles_is_member_of()) pg_database (for
|
|
|
|
* roles_is_member_of())
|
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);
|
2020-12-25 19:41:59 +01:00
|
|
|
CacheRegisterSyscacheCallback(AUTHOID,
|
|
|
|
RoleMembershipCacheCallback,
|
|
|
|
(Datum) 0);
|
2021-03-26 18:42:17 +01:00
|
|
|
CacheRegisterSyscacheCallback(DATABASEOID,
|
|
|
|
RoleMembershipCacheCallback,
|
|
|
|
(Datum) 0);
|
2005-06-28 07:09:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-06-29 22:34:15 +02:00
|
|
|
* RoleMembershipCacheCallback
|
2005-06-28 07:09:14 +02:00
|
|
|
* Syscache inval callback function
|
|
|
|
*/
|
|
|
|
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
|
|
|
{
|
2021-03-26 18:42:17 +01:00
|
|
|
if (cacheid == DATABASEOID &&
|
|
|
|
hashvalue != cached_db_hash &&
|
|
|
|
hashvalue != 0)
|
|
|
|
{
|
|
|
|
return; /* ignore pg_database changes for other DBs */
|
|
|
|
}
|
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
/* Force membership caches to be recomputed on next use */
|
2021-03-26 18:42:16 +01:00
|
|
|
cached_role[ROLERECURSE_MEMBERS] = InvalidOid;
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
cached_role[ROLERECURSE_PRIVS] = InvalidOid;
|
|
|
|
cached_role[ROLERECURSE_SETROLE] = InvalidOid;
|
2005-07-26 18:38:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-03-26 18:42:16 +01:00
|
|
|
* Get a list of roles that the specified roleid is a member of
|
2005-07-26 18:38:29 +02:00
|
|
|
*
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
* Type ROLERECURSE_MEMBERS recurses through all grants; ROLERECURSE_PRIVS
|
2023-04-18 03:23:23 +02:00
|
|
|
* recurses only through inheritable grants; and ROLERECURSE_SETROLE recurses
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
* only through grants with set_option.
|
2005-07-26 18:38:29 +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
|
2021-03-26 18:42:16 +01:00
|
|
|
* the next call of roles_is_member_of()!
|
2005-10-10 20:49:04 +02:00
|
|
|
*
|
|
|
|
* For the benefit of select_best_grantor, the result is defined to be
|
|
|
|
* in breadth-first order, ie, closer relationships earlier.
|
Make role grant system more consistent with other privileges.
Previously, membership of role A in role B could be recorded in the
catalog tables only once. This meant that a new grant of role A to
role B would overwrite the previous grant. For other object types, a
new grant of permission on an object - in this case role A - exists
along side the existing grant provided that the grantor is different.
Either grant can be revoked independently of the other, and
permissions remain so long as at least one grant remains. Make role
grants work similarly.
Previously, when granting membership in a role, the superuser could
specify any role whatsoever as the grantor, but for other object types,
the grantor of record must be either the owner of the object, or a
role that currently has privileges to perform a similar GRANT.
Implement the same scheme for role grants, treating the bootstrap
superuser as the role owner since roles do not have owners. This means
that attempting to revoke a grant, or admin option on a grant, can now
fail if there are dependent privileges, and that CASCADE can be used
to revoke these. It also means that you can't grant ADMIN OPTION on
a role back to a user who granted it directly or indirectly to you,
similar to how you can't give WITH GRANT OPTION on a privilege back
to a role which granted it directly or indirectly to you.
Previously, only the superuser could specify GRANTED BY with a user
other than the current user. Relax that rule to allow the grantor
to be any role whose privileges the current user posseses. This
doesn't improve compatibility with what we do for other object types,
where support for GRANTED BY is entirely vestigial, but it makes this
feature more usable and seems to make sense to change at the same time
we're changing related behaviors.
Along the way, fix "ALTER GROUP group_name ADD USER user_name" to
require the same privileges as "GRANT group_name TO user_name".
Previously, CREATEROLE privileges were sufficient for either, but
only the former form was permissible with ADMIN OPTION on the role.
Now, either CREATEROLE or ADMIN OPTION on the role suffices for
either spelling.
Patch by me, reviewed by Stephen Frost.
Discussion: http://postgr.es/m/CA+TgmoaFr-RZeQ+WoQ5nKPv97oT9+aDgK_a5+qWHSgbDsMp1Vg@mail.gmail.com
2022-08-22 17:35:17 +02:00
|
|
|
*
|
|
|
|
* If admin_of is not InvalidOid, this function sets *admin_role, either
|
|
|
|
* to the OID of the first role in the result list that directly possesses
|
|
|
|
* ADMIN OPTION on the role corresponding to admin_of, or to InvalidOid if
|
|
|
|
* there is no such role.
|
2005-07-26 18:38:29 +02:00
|
|
|
*/
|
2005-10-10 20:49:04 +02:00
|
|
|
static List *
|
2021-03-26 18:42:16 +01:00
|
|
|
roles_is_member_of(Oid roleid, enum RoleRecurseType type,
|
Make role grant system more consistent with other privileges.
Previously, membership of role A in role B could be recorded in the
catalog tables only once. This meant that a new grant of role A to
role B would overwrite the previous grant. For other object types, a
new grant of permission on an object - in this case role A - exists
along side the existing grant provided that the grantor is different.
Either grant can be revoked independently of the other, and
permissions remain so long as at least one grant remains. Make role
grants work similarly.
Previously, when granting membership in a role, the superuser could
specify any role whatsoever as the grantor, but for other object types,
the grantor of record must be either the owner of the object, or a
role that currently has privileges to perform a similar GRANT.
Implement the same scheme for role grants, treating the bootstrap
superuser as the role owner since roles do not have owners. This means
that attempting to revoke a grant, or admin option on a grant, can now
fail if there are dependent privileges, and that CASCADE can be used
to revoke these. It also means that you can't grant ADMIN OPTION on
a role back to a user who granted it directly or indirectly to you,
similar to how you can't give WITH GRANT OPTION on a privilege back
to a role which granted it directly or indirectly to you.
Previously, only the superuser could specify GRANTED BY with a user
other than the current user. Relax that rule to allow the grantor
to be any role whose privileges the current user posseses. This
doesn't improve compatibility with what we do for other object types,
where support for GRANTED BY is entirely vestigial, but it makes this
feature more usable and seems to make sense to change at the same time
we're changing related behaviors.
Along the way, fix "ALTER GROUP group_name ADD USER user_name" to
require the same privileges as "GRANT group_name TO user_name".
Previously, CREATEROLE privileges were sufficient for either, but
only the former form was permissible with ADMIN OPTION on the role.
Now, either CREATEROLE or ADMIN OPTION on the role suffices for
either spelling.
Patch by me, reviewed by Stephen Frost.
Discussion: http://postgr.es/m/CA+TgmoaFr-RZeQ+WoQ5nKPv97oT9+aDgK_a5+qWHSgbDsMp1Vg@mail.gmail.com
2022-08-22 17:35:17 +02:00
|
|
|
Oid admin_of, Oid *admin_role)
|
2005-07-26 18:38:29 +02:00
|
|
|
{
|
2021-03-26 18:42:17 +01:00
|
|
|
Oid dba;
|
2005-07-26 18:38:29 +02:00
|
|
|
List *roles_list;
|
|
|
|
ListCell *l;
|
2021-03-26 18:42:16 +01:00
|
|
|
List *new_cached_roles;
|
2005-07-26 18:38:29 +02:00
|
|
|
MemoryContext oldctx;
|
|
|
|
|
Make role grant system more consistent with other privileges.
Previously, membership of role A in role B could be recorded in the
catalog tables only once. This meant that a new grant of role A to
role B would overwrite the previous grant. For other object types, a
new grant of permission on an object - in this case role A - exists
along side the existing grant provided that the grantor is different.
Either grant can be revoked independently of the other, and
permissions remain so long as at least one grant remains. Make role
grants work similarly.
Previously, when granting membership in a role, the superuser could
specify any role whatsoever as the grantor, but for other object types,
the grantor of record must be either the owner of the object, or a
role that currently has privileges to perform a similar GRANT.
Implement the same scheme for role grants, treating the bootstrap
superuser as the role owner since roles do not have owners. This means
that attempting to revoke a grant, or admin option on a grant, can now
fail if there are dependent privileges, and that CASCADE can be used
to revoke these. It also means that you can't grant ADMIN OPTION on
a role back to a user who granted it directly or indirectly to you,
similar to how you can't give WITH GRANT OPTION on a privilege back
to a role which granted it directly or indirectly to you.
Previously, only the superuser could specify GRANTED BY with a user
other than the current user. Relax that rule to allow the grantor
to be any role whose privileges the current user posseses. This
doesn't improve compatibility with what we do for other object types,
where support for GRANTED BY is entirely vestigial, but it makes this
feature more usable and seems to make sense to change at the same time
we're changing related behaviors.
Along the way, fix "ALTER GROUP group_name ADD USER user_name" to
require the same privileges as "GRANT group_name TO user_name".
Previously, CREATEROLE privileges were sufficient for either, but
only the former form was permissible with ADMIN OPTION on the role.
Now, either CREATEROLE or ADMIN OPTION on the role suffices for
either spelling.
Patch by me, reviewed by Stephen Frost.
Discussion: http://postgr.es/m/CA+TgmoaFr-RZeQ+WoQ5nKPv97oT9+aDgK_a5+qWHSgbDsMp1Vg@mail.gmail.com
2022-08-22 17:35:17 +02:00
|
|
|
Assert(OidIsValid(admin_of) == PointerIsValid(admin_role));
|
|
|
|
if (admin_role != NULL)
|
|
|
|
*admin_role = InvalidOid;
|
2021-03-26 18:42:16 +01:00
|
|
|
|
|
|
|
/* If cache is valid and ADMIN OPTION not sought, just return the list */
|
|
|
|
if (cached_role[type] == roleid && !OidIsValid(admin_of) &&
|
|
|
|
OidIsValid(cached_role[type]))
|
|
|
|
return cached_roles[type];
|
2005-07-26 18:38:29 +02:00
|
|
|
|
2021-03-26 18:42:17 +01:00
|
|
|
/*
|
|
|
|
* Role expansion happens in a non-database backend when guc.c checks
|
2021-04-10 21:01:41 +02:00
|
|
|
* ROLE_PG_READ_ALL_SETTINGS for a physical walsender SHOW command. In
|
|
|
|
* that case, no role gets pg_database_owner.
|
2021-03-26 18:42:17 +01:00
|
|
|
*/
|
|
|
|
if (!OidIsValid(MyDatabaseId))
|
|
|
|
dba = InvalidOid;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
HeapTuple dbtup;
|
|
|
|
|
|
|
|
dbtup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
|
|
|
|
if (!HeapTupleIsValid(dbtup))
|
|
|
|
elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
|
|
|
|
dba = ((Form_pg_database) GETSTRUCT(dbtup))->datdba;
|
|
|
|
ReleaseSysCache(dbtup);
|
|
|
|
}
|
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
/*
|
2005-10-10 20:49:04 +02:00
|
|
|
* Find all the roles that roleid is a member of, including multi-level
|
2005-07-26 18:38:29 +02:00
|
|
|
* recursion. The role itself will always be the first element of the
|
|
|
|
* resulting list.
|
|
|
|
*
|
|
|
|
* Each element of the list is scanned to see if it adds any indirect
|
|
|
|
* memberships. We can use a single list as both the record of
|
|
|
|
* already-found memberships and the agenda of roles yet to be scanned.
|
|
|
|
* This is a bit tricky but works because the foreach() macro doesn't
|
|
|
|
* fetch the next list element until the bottom of the loop.
|
|
|
|
*/
|
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)
|
|
|
|
{
|
|
|
|
Oid memberid = lfirst_oid(l);
|
|
|
|
CatCList *memlist;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* 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;
|
2022-08-25 16:06:02 +02:00
|
|
|
Form_pg_auth_members form = (Form_pg_auth_members) GETSTRUCT(tup);
|
|
|
|
Oid otherid = form->roleid;
|
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
/*
|
2021-03-26 18:42:16 +01:00
|
|
|
* While otherid==InvalidOid shouldn't appear in the catalog, the
|
|
|
|
* OidIsValid() avoids crashing if that arises.
|
2005-07-26 18:38:29 +02:00
|
|
|
*/
|
2022-08-25 16:06:02 +02:00
|
|
|
if (otherid == admin_of && form->admin_option &&
|
Make role grant system more consistent with other privileges.
Previously, membership of role A in role B could be recorded in the
catalog tables only once. This meant that a new grant of role A to
role B would overwrite the previous grant. For other object types, a
new grant of permission on an object - in this case role A - exists
along side the existing grant provided that the grantor is different.
Either grant can be revoked independently of the other, and
permissions remain so long as at least one grant remains. Make role
grants work similarly.
Previously, when granting membership in a role, the superuser could
specify any role whatsoever as the grantor, but for other object types,
the grantor of record must be either the owner of the object, or a
role that currently has privileges to perform a similar GRANT.
Implement the same scheme for role grants, treating the bootstrap
superuser as the role owner since roles do not have owners. This means
that attempting to revoke a grant, or admin option on a grant, can now
fail if there are dependent privileges, and that CASCADE can be used
to revoke these. It also means that you can't grant ADMIN OPTION on
a role back to a user who granted it directly or indirectly to you,
similar to how you can't give WITH GRANT OPTION on a privilege back
to a role which granted it directly or indirectly to you.
Previously, only the superuser could specify GRANTED BY with a user
other than the current user. Relax that rule to allow the grantor
to be any role whose privileges the current user posseses. This
doesn't improve compatibility with what we do for other object types,
where support for GRANTED BY is entirely vestigial, but it makes this
feature more usable and seems to make sense to change at the same time
we're changing related behaviors.
Along the way, fix "ALTER GROUP group_name ADD USER user_name" to
require the same privileges as "GRANT group_name TO user_name".
Previously, CREATEROLE privileges were sufficient for either, but
only the former form was permissible with ADMIN OPTION on the role.
Now, either CREATEROLE or ADMIN OPTION on the role suffices for
either spelling.
Patch by me, reviewed by Stephen Frost.
Discussion: http://postgr.es/m/CA+TgmoaFr-RZeQ+WoQ5nKPv97oT9+aDgK_a5+qWHSgbDsMp1Vg@mail.gmail.com
2022-08-22 17:35:17 +02:00
|
|
|
OidIsValid(admin_of) && !OidIsValid(*admin_role))
|
|
|
|
*admin_role = memberid;
|
2005-06-29 22:34:15 +02:00
|
|
|
|
2022-08-30 14:32:35 +02:00
|
|
|
/* If we're supposed to ignore non-heritable grants, do so. */
|
|
|
|
if (type == ROLERECURSE_PRIVS && !form->inherit_option)
|
|
|
|
continue;
|
|
|
|
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
/* If we're supposed to ignore non-SET grants, do so. */
|
|
|
|
if (type == ROLERECURSE_SETROLE && !form->set_option)
|
|
|
|
continue;
|
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
/*
|
|
|
|
* Even though there shouldn't be any loops in the membership
|
|
|
|
* graph, we must test for having already seen this role. It is
|
|
|
|
* legal for instance to have both A->B and A->C->B.
|
|
|
|
*/
|
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);
|
2021-03-26 18:42:17 +01:00
|
|
|
|
|
|
|
/* implement pg_database_owner implicit membership */
|
|
|
|
if (memberid == dba && OidIsValid(dba))
|
|
|
|
roles_list = list_append_unique_oid(roles_list,
|
2021-04-10 21:01:41 +02:00
|
|
|
ROLE_PG_DATABASE_OWNER);
|
2005-06-28 07:09:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
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);
|
2021-03-26 18:42:16 +01:00
|
|
|
new_cached_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
|
|
|
|
*/
|
2021-03-26 18:42:16 +01:00
|
|
|
cached_role[type] = InvalidOid; /* just paranoia */
|
|
|
|
list_free(cached_roles[type]);
|
|
|
|
cached_roles[type] = new_cached_roles;
|
|
|
|
cached_role[type] = roleid;
|
2005-06-28 07:09:14 +02:00
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
/* And now we can return the answer */
|
2021-03-26 18:42:16 +01:00
|
|
|
return cached_roles[type];
|
2005-10-10 20:49:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Does member have the privileges of role (directly or indirectly)?
|
|
|
|
*
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
* This is defined not to recurse through grants that are not inherited,
|
|
|
|
* and only inherited grants confer the associated privileges automatically.
|
|
|
|
*
|
|
|
|
* See also member_can_set_role, below.
|
2005-10-10 20:49:04 +02:00
|
|
|
*/
|
|
|
|
bool
|
|
|
|
has_privs_of_role(Oid member, Oid role)
|
|
|
|
{
|
|
|
|
/* Fast path for simple case */
|
|
|
|
if (member == role)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Superusers have every privilege, so are part of every role */
|
|
|
|
if (superuser_arg(member))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find all the roles that member has the privileges of, including
|
|
|
|
* multi-level recursion, then see if target role is any one of them.
|
|
|
|
*/
|
2021-03-26 18:42:16 +01:00
|
|
|
return list_member_oid(roles_is_member_of(member, ROLERECURSE_PRIVS,
|
|
|
|
InvalidOid, NULL),
|
|
|
|
role);
|
2005-10-10 20:49:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
* Can member use SET ROLE to this role?
|
2005-10-10 20:49:04 +02:00
|
|
|
*
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
* There must be a chain of grants from 'member' to 'role' each of which
|
|
|
|
* permits SET ROLE; that is, each of which has set_option = true.
|
2022-03-28 21:10:04 +02:00
|
|
|
*
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
* It doesn't matter whether the grants are inheritable. That's a separate
|
|
|
|
* question; see has_privs_of_role.
|
|
|
|
*
|
|
|
|
* This function should be used to determine whether the session user can
|
|
|
|
* use SET ROLE to become the target user. We also use it to determine whether
|
|
|
|
* the session user can change an existing object to be owned by the target
|
|
|
|
* user, or create new objects owned by the target user.
|
2005-10-10 20:49:04 +02:00
|
|
|
*/
|
|
|
|
bool
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
member_can_set_role(Oid member, Oid role)
|
2005-10-10 20:49:04 +02:00
|
|
|
{
|
|
|
|
/* Fast path for simple case */
|
|
|
|
if (member == role)
|
|
|
|
return true;
|
|
|
|
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
/* Superusers have every privilege, so can always SET ROLE */
|
2005-10-10 20:49:04 +02:00
|
|
|
if (superuser_arg(member))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
* Find all the roles that member can access via SET ROLE, including
|
|
|
|
* multi-level recursion, then see if target role is any one of them.
|
2005-10-10 20:49:04 +02:00
|
|
|
*/
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
return list_member_oid(roles_is_member_of(member, ROLERECURSE_SETROLE,
|
2021-03-26 18:42:16 +01:00
|
|
|
InvalidOid, NULL),
|
|
|
|
role);
|
2005-06-29 22:34:15 +02:00
|
|
|
}
|
|
|
|
|
2005-07-14 23:46:30 +02:00
|
|
|
/*
|
2023-04-18 03:23:23 +02:00
|
|
|
* Permission violation error unless able to SET ROLE to target role.
|
2005-07-14 23:46:30 +02:00
|
|
|
*/
|
|
|
|
void
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
check_can_set_role(Oid member, Oid role)
|
2005-07-14 23:46:30 +02:00
|
|
|
{
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
if (!member_can_set_role(member, role))
|
2005-07-14 23:46:30 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
errmsg("must be able to SET ROLE \"%s\"",
|
2015-05-09 19:06:49 +02:00
|
|
|
GetUserNameFromId(role, false))));
|
2005-07-14 23:46:30 +02:00
|
|
|
}
|
|
|
|
|
Add a SET option to the GRANT command.
Similar to how the INHERIT option controls whether or not the
permissions of the granted role are automatically available to the
grantee, the new SET permission controls whether or not the grantee
may use the SET ROLE command to assume the privileges of the granted
role.
In addition, the new SET permission controls whether or not it
is possible to transfer ownership of objects to the target role
or to create new objects owned by the target role using commands
such as CREATE DATABASE .. OWNER. We could alternatively have made
this controlled by the INHERIT option, or allow it when either
option is given. An advantage of this approach is that if you
are granted a predefined role with INHERIT TRUE, SET FALSE, you
can't go and create objects owned by that role.
The underlying theory here is that the ability to create objects
as a target role is not a privilege per se, and thus does not
depend on whether you inherit the target role's privileges. However,
it's surely something you could do anyway if you could SET ROLE
to the target role, and thus making it contingent on whether you
have that ability is reasonable.
Design review by Nathan Bossat, Wolfgang Walther, Jeff Davis,
Peter Eisentraut, and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmob+zDSRS6JXYrgq0NWdzCXuTNzT5eK54Dn2hhgt17nm8A@mail.gmail.com
2022-11-18 18:32:50 +01:00
|
|
|
/*
|
|
|
|
* Is member a member of role (directly or indirectly)?
|
|
|
|
*
|
|
|
|
* This is defined to recurse through grants whether they are inherited or not.
|
|
|
|
*
|
|
|
|
* Do not use this for privilege checking, instead use has_privs_of_role().
|
|
|
|
* Don't use it for determining whether it's possible to SET ROLE to some
|
|
|
|
* other role; for that, use member_can_set_role(). And don't use it for
|
|
|
|
* determining whether it's OK to create an object owned by some other role:
|
|
|
|
* use member_can_set_role() for that, too.
|
|
|
|
*
|
|
|
|
* In short, calling this function is the wrong thing to do nearly everywhere.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
is_member_of_role(Oid member, Oid role)
|
|
|
|
{
|
|
|
|
/* Fast path for simple case */
|
|
|
|
if (member == role)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Superusers have every privilege, so are part of every role */
|
|
|
|
if (superuser_arg(member))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find all the roles that member is a member of, including multi-level
|
|
|
|
* recursion, then see if target role is any one of them.
|
|
|
|
*/
|
|
|
|
return list_member_oid(roles_is_member_of(member, ROLERECURSE_MEMBERS,
|
|
|
|
InvalidOid, NULL),
|
|
|
|
role);
|
|
|
|
}
|
|
|
|
|
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.
|
2022-03-28 21:10:04 +02:00
|
|
|
*
|
|
|
|
* Do not use this for privilege checking, instead use has_privs_of_role()
|
2005-11-04 18:25:15 +01:00
|
|
|
*/
|
|
|
|
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.
|
|
|
|
*/
|
2021-03-26 18:42:16 +01:00
|
|
|
return list_member_oid(roles_is_member_of(member, ROLERECURSE_MEMBERS,
|
|
|
|
InvalidOid, NULL),
|
|
|
|
role);
|
2005-11-04 18:25:15 +01:00
|
|
|
}
|
|
|
|
|
2005-06-29 22:34:15 +02:00
|
|
|
|
|
|
|
/*
|
2014-02-17 15:33:31 +01:00
|
|
|
* Is member an admin of role? That is, is member the role itself (subject to
|
|
|
|
* 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)
|
|
|
|
{
|
Make role grant system more consistent with other privileges.
Previously, membership of role A in role B could be recorded in the
catalog tables only once. This meant that a new grant of role A to
role B would overwrite the previous grant. For other object types, a
new grant of permission on an object - in this case role A - exists
along side the existing grant provided that the grantor is different.
Either grant can be revoked independently of the other, and
permissions remain so long as at least one grant remains. Make role
grants work similarly.
Previously, when granting membership in a role, the superuser could
specify any role whatsoever as the grantor, but for other object types,
the grantor of record must be either the owner of the object, or a
role that currently has privileges to perform a similar GRANT.
Implement the same scheme for role grants, treating the bootstrap
superuser as the role owner since roles do not have owners. This means
that attempting to revoke a grant, or admin option on a grant, can now
fail if there are dependent privileges, and that CASCADE can be used
to revoke these. It also means that you can't grant ADMIN OPTION on
a role back to a user who granted it directly or indirectly to you,
similar to how you can't give WITH GRANT OPTION on a privilege back
to a role which granted it directly or indirectly to you.
Previously, only the superuser could specify GRANTED BY with a user
other than the current user. Relax that rule to allow the grantor
to be any role whose privileges the current user posseses. This
doesn't improve compatibility with what we do for other object types,
where support for GRANTED BY is entirely vestigial, but it makes this
feature more usable and seems to make sense to change at the same time
we're changing related behaviors.
Along the way, fix "ALTER GROUP group_name ADD USER user_name" to
require the same privileges as "GRANT group_name TO user_name".
Previously, CREATEROLE privileges were sufficient for either, but
only the former form was permissible with ADMIN OPTION on the role.
Now, either CREATEROLE or ADMIN OPTION on the role suffices for
either spelling.
Patch by me, reviewed by Stephen Frost.
Discussion: http://postgr.es/m/CA+TgmoaFr-RZeQ+WoQ5nKPv97oT9+aDgK_a5+qWHSgbDsMp1Vg@mail.gmail.com
2022-08-22 17:35:17 +02:00
|
|
|
Oid admin_role;
|
2005-06-29 22:34:15 +02:00
|
|
|
|
2005-07-26 02:04:19 +02:00
|
|
|
if (superuser_arg(member))
|
|
|
|
return true;
|
|
|
|
|
Remove the ability of a role to administer itself.
Commit f9fd1764615ed5d85fab703b0ffb0c323fe7dfd5 effectively gave
every role ADMIN OPTION on itself. However, this appears to be
something that happened accidentally as a result of refactoring
work rather than an intentional decision. Almost a decade later,
it was discovered that this was a security vulnerability. As a
result, commit fea164a72a7bfd50d77ba5fb418d357f8f2bb7d0 restricted
this implicit ADMIN OPTION privilege to be exercisable only when
the role being administered is the same as the session user and
when no security-restricted operation is in progress. That
commit also documented the existence of this implicit privilege
for what seems to be the first time.
The effect of the privilege is to allow a login role to grant
the privileges of that role, and optionally ADMIN OPTION on it,
to some other role. That's an unusual thing to do, because generally
membership is granted in roles used as groups, rather than roles
used as users. Therefore, it does not seem likely that removing
the privilege will break things for many PostgreSQL users.
However, it will make it easier to reason about the permissions
system. This is the only case where a user who has not been given any
special permission (superuser, or ADMIN OPTION on some role) can
modify role membership, so removing it makes things more consistent.
For example, if a superuser sets up role A and B and grants A to B
but no other privileges to anyone, she can now be sure that no one
else will be able to revoke that grant. Without this change, that
would have been true only if A was a non-login role.
Patch by me. Reviewed by Tom Lane and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmoawdt03kbA+dNyBcNWJpRxu0f4X=69Y3+DkXXZqmwMDLg@mail.gmail.com
2022-03-28 19:38:13 +02:00
|
|
|
/* By policy, a role cannot have WITH ADMIN OPTION on itself. */
|
2014-02-17 15:33:31 +01:00
|
|
|
if (member == role)
|
Remove the ability of a role to administer itself.
Commit f9fd1764615ed5d85fab703b0ffb0c323fe7dfd5 effectively gave
every role ADMIN OPTION on itself. However, this appears to be
something that happened accidentally as a result of refactoring
work rather than an intentional decision. Almost a decade later,
it was discovered that this was a security vulnerability. As a
result, commit fea164a72a7bfd50d77ba5fb418d357f8f2bb7d0 restricted
this implicit ADMIN OPTION privilege to be exercisable only when
the role being administered is the same as the session user and
when no security-restricted operation is in progress. That
commit also documented the existence of this implicit privilege
for what seems to be the first time.
The effect of the privilege is to allow a login role to grant
the privileges of that role, and optionally ADMIN OPTION on it,
to some other role. That's an unusual thing to do, because generally
membership is granted in roles used as groups, rather than roles
used as users. Therefore, it does not seem likely that removing
the privilege will break things for many PostgreSQL users.
However, it will make it easier to reason about the permissions
system. This is the only case where a user who has not been given any
special permission (superuser, or ADMIN OPTION on some role) can
modify role membership, so removing it makes things more consistent.
For example, if a superuser sets up role A and B and grants A to B
but no other privileges to anyone, she can now be sure that no one
else will be able to revoke that grant. Without this change, that
would have been true only if A was a non-login role.
Patch by me. Reviewed by Tom Lane and Stephen Frost.
Discussion: http://postgr.es/m/CA+Tgmoawdt03kbA+dNyBcNWJpRxu0f4X=69Y3+DkXXZqmwMDLg@mail.gmail.com
2022-03-28 19:38:13 +02:00
|
|
|
return false;
|
2014-02-17 15:33:31 +01:00
|
|
|
|
Make role grant system more consistent with other privileges.
Previously, membership of role A in role B could be recorded in the
catalog tables only once. This meant that a new grant of role A to
role B would overwrite the previous grant. For other object types, a
new grant of permission on an object - in this case role A - exists
along side the existing grant provided that the grantor is different.
Either grant can be revoked independently of the other, and
permissions remain so long as at least one grant remains. Make role
grants work similarly.
Previously, when granting membership in a role, the superuser could
specify any role whatsoever as the grantor, but for other object types,
the grantor of record must be either the owner of the object, or a
role that currently has privileges to perform a similar GRANT.
Implement the same scheme for role grants, treating the bootstrap
superuser as the role owner since roles do not have owners. This means
that attempting to revoke a grant, or admin option on a grant, can now
fail if there are dependent privileges, and that CASCADE can be used
to revoke these. It also means that you can't grant ADMIN OPTION on
a role back to a user who granted it directly or indirectly to you,
similar to how you can't give WITH GRANT OPTION on a privilege back
to a role which granted it directly or indirectly to you.
Previously, only the superuser could specify GRANTED BY with a user
other than the current user. Relax that rule to allow the grantor
to be any role whose privileges the current user posseses. This
doesn't improve compatibility with what we do for other object types,
where support for GRANTED BY is entirely vestigial, but it makes this
feature more usable and seems to make sense to change at the same time
we're changing related behaviors.
Along the way, fix "ALTER GROUP group_name ADD USER user_name" to
require the same privileges as "GRANT group_name TO user_name".
Previously, CREATEROLE privileges were sufficient for either, but
only the former form was permissible with ADMIN OPTION on the role.
Now, either CREATEROLE or ADMIN OPTION on the role suffices for
either spelling.
Patch by me, reviewed by Stephen Frost.
Discussion: http://postgr.es/m/CA+TgmoaFr-RZeQ+WoQ5nKPv97oT9+aDgK_a5+qWHSgbDsMp1Vg@mail.gmail.com
2022-08-22 17:35:17 +02:00
|
|
|
(void) roles_is_member_of(member, ROLERECURSE_MEMBERS, role, &admin_role);
|
|
|
|
return OidIsValid(admin_role);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find a role whose privileges "member" inherits which has ADMIN OPTION
|
|
|
|
* on "role", ignoring super-userness.
|
|
|
|
*
|
|
|
|
* There might be more than one such role; prefer one which involves fewer
|
|
|
|
* hops. That is, if member has ADMIN OPTION, prefer that over all other
|
|
|
|
* options; if not, prefer a role from which member inherits more directly
|
|
|
|
* over more indirect inheritance.
|
|
|
|
*/
|
|
|
|
Oid
|
|
|
|
select_best_admin(Oid member, Oid role)
|
|
|
|
{
|
|
|
|
Oid admin_role;
|
|
|
|
|
|
|
|
/* By policy, a role cannot have WITH ADMIN OPTION on itself. */
|
|
|
|
if (member == role)
|
|
|
|
return InvalidOid;
|
|
|
|
|
|
|
|
(void) roles_is_member_of(member, ROLERECURSE_PRIVS, role, &admin_role);
|
|
|
|
return admin_role;
|
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)
|
|
|
|
{
|
|
|
|
int nbits = 0;
|
|
|
|
|
|
|
|
/* this code relies on AclMode being an unsigned type */
|
|
|
|
while (mask)
|
|
|
|
{
|
|
|
|
if (mask & 1)
|
|
|
|
nbits++;
|
|
|
|
mask >>= 1;
|
|
|
|
}
|
|
|
|
return nbits;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Select the effective grantor ID for a GRANT or REVOKE operation.
|
|
|
|
*
|
|
|
|
* The grantor must always be either the object owner or some role that has
|
|
|
|
* been explicitly granted grant options. This ensures that all granted
|
|
|
|
* privileges appear to flow from the object owner, and there are never
|
|
|
|
* multiple "original sources" of a privilege. Therefore, if the would-be
|
|
|
|
* grantor is a member of a role that has the needed grant options, we have
|
|
|
|
* to do the grant as that role instead.
|
|
|
|
*
|
|
|
|
* It is possible that the would-be grantor is a member of several roles
|
|
|
|
* that have different subsets of the desired grant options, but no one
|
|
|
|
* role has 'em all. In this case we pick a role with the largest number
|
|
|
|
* of desired options. Ties are broken in favor of closer ancestors.
|
|
|
|
*
|
|
|
|
* roleId: the role attempting to do the GRANT/REVOKE
|
|
|
|
* privileges: the privileges to be granted/revoked
|
|
|
|
* acl: the ACL of the object in question
|
|
|
|
* ownerId: the role owning the object in question
|
|
|
|
* *grantorId: receives the OID of the role to do the grant as
|
|
|
|
* *grantOptions: receives the grant options actually held by grantorId
|
|
|
|
*
|
|
|
|
* If no grant options exist, we set grantorId to roleId, grantOptions to 0.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
select_best_grantor(Oid roleId, AclMode privileges,
|
|
|
|
const Acl *acl, Oid ownerId,
|
|
|
|
Oid *grantorId, AclMode *grantOptions)
|
|
|
|
{
|
|
|
|
AclMode needed_goptions = ACL_GRANT_OPTION_FOR(privileges);
|
|
|
|
List *roles_list;
|
|
|
|
int nrights;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The object owner is always treated as having all grant options, so if
|
|
|
|
* roleId is the owner it's easy. Also, if roleId is a superuser it's
|
|
|
|
* easy: superusers are implicitly members of every role, so they act as
|
|
|
|
* the object owner.
|
|
|
|
*/
|
|
|
|
if (roleId == ownerId || superuser_arg(roleId))
|
|
|
|
{
|
|
|
|
*grantorId = ownerId;
|
|
|
|
*grantOptions = needed_goptions;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise we have to do a careful search to see if roleId has the
|
|
|
|
* privileges of any suitable role. Note: we can hang onto the result of
|
2021-03-26 18:42:16 +01:00
|
|
|
* roles_is_member_of() throughout this loop, because aclmask_direct()
|
2005-10-10 20:49:04 +02:00
|
|
|
* doesn't query any role memberships.
|
|
|
|
*/
|
2021-03-26 18:42:16 +01:00
|
|
|
roles_list = roles_is_member_of(roleId, ROLERECURSE_PRIVS,
|
|
|
|
InvalidOid, NULL);
|
2005-10-10 20:49:04 +02:00
|
|
|
|
|
|
|
/* initialize candidate result as default */
|
|
|
|
*grantorId = roleId;
|
|
|
|
*grantOptions = ACL_NO_RIGHTS;
|
|
|
|
nrights = 0;
|
|
|
|
|
|
|
|
foreach(l, roles_list)
|
|
|
|
{
|
|
|
|
Oid otherrole = lfirst_oid(l);
|
|
|
|
AclMode otherprivs;
|
|
|
|
|
|
|
|
otherprivs = aclmask_direct(acl, otherrole, ownerId,
|
|
|
|
needed_goptions, ACLMASK_ALL);
|
|
|
|
if (otherprivs == needed_goptions)
|
|
|
|
{
|
|
|
|
/* Found a suitable grantor */
|
|
|
|
*grantorId = otherrole;
|
|
|
|
*grantOptions = otherprivs;
|
|
|
|
return;
|
|
|
|
}
|
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)
|
|
|
|
{
|
|
|
|
int nnewrights = count_one_bits(otherprivs);
|
|
|
|
|
|
|
|
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
|
|
|
|
* role name is "public".
|
|
|
|
*/
|
Row-Level Security Policies (RLS)
Building on the updatable security-barrier views work, add the
ability to define policies on tables to limit the set of rows
which are returned from a query and which are allowed to be added
to a table. Expressions defined by the policy for filtering are
added to the security barrier quals of the query, while expressions
defined to check records being added to a table are added to the
with-check options of the query.
New top-level commands are CREATE/ALTER/DROP POLICY and are
controlled by the table owner. Row Security is able to be enabled
and disabled by the owner on a per-table basis using
ALTER TABLE .. ENABLE/DISABLE ROW SECURITY.
Per discussion, ROW SECURITY is disabled on tables by default and
must be enabled for policies on the table to be used. If no
policies exist on a table with ROW SECURITY enabled, a default-deny
policy is used and no records will be visible.
By default, row security is applied at all times except for the
table owner and the superuser. A new GUC, row_security, is added
which can be set to ON, OFF, or FORCE. When set to FORCE, row
security will be applied even for the table owner and superusers.
When set to OFF, row security will be disabled when allowed and an
error will be thrown if the user does not have rights to bypass row
security.
Per discussion, pg_dump sets row_security = OFF by default to ensure
that exports and backups will have all data in the table or will
error if there are insufficient privileges to bypass row security.
A new option has been added to pg_dump, --enable-row-security, to
ask pg_dump to export with row security enabled.
A new role capability, BYPASSRLS, which can only be set by the
superuser, is added to allow other users to be able to bypass row
security using row_security = OFF.
Many thanks to the various individuals who have helped with the
design, particularly Robert Haas for his feedback.
Authors include Craig Ringer, KaiGai Kohei, Adam Brightwell, Dean
Rasheed, with additional changes and rework by me.
Reviewers have included all of the above, Greg Smith,
Jeff McCormick, and Robert Haas.
2014-09-19 17:18:35 +02:00
|
|
|
Oid
|
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;
|
|
|
|
|
2020-09-17 11:39:28 +02:00
|
|
|
case ROLESPEC_CURRENT_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
|
|
|
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),
|
|
|
|
errmsg("role \"%s\" does not exist", role->rolename)));
|
|
|
|
break;
|
|
|
|
|
2020-09-17 11:39:28 +02:00
|
|
|
case ROLESPEC_CURRENT_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
|
|
|
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")));
|
|
|
|
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,
|
2022-09-28 17:14:53 +02:00
|
|
|
* if provided (which must be already translated).
|
2016-04-08 22:56:27 +02:00
|
|
|
*
|
|
|
|
* 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-04-08 22:56:27 +02:00
|
|
|
role->rolename),
|
2022-09-28 17:14:53 +02:00
|
|
|
errdetail_internal("%s", detail_msg)));
|
2016-04-08 22:56:27 +02:00
|
|
|
else
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_RESERVED_NAME),
|
2016-07-26 04:07:44 +02:00
|
|
|
errmsg("role name \"%s\" is reserved",
|
2016-04-08 22:56:27 +02:00
|
|
|
role->rolename)));
|
|
|
|
}
|
|
|
|
}
|