Commit 4ceb2d0c authored by Peter Eisentraut's avatar Peter Eisentraut

* User management commands no longer user pg_exec_query_dest -> more robust

* Let unprivileged users change their own passwords.

* The password is now an Sconst in the parser, which better reflects its text datatype and also
forces users to quote them.

* If your password is NULL you won't be written to the password file, meaning you can't connect
until you have a password set up (if you use password authentication).

* When you drop a user that owns a database you get an error. The database is not gone.
parent 2af360ed
......@@ -8,7 +8,7 @@
#
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/doc/src/sgml/Makefile,v 1.12 1999/12/05 20:21:59 momjian Exp $
# $Header: /cvsroot/pgsql/doc/src/sgml/Makefile,v 1.13 2000/01/14 22:11:31 petere Exp $
#
#----------------------------------------------------------------------------
......@@ -85,15 +85,17 @@ APPLICATIONS= createdb.sgml createuser.sgml \
psql-ref.sgml \
vacuumdb.sgml
COMMANDS= abort.sgml alter_table.sgml alter_user.sgml \
COMMANDS= abort.sgml alter_group.sgml alter_table.sgml alter_user.sgml \
begin.sgml \
close.sgml cluster.sgml commit.sgml copy.sgml \
create_aggregate.sgml create_database.sgml create_function.sgml create_index.sgml \
create_aggregate.sgml create_database.sgml create_function.sgml create_group.sgml \
create_index.sgml \
create_language.sgml create_operator.sgml create_rule.sgml create_sequence.sgml \
create_table.sgml create_table_as.sgml create_trigger.sgml create_type.sgml \
create_user.sgml create_view.sgml \
declare.sgml delete.sgml \
drop_aggregate.sgml drop_database.sgml drop_function.sgml drop_index.sgml \
drop_aggregate.sgml drop_database.sgml drop_function.sgml drop_group.sgml \
drop_index.sgml \
drop_language.sgml drop_operator.sgml drop_rule.sgml drop_sequence.sgml \
drop_table.sgml drop_trigger.sgml drop_type.sgml drop_user.sgml drop_view.sgml \
explain.sgml fetch.sgml grant.sgml \
......
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.14 1999/12/05 20:02:42 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.15 2000/01/14 22:11:32 petere Exp $
Postgres documentation
Complete list of usable sgml source files in this directory.
-->
......@@ -40,6 +40,7 @@ Complete list of usable sgml source files in this directory.
<!-- these are in the "commands" reference chapter -->
<!entity abort system "abort.sgml">
<!entity alterGroup system "alter_group.sgml">
<!entity alterTable system "alter_table.sgml">
<!entity alterUser system "alter_user.sgml">
<!entity begin system "begin.sgml">
......@@ -50,6 +51,7 @@ Complete list of usable sgml source files in this directory.
<!entity createAggregate system "create_aggregate.sgml">
<!entity createDatabase system "create_database.sgml">
<!entity createFunction system "create_function.sgml">
<!entity createGroup system "create_group.sgml">
<!entity createIndex system "create_index.sgml">
<!entity createLanguage system "create_language.sgml">
<!entity createOperator system "create_operator.sgml">
......@@ -66,6 +68,7 @@ Complete list of usable sgml source files in this directory.
<!entity dropAggregate system "drop_aggregate.sgml">
<!entity dropDatabase system "drop_database.sgml">
<!entity dropFunction system "drop_function.sgml">
<!entity dropGroup system "drop_group.sgml">
<!entity dropIndex system "drop_index.sgml">
<!entity dropLanguage system "drop_language.sgml">
<!entity dropOperator system "drop_operator.sgml">
......
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_group.sgml,v 1.1 2000/01/14 22:11:32 petere Exp $
Postgres documentation
-->
<refentry id="SQL-ALTERGROUP">
<refmeta>
<refentrytitle id="SQL-ALTERGROUP-title">
ALTER GROUP
</refentrytitle>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>
ALTER GROUP
</refname>
<refpurpose>
Add users to a group, remove users from a group
</refpurpose>
</refnamediv>
<refsynopsisdiv>
<refsynopsisdivinfo>
<date>2000-01-14</date>
</refsynopsisdivinfo>
<synopsis>
ALTER GROUP <replaceable class="PARAMETER">name</replaceable> ADD USER <replaceable class="PARAMETER">username</replaceable> [, ... ]
ALTER GROUP <replaceable class="PARAMETER">name</replaceable> DROP USER <replaceable class="PARAMETER">username</replaceable> [, ... ]
</synopsis>
<refsect2 id="R2-SQL-ALTERGROUP-1">
<refsect2info>
<date>2000-01-14</date>
</refsect2info>
<title>
Inputs
</title>
<para>
<variablelist>
<varlistentry>
<term><replaceable class="PARAMETER">name</replaceable></term>
<listitem>
<para>
The name of the group to modify.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">username</replaceable></term>
<listitem>
<para>
Users which are to be added or removed from the group. The user
names must exist.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
<refsect2 id="R2-SQL-ALTERGROUP-2">
<refsect2info>
<date>2000-01-14</date>
</refsect2info>
<title>
Outputs
</title>
<para>
<variablelist>
<varlistentry>
<term><computeroutput>ALTER GROUP</computeroutput></term>
<listitem>
<para>
Message returned if the alteration was successful.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
</refsynopsisdiv>
<refsect1 id="R1-SQL-ALTERGROUP-1">
<refsect1info>
<date>2000-01-14</date>
</refsect1info>
<title>
Description
</title>
<para>
<command>ALTER GROUP</command> is used to change add users to a group or
remove them from a group. Only database superusers can use this command.
Adding a user to a group does not create the user. Similarly, removing
a user from a group does not drop the user itself.
</para>
<para>
Use <xref linkend="SQL-CREATEGROUP" endterm="SQL-CREATEGROUP-title">
to create a new group and <xref linkend="SQL-DROPGROUP"
endterm="SQL-DROPGROUP-title"> to remove a group.
</para>
</refsect1>
<refsect1 id="R1-SQL-ALTERGROUP-2">
<title>
Usage
</title>
<para>
Add users to a group:
<programlisting>
ALTER GROUP staff ADD USER karl, john
</programlisting>
Remove a user from a group
<programlisting>
ALTER GROUP workers DROP USER beth
</programlisting>
</para>
</refsect1>
<refsect1 id="R1-SQL-ALTERGROUP-3">
<title>
Compatibility
</title>
<refsect2 id="R2-SQL-ALTERGROUP-4">
<refsect2info>
<date>2000-01-14</date>
</refsect2info>
<title>
SQL92
</title>
<para>
There is no <command>ALTER GROUP</command> statement in
<acronym>SQL92</acronym>. The concept of roles is
similar.
</para>
</refsect2>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:nil
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
sgml-parent-document:nil
sgml-default-dtd-file:"../reference.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:"/usr/lib/sgml/catalog"
sgml-local-ecat-files:nil
End:
-->
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_user.sgml,v 1.9 1999/11/30 03:57:22 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_user.sgml,v 1.10 2000/01/14 22:11:32 petere Exp $
Postgres documentation
-->
......@@ -24,11 +24,8 @@ Postgres documentation
</refsynopsisdivinfo>
<synopsis>
ALTER USER <replaceable class="PARAMETER">username</replaceable>
[ WITH
[ SYSID <replaceable class="PARAMETER">uid</replaceable> ]
[ PASSWORD <replaceable class="PARAMETER">password</replaceable> ] ]
[ WITH PASSWORD '<replaceable class="PARAMETER">password</replaceable>' ]
[ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]
[ IN GROUP <replaceable class="PARAMETER">groupname</replaceable> [, ...] ]
[ VALID UNTIL '<replaceable class="PARAMETER">abstime</replaceable>' ]
</synopsis>
......@@ -40,24 +37,19 @@ ALTER USER <replaceable class="PARAMETER">username</replaceable>
Inputs
</title>
<para>
Refer to <command>CREATE USER</command> for a detailed description of each
clause.
</para>
<para>
<variablelist>
<varlistentry>
<term><replaceable class="PARAMETER"> username </replaceable></term>
<term><replaceable class="PARAMETER">username</replaceable></term>
<listitem>
<para>
The Postgres account name of the user whose details are to be altered.
The name of the user whose details are to be altered.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER"> password </replaceable></term>
<term><replaceable class="PARAMETER">password</replaceable></term>
<listitem>
<para>
The new password to be used for this account.
......@@ -66,36 +58,36 @@ ALTER USER <replaceable class="PARAMETER">username</replaceable>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">uid</replaceable></term>
<term>CREATEDB</term>
<term>NOCREATEDB</term>
<listitem>
<para>
The new <productname>PostgreSQL</productname> user id of the user.
Since this number is used as a key into the
<literal>pg_shadow</literal>/<literal>pg_user</literal> table
throughout the system catalogs, it is not recommended that you change
it unless the user in question does not own anything at all and/or
you really know what you are doing. Note that it is not necessary that
database and <acronym>UNIX</acronym> user ids match, but some people
choose to keep the numbers the same.
<para>
These clauses define a user's ability to create databases.
If CREATEDB is specified, the user being defined will
be allowed to create his own databases. Using NOCREATEDB
will deny a user the ability to create databases.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER"> groupname </replaceable></term>
<term>CREATEUSER</term>
<term>NOCREATEUSER</term>
<listitem>
<para>
The name of an access group into which this account is to be put.
These clauses determine whether a user will be permitted to
create new users himself. This option will also make the user
a superuser who can override all access restrictions.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER"> abstime </replaceable></term>
<term><replaceable class="PARAMETER">abstime</replaceable></term>
<listitem>
<para>
The date (and, optionally, the time)
at which this user's access is to be terminated.
at which this user's password is to expire.
</para>
</listitem>
</varlistentry>
......@@ -113,9 +105,7 @@ ALTER USER <replaceable class="PARAMETER">username</replaceable>
<para>
<variablelist>
<varlistentry>
<term><computeroutput>
ALTER USER
</computeroutput></term>
<term><computeroutput>ALTER USER</computeroutput></term>
<listitem>
<para>
Message returned if the alteration was successful.
......@@ -125,7 +115,7 @@ ALTER USER
<varlistentry>
<term><computeroutput>
ERROR: alterUser: user "username" does not exist
ERROR: ALTER USER: user "username" does not exist
</computeroutput></term>
<listitem>
<para>
......@@ -148,39 +138,15 @@ ERROR: alterUser: user "username" does not exist
</title>
<para>
<command>ALTER USER</command> is used to change the attributes of a user's
<productname>Postgres</productname> account.
Also, it is only possible for the
<productname>Postgres</productname>
user or any user with read and modify permissions on
<literal>pg_shadow</literal> to alter user passwords.
<productname>PostgreSQL</productname> account. Only a database superuser
can change privileges and password expiration with this command. Ordinary
users can only change their own password.
</para>
<para>
If any of the clauses of the alter user statement are
omitted, the corresponding value in the <literal>pg_shadow</literal> table
is left unchanged.
Use <xref linkend="SQL-CREATEUSER" endterm="SQL-CREATEUSER-title">
to create a new user and <xref linkend="SQL-DROPUSER"
endterm="SQL-DROPUSER-title"> to remove a user.
</para>
<refsect2 id="R2-SQL-ALTERUSER-3">
<refsect2info>
<date>1998-09-08</date>
</refsect2info>
<title>
Notes
</title>
<para>
<command>ALTER USER</command>
is a <productname>Postgres</productname>
language extension.
</para>
<para>
Refer to <command>CREATE/DROP USER</command>
to create or remove a user account.
</para>
<para>
The IN GROUP clause is not yet implemented.
</para>
</refsect2>
</refsect1>
<refsect1 id="R1-SQL-ALTERUSER-2">
......@@ -190,34 +156,29 @@ ERROR: alterUser: user "username" does not exist
<para>
Change a user password:
<programlisting>
ALTER USER davide WITH PASSWORD hu8jmn3;
</programlisting>
<programlisting>
ALTER USER davide WITH PASSWORD 'hu8jmn3';
</programlisting>
Change a user's valid until date
<programlisting>
<programlisting>
ALTER USER manuel VALID UNTIL 'Jan 31 2030';
</programlisting>
</programlisting>
Change a user's valid until date, specifying that his
authorisation should expire at midday on 4th May 1998 using
the time zone which is one hour ahead of UTC
<programlisting>
<programlisting>
ALTER USER chris VALID UNTIL 'May 4 12:00:00 1998 +1';
</programlisting>
</programlisting>
Give a user the ability to create other users and new databases.
<programlisting>
<programlisting>
ALTER USER miriam CREATEUSER CREATEDB;
</programlisting>
Place a user in two groups
</programlisting>
<programlisting>
ALTER USER miriam IN GROUP sales, payroll;
</programlisting>
</para>
</refsect1>
......
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/Attic/commands.sgml,v 1.21 1999/12/05 20:02:42 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/Attic/commands.sgml,v 1.22 2000/01/14 22:11:32 petere Exp $
Postgres documentation
-->
......@@ -14,6 +14,7 @@ Postgres documentation
</abstract>
&abort;
&alterGroup;
&alterTable;
&alterUser;
&begin;
......@@ -24,6 +25,7 @@ Postgres documentation
&createAggregate;
&createDatabase;
&createFunction;
&createGroup;
&createIndex;
&createLanguage;
&createOperator;
......@@ -40,6 +42,7 @@ Postgres documentation
&dropAggregate;
&dropDatabase;
&dropFunction;
&dropGroup;
&dropIndex;
&dropLanguage;
&dropOperator;
......
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_group.sgml,v 1.1 2000/01/14 22:11:32 petere Exp $
Postgres documentation
-->
<refentry id="SQL-CREATEGROUP">
<refmeta>
<refentrytitle id="sql-creategroup-title">
CREATE GROUP
</refentrytitle>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>
CREATE GROUP
</refname>
<refpurpose>
Creates a new group
</refpurpose>
</refnamediv>
<refsynopsisdiv>
<refsynopsisdivinfo>
<date>2000-01-14</date>
</refsynopsisdivinfo>
<synopsis>
CREATE GROUP <replaceable class="PARAMETER">name</replaceable>
[ WITH
[ SYSID <replaceable class="PARAMETER">gid</replaceable> ]
[ USER <replaceable class="PARAMETER">username</replaceable> [, ...] ] ]
</synopsis>
<refsect2 id="R2-SQL-CREATEGROUP-1">
<refsect2info>
<date>2000-01-14</date>
</refsect2info>
<title>
Inputs
</title>
<para>
<variablelist>
<varlistentry>
<term><replaceable class="parameter">name</replaceable></term>
<listitem>
<para>
The name of the group.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">gid</replaceable></term>
<listitem>
<para>
The <literal>SYSID</literal> clause can be used to choose
the <productname>PostgreSQL</productname> group id of the new
group. It is not necessary to do so, however.
</para>
<para>
If this is not specified, the highest assigned group id plus one,
starting at 1, will be used as default.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">username</replaceable></term>
<listitem>
<para>
A list of users to include in the group. The users must already exist.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
<refsect2 id="R2-SQL-CREATEGROUP-2">
<refsect2info>
<date>2000-01-14</date>
</refsect2info>
<title>
Outputs
</title>
<para>
<variablelist>
<varlistentry>
<term><computeroutput>CREATE GROUP</computeroutput></term>
<listitem>
<para>
Message returned if the command completes successfully.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
</refsynopsisdiv>
<refsect1 id="R1-SQL-CREATEGROUP-1">
<refsect1info>
<date>2000-01-14</date>
</refsect1info>
<title>
Description
</title>
<para>
CREATE GROUP will create a new group in the database installation.
Refer to the adminstrator's guide for information about using groups
for authentication.
You must be a database superuser to use this command.
</para>
<para>
Use <xref linkend="SQL-ALTERGROUP" endterm="SQL-ALTERGROUP-title">
to change a group's membership, and <xref linkend="SQL-DROPGROUP"
endterm="SQL-DROPGROUP-title"> to remove a group.
</para>
</refsect1>
<refsect1 id="R1-SQL-CREATEGROUP-2">
<title>
Usage
</title>
<para>
Create an empty group:
<programlisting>
CREATE GROUP staff
</programlisting>
</para>
<para>
Create a group with members:
<programlisting>
CREATE GROUP marketing WITH USER jonathan, david
</programlisting>
</para>
</refsect1>
<refsect1 id="R1-SQL-CREATEGROUP-3">
<title>
Compatibility
</title>
<refsect2 id="R2-SQL-CREATEGROUP-4">
<refsect2info>
<date>2000-01-14</date>
</refsect2info>
<title>
SQL92
</title>
<para>
There is no <command>CREATE GROUP</command> statement in SQL92.
Roles are similar in concept to groups.
</para>
</refsect2>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:nil
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
sgml-parent-document:nil
sgml-default-dtd-file:"../reference.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:"/usr/lib/sgml/catalog"
sgml-local-ecat-files:nil
End:
-->
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_user.sgml,v 1.12 1999/12/04 05:03:49 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_user.sgml,v 1.13 2000/01/14 22:11:32 petere Exp $
Postgres documentation
-->
......@@ -15,7 +15,7 @@ Postgres documentation
CREATE USER
</refname>
<refpurpose>
Creates account information for a new user
Creates a new database user
</refpurpose>
</refnamediv>
<refsynopsisdiv>
......@@ -26,7 +26,7 @@ Postgres documentation
CREATE USER <replaceable class="PARAMETER">username</replaceable>
[ WITH
[ SYSID <replaceable class="PARAMETER">uid</replaceable> ]
[ PASSWORD <replaceable class="PARAMETER">password</replaceable> ] ]
[ PASSWORD '<replaceable class="PARAMETER">password</replaceable>' ] ]
[ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]
[ IN GROUP <replaceable class="PARAMETER">groupname</replaceable> [, ...] ]
[ VALID UNTIL '<replaceable class="PARAMETER">abstime</replaceable>' ]
......@@ -61,13 +61,6 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
match the <acronym>UNIX</acronym> user ids, but some people
choose to keep the numbers the same.
</para>
<para>
If you still want the
OS user id and the <filename>usesysid</filename> to match
for any given user,
use the <application>createuser</application> script provided with
the <productname>Postgres</productname> distribution.
</para>
<para>
If this is not specified, the highest assigned user id plus one
will be used as default.
......@@ -79,30 +72,11 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
<term><replaceable class="parameter">password</replaceable></term>
<listitem>
<para>
The PASSWORD clause sets the user's password within
the "<filename>pg_shadow</filename>" table. For this reason,
<filename>"pg_shadow</filename>" is no
longer accessible to the instance of
<productname>Postgres</productname> that the
<productname>Postgres</productname>
user's password is initially set to NULL.
</para>
<para>
When a
user's password in the "<filename>pg_shadow</filename>"
table is NULL, user
authentication proceeds as it historically has (HBA,
PG_PASSWORD, etc). However, if a password is set for a
user, a new authentication system supplants any other
configured for the <productname>Postgres</productname>
instance, and the password
stored in the "<filename>pg_shadow</filename>" table is used
for authentication.
For more details on how this authentication system
functions see pg_crypt(3). If the WITH PASSWORD clause is
omitted, the user's password is set to the empty
string which equates to a NULL value in the authentication
system mentioned above.
Sets the user's password. If you do not plan to use password
authentication you can omit this option, otherwise the user
won't be able to connect to a password-authenticated server.
See pg_hba.conf(5) or the administrator's guide for details on
how to set up authentication mechanisms.
</para>
</listitem>
</varlistentry>
......@@ -127,8 +101,8 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
<listitem>
<para>
These clauses determine whether a user will be permitted to
create new
users in an instance of <productname>Postgres</productname>.
create new users himself. This option will also make the user
a superuser who can override all access restrictions.
Omitting this clause will set the user's value of this
attribute to be NOCREATEUSER.
</para>
......@@ -149,15 +123,8 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
<listitem>
<para>
The VALID UNTIL clause sets an absolute time after which the
user's <productname>Postgres</productname>
login is no longer valid. Please note that
if a user does not have a password defined in the
"<filename>pg_shadow</filename>"
table, the valid until date will not be checked
during user authentication. If this clause is omitted,
a NULL value is stored in "<filename>pg_shadow</filename>"
for this attribute,
and the login will be valid for all time.
user's password is no longer valid.
If this clause is omitted the login will be valid for all time.
</para>
</listitem>
</varlistentry>
......@@ -176,9 +143,7 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
<para>
<variablelist>
<varlistentry>
<term><computeroutput>
CREATE USER
</computeroutput></term>
<term><computeroutput>CREATE USER</computeroutput></term>
<listitem>
<para>
Message returned if the command completes successfully.
......@@ -199,61 +164,38 @@ CREATE USER
</title>
<para>
CREATE USER will add a new user to an instance of
<productname>PostgreSQL</productname>.
<productname>PostgreSQL</productname>. Refer to the adminstrator's
guide for information about managing users and authentication.
You must be a database superuser to use this command.
</para>
<refsect2 id="R2-SQL-CREATEUSER-3">
<refsect2info>
<date>1998-09-21</date>
</refsect2info>
<title>
Notes
</title>
<para>
<command>CREATE USER</command> statement is a
<productname>Postgres</productname> language extension.
</para>
<para>
Use <command>DROP USER</command> or <command>ALTER USER</command>
statements to remove or modify a user account.
</para>
<para>
Refer to the <filename>pg_shadow</filename> table for further information.
</para>
<programlisting>
Table "pg_shadow"
Attribute | Type | Extra
-------------+---------+-------
usename | name |
usesysid | int4 |
usecreatedb | bool |
usetrace | bool |
usesuper | bool |
usecatupd | bool |
passwd | text |
valuntil | abstime |
</programlisting>
</refsect2>
</refsect1>
<para>
Use <xref linkend="SQL-ALTERUSER" endterm="SQL-ALTERUSER-title">
to change a user's password and privileges, and <xref linkend="SQL-DROPUSER"
endterm="SQL-DROPUSER-title"> to remove a user.
Use <command>ALTER GROUP</command> to add or remove the user from other groups.
<productname>PostgreSQL</productname>
comes with a script <xref linkend="APP-CREATEUSER"
endterm="APP-CREATEUSER-title">
which has the same functionality as this command (in fact, it calls this command)
but can be run from the command shell.
</para>
</refsect1>
<refsect1 id="R1-SQL-CREATEUSER-2">
<title>
Usage
</title>
<para>
Create a user with no password:
<programlisting>
<programlisting>
CREATE USER jonathan
</programlisting>
</programlisting>
</para>
<para>
Create a user with a password:
<programlisting>
CREATE USER davide WITH PASSWORD "jw8s0F4"
</programlisting>
<programlisting>
CREATE USER davide WITH PASSWORD 'jw8s0F4'
</programlisting>
</para>
<para>
......@@ -261,17 +203,16 @@ CREATE USER davide WITH PASSWORD "jw8s0F4"
Note that after one second has ticked in 2002, the account is not
valid:
<programlisting>
CREATE USER miriam WITH PASSWORD "jw8s0F4" VALID UNTIL 'Jan 1 2002'
</programlisting>
<programlisting>
CREATE USER miriam WITH PASSWORD 'jw8s0F4' VALID UNTIL 'Jan 1 2002'
</programlisting>
</para>
<para>
Create an account where the user can create databases:
<programlisting>
CREATE USER manuel WITH PASSWORD "jw8s0F4" CREATEDB
</programlisting>
<programlisting>
CREATE USER manuel WITH PASSWORD 'jw8s0F4' CREATEDB
</programlisting>
</para>
</refsect1>
......
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_group.sgml,v 1.1 2000/01/14 22:11:32 petere Exp $
Postgres documentation
-->
<refentry id="SQL-DROPGROUP">
<refmeta>
<refentrytitle id="SQL-DROPGROUP-TITLE">
DROP GROUP
</refentrytitle>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>
DROP GROUP
</refname>
<refpurpose>
Removes a group
</refpurpose>
</refnamediv>
<refsynopsisdiv>
<refsynopsisdivinfo>
<date>2000-01-14</date>
</refsynopsisdivinfo>
<synopsis>
DROP GROUP <replaceable class="PARAMETER">name</replaceable>
</synopsis>
<refsect2 id="R2-SQL-DROPGROUP-1">
<refsect2info>
<date>2000-01-14</date>
</refsect2info>
<title>
Inputs
</title>
<para>
<variablelist>
<varlistentry>
<term><replaceable class="PARAMETER">name</replaceable></term>
<listitem>
<para>
The name of an existing group.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
<refsect2 id="R2-SQL-DROPGROUP-2">
<refsect2info>
<date>2000-01-14</date>
</refsect2info>
<title>
Outputs
</title>
<para>
<variablelist>
<varlistentry>
<term><computeroutput>DROP GROUP</computeroutput></term>
<listitem>
<para>
The message returned if the group is successfully deleted.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
</refsynopsisdiv>
<refsect1 id="R1-SQL-DROPGROUP-1">
<refsect1info>
<date>2000-01-14</date>
</refsect1info>
<title>
Description
</title>
<para>
<command>DROP GROUP</command> removes the specified group from the database.
The users in the group are not deleted.
</para>
<para>
Use <xref linkend="SQL-CREATEGROUP" endterm="SQL-CREATEGROUP-title">
to add new groups, and <xref linkend="SQL-ALTERGROUP"
endterm="SQL-ALTERGROUP-title"> to change a group's membership.
</para>
</refsect1>
<refsect1 id="R1-SQL-DROPGROUP-2">
<title>
Usage
</title>
<para>
To drop a group:
<programlisting>
DROP GROUP staff;
</programlisting>
</para>
</refsect1>
<refsect1 id="R1-SQL-DROPGROUP-3">
<title>
Compatibility
</title>
<refsect2 id="R2-SQL-DROPGROUP-4">
<refsect2info>
<date>2000-01-14</date>
</refsect2info>
<title>
SQL92
</title>
<para>
There is no <command>DROP GROUP</command> in <acronym>SQL92</acronym>.
</para>
</refsect2>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:nil
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
sgml-parent-document:nil
sgml-default-dtd-file:"../reference.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:"/usr/lib/sgml/catalog"
sgml-local-ecat-files:nil
End:
-->
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_user.sgml,v 1.9 1999/12/07 22:41:41 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_user.sgml,v 1.10 2000/01/14 22:11:32 petere Exp $
Postgres documentation
-->
......@@ -15,7 +15,7 @@ Postgres documentation
DROP USER
</refname>
<refpurpose>
Removes an user account information
Removes a user
</refpurpose>
</refnamediv>
<refsynopsisdiv>
......@@ -58,18 +58,17 @@ DROP USER <replaceable class="PARAMETER">name</replaceable>
<para>
<variablelist>
<varlistentry>
<term><computeroutput>
DROP
</computeroutput></term>
<term><computeroutput>DROP USER</computeroutput></term>
<listitem>
<para>
The message returned if the user is successfully deleted.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>
ERROR: removeUser: user "<replaceable class="parameter">name</replaceable>" does not exist.
ERROR: DROP USER: user "<replaceable class="parameter">name</replaceable>" does not exist
</computeroutput></term>
<listitem>
<para>
......@@ -77,6 +76,18 @@ ERROR: removeUser: user "<replaceable class="parameter">name</replaceable>" does
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>
DROP USER: user "<replaceable class="parameter">name</replaceable>" owns database "<replaceable class="parameter">name</replaceable>", cannot be removed
</computeroutput></term>
<listitem>
<para>
You must drop the database first or change its ownership.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
......@@ -90,30 +101,20 @@ ERROR: removeUser: user "<replaceable class="parameter">name</replaceable>" does
Description
</title>
<para>
<command>DROP USER</command> removes the specified
user from the database,
along with any databases owned by the user. It
does not remove tables, views, or triggers owned by the
named user in databases not owned by the user.
<command>DROP USER</command> removes the specified user from the database.
It does not remove tables, views, or other objects owned by the user. If the
user owns any database you get an error.
</para>
<refsect2 id="R2-SQL-DROPUSER-3">
<refsect2info>
<date>1998-09-22</date>
</refsect2info>
<title>
Notes
</title>
<para>
<command>DROP USER</command> is a <productname>Postgres</productname>
language extension.
</para>
<para>
Refer to <command>CREATE USER</command> and
<command>ALTER USER</command> for information on
how to create or modify user accounts.
</para>
</refsect2>
<para>
Use <xref linkend="SQL-CREATEUSER" endterm="SQL-CREATEUSER-title">
to add new users, and <xref linkend="SQL-ALTERUSER"
endterm="SQL-ALTERUSER-title"> to change a user's properties.
<productname>PostgreSQL</productname>
comes with a script <xref linkend="APP-DROPUSER"
endterm="APP-DROPUSER-title">
which has the same functionality as this command (in fact, it calls this command)
but can be run from the command shell.
</para>
</refsect1>
<refsect1 id="R1-SQL-DROPUSER-2">
......@@ -122,9 +123,9 @@ ERROR: removeUser: user "<replaceable class="parameter">name</replaceable>" does
</title>
<para>
To drop a user account:
<programlisting>
DROP USER Jonathan;
</programlisting>
<programlisting>
DROP USER jonathan;
</programlisting>
</para>
</refsect1>
......
......@@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.94 1999/12/16 22:19:41 wieck Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.95 2000/01/14 22:11:33 petere Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -97,7 +97,11 @@ CopySendData(void *databuf, int datasize, FILE *fp)
fe_eof = true;
}
else
{
fwrite(databuf, datasize, 1, fp);
if (ferror(fp))
elog(ERROR, "CopySendData: %s", strerror(errno));
}
}
static void
......@@ -219,7 +223,7 @@ CopyDonePeek(FILE *fp, int c, int pickup)
void
DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
char *filename, char *delim, char *null_print, int fileumask)
char *filename, char *delim, char *null_print)
{
/*----------------------------------------------------------------------------
Either unload or reload contents of class <relname>, depending on <from>.
......@@ -235,11 +239,6 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
If in the text format, delimit columns with delimiter <delim> and print
NULL values as <null_print>.
<fileumask> is the umask(2) setting to use while creating an output file.
This should usually be more liberal than the backend's normal 077 umask,
but not always (in particular, "pg_pwd" should be written with 077!).
Up through version 6.5, <fileumask> was always 000, which was foolhardy.
When loading in the text format from an input stream (as opposed to
a file), recognize a "." on a line by itself as EOF. Also recognize
a stream EOF. When unloading in the text format to an output stream,
......@@ -272,12 +271,11 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
result = pg_aclcheck(relname, UserName, required_access);
if (result != ACLCHECK_OK)
elog(ERROR, "%s: %s", relname, aclcheck_error_strings[result]);
else if (!pipe && !superuser())
if (!pipe && !superuser())
elog(ERROR, "You must have Postgres superuser privilege to do a COPY "
"directly to or from a file. Anyone can COPY to stdout or "
"from stdin. Psql's \\copy command also works for anyone.");
else
{
if (from)
{ /* copy from file to database */
if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
......@@ -324,7 +322,7 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
{
mode_t oumask; /* Pre-existing umask value */
oumask = umask((mode_t) fileumask);
oumask = umask((mode_t) 022);
#ifndef __CYGWIN32__
fp = AllocateFile(filename, "w");
#else
......@@ -350,7 +348,6 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
if (IsUnderPostmaster)
pq_endcopyout(false);
}
}
/*
* Close the relation. If reading, we can release the AccessShareLock
......
......@@ -5,7 +5,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: user.c,v 1.47 1999/12/21 22:39:01 wieck Exp $
* $Id: user.c,v 1.48 2000/01/14 22:11:33 petere Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -31,6 +31,7 @@
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
static void CheckPgUserAclNotNull(void);
......@@ -38,30 +39,26 @@ static void CheckPgUserAclNotNull(void);
#define SQL_LENGTH 512
/*---------------------------------------------------------------------
* update_pg_pwd
* write_password_file / update_pg_pwd
*
* copy the modified contents of pg_shadow to a file used by the postmaster
* for user authentication. The file is stored as $PGDATA/pg_pwd.
*
* NB: caller is responsible for ensuring that only one backend can
* execute this routine at a time. Acquiring AccessExclusiveLock on
* pg_shadow is the standard way to do that.
* This function set is both a trigger function for direct updates to pg_shadow
* as well as being called directly from create/alter/drop user.
*---------------------------------------------------------------------
*/
HeapTuple
update_pg_pwd(void)
static void
write_password_file(Relation rel)
{
char *filename,
*tempname;
int bufsize;
/*
* This is a trigger, so clean out the information provided by
* the trigger manager.
*/
CurrentTriggerData = NULL;
FILE *fp;
mode_t oumask;
HeapScanDesc scan;
HeapTuple tuple;
TupleDesc dsc = RelationGetDescr(rel);
/*
* Create a temporary filename to be renamed later. This prevents the
......@@ -71,86 +68,134 @@ update_pg_pwd(void)
filename = crypt_getpwdfilename();
bufsize = strlen(filename) + 12;
tempname = (char *) palloc(bufsize);
snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
oumask = umask((mode_t) 077);
fp = AllocateFile(tempname, "w");
umask(oumask);
if (fp == NULL)
elog(ERROR, "%s: %s", tempname, strerror(errno));
/* read table */
scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL);
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
Datum datum_n, datum_p, datum_v;
bool null_n, null_p, null_v;
datum_n = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &null_n);
if (null_n)
continue; /* don't allow empty users */
datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p);
/* It could be argued that people having a null password
shouldn't be allowed to connect, because they need
to have a password set up first. If you think assuming
an empty password in that case is better, erase the following line. */
if (null_p)
continue;
datum_v = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &null_v);
/* These fake entries are not really necessary. To remove them, the parser
in backend/libpq/crypt.c would need to be adjusted. Initdb might also
need adjustments. */
fprintf(fp,
"%s"
CRYPT_PWD_FILE_SEPSTR
"0"
CRYPT_PWD_FILE_SEPSTR
"x"
CRYPT_PWD_FILE_SEPSTR
"x"
CRYPT_PWD_FILE_SEPSTR
"x"
CRYPT_PWD_FILE_SEPSTR
"x"
CRYPT_PWD_FILE_SEPSTR
"%s"
CRYPT_PWD_FILE_SEPSTR
"%s\n",
nameout(DatumGetName(datum_n)),
null_p ? "" : textout((text*)datum_p),
null_v ? "\\N" : nabstimeout((AbsoluteTime)datum_v) /* this is how the parser wants it */
);
if (ferror(fp))
elog(ERROR, "%s: %s", tempname, strerror(errno));
fflush(fp);
}
heap_endscan(scan);
FreeFile(fp);
/*
* Copy the contents of pg_shadow to the pg_pwd ASCII file using the
* SEPCHAR character as the delimiter between fields. Make sure the
* file is created with mode 600 (umask 077).
*/
DoCopy(ShadowRelationName, /* relname */
false, /* binary */
false, /* oids */
false, /* from */
false, /* pipe */
tempname, /* filename */
CRYPT_PWD_FILE_SEPSTR, /* delim */
"", /* nulls */
0077); /* fileumask */
/*
* And rename the temp file to its final name, deleting the old pg_pwd.
*/
rename(tempname, filename);
/*
* And rename the temp file to its final name, deleting the old pg_pwd.
*/
rename(tempname, filename);
/*
/*
* Create a flag file the postmaster will detect the next time it
* tries to authenticate a user. The postmaster will know to reload
* the pg_pwd file contents.
*/
filename = crypt_getpwdreloadfilename();
creat(filename, S_IRUSR | S_IWUSR);
if (creat(filename, S_IRUSR | S_IWUSR) == -1)
elog(ERROR, "%s: %s", filename, strerror(errno));
pfree((void *) tempname);
}
/* This is the wrapper for triggers. */
HeapTuple
update_pg_pwd(void)
{
Relation rel = heap_openr(ShadowRelationName, AccessExclusiveLock);
write_password_file(rel);
heap_close(rel, AccessExclusiveLock);
return NULL;
/*
* This is a trigger, so clean out the information provided by
* the trigger manager.
*/
CurrentTriggerData = NULL;
return NULL;
}
/*---------------------------------------------------------------------
* DefineUser
*
* Add the user to the pg_shadow relation, and if specified make sure the
* user is specified in the desired groups of defined in pg_group.
*---------------------------------------------------------------------
/*
* CREATE USER
*/
void
DefineUser(CreateUserStmt *stmt, CommandDest dest)
CreateUser(CreateUserStmt *stmt)
{
char *pg_shadow,
sql[SQL_LENGTH];
Relation pg_shadow_rel;
TupleDesc pg_shadow_dsc;
HeapScanDesc scan;
HeapTuple tuple;
Datum new_record[Natts_pg_shadow];
char new_record_nulls[Natts_pg_shadow];
bool user_exists = false,
sysid_exists = false,
inblock,
havesysid,
havepassword,
havevaluntil;
havesysid;
int max_id = -1;
List *item;
havesysid = stmt->sysid >= 0;
havepassword = stmt->password && stmt->password[0];
havevaluntil = stmt->validUntil && stmt->validUntil[0];
havesysid = stmt->sysid > 0;
if (havepassword)
/* Check some permissions first */
if (stmt->password)
CheckPgUserAclNotNull();
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
/*
* Make sure the user attempting to create a user can insert into the
* pg_shadow relation.
*/
pg_shadow = GetPgUserName();
if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR | ACL_AP) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
elog(ERROR, "DefineUser: user \"%s\" does not have SELECT and INSERT privilege for \"%s\"",
pg_shadow, ShadowRelationName);
return;
}
if (!superuser())
elog(ERROR, "CREATE USER: permission denied");
/* The reason for the following is this:
* If you start a transaction block, create a user, then roll back the
* transaction, the pg_pwd won't get rolled back due to a bug in the
* Unix file system ( :}). Hence this is in the interest of security.
*/
if (IsTransactionBlock())
elog(ERROR, "CREATE USER: may not be called in a transaction block");
/*
* Scan the pg_shadow relation to be certain the user or id doesn't already
......@@ -184,49 +229,64 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
if (user_exists || sysid_exists)
{
heap_close(pg_shadow_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
if (user_exists)
elog(ERROR, "DefineUser: user name \"%s\" already exists", stmt->user);
elog(ERROR, "CREATE USER: user name \"%s\" already exists", stmt->user);
else
elog(ERROR, "DefineUser: sysid %d is already assigned", stmt->sysid);
elog(ERROR, "CREATE USER: sysid %d is already assigned", stmt->sysid);
return;
}
/*
* Build the insert statement to be executed.
*
* XXX Ugly as this code is, it still fails to cope with ' or \ in any of
* the provided strings.
*
* XXX This routine would be *lots* better if it inserted the new
* tuple with formtuple/heap_insert. For one thing, all of the
* transaction-block gamesmanship could be eliminated, because
* it's only there to make the world safe for a recursive call
* to pg_exec_query_dest().
*/
snprintf(sql, SQL_LENGTH,
"insert into %s (usename,usesysid,usecreatedb,usetrace,"
"usesuper,usecatupd,passwd,valuntil) "
"values('%s',%d,'%c','f','%c','%c',%s%s%s,%s%s%s)",
ShadowRelationName,
stmt->user,
havesysid ? stmt->sysid : max_id + 1,
(stmt->createdb && *stmt->createdb) ? 't' : 'f',
(stmt->createuser && *stmt->createuser) ? 't' : 'f',
((stmt->createdb && *stmt->createdb) ||
(stmt->createuser && *stmt->createuser)) ? 't' : 'f',
havepassword ? "'" : "",
havepassword ? stmt->password : "NULL",
havepassword ? "'" : "",
havevaluntil ? "'" : "",
havevaluntil ? stmt->validUntil : "NULL",
havevaluntil ? "'" : "");
/*
* Build a tuple to insert
*/
new_record[Anum_pg_shadow_usename-1] = PointerGetDatum(namein(stmt->user)); /* this truncated properly */
new_record[Anum_pg_shadow_usesysid-1] = Int32GetDatum(havesysid ? stmt->sysid : max_id + 1);
/*
* XXX If insert fails, say because a bogus valuntil date is given,
* need to catch the resulting error and undo our transaction.
*/
pg_exec_query_dest(sql, dest, false);
AssertState(BoolIsValid(stmt->createdb));
new_record[Anum_pg_shadow_usecreatedb-1] = (Datum)(stmt->createdb);
new_record[Anum_pg_shadow_usetrace-1] = (Datum)(false);
AssertState(BoolIsValid(stmt->createuser));
new_record[Anum_pg_shadow_usesuper-1] = (Datum)(stmt->createuser);
/* superuser gets catupd right by default */
new_record[Anum_pg_shadow_usecatupd-1] = (Datum)(stmt->createuser);
if (stmt->password)
new_record[Anum_pg_shadow_passwd-1] = PointerGetDatum(textin(stmt->password));
if (stmt->validUntil)
new_record[Anum_pg_shadow_valuntil-1] = PointerGetDatum(nabstimein(stmt->validUntil));
new_record_nulls[Anum_pg_shadow_usename-1] = ' ';
new_record_nulls[Anum_pg_shadow_usesysid-1] = ' ';
new_record_nulls[Anum_pg_shadow_usecreatedb-1] = ' ';
new_record_nulls[Anum_pg_shadow_usetrace-1] = ' ';
new_record_nulls[Anum_pg_shadow_usesuper-1] = ' ';
new_record_nulls[Anum_pg_shadow_usecatupd-1] = ' ';
new_record_nulls[Anum_pg_shadow_passwd-1] = stmt->password ? ' ' : 'n';
new_record_nulls[Anum_pg_shadow_valuntil-1] = stmt->validUntil ? ' ' : 'n';
tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
Assert(tuple);
/*
* Insert a new record in the pg_shadow table
*/
if (heap_insert(pg_shadow_rel, tuple) == InvalidOid)
elog(ERROR, "CREATE USER: heap_insert failed");
/*
* Update indexes
*/
if (RelationGetForm(pg_shadow_rel)->relhasindex) {
Relation idescs[Num_pg_shadow_indices];
CatalogOpenIndices(Num_pg_shadow_indices,
Name_pg_shadow_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_shadow_indices, pg_shadow_rel,
tuple);
CatalogCloseIndices(Num_pg_shadow_indices, idescs);
}
/*
* Add the user to the groups specified. We'll just call the below
......@@ -236,59 +296,49 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
{
AlterGroupStmt ags;
ags.name = strVal(lfirst(item));
ags.name = strVal(lfirst(item)); /* the group name to add this in */
ags.action = +1;
ags.listUsers = lcons((void*)makeString(stmt->user), NIL);
AlterGroup(&ags, dest);
ags.listUsers = lcons((void*)makeInteger(havesysid ? stmt->sysid : max_id + 1), NIL);
AlterGroup(&ags, "CREATE USER");
}
/*
* Write the updated pg_shadow data to the flat password file.
* Because we are still holding AccessExclusiveLock on pg_shadow,
* we can be sure no other backend will try to write the flat
* file at the same time.
*/
update_pg_pwd();
write_password_file(pg_shadow_rel);
/*
* Now we can clean up.
*/
heap_close(pg_shadow_rel, AccessExclusiveLock);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
}
/*
* ALTER USER
*/
extern void
AlterUser(AlterUserStmt *stmt, CommandDest dest)
AlterUser(AlterUserStmt *stmt)
{
char *pg_shadow,
sql[SQL_LENGTH];
Datum new_record[Natts_pg_shadow];
char new_record_nulls[Natts_pg_shadow];
Relation pg_shadow_rel;
TupleDesc pg_shadow_dsc;
HeapTuple tuple;
bool inblock;
bool comma = false;
HeapTuple tuple, new_tuple;
bool null;
if (stmt->password)
CheckPgUserAclNotNull();
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
/*
* Make sure the user attempting to create a user can insert into the
* pg_shadow relation.
*/
pg_shadow = GetPgUserName();
if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
elog(ERROR, "AlterUser: user \"%s\" does not have SELECT and UPDATE privilege for \"%s\"",
pg_shadow, ShadowRelationName);
return;
}
/* must be superuser or just want to change your own password */
if (!superuser() &&
!(stmt->createdb==0 && stmt->createuser==0 && !stmt->validUntil
&& stmt->password && strcmp(GetPgUserName(), stmt->user)==0))
elog(ERROR, "ALTER USER: permission denied");
/* see comments in create user */
if (IsTransactionBlock())
elog(ERROR, "ALTER USER: may not be called in a transaction block");
/*
* Scan the pg_shadow relation to be certain the user exists.
......@@ -304,142 +354,135 @@ AlterUser(AlterUserStmt *stmt, CommandDest dest)
if (!HeapTupleIsValid(tuple))
{
heap_close(pg_shadow_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "AlterUser: user \"%s\" does not exist", stmt->user);
elog(ERROR, "ALTER USER: user \"%s\" does not exist", stmt->user);
}
/* look for duplicate sysid */
tuple = SearchSysCacheTuple(SHADOWSYSID,
Int32GetDatum(stmt->sysid),
0, 0, 0);
if (HeapTupleIsValid(tuple))
{
Datum datum;
bool null;
/*
* Build a tuple to update, perusing the information just obtained
*/
new_record[Anum_pg_shadow_usename-1] = PointerGetDatum(namein(stmt->user));
new_record_nulls[Anum_pg_shadow_usename-1] = ' ';
datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &null);
if (datum && !null && strcmp((char *) datum, stmt->user) != 0)
{
heap_close(pg_shadow_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "AlterUser: sysid %d is already assigned", stmt->sysid);
}
}
/* sysid - leave as is */
new_record[Anum_pg_shadow_usesysid-1] = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_usesysid-1] = null ? 'n' : ' ';
/* createdb */
if (stmt->createdb == 0)
{
/* don't change */
new_record[Anum_pg_shadow_usecreatedb-1] = heap_getattr(tuple, Anum_pg_shadow_usecreatedb, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_usecreatedb-1] = null ? 'n' : ' ';
}
else
{
new_record[Anum_pg_shadow_usecreatedb-1] = (Datum)(stmt->createdb > 0 ? true : false);
new_record_nulls[Anum_pg_shadow_usecreatedb-1] = ' ';
}
/*
* Create the update statement to modify the user.
*
* XXX see diatribe in preceding routine. This code is just as bogus.
*/
snprintf(sql, SQL_LENGTH, "update %s set ", ShadowRelationName);
/* trace - leave as is */
new_record[Anum_pg_shadow_usetrace-1] = heap_getattr(tuple, Anum_pg_shadow_usetrace, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_usetrace-1] = null ? 'n' : ' ';
if (stmt->password)
/* createuser (superuser) */
if (stmt->createuser == 0)
{
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
"passwd = '%s'", stmt->password);
comma = true;
/* don't change */
new_record[Anum_pg_shadow_usesuper-1] = heap_getattr(tuple, Anum_pg_shadow_usesuper, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_usesuper-1] = null ? 'n' : ' ';
}
if (stmt->sysid>=0)
else
{
if (comma)
strcat(sql, ", ");
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
"usesysid = %d", stmt->sysid);
comma = true;
new_record[Anum_pg_shadow_usesuper-1] = (Datum)(stmt->createuser > 0 ? true : false);
new_record_nulls[Anum_pg_shadow_usesuper-1] = ' ';
}
if (stmt->createdb)
/* catupd - set to false if someone's superuser priv is being yanked */
if (stmt->createuser < 0)
{
if (comma)
strcat(sql, ", ");
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
"usecreatedb='%c'",
*stmt->createdb ? 't' : 'f');
comma = true;
new_record[Anum_pg_shadow_usecatupd-1] = (Datum)(false);
new_record_nulls[Anum_pg_shadow_usecatupd-1] = ' ';
}
else
{
/* leave alone */
new_record[Anum_pg_shadow_usecatupd-1] = heap_getattr(tuple, Anum_pg_shadow_usecatupd, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_usecatupd-1] = null ? 'n' : ' ';
}
if (stmt->createuser)
/* password */
if (stmt->password)
{
if (comma)
strcat(sql, ", ");
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
"usesuper='%c'",
*stmt->createuser ? 't' : 'f');
comma = true;
new_record[Anum_pg_shadow_passwd-1] = PointerGetDatum(textin(stmt->password));
new_record_nulls[Anum_pg_shadow_passwd-1] = ' ';
}
else
{
/* leave as is */
new_record[Anum_pg_shadow_passwd-1] = heap_getattr(tuple, Anum_pg_shadow_passwd, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_passwd-1] = null ? 'n' : ' ';
}
if (stmt->validUntil)
/* valid until */
if (stmt->validUntil)
{
new_record[Anum_pg_shadow_valuntil-1] = PointerGetDatum(nabstimein(stmt->validUntil));
new_record_nulls[Anum_pg_shadow_valuntil-1] = ' ';
}
else
{
if (comma)
strcat(sql, ", ");
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
"valuntil='%s'",
stmt->validUntil);
/* leave as is */
new_record[Anum_pg_shadow_valuntil-1] = heap_getattr(tuple, Anum_pg_shadow_valuntil, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_valuntil-1] = null ? 'n' : ' ';
}
snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
" where usename = '%s'",
stmt->user);
new_tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
Assert(new_tuple);
/* XXX check return value of this? */
heap_update(pg_shadow_rel, &tuple->t_self, new_tuple, NULL);
pg_exec_query_dest(sql, dest, false);
/*
* Add stuff here for groups?
*/
if (stmt->groupElts)
elog(NOTICE, "IN GROUP is not implemented for ALTER USER.");
/* Update indexes */
if (RelationGetForm(pg_shadow_rel)->relhasindex)
{
Relation idescs[Num_pg_shadow_indices];
CatalogOpenIndices(Num_pg_shadow_indices,
Name_pg_shadow_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_shadow_indices, pg_shadow_rel,
tuple);
CatalogCloseIndices(Num_pg_shadow_indices, idescs);
}
/*
* Write the updated pg_shadow data to the flat password file.
* Because we are still holding AccessExclusiveLock on pg_shadow,
* we can be sure no other backend will try to write the flat
* file at the same time.
*/
update_pg_pwd();
write_password_file(pg_shadow_rel);
/*
* Now we can clean up.
*/
heap_close(pg_shadow_rel, AccessExclusiveLock);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
}
extern void
RemoveUser(char *user, CommandDest dest)
/*
* DROP USER
*/
void
DropUser(DropUserStmt *stmt)
{
char *pg_shadow;
Relation pg_shadow_rel,
pg_rel;
TupleDesc pg_dsc;
HeapScanDesc scan;
HeapTuple tuple;
Datum datum;
char sql[SQL_LENGTH];
bool n,
inblock;
int32 usesysid;
int ndbase = 0;
char **dbase = NULL;
Relation pg_shadow_rel;
TupleDesc pg_shadow_dsc;
List *item;
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
if (!superuser())
elog(ERROR, "DROP USER: permission denied");
/*
* Make sure the user attempting to create a user can delete from the
* pg_shadow relation.
*/
pg_shadow = GetPgUserName();
if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
elog(ERROR, "RemoveUser: user \"%s\" does not have SELECT and DELETE privilege for \"%s\"",
pg_shadow, ShadowRelationName);
}
if (IsTransactionBlock())
elog(ERROR, "DROP USER: may not be called in a transaction block");
/*
* Scan the pg_shadow relation to find the usesysid of the user to be
......@@ -447,100 +490,109 @@ RemoveUser(char *user, CommandDest dest)
* our update of the flat password file.
*/
pg_shadow_rel = heap_openr(ShadowRelationName, AccessExclusiveLock);
pg_dsc = RelationGetDescr(pg_shadow_rel);
pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
tuple = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(user),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
heap_close(pg_shadow_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "RemoveUser: user \"%s\" does not exist", user);
}
foreach(item, stmt->users)
{
HeapTuple tuple,
tmp_tuple;
Relation pg_rel;
TupleDesc pg_dsc;
ScanKeyData scankey;
HeapScanDesc scan;
Datum datum;
bool null;
int32 usesysid;
const char *user = strVal(lfirst(item));
tuple = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(user),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
heap_close(pg_shadow_rel, AccessExclusiveLock);
elog(ERROR, "DROP USER: user \"%s\" does not exist%s", user,
(length(stmt->users) > 1) ? " (no users removed)" : "");
}
usesysid = (int32) heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_dsc, &n);
usesysid = DatumGetInt32(heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null));
/*
* Perform a scan of the pg_database relation to find the databases
* owned by usesysid. Then drop them.
*/
pg_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
pg_dsc = RelationGetDescr(pg_rel);
/*-------------------
* Check if user still owns a database. If so, error out.
*
* (It used to be that this function would drop the database automatically.
* This is not only very dangerous for people that don't read the manual,
* it doesn't seem to be the behaviour one would expect either.)
* -- petere 2000/01/14)
*-------------------*/
pg_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
pg_dsc = RelationGetDescr(pg_rel);
scan = heap_beginscan(pg_rel, false, SnapshotNow, 0, NULL);
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
datum = heap_getattr(tuple, Anum_pg_database_datdba, pg_dsc, &n);
if ((int) datum == usesysid)
{
datum = heap_getattr(tuple, Anum_pg_database_datname, pg_dsc, &n);
if (memcmp((void *) datum, "template1", 9) != 0)
{
dbase =
(char **) repalloc((void *) dbase, sizeof(char *) * (ndbase + 1));
dbase[ndbase] = (char *) palloc(NAMEDATALEN + 1);
memcpy((void *) dbase[ndbase], (void *) datum, NAMEDATALEN);
dbase[ndbase++][NAMEDATALEN] = '\0';
}
}
}
heap_endscan(scan);
heap_close(pg_rel, AccessExclusiveLock);
ScanKeyEntryInitialize(&scankey, 0x0, Anum_pg_database_datdba, F_INT4EQ,
Int32GetDatum(usesysid));
while (ndbase--)
{
elog(NOTICE, "Dropping database %s", dbase[ndbase]);
snprintf(sql, SQL_LENGTH, "DROP DATABASE %s", dbase[ndbase]);
pfree((void *) dbase[ndbase]);
pg_exec_query_dest(sql, dest, false);
}
if (dbase)
pfree((void *) dbase);
scan = heap_beginscan(pg_rel, false, SnapshotNow, 1, &scankey);
/*
* Since pg_shadow is global over all databases, one of two things
* must be done to insure complete consistency. First, pg_shadow
* could be made non-global. This would elminate the code above for
* deleting database and would require the addition of code to delete
* tables, views, etc owned by the user.
*
* The second option would be to create a means of deleting tables, view,
* etc. owned by the user from other databases. pg_shadow is global
* and so this must be done at some point.
*
* Let us not forget that the user should be removed from the pg_groups
* also.
*
* Todd A. Brandys 11/18/1997
*
*/
if (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
{
datum = heap_getattr(tmp_tuple, Anum_pg_database_datname, pg_dsc, &null);
heap_close(pg_shadow_rel, AccessExclusiveLock);
elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
user, nameout(DatumGetName(datum)),
(length(stmt->users) > 1) ? " (no users removed)" : ""
);
}
heap_endscan(scan);
heap_close(pg_rel, AccessExclusiveLock);
/*
* Remove the user from the pg_shadow table
*/
snprintf(sql, SQL_LENGTH,
"delete from %s where usename = '%s'", ShadowRelationName, user);
pg_exec_query_dest(sql, dest, false);
/*
* Somehow we'd have to check for tables, views, etc. owned by the user
* as well, but those could be spread out over all sorts of databases
* which we don't have access to (easily).
*/
/*
* Write the updated pg_shadow data to the flat password file.
* Because we are still holding AccessExclusiveLock on pg_shadow,
* we can be sure no other backend will try to write the flat
* file at the same time.
*/
update_pg_pwd();
/*
* Remove the user from the pg_shadow table
*/
heap_delete(pg_shadow_rel, &tuple->t_self, NULL);
/*
* Remove user from groups
*
* try calling alter group drop user for every group
*/
pg_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
pg_dsc = RelationGetDescr(pg_rel);
scan = heap_beginscan(pg_rel, false, SnapshotNow, 0, NULL);
while (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
{
AlterGroupStmt ags;
datum = heap_getattr(tmp_tuple, Anum_pg_group_groname, pg_dsc, &null);
ags.name = nameout(DatumGetName(datum)); /* the group name from which to try to drop the user */
ags.action = -1;
ags.listUsers = lcons((void*)makeInteger(usesysid), NIL);
AlterGroup(&ags, "DROP USER");
}
heap_endscan(scan);
heap_close(pg_rel, AccessExclusiveLock);
}
/*
* Now we can clean up.
* Write the updated pg_shadow data to the flat password file.
*/
heap_close(pg_shadow_rel, AccessExclusiveLock);
write_password_file(pg_shadow_rel);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
/*
* Now we can clean up.
*/
heap_close(pg_shadow_rel, AccessExclusiveLock);
}
/*
* CheckPgUserAclNotNull
*
......@@ -556,51 +608,56 @@ CheckPgUserAclNotNull()
0, 0, 0);
if (!HeapTupleIsValid(htup))
{
elog(ERROR, "IsPgUserAclNull: class \"%s\" not found",
/* BIG problem */
elog(ERROR, "IsPgUserAclNull: \"%s\" not found",
ShadowRelationName);
}
if (heap_attisnull(htup, Anum_pg_class_relacl))
{
elog(NOTICE, "To use passwords, you have to revoke permissions on pg_shadow");
elog(NOTICE, "so normal users can not read the passwords.");
elog(ERROR, "Try 'REVOKE ALL ON pg_shadow FROM PUBLIC'");
elog(ERROR,
"To use passwords, you have to revoke permissions on %s "
"so normal users cannot read the passwords. "
"Try 'REVOKE ALL ON \"%s\" FROM PUBLIC'.",
ShadowRelationName, ShadowRelationName);
}
return;
}
/*** GROUP THINGS ***/
/*
* CREATE GROUP
*/
void
CreateGroup(CreateGroupStmt *stmt, CommandDest dest)
CreateGroup(CreateGroupStmt *stmt)
{
Relation pg_group_rel;
HeapScanDesc scan;
HeapTuple tuple;
TupleDesc pg_group_dsc;
bool inblock;
bool group_exists = false,
sysid_exists = false;
int max_id = -1;
int max_id = 0;
Datum new_record[Natts_pg_group];
char new_record_nulls[Natts_pg_group];
List *item, *newlist=NULL;
ArrayType *userarray;
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
/*
* Make sure the user can do this.
*/
if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_AP) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
elog(ERROR, "CreateGroup: Permission denied.");
}
if (!superuser())
elog(ERROR, "CREATE GROUP: permission denied");
/*
* There is not real reason for this, but it makes it consistent
* with create user, and it seems like a good idea anyway.
*/
if (IsTransactionBlock())
elog(ERROR, "CREATE GROUP: may not be called in a transaction block");
pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
pg_group_dsc = RelationGetDescr(pg_group_rel);
......@@ -628,11 +685,10 @@ CreateGroup(CreateGroupStmt *stmt, CommandDest dest)
if (group_exists || sysid_exists)
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
if (group_exists)
elog(ERROR, "CreateGroup: Group name \"%s\" already exists.", stmt->name);
elog(ERROR, "CREATE GROUP: group name \"%s\" already exists", stmt->name);
else
elog(ERROR, "CreateGroup: Group sysid %d is already assigned.", stmt->sysid);
elog(ERROR, "CREATE GROUP: group sysid %d is already assigned", stmt->sysid);
}
/*
......@@ -650,8 +706,7 @@ CreateGroup(CreateGroupStmt *stmt, CommandDest dest)
if (!HeapTupleIsValid(tuple))
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "CreateGroup: User \"%s\" does not exist.", groupuser);
elog(ERROR, "CREATE GROUP: user \"%s\" does not exist", groupuser);
}
v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
......@@ -716,33 +771,34 @@ CreateGroup(CreateGroupStmt *stmt, CommandDest dest)
CatalogCloseIndices(Num_pg_group_indices, idescs);
}
heap_close(pg_group_rel, NoLock);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
heap_close(pg_group_rel, AccessExclusiveLock);
}
/*
* ALTER GROUP
*/
void
AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
AlterGroup(AlterGroupStmt *stmt, const char * tag)
{
Relation pg_group_rel;
TupleDesc pg_group_dsc;
bool inblock;
HeapTuple group_tuple;
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
/*
/*
* Make sure the user can do this.
*/
if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_WR) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
elog(ERROR, "AlterGroup: Permission denied.");
}
if (!superuser())
elog(ERROR, "%s: permission denied", tag);
/*
* There is not real reason for this, but it makes it consistent
* with alter user, and it seems like a good idea anyway.
*/
if (IsTransactionBlock())
elog(ERROR, "%s: may not be called in a transaction block", tag);
pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
pg_group_dsc = RelationGetDescr(pg_group_rel);
......@@ -755,69 +811,14 @@ AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
if (!HeapTupleIsValid(group_tuple = SearchSysCacheTupleCopy(GRONAME, PointerGetDatum(stmt->name), 0, 0, 0)))
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "AlterGroup: Group \"%s\" does not exist.", stmt->name);
elog(ERROR, "%s: group \"%s\" does not exist", tag, stmt->name);
}
AssertState(stmt->action == +1 || stmt->action == -1);
/*
* Now decide what to do.
*/
if (stmt->action == 0) /* change sysid */
{
ScanKeyData keys[2];
HeapTuple tuple;
HeapScanDesc scan;
Datum new_record[Natts_pg_group];
char new_record_nulls[Natts_pg_group];
bool null;
/*
* First check if the id is already assigned.
*/
ScanKeyEntryInitialize(&keys[0], 0x0, Anum_pg_group_grosysid, F_INT4EQ,
Int32GetDatum(stmt->sysid));
ScanKeyEntryInitialize(&keys[1], 0x0, Anum_pg_group_groname, F_NAMENE,
PointerGetDatum(stmt->name));
scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 2, keys);
if (HeapTupleIsValid(heap_getnext(scan, false)))
{
heap_endscan(scan);
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "AlterGroup: Group sysid %d is already assigned.", stmt->sysid);
}
heap_endscan(scan);
/*
* Insert the new tuple with the updated sysid
*/
new_record[Anum_pg_group_groname-1] = (Datum)(stmt->name);
new_record[Anum_pg_group_grosysid-1] = (Datum)(stmt->sysid);
new_record[Anum_pg_group_grolist-1] = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
new_record_nulls[Anum_pg_group_groname-1] = ' ';
new_record_nulls[Anum_pg_group_grosysid-1] = ' ';
new_record_nulls[Anum_pg_group_grolist-1] = null ? 'n' : ' ';
tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
heap_update(pg_group_rel, &group_tuple->t_self, tuple, NULL);
/* Update indexes */
if (RelationGetForm(pg_group_rel)->relhasindex) {
Relation idescs[Num_pg_group_indices];
CatalogOpenIndices(Num_pg_group_indices,
Name_pg_group_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
tuple);
CatalogCloseIndices(Num_pg_group_indices, idescs);
}
}
/*
* add users to group
*/
else if (stmt->action > 0)
if (stmt->action == +1) /* add users, might also be invoked by create user */
{
Datum new_record[Natts_pg_group];
char new_record_nulls[Natts_pg_group] = { ' ', ' ', ' '};
......@@ -853,22 +854,34 @@ AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
foreach(item, stmt->listUsers)
{
Value *v;
/* Get the uid of the proposed user to add. */
tuple = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(strVal(lfirst(item))),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
if (strcmp(tag, "ALTER GROUP")==0)
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "AlterGroup: User \"%s\" does not exist.", strVal(lfirst(item)));
/* Get the uid of the proposed user to add. */
tuple = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(strVal(lfirst(item))),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
heap_close(pg_group_rel, AccessExclusiveLock);
elog(ERROR, "%s: user \"%s\" does not exist", tag, strVal(lfirst(item)));
}
v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
}
v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
else if (strcmp(tag, "CREATE USER")==0)
{
/* in this case we already know the uid and it wouldn't
be in the cache anyway yet */
v = lfirst(item);
}
else
elog(ERROR, "AlterGroup: unknown tag %s", tag);
if (!member(v, newlist))
newlist = lcons(v, newlist);
else
elog(NOTICE, "AlterGroup: User \"%s\" is already in group \"%s\".", strVal(lfirst(item)), stmt->name);
/* we silently assume here that this error will only come up
in a ALTER GROUP statement */
elog(NOTICE, "%s: user \"%s\" is already in group \"%s\"", tag, strVal(lfirst(item)), stmt->name);
}
newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
......@@ -906,17 +919,18 @@ AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
}
} /* endif alter group add user */
/*
* drop users from group
*/
else if (stmt->action < 0)
else if (stmt->action == -1) /*drop users from group */
{
Datum datum;
bool null;
bool is_dropuser = strcmp(tag, "DROP USER")==0;
datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
if (null)
elog(NOTICE, "AlterGroup: Group \"%s\"'s membership is NULL.", stmt->name);
{
if (!is_dropuser)
elog(NOTICE, "ALTER GROUP: group \"%s\" does not have any members", stmt->name);
}
else
{
HeapTuple tuple;
......@@ -950,22 +964,28 @@ AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
foreach(item, stmt->listUsers)
{
Value *v;
/* Get the uid of the proposed user to drop. */
tuple = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(strVal(lfirst(item))),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
if (!is_dropuser)
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "AlterGroup: User \"%s\" does not exist.", strVal(lfirst(item)));
/* Get the uid of the proposed user to drop. */
tuple = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(strVal(lfirst(item))),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
heap_close(pg_group_rel, AccessExclusiveLock);
elog(ERROR, "ALTER GROUP: user \"%s\" does not exist", strVal(lfirst(item)));
}
v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
}
else
{
/* for dropuser we already know the uid */
v = lfirst(item);
}
v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
if (member(v, newlist))
newlist = LispRemove(v, newlist);
else
elog(NOTICE, "AlterGroup: User \"%s\" is not in group \"%s\".", strVal(lfirst(item)), stmt->name);
else if (!is_dropuser)
elog(NOTICE, "ALTER GROUP: user \"%s\" is not in group \"%s\"", strVal(lfirst(item)), stmt->name);
}
newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
......@@ -1005,40 +1025,40 @@ AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
} /* endif group not null */
} /* endif alter group drop user */
heap_close(pg_group_rel, NoLock);
heap_close(pg_group_rel, AccessExclusiveLock);
pfree(group_tuple);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
}
/*
* DROP GROUP
*/
void
DropGroup(DropGroupStmt *stmt, CommandDest dest)
DropGroup(DropGroupStmt *stmt)
{
Relation pg_group_rel;
HeapScanDesc scan;
HeapTuple tuple;
TupleDesc pg_group_dsc;
bool inblock;
bool gro_exists = false;
if (!(inblock = IsTransactionBlock()))
BeginTransactionBlock();
/*
/*
* Make sure the user can do this.
*/
if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_WR) != ACLCHECK_OK)
{
UserAbortTransactionBlock();
elog(ERROR, "DropGroup: Permission denied.");
}
if (!superuser())
elog(ERROR, "DROP GROUP: permission denied");
/*
* Scan the pg_group table and delete all matching users.
* There is not real reason for this, but it makes it consistent
* with drop user, and it seems like a good idea anyway.
*/
if (IsTransactionBlock())
elog(ERROR, "DROP GROUP: may not be called in a transaction block");
/*
* Scan the pg_group table and delete all matching groups.
*/
pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
pg_group_dsc = RelationGetDescr(pg_group_rel);
......@@ -1055,7 +1075,6 @@ DropGroup(DropGroupStmt *stmt, CommandDest dest)
gro_exists = true;
heap_delete(pg_group_rel, &tuple->t_self, NULL);
}
}
heap_endscan(scan);
......@@ -1067,12 +1086,8 @@ DropGroup(DropGroupStmt *stmt, CommandDest dest)
{
heap_close(pg_group_rel, AccessExclusiveLock);
UserAbortTransactionBlock();
elog(ERROR, "DropGroup: Group \"%s\" does not exist.", stmt->name);
elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name);
}
heap_close(pg_group_rel, NoLock);
if (IsTransactionBlock() && !inblock)
EndTransactionBlock();
heap_close(pg_group_rel, AccessExclusiveLock);
}
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.124 2000/01/13 18:26:07 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.125 2000/01/14 22:11:34 petere Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -90,7 +90,6 @@ static Node *doNegate(Node *n);
char chr;
char *str;
bool boolean;
bool* pboolean; /* for pg_shadow privileges */
List *list;
Node *node;
Value *value;
......@@ -137,11 +136,11 @@ static Node *doNegate(Node *n);
%type <ival> opt_lock, lock_type
%type <boolean> opt_lmode
%type <pboolean> user_createdb_clause, user_createuser_clause
%type <ival> user_createdb_clause, user_createuser_clause
%type <str> user_passwd_clause
%type <ival> sysid_clause
%type <str> user_valid_clause
%type <list> user_group_list, user_group_clause, users_in_new_group_clause
%type <list> user_list, user_group_clause, users_in_new_group_clause
%type <boolean> TriggerActionTime, TriggerForSpec, PLangTrusted
......@@ -459,8 +458,8 @@ CreateUserStmt: CREATE USER UserId
n->user = $3;
n->sysid = -1;
n->password = NULL;
n->createdb = $4;
n->createuser = $5;
n->createdb = $4 == +1 ? true : false;
n->createuser = $5 == +1 ? true : false;
n->groupElts = $6;
n->validUntil = $7;
$$ = (Node *)n;
......@@ -473,8 +472,8 @@ CreateUserStmt: CREATE USER UserId
n->user = $3;
n->sysid = $5;
n->password = $6;
n->createdb = $7;
n->createuser = $8;
n->createdb = $7 == +1 ? true : false;
n->createuser = $8 == +1 ? true : false;
n->groupElts = $9;
n->validUntil = $10;
$$ = (Node *)n;
......@@ -489,30 +488,26 @@ CreateUserStmt: CREATE USER UserId
*****************************************************************************/
AlterUserStmt: ALTER USER UserId user_createdb_clause
user_createuser_clause user_group_clause user_valid_clause
user_createuser_clause user_valid_clause
{
AlterUserStmt *n = makeNode(AlterUserStmt);
n->user = $3;
n->sysid = -1;
n->password = NULL;
n->createdb = $4;
n->createuser = $5;
n->groupElts = $6;
n->validUntil = $7;
n->validUntil = $6;
$$ = (Node *)n;
}
| ALTER USER UserId WITH sysid_clause user_passwd_clause
| ALTER USER UserId WITH PASSWORD Sconst
user_createdb_clause
user_createuser_clause user_group_clause user_valid_clause
user_createuser_clause user_valid_clause
{
AlterUserStmt *n = makeNode(AlterUserStmt);
n->user = $3;
n->sysid = $5;
n->password = $6;
n->createdb = $7;
n->createuser = $8;
n->groupElts = $9;
n->validUntil = $10;
n->validUntil = $9;
$$ = (Node *)n;
}
;
......@@ -524,53 +519,38 @@ AlterUserStmt: ALTER USER UserId user_createdb_clause
*
*****************************************************************************/
DropUserStmt: DROP USER UserId
DropUserStmt: DROP USER user_list
{
DropUserStmt *n = makeNode(DropUserStmt);
n->user = $3;
n->users = $3;
$$ = (Node *)n;
}
;
user_passwd_clause: PASSWORD UserId { $$ = $2; }
user_passwd_clause: PASSWORD Sconst { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; }
;
sysid_clause: SYSID Iconst { $$ = $2; }
sysid_clause: SYSID Iconst
{
if ($2 <= 0)
elog(ERROR, "sysid must be positive");
$$ = $2;
}
| /*EMPTY*/ { $$ = -1; }
;
user_createdb_clause: CREATEDB
{
bool* b;
$$ = (b = (bool*)palloc(sizeof(bool)));
*b = true;
}
| NOCREATEDB
{
bool* b;
$$ = (b = (bool*)palloc(sizeof(bool)));
*b = false;
}
| /*EMPTY*/ { $$ = NULL; }
user_createdb_clause: CREATEDB { $$ = +1; }
| NOCREATEDB { $$ = -1; }
| /*EMPTY*/ { $$ = 0; }
;
user_createuser_clause: CREATEUSER
{
bool* b;
$$ = (b = (bool*)palloc(sizeof(bool)));
*b = true;
}
| NOCREATEUSER
{
bool* b;
$$ = (b = (bool*)palloc(sizeof(bool)));
*b = false;
}
| /*EMPTY*/ { $$ = NULL; }
user_createuser_clause: CREATEUSER { $$ = +1; }
| NOCREATEUSER { $$ = -1; }
| /*EMPTY*/ { $$ = 0; }
;
user_group_list: user_group_list ',' UserId
user_list: user_list ',' UserId
{
$$ = lcons((void*)makeString($3), $1);
}
......@@ -580,7 +560,7 @@ user_group_list: user_group_list ',' UserId
}
;
user_group_clause: IN GROUP user_group_list { $$ = $3; }
user_group_clause: IN GROUP user_list { $$ = $3; }
| /*EMPTY*/ { $$ = NULL; }
;
......@@ -615,7 +595,7 @@ CreateGroupStmt: CREATE GROUP UserId
}
;
users_in_new_group_clause: USER user_group_list { $$ = $2; }
users_in_new_group_clause: USER user_list { $$ = $2; }
| /* EMPTY */ { $$ = NULL; }
;
......@@ -626,17 +606,7 @@ users_in_new_group_clause: USER user_group_list { $$ = $2; }
*
*****************************************************************************/
AlterGroupStmt: ALTER GROUP UserId WITH SYSID Iconst
{
AlterGroupStmt *n = makeNode(AlterGroupStmt);
n->name = $3;
n->sysid = $6;
n->action = 0;
n->listUsers = NULL;
$$ = (Node *)n;
}
|
ALTER GROUP UserId ADD USER user_group_list
AlterGroupStmt: ALTER GROUP UserId ADD USER user_list
{
AlterGroupStmt *n = makeNode(AlterGroupStmt);
n->name = $3;
......@@ -646,7 +616,7 @@ AlterGroupStmt: ALTER GROUP UserId WITH SYSID Iconst
$$ = (Node *)n;
}
|
ALTER GROUP UserId DROP USER user_group_list
ALTER GROUP UserId DROP USER user_list
{
AlterGroupStmt *n = makeNode(AlterGroupStmt);
n->name = $3;
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.77 2000/01/13 18:26:10 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.78 2000/01/14 22:11:35 petere Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -266,11 +266,7 @@ ProcessUtility(Node *parsetree,
*/
stmt->filename,
stmt->delimiter,
stmt->null_print,
/*
* specify 022 umask while writing files with COPY.
*/
0022);
stmt->null_print);
}
break;
......@@ -775,21 +771,21 @@ ProcessUtility(Node *parsetree,
PS_SET_STATUS(commandTag = "CREATE USER");
CHECK_IF_ABORTED();
DefineUser((CreateUserStmt *) parsetree, dest);
CreateUser((CreateUserStmt *) parsetree);
break;
case T_AlterUserStmt:
PS_SET_STATUS(commandTag = "ALTER USER");
CHECK_IF_ABORTED();
AlterUser((AlterUserStmt *) parsetree, dest);
AlterUser((AlterUserStmt *) parsetree);
break;
case T_DropUserStmt:
PS_SET_STATUS(commandTag = "DROP USER");
CHECK_IF_ABORTED();
RemoveUser(((DropUserStmt *) parsetree)->user, dest);
DropUser((DropUserStmt *) parsetree);
break;
case T_LockStmt:
......@@ -810,21 +806,21 @@ ProcessUtility(Node *parsetree,
PS_SET_STATUS(commandTag = "CREATE GROUP");
CHECK_IF_ABORTED();
CreateGroup((CreateGroupStmt *) parsetree, dest);
CreateGroup((CreateGroupStmt *) parsetree);
break;
case T_AlterGroupStmt:
PS_SET_STATUS(commandTag = "ALTER GROUP");
CHECK_IF_ABORTED();
AlterGroup((AlterGroupStmt *) parsetree, dest);
AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
break;
case T_DropGroupStmt:
PS_SET_STATUS(commandTag = "DROP GROUP");
CHECK_IF_ABORTED();
DropGroup((DropGroupStmt *) parsetree, dest);
DropGroup((DropGroupStmt *) parsetree);
break;
/*
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/misc/superuser.c,v 1.12 1999/11/24 16:52:45 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/misc/superuser.c,v 1.13 2000/01/14 22:11:36 petere Exp $
*
* DESCRIPTION
* See superuser().
......@@ -18,6 +18,7 @@
#include "postgres.h"
#include "catalog/pg_shadow.h"
#include "utils/syscache.h"
#include "miscadmin.h"
bool
superuser(void)
......@@ -26,12 +27,10 @@ superuser(void)
The Postgres user running this command has Postgres superuser
privileges.
--------------------------------------------------------------------------*/
extern char *UserName; /* defined in global.c */
HeapTuple utup;
utup = SearchSysCacheTuple(SHADOWNAME,
PointerGetDatum(UserName),
PointerGetDatum(GetPgUserName()),
0, 0, 0);
Assert(utup != NULL);
return ((Form_pg_shadow) GETSTRUCT(utup))->usesuper;
......
......@@ -8,7 +8,7 @@
#
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/bin/scripts/Attic/createuser,v 1.5 2000/01/12 19:36:36 petere Exp $
# $Header: /cvsroot/pgsql/src/bin/scripts/Attic/createuser,v 1.6 2000/01/14 22:11:36 petere Exp $
#
# Note - this should NOT be setuid.
#
......@@ -193,7 +193,7 @@ QUERY="CREATE USER \"$NewUser\""
SUBQUERY=
[ "$SysID" ] && SUBQUERY="$SUBQUERY SYSID $SysID"
[ "$Password" ] && SUBQUERY="$SUBQUERY PASSWORD \"$Password\""
[ "$Password" ] && SUBQUERY="$SUBQUERY PASSWORD '$Password'"
[ "$SUBQUERY" ] && QUERY="$QUERY WITH $SUBQUERY"
[ "$CanCreateDb" = t ] && QUERY="$QUERY CREATEDB"
......
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: copy.h,v 1.7 1999/12/14 00:08:19 momjian Exp $
* $Id: copy.h,v 1.8 2000/01/14 22:11:37 petere Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -15,6 +15,6 @@
void DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
char *filename, char *delim, char *null_print, int fileumask);
char *filename, char *delim, char *null_print);
#endif /* COPY_H */
......@@ -11,15 +11,15 @@
#define USER_H
#include "nodes/parsenodes.h"
#include "tcop/dest.h"
#include "access/htup.h"
extern void DefineUser(CreateUserStmt *stmt, CommandDest);
extern void AlterUser(AlterUserStmt *stmt, CommandDest);
extern void RemoveUser(char *user, CommandDest);
extern void CreateUser(CreateUserStmt *stmt);
extern void AlterUser(AlterUserStmt *stmt);
extern void DropUser(DropUserStmt *stmt);
extern void CreateGroup(CreateGroupStmt *stmt, CommandDest dest);
extern void AlterGroup(AlterGroupStmt *stmt, CommandDest dest);
extern void DropGroup(DropGroupStmt *stmt, CommandDest dest);
extern void CreateGroup(CreateGroupStmt *stmt);
extern void AlterGroup(AlterGroupStmt *stmt, const char * tag);
extern void DropGroup(DropGroupStmt *stmt);
extern HeapTuple update_pg_pwd(void);
......
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.92 1999/12/16 17:24:19 momjian Exp $
* $Id: parsenodes.h,v 1.93 2000/01/14 22:11:38 petere Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -270,18 +270,26 @@ typedef struct CreateUserStmt
char *user; /* PostgreSQL user login */
char *password; /* PostgreSQL user password */
int sysid; /* PgSQL system id (-1 if don't care) */
bool *createdb; /* Can the user create databases? */
bool *createuser; /* Can this user create users? */
bool createdb; /* Can the user create databases? */
bool createuser; /* Can this user create users? */
List *groupElts; /* The groups the user is a member of */
char *validUntil; /* The time the login is valid until */
} CreateUserStmt;
typedef CreateUserStmt AlterUserStmt;
typedef struct AlterUserStmt
{
NodeTag type;
char *user; /* PostgreSQL user login */
char *password; /* PostgreSQL user password */
int createdb; /* Can the user create databases? */
int createuser; /* Can this user create users? */
char *validUntil; /* The time the login is valid until */
} AlterUserStmt;
typedef struct DropUserStmt
{
NodeTag type;
char *user; /* PostgreSQL user login */
List *users; /* List of users to remove */
} DropUserStmt;
......@@ -301,7 +309,7 @@ typedef struct AlterGroupStmt
{
NodeTag type;
char *name; /* name of group to alter */
int action; /* +1 = add, -1 = drop, 0 = other (HACK!) */
int action; /* +1 = add, -1 = drop user */
int sysid; /* sysid change */
List *listUsers; /* list of users to add/drop */
} AlterGroupStmt;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment