mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-28 03:51:50 +02:00
Grant options, and cascading revoke. Grant options are allowed only for
users right now, not groups. Extension of has_foo_privileges functions to query the grant options. Extension of aclitem type to store grantor.
This commit is contained in:
parent
aa78ca3a95
commit
ef7422510e
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.135 2003/01/23 01:22:59 tgl Exp $
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.136 2003/01/23 23:38:51 petere Exp $
|
||||
PostgreSQL documentation
|
||||
-->
|
||||
|
||||
@ -5786,6 +5786,12 @@ SELECT has_table_privilege('myschema.mytable', 'select');
|
||||
<literal>USAGE</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To evaluate whether a user holds a grant option on the privilege,
|
||||
append <literal> WITH GRANT OPTION</literal> to the privilege key
|
||||
word; for example <literal>'UPDATE WITH GRANT OPTION'</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<xref linkend="functions-misc-schema-table"> shows functions that
|
||||
determine whether a certain object is <firstterm>visible</> in the
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/grant.sgml,v 1.31 2002/11/21 23:34:43 petere Exp $
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/grant.sgml,v 1.32 2003/01/23 23:38:53 petere Exp $
|
||||
PostgreSQL documentation
|
||||
-->
|
||||
|
||||
@ -19,23 +19,23 @@ PostgreSQL documentation
|
||||
GRANT { { SELECT | INSERT | UPDATE | DELETE | RULE | REFERENCES | TRIGGER }
|
||||
[,...] | ALL [ PRIVILEGES ] }
|
||||
ON [ TABLE ] <replaceable class="PARAMETER">tablename</replaceable> [, ...]
|
||||
TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...]
|
||||
TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
|
||||
|
||||
GRANT { { CREATE | TEMPORARY | TEMP } [,...] | ALL [ PRIVILEGES ] }
|
||||
ON DATABASE <replaceable>dbname</replaceable> [, ...]
|
||||
TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...]
|
||||
TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
|
||||
|
||||
GRANT { EXECUTE | ALL [ PRIVILEGES ] }
|
||||
ON FUNCTION <replaceable>funcname</replaceable> ([<replaceable>type</replaceable>, ...]) [, ...]
|
||||
TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...]
|
||||
TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
|
||||
|
||||
GRANT { USAGE | ALL [ PRIVILEGES ] }
|
||||
ON LANGUAGE <replaceable>langname</replaceable> [, ...]
|
||||
TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...]
|
||||
TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
|
||||
|
||||
GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
ON SCHEMA <replaceable>schemaname</replaceable> [, ...]
|
||||
TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...]
|
||||
TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -63,13 +63,18 @@ GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
|
||||
<para>
|
||||
There is no need to grant privileges to the creator of an object,
|
||||
as the creator has all privileges by default.
|
||||
(The creator could, however, choose to revoke
|
||||
some of his own privileges for safety.) Note that the ability to
|
||||
grant and revoke privileges is inherent in the creator and cannot
|
||||
be lost. The right to drop an object, or to alter it in any way
|
||||
not described by a grantable right, is likewise inherent in the
|
||||
creator, and cannot be granted or revoked.
|
||||
as the creator has all privileges by default. (The creator could,
|
||||
however, choose to revoke some of his own privileges for safety.)
|
||||
Note that the right to drop an object, or to alter it in any way is
|
||||
not described by a grantable right; it is inherent in the creator,
|
||||
and cannot be granted or revoked.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If <literal>WITH GRANT OPTION</literal> is specified, the recipient
|
||||
of the privilege may in turn grant it to others. By default this
|
||||
is not possible. Grant options can only be granted to individual
|
||||
users, not groups or <literal>PUBLIC</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -269,7 +274,7 @@ lusitania=> \dp mytable
|
||||
Access privileges for database "lusitania"
|
||||
Schema | Table | Access privileges
|
||||
--------+---------+---------------------------------------
|
||||
public | mytable | {=r,miriam=arwdRxt,"group todos=arw"}
|
||||
public | mytable | {=r/postgres,miriam=arwdRxt/postgres,"group todos=arw/postgres"}
|
||||
(1 row)
|
||||
</programlisting>
|
||||
The entries shown by <command>\dp</command> are interpreted thus:
|
||||
@ -290,6 +295,9 @@ lusitania=> \dp mytable
|
||||
C -- CREATE
|
||||
T -- TEMPORARY
|
||||
arwdRxt -- ALL PRIVILEGES (for tables)
|
||||
* -- grant option for preceding privilege
|
||||
|
||||
/yyyy -- user who granted this privilege
|
||||
</programlisting>
|
||||
|
||||
The above example display would be seen by user <literal>miriam</> after
|
||||
@ -346,13 +354,12 @@ GRANT ALL PRIVILEGES ON kinds TO manuel;
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <acronym>SQL92</acronym> syntax for GRANT allows setting
|
||||
privileges for individual columns within a table, and allows
|
||||
setting a privilege to grant the same privileges to others:
|
||||
The <acronym>SQL</acronym> syntax for <literal>GRANT</literal>
|
||||
allows setting privileges for individual columns within a table:
|
||||
|
||||
<synopsis>
|
||||
GRANT <replaceable class="PARAMETER">privilege</replaceable> [, ...]
|
||||
ON <replaceable class="PARAMETER">object</replaceable> [ ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) ] [, ...]
|
||||
ON <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) ] [, ...]
|
||||
TO { PUBLIC | <replaceable class="PARAMETER">username</replaceable> [, ...] } [ WITH GRANT OPTION ]
|
||||
</synopsis>
|
||||
</para>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/revoke.sgml,v 1.24 2003/01/10 11:02:51 petere Exp $
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/revoke.sgml,v 1.25 2003/01/23 23:38:53 petere Exp $
|
||||
PostgreSQL documentation
|
||||
-->
|
||||
|
||||
@ -16,31 +16,36 @@ PostgreSQL documentation
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
REVOKE { { SELECT | INSERT | UPDATE | DELETE | RULE | REFERENCES | TRIGGER }
|
||||
REVOKE [ GRANT OPTION FOR ]
|
||||
{ { SELECT | INSERT | UPDATE | DELETE | RULE | REFERENCES | TRIGGER }
|
||||
[,...] | ALL [ PRIVILEGES ] }
|
||||
ON [ TABLE ] <replaceable class="PARAMETER">tablename</replaceable> [, ...]
|
||||
FROM { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...]
|
||||
[ RESTRICT ]
|
||||
[ CASCADE | RESTRICT ]
|
||||
|
||||
REVOKE { { CREATE | TEMPORARY | TEMP } [,...] | ALL [ PRIVILEGES ] }
|
||||
REVOKE [ GRANT OPTION FOR ]
|
||||
{ { CREATE | TEMPORARY | TEMP } [,...] | ALL [ PRIVILEGES ] }
|
||||
ON DATABASE <replaceable>dbname</replaceable> [, ...]
|
||||
FROM { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...]
|
||||
[ RESTRICT ]
|
||||
[ CASCADE | RESTRICT ]
|
||||
|
||||
REVOKE { EXECUTE | ALL [ PRIVILEGES ] }
|
||||
REVOKE [ GRANT OPTION FOR ]
|
||||
{ EXECUTE | ALL [ PRIVILEGES ] }
|
||||
ON FUNCTION <replaceable>funcname</replaceable> ([<replaceable>type</replaceable>, ...]) [, ...]
|
||||
FROM { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...]
|
||||
[ RESTRICT ]
|
||||
[ CASCADE | RESTRICT ]
|
||||
|
||||
REVOKE { USAGE | ALL [ PRIVILEGES ] }
|
||||
REVOKE [ GRANT OPTION FOR ]
|
||||
{ USAGE | ALL [ PRIVILEGES ] }
|
||||
ON LANGUAGE <replaceable>langname</replaceable> [, ...]
|
||||
FROM { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...]
|
||||
[ RESTRICT ]
|
||||
[ CASCADE | RESTRICT ]
|
||||
|
||||
REVOKE { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
REVOKE [ GRANT OPTION FOR ]
|
||||
{ { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
ON SCHEMA <replaceable>schemaname</replaceable> [, ...]
|
||||
FROM { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable class="PARAMETER">groupname</replaceable> | PUBLIC } [, ...]
|
||||
[ RESTRICT ]
|
||||
[ CASCADE | RESTRICT ]
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -70,8 +75,22 @@ REVOKE { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <literal>RESTRICT</literal> key word is currently only noise.
|
||||
See also the compatibility notes below.
|
||||
If <literal>GRANT OPTION FOR</literal> is specified, only the grant
|
||||
option for the privilege is revoked, not the privilege itself.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If a user holds a privilege with grant option and has granted it to
|
||||
other users then the privileges held by those other users are
|
||||
called dependent privileges. If the privilege or the grant option
|
||||
held by the first user is being revoked and dependent privileges
|
||||
exist, those dependent privileges are also revoked if
|
||||
<literal>CASCADE</literal> is specified, else the revoke action
|
||||
will fail. This recursive revocation only affects privileges that
|
||||
were granted through a chain of users that is traceable to the user
|
||||
that is the subject of this <literal>REVOKE</literal> command.
|
||||
Thus, the affected users may effectively keep the privilege if it
|
||||
was also granted through other users.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
@ -83,6 +102,16 @@ REVOKE { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
display the privileges granted on existing objects. See also <xref
|
||||
linkend="sql-grant" endterm="sql-grant-title"> for information about the format.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A user can only revoke privileges that were granted directly by
|
||||
that user. If, for example, user A has granted a privilege with
|
||||
grant option to user B, and user B has in turned granted it to user
|
||||
C, then user A cannot revoke the privilege directly from C.
|
||||
Instead, user A could revoke the grant option from user B and use
|
||||
the <literal>CASCADE</literal> option so that the privilege is
|
||||
automatically revoked from user C.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="SQL-REVOKE-examples">
|
||||
@ -122,16 +151,8 @@ REVOKE [ GRANT OPTION FOR ] { SELECT | INSERT | UPDATE | DELETE | REFERENCES }
|
||||
FROM { PUBLIC | <replaceable class="parameter">username</replaceable> [, ...] }
|
||||
{ RESTRICT | CASCADE }
|
||||
</synopsis>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If user1 gives a privilege WITH GRANT OPTION to user2,
|
||||
and user2 gives it to user3 then user1 can revoke
|
||||
this privilege in cascade using the CASCADE keyword.
|
||||
If user1 gives a privilege WITH GRANT OPTION to user2,
|
||||
and user2 gives it to user3, then if user1 tries to revoke
|
||||
this privilege it fails if he specifies the RESTRICT
|
||||
keyword.
|
||||
One of <literal>RESTRICT</literal> or <literal>CASCADE</literal>
|
||||
is required.
|
||||
</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.179 2003/01/20 18:54:44 tgl Exp $
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.180 2003/01/23 23:38:51 petere Exp $
|
||||
-->
|
||||
|
||||
<appendix id="release">
|
||||
@ -38,6 +38,7 @@ ON COMMIT options for temp tables
|
||||
extra_float_digits option allows pg_dump to dump float data accurately
|
||||
Long options for psql and pg_dump are now available on all platforms
|
||||
Read-only transactions
|
||||
Object owners can allow grantees to grant the privilege to others (grant option)
|
||||
]]></literallayout>
|
||||
|
||||
</sect1>
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.79 2002/12/04 05:18:31 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.80 2003/01/23 23:38:55 petere Exp $
|
||||
*
|
||||
* NOTES
|
||||
* See acl.h.
|
||||
@ -47,7 +47,7 @@ static void ExecuteGrantStmt_Namespace(GrantStmt *stmt);
|
||||
|
||||
static const char *privilege_to_string(AclMode privilege);
|
||||
|
||||
static AclResult aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode);
|
||||
static AclResult aclcheck(Acl *acl, AclId userid, AclMode mode);
|
||||
|
||||
|
||||
#ifdef ACLDEBUG
|
||||
@ -75,7 +75,8 @@ dumpacl(Acl *acl)
|
||||
*/
|
||||
static Acl *
|
||||
merge_acl_with_grant(Acl *old_acl, bool is_grant,
|
||||
List *grantees, AclMode privileges)
|
||||
List *grantees, AclMode privileges,
|
||||
bool grant_option, DropBehavior behavior)
|
||||
{
|
||||
unsigned modechg;
|
||||
List *j;
|
||||
@ -96,26 +97,38 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant,
|
||||
|
||||
if (grantee->username)
|
||||
{
|
||||
aclitem. ai_id = get_usesysid(grantee->username);
|
||||
|
||||
aclitem.ai_grantee = get_usesysid(grantee->username);
|
||||
idtype = ACL_IDTYPE_UID;
|
||||
}
|
||||
else if (grantee->groupname)
|
||||
{
|
||||
aclitem. ai_id = get_grosysid(grantee->groupname);
|
||||
|
||||
aclitem.ai_grantee = get_grosysid(grantee->groupname);
|
||||
idtype = ACL_IDTYPE_GID;
|
||||
}
|
||||
else
|
||||
{
|
||||
aclitem. ai_id = ACL_ID_WORLD;
|
||||
|
||||
aclitem.ai_grantee = ACL_ID_WORLD;
|
||||
idtype = ACL_IDTYPE_WORLD;
|
||||
}
|
||||
|
||||
ACLITEM_SET_PRIVS_IDTYPE(aclitem, privileges, idtype);
|
||||
/*
|
||||
* Grant options can only be granted to individual users, not
|
||||
* groups or public. The reason is that if a user would
|
||||
* re-grant a privilege that he held through a group having a
|
||||
* grant option, and later the user is removed from the group,
|
||||
* the situation is impossible to clean up.
|
||||
*/
|
||||
if (is_grant && idtype != ACL_IDTYPE_UID && grant_option)
|
||||
elog(ERROR, "grant options can only be granted to individual users");
|
||||
|
||||
new_acl = aclinsert3(new_acl, &aclitem, modechg);
|
||||
aclitem.ai_grantor = GetUserId();
|
||||
|
||||
ACLITEM_SET_PRIVS_IDTYPE(aclitem,
|
||||
(is_grant || !grant_option) ? privileges : ACL_NO_RIGHTS,
|
||||
(grant_option || !is_grant) ? privileges : ACL_NO_RIGHTS,
|
||||
idtype);
|
||||
|
||||
new_acl = aclinsert3(new_acl, &aclitem, modechg, behavior);
|
||||
|
||||
#ifdef ACLDEBUG
|
||||
dumpacl(new_acl);
|
||||
@ -202,8 +215,10 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
|
||||
elog(ERROR, "relation %u not found", relOid);
|
||||
pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
|
||||
|
||||
if (!pg_class_ownercheck(relOid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, relvar->relname);
|
||||
if (stmt->is_grant
|
||||
&& !pg_class_ownercheck(relOid, GetUserId())
|
||||
&& pg_class_aclcheck(relOid, GetUserId(), ACL_GRANT_OPTION_FOR(privileges)) != ACLCHECK_OK)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, relvar->relname);
|
||||
|
||||
if (pg_class_tuple->relkind == RELKIND_INDEX)
|
||||
elog(ERROR, "\"%s\" is an index",
|
||||
@ -223,7 +238,8 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
|
||||
old_acl = DatumGetAclPCopy(aclDatum);
|
||||
|
||||
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
|
||||
stmt->grantees, privileges);
|
||||
stmt->grantees, privileges,
|
||||
stmt->grant_option, stmt->behavior);
|
||||
|
||||
/* finished building new ACL value, now insert it */
|
||||
MemSet(values, 0, sizeof(values));
|
||||
@ -298,8 +314,10 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
|
||||
elog(ERROR, "database \"%s\" not found", dbname);
|
||||
pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple);
|
||||
|
||||
if (!superuser() && pg_database_tuple->datdba != GetUserId())
|
||||
elog(ERROR, "permission denied");
|
||||
if (stmt->is_grant
|
||||
&& pg_database_tuple->datdba != GetUserId()
|
||||
&& pg_database_aclcheck(HeapTupleGetOid(tuple), GetUserId(), ACL_GRANT_OPTION_FOR(privileges)) != ACLCHECK_OK)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, NameStr(pg_database_tuple->datname));
|
||||
|
||||
/*
|
||||
* If there's no ACL, create a default.
|
||||
@ -314,7 +332,8 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
|
||||
old_acl = DatumGetAclPCopy(aclDatum);
|
||||
|
||||
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
|
||||
stmt->grantees, privileges);
|
||||
stmt->grantees, privileges,
|
||||
stmt->grant_option, stmt->behavior);
|
||||
|
||||
/* finished building new ACL value, now insert it */
|
||||
MemSet(values, 0, sizeof(values));
|
||||
@ -389,8 +408,10 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
|
||||
elog(ERROR, "function %u not found", oid);
|
||||
pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple);
|
||||
|
||||
if (!pg_proc_ownercheck(oid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER,
|
||||
if (stmt->is_grant
|
||||
&& !pg_proc_ownercheck(oid, GetUserId())
|
||||
&& pg_proc_aclcheck(oid, GetUserId(), ACL_GRANT_OPTION_FOR(privileges)) != ACLCHECK_OK)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV,
|
||||
NameStr(pg_proc_tuple->proname));
|
||||
|
||||
/*
|
||||
@ -407,7 +428,8 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
|
||||
old_acl = DatumGetAclPCopy(aclDatum);
|
||||
|
||||
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
|
||||
stmt->grantees, privileges);
|
||||
stmt->grantees, privileges,
|
||||
stmt->grant_option, stmt->behavior);
|
||||
|
||||
/* finished building new ACL value, now insert it */
|
||||
MemSet(values, 0, sizeof(values));
|
||||
@ -470,9 +492,6 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
|
||||
char nulls[Natts_pg_language];
|
||||
char replaces[Natts_pg_language];
|
||||
|
||||
if (!superuser())
|
||||
elog(ERROR, "permission denied");
|
||||
|
||||
relation = heap_openr(LanguageRelationName, RowExclusiveLock);
|
||||
tuple = SearchSysCache(LANGNAME,
|
||||
PointerGetDatum(langname),
|
||||
@ -484,6 +503,11 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
|
||||
if (!pg_language_tuple->lanpltrusted && stmt->is_grant)
|
||||
elog(ERROR, "language \"%s\" is not trusted", langname);
|
||||
|
||||
if (stmt->is_grant
|
||||
&& !superuser()
|
||||
&& pg_language_aclcheck(HeapTupleGetOid(tuple), GetUserId(), ACL_GRANT_OPTION_FOR(privileges)) != ACLCHECK_OK)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, NameStr(pg_language_tuple->lanname));
|
||||
|
||||
/*
|
||||
* If there's no ACL, create a default.
|
||||
*/
|
||||
@ -497,7 +521,8 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
|
||||
old_acl = DatumGetAclPCopy(aclDatum);
|
||||
|
||||
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
|
||||
stmt->grantees, privileges);
|
||||
stmt->grantees, privileges,
|
||||
stmt->grant_option, stmt->behavior);
|
||||
|
||||
/* finished building new ACL value, now insert it */
|
||||
MemSet(values, 0, sizeof(values));
|
||||
@ -568,8 +593,10 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
|
||||
elog(ERROR, "namespace \"%s\" not found", nspname);
|
||||
pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple);
|
||||
|
||||
if (!pg_namespace_ownercheck(HeapTupleGetOid(tuple), GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, nspname);
|
||||
if (stmt->is_grant
|
||||
&& !pg_namespace_ownercheck(HeapTupleGetOid(tuple), GetUserId())
|
||||
&& pg_namespace_aclcheck(HeapTupleGetOid(tuple), GetUserId(), ACL_GRANT_OPTION_FOR(privileges)) != ACLCHECK_OK)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, nspname);
|
||||
|
||||
/*
|
||||
* If there's no ACL, create a default using the
|
||||
@ -586,7 +613,8 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
|
||||
old_acl = DatumGetAclPCopy(aclDatum);
|
||||
|
||||
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
|
||||
stmt->grantees, privileges);
|
||||
stmt->grantees, privileges,
|
||||
stmt->grant_option, stmt->behavior);
|
||||
|
||||
/* finished building new ACL value, now insert it */
|
||||
MemSet(values, 0, sizeof(values));
|
||||
@ -741,125 +769,53 @@ in_group(AclId uid, AclId gid)
|
||||
/*
|
||||
* aclcheck
|
||||
*
|
||||
* Returns ACLCHECK_OK if the 'id' of type 'idtype' has ACL entries in 'acl'
|
||||
* to satisfy any one of the requirements of 'mode'. Returns an appropriate
|
||||
* ACLCHECK_* error code otherwise.
|
||||
*
|
||||
* The ACL list is expected to be sorted in standard order.
|
||||
* Returns ACLCHECK_OK if the 'userid' has ACL entries in 'acl' to
|
||||
* satisfy any one of the requirements of 'mode'. Returns an
|
||||
* appropriate ACLCHECK_* error code otherwise.
|
||||
*/
|
||||
static AclResult
|
||||
aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode)
|
||||
aclcheck(Acl *acl, AclId userid, AclMode mode)
|
||||
{
|
||||
AclItem *aip,
|
||||
*aidat;
|
||||
AclItem *aidat;
|
||||
int i,
|
||||
num;
|
||||
|
||||
/*
|
||||
* If ACL is null, default to "OK" --- this should not happen, since
|
||||
* caller should have inserted appropriate default
|
||||
* Null ACL should not happen, since caller should have inserted
|
||||
* appropriate default
|
||||
*/
|
||||
if (!acl)
|
||||
if (acl == NULL)
|
||||
{
|
||||
elog(DEBUG1, "aclcheck: null ACL, returning OK");
|
||||
return ACLCHECK_OK;
|
||||
elog(ERROR, "aclcheck: internal error -- null ACL");
|
||||
return ACLCHECK_NO_PRIV;
|
||||
}
|
||||
|
||||
num = ACL_NUM(acl);
|
||||
aidat = ACL_DAT(acl);
|
||||
|
||||
/*
|
||||
* We'll treat the empty ACL like that, too, although this is more
|
||||
* like an error (i.e., you manually blew away your ACL array) -- the
|
||||
* system never creates an empty ACL, since there must always be a
|
||||
* "world" entry in the first slot.
|
||||
* See if privilege is granted directly to user or to public
|
||||
*/
|
||||
if (num < 1)
|
||||
for (i = 0; i < num; i++)
|
||||
if (ACLITEM_GET_IDTYPE(aidat[i]) == ACL_IDTYPE_WORLD
|
||||
|| (ACLITEM_GET_IDTYPE(aidat[i]) == ACL_IDTYPE_UID
|
||||
&& aidat[i].ai_grantee == userid))
|
||||
{
|
||||
elog(DEBUG1, "aclcheck: zero-length ACL, returning OK");
|
||||
if (aidat[i].ai_privs & mode)
|
||||
return ACLCHECK_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* "World" rights are applicable regardless of the passed-in ID, and
|
||||
* since they're much the cheapest to check, check 'em first.
|
||||
* See if he has the permission via any group (do this in a
|
||||
* separate pass to avoid expensive(?) lookups in pg_group)
|
||||
*/
|
||||
if (ACLITEM_GET_IDTYPE(*aidat) != ACL_IDTYPE_WORLD)
|
||||
elog(ERROR, "aclcheck: first entry in ACL is not 'world' entry");
|
||||
if (aidat->ai_privs & mode)
|
||||
{
|
||||
#ifdef ACLDEBUG
|
||||
elog(DEBUG1, "aclcheck: using world=%d", ACLITEM_GET_PRIVS(*aidat));
|
||||
#endif
|
||||
for (i = 0; i < num; i++)
|
||||
if (ACLITEM_GET_IDTYPE(aidat[i]) == ACL_IDTYPE_GID
|
||||
&& aidat[i].ai_privs & mode
|
||||
&& in_group(userid, aidat[i].ai_grantee))
|
||||
return ACLCHECK_OK;
|
||||
}
|
||||
|
||||
switch (idtype)
|
||||
{
|
||||
case ACL_IDTYPE_UID:
|
||||
/* See if permission is granted directly to user */
|
||||
for (i = 1, aip = aidat + 1; /* skip world entry */
|
||||
i < num && ACLITEM_GET_IDTYPE(*aip) == ACL_IDTYPE_UID;
|
||||
++i, ++aip)
|
||||
{
|
||||
if (aip->ai_id == id)
|
||||
{
|
||||
#ifdef ACLDEBUG
|
||||
elog(DEBUG1, "aclcheck: found user %u/%d",
|
||||
aip->ai_id, ACLITEM_GET_PRIVS(*aip));
|
||||
#endif
|
||||
if (aip->ai_privs & mode)
|
||||
return ACLCHECK_OK;
|
||||
}
|
||||
}
|
||||
/* See if he has the permission via any group */
|
||||
for (;
|
||||
i < num && ACLITEM_GET_IDTYPE(*aip) == ACL_IDTYPE_GID;
|
||||
++i, ++aip)
|
||||
{
|
||||
if (aip->ai_privs & mode)
|
||||
{
|
||||
if (in_group(id, aip->ai_id))
|
||||
{
|
||||
#ifdef ACLDEBUG
|
||||
elog(DEBUG1, "aclcheck: found group %u/%d",
|
||||
aip->ai_id, ACLITEM_GET_PRIVS(*aip));
|
||||
#endif
|
||||
return ACLCHECK_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACL_IDTYPE_GID:
|
||||
/* Look for this group ID */
|
||||
for (i = 1, aip = aidat + 1; /* skip world entry */
|
||||
i < num && ACLITEM_GET_IDTYPE(*aip) == ACL_IDTYPE_UID;
|
||||
++i, ++aip)
|
||||
/* skip UID entry */ ;
|
||||
for (;
|
||||
i < num && ACLITEM_GET_IDTYPE(*aip) == ACL_IDTYPE_GID;
|
||||
++i, ++aip)
|
||||
{
|
||||
if (aip->ai_id == id)
|
||||
{
|
||||
#ifdef ACLDEBUG
|
||||
elog(DEBUG1, "aclcheck: found group %u/%d",
|
||||
aip->ai_id, ACLITEM_GET_PRIVS(*aip));
|
||||
#endif
|
||||
if (aip->ai_privs & mode)
|
||||
return ACLCHECK_OK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACL_IDTYPE_WORLD:
|
||||
/* Only check the world entry */
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "aclcheck: bogus ACL id type: %d", idtype);
|
||||
break;
|
||||
}
|
||||
|
||||
/* If get here, he doesn't have the privilege nohow */
|
||||
/* If here, doesn't have the privilege. */
|
||||
return ACLCHECK_NO_PRIV;
|
||||
}
|
||||
|
||||
@ -976,7 +932,7 @@ pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode)
|
||||
acl = DatumGetAclP(aclDatum);
|
||||
}
|
||||
|
||||
result = aclcheck(acl, userid, ACL_IDTYPE_UID, mode);
|
||||
result = aclcheck(acl, userid, mode);
|
||||
|
||||
/* if we have a detoasted copy, free it */
|
||||
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
||||
@ -1038,7 +994,7 @@ pg_database_aclcheck(Oid db_oid, AclId userid, AclMode mode)
|
||||
acl = DatumGetAclP(aclDatum);
|
||||
}
|
||||
|
||||
result = aclcheck(acl, userid, ACL_IDTYPE_UID, mode);
|
||||
result = aclcheck(acl, userid, mode);
|
||||
|
||||
/* if we have a detoasted copy, free it */
|
||||
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
||||
@ -1092,7 +1048,7 @@ pg_proc_aclcheck(Oid proc_oid, AclId userid, AclMode mode)
|
||||
acl = DatumGetAclP(aclDatum);
|
||||
}
|
||||
|
||||
result = aclcheck(acl, userid, ACL_IDTYPE_UID, mode);
|
||||
result = aclcheck(acl, userid, mode);
|
||||
|
||||
/* if we have a detoasted copy, free it */
|
||||
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
||||
@ -1142,7 +1098,7 @@ pg_language_aclcheck(Oid lang_oid, AclId userid, AclMode mode)
|
||||
acl = DatumGetAclP(aclDatum);
|
||||
}
|
||||
|
||||
result = aclcheck(acl, userid, ACL_IDTYPE_UID, mode);
|
||||
result = aclcheck(acl, userid, mode);
|
||||
|
||||
/* if we have a detoasted copy, free it */
|
||||
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
||||
@ -1202,7 +1158,7 @@ pg_namespace_aclcheck(Oid nsp_oid, AclId userid, AclMode mode)
|
||||
acl = DatumGetAclP(aclDatum);
|
||||
}
|
||||
|
||||
result = aclcheck(acl, userid, ACL_IDTYPE_UID, mode);
|
||||
result = aclcheck(acl, userid, mode);
|
||||
|
||||
/* if we have a detoasted copy, free it */
|
||||
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
||||
|
@ -72,7 +72,7 @@ E081 Basic Privileges 04 UPDATE privilege at the table level YES
|
||||
E081 Basic Privileges 05 UPDATE privilege at the column level NO
|
||||
E081 Basic Privileges 06 REFERENCES privilege at the table level YES
|
||||
E081 Basic Privileges 07 REFERENCES privilege at the column level NO
|
||||
E081 Basic Privileges 08 WITH GRANT OPTION NO
|
||||
E081 Basic Privileges 08 WITH GRANT OPTION YES
|
||||
E091 Set functions YES
|
||||
E091 Set functions 01 AVG YES
|
||||
E091 Set functions 02 COUNT YES
|
||||
@ -133,10 +133,10 @@ F031 Basic schema manipulation 16 DROP VIEW statement: RESTRICT clause YES
|
||||
F031 Basic schema manipulation 19 REVOKE statement: RESTRICT clause YES
|
||||
F032 CASCADE drop behavior YES
|
||||
F033 ALTER TABLE statement: DROP COLUMN clause YES
|
||||
F034 Extended REVOKE statement NO
|
||||
F034 Extended REVOKE statement 01 REVOKE statement performed by other than the owner of a schema object NO
|
||||
F034 Extended REVOKE statement 02 REVOKE statement: GRANT OPTION FOR clause NO
|
||||
F034 Extended REVOKE statement 03 REVOKE statement to revoke a privilege that the grantee has WITH GRANT OPTION NO
|
||||
F034 Extended REVOKE statement YES
|
||||
F034 Extended REVOKE statement 01 REVOKE statement performed by other than the owner of a schema object YES
|
||||
F034 Extended REVOKE statement 02 REVOKE statement: GRANT OPTION FOR clause YES
|
||||
F034 Extended REVOKE statement 03 REVOKE statement to revoke a privilege that the grantee has WITH GRANT OPTION YES
|
||||
F041 Basic joined table YES
|
||||
F041 Basic joined table 01 Inner join (but not necessarily the INNER keyword) YES
|
||||
F041 Basic joined table 02 INNER keyword YES
|
||||
|
@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.237 2003/01/20 18:54:46 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.238 2003/01/23 23:38:56 petere Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1563,6 +1563,8 @@ _copyGrantStmt(GrantStmt *from)
|
||||
COPY_NODE_FIELD(objects);
|
||||
COPY_INTLIST_FIELD(privileges);
|
||||
COPY_NODE_FIELD(grantees);
|
||||
COPY_SCALAR_FIELD(grant_option);
|
||||
COPY_SCALAR_FIELD(behavior);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.181 2003/01/20 18:54:46 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.182 2003/01/23 23:38:56 petere Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -635,6 +635,8 @@ _equalGrantStmt(GrantStmt *a, GrantStmt *b)
|
||||
COMPARE_NODE_FIELD(objects);
|
||||
COMPARE_INTLIST_FIELD(privileges);
|
||||
COMPARE_NODE_FIELD(grantees);
|
||||
COMPARE_SCALAR_FIELD(grant_option);
|
||||
COMPARE_SCALAR_FIELD(behavior);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.395 2003/01/10 22:03:27 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.396 2003/01/23 23:38:56 petere Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -163,6 +163,7 @@ static void doNegateFloat(Value *v);
|
||||
|
||||
%type <ival> opt_lock lock_type cast_context
|
||||
%type <boolean> opt_force opt_or_replace transaction_access_mode
|
||||
opt_grant_grant_option opt_revoke_grant_option
|
||||
|
||||
%type <list> user_list
|
||||
|
||||
@ -2737,6 +2738,7 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
|
||||
n->objtype = ($4)->objtype;
|
||||
n->objects = ($4)->objs;
|
||||
n->grantees = $6;
|
||||
n->grant_option = $7;
|
||||
$$ = (Node*)n;
|
||||
}
|
||||
;
|
||||
@ -2750,9 +2752,8 @@ RevokeStmt: REVOKE opt_revoke_grant_option privileges ON privilege_target
|
||||
n->objtype = ($5)->objtype;
|
||||
n->objects = ($5)->objs;
|
||||
n->grantees = $7;
|
||||
|
||||
if ($8 == DROP_CASCADE)
|
||||
elog(ERROR, "REVOKE ... CASCADE is not implemented");
|
||||
n->grant_option = $2;
|
||||
n->behavior = $8;
|
||||
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
@ -2867,19 +2868,13 @@ grantee: ColId
|
||||
|
||||
|
||||
opt_grant_grant_option:
|
||||
WITH GRANT OPTION
|
||||
{
|
||||
elog(ERROR, "grant options are not implemented");
|
||||
}
|
||||
| /*EMPTY*/
|
||||
WITH GRANT OPTION { $$ = TRUE; }
|
||||
| /*EMPTY*/ { $$ = FALSE; }
|
||||
;
|
||||
|
||||
opt_revoke_grant_option:
|
||||
GRANT OPTION FOR
|
||||
{
|
||||
elog(ERROR, "grant options are not implemented");
|
||||
}
|
||||
| /*EMPTY*/
|
||||
GRANT OPTION FOR { $$ = TRUE; }
|
||||
| /*EMPTY*/ { $$ = FALSE; }
|
||||
;
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.84 2002/12/05 04:04:42 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.85 2003/01/23 23:39:01 petere Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -32,9 +32,10 @@
|
||||
|
||||
static const char *getid(const char *s, char *n);
|
||||
static Acl *makeacl(int n);
|
||||
static const char *aclparse(const char *s, AclItem *aip, unsigned *modechg);
|
||||
static const char *aclparse(const char *s, AclItem *aip);
|
||||
static bool aclitemeq(const AclItem *a1, const AclItem *a2);
|
||||
static bool aclitemgt(const AclItem *a1, const AclItem *a2);
|
||||
static Acl *recursive_revoke(Acl *acl, AclId grantee,
|
||||
AclMode revoke_privs, DropBehavior behavior);
|
||||
|
||||
static Oid convert_table_name(text *tablename);
|
||||
static AclMode convert_table_priv_string(text *priv_type_text);
|
||||
@ -102,7 +103,7 @@ getid(const char *s, char *n)
|
||||
/*
|
||||
* aclparse
|
||||
* Consumes and parses an ACL specification of the form:
|
||||
* [group|user] [A-Za-z0-9]*[+-=][rwaR]*
|
||||
* [group|user] [A-Za-z0-9]*=[rwaR]*
|
||||
* from string 's', ignoring any leading white space or white space
|
||||
* between the optional id type keyword (group|user) and the actual
|
||||
* ACL specification.
|
||||
@ -115,25 +116,23 @@ getid(const char *s, char *n)
|
||||
* specification. Also:
|
||||
* - loads the structure pointed to by 'aip' with the appropriate
|
||||
* UID/GID, id type identifier and mode type values.
|
||||
* - loads 'modechg' with the mode change flag.
|
||||
*/
|
||||
static const char *
|
||||
aclparse(const char *s, AclItem *aip, unsigned *modechg)
|
||||
aclparse(const char *s, AclItem *aip)
|
||||
{
|
||||
AclMode privs;
|
||||
AclMode privs, goption, read;
|
||||
uint32 idtype;
|
||||
char name[NAMEDATALEN];
|
||||
char name2[NAMEDATALEN];
|
||||
|
||||
Assert(s && aip && modechg);
|
||||
Assert(s && aip);
|
||||
|
||||
#ifdef ACLDEBUG
|
||||
elog(LOG, "aclparse: input = '%s'", s);
|
||||
#endif
|
||||
idtype = ACL_IDTYPE_UID;
|
||||
s = getid(s, name);
|
||||
if (*s != ACL_MODECHG_ADD_CHR &&
|
||||
*s != ACL_MODECHG_DEL_CHR &&
|
||||
*s != ACL_MODECHG_EQL_CHR)
|
||||
if (*s != '=')
|
||||
{
|
||||
/* we just read a keyword, not a name */
|
||||
if (strncmp(name, ACL_IDTYPE_GID_KEYWORD, sizeof(name)) == 0)
|
||||
@ -147,87 +146,93 @@ aclparse(const char *s, AclItem *aip, unsigned *modechg)
|
||||
if (name[0] == '\0')
|
||||
idtype = ACL_IDTYPE_WORLD;
|
||||
|
||||
switch (*s)
|
||||
{
|
||||
case ACL_MODECHG_ADD_CHR:
|
||||
*modechg = ACL_MODECHG_ADD;
|
||||
break;
|
||||
case ACL_MODECHG_DEL_CHR:
|
||||
*modechg = ACL_MODECHG_DEL;
|
||||
break;
|
||||
case ACL_MODECHG_EQL_CHR:
|
||||
*modechg = ACL_MODECHG_EQL;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "aclparse: mode change flag must use \"%c%c%c\"",
|
||||
ACL_MODECHG_ADD_CHR,
|
||||
ACL_MODECHG_DEL_CHR,
|
||||
ACL_MODECHG_EQL_CHR);
|
||||
}
|
||||
if (*s != '=')
|
||||
elog(ERROR, "aclparse: expecting \"=\" sign");
|
||||
|
||||
privs = ACL_NO_RIGHTS;
|
||||
privs = goption = ACL_NO_RIGHTS;
|
||||
|
||||
while (isalpha((unsigned char) *++s))
|
||||
for (++s, read=0; isalpha((unsigned char) *s) || *s == '*'; s++)
|
||||
{
|
||||
switch (*s)
|
||||
{
|
||||
case '*':
|
||||
goption |= read;
|
||||
break;
|
||||
case ACL_INSERT_CHR:
|
||||
privs |= ACL_INSERT;
|
||||
read = ACL_INSERT;
|
||||
break;
|
||||
case ACL_SELECT_CHR:
|
||||
privs |= ACL_SELECT;
|
||||
read = ACL_SELECT;
|
||||
break;
|
||||
case ACL_UPDATE_CHR:
|
||||
privs |= ACL_UPDATE;
|
||||
read = ACL_UPDATE;
|
||||
break;
|
||||
case ACL_DELETE_CHR:
|
||||
privs |= ACL_DELETE;
|
||||
read = ACL_DELETE;
|
||||
break;
|
||||
case ACL_RULE_CHR:
|
||||
privs |= ACL_RULE;
|
||||
read = ACL_RULE;
|
||||
break;
|
||||
case ACL_REFERENCES_CHR:
|
||||
privs |= ACL_REFERENCES;
|
||||
read = ACL_REFERENCES;
|
||||
break;
|
||||
case ACL_TRIGGER_CHR:
|
||||
privs |= ACL_TRIGGER;
|
||||
read = ACL_TRIGGER;
|
||||
break;
|
||||
case ACL_EXECUTE_CHR:
|
||||
privs |= ACL_EXECUTE;
|
||||
read = ACL_EXECUTE;
|
||||
break;
|
||||
case ACL_USAGE_CHR:
|
||||
privs |= ACL_USAGE;
|
||||
read = ACL_USAGE;
|
||||
break;
|
||||
case ACL_CREATE_CHR:
|
||||
privs |= ACL_CREATE;
|
||||
read = ACL_CREATE;
|
||||
break;
|
||||
case ACL_CREATE_TEMP_CHR:
|
||||
privs |= ACL_CREATE_TEMP;
|
||||
read = ACL_CREATE_TEMP;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "aclparse: mode flags must use \"%s\"",
|
||||
ACL_ALL_RIGHTS_STR);
|
||||
}
|
||||
|
||||
privs |= read;
|
||||
}
|
||||
|
||||
switch (idtype)
|
||||
{
|
||||
case ACL_IDTYPE_UID:
|
||||
aip->ai_id = get_usesysid(name);
|
||||
aip->ai_grantee = get_usesysid(name);
|
||||
break;
|
||||
case ACL_IDTYPE_GID:
|
||||
aip->ai_id = get_grosysid(name);
|
||||
aip->ai_grantee = get_grosysid(name);
|
||||
break;
|
||||
case ACL_IDTYPE_WORLD:
|
||||
aip->ai_id = ACL_ID_WORLD;
|
||||
aip->ai_grantee = ACL_ID_WORLD;
|
||||
break;
|
||||
}
|
||||
|
||||
ACLITEM_SET_PRIVS_IDTYPE(*aip, privs, idtype);
|
||||
/* XXX Allow a degree of backward compatibility by defaulting the
|
||||
* grantor to the superuser. */
|
||||
if (*s == '/')
|
||||
{
|
||||
s = getid(s + 1, name2);
|
||||
if (name2[0] == '\0')
|
||||
elog(ERROR, "aclparse: a name must follow the \"/\" sign");
|
||||
|
||||
aip->ai_grantor = get_usesysid(name2);
|
||||
}
|
||||
else
|
||||
{
|
||||
aip->ai_grantor = BOOTSTRAP_USESYSID;
|
||||
elog(WARNING, "defaulting grantor to %u", BOOTSTRAP_USESYSID);
|
||||
}
|
||||
|
||||
ACLITEM_SET_PRIVS_IDTYPE(*aip, privs, goption, idtype);
|
||||
|
||||
#ifdef ACLDEBUG
|
||||
elog(LOG, "aclparse: correctly read [%x %d %x], modechg=%x",
|
||||
idtype, aip->ai_id, privs, *modechg);
|
||||
elog(LOG, "aclparse: correctly read [%x %d %x]",
|
||||
idtype, aip->ai_grantee, privs);
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
@ -271,12 +276,9 @@ aclitemin(PG_FUNCTION_ARGS)
|
||||
{
|
||||
const char *s = PG_GETARG_CSTRING(0);
|
||||
AclItem *aip;
|
||||
unsigned modechg;
|
||||
|
||||
aip = (AclItem *) palloc(sizeof(AclItem));
|
||||
s = aclparse(s, aip, &modechg);
|
||||
if (modechg != ACL_MODECHG_EQL)
|
||||
elog(ERROR, "aclitemin: cannot accept anything but = ACLs");
|
||||
s = aclparse(s, aip);
|
||||
while (isspace((unsigned char) *s))
|
||||
++s;
|
||||
if (*s)
|
||||
@ -302,14 +304,14 @@ aclitemout(PG_FUNCTION_ARGS)
|
||||
unsigned i;
|
||||
char *tmpname;
|
||||
|
||||
p = out = palloc(strlen("group = ") + N_ACL_RIGHTS + NAMEDATALEN + 1);
|
||||
p = out = palloc(strlen("group = ") + 2 * N_ACL_RIGHTS + 2* NAMEDATALEN + 2);
|
||||
*p = '\0';
|
||||
|
||||
switch (ACLITEM_GET_IDTYPE(*aip))
|
||||
{
|
||||
case ACL_IDTYPE_UID:
|
||||
htup = SearchSysCache(SHADOWSYSID,
|
||||
ObjectIdGetDatum(aip->ai_id),
|
||||
ObjectIdGetDatum(aip->ai_grantee),
|
||||
0, 0, 0);
|
||||
if (HeapTupleIsValid(htup))
|
||||
{
|
||||
@ -324,14 +326,14 @@ aclitemout(PG_FUNCTION_ARGS)
|
||||
char *tmp;
|
||||
|
||||
tmp = DatumGetCString(DirectFunctionCall1(int4out,
|
||||
Int32GetDatum((int32) aip->ai_id)));
|
||||
Int32GetDatum((int32) aip->ai_grantee)));
|
||||
strcat(p, tmp);
|
||||
pfree(tmp);
|
||||
}
|
||||
break;
|
||||
case ACL_IDTYPE_GID:
|
||||
strcat(p, "group ");
|
||||
tmpname = get_groname(aip->ai_id);
|
||||
tmpname = get_groname(aip->ai_grantee);
|
||||
if (tmpname != NULL)
|
||||
strncat(p, tmpname, NAMEDATALEN);
|
||||
else
|
||||
@ -340,7 +342,7 @@ aclitemout(PG_FUNCTION_ARGS)
|
||||
char *tmp;
|
||||
|
||||
tmp = DatumGetCString(DirectFunctionCall1(int4out,
|
||||
Int32GetDatum((int32) aip->ai_id)));
|
||||
Int32GetDatum((int32) aip->ai_grantee)));
|
||||
strcat(p, tmp);
|
||||
pfree(tmp);
|
||||
}
|
||||
@ -354,10 +356,43 @@ aclitemout(PG_FUNCTION_ARGS)
|
||||
}
|
||||
while (*p)
|
||||
++p;
|
||||
|
||||
*p++ = '=';
|
||||
|
||||
for (i = 0; i < N_ACL_RIGHTS; ++i)
|
||||
if (aip->ai_privs & (1 << i))
|
||||
{
|
||||
if (ACLITEM_GET_PRIVS(*aip) & (1 << i))
|
||||
*p++ = ACL_ALL_RIGHTS_STR[i];
|
||||
if (ACLITEM_GET_GOPTIONS(*aip) & (1 << i))
|
||||
*p++ = '*';
|
||||
}
|
||||
|
||||
*p++ = '/';
|
||||
*p = '\0';
|
||||
|
||||
htup = SearchSysCache(SHADOWSYSID,
|
||||
ObjectIdGetDatum(aip->ai_grantor),
|
||||
0, 0, 0);
|
||||
if (HeapTupleIsValid(htup))
|
||||
{
|
||||
strncat(p,
|
||||
NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename),
|
||||
NAMEDATALEN);
|
||||
ReleaseSysCache(htup);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Generate numeric UID if we don't find an entry */
|
||||
char *tmp;
|
||||
|
||||
tmp = DatumGetCString(DirectFunctionCall1(int4out,
|
||||
Int32GetDatum((int32) aip->ai_grantor)));
|
||||
strcat(p, tmp);
|
||||
pfree(tmp);
|
||||
}
|
||||
|
||||
while (*p)
|
||||
++p;
|
||||
*p = '\0';
|
||||
|
||||
PG_RETURN_CSTRING(out);
|
||||
@ -365,29 +400,15 @@ aclitemout(PG_FUNCTION_ARGS)
|
||||
|
||||
/*
|
||||
* aclitemeq
|
||||
* aclitemgt
|
||||
* AclItem equality and greater-than comparison routines.
|
||||
* Two AclItems are considered equal iff they have the same
|
||||
* identifier (and identifier type); the privileges are ignored.
|
||||
* Note that these routines are really only useful for sorting
|
||||
* AclItems into identifier order.
|
||||
*
|
||||
* RETURNS:
|
||||
* a boolean value indicating = or >
|
||||
* grantee and grantor; the privileges are ignored.
|
||||
*/
|
||||
static bool
|
||||
aclitemeq(const AclItem *a1, const AclItem *a2)
|
||||
{
|
||||
return ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) &&
|
||||
a1->ai_id == a2->ai_id;
|
||||
}
|
||||
|
||||
static bool
|
||||
aclitemgt(const AclItem *a1, const AclItem *a2)
|
||||
{
|
||||
return ((ACLITEM_GET_IDTYPE(*a1) > ACLITEM_GET_IDTYPE(*a2)) ||
|
||||
(ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) &&
|
||||
a1->ai_id > a2->ai_id));
|
||||
a1->ai_grantee == a2->ai_grantee &&
|
||||
a1->ai_grantor == a2->ai_grantor;
|
||||
}
|
||||
|
||||
|
||||
@ -436,15 +457,25 @@ acldefault(GrantObjectType objtype, AclId ownerid)
|
||||
break;
|
||||
}
|
||||
|
||||
acl = makeacl(ownerid ? 2 : 1);
|
||||
acl = makeacl((world_default != ACL_NO_RIGHTS ? 1 : 0)
|
||||
+ (ownerid ? 1 : 0));
|
||||
aip = ACL_DAT(acl);
|
||||
|
||||
aip[0].ai_id = ACL_ID_WORLD;
|
||||
ACLITEM_SET_PRIVS_IDTYPE(aip[0], world_default, ACL_IDTYPE_WORLD);
|
||||
if (world_default != ACL_NO_RIGHTS)
|
||||
{
|
||||
aip[0].ai_grantee = ACL_ID_WORLD;
|
||||
aip[0].ai_grantor = ownerid;
|
||||
ACLITEM_SET_PRIVS_IDTYPE(aip[0], world_default, ACL_NO_RIGHTS, ACL_IDTYPE_WORLD);
|
||||
}
|
||||
|
||||
if (ownerid)
|
||||
{
|
||||
aip[1].ai_id = ownerid;
|
||||
ACLITEM_SET_PRIVS_IDTYPE(aip[1], owner_default, ACL_IDTYPE_UID);
|
||||
int index = (world_default != ACL_NO_RIGHTS ? 1: 0);
|
||||
|
||||
aip[index].ai_grantee = ownerid;
|
||||
aip[index].ai_grantor = ownerid;
|
||||
/* owner gets default privileges with grant option */
|
||||
ACLITEM_SET_PRIVS_IDTYPE(aip[index], owner_default, owner_default, ACL_IDTYPE_UID);
|
||||
}
|
||||
|
||||
return acl;
|
||||
@ -458,7 +489,7 @@ acldefault(GrantObjectType objtype, AclId ownerid)
|
||||
* NB: caller is responsible for having detoasted the input ACL, if needed.
|
||||
*/
|
||||
Acl *
|
||||
aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg)
|
||||
aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg, DropBehavior behavior)
|
||||
{
|
||||
Acl *new_acl;
|
||||
AclItem *old_aip,
|
||||
@ -480,49 +511,35 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg)
|
||||
old_aip = ACL_DAT(old_acl);
|
||||
|
||||
/*
|
||||
* Search the ACL for an existing entry for 'id'. 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 in
|
||||
* sort-order.
|
||||
* 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.
|
||||
*/
|
||||
/* find the first element not less than the element to be inserted */
|
||||
for (dst = 0; dst < num && aclitemgt(mod_aip, old_aip + dst); ++dst)
|
||||
;
|
||||
|
||||
if (dst < num && aclitemeq(mod_aip, old_aip + dst))
|
||||
for (dst = 0; dst < num; ++dst)
|
||||
{
|
||||
if (aclitemeq(mod_aip, old_aip + dst))
|
||||
{
|
||||
/* found a match, so modify existing item */
|
||||
new_acl = makeacl(num);
|
||||
new_aip = ACL_DAT(new_acl);
|
||||
memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
|
||||
memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
|
||||
break;
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
if (dst == num)
|
||||
{
|
||||
/* need to insert a new item */
|
||||
/* need to append a new item */
|
||||
new_acl = makeacl(num + 1);
|
||||
new_aip = ACL_DAT(new_acl);
|
||||
if (dst == 0)
|
||||
{ /* start */
|
||||
elog(ERROR, "aclinsert3: insertion before world ACL??");
|
||||
}
|
||||
else if (dst >= num)
|
||||
{ /* end */
|
||||
memcpy((char *) new_aip,
|
||||
(char *) old_aip,
|
||||
num * sizeof(AclItem));
|
||||
}
|
||||
else
|
||||
{ /* middle */
|
||||
memcpy((char *) new_aip,
|
||||
(char *) old_aip,
|
||||
dst * sizeof(AclItem));
|
||||
memcpy((char *) (new_aip + dst + 1),
|
||||
(char *) (old_aip + dst),
|
||||
(num - dst) * sizeof(AclItem));
|
||||
}
|
||||
memcpy(new_aip, old_aip, num * sizeof(AclItem));
|
||||
|
||||
/* initialize the new entry with no permissions */
|
||||
new_aip[dst].ai_id = mod_aip->ai_id;
|
||||
ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst], ACL_NO_RIGHTS,
|
||||
new_aip[dst].ai_grantee = mod_aip->ai_grantee;
|
||||
new_aip[dst].ai_grantor = mod_aip->ai_grantor;
|
||||
ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst], ACL_NO_RIGHTS, ACL_NO_RIGHTS,
|
||||
ACLITEM_GET_IDTYPE(*mod_aip));
|
||||
num++; /* set num to the size of new_acl */
|
||||
}
|
||||
@ -531,35 +548,89 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg)
|
||||
switch (modechg)
|
||||
{
|
||||
case ACL_MODECHG_ADD:
|
||||
new_aip[dst].ai_privs |= ACLITEM_GET_PRIVS(*mod_aip);
|
||||
ACLITEM_SET_PRIVS(new_aip[dst], ACLITEM_GET_PRIVS(new_aip[dst]) | ACLITEM_GET_PRIVS(*mod_aip));
|
||||
ACLITEM_SET_GOPTIONS(new_aip[dst], ACLITEM_GET_GOPTIONS(new_aip[dst]) | ACLITEM_GET_GOPTIONS(*mod_aip));
|
||||
break;
|
||||
case ACL_MODECHG_DEL:
|
||||
new_aip[dst].ai_privs &= ~ACLITEM_GET_PRIVS(*mod_aip);
|
||||
ACLITEM_SET_PRIVS(new_aip[dst], ACLITEM_GET_PRIVS(new_aip[dst]) & ~ACLITEM_GET_PRIVS(*mod_aip));
|
||||
ACLITEM_SET_GOPTIONS(new_aip[dst], ACLITEM_GET_GOPTIONS(new_aip[dst]) & ~ACLITEM_GET_GOPTIONS(*mod_aip));
|
||||
break;
|
||||
case ACL_MODECHG_EQL:
|
||||
ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst],
|
||||
ACLITEM_GET_PRIVS(*mod_aip),
|
||||
ACLITEM_GET_GOPTIONS(*mod_aip),
|
||||
ACLITEM_GET_IDTYPE(new_aip[dst]));
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the adjusted entry has no permissions, delete it from the list.
|
||||
* For example, this helps in removing entries for users who no longer
|
||||
* exist. EXCEPTION: never remove the world entry.
|
||||
* If the adjusted entry has no permissions, delete it from the list.
|
||||
*/
|
||||
if (ACLITEM_GET_PRIVS(new_aip[dst]) == ACL_NO_RIGHTS && dst > 0)
|
||||
if (ACLITEM_GET_PRIVS(new_aip[dst]) == ACL_NO_RIGHTS)
|
||||
{
|
||||
memmove((char *) (new_aip + dst),
|
||||
(char *) (new_aip + dst + 1),
|
||||
memmove(new_aip + dst,
|
||||
new_aip + dst + 1,
|
||||
(num - dst - 1) * sizeof(AclItem));
|
||||
ARR_DIMS(new_acl)[0] = num - 1;
|
||||
ARR_SIZE(new_acl) -= sizeof(AclItem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove abandoned privileges (cascading revoke)
|
||||
*/
|
||||
if (modechg != ACL_MODECHG_ADD
|
||||
&& ACLITEM_GET_IDTYPE(*mod_aip) == ACL_IDTYPE_UID
|
||||
&& ACLITEM_GET_GOPTIONS(*mod_aip))
|
||||
new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee, ACLITEM_GET_GOPTIONS(*mod_aip), behavior);
|
||||
|
||||
return new_acl;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Ensure that no privilege is "abandoned". A privilege is abandoned
|
||||
* if the user that granted the privilege loses the grant option. (So
|
||||
* the chain through which it was granted is broken.) Either the
|
||||
* abandoned privileges are revoked as well, or an error message is
|
||||
* printed, depending on the drop behavior option.
|
||||
*/
|
||||
static Acl *
|
||||
recursive_revoke(Acl *acl,
|
||||
AclId grantee,
|
||||
AclMode revoke_privs,
|
||||
DropBehavior behavior)
|
||||
{
|
||||
int i;
|
||||
|
||||
restart:
|
||||
for (i = 0; i < ACL_NUM(acl); i++)
|
||||
{
|
||||
AclItem *aip = ACL_DAT(acl);
|
||||
|
||||
if (aip[i].ai_grantor == grantee
|
||||
&& (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0)
|
||||
{
|
||||
AclItem mod_acl;
|
||||
|
||||
if (behavior == DROP_RESTRICT)
|
||||
elog(ERROR, "dependent privileges exist (use CASCADE to revoke them too)");
|
||||
|
||||
mod_acl.ai_grantor = grantee;
|
||||
mod_acl.ai_grantee = aip[i].ai_grantee;
|
||||
ACLITEM_SET_PRIVS_IDTYPE(mod_acl,
|
||||
revoke_privs,
|
||||
revoke_privs,
|
||||
ACLITEM_GET_IDTYPE(aip[i]));
|
||||
|
||||
acl = aclinsert3(acl, &mod_acl, ACL_MODECHG_DEL, behavior);
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
||||
return acl;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* aclinsert (exported function)
|
||||
*/
|
||||
@ -569,7 +640,7 @@ aclinsert(PG_FUNCTION_ARGS)
|
||||
Acl *old_acl = PG_GETARG_ACL_P(0);
|
||||
AclItem *mod_aip = PG_GETARG_ACLITEM_P(1);
|
||||
|
||||
PG_RETURN_ACL_P(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL));
|
||||
PG_RETURN_ACL_P(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL, DROP_CASCADE));
|
||||
}
|
||||
|
||||
Datum
|
||||
@ -649,7 +720,7 @@ aclcontains(PG_FUNCTION_ARGS)
|
||||
aidat = ACL_DAT(acl);
|
||||
for (i = 0; i < num; ++i)
|
||||
{
|
||||
if (aip->ai_id == aidat[i].ai_id &&
|
||||
if (aip->ai_grantee == aidat[i].ai_grantee &&
|
||||
aip->ai_privs == aidat[i].ai_privs)
|
||||
PG_RETURN_BOOL(true);
|
||||
}
|
||||
@ -842,24 +913,38 @@ convert_table_priv_string(text *priv_type_text)
|
||||
*/
|
||||
if (strcasecmp(priv_type, "SELECT") == 0)
|
||||
return ACL_SELECT;
|
||||
if (strcasecmp(priv_type, "SELECT WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_SELECT);
|
||||
|
||||
if (strcasecmp(priv_type, "INSERT") == 0)
|
||||
return ACL_INSERT;
|
||||
if (strcasecmp(priv_type, "INSERT WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_INSERT);
|
||||
|
||||
if (strcasecmp(priv_type, "UPDATE") == 0)
|
||||
return ACL_UPDATE;
|
||||
if (strcasecmp(priv_type, "UPDATE WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_UPDATE);
|
||||
|
||||
if (strcasecmp(priv_type, "DELETE") == 0)
|
||||
return ACL_DELETE;
|
||||
if (strcasecmp(priv_type, "DELETE WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_DELETE);
|
||||
|
||||
if (strcasecmp(priv_type, "RULE") == 0)
|
||||
return ACL_RULE;
|
||||
if (strcasecmp(priv_type, "RULE WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_RULE);
|
||||
|
||||
if (strcasecmp(priv_type, "REFERENCES") == 0)
|
||||
return ACL_REFERENCES;
|
||||
if (strcasecmp(priv_type, "REFERENCES WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_REFERENCES);
|
||||
|
||||
if (strcasecmp(priv_type, "TRIGGER") == 0)
|
||||
return ACL_TRIGGER;
|
||||
if (strcasecmp(priv_type, "TRIGGER WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_TRIGGER);
|
||||
|
||||
elog(ERROR, "has_table_privilege: invalid privilege type %s",
|
||||
priv_type);
|
||||
@ -1057,12 +1142,18 @@ convert_database_priv_string(text *priv_type_text)
|
||||
*/
|
||||
if (strcasecmp(priv_type, "CREATE") == 0)
|
||||
return ACL_CREATE;
|
||||
if (strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_CREATE);
|
||||
|
||||
if (strcasecmp(priv_type, "TEMPORARY") == 0)
|
||||
return ACL_CREATE_TEMP;
|
||||
if (strcasecmp(priv_type, "TEMPORARY WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
|
||||
|
||||
if (strcasecmp(priv_type, "TEMP") == 0)
|
||||
return ACL_CREATE_TEMP;
|
||||
if (strcasecmp(priv_type, "TEMP WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP);
|
||||
|
||||
elog(ERROR, "has_database_privilege: invalid privilege type %s",
|
||||
priv_type);
|
||||
@ -1262,6 +1353,8 @@ convert_function_priv_string(text *priv_type_text)
|
||||
*/
|
||||
if (strcasecmp(priv_type, "EXECUTE") == 0)
|
||||
return ACL_EXECUTE;
|
||||
if (strcasecmp(priv_type, "EXECUTE WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_EXECUTE);
|
||||
|
||||
elog(ERROR, "has_function_privilege: invalid privilege type %s",
|
||||
priv_type);
|
||||
@ -1461,6 +1554,8 @@ convert_language_priv_string(text *priv_type_text)
|
||||
*/
|
||||
if (strcasecmp(priv_type, "USAGE") == 0)
|
||||
return ACL_USAGE;
|
||||
if (strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_USAGE);
|
||||
|
||||
elog(ERROR, "has_language_privilege: invalid privilege type %s",
|
||||
priv_type);
|
||||
@ -1660,9 +1755,13 @@ convert_schema_priv_string(text *priv_type_text)
|
||||
*/
|
||||
if (strcasecmp(priv_type, "CREATE") == 0)
|
||||
return ACL_CREATE;
|
||||
if (strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_CREATE);
|
||||
|
||||
if (strcasecmp(priv_type, "USAGE") == 0)
|
||||
return ACL_USAGE;
|
||||
if (strcasecmp(priv_type, "USAGE WITH GRANT OPTION") == 0)
|
||||
return ACL_GRANT_OPTION_FOR(ACL_USAGE);
|
||||
|
||||
elog(ERROR, "has_schema_privilege: invalid privilege type %s",
|
||||
priv_type);
|
||||
|
@ -27,7 +27,7 @@
|
||||
# Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
# Portions Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.181 2003/01/21 10:11:52 petere Exp $
|
||||
# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.182 2003/01/23 23:39:01 petere Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -1029,14 +1029,12 @@ echo "ok"
|
||||
$ECHO_N "setting privileges on built-in objects... "$ECHO_C
|
||||
(
|
||||
cat <<EOF
|
||||
UPDATE pg_class SET relacl = '{"=r"}' \
|
||||
UPDATE pg_class SET relacl = '{"=r/$POSTGRES_SUPERUSERNAME"}' \
|
||||
WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;
|
||||
UPDATE pg_proc SET proacl = '{"=X"}' \
|
||||
UPDATE pg_proc SET proacl = '{"=X/$POSTGRES_SUPERUSERNAME"}' \
|
||||
WHERE proacl IS NULL;
|
||||
UPDATE pg_language SET lanacl = '{"=U"}' \
|
||||
UPDATE pg_language SET lanacl = '{"=U/$POSTGRES_SUPERUSERNAME"}' \
|
||||
WHERE lanpltrusted;
|
||||
UPDATE pg_language SET lanacl = '{"="}' \
|
||||
WHERE NOT lanpltrusted;
|
||||
EOF
|
||||
) \
|
||||
| "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely
|
||||
|
@ -12,7 +12,7 @@
|
||||
* by PostgreSQL
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.314 2003/01/06 18:53:24 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.315 2003/01/23 23:39:01 petere Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -92,7 +92,7 @@ static void dumpFuncACL(Archive *fout, FuncInfo *finfo);
|
||||
static void dumpAggACL(Archive *fout, AggInfo *finfo);
|
||||
static void dumpACL(Archive *fout, const char *type, const char *name,
|
||||
const char *tag, const char *nspname,
|
||||
const char *usename, const char *acl, const char *objoid);
|
||||
const char *owner, const char *acl, const char *objoid);
|
||||
|
||||
static void dumpConstraints(Archive *fout, TableInfo *tblinfo, int numTables);
|
||||
static void dumpTriggers(Archive *fout, TableInfo *tblinfo, int numTables);
|
||||
@ -114,8 +114,10 @@ static char *getFormattedTypeName(const char *oid, OidOptions opts);
|
||||
static char *myFormatType(const char *typname, int32 typmod);
|
||||
static const char *fmtQualifiedId(const char *schema, const char *id);
|
||||
|
||||
static void AddAcl(char *aclbuf, const char *keyword);
|
||||
static char *GetPrivileges(Archive *AH, const char *s, const char *type);
|
||||
static void AddAcl(PQExpBuffer aclbuf, const char *keyword);
|
||||
static void
|
||||
parseAclItem(const char *item, const char *type, const char *name, int remoteVersion,
|
||||
PQExpBuffer grantee, PQExpBuffer grantor, PQExpBuffer privs, PQExpBuffer privswgo);
|
||||
|
||||
static int dumpBlobs(Archive *AH, char *, void *);
|
||||
static int dumpDatabase(Archive *AH);
|
||||
@ -4777,42 +4779,80 @@ dumpOneAgg(Archive *fout, AggInfo *agginfo)
|
||||
|
||||
|
||||
/*
|
||||
* These are some support functions to fix the acl problem of pg_dump
|
||||
*
|
||||
* Matthew C. Aycock 12/02/97
|
||||
*/
|
||||
|
||||
/* Append a keyword to a keyword list, inserting comma if needed.
|
||||
* Caller must make aclbuf big enough for all possible keywords.
|
||||
* Append a privilege keyword to a keyword list, inserting comma if needed.
|
||||
*/
|
||||
static void
|
||||
AddAcl(char *aclbuf, const char *keyword)
|
||||
AddAcl(PQExpBuffer aclbuf, const char *keyword)
|
||||
{
|
||||
if (*aclbuf)
|
||||
strcat(aclbuf, ",");
|
||||
strcat(aclbuf, keyword);
|
||||
if (aclbuf->len > 0)
|
||||
appendPQExpBufferChar(aclbuf, ',');
|
||||
appendPQExpBuffer(aclbuf, "%s", keyword);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This will take a string of privilege code letters and return a malloced,
|
||||
* comma delimited string of keywords for GRANT.
|
||||
* This will take an aclitem string of privilege code letters and
|
||||
* parse it into grantee, grantor, and privilege information. The
|
||||
* privilege information is split between privileges with grant option
|
||||
* (privswgo) and without (privs).
|
||||
*
|
||||
* Note: for cross-version compatibility, it's important to use ALL when
|
||||
* appropriate.
|
||||
*/
|
||||
static char *
|
||||
GetPrivileges(Archive *AH, const char *s, const char *type)
|
||||
static void
|
||||
parseAclItem(const char *item, const char *type, const char *name, int remoteVersion,
|
||||
PQExpBuffer grantee, PQExpBuffer grantor, PQExpBuffer privs, PQExpBuffer privswgo)
|
||||
{
|
||||
char aclbuf[100];
|
||||
bool all = true;
|
||||
char *buf;
|
||||
bool all_with_go = true;
|
||||
bool all_without_go = true;
|
||||
char *eqpos;
|
||||
char *slpos;
|
||||
char *pos;
|
||||
|
||||
aclbuf[0] = '\0';
|
||||
buf = strdup(item);
|
||||
|
||||
#define CONVERT_PRIV(code,keywd) \
|
||||
if (strchr(s, code)) \
|
||||
AddAcl(aclbuf, keywd); \
|
||||
/* user name is string up to = */
|
||||
eqpos = strchr(buf, '=');
|
||||
if (!eqpos)
|
||||
{
|
||||
write_msg(NULL, "could not parse ACL list (%s) for object %s (%s)\n",
|
||||
item, name, type);
|
||||
exit_nicely();
|
||||
}
|
||||
*eqpos = '\0';
|
||||
printfPQExpBuffer(grantee, "%s", buf);
|
||||
|
||||
/* grantor may be listed after / */
|
||||
slpos = strchr(eqpos + 1, '/');
|
||||
if (slpos)
|
||||
{
|
||||
*slpos = '\0';
|
||||
printfPQExpBuffer(grantor, "%s", slpos + 1);
|
||||
}
|
||||
else
|
||||
resetPQExpBuffer(grantor);
|
||||
|
||||
/* privilege codes */
|
||||
#define CONVERT_PRIV(code, keywd) \
|
||||
if ((pos = strchr(eqpos + 1, code))) \
|
||||
{ \
|
||||
if (*(pos + 1) == '*') \
|
||||
{ \
|
||||
AddAcl(privswgo, keywd); \
|
||||
all_without_go = false; \
|
||||
} \
|
||||
else \
|
||||
all = false
|
||||
{ \
|
||||
AddAcl(privs, keywd); \
|
||||
all_with_go = false; \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
all_with_go = all_without_go = false
|
||||
|
||||
resetPQExpBuffer(privs);
|
||||
resetPQExpBuffer(privswgo);
|
||||
|
||||
if (strcmp(type, "TABLE") == 0)
|
||||
{
|
||||
@ -4820,7 +4860,7 @@ GetPrivileges(Archive *AH, const char *s, const char *type)
|
||||
CONVERT_PRIV('r', "SELECT");
|
||||
CONVERT_PRIV('R', "RULE");
|
||||
|
||||
if (AH->remoteVersion >= 70200)
|
||||
if (remoteVersion >= 70200)
|
||||
{
|
||||
CONVERT_PRIV('w', "UPDATE");
|
||||
CONVERT_PRIV('d', "DELETE");
|
||||
@ -4847,10 +4887,16 @@ GetPrivileges(Archive *AH, const char *s, const char *type)
|
||||
|
||||
#undef CONVERT_PRIV
|
||||
|
||||
if (all)
|
||||
return strdup("ALL");
|
||||
else
|
||||
return strdup(aclbuf);
|
||||
if (all_with_go)
|
||||
{
|
||||
resetPQExpBuffer(privs);
|
||||
printfPQExpBuffer(privswgo, "ALL");
|
||||
}
|
||||
else if (all_without_go)
|
||||
{
|
||||
resetPQExpBuffer(privswgo);
|
||||
printfPQExpBuffer(privs, "ALL");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4861,7 +4907,7 @@ GetPrivileges(Archive *AH, const char *s, const char *type)
|
||||
* 'name' is the formatted name of the object. Must be quoted etc. already.
|
||||
* 'tag' is the tag for the archive entry (typ. unquoted name of object).
|
||||
* 'nspname' is the namespace the object is in (NULL if none).
|
||||
* 'usename' is the owner, NULL if there is no owner (for languages).
|
||||
* 'owner' is the owner, NULL if there is no owner (for languages).
|
||||
* 'acls' is the string read out of the fooacl system catalog field;
|
||||
* it will be parsed here.
|
||||
* 'objoid' is the OID of the object for purposes of ordering.
|
||||
@ -4869,28 +4915,34 @@ GetPrivileges(Archive *AH, const char *s, const char *type)
|
||||
*/
|
||||
static void
|
||||
dumpACL(Archive *fout, const char *type, const char *name,
|
||||
const char *tag, const char *nspname, const char *usename,
|
||||
const char *tag, const char *nspname, const char *owner,
|
||||
const char *acls, const char *objoid)
|
||||
{
|
||||
char *aclbuf,
|
||||
*tok,
|
||||
*eqpos,
|
||||
*priv;
|
||||
PQExpBuffer sql;
|
||||
*tok;
|
||||
PQExpBuffer sql, grantee, grantor, privs, privswgo;
|
||||
bool found_owner_privs = false;
|
||||
|
||||
if (strlen(acls) == 0)
|
||||
return; /* object has default permissions */
|
||||
|
||||
#define MKENTRY(grantor, command) \
|
||||
ArchiveEntry(fout, objoid, tag, nspname, grantor ? grantor : "", "ACL", NULL, command, "", NULL, NULL, NULL)
|
||||
|
||||
sql = createPQExpBuffer();
|
||||
grantee = createPQExpBuffer();
|
||||
grantor = createPQExpBuffer();
|
||||
privs = createPQExpBuffer();
|
||||
privswgo = createPQExpBuffer();
|
||||
|
||||
/*
|
||||
* Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
|
||||
* wire-in knowledge about the default public privileges for different
|
||||
* kinds of objects.
|
||||
*/
|
||||
appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM PUBLIC;\n",
|
||||
printfPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM PUBLIC;\n",
|
||||
type, name);
|
||||
MKENTRY(owner, sql->data);
|
||||
|
||||
/* Make a working copy of acls so we can use strtok */
|
||||
aclbuf = strdup(acls);
|
||||
@ -4898,6 +4950,10 @@ dumpACL(Archive *fout, const char *type, const char *name,
|
||||
/* Scan comma-separated ACL items */
|
||||
for (tok = strtok(aclbuf, ","); tok != NULL; tok = strtok(NULL, ","))
|
||||
{
|
||||
size_t toklen;
|
||||
|
||||
resetPQExpBuffer(sql);
|
||||
|
||||
/*
|
||||
* Token may start with '{' and/or '"'. Actually only the start
|
||||
* of the string should have '{', but we don't verify that.
|
||||
@ -4906,39 +4962,33 @@ dumpACL(Archive *fout, const char *type, const char *name,
|
||||
tok++;
|
||||
if (*tok == '"')
|
||||
tok++;
|
||||
toklen = strlen(tok);
|
||||
while (toklen >=0 && (tok[toklen-1] == '"' || tok[toklen-1] == '}'))
|
||||
tok[toklen-- - 1] = '\0';
|
||||
|
||||
/* User name is string up to = in tok */
|
||||
eqpos = strchr(tok, '=');
|
||||
if (!eqpos)
|
||||
parseAclItem(tok, type, name, fout->remoteVersion,
|
||||
grantee, grantor, privs, privswgo);
|
||||
if (grantor->len == 0 && owner)
|
||||
printfPQExpBuffer(grantor, "%s", owner);
|
||||
|
||||
if (privs->len > 0 || privswgo->len > 0)
|
||||
{
|
||||
write_msg(NULL, "could not parse ACL list (%s) for object %s (%s)\n",
|
||||
acls, name, type);
|
||||
exit_nicely();
|
||||
}
|
||||
*eqpos = '\0'; /* it's ok to clobber aclbuf */
|
||||
|
||||
/*
|
||||
* Parse the privileges (right-hand side).
|
||||
*/
|
||||
priv = GetPrivileges(fout, eqpos + 1, type);
|
||||
|
||||
if (*priv)
|
||||
{
|
||||
if (usename && strcmp(tok, usename) == 0)
|
||||
if (owner && strcmp(grantee->data, owner) == 0)
|
||||
{
|
||||
/*
|
||||
* For the owner, the default privilege level is ALL.
|
||||
* For the owner, the default privilege level is ALL WITH GRANT OPTION.
|
||||
*/
|
||||
found_owner_privs = true;
|
||||
if (strcmp(priv, "ALL") != 0)
|
||||
if (strcmp(privswgo->data, "ALL") != 0)
|
||||
{
|
||||
/* NB: only one fmtId per appendPQExpBuffer! */
|
||||
appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM ",
|
||||
type, name);
|
||||
appendPQExpBuffer(sql, "%s;\n", fmtId(tok));
|
||||
appendPQExpBuffer(sql, "GRANT %s ON %s %s TO ",
|
||||
priv, type, name);
|
||||
appendPQExpBuffer(sql, "%s;\n", fmtId(tok));
|
||||
appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM %s;\n",
|
||||
type, name, fmtId(grantee->data));
|
||||
if (privs->len > 0)
|
||||
appendPQExpBuffer(sql, "GRANT %s ON %s %s TO %s;\n",
|
||||
privs->data, type, name, fmtId(grantee->data));
|
||||
if (privswgo->len > 0)
|
||||
appendPQExpBuffer(sql, "GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
|
||||
privswgo->data, type, name, fmtId(grantee->data));
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -4946,57 +4996,69 @@ dumpACL(Archive *fout, const char *type, const char *name,
|
||||
/*
|
||||
* Otherwise can assume we are starting from no privs.
|
||||
*/
|
||||
appendPQExpBuffer(sql, "GRANT %s ON %s %s TO ",
|
||||
priv, type, name);
|
||||
if (eqpos == tok)
|
||||
if (privs->len > 0)
|
||||
{
|
||||
/* Empty left-hand side means "PUBLIC" */
|
||||
appendPQExpBuffer(sql, "GRANT %s ON %s %s TO ",
|
||||
privs->data, type, name);
|
||||
if (grantee->len == 0)
|
||||
appendPQExpBuffer(sql, "PUBLIC;\n");
|
||||
}
|
||||
else if (strncmp(tok, "group ", strlen("group ")) == 0)
|
||||
else if (strncmp(grantee->data, "group ", strlen("group ")) == 0)
|
||||
appendPQExpBuffer(sql, "GROUP %s;\n",
|
||||
fmtId(tok + strlen("group ")));
|
||||
fmtId(grantee->data + strlen("group ")));
|
||||
else
|
||||
appendPQExpBuffer(sql, "%s;\n", fmtId(tok));
|
||||
appendPQExpBuffer(sql, "%s;\n", fmtId(grantee->data));
|
||||
}
|
||||
if (privswgo->len > 0)
|
||||
{
|
||||
appendPQExpBuffer(sql, "GRANT %s ON %s %s TO ",
|
||||
privswgo->data, type, name);
|
||||
if (grantee->len == 0)
|
||||
appendPQExpBuffer(sql, "PUBLIC");
|
||||
else if (strncmp(grantee->data, "group ", strlen("group ")) == 0)
|
||||
appendPQExpBuffer(sql, "GROUP %s",
|
||||
fmtId(grantee->data + strlen("group ")));
|
||||
else
|
||||
appendPQExpBuffer(sql, "%s", fmtId(grantee->data));
|
||||
appendPQExpBuffer(sql, " WITH GRANT OPTION;\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No privileges. Issue explicit REVOKE for safety. */
|
||||
if (eqpos == tok)
|
||||
{
|
||||
/* Empty left-hand side means "PUBLIC"; already did it */
|
||||
}
|
||||
else if (strncmp(tok, "group ", strlen("group ")) == 0)
|
||||
{
|
||||
if (grantee->len == 0)
|
||||
; /* Empty left-hand side means "PUBLIC"; already did it */
|
||||
else if (strncmp(grantee->data, "group ", strlen("group ")) == 0)
|
||||
appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM GROUP %s;\n",
|
||||
type, name,
|
||||
fmtId(tok + strlen("group ")));
|
||||
}
|
||||
fmtId(grantee->data + strlen("group ")));
|
||||
else
|
||||
{
|
||||
appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM %s;\n",
|
||||
type, name, fmtId(tok));
|
||||
type, name, fmtId(grantee->data));
|
||||
}
|
||||
}
|
||||
free(priv);
|
||||
|
||||
if (sql->len > 0)
|
||||
MKENTRY(grantor->data, sql->data);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we didn't find any owner privs, the owner must have revoked 'em
|
||||
* all
|
||||
*/
|
||||
if (!found_owner_privs && usename)
|
||||
if (!found_owner_privs && owner)
|
||||
{
|
||||
appendPQExpBuffer(sql, "REVOKE ALL ON %s %s FROM %s;\n",
|
||||
type, name, fmtId(usename));
|
||||
type, name, fmtId(owner));
|
||||
MKENTRY(owner, sql->data);
|
||||
}
|
||||
|
||||
ArchiveEntry(fout, objoid, tag, nspname, usename ? usename : "",
|
||||
"ACL", NULL, sql->data, "", NULL, NULL, NULL);
|
||||
|
||||
free(aclbuf);
|
||||
destroyPQExpBuffer(sql);
|
||||
destroyPQExpBuffer(grantee);
|
||||
destroyPQExpBuffer(grantor);
|
||||
destroyPQExpBuffer(privs);
|
||||
destroyPQExpBuffer(privswgo);
|
||||
#undef MKENTRY
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* catversion.h
|
||||
* "Catalog version number" for Postgres.
|
||||
* "Catalog version number" for PostgreSQL.
|
||||
*
|
||||
* The catalog version number is used to flag incompatible changes in
|
||||
* the Postgres system catalogs. Whenever anyone changes the format of
|
||||
* the PostgreSQL system catalogs. Whenever anyone changes the format of
|
||||
* a system catalog relation, or adds, deletes, or modifies standard
|
||||
* catalog entries in such a way that an updated backend wouldn't work
|
||||
* with an old database (or vice versa), the catalog version number
|
||||
@ -37,7 +37,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: catversion.h,v 1.172 2003/01/10 21:08:15 tgl Exp $
|
||||
* $Id: catversion.h,v 1.173 2003/01/23 23:39:04 petere Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 200301101
|
||||
#define CATALOG_VERSION_NO 200301241
|
||||
|
||||
#endif
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pg_type.h,v 1.138 2003/01/08 21:40:39 tgl Exp $
|
||||
* $Id: pg_type.h,v 1.139 2003/01/23 23:39:06 petere Exp $
|
||||
*
|
||||
* NOTES
|
||||
* the genbki.sh script reads this file and generates .bki
|
||||
@ -412,7 +412,7 @@ DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b t \054 0 702 array_in arr
|
||||
DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b t \054 0 703 array_in array_out i x f 0 -1 0 _null_ _null_ ));
|
||||
DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b t \054 0 704 array_in array_out i x f 0 -1 0 _null_ _null_ ));
|
||||
DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b t \054 0 604 array_in array_out d x f 0 -1 0 _null_ _null_ ));
|
||||
DATA(insert OID = 1033 ( aclitem PGNSP PGUID 8 f b t \054 0 0 aclitemin aclitemout i p f 0 -1 0 _null_ _null_ ));
|
||||
DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b t \054 0 0 aclitemin aclitemout i p f 0 -1 0 _null_ _null_ ));
|
||||
DESCR("access control list");
|
||||
#define ACLITEMOID 1033
|
||||
DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b t \054 0 1033 array_in array_out i x f 0 -1 0 _null_ _null_ ));
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parsenodes.h,v 1.226 2003/01/20 18:55:00 tgl Exp $
|
||||
* $Id: parsenodes.h,v 1.227 2003/01/23 23:39:07 petere Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -750,7 +750,7 @@ typedef enum GrantObjectType
|
||||
|
||||
/*
|
||||
* Grantable rights are encoded so that we can OR them together in a bitmask.
|
||||
* The present representation of AclItem limits us to 30 distinct rights.
|
||||
* The present representation of AclItem limits us to 15 distinct rights.
|
||||
* Caution: changing these codes breaks stored ACLs, hence forces initdb.
|
||||
*/
|
||||
#define ACL_INSERT (1<<0) /* for relations */
|
||||
@ -778,6 +778,8 @@ typedef struct GrantStmt
|
||||
* strings) */
|
||||
List *privileges; /* integer list of privilege codes */
|
||||
List *grantees; /* list of PrivGrantee nodes */
|
||||
bool grant_option; /* grant or revoke grant option */
|
||||
DropBehavior behavior; /* drop behavior (for REVOKE) */
|
||||
} GrantStmt;
|
||||
|
||||
typedef struct PrivGrantee
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: acl.h,v 1.50 2003/01/09 18:00:24 tgl Exp $
|
||||
* $Id: acl.h,v 1.51 2003/01/23 23:39:07 petere Exp $
|
||||
*
|
||||
* NOTES
|
||||
* For backward-compatibility purposes we have to allow there
|
||||
@ -50,27 +50,37 @@ typedef uint32 AclMode;
|
||||
*/
|
||||
typedef struct AclItem
|
||||
{
|
||||
AclId ai_id; /* ID that this item applies to */
|
||||
AclId ai_grantee; /* ID that this item applies to */
|
||||
AclId ai_grantor;
|
||||
AclMode ai_privs; /* AclIdType plus privilege bits */
|
||||
} AclItem;
|
||||
|
||||
/*
|
||||
* The AclIdType is stored in the top two bits of the ai_privs field of an
|
||||
* AclItem, leaving us with thirty usable privilege bits.
|
||||
* The AclIdType is stored in the top two bits of the ai_privs field
|
||||
* of an AclItem. The middle 15 bits are the grant option markers,
|
||||
* and the lower 15 bits are the actual privileges.
|
||||
*/
|
||||
#define ACLITEM_GET_PRIVS(item) ((item).ai_privs & 0x3FFFFFFF)
|
||||
#define ACLITEM_GET_PRIVS(item) ((item).ai_privs & 0x7FFF)
|
||||
#define ACLITEM_GET_GOPTIONS(item) (((item).ai_privs >> 15) & 0x7FFF)
|
||||
#define ACLITEM_GET_IDTYPE(item) ((item).ai_privs >> 30)
|
||||
#define ACLITEM_SET_PRIVS_IDTYPE(item,privs,idtype) \
|
||||
((item).ai_privs = ((privs) & 0x3FFFFFFF) | ((idtype) << 30))
|
||||
|
||||
#define ACL_GRANT_OPTION_FOR(privs) (((privs) & 0x7FFF) << 15)
|
||||
|
||||
#define ACLITEM_SET_PRIVS(item,privs) \
|
||||
((item).ai_privs = (ACLITEM_GET_IDTYPE(item)<<30) | (ACLITEM_GET_GOPTIONS(item)<<15) | ((privs) & 0x7FFF))
|
||||
#define ACLITEM_SET_GOPTIONS(item,goptions) \
|
||||
((item).ai_privs = (ACLITEM_GET_IDTYPE(item)<<30) | (((goptions) & 0x7FFF) << 15) | ACLITEM_GET_PRIVS(item))
|
||||
#define ACLITEM_SET_PRIVS_IDTYPE(item,privs,goption,idtype) \
|
||||
((item).ai_privs = ((privs) & 0x7FFF) |(((goption) & 0x7FFF) << 15) | ((idtype) << 30))
|
||||
|
||||
|
||||
/*
|
||||
* Definitions for convenient access to Acl (array of AclItem) and IdList
|
||||
* (array of AclId). These are standard Postgres arrays, but are restricted
|
||||
* (array of AclId). These are standard PostgreSQL arrays, but are restricted
|
||||
* to have one dimension. We also ignore the lower bound when reading,
|
||||
* and set it to zero when writing.
|
||||
*
|
||||
* CAUTION: as of Postgres 7.1, these arrays are toastable (just like all
|
||||
* CAUTION: as of PostgreSQL 7.1, these arrays are toastable (just like all
|
||||
* other array types). Therefore, be careful to detoast them with the
|
||||
* macros provided, unless you know for certain that a particular array
|
||||
* can't have been toasted. Presently, we do not provide toast tables for
|
||||
@ -80,7 +90,7 @@ typedef struct AclItem
|
||||
|
||||
|
||||
/*
|
||||
* Acl a one-dimensional POSTGRES array of AclItem
|
||||
* Acl a one-dimensional array of AclItem
|
||||
*/
|
||||
typedef ArrayType Acl;
|
||||
|
||||
@ -90,7 +100,7 @@ typedef ArrayType Acl;
|
||||
#define ACL_SIZE(ACL) ARR_SIZE(ACL)
|
||||
|
||||
/*
|
||||
* IdList a one-dimensional POSTGRES array of AclId
|
||||
* IdList a one-dimensional array of AclId
|
||||
*/
|
||||
typedef ArrayType IdList;
|
||||
|
||||
@ -126,11 +136,6 @@ typedef ArrayType IdList;
|
||||
#define ACL_MODECHG_DEL 2
|
||||
#define ACL_MODECHG_EQL 3
|
||||
|
||||
/* external representation of mode indicators for I/O */
|
||||
#define ACL_MODECHG_ADD_CHR '+'
|
||||
#define ACL_MODECHG_DEL_CHR '-'
|
||||
#define ACL_MODECHG_EQL_CHR '='
|
||||
|
||||
/*
|
||||
* External representations of the privilege bits --- aclitemin/aclitemout
|
||||
* represent each possible privilege bit with a distinct 1-character code
|
||||
@ -173,7 +178,7 @@ typedef enum
|
||||
*/
|
||||
extern Acl *acldefault(GrantObjectType objtype, AclId ownerid);
|
||||
extern Acl *aclinsert3(const Acl *old_acl, const AclItem *mod_aip,
|
||||
unsigned modechg);
|
||||
unsigned modechg, DropBehavior behavior);
|
||||
|
||||
/*
|
||||
* SQL functions (from acl.c)
|
||||
|
@ -90,7 +90,7 @@ ERROR: atest2: permission denied
|
||||
COPY atest2 FROM stdin; -- fail
|
||||
ERROR: atest2: permission denied
|
||||
GRANT ALL ON atest1 TO PUBLIC; -- fail
|
||||
ERROR: atest1: must be owner
|
||||
ERROR: atest1: permission denied
|
||||
-- checks in subquery, both ok
|
||||
SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) );
|
||||
a | b
|
||||
@ -227,7 +227,7 @@ GRANT USAGE ON LANGUAGE c TO PUBLIC; -- fail
|
||||
ERROR: language "c" is not trusted
|
||||
SET SESSION AUTHORIZATION regressuser1;
|
||||
GRANT USAGE ON LANGUAGE sql TO regressuser2; -- fail
|
||||
ERROR: permission denied
|
||||
ERROR: sql: permission denied
|
||||
CREATE FUNCTION testfunc1(int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql;
|
||||
CREATE FUNCTION testfunc2(int) RETURNS int AS 'select 3 * $1;' LANGUAGE sql;
|
||||
REVOKE ALL ON FUNCTION testfunc1(int), testfunc2(int) FROM PUBLIC;
|
||||
@ -544,6 +544,46 @@ from (select oid from pg_class where relname = 'atest1') as t1;
|
||||
f
|
||||
(1 row)
|
||||
|
||||
-- Grant options
|
||||
SET SESSION AUTHORIZATION regressuser1;
|
||||
CREATE TABLE atest4 (a int);
|
||||
GRANT SELECT ON atest4 TO regressuser2 WITH GRANT OPTION;
|
||||
GRANT UPDATE ON atest4 TO regressuser2;
|
||||
GRANT SELECT ON atest4 TO GROUP regressgroup1 WITH GRANT OPTION; -- fail
|
||||
ERROR: grant options can only be granted to individual users
|
||||
SET SESSION AUTHORIZATION regressuser2;
|
||||
GRANT SELECT ON atest4 TO regressuser3;
|
||||
GRANT UPDATE ON atest4 TO regressuser3; -- fail
|
||||
ERROR: atest4: permission denied
|
||||
SET SESSION AUTHORIZATION regressuser1;
|
||||
REVOKE SELECT ON atest4 FROM regressuser3; -- does nothing
|
||||
SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- true
|
||||
has_table_privilege
|
||||
---------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
REVOKE SELECT ON atest4 FROM regressuser2; -- fail
|
||||
ERROR: dependent privileges exist (use CASCADE to revoke them too)
|
||||
REVOKE GRANT OPTION FOR SELECT ON atest4 FROM regressuser2 CASCADE; -- ok
|
||||
SELECT has_table_privilege('regressuser2', 'atest4', 'SELECT'); -- true
|
||||
has_table_privilege
|
||||
---------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- false
|
||||
has_table_privilege
|
||||
---------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true
|
||||
has_table_privilege
|
||||
---------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-- clean up
|
||||
\c regression
|
||||
SET autocommit TO 'on';
|
||||
@ -561,6 +601,7 @@ ERROR: view "atestv4" does not exist
|
||||
DROP TABLE atest1;
|
||||
DROP TABLE atest2;
|
||||
DROP TABLE atest3;
|
||||
DROP TABLE atest4;
|
||||
DROP GROUP regressgroup1;
|
||||
DROP GROUP regressgroup2;
|
||||
DROP USER regressuser1;
|
||||
|
@ -293,6 +293,33 @@ select has_table_privilege(t1.oid,'trigger')
|
||||
from (select oid from pg_class where relname = 'atest1') as t1;
|
||||
|
||||
|
||||
-- Grant options
|
||||
|
||||
SET SESSION AUTHORIZATION regressuser1;
|
||||
|
||||
CREATE TABLE atest4 (a int);
|
||||
|
||||
GRANT SELECT ON atest4 TO regressuser2 WITH GRANT OPTION;
|
||||
GRANT UPDATE ON atest4 TO regressuser2;
|
||||
GRANT SELECT ON atest4 TO GROUP regressgroup1 WITH GRANT OPTION; -- fail
|
||||
|
||||
SET SESSION AUTHORIZATION regressuser2;
|
||||
|
||||
GRANT SELECT ON atest4 TO regressuser3;
|
||||
GRANT UPDATE ON atest4 TO regressuser3; -- fail
|
||||
|
||||
SET SESSION AUTHORIZATION regressuser1;
|
||||
|
||||
REVOKE SELECT ON atest4 FROM regressuser3; -- does nothing
|
||||
SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- true
|
||||
REVOKE SELECT ON atest4 FROM regressuser2; -- fail
|
||||
REVOKE GRANT OPTION FOR SELECT ON atest4 FROM regressuser2 CASCADE; -- ok
|
||||
SELECT has_table_privilege('regressuser2', 'atest4', 'SELECT'); -- true
|
||||
SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- false
|
||||
|
||||
SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true
|
||||
|
||||
|
||||
-- clean up
|
||||
|
||||
\c regression
|
||||
@ -311,6 +338,7 @@ DROP VIEW atestv4;
|
||||
DROP TABLE atest1;
|
||||
DROP TABLE atest2;
|
||||
DROP TABLE atest3;
|
||||
DROP TABLE atest4;
|
||||
|
||||
DROP GROUP regressgroup1;
|
||||
DROP GROUP regressgroup2;
|
||||
|
Loading…
Reference in New Issue
Block a user