the privileges that will be applied to subsequently-created objects.
Such adjustments are always per owning role, and can be restricted to objects
created in particular schemas too. A notable benefit is that users can
override the traditional default privilege settings, eg, the PUBLIC EXECUTE
privilege traditionally granted by default for functions.
Petr Jelinek
<entry>databases within this database cluster</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-default-acl"><structname>pg_default_acl</structname></link></entry>
+ <entry>default privileges for object types</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-depend"><structname>pg_depend</structname></link></entry>
<entry>dependencies between database objects</entry>
</sect1>
+ <sect1 id="catalog-pg-default-acl">
+ <title><structname>pg_default_acl</structname></title>
+
+ <indexterm zone="catalog-pg-default-acl">
+ <primary>pg_default_acl</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_default_acl</> stores initial
+ privileges to be assigned to newly created objects.
+ </para>
+
+ <table>
+ <title><structname>pg_default_acl</> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><structfield>defaclrole</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+ <entry>The OID of the role associated with this entry</entry>
+ </row>
+
+ <row>
+ <entry><structfield>defaclnamespace</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+ <entry>The OID of the namespace associated with this entry,
+ or 0 if none</entry>
+ </row>
+
+ <row>
+ <entry><structfield>defaclobjtype</structfield></entry>
+ <entry><type>char</type></entry>
+ <entry></entry>
+ <entry>
+ Type of object this entry is for:
+ <literal>r</> = relation (table, view),
+ <literal>S</> = sequence,
+ <literal>f</> = function
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>defaclacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry></entry>
+ <entry>
+ Access privileges that this type of object should have on creation
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ A <structname>pg_default_acl</> entry shows the initial privileges to
+ be assigned to an object belonging to the indicated user. There are
+ currently two types of entry: <quote>global</> entries with
+ <structfield>defaclnamespace</> = 0, and <quote>per-schema</> entries
+ that reference a particular schema. If a global entry is present then
+ it <emphasis>overrides</> the normal hard-wired default privileges
+ for the object type. A per-schema entry, if present, represents privileges
+ to be <emphasis>added to</> the global or hard-wired default privileges.
+ </para>
+
+ <para>
+ Note that when an ACL entry in another catalog is NULL, it is taken
+ to represent the hard-wired default privileges for its object,
+ <emphasis>not</> whatever might be in <structname>pg_default_acl</>
+ at the moment. <structname>pg_default_acl</> is only consulted during
+ object creation.
+ </para>
+
+ </sect1>
+
+
<sect1 id="catalog-pg-depend">
<title><structname>pg_depend</structname></title>
<!entity alterAggregate system "alter_aggregate.sgml">
<!entity alterConversion system "alter_conversion.sgml">
<!entity alterDatabase system "alter_database.sgml">
+<!entity alterDefaultPrivileges system "alter_default_privileges.sgml">
<!entity alterDomain system "alter_domain.sgml">
<!entity alterForeignDataWrapper system "alter_foreign_data_wrapper.sgml">
<!entity alterFunction system "alter_function.sgml">
--- /dev/null
+<!--
+$PostgreSQL$
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-ALTERDEFAULTPRIVILEGES">
+ <refmeta>
+ <refentrytitle id="SQL-ALTERDEFAULTPRIVILEGES-TITLE">ALTER DEFAULT PRIVILEGES</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ALTER DEFAULT PRIVILEGES</refname>
+ <refpurpose>define default access privileges</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-alterdefaultprivileges">
+ <primary>ALTER DEFAULT PRIVILEGES</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER DEFAULT PRIVILEGES
+ [ FOR { ROLE | USER } <replaceable>target_role</replaceable> [, ...] ]
+ [ IN SCHEMA <replaceable>schema_name</replaceable> [, ...] ]
+ <replaceable class="parameter">abbreviated_grant_or_revoke</replaceable>
+
+<phrase>where <replaceable class="parameter">abbreviated_grant_or_revoke</replaceable> is one of:</phrase>
+
+GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON TABLE
+ TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
+GRANT { { USAGE | SELECT | UPDATE }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON SEQUENCE
+ TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
+GRANT { EXECUTE | ALL [ PRIVILEGES ] }
+ ON FUNCTION
+ TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON TABLE
+ FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { { USAGE | SELECT | UPDATE }
+ [,...] | ALL [ PRIVILEGES ] }
+ ON SEQUENCE
+ FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+ { EXECUTE | ALL [ PRIVILEGES ] }
+ ON FUNCTION
+ FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="sql-alterdefaultprivileges-description">
+ <title>Description</title>
+
+ <para>
+ <command>ALTER DEFAULT PRIVILEGES</> allows you to set the privileges
+ that will be applied to objects created in the future. (It does not
+ affect privileges assigned to already-existing objects.) Currently,
+ only the privileges for tables (including views), sequences, and
+ functions can be altered.
+ </para>
+
+ <para>
+ You can change default privileges only for objects that will be created by
+ yourself or by roles that you are a member of. The privileges can be set
+ globally (i.e., for all objects created in the current database),
+ or just for objects created in specified schemas. Default privileges
+ that are specified per-schema are added to whatever the global default
+ privileges are for the particular object type.
+ </para>
+
+ <para>
+ As explained under <xref linkend="sql-grant" endterm="sql-grant-title">,
+ the default privileges for any object type normally grant all grantable
+ permissions to the object owner, and may grant some privileges to
+ <literal>PUBLIC</> as well. However, this behavior can be changed by
+ altering the global default privileges with
+ <command>ALTER DEFAULT PRIVILEGES</>.
+ </para>
+
+ <refsect2>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><replaceable>target_role</replaceable></term>
+ <listitem>
+ <para>
+ The name of an existing role of which the current role is a member.
+ If <literal>FOR ROLE</> is omitted, the current role is assumed.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable>schema_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of an existing schema. Each <replaceable>target_role</>
+ must have <literal>CREATE</> privileges for each specified schema.
+ If <literal>IN SCHEMA</> is omitted, the global default privileges
+ are altered.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable>role_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of an existing role to grant or revoke privileges for.
+ This parameter, and all the other parameters in
+ <replaceable class="parameter">abbreviated_grant_or_revoke</>,
+ act as described under
+ <xref linkend="sql-grant" endterm="sql-grant-title"> or
+ <xref linkend="sql-revoke" endterm="sql-revoke-title">,
+ except that one is setting permissions for a whole class of objects
+ rather than specific named objects.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
+ </refsect1>
+
+ <refsect1 id="sql-alterdefaultprivileges-notes">
+ <title>Notes</title>
+
+ <para>
+ Use <xref linkend="app-psql">'s <command>\ddp</command> command
+ to obtain information about existing assignments of default privileges.
+ The meaning of the privilege values is the same as explained for
+ <command>\dp</command> under
+ <xref linkend="sql-grant" endterm="sql-grant-title">.
+ </para>
+
+ <para>
+ If you wish to drop a role that has had its global default privileges
+ altered, it is necessary to use <command>DROP OWNED BY</> first,
+ to get rid of the default privileges entry for the role.
+ </para>
+ </refsect1>
+
+ <refsect1 id="sql-alterdefaultprivileges-examples">
+ <title>Examples</title>
+
+ <para>
+ Grant SELECT privilege to everyone for all tables (and views) you
+ subsequently create in schema <literal>myschema</literal>, and allow
+ role <literal>webuser</> to INSERT into them too:
+
+<programlisting>
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT SELECT ON TABLE TO PUBLIC;
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT INSERT ON TABLE TO webuser;
+</programlisting>
+ </para>
+
+ <para>
+ Undo the above, so that subsequently-created tables won't have any
+ more permissions than normal:
+
+<programlisting>
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE SELECT ON TABLE FROM PUBLIC;
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE INSERT ON TABLE FROM webuser;
+</programlisting>
+ </para>
+
+ <para>
+ Remove the public EXECUTE permission that is normally granted on functions,
+ for all functions subsequently created by role <literal>admin</>:
+
+<programlisting>
+ALTER DEFAULT PRIVILEGES FOR ROLE admin REVOKE EXECUTE ON FUNCTION FROM PUBLIC;
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ There is no <command>ALTER DEFAULT PRIVILEGES</command> statement in the SQL
+ standard.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-grant" endterm="sql-grant-title"></member>
+ <member><xref linkend="sql-revoke" endterm="sql-revoke-title"></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
they are different enough to be described separately.
</para>
- <para>
- As of <productname>PostgreSQL</productname> 8.1, the concepts of users and
- groups have been unified into a single kind of entity called a role.
- It is therefore no longer necessary to use the keyword <literal>GROUP</>
- to identify whether a grantee is a user or a group. <literal>GROUP</>
- is still allowed in the command, but it is a noise word.
- </para>
-
<refsect2 id="sql-grant-description-objects">
<title>GRANT on Database Objects</title>
security, issue the <command>REVOKE</> in the same transaction that
creates the object; then there is no window in which another user
can use the object.)
+ Also, these initial default privilege settings can be changed using the
+ <xref linkend="sql-alterdefaultprivileges" endterm="sql-alterdefaultprivileges-title">
+ command.
</para>
<para>
to revoke access privileges.
</para>
+ <para>
+ Since <productname>PostgreSQL</productname> 8.1, the concepts of users and
+ groups have been unified into a single kind of entity called a role.
+ It is therefore no longer necessary to use the keyword <literal>GROUP</>
+ to identify whether a grantee is a user or a group. <literal>GROUP</>
+ is still allowed in the command, but it is a noise word.
+ </para>
+
<para>
A user may perform <command>SELECT</>, <command>INSERT</>, etc. on a
column if he holds that privilege for either the specific column or
<command>REVOKE</> on an object
will instantiate the default privileges (producing, for example,
<literal>{miriam=arwdDxt/miriam}</>) and then modify them per the
- specified request. Entries are shown in <quote>Column access
+ specified request. Similarly, entries are shown in <quote>Column access
privileges</> only for columns with nondefault privileges.
+ (Note: for this purpose, <quote>default privileges</> always means the
+ built-in default privileges for the object's type. An object whose
+ privileges have been affected by an <command>ALTER DEFAULT PRIVILEGES</>
+ command will always be shown with an explicit privilege entry that
+ includes the effects of the <command>ALTER</>.)
</para>
<para>
<refsect1>
<title>See Also</title>
- <simpara>
- <xref linkend="sql-revoke" endterm="sql-revoke-title">
- </simpara>
+ <simplelist type="inline">
+ <member><xref linkend="sql-revoke" endterm="sql-revoke-title"></member>
+ <member><xref linkend="sql-alterdefaultprivileges" endterm="sql-alterdefaultprivileges-title"></member>
+ </simplelist>
</refsect1>
</refentry>
</varlistentry>
+ <varlistentry>
+ <term><literal>\ddp [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
+ <listitem>
+ <para>
+ Lists default access privilege settings. An entry is shown for
+ each role (and schema, if applicable) for which the default
+ privilege settings have been changed from the built-in defaults.
+ If <replaceable class="parameter">pattern</replaceable> is
+ specified, only entries whose role name or schema name matches
+ the pattern are listed.
+ </para>
+
+ <para>
+ The <xref linkend="sql-alterdefaultprivileges"
+ endterm="sql-alterdefaultprivileges-title"> command is used to set
+ default access privileges. The meaning of the
+ privilege display is explained under
+ <xref linkend="sql-grant" endterm="sql-grant-title">.
+ </para>
+ </listitem>
+ </varlistentry>
+
+
<varlistentry>
<term><literal>\dD[S] [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
<listitem>
class="parameter">pattern</replaceable> is specified, only
those roles whose names match the pattern are listed.
(This command is now effectively the same as <literal>\du</literal>).
- If the form <literal>\dg+</literal> is used, additional information
- is shown about each role, including the comment for each role.
+ If the form <literal>\dg+</literal> is used, additional information
+ is shown about each role, including the comment for each role.
</para>
</listitem>
</varlistentry>
<para>
The <xref linkend="sql-grant" endterm="sql-grant-title"> and
<xref linkend="sql-revoke" endterm="sql-revoke-title">
- commands are used to set access privileges.
+ commands are used to set access privileges. The meaning of the
+ privilege display is explained under
+ <xref linkend="sql-grant" endterm="sql-grant-title">.
</para>
</listitem>
</varlistentry>
specified, only tables,views and sequences whose names match the pattern are listed.
</para>
- <para>
- The <xref linkend="sql-grant" endterm="sql-grant-title"> and
- <xref linkend="sql-revoke" endterm="sql-revoke-title">
- commands are used to set access privileges.
- </para>
-
<para>
This is an alias for <command>\dp</command> (<quote>display
privileges</quote>).
&alterAggregate;
&alterConversion;
&alterDatabase;
+ &alterDefaultPrivileges;
&alterDomain;
&alterForeignDataWrapper;
&alterFunction;
0,
ONCOMMIT_NOOP,
(Datum) 0,
+ false,
true);
elog(DEBUG4, "relation created with oid %u", id);
}
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
+ pg_default_acl.h \
toasting.h indexing.h \
)
#include "catalog/pg_authid.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "utils/tqual.h"
+/*
+ * The information about one Grant/Revoke statement, in internal format: object
+ * and grantees names have been turned into Oids, the privilege list is an
+ * AclMode bitmask. If 'privileges' is ACL_NO_RIGHTS (the 0 value) and
+ * all_privs is true, 'privileges' will be internally set to the right kind of
+ * ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the
+ * InternalGrant struct!)
+ *
+ * Note: 'all_privs' and 'privileges' represent object-level privileges only.
+ * There might also be column-level privilege specifications, which are
+ * represented in col_privs (this is a list of untransformed AccessPriv nodes).
+ * Column privileges are only valid for objtype ACL_OBJECT_RELATION.
+ */
+typedef struct
+{
+ bool is_grant;
+ GrantObjectType objtype;
+ List *objects;
+ bool all_privs;
+ AclMode privileges;
+ List *col_privs;
+ List *grantees;
+ bool grant_option;
+ DropBehavior behavior;
+} InternalGrant;
+
+/*
+ * Internal format used by ALTER DEFAULT PRIVILEGES.
+ */
+typedef struct
+{
+ Oid roleid; /* owning role */
+ Oid nspid; /* namespace, or InvalidOid if none */
+ /* remaining fields are same as in InternalGrant: */
+ bool is_grant;
+ GrantObjectType objtype;
+ bool all_privs;
+ AclMode privileges;
+ List *grantees;
+ bool grant_option;
+ DropBehavior behavior;
+} InternalDefaultACL;
+
+
+static void ExecGrantStmt_oids(InternalGrant *istmt);
static void ExecGrant_Relation(InternalGrant *grantStmt);
static void ExecGrant_Database(InternalGrant *grantStmt);
static void ExecGrant_Fdw(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
+static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
+static void SetDefaultACL(InternalDefaultACL *iacls);
+
static List *objectNamesToOids(GrantObjectType objtype, List *objnames);
static void expand_col_privileges(List *colnames, Oid table_oid,
AclMode this_privileges,
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
default:
+ elog(ERROR, "unrecognized GrantStmt.objtype: %d",
+ (int) stmt->objtype);
/* keep compiler quiet */
all_privileges = ACL_NO_RIGHTS;
errormsg = NULL;
- elog(ERROR, "unrecognized GrantStmt.objtype: %d",
- (int) stmt->objtype);
}
if (stmt->privileges == NIL)
/*
* ExecGrantStmt_oids
*
- * "Internal" entrypoint for granting and revoking privileges. This is
- * exported for pg_shdepend.c to use in revoking privileges when dropping
- * a role.
+ * Internal entry point for granting and revoking privileges.
*/
-void
+static void
ExecGrantStmt_oids(InternalGrant *istmt)
{
switch (istmt->objtype)
return objects;
}
+/*
+ * ALTER DEFAULT PRIVILEGES statement
+ */
+void
+ExecAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *stmt)
+{
+ GrantStmt *action = stmt->action;
+ InternalDefaultACL iacls;
+ ListCell *cell;
+ List *rolenames = NIL;
+ List *nspnames = NIL;
+ DefElem *drolenames = NULL;
+ DefElem *dnspnames = NULL;
+ AclMode all_privileges;
+ const char *errormsg;
+
+ /* Deconstruct the "options" part of the statement */
+ foreach(cell, stmt->options)
+ {
+ DefElem *defel = (DefElem *) lfirst(cell);
+
+ if (strcmp(defel->defname, "schemas") == 0)
+ {
+ if (dnspnames)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ dnspnames = defel;
+ }
+ else if (strcmp(defel->defname, "roles") == 0)
+ {
+ if (drolenames)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ drolenames = defel;
+ }
+ else
+ elog(ERROR, "option \"%s\" not recognized", defel->defname);
+ }
+
+ if (dnspnames)
+ nspnames = (List *) dnspnames->arg;
+ if (drolenames)
+ rolenames = (List *) drolenames->arg;
+
+ /* Prepare the InternalDefaultACL representation of the statement */
+ /* roleid to be filled below */
+ /* nspid to be filled in SetDefaultACLsInSchemas */
+ iacls.is_grant = action->is_grant;
+ iacls.objtype = action->objtype;
+ /* all_privs to be filled below */
+ /* privileges to be filled below */
+ iacls.grantees = NIL; /* filled below */
+ iacls.grant_option = action->grant_option;
+ iacls.behavior = action->behavior;
+
+ /*
+ * Convert the PrivGrantee list into an Oid list. Note that at this point
+ * we insert an ACL_ID_PUBLIC into the list if an empty role name is
+ * detected (which is what the grammar uses if PUBLIC is found), so
+ * downstream there shouldn't be any additional work needed to support
+ * this case.
+ */
+ foreach(cell, action->grantees)
+ {
+ PrivGrantee *grantee = (PrivGrantee *) lfirst(cell);
+
+ if (grantee->rolname == NULL)
+ iacls.grantees = lappend_oid(iacls.grantees, ACL_ID_PUBLIC);
+ else
+ iacls.grantees =
+ lappend_oid(iacls.grantees,
+ get_roleid_checked(grantee->rolname));
+ }
+
+ /*
+ * Convert action->privileges, a list of privilege strings,
+ * into an AclMode bitmask.
+ */
+ switch (action->objtype)
+ {
+ case ACL_OBJECT_RELATION:
+ all_privileges = ACL_ALL_RIGHTS_RELATION;
+ errormsg = gettext_noop("invalid privilege type %s for relation");
+ break;
+ case ACL_OBJECT_SEQUENCE:
+ all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+ errormsg = gettext_noop("invalid privilege type %s for sequence");
+ break;
+ case ACL_OBJECT_FUNCTION:
+ all_privileges = ACL_ALL_RIGHTS_FUNCTION;
+ errormsg = gettext_noop("invalid privilege type %s for function");
+ break;
+ default:
+ elog(ERROR, "unrecognized GrantStmt.objtype: %d",
+ (int) action->objtype);
+ /* keep compiler quiet */
+ all_privileges = ACL_NO_RIGHTS;
+ errormsg = NULL;
+ }
+
+ if (action->privileges == NIL)
+ {
+ iacls.all_privs = true;
+
+ /*
+ * will be turned into ACL_ALL_RIGHTS_* by the internal routines
+ * depending on the object type
+ */
+ iacls.privileges = ACL_NO_RIGHTS;
+ }
+ else
+ {
+ iacls.all_privs = false;
+ iacls.privileges = ACL_NO_RIGHTS;
+
+ foreach(cell, action->privileges)
+ {
+ AccessPriv *privnode = (AccessPriv *) lfirst(cell);
+ AclMode priv;
+
+ if (privnode->cols)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("default privileges cannot be set for columns")));
+
+ if (privnode->priv_name == NULL) /* parser mistake? */
+ elog(ERROR, "AccessPriv node must specify privilege");
+ priv = string_to_privilege(privnode->priv_name);
+
+ if (priv & ~((AclMode) all_privileges))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg(errormsg, privilege_to_string(priv))));
+
+ iacls.privileges |= priv;
+ }
+ }
+
+ if (rolenames == NIL)
+ {
+ /* Set permissions for myself */
+ iacls.roleid = GetUserId();
+
+ SetDefaultACLsInSchemas(&iacls, nspnames);
+ }
+ else
+ {
+ /* Look up the role OIDs and do permissions checks */
+ ListCell *rolecell;
+
+ foreach(rolecell, rolenames)
+ {
+ char *rolename = strVal(lfirst(rolecell));
+
+ iacls.roleid = get_roleid_checked(rolename);
+
+ /*
+ * We insist that calling user be a member of each target role.
+ * If he has that, he could become that role anyway via SET ROLE,
+ * so FOR ROLE is just a syntactic convenience and doesn't give
+ * any special privileges.
+ */
+ check_is_member_of_role(GetUserId(), iacls.roleid);
+
+ SetDefaultACLsInSchemas(&iacls, nspnames);
+ }
+ }
+}
+
+/*
+ * Process ALTER DEFAULT PRIVILEGES for a list of target schemas
+ *
+ * All fields of *iacls except nspid were filled already
+ */
+static void
+SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames)
+{
+ if (nspnames == NIL)
+ {
+ /* Set database-wide permissions if no schema was specified */
+ iacls->nspid = InvalidOid;
+
+ SetDefaultACL(iacls);
+ }
+ else
+ {
+ /* Look up the schema OIDs and do permissions checks */
+ ListCell *nspcell;
+
+ foreach(nspcell, nspnames)
+ {
+ char *nspname = strVal(lfirst(nspcell));
+ AclResult aclresult;
+
+ /*
+ * Normally we'd use LookupCreationNamespace here, but it's
+ * important to do the permissions check against the target role
+ * not the calling user, so write it out in full. We require
+ * CREATE privileges, since without CREATE you won't be able to do
+ * anything using the default privs anyway.
+ */
+ iacls->nspid = GetSysCacheOid(NAMESPACENAME,
+ CStringGetDatum(nspname),
+ 0, 0, 0);
+ if (!OidIsValid(iacls->nspid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_SCHEMA),
+ errmsg("schema \"%s\" does not exist", nspname)));
+
+ aclresult = pg_namespace_aclcheck(iacls->nspid, iacls->roleid,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ nspname);
+
+ SetDefaultACL(iacls);
+ }
+ }
+}
+
+
+/*
+ * Create or update a pg_default_acl entry
+ */
+static void
+SetDefaultACL(InternalDefaultACL *iacls)
+{
+ AclMode this_privileges = iacls->privileges;
+ char objtype;
+ Relation rel;
+ HeapTuple tuple;
+ bool isNew;
+ Acl *old_acl;
+ Acl *new_acl;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_default_acl];
+ bool nulls[Natts_pg_default_acl];
+ bool replaces[Natts_pg_default_acl];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ rel = heap_open(DefaultAclRelationId, RowExclusiveLock);
+
+ /*
+ * Convert ACL object type to pg_default_acl object type
+ * and handle all_privs option
+ */
+ switch (iacls->objtype)
+ {
+ case ACL_OBJECT_RELATION:
+ objtype = DEFACLOBJ_RELATION;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_RELATION;
+ break;
+
+ case ACL_OBJECT_SEQUENCE:
+ objtype = DEFACLOBJ_SEQUENCE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+ break;
+
+ case ACL_OBJECT_FUNCTION:
+ objtype = DEFACLOBJ_FUNCTION;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_FUNCTION;
+ break;
+
+ default:
+ elog(ERROR, "unrecognized objtype: %d",
+ (int) iacls->objtype);
+ objtype = 0; /* keep compiler quiet */
+ break;
+ }
+
+ /* Search for existing row for this object type in catalog */
+ tuple = SearchSysCache(DEFACLROLENSPOBJ,
+ ObjectIdGetDatum(iacls->roleid),
+ ObjectIdGetDatum(iacls->nspid),
+ CharGetDatum(objtype),
+ 0);
+
+ if (HeapTupleIsValid(tuple))
+ {
+ Datum aclDatum;
+ bool isNull;
+
+ aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
+ Anum_pg_default_acl_defaclacl,
+ &isNull);
+ if (!isNull)
+ old_acl = DatumGetAclPCopy(aclDatum);
+ else
+ old_acl = NULL;
+ isNew = false;
+ }
+ else
+ {
+ old_acl = NULL;
+ isNew = true;
+ }
+
+ if (old_acl == NULL)
+ {
+ /*
+ * If we are creating a global entry, start with the hard-wired
+ * defaults and modify as per command. Otherwise, start with an empty
+ * ACL and modify that. This is needed because global entries
+ * replace the hard-wired defaults, while others do not.
+ */
+ if (!OidIsValid(iacls->nspid))
+ old_acl = acldefault(iacls->objtype, iacls->roleid);
+ else
+ old_acl = make_empty_acl();
+ }
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information. Collect data before
+ * merge_acl_with_grant throws away old_acl.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ /*
+ * Generate new ACL. Grantor of rights is always the same as the
+ * target role.
+ */
+ new_acl = merge_acl_with_grant(old_acl,
+ iacls->is_grant,
+ iacls->grant_option,
+ iacls->behavior,
+ iacls->grantees,
+ this_privileges,
+ iacls->roleid,
+ iacls->roleid);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ if (isNew)
+ {
+ values[Anum_pg_default_acl_defaclrole - 1] = ObjectIdGetDatum(iacls->roleid);
+ values[Anum_pg_default_acl_defaclnamespace - 1] = ObjectIdGetDatum(iacls->nspid);
+ values[Anum_pg_default_acl_defaclobjtype - 1] = CharGetDatum(objtype);
+ values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ simple_heap_insert(rel, newtuple);
+ }
+ else
+ {
+ values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
+ replaces[Anum_pg_default_acl_defaclacl - 1] = true;
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
+ values, nulls, replaces);
+ simple_heap_update(rel, &newtuple->t_self, newtuple);
+ }
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(rel, newtuple);
+
+ /* these dependencies don't change in an update */
+ if (isNew)
+ {
+ /* dependency on role */
+ recordDependencyOnOwner(DefaultAclRelationId,
+ HeapTupleGetOid(newtuple),
+ iacls->roleid);
+
+ /* dependency on namespace */
+ if (OidIsValid(iacls->nspid))
+ {
+ ObjectAddress myself,
+ referenced;
+
+ myself.classId = DefaultAclRelationId;
+ myself.objectId = HeapTupleGetOid(newtuple);
+ myself.objectSubId = 0;
+
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = iacls->nspid;
+ referenced.objectSubId = 0;
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+ }
+ }
+
+ /*
+ * Update the shared dependency ACL info
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ updateAclDependencies(DefaultAclRelationId, HeapTupleGetOid(newtuple), 0,
+ iacls->roleid, iacls->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ pfree(new_acl);
+
+ if (HeapTupleIsValid(tuple))
+ ReleaseSysCache(tuple);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+
+/*
+ * RemoveRoleFromObjectACL
+ *
+ * Used by shdepDropOwned to remove mentions of a role in ACLs
+ */
+void
+RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
+{
+ if (classid == DefaultAclRelationId)
+ {
+ InternalDefaultACL iacls;
+ Form_pg_default_acl pg_default_acl_tuple;
+ Relation rel;
+ ScanKeyData skey[1];
+ SysScanDesc scan;
+ HeapTuple tuple;
+
+ /* first fetch info needed by SetDefaultACL */
+ rel = heap_open(DefaultAclRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objid));
+
+ scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tuple = systable_getnext(scan);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "could not find tuple for default ACL %u", objid);
+
+ pg_default_acl_tuple = (Form_pg_default_acl) GETSTRUCT(tuple);
+
+ iacls.roleid = pg_default_acl_tuple->defaclrole;
+ iacls.nspid = pg_default_acl_tuple->defaclnamespace;
+
+ switch (pg_default_acl_tuple->defaclobjtype)
+ {
+ case DEFACLOBJ_RELATION:
+ iacls.objtype = ACL_OBJECT_RELATION;
+ break;
+ case ACL_OBJECT_SEQUENCE:
+ iacls.objtype = ACL_OBJECT_SEQUENCE;
+ break;
+ case DEFACLOBJ_FUNCTION:
+ iacls.objtype = ACL_OBJECT_FUNCTION;
+ break;
+ default:
+ /* Shouldn't get here */
+ elog(ERROR, "unexpected default ACL type %d",
+ pg_default_acl_tuple->defaclobjtype);
+ break;
+ }
+
+ systable_endscan(scan);
+ heap_close(rel, AccessShareLock);
+
+ iacls.is_grant = false;
+ iacls.all_privs = true;
+ iacls.privileges = ACL_NO_RIGHTS;
+ iacls.grantees = list_make1_oid(roleid);
+ iacls.grant_option = false;
+ iacls.behavior = DROP_CASCADE;
+
+ /* Do it */
+ SetDefaultACL(&iacls);
+ }
+ else
+ {
+ InternalGrant istmt;
+
+ switch (classid)
+ {
+ case RelationRelationId:
+ /* it's OK to use RELATION for a sequence */
+ istmt.objtype = ACL_OBJECT_RELATION;
+ break;
+ case DatabaseRelationId:
+ istmt.objtype = ACL_OBJECT_DATABASE;
+ break;
+ case ProcedureRelationId:
+ istmt.objtype = ACL_OBJECT_FUNCTION;
+ break;
+ case LanguageRelationId:
+ istmt.objtype = ACL_OBJECT_LANGUAGE;
+ break;
+ case NamespaceRelationId:
+ istmt.objtype = ACL_OBJECT_NAMESPACE;
+ break;
+ case TableSpaceRelationId:
+ istmt.objtype = ACL_OBJECT_TABLESPACE;
+ break;
+ default:
+ elog(ERROR, "unexpected object class %u", classid);
+ break;
+ }
+ istmt.is_grant = false;
+ istmt.objects = list_make1_oid(objid);
+ istmt.all_privs = true;
+ istmt.privileges = ACL_NO_RIGHTS;
+ istmt.col_privs = NIL;
+ istmt.grantees = list_make1_oid(roleid);
+ istmt.grant_option = false;
+ istmt.behavior = DROP_CASCADE;
+
+ ExecGrantStmt_oids(&istmt);
+ }
+}
+
+
+/*
+ * Remove a pg_default_acl entry
+ */
+void
+RemoveDefaultACLById(Oid defaclOid)
+{
+ Relation rel;
+ ScanKeyData skey[1];
+ SysScanDesc scan;
+ HeapTuple tuple;
+
+ rel = heap_open(DefaultAclRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(defaclOid));
+
+ scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tuple = systable_getnext(scan);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "could not find tuple for default ACL %u", defaclOid);
+
+ simple_heap_delete(rel, &tuple->t_self);
+
+ systable_endscan(scan);
+ heap_close(rel, RowExclusiveLock);
+}
+
+
/*
* expand_col_privileges
*
return has_privs_of_role(roleid, ownerId);
}
+
+/*
+ * Fetch pg_default_acl entry for given role, namespace and object type
+ * (object type must be given in pg_default_acl's encoding).
+ * Returns NULL if no such entry.
+ */
+static Acl *
+get_default_acl_internal(Oid roleId, Oid nsp_oid, char objtype)
+{
+ Acl *result = NULL;
+ HeapTuple tuple;
+
+ tuple = SearchSysCache(DEFACLROLENSPOBJ,
+ ObjectIdGetDatum(roleId),
+ ObjectIdGetDatum(nsp_oid),
+ CharGetDatum(objtype),
+ 0);
+
+ if (HeapTupleIsValid(tuple))
+ {
+ Datum aclDatum;
+ bool isNull;
+
+ aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
+ Anum_pg_default_acl_defaclacl,
+ &isNull);
+ if (!isNull)
+ result = DatumGetAclPCopy(aclDatum);
+ ReleaseSysCache(tuple);
+ }
+
+ return result;
+}
+
+/*
+ * Get default permissions for newly created object within given schema
+ *
+ * Returns NULL if built-in system defaults should be used
+ */
+Acl *
+get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid)
+{
+ Acl *result;
+ Acl *glob_acl;
+ Acl *schema_acl;
+ Acl *def_acl;
+ char defaclobjtype;
+
+ /*
+ * Use NULL during bootstrap, since pg_default_acl probably isn't there
+ * yet.
+ */
+ if (IsBootstrapProcessingMode())
+ return NULL;
+
+ /* Check if object type is supported in pg_default_acl */
+ switch (objtype)
+ {
+ case ACL_OBJECT_RELATION:
+ defaclobjtype = DEFACLOBJ_RELATION;
+ break;
+
+ case ACL_OBJECT_SEQUENCE:
+ defaclobjtype = DEFACLOBJ_SEQUENCE;
+ break;
+
+ case ACL_OBJECT_FUNCTION:
+ defaclobjtype = DEFACLOBJ_FUNCTION;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ /* Look up the relevant pg_default_acl entries */
+ glob_acl = get_default_acl_internal(ownerId, InvalidOid, defaclobjtype);
+ schema_acl = get_default_acl_internal(ownerId, nsp_oid, defaclobjtype);
+
+ /* Quick out if neither entry exists */
+ if (glob_acl == NULL && schema_acl == NULL)
+ return NULL;
+
+ /* We need to know the hard-wired default value, too */
+ def_acl = acldefault(objtype, ownerId);
+
+ /* If there's no global entry, substitute the hard-wired default */
+ if (glob_acl == NULL)
+ glob_acl = def_acl;
+
+ /* Merge in any per-schema privileges */
+ result = aclmerge(glob_acl, schema_acl, ownerId);
+
+ /*
+ * For efficiency, we want to return NULL if the result equals default.
+ * This requires sorting both arrays to get an accurate comparison.
+ */
+ aclitemsort(result);
+ aclitemsort(def_acl);
+ if (aclequal(result, def_acl))
+ result = NULL;
+
+ return result;
+}
#include "catalog/pg_conversion.h"
#include "catalog/pg_conversion_fn.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteRemove.h"
#include "storage/lmgr.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
TableSpaceRelationId, /* OCLASS_TBLSPACE */
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
- UserMappingRelationId /* OCLASS_USER_MAPPING */
+ UserMappingRelationId, /* OCLASS_USER_MAPPING */
+ DefaultAclRelationId /* OCLASS_DEFACL */
};
RemoveUserMappingById(object->objectId);
break;
+ case OCLASS_DEFACL:
+ RemoveDefaultACLById(object->objectId);
+ break;
+
default:
elog(ERROR, "unrecognized object class: %u",
object->classId);
case UserMappingRelationId:
Assert(object->objectSubId == 0);
return OCLASS_USER_MAPPING;
+
+ case DefaultAclRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_DEFACL;
}
/* shouldn't get here */
break;
}
+ case OCLASS_DEFACL:
+ {
+ Relation defaclrel;
+ ScanKeyData skey[1];
+ SysScanDesc rcscan;
+ HeapTuple tup;
+ Form_pg_default_acl defacl;
+
+ defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ rcscan = systable_beginscan(defaclrel, DefaultAclOidIndexId,
+ true, SnapshotNow, 1, skey);
+
+ tup = systable_getnext(rcscan);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "could not find tuple for default ACL %u",
+ object->objectId);
+
+ defacl = (Form_pg_default_acl) GETSTRUCT(tup);
+
+ switch (defacl->defaclobjtype)
+ {
+ case DEFACLOBJ_RELATION:
+ appendStringInfo(&buffer,
+ _("default privileges on new relations belonging to role %s"),
+ GetUserNameFromId(defacl->defaclrole));
+ break;
+ case DEFACLOBJ_SEQUENCE:
+ appendStringInfo(&buffer,
+ _("default privileges on new sequences belonging to role %s"),
+ GetUserNameFromId(defacl->defaclrole));
+ break;
+ case DEFACLOBJ_FUNCTION:
+ appendStringInfo(&buffer,
+ _("default privileges on new functions belonging to role %s"),
+ GetUserNameFromId(defacl->defaclrole));
+ break;
+ default:
+ /* shouldn't get here */
+ appendStringInfo(&buffer,
+ _("default privileges belonging to role %s"),
+ GetUserNameFromId(defacl->defaclrole));
+ break;
+ }
+
+ if (OidIsValid(defacl->defaclnamespace))
+ {
+ appendStringInfo(&buffer,
+ _(" in schema %s"),
+ get_namespace_name(defacl->defaclnamespace));
+ }
+
+ systable_endscan(rcscan);
+ heap_close(defaclrel, AccessShareLock);
+ break;
+ }
+
default:
appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId,
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/smgr.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
Oid new_rel_oid, Oid new_type_oid,
Oid relowner,
char relkind,
+ Datum relacl,
Datum reloptions);
static Oid AddNewRelationType(const char *typeName,
Oid typeNamespace,
* Caller has already opened and locked pg_class.
* Tuple data is taken from new_rel_desc->rd_rel, except for the
* variable-width fields which are not present in a cached reldesc.
- * We always initialize relacl to NULL (i.e., default permissions),
- * and reloptions is set to the passed-in text array (if any).
+ * relacl and reloptions are passed in Datum form (to avoid having
+ * to reference the data types in heap.h). Pass (Datum) 0 to set them
+ * to NULL.
* --------------------------------
*/
void
InsertPgClassTuple(Relation pg_class_desc,
Relation new_rel_desc,
Oid new_rel_oid,
+ Datum relacl,
Datum reloptions)
{
Form_pg_class rd_rel = new_rel_desc->rd_rel;
values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
- /* start out with empty permissions */
- nulls[Anum_pg_class_relacl - 1] = true;
+ if (relacl != (Datum) 0)
+ values[Anum_pg_class_relacl - 1] = relacl;
+ else
+ nulls[Anum_pg_class_relacl - 1] = true;
if (reloptions != (Datum) 0)
values[Anum_pg_class_reloptions - 1] = reloptions;
else
Oid new_type_oid,
Oid relowner,
char relkind,
+ Datum relacl,
Datum reloptions)
{
Form_pg_class new_rel_reltup;
new_rel_desc->rd_att->tdtypeid = new_type_oid;
/* Now build and insert the tuple */
- InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions);
+ InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
+ relacl, reloptions);
}
* heap_create_with_catalog
*
* creates a new cataloged relation. see comments above.
+ *
+ * Arguments:
+ * relname: name to give to new rel
+ * relnamespace: OID of namespace it goes in
+ * reltablespace: OID of tablespace it goes in
+ * relid: OID to assign to new rel, or InvalidOid to select a new OID
+ * reltypeid: OID to assign to rel's rowtype, or InvalidOid to select one
+ * ownerid: OID of new rel's owner
+ * tupdesc: tuple descriptor (source of column definitions)
+ * cooked_constraints: list of precooked check constraints and defaults
+ * relkind: relkind for new rel
+ * shared_relation: TRUE if it's to be a shared relation
+ * oidislocal: TRUE if oid column (if any) should be marked attislocal
+ * oidinhcount: attinhcount to assign to oid column (if any)
+ * oncommit: ON COMMIT marking (only relevant if it's a temp table)
+ * reloptions: reloptions in Datum form, or (Datum) 0 if none
+ * use_user_acl: TRUE if should look for user-defined default permissions;
+ * if FALSE, relacl is always set NULL
+ * allow_system_table_mods: TRUE to allow creation in system namespaces
+ *
+ * Returns the OID of the new relation
* --------------------------------
*/
Oid
int oidinhcount,
OnCommitAction oncommit,
Datum reloptions,
+ bool use_user_acl,
bool allow_system_table_mods)
{
Relation pg_class_desc;
Relation new_rel_desc;
+ Acl *relacl;
Oid old_type_oid;
Oid new_type_oid;
Oid new_array_oid = InvalidOid;
relid = GetNewRelFileNode(reltablespace, shared_relation,
pg_class_desc);
+ /*
+ * Determine the relation's initial permissions.
+ */
+ if (use_user_acl)
+ {
+ switch (relkind)
+ {
+ case RELKIND_RELATION:
+ case RELKIND_VIEW:
+ relacl = get_user_default_acl(ACL_OBJECT_RELATION, ownerid,
+ relnamespace);
+ break;
+ case RELKIND_SEQUENCE:
+ relacl = get_user_default_acl(ACL_OBJECT_SEQUENCE, ownerid,
+ relnamespace);
+ break;
+ default:
+ relacl = NULL;
+ break;
+ }
+ }
+ else
+ relacl = NULL;
+
/*
* Create the relcache entry (mostly dummy at this point) and the physical
* disk file. (If we fail further down, it's the smgr's responsibility to
new_type_oid,
ownerid,
relkind,
+ PointerGetDatum(relacl),
reloptions);
/*
/*
* Make a dependency link to force the relation to be deleted if its
- * namespace is. Also make a dependency link to its owner.
+ * namespace is. Also make a dependency link to its owner, as well
+ * as dependencies for any roles mentioned in the default ACL.
*
* For composite types, these dependencies are tracked for the pg_type
* entry, so we needn't record them here. Likewise, TOAST tables don't
* need a namespace dependency (they live in a pinned namespace) nor an
- * owner dependency (they depend indirectly through the parent table).
+ * owner dependency (they depend indirectly through the parent table),
+ * nor should they have any ACL entries.
+ *
* Also, skip this in bootstrap mode, since we don't make dependencies
* while bootstrapping.
*/
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
recordDependencyOnOwner(RelationRelationId, relid, ownerid);
+
+ if (relacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(relacl, &newmembers);
+ updateAclDependencies(RelationRelationId, relid, 0,
+ ownerid, true,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
}
/*
*/
InsertPgClassTuple(pg_class, indexRelation,
RelationGetRelid(indexRelation),
+ (Datum) 0,
reloptions);
/* done with pg_class */
bool internalOutParam = false;
Oid variadicType = InvalidOid;
Oid proowner = GetUserId();
+ Acl *proacl = NULL;
Relation rel;
HeapTuple tup;
HeapTuple oldtup;
values[Anum_pg_proc_proconfig - 1] = proconfig;
else
nulls[Anum_pg_proc_proconfig - 1] = true;
- /* start out with empty permissions */
- nulls[Anum_pg_proc_proacl - 1] = true;
+ /* proacl will be determined later */
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
tupDesc = RelationGetDescr(rel);
else
{
/* Creating a new procedure */
+
+ /* First, get default permissions and set up proacl */
+ proacl = get_user_default_acl(ACL_OBJECT_FUNCTION, proowner,
+ procNamespace);
+ if (proacl != NULL)
+ values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl);
+ else
+ nulls[Anum_pg_proc_proacl - 1] = true;
+
tup = heap_form_tuple(tupDesc, values, nulls);
simple_heap_insert(rel, tup);
is_update = false;
if (!is_update)
recordDependencyOnOwner(ProcedureRelationId, retval, proowner);
+ /* dependency on any roles mentioned in ACL */
+ if (!is_update && proacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(proacl, &newmembers);
+ updateAclDependencies(ProcedureRelationId, retval, 0,
+ proowner, true,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
#include "catalog/pg_authid.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
while ((tuple = systable_getnext(scan)) != NULL)
{
Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
- InternalGrant istmt;
ObjectAddress obj;
/* We only operate on objects in the current database */
elog(ERROR, "unexpected dependency type");
break;
case SHARED_DEPENDENCY_ACL:
- switch (sdepForm->classid)
- {
- case RelationRelationId:
- /* it's OK to use RELATION for a sequence */
- istmt.objtype = ACL_OBJECT_RELATION;
- break;
- case DatabaseRelationId:
- istmt.objtype = ACL_OBJECT_DATABASE;
- break;
- case ProcedureRelationId:
- istmt.objtype = ACL_OBJECT_FUNCTION;
- break;
- case LanguageRelationId:
- istmt.objtype = ACL_OBJECT_LANGUAGE;
- break;
- case NamespaceRelationId:
- istmt.objtype = ACL_OBJECT_NAMESPACE;
- break;
- case TableSpaceRelationId:
- istmt.objtype = ACL_OBJECT_TABLESPACE;
- break;
- default:
- elog(ERROR, "unexpected object type %d",
- sdepForm->classid);
- break;
- }
- istmt.is_grant = false;
- istmt.objects = list_make1_oid(sdepForm->objid);
- istmt.all_privs = true;
- istmt.privileges = ACL_NO_RIGHTS;
- istmt.col_privs = NIL;
- istmt.grantees = list_make1_oid(roleid);
- istmt.grant_option = false;
- istmt.behavior = DROP_CASCADE;
-
- ExecGrantStmt_oids(&istmt);
+ RemoveRoleFromObjectACL(roleid,
+ sdepForm->classid,
+ sdepForm->objid);
break;
case SHARED_DEPENDENCY_OWNER:
/* Save it for deletion below */
AlterLanguageOwner_oid(sdepForm->objid, newrole);
break;
+ case DefaultAclRelationId:
+ /*
+ * Ignore default ACLs; they should be handled by
+ * DROP OWNED, not REASSIGN OWNED.
+ */
+ break;
+
default:
- elog(ERROR, "unexpected classid %d", sdepForm->classid);
+ elog(ERROR, "unexpected classid %u", sdepForm->classid);
break;
}
/* Make sure the next iteration will see my changes */
0,
ONCOMMIT_NOOP,
reloptions,
+ false,
true);
/* make the toast relation visible, else index creation will fail */
0,
ONCOMMIT_NOOP,
reloptions,
+ false,
allowSystemTableMods);
ReleaseSysCache(tuple);
parentOidCount,
stmt->oncommit,
reloptions,
+ true,
allowSystemTableMods);
StoreCatalogInheritance(relationId, inheritOids);
case OCLASS_FDW:
case OCLASS_FOREIGN_SERVER:
case OCLASS_USER_MAPPING:
+ case OCLASS_DEFACL:
/*
* We don't expect any of these sorts of objects to depend on
0,
into->onCommit,
reloptions,
+ true,
allowSystemTableMods);
FreeTupleDesc(tupdesc);
return newnode;
}
+static AlterDefaultPrivilegesStmt *
+_copyAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *from)
+{
+ AlterDefaultPrivilegesStmt *newnode = makeNode(AlterDefaultPrivilegesStmt);
+
+ COPY_NODE_FIELD(options);
+ COPY_NODE_FIELD(action);
+
+ return newnode;
+}
+
static DeclareCursorStmt *
_copyDeclareCursorStmt(DeclareCursorStmt *from)
{
case T_GrantRoleStmt:
retval = _copyGrantRoleStmt(from);
break;
+ case T_AlterDefaultPrivilegesStmt:
+ retval = _copyAlterDefaultPrivilegesStmt(from);
+ break;
case T_DeclareCursorStmt:
retval = _copyDeclareCursorStmt(from);
break;
return true;
}
+static bool
+_equalAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *a, AlterDefaultPrivilegesStmt *b)
+{
+ COMPARE_NODE_FIELD(options);
+ COMPARE_NODE_FIELD(action);
+
+ return true;
+}
+
static bool
_equalDeclareCursorStmt(DeclareCursorStmt *a, DeclareCursorStmt *b)
{
case T_GrantRoleStmt:
retval = _equalGrantRoleStmt(a, b);
break;
+ case T_AlterDefaultPrivilegesStmt:
+ retval = _equalAlterDefaultPrivilegesStmt(a, b);
+ break;
case T_DeclareCursorStmt:
retval = _equalDeclareCursorStmt(a, b);
break;
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
- AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
+ AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
+ AlterRoleStmt AlterRoleSetStmt
+ AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateGroupStmt CreateOpClassStmt
%type <privtarget> privilege_target
%type <funwithargs> function_with_argtypes
%type <list> function_with_argtypes_list
+%type <ival> defacl_privilege_target
+%type <defelt> DefACLOption
+%type <list> DefACLOptionList
%type <list> stmtblock stmtmulti
OptTableElementList TableElementList OptInherit definition
stmt :
AlterDatabaseStmt
| AlterDatabaseSetStmt
+ | AlterDefaultPrivilegesStmt
| AlterDomainStmt
| AlterFdwStmt
| AlterForeignServerStmt
;
/* This should match def_elem and also allow qualified names */
-reloption_elem:
+reloption_elem:
ColLabel '=' def_arg
{
$$ = makeDefElem($1, (Node *) $3);
| /*EMPTY*/ { $$ = NULL; }
;
+/*****************************************************************************
+ *
+ * ALTER DEFAULT PRIVILEGES statement
+ *
+ *****************************************************************************/
+
+AlterDefaultPrivilegesStmt:
+ ALTER DEFAULT PRIVILEGES DefACLOptionList DefACLAction
+ {
+ AlterDefaultPrivilegesStmt *n = makeNode(AlterDefaultPrivilegesStmt);
+ n->options = $4;
+ n->action = (GrantStmt *) $5;
+ $$ = (Node*)n;
+ }
+ ;
+
+DefACLOptionList:
+ DefACLOptionList DefACLOption { $$ = lappend($1, $2); }
+ | /* EMPTY */ { $$ = NIL; }
+ ;
+
+DefACLOption:
+ IN_P SCHEMA name_list
+ {
+ $$ = makeDefElem("schemas", (Node *)$3);
+ }
+ | FOR ROLE name_list
+ {
+ $$ = makeDefElem("roles", (Node *)$3);
+ }
+ | FOR USER name_list
+ {
+ $$ = makeDefElem("roles", (Node *)$3);
+ }
+ ;
+
+/*
+ * This should match GRANT/REVOKE, except that target objects are missing
+ * and we only allow a subset of object types.
+ */
+DefACLAction:
+ GRANT privileges ON defacl_privilege_target TO grantee_list
+ opt_grant_grant_option
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = true;
+ n->privileges = $2;
+ n->objtype = $4;
+ n->objects = NIL;
+ n->grantees = $6;
+ n->grant_option = $7;
+ $$ = (Node*)n;
+ }
+ | REVOKE privileges ON defacl_privilege_target
+ FROM grantee_list opt_drop_behavior
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = false;
+ n->grant_option = false;
+ n->privileges = $2;
+ n->objtype = $4;
+ n->objects = NIL;
+ n->grantees = $6;
+ n->behavior = $7;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR privileges ON defacl_privilege_target
+ FROM grantee_list opt_drop_behavior
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = false;
+ &