while (isspace((unsignedchar) *s))
s++; for (;
*s != '\0' &&
(in_quotes || *s == '"' || is_safe_acl_char(*s, true));
s++)
{ if (*s == '"')
{ if (!in_quotes)
{
in_quotes = true; continue;
} /* safe to look at next char (could be '\0' though) */ if (*(s + 1) != '"')
{
in_quotes = false; continue;
} /* it's an escaped double quote; skip the escaping char */
s++;
}
/* Add the character to the string */ if (len >= NAMEDATALEN - 1)
ereturn(escontext, NULL,
(errcode(ERRCODE_NAME_TOO_LONG),
errmsg("identifier too long"),
errdetail("Identifier must be less than %d characters.",
NAMEDATALEN)));
/* Detect whether we need to use double quotes */ for (src = s; *src; src++)
{ if (!is_safe_acl_char(*src, false))
{
safe = false; break;
}
} if (!safe)
*p++ = '"'; for (src = s; *src; src++)
{ /* A double quote character in a username is encoded as "" */ if (*src == '"')
*p++ = '"';
*p++ = *src;
} if (!safe)
*p++ = '"';
*p = '\0';
}
s = getid(s, name, escontext); if (s == NULL) return NULL; if (*s != '=')
{ /* we just read a keyword, not a name */ if (strcmp(name, "group") != 0 && strcmp(name, "user") != 0)
ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("unrecognized key word: \"%s\"", name),
errhint("ACL key word must be \"group\" or \"user\"."))); /* move s to the name beyond the keyword */
s = getid(s, name, escontext); if (s == NULL) return NULL; if (name[0] == '\0')
ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("missing name"),
errhint("A name must follow the \"group\" or \"user\" key word.")));
}
if (*s != '=')
ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("missing \"=\" sign")));
privs = goption = ACL_NO_RIGHTS;
for (++s, read = 0; isalpha((unsignedchar) *s) || *s == '*'; s++)
{ switch (*s)
{ case'*':
goption |= read; break; case ACL_INSERT_CHR:
read = ACL_INSERT; break; case ACL_SELECT_CHR:
read = ACL_SELECT; break; case ACL_UPDATE_CHR:
read = ACL_UPDATE; break; case ACL_DELETE_CHR:
read = ACL_DELETE; break; case ACL_TRUNCATE_CHR:
read = ACL_TRUNCATE; break; case ACL_REFERENCES_CHR:
read = ACL_REFERENCES; break; case ACL_TRIGGER_CHR:
read = ACL_TRIGGER; break; case ACL_EXECUTE_CHR:
read = ACL_EXECUTE; break; case ACL_USAGE_CHR:
read = ACL_USAGE; break; case ACL_CREATE_CHR:
read = ACL_CREATE; break; case ACL_CREATE_TEMP_CHR:
read = ACL_CREATE_TEMP; break; case ACL_CONNECT_CHR:
read = ACL_CONNECT; break; case ACL_SET_CHR:
read = ACL_SET; break; case ACL_ALTER_SYSTEM_CHR:
read = ACL_ALTER_SYSTEM; break; case ACL_MAINTAIN_CHR:
read = ACL_MAINTAIN; break; default:
ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid mode character: must be one of \"%s\"",
ACL_ALL_RIGHTS_STR)));
}
privs |= read;
}
if (name[0] == '\0')
aip->ai_grantee = ACL_ID_PUBLIC; else
{
aip->ai_grantee = get_role_oid(name, true); if (!OidIsValid(aip->ai_grantee))
ereturn(escontext, NULL,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role \"%s\" does not exist", name)));
}
/* *XXXAllowadegreeofbackwardcompatibilitybydefaultingthegrantor *tothesuperuser.
*/ if (*s == '/')
{
s = getid(s + 1, name2, escontext); if (s == NULL) return NULL; if (name2[0] == '\0')
ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("a name must follow the \"/\" sign")));
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)));
} else
{
aip->ai_grantor = BOOTSTRAP_SUPERUSERID;
ereport(WARNING,
(errcode(ERRCODE_INVALID_GRANTOR),
errmsg("defaulting grantor to user ID %u",
BOOTSTRAP_SUPERUSERID)));
}
s = aclparse(s, aip, escontext); if (s == NULL)
PG_RETURN_NULL();
while (isspace((unsignedchar) *s))
++s; if (*s)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("extra garbage at the end of the ACL specification")));
/* Caller probably already checked old_acl, but be safe */
check_acl(old_acl);
/* If granting grant options, check for circularity */ if (modechg != ACL_MODECHG_DEL &&
ACLITEM_GET_GOPTIONS(*mod_aip) != ACL_NO_RIGHTS)
check_circularity(old_acl, mod_aip, ownerId);
num = ACL_NUM(old_acl);
old_aip = ACL_DAT(old_acl);
for (dst = 0; dst < num; ++dst)
{ if (aclitem_match(mod_aip, old_aip + dst))
{ /* found a match, so modify existing item */
new_acl = allocacl(num);
new_aip = ACL_DAT(new_acl);
memcpy(new_acl, old_acl, ACL_SIZE(old_acl)); break;
}
}
if (dst == num)
{ /* need to append a new item */
new_acl = allocacl(num + 1);
new_aip = ACL_DAT(new_acl);
memcpy(new_aip, old_aip, num * sizeof(AclItem));
/* initialize the new entry with no permissions */
new_aip[dst].ai_grantee = mod_aip->ai_grantee;
new_aip[dst].ai_grantor = mod_aip->ai_grantor;
ACLITEM_SET_PRIVS_GOPTIONS(new_aip[dst],
ACL_NO_RIGHTS, ACL_NO_RIGHTS);
num++; /* set num to the size of new_acl */
}
/* The owner always has grant options, no need to check */ if (mod_aip->ai_grantor == ownerId) return;
/* Make a working copy */
acl = allocacl(ACL_NUM(old_acl));
memcpy(acl, old_acl, ACL_SIZE(old_acl));
/* Zap all grant options of target grantee, plus what depends on 'em */
cc_restart:
num = ACL_NUM(acl);
aip = ACL_DAT(acl); for (i = 0; i < num; i++)
{ if (aip[i].ai_grantee == mod_aip->ai_grantee &&
ACLITEM_GET_GOPTIONS(aip[i]) != ACL_NO_RIGHTS)
{
Acl *new_acl;
/* We'll actually zap ordinary privs too, but no matter */
new_acl = aclupdate(acl, &aip[i], ACL_MODECHG_DEL,
ownerId, DROP_CASCADE);
pfree(acl);
acl = new_acl;
goto cc_restart;
}
}
/* Now we can compute grantor's independently-derived privileges */
own_privs = aclmask(acl,
mod_aip->ai_grantor,
ownerId,
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);
}
/* *Ensurethatnoprivilegeis"abandoned".Aprivilegeisabandoned *iftheuserthatgrantedtheprivilegelosesthegrantoption.(So *thechainthroughwhichitwasgrantedisbroken.)Eitherthe *abandonedprivilegesarerevokedaswell,oranerrormessageis *printed,dependingonthedropbehavioroption. * *acl:theinputACLlist *grantee:theuserfromwhomsomegrantoptionshavebeenrevoked *revoke_privs:thegrantoptionsbeingrevoked *ownerId:Oidofobjectowner *behavior:RESTRICTorCASCADEbehaviorforrecursiveremoval * *TheinputAclobjectispfree'difreplaced.
*/ static Acl *
recursive_revoke(Acl *acl,
Oid grantee,
AclMode revoke_privs,
Oid ownerId,
DropBehavior behavior)
{
AclMode still_has;
AclItem *aip; int i,
num;
check_acl(acl);
/* The owner can never truly lose grant options, so short-circuit */ if (grantee == ownerId) return acl;
/* The grantee might still have some grant options via another grantor */
still_has = aclmask(acl, grantee, ownerId,
ACL_GRANT_OPTION_FOR(revoke_privs),
ACLMASK_ALL);
revoke_privs &= ~ACL_OPTION_TO_PRIVS(still_has); if (revoke_privs == ACL_NO_RIGHTS) return acl;
restart:
num = ACL_NUM(acl);
aip = ACL_DAT(acl); for (i = 0; i < num; i++)
{ if (aip[i].ai_grantor == grantee
&& (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0)
{
AclItem mod_acl;
Acl *new_acl;
if (behavior == DROP_RESTRICT)
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("dependent privileges exist"),
errhint("Use CASCADE to revoke them too.")));
/* Quick exit for mask == 0 */ if (mask == 0) return0;
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);
/* *Checkprivilegesgranteddirectlytoroleid(andnottopublic)
*/ for (i = 0; i < num; i++)
{
AclItem *aidata = &aidat[i];
if (aidata->ai_grantee == roleid)
{
result |= aidata->ai_privs & mask; if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0)) return result;
}
}
return result;
}
/* *aclmembers *FindoutalltheroleidsmentionedinanAcl. *Notethatwedonotdistinguishgrantorsfromgrantees. * **roleidsissettopointtoapalloc'darraycontainingdistinctOIDs *insortedorder.Thelengthofthearrayisthefunctionresult.
*/ int
aclmembers(const Acl *acl, Oid **roleids)
{
Oid *list; const AclItem *acldat; int i,
j;
/* Allocate the worst-case space requirement */
list = palloc(ACL_NUM(acl) * 2 * sizeof(Oid));
acldat = ACL_DAT(acl);
/* *WalktheACLcollectingmentionedRoleIds.
*/
j = 0; for (i = 0; i < ACL_NUM(acl); i++)
{ const AclItem *ai = &acldat[i];
if (ai->ai_grantee != ACL_ID_PUBLIC)
list[j++] = ai->ai_grantee; /* grantor is currently never PUBLIC, but let's check anyway */ if (ai->ai_grantor != ACL_ID_PUBLIC)
list[j++] = ai->ai_grantor;
}
/* Sort the array */
qsort(list, j, sizeof(Oid), oid_cmp);
/* Remove duplicates from the array */ return qunique(list, j, sizeof(Oid), oid_cmp);
}
/* *aclinsert(exportedfunction)
*/
Datum
aclinsert(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("aclinsert is no longer supported")));
PG_RETURN_NULL(); /* keep compiler quiet */
}
Datum
aclremove(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("aclremove is no longer supported")));
PG_RETURN_NULL(); /* keep compiler quiet */
}
Datum
aclcontains(PG_FUNCTION_ARGS)
{
Acl *acl = PG_GETARG_ACL_P(0);
AclItem *aip = PG_GETARG_ACLITEM_P(1);
AclItem *aidat; int i,
num;
check_acl(acl);
num = ACL_NUM(acl);
aidat = ACL_DAT(acl); for (i = 0; i < num; ++i)
{ if (aip->ai_grantee == aidat[i].ai_grantee &&
aip->ai_grantor == aidat[i].ai_grantor &&
(ACLITEM_GET_RIGHTS(*aip) & ACLITEM_GET_RIGHTS(aidat[i])) == ACLITEM_GET_RIGHTS(*aip))
PG_RETURN_BOOL(true);
}
PG_RETURN_BOOL(false);
}
/* 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((unsignedchar) *chunk))
chunk++;
chunk_len = strlen(chunk); while (chunk_len > 0 && isspace((unsignedchar) 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;
}
staticconstchar *
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"; case ACL_SET: return"SET"; case ACL_ALTER_SYSTEM: return"ALTER SYSTEM"; case ACL_MAINTAIN: return"MAINTAIN"; default:
elog(ERROR, "unrecognized aclright: %d", aclright); return NULL;
}
}
/* *has_table_privilege_name_name *Checkuserprivilegesonatablegiven *nameusername,texttablename,andtextprivname.
*/
Datum
has_table_privilege_name_name(PG_FUNCTION_ARGS)
{
Name rolename = PG_GETARG_NAME(0);
text *tablename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid tableoid;
AclMode mode;
AclResult aclresult;
/* *has_table_privilege_name *Checkuserprivilegesonatablegiven *texttablenameandtextprivname. *current_userisassumed
*/
Datum
has_table_privilege_name(PG_FUNCTION_ARGS)
{
text *tablename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid tableoid;
AclMode mode;
AclResult aclresult;
/* We might not even have permissions on this relation; don't lock it. */ return RangeVarGetRelid(relrv, NoLock, false);
}
/* *convert_table_priv_string *ConverttextstringtoAclModevalue.
*/ static AclMode
convert_table_priv_string(text *priv_type_text)
{ staticconst 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)},
{"MAINTAIN", ACL_MAINTAIN},
{"MAINTAIN WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_MAINTAIN)},
{NULL, 0}
};
/* *has_sequence_privilege_name_name *Checkuserprivilegesonasequencegiven *nameusername,textsequencename,andtextprivname.
*/
Datum
has_sequence_privilege_name_name(PG_FUNCTION_ARGS)
{
Name rolename = PG_GETARG_NAME(0);
text *sequencename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid sequenceoid;
AclMode mode;
AclResult aclresult;
roleid = get_role_oid_or_public(NameStr(*rolename));
mode = convert_sequence_priv_string(priv_type_text);
sequenceoid = convert_table_name(sequencename); if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a sequence",
text_to_cstring(sequencename))));
/* *has_sequence_privilege_name *Checkuserprivilegesonasequencegiven *textsequencenameandtextprivname. *current_userisassumed
*/
Datum
has_sequence_privilege_name(PG_FUNCTION_ARGS)
{
text *sequencename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid sequenceoid;
AclMode mode;
AclResult aclresult;
roleid = GetUserId();
mode = convert_sequence_priv_string(priv_type_text);
sequenceoid = convert_table_name(sequencename); if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a sequence",
text_to_cstring(sequencename))));
/* *has_sequence_privilege_id_name *Checkuserprivilegesonasequencegiven *roleid,textsequencename,andtextprivname.
*/
Datum
has_sequence_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *sequencename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid sequenceoid;
AclMode mode;
AclResult aclresult;
mode = convert_sequence_priv_string(priv_type_text);
sequenceoid = convert_table_name(sequencename); if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a sequence",
text_to_cstring(sequencename))));
/* *has_any_column_privilege_name_name *Checkuserprivilegesonanycolumnofatablegiven *nameusername,texttablename,andtextprivname.
*/
Datum
has_any_column_privilege_name_name(PG_FUNCTION_ARGS)
{
Name rolename = PG_GETARG_NAME(0);
text *tablename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid tableoid;
AclMode mode;
AclResult aclresult;
/* First check at table level, then examine each column if needed */
aclresult = pg_class_aclcheck(tableoid, roleid, mode); if (aclresult != ACLCHECK_OK)
aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
ACLMASK_ANY);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/* *has_any_column_privilege_name *Checkuserprivilegesonanycolumnofatablegiven *texttablenameandtextprivname. *current_userisassumed
*/
Datum
has_any_column_privilege_name(PG_FUNCTION_ARGS)
{
text *tablename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid tableoid;
AclMode mode;
AclResult aclresult;
/* First check at table level, then examine each column if needed */
aclresult = pg_class_aclcheck(tableoid, roleid, mode); if (aclresult != ACLCHECK_OK)
aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
ACLMASK_ANY);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/* *has_any_column_privilege_name_id *Checkuserprivilegesonanycolumnofatablegiven *nameusename,tableoid,andtextprivname.
*/
Datum
has_any_column_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid tableoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult; bool is_missing = false;
/* First check at table level, then examine each column if needed */
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing); if (aclresult != ACLCHECK_OK)
{ if (is_missing)
PG_RETURN_NULL();
aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
ACLMASK_ANY, &is_missing); if (is_missing)
PG_RETURN_NULL();
}
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/* *has_any_column_privilege_id *Checkuserprivilegesonanycolumnofatablegiven *tableoid,andtextprivname. *current_userisassumed
*/
Datum
has_any_column_privilege_id(PG_FUNCTION_ARGS)
{
Oid tableoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult; bool is_missing = false;
/* First check at table level, then examine each column if needed */
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing); if (aclresult != ACLCHECK_OK)
{ if (is_missing)
PG_RETURN_NULL();
aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
ACLMASK_ANY, &is_missing); if (is_missing)
PG_RETURN_NULL();
}
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/* *has_any_column_privilege_id_name *Checkuserprivilegesonanycolumnofatablegiven *roleid,texttablename,andtextprivname.
*/
Datum
has_any_column_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *tablename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid tableoid;
AclMode mode;
AclResult aclresult;
/* First check at table level, then examine each column if needed */
aclresult = pg_class_aclcheck(tableoid, roleid, mode); if (aclresult != ACLCHECK_OK)
aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
ACLMASK_ANY);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/* *has_any_column_privilege_id_id *Checkuserprivilegesonanycolumnofatablegiven *roleid,tableoid,andtextprivname.
*/
Datum
has_any_column_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid tableoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult; bool is_missing = false;
/* First check at table level, then examine each column if needed */
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing); if (aclresult != ACLCHECK_OK)
{ if (is_missing)
PG_RETURN_NULL();
aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
ACLMASK_ANY, &is_missing); if (is_missing)
PG_RETURN_NULL();
}
/* Next check if we have the privilege at the table level */
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing); if (aclresult == ACLCHECK_OK) return1; elseif (is_missing) return -1; else return0;
}
/* *has_column_privilege_name_name_name *Checkuserprivilegesonacolumngiven *nameusername,texttablename,textcolname,andtextprivname.
*/
Datum
has_column_privilege_name_name_name(PG_FUNCTION_ARGS)
{
Name rolename = PG_GETARG_NAME(0);
text *tablename = PG_GETARG_TEXT_PP(1);
text *column = PG_GETARG_TEXT_PP(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
Oid roleid;
Oid tableoid;
AttrNumber colattnum;
AclMode mode; int privresult;
/* *has_column_privilege_name_name_attnum *Checkuserprivilegesonacolumngiven *nameusername,texttablename,intattnum,andtextprivname.
*/
Datum
has_column_privilege_name_name_attnum(PG_FUNCTION_ARGS)
{
Name rolename = PG_GETARG_NAME(0);
text *tablename = PG_GETARG_TEXT_PP(1);
AttrNumber colattnum = PG_GETARG_INT16(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
Oid roleid;
Oid tableoid;
AclMode mode; int privresult;
/* *has_column_privilege_name_id_name *Checkuserprivilegesonacolumngiven *nameusername,tableoid,textcolname,andtextprivname.
*/
Datum
has_column_privilege_name_id_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid tableoid = PG_GETARG_OID(1);
text *column = PG_GETARG_TEXT_PP(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
Oid roleid;
AttrNumber colattnum;
AclMode mode; int privresult;
/* *has_column_privilege_name_id_attnum *Checkuserprivilegesonacolumngiven *nameusername,tableoid,intattnum,andtextprivname.
*/
Datum
has_column_privilege_name_id_attnum(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid tableoid = PG_GETARG_OID(1);
AttrNumber colattnum = PG_GETARG_INT16(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
Oid roleid;
AclMode mode; int privresult;
/* *has_column_privilege_id_name_name *Checkuserprivilegesonacolumngiven *oidroleid,texttablename,textcolname,andtextprivname.
*/
Datum
has_column_privilege_id_name_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *tablename = PG_GETARG_TEXT_PP(1);
text *column = PG_GETARG_TEXT_PP(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
Oid tableoid;
AttrNumber colattnum;
AclMode mode; int privresult;
/* *has_column_privilege_id_name_attnum *Checkuserprivilegesonacolumngiven *oidroleid,texttablename,intattnum,andtextprivname.
*/
Datum
has_column_privilege_id_name_attnum(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *tablename = PG_GETARG_TEXT_PP(1);
AttrNumber colattnum = PG_GETARG_INT16(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
Oid tableoid;
AclMode mode; int privresult;
/* *has_column_privilege_id_id_name *Checkuserprivilegesonacolumngiven *oidroleid,tableoid,textcolname,andtextprivname.
*/
Datum
has_column_privilege_id_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid tableoid = PG_GETARG_OID(1);
text *column = PG_GETARG_TEXT_PP(2);
text *priv_type_text = PG_GETARG_TEXT_PP(3);
AttrNumber colattnum;
AclMode mode; int privresult;
/* *has_column_privilege_name_name *Checkuserprivilegesonacolumngiven *texttablename,textcolname,andtextprivname. *current_userisassumed
*/
Datum
has_column_privilege_name_name(PG_FUNCTION_ARGS)
{
text *tablename = PG_GETARG_TEXT_PP(0);
text *column = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid tableoid;
AttrNumber colattnum;
AclMode mode; int privresult;
/* *has_column_privilege_name_attnum *Checkuserprivilegesonacolumngiven *texttablename,intattnum,andtextprivname. *current_userisassumed
*/
Datum
has_column_privilege_name_attnum(PG_FUNCTION_ARGS)
{
text *tablename = PG_GETARG_TEXT_PP(0);
AttrNumber colattnum = PG_GETARG_INT16(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid tableoid;
AclMode mode; int privresult;
/* *has_column_privilege_id_name *Checkuserprivilegesonacolumngiven *tableoid,textcolname,andtextprivname. *current_userisassumed
*/
Datum
has_column_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid tableoid = PG_GETARG_OID(0);
text *column = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AttrNumber colattnum;
AclMode mode; int privresult;
/*
* 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;
}
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}
};
/*
* has_database_privilege variants
* These are all named "has_database_privilege" at the SQL level.
* They take various combinations of database name, database OID,
* user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
* privilege, false if not, or NULL if object doesn't exist.
*/
/*
* has_database_privilege_name_name
* Check user privileges on a database given
* name username, text databasename, and text priv name.
*/
Datum
has_database_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
text *databasename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid databaseoid;
AclMode mode;
AclResult aclresult;
/*
* has_database_privilege_name
* Check user privileges on a database given
* text databasename and text priv name.
* current_user is assumed
*/
Datum
has_database_privilege_name(PG_FUNCTION_ARGS)
{
text *databasename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid databaseoid;
AclMode mode;
AclResult aclresult;
/*
* has_database_privilege_name_id
* Check user privileges on a database given
* name usename, database oid, and text priv name.
*/
Datum
has_database_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid databaseoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_database_privilege_id
* Check user privileges on a database given
* database oid, and text priv name.
* current_user is assumed
*/
Datum
has_database_privilege_id(PG_FUNCTION_ARGS)
{
Oid databaseoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_database_privilege_id_name
* Check user privileges on a database given
* roleid, text databasename, and text priv name.
*/
Datum
has_database_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *databasename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid databaseoid;
AclMode mode;
AclResult aclresult;
/*
* has_database_privilege_id_id
* Check user privileges on a database given
* roleid, database oid, and text priv name.
*/
Datum
has_database_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid databaseoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* Support routines for has_database_privilege family.
*/
/*
* Given a database name expressed as a string, look it up and return Oid
*/
static Oid
convert_database_name(text *databasename)
{
char *dbname = text_to_cstring(databasename);
return get_database_oid(dbname, false);
}
/*
* convert_database_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_database_priv_string(text *priv_type_text)
{
static const priv_map database_priv_map[] = {
{"CREATE", ACL_CREATE},
{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
{"TEMPORARY", ACL_CREATE_TEMP},
{"TEMPORARY WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
{"TEMP", ACL_CREATE_TEMP},
{"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
{"CONNECT", ACL_CONNECT},
{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
{NULL, 0}
};
/*
* has_foreign_data_wrapper_privilege variants
* These are all named "has_foreign_data_wrapper_privilege" at the SQL level.
* They take various combinations of foreign-data wrapper name,
* fdw OID, user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
* privilege, false if not.
*/
/*
* has_foreign_data_wrapper_privilege_name_name
* Check user privileges on a foreign-data wrapper given
* name username, text fdwname, and text priv name.
*/
Datum
has_foreign_data_wrapper_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
text *fdwname = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid fdwid;
AclMode mode;
AclResult aclresult;
/*
* has_foreign_data_wrapper_privilege_name
* Check user privileges on a foreign-data wrapper given
* text fdwname and text priv name.
* current_user is assumed
*/
Datum
has_foreign_data_wrapper_privilege_name(PG_FUNCTION_ARGS)
{
text *fdwname = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid fdwid;
AclMode mode;
AclResult aclresult;
/*
* has_foreign_data_wrapper_privilege_name_id
* Check user privileges on a foreign-data wrapper given
* name usename, foreign-data wrapper oid, and text priv name.
*/
Datum
has_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid fdwid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_foreign_data_wrapper_privilege_id
* Check user privileges on a foreign-data wrapper given
* foreign-data wrapper oid, and text priv name.
* current_user is assumed
*/
Datum
has_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS)
{
Oid fdwid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_foreign_data_wrapper_privilege_id_name
* Check user privileges on a foreign-data wrapper given
* roleid, text fdwname, and text priv name.
*/
Datum
has_foreign_data_wrapper_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *fdwname = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid fdwid;
AclMode mode;
AclResult aclresult;
/*
* has_foreign_data_wrapper_privilege_id_id
* Check user privileges on a foreign-data wrapper given
* roleid, fdw oid, and text priv name.
*/
Datum
has_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid fdwid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* 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);
/*
* has_function_privilege variants
* These are all named "has_function_privilege" at the SQL level.
* They take various combinations of function name, function OID,
* user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
* privilege, false if not, or NULL if object doesn't exist.
*/
/*
* has_function_privilege_name_name
* Check user privileges on a function given
* name username, text functionname, and text priv name.
*/
Datum
has_function_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
text *functionname = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid functionoid;
AclMode mode;
AclResult aclresult;
/*
* has_function_privilege_name
* Check user privileges on a function given
* text functionname and text priv name.
* current_user is assumed
*/
Datum
has_function_privilege_name(PG_FUNCTION_ARGS)
{
text *functionname = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid functionoid;
AclMode mode;
AclResult aclresult;
/*
* has_function_privilege_name_id
* Check user privileges on a function given
* name usename, function oid, and text priv name.
*/
Datum
has_function_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid functionoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_function_privilege_id
* Check user privileges on a function given
* function oid, and text priv name.
* current_user is assumed
*/
Datum
has_function_privilege_id(PG_FUNCTION_ARGS)
{
Oid functionoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_function_privilege_id_name
* Check user privileges on a function given
* roleid, text functionname, and text priv name.
*/
Datum
has_function_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *functionname = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid functionoid;
AclMode mode;
AclResult aclresult;
/*
* has_function_privilege_id_id
* Check user privileges on a function given
* roleid, function oid, and text priv name.
*/
Datum
has_function_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid functionoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* Support routines for has_function_privilege family.
*/
/*
* Given a function name expressed as a string, look it up and return Oid
*/
static Oid
convert_function_name(text *functionname)
{
char *funcname = text_to_cstring(functionname);
Oid oid;
oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein,
CStringGetDatum(funcname)));
if (!OidIsValid(oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function \"%s\" does not exist", funcname)));
return oid;
}
/*
* convert_function_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_function_priv_string(text *priv_type_text)
{
static const priv_map function_priv_map[] = {
{"EXECUTE", ACL_EXECUTE},
{"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
{NULL, 0}
};
/*
* has_language_privilege variants
* These are all named "has_language_privilege" at the SQL level.
* They take various combinations of language name, language OID,
* user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
* privilege, false if not, or NULL if object doesn't exist.
*/
/*
* has_language_privilege_name_name
* Check user privileges on a language given
* name username, text languagename, and text priv name.
*/
Datum
has_language_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
text *languagename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid languageoid;
AclMode mode;
AclResult aclresult;
/*
* has_language_privilege_name
* Check user privileges on a language given
* text languagename and text priv name.
* current_user is assumed
*/
Datum
has_language_privilege_name(PG_FUNCTION_ARGS)
{
text *languagename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid languageoid;
AclMode mode;
AclResult aclresult;
/*
* has_language_privilege_name_id
* Check user privileges on a language given
* name usename, language oid, and text priv name.
*/
Datum
has_language_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid languageoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_language_privilege_id
* Check user privileges on a language given
* language oid, and text priv name.
* current_user is assumed
*/
Datum
has_language_privilege_id(PG_FUNCTION_ARGS)
{
Oid languageoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_language_privilege_id_name
* Check user privileges on a language given
* roleid, text languagename, and text priv name.
*/
Datum
has_language_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *languagename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid languageoid;
AclMode mode;
AclResult aclresult;
/*
* has_language_privilege_id_id
* Check user privileges on a language given
* roleid, language oid, and text priv name.
*/
Datum
has_language_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid languageoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* Support routines for has_language_privilege family.
*/
/*
* Given a language name expressed as a string, look it up and return Oid
*/
static Oid
convert_language_name(text *languagename)
{
char *langname = text_to_cstring(languagename);
return get_language_oid(langname, false);
}
/*
* convert_language_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_language_priv_string(text *priv_type_text)
{
static const priv_map language_priv_map[] = {
{"USAGE", ACL_USAGE},
{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
{NULL, 0}
};
/*
* has_schema_privilege variants
* These are all named "has_schema_privilege" at the SQL level.
* They take various combinations of schema name, schema OID,
* user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
* privilege, false if not, or NULL if object doesn't exist.
*/
/*
* has_schema_privilege_name_name
* Check user privileges on a schema given
* java.lang.StringIndexOutOfBoundsException: Range [0, 8) out of bounds for length 6
*/
Datum
has_schema_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
text *schemaname = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid schemaoid;
AclMode mode;
AclResult aclresult;
/*
* has_schema_privilege_name
* Check user privileges on a schema given
* text schemaname and text priv name.
* current_user is assumed
*/
Datum
has_schema_privilege_name(PG_FUNCTION_ARGS)
{
text *schemaname = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid schemaoid;
AclMode mode;
AclResult aclresult;
/*
* has_schema_privilege_name_id
* Check user privileges on a schema given
* name usename, schema oid, and text priv name.
*/
Datum
has_schema_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid schemaoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_schema_privilege_id
* Check user privileges on a schema given
* schema oid, and text priv name.
* current_user is assumed
*/
Datum
has_schema_privilege_id(PG_FUNCTION_ARGS)
{
Oid schemaoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_schema_privilege_id_name
* Check user privileges on a schema given
* roleid, text schemaname, and text priv name.
*/
Datum
has_schema_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *schemaname = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid schemaoid;
AclMode mode;
AclResult aclresult;
/*
* has_schema_privilege_id_id
* Check user privileges on a schema given
* roleid, schema oid, and text priv name.
*/
Datum
has_schema_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid schemaoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* Support routines for has_schema_privilege family.
*/
/*
* Given a schema name expressed as a string, look it up and return Oid
*/
static Oid
convert_schema_name(text *schemaname)
{
char *nspname = text_to_cstring(schemaname);
return get_namespace_oid(nspname, false);
}
/*
* convert_schema_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_schema_priv_string(text *priv_type_text)
{
static const priv_map schema_priv_map[] = {
{"CREATE", ACL_CREATE},
{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
{"USAGE", ACL_USAGE},
{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
{NULL, 0}
};
/*
* has_server_privilege variants
* These are all named "has_server_privilege" at the SQL level.
* They take various combinations of foreign server name,
* server OID, user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
* privilege, false if not.
*/
/*
* has_server_privilege_name_name
* Check user privileges on a foreign server given
* name username, text servername, and text priv name.
*/
Datum
has_server_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
text *servername = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid serverid;
AclMode mode;
AclResult aclresult;
/*
* has_server_privilege_name
* Check user privileges on a foreign server given
* text servername and text priv name.
* current_user is assumed
*/
Datum
has_server_privilege_name(PG_FUNCTION_ARGS)
{
text *servername = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid serverid;
AclMode mode;
AclResult aclresult;
/*
* has_server_privilege_name_id
* Check user privileges on a foreign server given
* name usename, foreign server oid, and text priv name.
*/
Datum
has_server_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid serverid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_server_privilege_id
* Check user privileges on a foreign server given
* server oid, and text priv name.
* current_user is assumed
*/
Datum
has_server_privilege_id(PG_FUNCTION_ARGS)
{
Oid serverid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_server_privilege_id_name
* Check user privileges on a foreign server given
* roleid, text servername, and text priv name.
*/
Datum
has_server_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *servername = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid serverid;
AclMode mode;
AclResult aclresult;
/*
* has_server_privilege_id_id
* Check user privileges on a foreign server given
* roleid, server oid, and text priv name.
*/
Datum
has_server_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid serverid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* 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);
/*
* has_tablespace_privilege variants
* These are all named "has_tablespace_privilege" at the SQL level.
* They take various combinations of tablespace name, tablespace OID,
* user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
* privilege, false if not.
*/
/*
* has_tablespace_privilege_name_name
* Check user privileges on a tablespace given
* name username, text tablespacename, and text priv name.
*/
Datum
has_tablespace_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
text *tablespacename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid tablespaceoid;
AclMode mode;
AclResult aclresult;
/*
* has_tablespace_privilege_name
* Check user privileges on a tablespace given
* text tablespacename and text priv name.
* current_user is assumed
*/
Datum
has_tablespace_privilege_name(PG_FUNCTION_ARGS)
{
text *tablespacename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid tablespaceoid;
AclMode mode;
AclResult aclresult;
/*
* has_tablespace_privilege_name_id
* Check user privileges on a tablespace given
* name usename, tablespace oid, and text priv name.
*/
Datum
has_tablespace_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid tablespaceoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_tablespace_privilege_id
* Check user privileges on a tablespace given
* tablespace oid, and text priv name.
* current_user is assumed
*/
Datum
has_tablespace_privilege_id(PG_FUNCTION_ARGS)
{
Oid tablespaceoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_tablespace_privilege_id_name
* Check user privileges on a tablespace given
* roleid, text tablespacename, and text priv name.
*/
Datum
has_tablespace_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *tablespacename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid tablespaceoid;
AclMode mode;
AclResult aclresult;
/*
* has_tablespace_privilege_id_id
* Check user privileges on a tablespace given
* roleid, tablespace oid, and text priv name.
*/
Datum
has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid tablespaceoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* Support routines for has_tablespace_privilege family.
*/
/*
* Given a tablespace name expressed as a string, look it up and return Oid
*/
static Oid
convert_tablespace_name(text *tablespacename)
{
char *spcname = text_to_cstring(tablespacename);
return get_tablespace_oid(spcname, false);
}
/*
* convert_tablespace_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_tablespace_priv_string(text *priv_type_text)
{
static const priv_map tablespace_priv_map[] = {
{"CREATE", ACL_CREATE},
{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
{NULL, 0}
};
/*
* has_type_privilege variants
* These are all named "has_type_privilege" at the SQL level.
* They take various combinations of type name, type OID,
* user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
* privilege, false if not, or NULL if object doesn't exist.
*/
/*
* has_type_privilege_name_name
* Check user privileges on a type given
* name username, text typename, and text priv name.
*/
Datum
has_type_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
text *typename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid typeoid;
AclMode mode;
AclResult aclresult;
/*
* has_type_privilege_name
* Check user privileges on a type given
* text typename and text priv name.
* current_user is assumed
*/
Datum
has_type_privilege_name(PG_FUNCTION_ARGS)
{
text *typename = PG_GETARG_TEXT_PP(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid typeoid;
AclMode mode;
AclResult aclresult;
/*
* has_type_privilege_name_id
* Check user privileges on a type given
* name usename, type oid, and text priv name.
*/
Datum
has_type_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid typeoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_type_privilege_id
* Check user privileges on a type given
* type oid, and text priv name.
* current_user is assumed
*/
Datum
has_type_privilege_id(PG_FUNCTION_ARGS)
{
Oid typeoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* has_type_privilege_id_name
* Check user privileges on a type given
* roleid, text typename, and text priv name.
*/
Datum
has_type_privilege_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
text *typename = PG_GETARG_TEXT_PP(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid typeoid;
AclMode mode;
AclResult aclresult;
/*
* has_type_privilege_id_id
* Check user privileges on a type given
* roleid, type oid, and text priv name.
*/
Datum
has_type_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid typeoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
/*
* 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}
};
/*
* 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);
/*
* 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));
/*
* 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));
/*
* 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));
/*
* has_largeobject_privilege variants
* These are all named "has_largeobject_privilege" at the SQL level.
* They take various combinations of large object OID with
* 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_lo_priv_byid
*
* Helper function to check user privileges on a large object given the
* role by Oid, large object by Oid, and privileges as AclMode.
*/
static bool
has_lo_priv_byid(Oid roleid, Oid lobjId, AclMode priv, bool *is_missing)
{
Snapshot snapshot = NULL;
AclResult aclresult;
/*
* has_largeobject_privilege_name_id
* Check user privileges on a large object given
* name username, large object oid, and text priv name.
*/
Datum
has_largeobject_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid roleid = get_role_oid_or_public(NameStr(*username));
Oid lobjId = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
bool is_missing = false;
bool result;
mode = convert_largeobject_priv_string(priv_type_text);
result = has_lo_priv_byid(roleid, lobjId, mode, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
/*
* has_largeobject_privilege_id
* Check user privileges on a large object given
* large object oid, and text priv name.
* current_user is assumed
*/
Datum
has_largeobject_privilege_id(PG_FUNCTION_ARGS)
{
Oid lobjId = PG_GETARG_OID(0);
Oid roleid = GetUserId();
text *priv_type_text = PG_GETARG_TEXT_PP(1);
AclMode mode;
bool is_missing = false;
bool result;
mode = convert_largeobject_priv_string(priv_type_text);
result = has_lo_priv_byid(roleid, lobjId, mode, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
/*
* has_largeobject_privilege_id_id
* Check user privileges on a large object given
* roleid, large object oid, and text priv name.
*/
Datum
has_largeobject_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid lobjId = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
bool is_missing = false;
bool result;
mode = convert_largeobject_priv_string(priv_type_text);
result = has_lo_priv_byid(roleid, lobjId, mode, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
/*
* convert_largeobject_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_largeobject_priv_string(text *priv_type_text)
{
static const priv_map largeobject_priv_map[] = {
{"SELECT", ACL_SELECT},
{"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
{"UPDATE", ACL_UPDATE},
{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
{NULL, 0}
};
/*
* pg_has_role variants
* These are all named "pg_has_role" at the SQL level.
* They take various combinations of role name, role OID,
* user name, user OID, or implicit user = current_user.
*
* The result is a boolean value: true if user has the indicated
* privilege, false if not.
*/
/*
* pg_has_role_name_name
* Check user privileges on a role given
* name username, name rolename, and text priv name.
*/
Datum
pg_has_role_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Name rolename = PG_GETARG_NAME(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
Oid roleoid;
AclMode mode;
AclResult aclresult;
/*
* pg_has_role_name
* Check user privileges on a role given
* name rolename and text priv name.
* current_user is assumed
*/
Datum
pg_has_role_name(PG_FUNCTION_ARGS)
{
Name rolename = PG_GETARG_NAME(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
Oid roleoid;
AclMode mode;
AclResult aclresult;
/*
* pg_has_role_name_id
* Check user privileges on a role given
* name usename, role oid, and text priv name.
*/
Datum
pg_has_role_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid roleoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleid;
AclMode mode;
AclResult aclresult;
/*
* pg_has_role_id
* Check user privileges on a role given
* role oid, and text priv name.
* current_user is assumed
*/
Datum
pg_has_role_id(PG_FUNCTION_ARGS)
{
Oid roleoid = PG_GETARG_OID(0);
text *priv_type_text = PG_GETARG_TEXT_PP(1);
Oid roleid;
AclMode mode;
AclResult aclresult;
/*
* pg_has_role_id_name
* Check user privileges on a role given
* roleid, name rolename, and text priv name.
*/
Datum
pg_has_role_id_name(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Name rolename = PG_GETARG_NAME(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
Oid roleoid;
AclMode mode;
AclResult aclresult;
/*
* pg_has_role_id_id
* Check user privileges on a role given
* roleid, role oid, and text priv name.
*/
Datum
pg_has_role_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid roleoid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
/*
* convert_role_priv_string
* Convert text string to AclMode value.
*
* We use USAGE to denote whether the privileges of the role are accessible
* (has_privs_of_role), MEMBER to denote is_member, and MEMBER WITH GRANT
* (or ADMIN) OPTION to denote is_admin. There is no ACL bit corresponding
* to MEMBER so we cheat and use ACL_CREATE for that. This convention
* is shared only with pg_role_aclcheck, below.
*/
static AclMode
convert_role_priv_string(text *priv_type_text)
{
static const priv_map role_priv_map[] = {
{"USAGE", ACL_USAGE},
{"MEMBER", ACL_CREATE},
{"SET", ACL_SET},
{"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)},
{"SET WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
{"SET WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
{NULL, 0}
};
/*
* pg_role_aclcheck
* Quick-and-dirty support for pg_has_role
*/
static AclResult
pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode)
{
if (mode & ACL_GRANT_OPTION_FOR(ACL_CREATE))
{
if (is_admin_of_role(roleid, role_oid))
return ACLCHECK_OK;
}
if (mode & ACL_CREATE)
{
if (is_member_of_role(roleid, role_oid))
return ACLCHECK_OK;
}
if (mode & ACL_USAGE)
{
if (has_privs_of_role(roleid, role_oid))
return ACLCHECK_OK;
}
if (mode & ACL_SET)
{
if (member_can_set_role(roleid, role_oid))
return ACLCHECK_OK;
}
return ACLCHECK_NO_PRIV;
}
/*
* initialization function (called by InitPostgres)
*/
void
initialize_acl(void)
{
if (!IsBootstrapProcessingMode())
{
cached_db_hash =
GetSysCacheHashValue1(DATABASEOID,
ObjectIdGetDatum(MyDatabaseId));
/*
* In normal mode, set a callback on any syscache invalidation of rows
* of pg_auth_members (for roles_is_member_of()) pg_database (for
* roles_is_member_of())
*/
CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
RoleMembershipCacheCallback,
(Datum) 0);
CacheRegisterSyscacheCallback(AUTHOID,
RoleMembershipCacheCallback,
(Datum) 0);
CacheRegisterSyscacheCallback(DATABASEOID,
RoleMembershipCacheCallback,
(Datum) 0);
}
}
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
*/
static void
RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
{
if (cacheid == DATABASEOID &&
hashvalue != cached_db_hash &&
hashvalue != 0)
{
return; /* ignore pg_database changes for other DBs */
}
/* Force membership caches to be recomputed on next use */
cached_role[ROLERECURSE_MEMBERS] = InvalidOid;
cached_role[ROLERECURSE_PRIVS] = InvalidOid;
cached_role[ROLERECURSE_SETROLE] = InvalidOid;
}
/*
* A helper function for roles_is_member_of() that provides an optimized
* implementation of list_append_unique_oid() via a Bloom filter. The caller
* (i.e., roles_is_member_of()) is responsible for freeing bf once it is done
* using this function.
*/
static inline List *
roles_list_append(List *roles_list, bloom_filter **bf, Oid role)
{
unsigned char *roleptr = (unsigned char *) &role;
/*
* If there is a previously-created Bloom filter, use it to try to
* determine whether the role is missing from the list. If it says yes,
* that's a hard fact and we can go ahead and add the role. If it says
* no, that's only probabilistic and we'd better search the list. Without
* a filter, we must always do an ordinary linear search through the
* existing list.
*/
if ((*bf && bloom_lacks_element(*bf, roleptr, sizeof(Oid))) ||
!list_member_oid(roles_list, role))
{
/*
* If the list is large, we take on the overhead of creating and
* populating a Bloom filter to speed up future calls to this
* function.
*/
if (*bf == NULL &&
list_length(roles_list) > ROLES_LIST_BLOOM_THRESHOLD)
{
*bf = bloom_create(ROLES_LIST_BLOOM_THRESHOLD * 10, work_mem, 0);
foreach_oid(roleid, roles_list)
bloom_add_element(*bf, (unsigned char *) &roleid, sizeof(Oid));
}
/*
* Finally, add the role to the list and the Bloom filter, if it
* exists.
*/
roles_list = lappend_oid(roles_list, role);
if (*bf)
bloom_add_element(*bf, roleptr, sizeof(Oid));
}
return roles_list;
}
/*
* Get a list of roles that the specified roleid is a member of
*
* Type ROLERECURSE_MEMBERS recurses through all grants; ROLERECURSE_PRIVS
* recurses only through inheritable grants; and ROLERECURSE_SETROLE recurses
* only through grants with set_option.
*
* Since indirect membership testing is relatively expensive, we cache
* a list of memberships. Hence, the result is only guaranteed good until
* the next call of roles_is_member_of()!
*
* For the benefit of select_best_grantor, the result is defined to be
* in breadth-first order, ie, closer relationships earlier.
*
* 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.
*/
static List *
roles_is_member_of(Oid roleid, enum RoleRecurseType type,
Oid admin_of, Oid *admin_role)
{
Oid dba;
List *roles_list;
ListCell *l;
List *new_cached_roles;
MemoryContext oldctx;
bloom_filter *bf = NULL;
Assert(OidIsValid(admin_of) == PointerIsValid(admin_role));
if (admin_role != NULL)
*admin_role = InvalidOid;
/* 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];
/*
* Role expansion happens in a non-database backend when guc.c checks
* ROLE_PG_READ_ALL_SETTINGS for a physical walsender SHOW command. In
* that case, no role gets pg_database_owner.
*/
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);
}
/*
* Find all the roles that roleid is a member of, including multi-level
* recursion. The role itself will always be the first element of the
* resulting list.
*
* Each element of the list is scanned to see if it adds any indirect
* memberships. We can use a single list as both the record of
* already-found memberships and the agenda of roles yet to be scanned.
* This is a bit tricky but works because the foreach() macro doesn't
* fetch the next list element until the bottom of the loop.
*/
roles_list = list_make1_oid(roleid);
foreach(l, roles_list)
{
Oid memberid = lfirst_oid(l);
CatCList *memlist;
int i;
/* Find roles that memberid is directly a member of */
memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
ObjectIdGetDatum(memberid));
for (i = 0; i < memlist->n_members; i++)
{
HeapTuple tup = &memlist->members[i]->tuple;
Form_pg_auth_members form = (Form_pg_auth_members) GETSTRUCT(tup);
Oid otherid = form->roleid;
/*
* While otherid==InvalidOid shouldn't appear in the catalog, the
* OidIsValid() avoids crashing if that arises.
*/
if (otherid == admin_of && form->admin_option &&
OidIsValid(admin_of) && !OidIsValid(*admin_role))
*admin_role = memberid;
/* If we're supposed to ignore non-heritable grants, do so. */
if (type == ROLERECURSE_PRIVS && !form->inherit_option)
continue;
/* If we're supposed to ignore non-SET grants, do so. */
if (type == ROLERECURSE_SETROLE && !form->set_option)
continue;
/*
* Even though there shouldn't be any loops in the membership
* graph, we must test for having already seen this role. It is
* legal for instance to have both A->B and A->C->B.
*/
roles_list = roles_list_append(roles_list, &bf, otherid);
}
ReleaseSysCacheList(memlist);
/*
* Free the Bloom filter created by roles_list_append(), if there is one.
*/
if (bf)
bloom_free(bf);
/*
* Copy the completed list into TopMemoryContext so it will persist.
*/
oldctx = MemoryContextSwitchTo(TopMemoryContext);
new_cached_roles = list_copy(roles_list);
MemoryContextSwitchTo(oldctx);
list_free(roles_list);
/*
* Now safe to assign to state variable
*/
cached_role[type] = InvalidOid; /* just paranoia */
list_free(cached_roles[type]);
cached_roles[type] = new_cached_roles;
cached_role[type] = roleid;
/* And now we can return the answer */
return cached_roles[type];
}
/*
* Does member have the privileges of role (directly or indirectly)?
*
* 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.
*/
bool
has_privs_of_role(Oid member, Oid role)
{
/* Fast path for simple case */
if (member == role)
return true;
/* Superusers have every privilege, so are part of every role */
if (superuser_arg(member))
return true;
/*
* Find all the roles that member has the privileges of, including
* multi-level recursion, then see if target role is any one of them.
*/
return list_member_oid(roles_is_member_of(member, ROLERECURSE_PRIVS,
InvalidOid, NULL),
role);
}
/*
* Can member use SET ROLE to this role?
*
* 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.
*
* It doesn't matter whether the grants are inheritable. That'sa 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.
*/
bool
member_can_set_role(Oid member, Oid role)
{
/* Fast path for simple case */
if (member == role)
return true;
/* Superusers have every privilege, so can always SET ROLE */
if (superuser_arg(member))
return true;
/*
* 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.
*/
return list_member_oid(roles_is_member_of(member, ROLERECURSE_SETROLE,
InvalidOid, NULL),
role);
}
/*
* Permission violation error unless able to SET ROLE to target role.
*/
void
check_can_set_role(Oid member, Oid role)
{
if (!member_can_set_role(member, role))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be able to SET ROLE \"%s\"",
GetUserNameFromId(role, false))));
}
/*
* 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);
}
/*
* Is member a member of role, not considering superuserness?
*
* This is identical to is_member_of_role except we ignore superuser
* status.
*
* Do not use this for privilege checking, instead use has_privs_of_role()
*/
bool
is_member_of_role_nosuper(Oid member, Oid role)
{
/* Fast path for simple case */
if (member == role)
return true;
/*
* Find all the roles that member is a member of, including multi-level
* recursion, then see if target role is any one of them.
*/
return list_member_oid(roles_is_member_of(member, ROLERECURSE_MEMBERS,
InvalidOid, NULL),
role);
}
/*
* 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?
*/
bool
is_admin_of_role(Oid member, Oid role)
{
Oid admin_role;
if (superuser_arg(member))
return true;
/* By policy, a role cannot have WITH ADMIN OPTION on itself. */
if (member == role)
return false;
/*
* 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;
/*
* Select the effective grantor ID for a GRANT or REVOKE operation.
*
* The grantor must always be either the object owner or some role that has
* been explicitly granted grant options. This ensures that all granted
* privileges appear to flow from the object owner, and there are never
* multiple "original sources" of a privilege. Therefore, if the would-be
* grantor is a member of a role that has the needed grant options, we have
* to do the grant as that role instead.
*
* It is possible that the would-be grantor is a member of several roles
* that have different subsets of the desired grant options, but no one
* role has 'em all. In this case we pick a role with the largest number
* of desired options. Ties are broken in favor of closer ancestors.
*
* roleId: the role attempting to do the GRANT/REVOKE
* privileges: the privileges to be granted/revoked
* acl: the ACL of the object in question
* ownerId: the role owning the object in question
* *grantorId: receives the OID of the role to do the grant as
* *grantOptions: receives the grant options actually held by grantorId
*
* If no grant options exist, we set grantorId to roleId, grantOptions to 0.
*/
void
select_best_grantor(Oid roleId, AclMode privileges,
const Acl *acl, Oid ownerId,
Oid *grantorId, AclMode *grantOptions)
{
AclMode needed_goptions = ACL_GRANT_OPTION_FOR(privileges);
List *roles_list;
int nrights;
ListCell *l;
/*
* The object owner is always treated as having all grant options, so if
* roleId is the owner it's easy. Also, if roleId is a superuser it's
* easy: superusers are implicitly members of every role, so they act as
* the object owner.
*/
if (roleId == ownerId || superuser_arg(roleId))
{
*grantorId = ownerId;
*grantOptions = needed_goptions;
return;
}
/*
* Otherwise we have to do a careful search to see if roleId has the
* privileges of any suitable role. Note: we can hang onto the result of
* roles_is_member_of() throughout this loop, because aclmask_direct()
* doesn't query any role memberships.
*/
roles_list = roles_is_member_of(roleId, ROLERECURSE_PRIVS,
InvalidOid, NULL);
/* initialize candidate result as default */
*grantorId = roleId;
*grantOptions = ACL_NO_RIGHTS;
nrights = 0;
foreach(l, roles_list)
{
Oid otherrole = lfirst_oid(l);
AclMode otherprivs;
otherprivs = aclmask_direct(acl, otherrole, ownerId,
needed_goptions, ACLMASK_ALL);
if (otherprivs == needed_goptions)
{
/* Found a suitable grantor */
*grantorId = otherrole;
*grantOptions = otherprivs;
return;
}
/*
* If it has just some of the needed privileges, remember best
* candidate.
*/
if (otherprivs != ACL_NO_RIGHTS)
{
int nnewrights = pg_popcount64(otherprivs);
/*
* get_role_oid - Given a role name, look up the role's OID.
*
* If missing_ok is false, throw an error if role name not found. If
* true, just return InvalidOid.
*/
Oid
get_role_oid(const char *rolname, bool missing_ok)
{
Oid oid;
oid = GetSysCacheOid1(AUTHNAME, Anum_pg_authid_oid,
CStringGetDatum(rolname));
if (!OidIsValid(oid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role \"%s\" does not exist", rolname)));
return oid;
}
/*
* get_role_oid_or_public - As above, but return ACL_ID_PUBLIC if the
* role name is "public".
*/
Oid
get_role_oid_or_public(const char *rolname)
{
if (strcmp(rolname, "public") == 0)
return ACL_ID_PUBLIC;
return get_role_oid(rolname, false);
}
/*
* Given a RoleSpec node, return the OID it corresponds to. If missing_ok is
* true, return InvalidOid if the role does not exist.
*
* PUBLIC is always disallowed here. Routines wanting to handle the PUBLIC
* case must check the case separately.
*/
Oid
get_rolespec_oid(const RoleSpec *role, bool missing_ok)
{
Oid oid;
switch (role->roletype)
{
case ROLESPEC_CSTRING:
Assert(role->rolename);
oid = get_role_oid(role->rolename, missing_ok);
break;
case ROLESPEC_CURRENT_ROLE:
case ROLESPEC_CURRENT_USER:
oid = GetUserId();
break;
case ROLESPEC_SESSION_USER:
oid = GetSessionUserId();
break;
case ROLESPEC_PUBLIC:
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role \"%s\" does not exist", "public")));
oid = InvalidOid; /* make compiler happy */
break;
default:
elog(ERROR, "unexpected role type %d", role->roletype);
}
return oid;
}
/*
* Given a RoleSpec node, return the pg_authid HeapTuple it corresponds to.
* Caller must ReleaseSysCache when done with the result tuple.
*/
HeapTuple
get_rolespec_tuple(const RoleSpec *role)
{
HeapTuple tuple;
switch (role->roletype)
{
case ROLESPEC_CSTRING:
Assert(role->rolename);
tuple = SearchSysCache1(AUTHNAME, CStringGetDatum(role->rolename));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role \"%s\" does not exist", role->rolename)));
break;
case ROLESPEC_CURRENT_ROLE:
case ROLESPEC_CURRENT_USER:
tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetUserId()));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for role %u", GetUserId());
break;
case ROLESPEC_SESSION_USER:
tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(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 */
break;
default:
elog(ERROR, "unexpected role type %d", role->roletype);
}
return tuple;
}
/*
* Given a RoleSpec, returns a palloc'ed copy of the corresponding role's name.
*/
char *
get_rolespec_name(const RoleSpec *role)
{
HeapTuple tp;
Form_pg_authid authForm;
char *rolename;
/*
* Given a RoleSpec, throw an error if the name is reserved, using detail_msg,
* if provided (which must be already translated).
*
* If node is NULL, no error is thrown. If detail_msg is NULL then no detail
* message is provided.
*/
void
check_rolespec_name(const RoleSpec *role, const char *detail_msg)
{
if (!role)
return;
if (role->roletype != ROLESPEC_CSTRING)
return;
if (IsReservedName(role->rolename))
{
if (detail_msg)
ereport(ERROR,
(errcode(ERRCODE_RESERVED_NAME),
errmsg("role name \"%s\" is reserved",
role->rolename),
errdetail_internal("%s", detail_msg)));
else
ereport(ERROR,
(errcode(ERRCODE_RESERVED_NAME),
errmsg("role name \"%s\" is reserved",
role->rolename)));
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.