1997-12-04 01:34:01 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* user.c
|
2000-06-28 05:33:33 +02:00
|
|
|
* Commands for manipulating users and groups.
|
1997-12-04 01:34:01 +01:00
|
|
|
*
|
2001-01-24 20:43:33 +01:00
|
|
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1997-12-04 01:34:01 +01:00
|
|
|
*
|
2002-04-26 21:29:47 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.97 2002/04/26 19:29:47 tgl Exp $
|
1997-12-04 01:34:01 +01:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
2001-06-12 07:55:50 +02:00
|
|
|
#include "postgres.h"
|
|
|
|
|
1997-12-30 03:26:56 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2002-04-04 06:25:54 +02:00
|
|
|
#include <errno.h>
|
1997-12-30 03:26:56 +01:00
|
|
|
#include <unistd.h>
|
1997-12-04 01:34:01 +01:00
|
|
|
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "access/heapam.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "catalog/catname.h"
|
|
|
|
#include "catalog/pg_database.h"
|
|
|
|
#include "catalog/pg_shadow.h"
|
1999-12-16 18:24:19 +01:00
|
|
|
#include "catalog/pg_group.h"
|
|
|
|
#include "catalog/indexing.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "commands/user.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "libpq/crypt.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "miscadmin.h"
|
2001-11-04 20:55:31 +01:00
|
|
|
#include "storage/pmsignal.h"
|
2002-04-04 06:25:54 +02:00
|
|
|
#include "utils/acl.h"
|
2000-07-17 05:05:41 +02:00
|
|
|
#include "utils/array.h"
|
2000-01-14 23:11:38 +01:00
|
|
|
#include "utils/builtins.h"
|
2000-05-28 19:56:29 +02:00
|
|
|
#include "utils/fmgroids.h"
|
2002-03-01 23:45:19 +01:00
|
|
|
#include "utils/guc.h"
|
2001-06-14 03:09:22 +02:00
|
|
|
#include "utils/lsyscache.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "utils/syscache.h"
|
1997-12-04 01:34:01 +01:00
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
|
2001-08-15 20:42:16 +02:00
|
|
|
extern bool Password_encryption;
|
1998-12-14 07:50:32 +01:00
|
|
|
|
2001-11-02 19:39:57 +01:00
|
|
|
static void CheckPgUserAclNotNull(void);
|
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fputs_quote
|
|
|
|
*
|
|
|
|
* Outputs string in quotes, with double-quotes duplicated.
|
|
|
|
* We could use quote_ident(), but that expects varlena.
|
|
|
|
*/
|
|
|
|
static void fputs_quote(char *str, FILE *fp)
|
|
|
|
{
|
|
|
|
fputc('"', fp);
|
|
|
|
while (*str)
|
|
|
|
{
|
|
|
|
fputc(*str, fp);
|
|
|
|
if (*str == '"')
|
|
|
|
fputc('"', fp);
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
fputc('"', fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* group_getfilename --- get full pathname of group file
|
|
|
|
*
|
|
|
|
* Note that result string is palloc'd, and should be freed by the caller.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
group_getfilename(void)
|
|
|
|
{
|
|
|
|
int bufsize;
|
|
|
|
char *pfnam;
|
|
|
|
|
|
|
|
bufsize = strlen(DataDir) + strlen("/global/") +
|
|
|
|
strlen(USER_GROUP_FILE) + 1;
|
|
|
|
pfnam = (char *) palloc(bufsize);
|
|
|
|
snprintf(pfnam, bufsize, "%s/global/%s", DataDir, USER_GROUP_FILE);
|
|
|
|
|
|
|
|
return pfnam;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get full pathname of password file.
|
|
|
|
* Note that result string is palloc'd, and should be freed by the caller.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
user_getfilename(void)
|
|
|
|
{
|
|
|
|
int bufsize;
|
|
|
|
char *pfnam;
|
|
|
|
|
|
|
|
bufsize = strlen(DataDir) + strlen("/global/") +
|
|
|
|
strlen(PWD_FILE) + 1;
|
|
|
|
pfnam = (char *) palloc(bufsize);
|
|
|
|
snprintf(pfnam, bufsize, "%s/global/%s", DataDir, PWD_FILE);
|
|
|
|
|
|
|
|
return pfnam;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* write_group_file for trigger update_pg_pwd_and_pg_group
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
write_group_file(Relation urel, Relation grel)
|
|
|
|
{
|
|
|
|
char *filename,
|
|
|
|
*tempname;
|
|
|
|
int bufsize;
|
|
|
|
FILE *fp;
|
|
|
|
mode_t oumask;
|
|
|
|
HeapScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
|
|
|
TupleDesc dsc = RelationGetDescr(grel);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a temporary filename to be renamed later. This prevents the
|
|
|
|
* backend from clobbering the pg_group file while the postmaster might
|
|
|
|
* be reading from it.
|
|
|
|
*/
|
|
|
|
filename = group_getfilename();
|
|
|
|
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, "write_group_file: unable to write %s: %m", tempname);
|
|
|
|
|
|
|
|
/* read table */
|
|
|
|
scan = heap_beginscan(grel, false, SnapshotSelf, 0, NULL);
|
|
|
|
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
|
|
|
{
|
|
|
|
Datum datum, grolist_datum;
|
|
|
|
bool isnull;
|
|
|
|
char *groname;
|
|
|
|
IdList *grolist_p;
|
|
|
|
AclId *aidp;
|
|
|
|
int i, j,
|
|
|
|
num;
|
|
|
|
char *usename;
|
|
|
|
bool first_user = true;
|
|
|
|
|
|
|
|
datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull);
|
|
|
|
if (isnull)
|
|
|
|
continue; /* ignore NULL groupnames */
|
2002-04-18 23:16:16 +02:00
|
|
|
groname = NameStr(*DatumGetName(datum));
|
2002-04-04 06:25:54 +02:00
|
|
|
|
|
|
|
grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull);
|
|
|
|
/* Ignore NULL group lists */
|
|
|
|
if (isnull)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
grolist_p = DatumGetIdListP(grolist_datum);
|
|
|
|
/*
|
|
|
|
* Check for illegal characters in the group name.
|
|
|
|
*/
|
|
|
|
i = strcspn(groname, "\n");
|
|
|
|
if (groname[i] != '\0')
|
|
|
|
{
|
|
|
|
elog(LOG, "Invalid group name '%s'", groname);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* be sure the IdList is not toasted */
|
|
|
|
/* scan it */
|
|
|
|
num = IDLIST_NUM(grolist_p);
|
|
|
|
aidp = IDLIST_DAT(grolist_p);
|
|
|
|
for (i = 0; i < num; ++i)
|
|
|
|
{
|
|
|
|
tuple = SearchSysCache(SHADOWSYSID,
|
|
|
|
PointerGetDatum(aidp[i]),
|
|
|
|
0, 0, 0);
|
|
|
|
if (HeapTupleIsValid(tuple))
|
|
|
|
{
|
|
|
|
usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for illegal characters in the user name.
|
|
|
|
*/
|
|
|
|
j = strcspn(usename, "\n");
|
|
|
|
if (usename[j] != '\0')
|
|
|
|
{
|
|
|
|
elog(LOG, "Invalid user name '%s'", usename);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* File format is:
|
|
|
|
* "dbname" "user1","user2","user3"
|
|
|
|
* This matches pg_hba.conf.
|
|
|
|
*/
|
|
|
|
if (first_user)
|
|
|
|
{
|
|
|
|
fputs_quote(groname, fp);
|
|
|
|
fputs("\t", fp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fputs(" ", fp);
|
|
|
|
|
|
|
|
first_user = false;
|
|
|
|
fputs_quote(usename, fp);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!first_user)
|
|
|
|
fputs("\n", fp);
|
|
|
|
/* if IdList was toasted, free detoasted copy */
|
|
|
|
if ((Pointer) grolist_p != DatumGetPointer(grolist_datum))
|
|
|
|
pfree(grolist_p);
|
|
|
|
}
|
|
|
|
heap_endscan(scan);
|
|
|
|
|
|
|
|
fflush(fp);
|
|
|
|
if (ferror(fp))
|
|
|
|
elog(ERROR, "%s: %m", tempname);
|
|
|
|
FreeFile(fp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Rename the temp file to its final name, deleting the old pg_pwd. We
|
|
|
|
* expect that rename(2) is an atomic action.
|
|
|
|
*/
|
|
|
|
if (rename(tempname, filename))
|
|
|
|
elog(ERROR, "rename %s to %s: %m", tempname, filename);
|
|
|
|
|
|
|
|
pfree((void *) tempname);
|
|
|
|
pfree((void *) filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* write_password_file for trigger update_pg_pwd_and_pg_group
|
1997-12-04 01:34:01 +01:00
|
|
|
*
|
1998-02-25 14:09:49 +01:00
|
|
|
* copy the modified contents of pg_shadow to a file used by the postmaster
|
2001-11-02 19:39:57 +01:00
|
|
|
* for user authentication. The file is stored as $PGDATA/global/pg_pwd.
|
1999-09-18 21:08:25 +02:00
|
|
|
*
|
2000-01-14 23:11:38 +01:00
|
|
|
* 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.
|
2001-11-01 19:09:58 +01:00
|
|
|
*
|
|
|
|
* We raise an error to force transaction rollback if we detect an illegal
|
|
|
|
* username or password --- illegal being defined as values that would
|
|
|
|
* mess up the pg_pwd parser.
|
1997-12-04 01:34:01 +01:00
|
|
|
*/
|
2000-01-14 23:11:38 +01:00
|
|
|
static void
|
2002-04-04 06:25:54 +02:00
|
|
|
write_user_file(Relation urel)
|
1998-02-26 05:46:47 +01:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
char *filename,
|
|
|
|
*tempname;
|
|
|
|
int bufsize;
|
2000-04-12 19:17:23 +02:00
|
|
|
FILE *fp;
|
|
|
|
mode_t oumask;
|
|
|
|
HeapScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
2002-04-04 06:25:54 +02:00
|
|
|
TupleDesc dsc = RelationGetDescr(urel);
|
1999-12-21 23:39:02 +01:00
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
/*
|
|
|
|
* Create a temporary filename to be renamed later. This prevents the
|
|
|
|
* backend from clobbering the pg_pwd file while the postmaster might
|
|
|
|
* be reading from it.
|
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
filename = user_getfilename();
|
1998-12-14 07:50:32 +01:00
|
|
|
bufsize = strlen(filename) + 12;
|
|
|
|
tempname = (char *) palloc(bufsize);
|
2000-01-14 23:11:38 +01:00
|
|
|
|
1998-12-14 07:50:32 +01:00
|
|
|
snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
|
2000-04-12 19:17:23 +02:00
|
|
|
oumask = umask((mode_t) 077);
|
|
|
|
fp = AllocateFile(tempname, "w");
|
|
|
|
umask(oumask);
|
|
|
|
if (fp == NULL)
|
2000-08-27 23:50:18 +02:00
|
|
|
elog(ERROR, "write_password_file: unable to write %s: %m", tempname);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/* read table */
|
2002-04-04 06:25:54 +02:00
|
|
|
scan = heap_beginscan(urel, false, SnapshotSelf, 0, NULL);
|
2000-04-12 19:17:23 +02:00
|
|
|
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
|
|
|
{
|
2002-04-04 06:25:54 +02:00
|
|
|
Datum datum;
|
|
|
|
bool isnull;
|
|
|
|
char *usename,
|
|
|
|
*passwd,
|
|
|
|
*valuntil;
|
2001-11-01 19:09:58 +01:00
|
|
|
int i;
|
2000-01-14 23:11:38 +01:00
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull);
|
|
|
|
if (isnull)
|
2001-11-01 19:09:58 +01:00
|
|
|
continue; /* ignore NULL usernames */
|
2002-04-18 23:16:16 +02:00
|
|
|
usename = NameStr(*DatumGetName(datum));
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull);
|
2001-11-05 18:46:40 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2001-11-01 19:09:58 +01:00
|
|
|
* It can be argued that people having a null password shouldn't
|
|
|
|
* be allowed to connect under password authentication, because
|
2001-11-05 18:46:40 +01:00
|
|
|
* they need to have a password set up first. If you think
|
|
|
|
* assuming an empty password in that case is better, change this
|
|
|
|
* logic to look something like the code for valuntil.
|
2000-04-12 19:17:23 +02:00
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
if (isnull)
|
2000-04-12 19:17:23 +02:00
|
|
|
continue;
|
2001-11-01 19:09:58 +01:00
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
passwd = DatumGetCString(DirectFunctionCall1(textout, datum));
|
|
|
|
|
|
|
|
datum = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &isnull);
|
|
|
|
if (isnull)
|
|
|
|
valuntil = pstrdup("");
|
2001-11-01 19:09:58 +01:00
|
|
|
else
|
2002-04-04 06:25:54 +02:00
|
|
|
valuntil = DatumGetCString(DirectFunctionCall1(nabstimeout, datum));
|
2000-01-14 23:11:38 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2001-11-01 19:09:58 +01:00
|
|
|
* Check for illegal characters in the username and password.
|
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
i = strcspn(usename, "\n");
|
|
|
|
if (usename[i] != '\0')
|
|
|
|
elog(ERROR, "Invalid user name '%s'", usename);
|
|
|
|
i = strcspn(passwd, "\n");
|
|
|
|
if (passwd[i] != '\0')
|
|
|
|
elog(ERROR, "Invalid user password '%s'", passwd);
|
2001-11-01 19:09:58 +01:00
|
|
|
|
|
|
|
/*
|
2001-11-05 18:46:40 +01:00
|
|
|
* The extra columns we emit here are not really necessary. To
|
|
|
|
* remove them, the parser in backend/libpq/crypt.c would need to
|
|
|
|
* be adjusted.
|
2000-04-12 19:17:23 +02:00
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
fputs_quote(usename, fp);
|
|
|
|
fputs(" ", fp);
|
|
|
|
fputs_quote(passwd, fp);
|
|
|
|
fputs(" ", fp);
|
|
|
|
fputs_quote(valuntil, fp);
|
|
|
|
|
|
|
|
pfree(passwd);
|
|
|
|
pfree(valuntil);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
heap_endscan(scan);
|
2000-05-04 22:06:07 +02:00
|
|
|
|
|
|
|
fflush(fp);
|
|
|
|
if (ferror(fp))
|
|
|
|
elog(ERROR, "%s: %m", tempname);
|
2000-04-12 19:17:23 +02:00
|
|
|
FreeFile(fp);
|
|
|
|
|
|
|
|
/*
|
2001-11-05 18:46:40 +01:00
|
|
|
* Rename the temp file to its final name, deleting the old pg_pwd. We
|
|
|
|
* expect that rename(2) is an atomic action.
|
2000-04-12 19:17:23 +02:00
|
|
|
*/
|
2000-05-04 22:06:07 +02:00
|
|
|
if (rename(tempname, filename))
|
|
|
|
elog(ERROR, "rename %s to %s: %m", tempname, filename);
|
|
|
|
|
|
|
|
pfree((void *) tempname);
|
|
|
|
pfree((void *) filename);
|
2000-01-14 23:11:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This is the wrapper for triggers. */
|
2000-05-29 03:59:17 +02:00
|
|
|
Datum
|
2002-04-04 06:25:54 +02:00
|
|
|
update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS)
|
2000-01-14 23:11:38 +01:00
|
|
|
{
|
2001-06-12 07:55:50 +02:00
|
|
|
/*
|
|
|
|
* ExclusiveLock ensures no one modifies pg_shadow while we read it,
|
2001-10-25 07:50:21 +02:00
|
|
|
* and that only one backend rewrites the flat file at a time. It's
|
2001-06-12 07:55:50 +02:00
|
|
|
* OK to allow normal reads of pg_shadow in parallel, however.
|
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
Relation urel = heap_openr(ShadowRelationName, ExclusiveLock);
|
|
|
|
Relation grel = heap_openr(GroupRelationName, ExclusiveLock);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
write_user_file(urel);
|
|
|
|
write_group_file(urel, grel);
|
2001-06-12 07:55:50 +02:00
|
|
|
/* OK to release lock, since we did not modify the relation */
|
2002-04-04 06:25:54 +02:00
|
|
|
heap_close(grel, ExclusiveLock);
|
|
|
|
heap_close(urel, ExclusiveLock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Signal the postmaster to reload its password & group-file cache.
|
|
|
|
*/
|
|
|
|
SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
|
|
|
|
|
2000-05-29 03:59:17 +02:00
|
|
|
return PointerGetDatum(NULL);
|
1997-12-04 01:34:01 +01:00
|
|
|
}
|
|
|
|
|
2000-01-14 23:11:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CREATE USER
|
1997-12-04 01:34:01 +01:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
void
|
2000-01-14 23:11:38 +01:00
|
|
|
CreateUser(CreateUserStmt *stmt)
|
1998-02-26 05:46:47 +01:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
Relation pg_shadow_rel;
|
|
|
|
TupleDesc pg_shadow_dsc;
|
|
|
|
HeapScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
2000-04-12 19:17:23 +02:00
|
|
|
Datum new_record[Natts_pg_shadow];
|
|
|
|
char new_record_nulls[Natts_pg_shadow];
|
1999-11-30 04:57:29 +01:00
|
|
|
bool user_exists = false,
|
2000-04-12 19:17:23 +02:00
|
|
|
sysid_exists = false,
|
2001-07-11 00:09:29 +02:00
|
|
|
havesysid = false;
|
2001-09-08 17:24:00 +02:00
|
|
|
int max_id;
|
2001-10-25 07:50:21 +02:00
|
|
|
List *item,
|
|
|
|
*option;
|
2001-10-28 07:26:15 +01:00
|
|
|
char *password = NULL; /* PostgreSQL user password */
|
2001-10-25 07:50:21 +02:00
|
|
|
bool encrypt_password = Password_encryption; /* encrypt password? */
|
|
|
|
char encrypted_password[MD5_PASSWD_LEN + 1];
|
|
|
|
int sysid = 0; /* PgSQL system id (valid if havesysid) */
|
|
|
|
bool createdb = false; /* Can the user create databases? */
|
|
|
|
bool createuser = false; /* Can this user create users? */
|
2001-10-28 07:26:15 +01:00
|
|
|
List *groupElts = NIL; /* The groups the user is a member of */
|
2001-10-25 07:50:21 +02:00
|
|
|
char *validUntil = NULL; /* The time the login is valid
|
|
|
|
* until */
|
2001-08-15 20:42:16 +02:00
|
|
|
DefElem *dpassword = NULL;
|
|
|
|
DefElem *dsysid = NULL;
|
|
|
|
DefElem *dcreatedb = NULL;
|
|
|
|
DefElem *dcreateuser = NULL;
|
|
|
|
DefElem *dgroupElts = NULL;
|
|
|
|
DefElem *dvalidUntil = NULL;
|
2001-07-11 00:09:29 +02:00
|
|
|
|
|
|
|
/* Extract options from the statement node tree */
|
|
|
|
foreach(option, stmt->options)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
DefElem *defel = (DefElem *) lfirst(option);
|
2001-07-11 00:09:29 +02:00
|
|
|
|
2001-09-19 11:48:42 +02:00
|
|
|
if (strcmp(defel->defname, "password") == 0 ||
|
|
|
|
strcmp(defel->defname, "encryptedPassword") == 0 ||
|
2001-10-25 07:50:21 +02:00
|
|
|
strcmp(defel->defname, "unencryptedPassword") == 0)
|
|
|
|
{
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dpassword)
|
|
|
|
elog(ERROR, "CREATE USER: conflicting options");
|
|
|
|
dpassword = defel;
|
2001-09-19 11:48:42 +02:00
|
|
|
if (strcmp(defel->defname, "encryptedPassword") == 0)
|
2001-08-15 20:42:16 +02:00
|
|
|
encrypt_password = true;
|
2001-09-19 11:48:42 +02:00
|
|
|
else if (strcmp(defel->defname, "unencryptedPassword") == 0)
|
2001-08-15 20:42:16 +02:00
|
|
|
encrypt_password = false;
|
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
else if (strcmp(defel->defname, "sysid") == 0)
|
|
|
|
{
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dsysid)
|
|
|
|
elog(ERROR, "CREATE USER: conflicting options");
|
|
|
|
dsysid = defel;
|
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
else if (strcmp(defel->defname, "createdb") == 0)
|
|
|
|
{
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dcreatedb)
|
|
|
|
elog(ERROR, "CREATE USER: conflicting options");
|
|
|
|
dcreatedb = defel;
|
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
else if (strcmp(defel->defname, "createuser") == 0)
|
|
|
|
{
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dcreateuser)
|
|
|
|
elog(ERROR, "CREATE USER: conflicting options");
|
|
|
|
dcreateuser = defel;
|
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
else if (strcmp(defel->defname, "groupElts") == 0)
|
|
|
|
{
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dgroupElts)
|
|
|
|
elog(ERROR, "CREATE USER: conflicting options");
|
|
|
|
dgroupElts = defel;
|
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
else if (strcmp(defel->defname, "validUntil") == 0)
|
|
|
|
{
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dvalidUntil)
|
|
|
|
elog(ERROR, "CREATE USER: conflicting options");
|
|
|
|
dvalidUntil = defel;
|
|
|
|
}
|
|
|
|
else
|
2001-10-25 07:50:21 +02:00
|
|
|
elog(ERROR, "CREATE USER: option \"%s\" not recognized",
|
2001-08-15 20:42:16 +02:00
|
|
|
defel->defname);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dcreatedb)
|
2001-07-11 00:09:29 +02:00
|
|
|
createdb = intVal(dcreatedb->arg) != 0;
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dcreateuser)
|
2001-07-11 00:09:29 +02:00
|
|
|
createuser = intVal(dcreateuser->arg) != 0;
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dsysid)
|
2001-07-11 00:09:29 +02:00
|
|
|
{
|
|
|
|
sysid = intVal(dsysid->arg);
|
2001-09-08 17:24:00 +02:00
|
|
|
if (sysid <= 0)
|
|
|
|
elog(ERROR, "user id must be positive");
|
2001-07-11 00:09:29 +02:00
|
|
|
havesysid = true;
|
|
|
|
}
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dvalidUntil)
|
2001-07-11 00:09:29 +02:00
|
|
|
validUntil = strVal(dvalidUntil->arg);
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dpassword)
|
2001-07-11 00:09:29 +02:00
|
|
|
password = strVal(dpassword->arg);
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dgroupElts)
|
2001-07-11 00:09:29 +02:00
|
|
|
groupElts = (List *) dgroupElts->arg;
|
1999-04-02 08:16:36 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/* Check some permissions first */
|
2001-07-11 00:09:29 +02:00
|
|
|
if (password)
|
1998-02-26 05:46:47 +01:00
|
|
|
CheckPgUserAclNotNull();
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!superuser())
|
|
|
|
elog(ERROR, "CREATE USER: permission denied");
|
2000-01-14 23:11:38 +01:00
|
|
|
|
2002-04-18 23:16:16 +02:00
|
|
|
if (strcmp(stmt->user, "public") == 0)
|
|
|
|
elog(ERROR, "CREATE USER: user name \"%s\" is reserved",
|
|
|
|
stmt->user);
|
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
/*
|
2000-04-12 19:17:23 +02:00
|
|
|
* Scan the pg_shadow relation to be certain the user or id doesn't
|
|
|
|
* already exist. Note we secure exclusive lock, because we also need
|
|
|
|
* to be sure of what the next usesysid should be, and we need to
|
|
|
|
* protect our update of the flat password file.
|
1998-02-26 05:46:47 +01:00
|
|
|
*/
|
2001-06-12 07:55:50 +02:00
|
|
|
pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
|
1998-09-01 05:29:17 +02:00
|
|
|
pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-07-27 21:38:40 +02:00
|
|
|
scan = heap_beginscan(pg_shadow_rel, false, SnapshotNow, 0, NULL);
|
2001-09-08 17:24:00 +02:00
|
|
|
max_id = 99; /* start auto-assigned ids at 100 */
|
2001-06-12 07:55:50 +02:00
|
|
|
while (!user_exists && !sysid_exists &&
|
|
|
|
HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
1998-02-26 05:46:47 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
Datum datum;
|
|
|
|
bool null;
|
1999-11-30 04:57:29 +01:00
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
datum = heap_getattr(tuple, Anum_pg_shadow_usename,
|
|
|
|
pg_shadow_dsc, &null);
|
|
|
|
Assert(!null);
|
2002-04-18 23:16:16 +02:00
|
|
|
user_exists = (strcmp(NameStr(*DatumGetName(datum)), stmt->user) == 0);
|
1999-11-30 04:57:29 +01:00
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
datum = heap_getattr(tuple, Anum_pg_shadow_usesysid,
|
|
|
|
pg_shadow_dsc, &null);
|
|
|
|
Assert(!null);
|
2000-04-12 19:17:23 +02:00
|
|
|
if (havesysid) /* customized id wanted */
|
2001-07-11 00:09:29 +02:00
|
|
|
sysid_exists = (DatumGetInt32(datum) == sysid);
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
|
|
|
{
|
2001-06-12 07:55:50 +02:00
|
|
|
/* pick 1 + max */
|
|
|
|
if (DatumGetInt32(datum) > max_id)
|
|
|
|
max_id = DatumGetInt32(datum);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
}
|
|
|
|
heap_endscan(scan);
|
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
if (user_exists)
|
|
|
|
elog(ERROR, "CREATE USER: user name \"%s\" already exists",
|
|
|
|
stmt->user);
|
|
|
|
if (sysid_exists)
|
2001-07-11 00:09:29 +02:00
|
|
|
elog(ERROR, "CREATE USER: sysid %d is already assigned", sysid);
|
|
|
|
|
|
|
|
/* If no sysid given, use max existing id + 1 */
|
2001-10-25 07:50:21 +02:00
|
|
|
if (!havesysid)
|
2001-07-11 00:09:29 +02:00
|
|
|
sysid = max_id + 1;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Build a tuple to insert
|
|
|
|
*/
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record[Anum_pg_shadow_usename - 1] =
|
|
|
|
DirectFunctionCall1(namein, CStringGetDatum(stmt->user));
|
|
|
|
new_record[Anum_pg_shadow_usesysid - 1] = Int32GetDatum(sysid);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-07-11 00:09:29 +02:00
|
|
|
AssertState(BoolIsValid(createdb));
|
|
|
|
new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb);
|
2001-06-12 07:55:50 +02:00
|
|
|
new_record[Anum_pg_shadow_usetrace - 1] = BoolGetDatum(false);
|
2001-07-11 00:09:29 +02:00
|
|
|
AssertState(BoolIsValid(createuser));
|
|
|
|
new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser);
|
2000-04-12 19:17:23 +02:00
|
|
|
/* superuser gets catupd right by default */
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-07-11 00:09:29 +02:00
|
|
|
if (password)
|
2001-08-15 20:42:16 +02:00
|
|
|
{
|
|
|
|
if (!encrypt_password || isMD5(password))
|
|
|
|
new_record[Anum_pg_shadow_passwd - 1] =
|
|
|
|
DirectFunctionCall1(textin, CStringGetDatum(password));
|
|
|
|
else
|
|
|
|
{
|
2001-08-17 04:59:20 +02:00
|
|
|
if (!EncryptMD5(password, stmt->user, strlen(stmt->user),
|
2001-10-25 07:50:21 +02:00
|
|
|
encrypted_password))
|
2001-08-15 20:42:16 +02:00
|
|
|
elog(ERROR, "CREATE USER: password encryption failed");
|
|
|
|
new_record[Anum_pg_shadow_passwd - 1] =
|
|
|
|
DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
|
|
|
|
}
|
|
|
|
}
|
2001-07-11 00:09:29 +02:00
|
|
|
if (validUntil)
|
2000-06-09 03:11:16 +02:00
|
|
|
new_record[Anum_pg_shadow_valuntil - 1] =
|
2001-07-11 00:09:29 +02:00
|
|
|
DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
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] = ' ';
|
|
|
|
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record_nulls[Anum_pg_shadow_passwd - 1] = password ? ' ' : 'n';
|
|
|
|
new_record_nulls[Anum_pg_shadow_valuntil - 1] = validUntil ? ' ' : 'n';
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2002-03-01 23:45:19 +01:00
|
|
|
new_record_nulls[Anum_pg_shadow_useconfig - 1] = 'n';
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
|
|
|
|
|
|
|
|
/*
|
2001-06-12 07:55:50 +02:00
|
|
|
* Insert new record in the pg_shadow table
|
2000-04-12 19:17:23 +02:00
|
|
|
*/
|
2001-06-12 07:55:50 +02:00
|
|
|
heap_insert(pg_shadow_rel, tuple);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
|
|
|
/*
|
1999-12-16 18:24:19 +01:00
|
|
|
* Add the user to the groups specified. We'll just call the below
|
2000-04-12 19:17:23 +02:00
|
|
|
* AlterGroup for this.
|
1998-02-26 05:46:47 +01:00
|
|
|
*/
|
2001-07-11 00:09:29 +02:00
|
|
|
foreach(item, groupElts)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
AlterGroupStmt ags;
|
1999-12-16 18:24:19 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
ags.name = strVal(lfirst(item)); /* the group name to add
|
|
|
|
* this in */
|
|
|
|
ags.action = +1;
|
2001-07-11 00:09:29 +02:00
|
|
|
ags.listUsers = makeList1(makeInteger(sysid));
|
2000-04-12 19:17:23 +02:00
|
|
|
AlterGroup(&ags, "CREATE USER");
|
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
/*
|
2002-04-04 06:25:54 +02:00
|
|
|
* Now we can clean up; but keep lock until commit.
|
1999-09-18 21:08:25 +02:00
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
heap_close(pg_shadow_rel, NoLock);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
/*
|
2002-04-04 06:25:54 +02:00
|
|
|
* Write the updated pg_shadow and pg_group data to the flat file.
|
1998-02-26 05:46:47 +01:00
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
update_pg_pwd_and_pg_group(NULL);
|
1997-12-04 01:34:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-01-14 23:11:38 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ALTER USER
|
|
|
|
*/
|
2002-04-26 21:29:47 +02:00
|
|
|
void
|
2000-01-14 23:11:38 +01:00
|
|
|
AlterUser(AlterUserStmt *stmt)
|
1998-02-26 05:46:47 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
Datum new_record[Natts_pg_shadow];
|
|
|
|
char new_record_nulls[Natts_pg_shadow];
|
1998-02-26 05:46:47 +01:00
|
|
|
Relation pg_shadow_rel;
|
|
|
|
TupleDesc pg_shadow_dsc;
|
2000-04-12 19:17:23 +02:00
|
|
|
HeapTuple tuple,
|
|
|
|
new_tuple;
|
|
|
|
bool null;
|
2001-10-25 07:50:21 +02:00
|
|
|
List *option;
|
2001-10-28 07:26:15 +01:00
|
|
|
char *password = NULL; /* PostgreSQL user password */
|
2001-10-25 07:50:21 +02:00
|
|
|
bool encrypt_password = Password_encryption; /* encrypt password? */
|
|
|
|
char encrypted_password[MD5_PASSWD_LEN + 1];
|
|
|
|
int createdb = -1; /* Can the user create databases? */
|
2001-10-28 07:26:15 +01:00
|
|
|
int createuser = -1; /* Can this user create users? */
|
2001-10-25 07:50:21 +02:00
|
|
|
char *validUntil = NULL; /* The time the login is valid
|
|
|
|
* until */
|
2001-07-11 00:09:29 +02:00
|
|
|
DefElem *dpassword = NULL;
|
|
|
|
DefElem *dcreatedb = NULL;
|
|
|
|
DefElem *dcreateuser = NULL;
|
|
|
|
DefElem *dvalidUntil = NULL;
|
|
|
|
|
|
|
|
/* Extract options from the statement node tree */
|
2001-10-25 07:50:21 +02:00
|
|
|
foreach(option, stmt->options)
|
2001-07-11 00:09:29 +02:00
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
DefElem *defel = (DefElem *) lfirst(option);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2001-09-19 11:48:42 +02:00
|
|
|
if (strcmp(defel->defname, "password") == 0 ||
|
|
|
|
strcmp(defel->defname, "encryptedPassword") == 0 ||
|
2001-10-25 07:50:21 +02:00
|
|
|
strcmp(defel->defname, "unencryptedPassword") == 0)
|
|
|
|
{
|
2001-07-11 00:09:29 +02:00
|
|
|
if (dpassword)
|
|
|
|
elog(ERROR, "ALTER USER: conflicting options");
|
|
|
|
dpassword = defel;
|
2001-09-19 11:48:42 +02:00
|
|
|
if (strcmp(defel->defname, "encryptedPassword") == 0)
|
2001-08-15 20:42:16 +02:00
|
|
|
encrypt_password = true;
|
2001-09-19 11:48:42 +02:00
|
|
|
else if (strcmp(defel->defname, "unencryptedPassword") == 0)
|
2001-08-15 20:42:16 +02:00
|
|
|
encrypt_password = false;
|
2001-07-11 00:09:29 +02:00
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
else if (strcmp(defel->defname, "createdb") == 0)
|
|
|
|
{
|
2001-07-11 00:09:29 +02:00
|
|
|
if (dcreatedb)
|
|
|
|
elog(ERROR, "ALTER USER: conflicting options");
|
|
|
|
dcreatedb = defel;
|
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
else if (strcmp(defel->defname, "createuser") == 0)
|
|
|
|
{
|
2001-07-11 00:09:29 +02:00
|
|
|
if (dcreateuser)
|
|
|
|
elog(ERROR, "ALTER USER: conflicting options");
|
|
|
|
dcreateuser = defel;
|
2001-08-15 20:42:16 +02:00
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
else if (strcmp(defel->defname, "validUntil") == 0)
|
|
|
|
{
|
2001-07-11 00:09:29 +02:00
|
|
|
if (dvalidUntil)
|
|
|
|
elog(ERROR, "ALTER USER: conflicting options");
|
|
|
|
dvalidUntil = defel;
|
|
|
|
}
|
2001-08-15 20:42:16 +02:00
|
|
|
else
|
2001-10-25 07:50:21 +02:00
|
|
|
elog(ERROR, "ALTER USER: option \"%s\" not recognized",
|
2001-07-11 00:09:29 +02:00
|
|
|
defel->defname);
|
|
|
|
}
|
2001-08-15 20:42:16 +02:00
|
|
|
|
2001-07-11 00:09:29 +02:00
|
|
|
if (dcreatedb)
|
|
|
|
createdb = intVal(dcreatedb->arg);
|
|
|
|
if (dcreateuser)
|
|
|
|
createuser = intVal(dcreateuser->arg);
|
|
|
|
if (dvalidUntil)
|
|
|
|
validUntil = strVal(dvalidUntil->arg);
|
|
|
|
if (dpassword)
|
|
|
|
password = strVal(dpassword->arg);
|
2001-08-15 20:42:16 +02:00
|
|
|
|
|
|
|
if (password)
|
1998-02-26 05:46:47 +01:00
|
|
|
CheckPgUserAclNotNull();
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/* must be superuser or just want to change your own password */
|
|
|
|
if (!superuser() &&
|
2001-07-11 00:09:29 +02:00
|
|
|
!(createdb < 0 &&
|
|
|
|
createuser < 0 &&
|
|
|
|
!validUntil &&
|
|
|
|
password &&
|
2001-06-12 07:55:50 +02:00
|
|
|
strcmp(GetUserName(GetUserId()), stmt->user) == 0))
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "ALTER USER: permission denied");
|
2000-01-14 23:11:38 +01:00
|
|
|
|
2000-06-25 16:24:59 +02:00
|
|
|
/* changes to the flat password file cannot be rolled back */
|
2001-07-11 00:09:29 +02:00
|
|
|
if (IsTransactionBlock() && password)
|
2002-03-06 07:10:59 +01:00
|
|
|
elog(NOTICE, "ALTER USER: password changes cannot be rolled back");
|
1998-02-26 05:46:47 +01:00
|
|
|
|
|
|
|
/*
|
2000-04-12 19:17:23 +02:00
|
|
|
* Scan the pg_shadow relation to be certain the user exists. Note we
|
|
|
|
* secure exclusive lock to protect our update of the flat password
|
|
|
|
* file.
|
1998-02-26 05:46:47 +01:00
|
|
|
*/
|
2001-06-12 07:55:50 +02:00
|
|
|
pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
|
1998-09-01 05:29:17 +02:00
|
|
|
pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
tuple = SearchSysCache(SHADOWNAME,
|
|
|
|
PointerGetDatum(stmt->user),
|
|
|
|
0, 0, 0);
|
1998-08-19 04:04:17 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2000-01-14 23:11:38 +01:00
|
|
|
elog(ERROR, "ALTER USER: user \"%s\" does not exist", stmt->user);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Build a tuple to update, perusing the information just obtained
|
|
|
|
*/
|
2000-08-03 18:35:08 +02:00
|
|
|
new_record[Anum_pg_shadow_usename - 1] = DirectFunctionCall1(namein,
|
2001-03-22 05:01:46 +01:00
|
|
|
CStringGetDatum(stmt->user));
|
2000-06-12 05:41:03 +02:00
|
|
|
new_record_nulls[Anum_pg_shadow_usename - 1] = ' ';
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/* 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 */
|
2001-07-11 00:09:29 +02:00
|
|
|
if (createdb < 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
/* 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
|
|
|
|
{
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb > 0);
|
2000-04-12 19:17:23 +02:00
|
|
|
new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = ' ';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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' : ' ';
|
|
|
|
|
2001-09-08 17:24:00 +02:00
|
|
|
/*
|
|
|
|
* createuser (superuser) and catupd
|
|
|
|
*
|
2001-10-25 07:50:21 +02:00
|
|
|
* XXX It's rather unclear how to handle catupd. It's probably best to
|
|
|
|
* keep it equal to the superuser status, otherwise you could end up
|
|
|
|
* with a situation where no existing superuser can alter the
|
|
|
|
* catalogs, including pg_shadow!
|
2001-09-08 17:24:00 +02:00
|
|
|
*/
|
2001-07-11 00:09:29 +02:00
|
|
|
if (createuser < 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
/* 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' : ' ';
|
2001-09-08 17:24:00 +02:00
|
|
|
|
|
|
|
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' : ' ';
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser > 0);
|
2000-04-12 19:17:23 +02:00
|
|
|
new_record_nulls[Anum_pg_shadow_usesuper - 1] = ' ';
|
|
|
|
|
2001-09-08 17:24:00 +02:00
|
|
|
new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser > 0);
|
2000-04-12 19:17:23 +02:00
|
|
|
new_record_nulls[Anum_pg_shadow_usecatupd - 1] = ' ';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* password */
|
2001-07-11 00:09:29 +02:00
|
|
|
if (password)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2001-08-15 20:42:16 +02:00
|
|
|
if (!encrypt_password || isMD5(password))
|
|
|
|
new_record[Anum_pg_shadow_passwd - 1] =
|
|
|
|
DirectFunctionCall1(textin, CStringGetDatum(password));
|
|
|
|
else
|
|
|
|
{
|
2001-08-17 04:59:20 +02:00
|
|
|
if (!EncryptMD5(password, stmt->user, strlen(stmt->user),
|
2001-10-25 07:50:21 +02:00
|
|
|
encrypted_password))
|
2001-08-15 20:42:16 +02:00
|
|
|
elog(ERROR, "CREATE USER: password encryption failed");
|
|
|
|
new_record[Anum_pg_shadow_passwd - 1] =
|
|
|
|
DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
|
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
new_record_nulls[Anum_pg_shadow_passwd - 1] = ' ';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* leave as is */
|
2000-07-06 01:12:09 +02:00
|
|
|
new_record[Anum_pg_shadow_passwd - 1] =
|
|
|
|
heap_getattr(tuple, Anum_pg_shadow_passwd, pg_shadow_dsc, &null);
|
2000-04-12 19:17:23 +02:00
|
|
|
new_record_nulls[Anum_pg_shadow_passwd - 1] = null ? 'n' : ' ';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* valid until */
|
2001-07-11 00:09:29 +02:00
|
|
|
if (validUntil)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2000-06-09 03:11:16 +02:00
|
|
|
new_record[Anum_pg_shadow_valuntil - 1] =
|
2001-07-11 00:09:29 +02:00
|
|
|
DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
|
2000-04-12 19:17:23 +02:00
|
|
|
new_record_nulls[Anum_pg_shadow_valuntil - 1] = ' ';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* leave as is */
|
2000-07-06 01:12:09 +02:00
|
|
|
new_record[Anum_pg_shadow_valuntil - 1] =
|
|
|
|
heap_getattr(tuple, Anum_pg_shadow_valuntil, pg_shadow_dsc, &null);
|
2000-04-12 19:17:23 +02:00
|
|
|
new_record_nulls[Anum_pg_shadow_valuntil - 1] = null ? 'n' : ' ';
|
|
|
|
}
|
|
|
|
|
2002-03-01 23:45:19 +01:00
|
|
|
/* leave useconfig as is */
|
|
|
|
new_record[Anum_pg_shadow_useconfig - 1] =
|
|
|
|
heap_getattr(tuple, Anum_pg_shadow_useconfig, pg_shadow_dsc, &null);
|
|
|
|
new_record_nulls[Anum_pg_shadow_useconfig - 1] = null ? 'n' : ' ';
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
new_tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
|
2001-01-23 05:32:23 +01:00
|
|
|
simple_heap_update(pg_shadow_rel, &tuple->t_self, new_tuple);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/* 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,
|
2000-11-16 23:30:52 +01:00
|
|
|
new_tuple);
|
2000-04-12 19:17:23 +02:00
|
|
|
CatalogCloseIndices(Num_pg_shadow_indices, idescs);
|
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
heap_freetuple(new_tuple);
|
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
/*
|
2002-04-04 06:25:54 +02:00
|
|
|
* Now we can clean up.
|
1999-09-18 21:08:25 +02:00
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
heap_close(pg_shadow_rel, NoLock);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
/*
|
2002-04-04 06:25:54 +02:00
|
|
|
* Write the updated pg_shadow and pg_group data to the flat file.
|
1999-09-18 21:08:25 +02:00
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
update_pg_pwd_and_pg_group(NULL);
|
1997-12-04 01:34:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-01-14 23:11:38 +01:00
|
|
|
|
2002-03-01 23:45:19 +01:00
|
|
|
/*
|
|
|
|
* ALTER USER ... SET
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
AlterUserSet(AlterUserSetStmt *stmt)
|
|
|
|
{
|
|
|
|
char *valuestr;
|
|
|
|
HeapTuple oldtuple,
|
|
|
|
newtuple;
|
|
|
|
Relation rel;
|
|
|
|
Datum repl_val[Natts_pg_shadow];
|
|
|
|
char repl_null[Natts_pg_shadow];
|
|
|
|
char repl_repl[Natts_pg_shadow];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
valuestr = (stmt->value
|
|
|
|
? ((A_Const *) lfirst(stmt->value))->val.val.str
|
|
|
|
: NULL);
|
|
|
|
|
|
|
|
rel = heap_openr(ShadowRelationName, RowExclusiveLock);
|
|
|
|
oldtuple = SearchSysCache(SHADOWNAME,
|
|
|
|
PointerGetDatum(stmt->user),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(oldtuple))
|
|
|
|
elog(ERROR, "user \"%s\" does not exist", stmt->user);
|
|
|
|
|
|
|
|
if (!(superuser()
|
|
|
|
|| ((Form_pg_shadow) GETSTRUCT(oldtuple))->usesysid == GetUserId()))
|
|
|
|
elog(ERROR, "permission denied");
|
|
|
|
|
|
|
|
for (i = 0; i < Natts_pg_shadow; i++)
|
|
|
|
repl_repl[i] = ' ';
|
|
|
|
|
|
|
|
repl_repl[Anum_pg_shadow_useconfig-1] = 'r';
|
|
|
|
if (strcmp(stmt->variable, "all")==0 && stmt->value == NULL)
|
|
|
|
/* RESET ALL */
|
|
|
|
repl_null[Anum_pg_shadow_useconfig-1] = 'n';
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Datum datum;
|
|
|
|
bool isnull;
|
2002-04-04 06:25:54 +02:00
|
|
|
ArrayType *array;
|
2002-03-01 23:45:19 +01:00
|
|
|
|
|
|
|
repl_null[Anum_pg_shadow_useconfig-1] = ' ';
|
|
|
|
|
|
|
|
datum = SysCacheGetAttr(SHADOWNAME, oldtuple,
|
|
|
|
Anum_pg_shadow_useconfig, &isnull);
|
|
|
|
|
|
|
|
if (valuestr)
|
2002-04-04 06:25:54 +02:00
|
|
|
array = GUCArrayAdd(isnull
|
2002-03-01 23:45:19 +01:00
|
|
|
? NULL
|
|
|
|
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
|
|
|
|
stmt->variable, valuestr);
|
|
|
|
else
|
2002-04-04 06:25:54 +02:00
|
|
|
array = GUCArrayDelete(isnull
|
2002-03-01 23:45:19 +01:00
|
|
|
? NULL
|
|
|
|
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
|
|
|
|
stmt->variable);
|
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(array);
|
2002-03-01 23:45:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl);
|
|
|
|
simple_heap_update(rel, &oldtuple->t_self, newtuple);
|
|
|
|
|
|
|
|
{
|
|
|
|
Relation idescs[Num_pg_shadow_indices];
|
|
|
|
|
|
|
|
CatalogOpenIndices(Num_pg_shadow_indices, Name_pg_shadow_indices, idescs);
|
|
|
|
CatalogIndexInsert(idescs, Num_pg_shadow_indices, rel, newtuple);
|
|
|
|
CatalogCloseIndices(Num_pg_shadow_indices, idescs);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseSysCache(oldtuple);
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-01-14 23:11:38 +01:00
|
|
|
/*
|
|
|
|
* DROP USER
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
DropUser(DropUserStmt *stmt)
|
1998-02-26 05:46:47 +01:00
|
|
|
{
|
2000-01-14 23:11:38 +01:00
|
|
|
Relation pg_shadow_rel;
|
|
|
|
TupleDesc pg_shadow_dsc;
|
2000-04-12 19:17:23 +02:00
|
|
|
List *item;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!superuser())
|
|
|
|
elog(ERROR, "DROP USER: permission denied");
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-01-14 23:11:38 +01:00
|
|
|
if (IsTransactionBlock())
|
2002-03-06 07:10:59 +01:00
|
|
|
elog(NOTICE, "DROP USER cannot be rolled back completely");
|
1998-02-26 05:46:47 +01:00
|
|
|
|
|
|
|
/*
|
1999-09-18 21:08:25 +02:00
|
|
|
* Scan the pg_shadow relation to find the usesysid of the user to be
|
|
|
|
* deleted. Note we secure exclusive lock, because we need to protect
|
|
|
|
* our update of the flat password file.
|
1998-02-26 05:46:47 +01:00
|
|
|
*/
|
2001-06-12 07:55:50 +02:00
|
|
|
pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
|
2000-01-14 23:11:38 +01:00
|
|
|
pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
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));
|
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
tuple = SearchSysCache(SHADOWNAME,
|
|
|
|
PointerGetDatum(user),
|
|
|
|
0, 0, 0);
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "DROP USER: user \"%s\" does not exist%s", user,
|
|
|
|
(length(stmt->users) > 1) ? " (no users removed)" : "");
|
|
|
|
|
|
|
|
usesysid = DatumGetInt32(heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null));
|
|
|
|
|
2001-09-08 17:24:00 +02:00
|
|
|
if (usesysid == GetUserId())
|
|
|
|
elog(ERROR, "current user cannot be dropped");
|
|
|
|
if (usesysid == GetSessionUserId())
|
|
|
|
elog(ERROR, "session user cannot be dropped");
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-04-12 19:17:23 +02:00
|
|
|
* Check if user still owns a database. If so, error out.
|
|
|
|
*
|
2001-03-22 07:16:21 +01:00
|
|
|
* (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)
|
|
|
|
*/
|
2001-06-12 07:55:50 +02:00
|
|
|
pg_rel = heap_openr(DatabaseRelationName, AccessShareLock);
|
2000-04-12 19:17:23 +02:00
|
|
|
pg_dsc = RelationGetDescr(pg_rel);
|
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
ScanKeyEntryInitialize(&scankey, 0x0,
|
|
|
|
Anum_pg_database_datdba, F_INT4EQ,
|
2000-04-12 19:17:23 +02:00
|
|
|
Int32GetDatum(usesysid));
|
|
|
|
|
|
|
|
scan = heap_beginscan(pg_rel, false, SnapshotNow, 1, &scankey);
|
|
|
|
|
|
|
|
if (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
char *dbname;
|
2001-06-12 07:55:50 +02:00
|
|
|
|
|
|
|
datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
|
|
|
|
pg_dsc, &null);
|
|
|
|
Assert(!null);
|
2002-04-18 23:16:16 +02:00
|
|
|
dbname = NameStr(*DatumGetName(datum));
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
|
2001-06-12 07:55:50 +02:00
|
|
|
user, dbname,
|
|
|
|
(length(stmt->users) > 1) ? " (no users removed)" : "");
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
heap_endscan(scan);
|
2001-06-12 07:55:50 +02:00
|
|
|
heap_close(pg_rel, AccessShareLock);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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).
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove the user from the pg_shadow table
|
|
|
|
*/
|
2001-01-23 05:32:23 +01:00
|
|
|
simple_heap_delete(pg_shadow_rel, &tuple->t_self);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Remove user from groups
|
|
|
|
*
|
|
|
|
* try calling alter group drop user for every group
|
|
|
|
*/
|
2001-06-12 07:55:50 +02:00
|
|
|
pg_rel = heap_openr(GroupRelationName, ExclusiveLock);
|
2000-04-12 19:17:23 +02:00
|
|
|
pg_dsc = RelationGetDescr(pg_rel);
|
|
|
|
scan = heap_beginscan(pg_rel, false, SnapshotNow, 0, NULL);
|
|
|
|
while (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
|
|
|
|
{
|
|
|
|
AlterGroupStmt ags;
|
|
|
|
|
2000-08-03 18:35:08 +02:00
|
|
|
/* the group name from which to try to drop the user: */
|
2000-04-12 19:17:23 +02:00
|
|
|
datum = heap_getattr(tmp_tuple, Anum_pg_group_groname, pg_dsc, &null);
|
2000-08-03 18:35:08 +02:00
|
|
|
ags.name = DatumGetCString(DirectFunctionCall1(nameout, datum));
|
2000-04-12 19:17:23 +02:00
|
|
|
ags.action = -1;
|
2001-01-17 18:26:45 +01:00
|
|
|
ags.listUsers = makeList1(makeInteger(usesysid));
|
2000-04-12 19:17:23 +02:00
|
|
|
AlterGroup(&ags, "DROP USER");
|
|
|
|
}
|
|
|
|
heap_endscan(scan);
|
2001-06-12 07:55:50 +02:00
|
|
|
heap_close(pg_rel, ExclusiveLock);
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2000-10-19 05:55:51 +02:00
|
|
|
/*
|
|
|
|
* Advance command counter so that later iterations of this loop
|
|
|
|
* will see the changes already made. This is essential if, for
|
|
|
|
* example, we are trying to drop two users who are members of the
|
|
|
|
* same group --- the AlterGroup for the second user had better
|
|
|
|
* see the tuple updated from the first one.
|
|
|
|
*/
|
|
|
|
CommandCounterIncrement();
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
/*
|
2002-04-04 06:25:54 +02:00
|
|
|
* Now we can clean up.
|
1999-09-18 21:08:25 +02:00
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
heap_close(pg_shadow_rel, NoLock);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2002-04-04 06:25:54 +02:00
|
|
|
* Write the updated pg_shadow and pg_group data to the flat file.
|
2000-04-12 19:17:23 +02:00
|
|
|
*/
|
2002-04-04 06:25:54 +02:00
|
|
|
update_pg_pwd_and_pg_group(NULL);
|
1997-12-04 01:34:01 +01:00
|
|
|
}
|
1998-02-19 18:20:01 +01:00
|
|
|
|
2000-01-14 23:11:38 +01:00
|
|
|
|
|
|
|
|
1998-02-19 18:20:01 +01:00
|
|
|
/*
|
|
|
|
* CheckPgUserAclNotNull
|
|
|
|
*
|
1998-02-25 14:09:49 +01:00
|
|
|
* check to see if there is an ACL on pg_shadow
|
1998-02-19 18:20:01 +01:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
static void
|
2002-03-26 20:17:02 +01:00
|
|
|
CheckPgUserAclNotNull(void)
|
1998-02-19 18:20:01 +01:00
|
|
|
{
|
1998-08-19 04:04:17 +02:00
|
|
|
HeapTuple htup;
|
1998-02-19 18:20:01 +01:00
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
htup = SearchSysCache(RELOID,
|
|
|
|
ObjectIdGetDatum(RelOid_pg_shadow),
|
2000-11-16 23:30:52 +01:00
|
|
|
0, 0, 0);
|
1998-08-19 04:04:17 +02:00
|
|
|
if (!HeapTupleIsValid(htup))
|
2000-11-16 23:30:52 +01:00
|
|
|
elog(ERROR, "CheckPgUserAclNotNull: \"%s\" not found",
|
1998-02-25 14:09:49 +01:00
|
|
|
ShadowRelationName);
|
1998-02-19 18:20:01 +01:00
|
|
|
|
1998-08-19 04:04:17 +02:00
|
|
|
if (heap_attisnull(htup, Anum_pg_class_relacl))
|
2000-01-14 23:11:38 +01:00
|
|
|
elog(ERROR,
|
2000-04-12 19:17:23 +02:00
|
|
|
"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);
|
2000-11-16 23:30:52 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(htup);
|
1998-02-19 18:20:01 +01:00
|
|
|
}
|
1999-12-16 18:24:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2000-01-14 23:11:38 +01:00
|
|
|
/*
|
|
|
|
* CREATE GROUP
|
|
|
|
*/
|
1999-12-16 18:24:19 +01:00
|
|
|
void
|
2000-01-14 23:11:38 +01:00
|
|
|
CreateGroup(CreateGroupStmt *stmt)
|
1999-12-16 18:24:19 +01:00
|
|
|
{
|
|
|
|
Relation pg_group_rel;
|
|
|
|
HeapScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
2000-04-12 19:17:23 +02:00
|
|
|
TupleDesc pg_group_dsc;
|
|
|
|
bool group_exists = false,
|
2001-07-12 20:03:00 +02:00
|
|
|
sysid_exists = false,
|
|
|
|
havesysid = false;
|
2001-09-08 17:24:00 +02:00
|
|
|
int max_id;
|
2000-04-12 19:17:23 +02:00
|
|
|
Datum new_record[Natts_pg_group];
|
|
|
|
char new_record_nulls[Natts_pg_group];
|
|
|
|
List *item,
|
2001-08-15 20:42:16 +02:00
|
|
|
*option,
|
2001-07-12 20:03:00 +02:00
|
|
|
*newlist = NIL;
|
2000-04-12 19:17:23 +02:00
|
|
|
ArrayType *userarray;
|
2001-08-15 20:42:16 +02:00
|
|
|
int sysid = 0;
|
|
|
|
List *userElts = NIL;
|
|
|
|
DefElem *dsysid = NULL;
|
|
|
|
DefElem *duserElts = NULL;
|
2001-07-12 20:03:00 +02:00
|
|
|
|
|
|
|
foreach(option, stmt->options)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
DefElem *defel = (DefElem *) lfirst(option);
|
2001-07-12 20:03:00 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
if (strcmp(defel->defname, "sysid") == 0)
|
|
|
|
{
|
2001-08-15 20:42:16 +02:00
|
|
|
if (dsysid)
|
|
|
|
elog(ERROR, "CREATE GROUP: conflicting options");
|
|
|
|
dsysid = defel;
|
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
else if (strcmp(defel->defname, "userElts") == 0)
|
|
|
|
{
|
2001-08-15 20:42:16 +02:00
|
|
|
if (duserElts)
|
|
|
|
elog(ERROR, "CREATE GROUP: conflicting options");
|
|
|
|
duserElts = defel;
|
|
|
|
}
|
|
|
|
else
|
2001-10-25 07:50:21 +02:00
|
|
|
elog(ERROR, "CREATE GROUP: option \"%s\" not recognized",
|
2001-08-15 20:42:16 +02:00
|
|
|
defel->defname);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dsysid)
|
2001-07-12 20:03:00 +02:00
|
|
|
{
|
|
|
|
sysid = intVal(dsysid->arg);
|
2001-09-08 17:24:00 +02:00
|
|
|
if (sysid <= 0)
|
|
|
|
elog(ERROR, "group id must be positive");
|
2001-07-12 20:03:00 +02:00
|
|
|
havesysid = true;
|
|
|
|
}
|
|
|
|
|
2001-08-15 20:42:16 +02:00
|
|
|
if (duserElts)
|
2001-07-12 20:03:00 +02:00
|
|
|
userElts = (List *) duserElts->arg;
|
1999-12-16 18:24:19 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the user can do this.
|
|
|
|
*/
|
2000-01-14 23:11:38 +01:00
|
|
|
if (!superuser())
|
|
|
|
elog(ERROR, "CREATE GROUP: permission denied");
|
|
|
|
|
2002-04-18 23:16:16 +02:00
|
|
|
if (strcmp(stmt->name, "public") == 0)
|
|
|
|
elog(ERROR, "CREATE GROUP: group name \"%s\" is reserved",
|
|
|
|
stmt->name);
|
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
|
1999-12-16 18:24:19 +01:00
|
|
|
pg_group_dsc = RelationGetDescr(pg_group_rel);
|
|
|
|
|
|
|
|
scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
|
2001-09-08 17:24:00 +02:00
|
|
|
max_id = 99; /* start auto-assigned ids at 100 */
|
2001-06-12 07:55:50 +02:00
|
|
|
while (!group_exists && !sysid_exists &&
|
|
|
|
HeapTupleIsValid(tuple = heap_getnext(scan, false)))
|
1999-12-16 18:24:19 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
Datum datum;
|
|
|
|
bool null;
|
1999-12-16 18:24:19 +01:00
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
datum = heap_getattr(tuple, Anum_pg_group_groname,
|
|
|
|
pg_group_dsc, &null);
|
|
|
|
Assert(!null);
|
2002-04-18 23:16:16 +02:00
|
|
|
group_exists = (strcmp(NameStr(*DatumGetName(datum)), stmt->name) == 0);
|
1999-12-16 18:24:19 +01:00
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
datum = heap_getattr(tuple, Anum_pg_group_grosysid,
|
|
|
|
pg_group_dsc, &null);
|
|
|
|
Assert(!null);
|
2001-10-25 07:50:21 +02:00
|
|
|
if (havesysid) /* customized id wanted */
|
2001-07-12 20:03:00 +02:00
|
|
|
sysid_exists = (DatumGetInt32(datum) == sysid);
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
|
|
|
{
|
2001-06-12 07:55:50 +02:00
|
|
|
/* pick 1 + max */
|
|
|
|
if (DatumGetInt32(datum) > max_id)
|
|
|
|
max_id = DatumGetInt32(datum);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1999-12-16 18:24:19 +01:00
|
|
|
}
|
|
|
|
heap_endscan(scan);
|
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
if (group_exists)
|
|
|
|
elog(ERROR, "CREATE GROUP: group name \"%s\" already exists",
|
|
|
|
stmt->name);
|
|
|
|
if (sysid_exists)
|
|
|
|
elog(ERROR, "CREATE GROUP: group sysid %d is already assigned",
|
2001-07-12 20:03:00 +02:00
|
|
|
sysid);
|
1999-12-16 18:24:19 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Translate the given user names to ids
|
|
|
|
*/
|
2001-07-12 20:03:00 +02:00
|
|
|
foreach(item, userElts)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
const char *groupuser = strVal(lfirst(item));
|
|
|
|
Value *v;
|
|
|
|
|
2001-06-14 03:09:22 +02:00
|
|
|
v = makeInteger(get_usesysid(groupuser));
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!member(v, newlist))
|
2001-07-12 20:03:00 +02:00
|
|
|
newlist = lappend(newlist, v);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* build an array to insert */
|
|
|
|
if (newlist)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
userarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
|
2000-07-22 06:16:13 +02:00
|
|
|
userarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
|
|
|
|
userarray->flags = 0;
|
2001-10-28 07:26:15 +01:00
|
|
|
ARR_NDIM(userarray) = 1; /* one dimensional array */
|
2000-04-12 19:17:23 +02:00
|
|
|
ARR_LBOUND(userarray)[0] = 1; /* axis starts at one */
|
|
|
|
ARR_DIMS(userarray)[0] = length(newlist); /* axis is this long */
|
|
|
|
/* fill the array */
|
|
|
|
i = 0;
|
|
|
|
foreach(item, newlist)
|
|
|
|
((int *) ARR_DATA_PTR(userarray))[i++] = intVal(lfirst(item));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
userarray = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Form a tuple to insert
|
|
|
|
*/
|
2001-07-12 20:03:00 +02:00
|
|
|
if (!havesysid)
|
|
|
|
sysid = max_id + 1;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record[Anum_pg_group_groname - 1] =
|
|
|
|
DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
|
2001-07-12 20:03:00 +02:00
|
|
|
new_record[Anum_pg_group_grosysid - 1] = Int32GetDatum(sysid);
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(userarray);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
new_record_nulls[Anum_pg_group_groname - 1] = ' ';
|
|
|
|
new_record_nulls[Anum_pg_group_grosysid - 1] = ' ';
|
|
|
|
new_record_nulls[Anum_pg_group_grolist - 1] = userarray ? ' ' : 'n';
|
|
|
|
|
|
|
|
tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert a new record in the pg_group_table
|
|
|
|
*/
|
|
|
|
heap_insert(pg_group_rel, tuple);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
1999-12-16 18:24:19 +01:00
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
heap_close(pg_group_rel, NoLock);
|
2002-04-04 06:25:54 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the updated pg_shadow and pg_group data to the flat file.
|
|
|
|
*/
|
|
|
|
update_pg_pwd_and_pg_group(NULL);
|
1999-12-16 18:24:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-01-14 23:11:38 +01:00
|
|
|
/*
|
|
|
|
* ALTER GROUP
|
|
|
|
*/
|
1999-12-16 18:24:19 +01:00
|
|
|
void
|
2000-04-12 19:17:23 +02:00
|
|
|
AlterGroup(AlterGroupStmt *stmt, const char *tag)
|
1999-12-16 18:24:19 +01:00
|
|
|
{
|
|
|
|
Relation pg_group_rel;
|
2000-04-12 19:17:23 +02:00
|
|
|
TupleDesc pg_group_dsc;
|
|
|
|
HeapTuple group_tuple;
|
1999-12-16 18:24:19 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
1999-12-16 18:24:19 +01:00
|
|
|
* Make sure the user can do this.
|
|
|
|
*/
|
2000-01-14 23:11:38 +01:00
|
|
|
if (!superuser())
|
|
|
|
elog(ERROR, "%s: permission denied", tag);
|
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
|
2000-04-12 19:17:23 +02:00
|
|
|
pg_group_dsc = RelationGetDescr(pg_group_rel);
|
1999-12-16 18:24:19 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2000-11-16 23:30:52 +01:00
|
|
|
* Fetch existing tuple for group.
|
2000-04-12 19:17:23 +02:00
|
|
|
*/
|
2000-11-16 23:30:52 +01:00
|
|
|
group_tuple = SearchSysCache(GRONAME,
|
|
|
|
PointerGetDatum(stmt->name),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(group_tuple))
|
2000-01-14 23:11:38 +01:00
|
|
|
elog(ERROR, "%s: group \"%s\" does not exist", tag, stmt->name);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now decide what to do.
|
|
|
|
*/
|
2000-11-16 23:30:52 +01:00
|
|
|
AssertState(stmt->action == +1 || stmt->action == -1);
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (stmt->action == +1) /* add users, might also be invoked by
|
|
|
|
* create user */
|
|
|
|
{
|
|
|
|
Datum new_record[Natts_pg_group];
|
2001-07-11 00:09:29 +02:00
|
|
|
char new_record_nulls[Natts_pg_group];
|
2000-04-12 19:17:23 +02:00
|
|
|
ArrayType *newarray,
|
|
|
|
*oldarray;
|
2001-07-12 20:03:00 +02:00
|
|
|
List *newlist = NIL,
|
2000-04-12 19:17:23 +02:00
|
|
|
*item;
|
|
|
|
HeapTuple tuple;
|
|
|
|
bool null = false;
|
|
|
|
Datum datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
oldarray = (ArrayType *) datum;
|
|
|
|
Assert(null || ARR_NDIM(oldarray) == 1);
|
|
|
|
/* first add the old array to the hitherto empty list */
|
|
|
|
if (!null)
|
|
|
|
for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
|
|
|
|
{
|
|
|
|
int index,
|
|
|
|
arrval;
|
|
|
|
Value *v;
|
|
|
|
bool valueNull;
|
|
|
|
|
|
|
|
index = i;
|
|
|
|
arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true /* by value */ ,
|
|
|
|
sizeof(int), 0, &valueNull));
|
|
|
|
v = makeInteger(arrval);
|
|
|
|
/* filter out duplicates */
|
|
|
|
if (!member(v, newlist))
|
2001-07-12 20:03:00 +02:00
|
|
|
newlist = lappend(newlist, v);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* now convert the to be added usernames to sysids and add them to
|
|
|
|
* the list
|
|
|
|
*/
|
|
|
|
foreach(item, stmt->listUsers)
|
|
|
|
{
|
|
|
|
Value *v;
|
|
|
|
|
|
|
|
if (strcmp(tag, "ALTER GROUP") == 0)
|
|
|
|
{
|
|
|
|
/* Get the uid of the proposed user to add. */
|
2001-06-14 03:09:22 +02:00
|
|
|
v = makeInteger(get_usesysid(strVal(lfirst(item))));
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
else if (strcmp(tag, "CREATE USER") == 0)
|
2000-02-15 19:17:33 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* 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);
|
2000-02-15 19:17:33 +01:00
|
|
|
v = NULL; /* keep compiler quiet */
|
|
|
|
}
|
2000-01-14 23:11:38 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!member(v, newlist))
|
2001-07-12 20:03:00 +02:00
|
|
|
newlist = lappend(newlist, v);
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
|
|
|
/*
|
|
|
|
* we silently assume here that this error will only come
|
|
|
|
* up in a ALTER GROUP statement
|
|
|
|
*/
|
2002-03-06 07:10:59 +01:00
|
|
|
elog(WARNING, "%s: user \"%s\" is already in group \"%s\"",
|
2001-06-14 03:09:22 +02:00
|
|
|
tag, strVal(lfirst(item)), stmt->name);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
|
2000-07-22 06:16:13 +02:00
|
|
|
newarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
|
|
|
|
newarray->flags = 0;
|
2000-04-12 19:17:23 +02:00
|
|
|
ARR_NDIM(newarray) = 1; /* one dimensional array */
|
|
|
|
ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
|
|
|
|
ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
|
|
|
|
/* fill the array */
|
|
|
|
i = 0;
|
|
|
|
foreach(item, newlist)
|
|
|
|
((int *) ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Form a tuple with the new array and write it back.
|
|
|
|
*/
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record[Anum_pg_group_groname - 1] =
|
|
|
|
DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
|
|
|
|
new_record_nulls[Anum_pg_group_groname - 1] = ' ';
|
2000-04-12 19:17:23 +02:00
|
|
|
new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record_nulls[Anum_pg_group_grosysid - 1] = null ? 'n' : ' ';
|
2000-04-12 19:17:23 +02:00
|
|
|
new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record_nulls[Anum_pg_group_grolist - 1] = newarray ? ' ' : 'n';
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
|
2001-01-23 05:32:23 +01:00
|
|
|
simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
} /* endif alter group add user */
|
|
|
|
|
2001-10-28 07:26:15 +01:00
|
|
|
else if (stmt->action == -1) /* drop users from group */
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
if (!is_dropuser)
|
2002-03-06 07:10:59 +01:00
|
|
|
elog(WARNING, "ALTER GROUP: group \"%s\" does not have any members", stmt->name);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Datum new_record[Natts_pg_group];
|
2001-07-11 00:09:29 +02:00
|
|
|
char new_record_nulls[Natts_pg_group];
|
2000-04-12 19:17:23 +02:00
|
|
|
ArrayType *oldarray,
|
|
|
|
*newarray;
|
2001-07-12 20:03:00 +02:00
|
|
|
List *newlist = NIL,
|
2000-04-12 19:17:23 +02:00
|
|
|
*item;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
oldarray = (ArrayType *) datum;
|
|
|
|
Assert(ARR_NDIM(oldarray) == 1);
|
|
|
|
/* first add the old array to the hitherto empty list */
|
|
|
|
for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
|
|
|
|
{
|
|
|
|
int index,
|
|
|
|
arrval;
|
|
|
|
Value *v;
|
|
|
|
bool valueNull;
|
|
|
|
|
|
|
|
index = i;
|
|
|
|
arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true /* by value */ ,
|
|
|
|
sizeof(int), 0, &valueNull));
|
|
|
|
v = makeInteger(arrval);
|
|
|
|
/* filter out duplicates */
|
|
|
|
if (!member(v, newlist))
|
2001-07-12 20:03:00 +02:00
|
|
|
newlist = lappend(newlist, v);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* now convert the to be dropped usernames to sysids and
|
|
|
|
* remove them from the list
|
|
|
|
*/
|
|
|
|
foreach(item, stmt->listUsers)
|
|
|
|
{
|
|
|
|
Value *v;
|
|
|
|
|
|
|
|
if (!is_dropuser)
|
|
|
|
{
|
|
|
|
/* Get the uid of the proposed user to drop. */
|
2001-06-14 03:09:22 +02:00
|
|
|
v = makeInteger(get_usesysid(strVal(lfirst(item))));
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* for dropuser we already know the uid */
|
|
|
|
v = lfirst(item);
|
|
|
|
}
|
|
|
|
if (member(v, newlist))
|
|
|
|
newlist = LispRemove(v, newlist);
|
|
|
|
else if (!is_dropuser)
|
2002-03-06 07:10:59 +01:00
|
|
|
elog(WARNING, "ALTER GROUP: user \"%s\" is not in group \"%s\"", strVal(lfirst(item)), stmt->name);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
|
2000-07-22 06:16:13 +02:00
|
|
|
newarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
|
|
|
|
newarray->flags = 0;
|
2000-04-12 19:17:23 +02:00
|
|
|
ARR_NDIM(newarray) = 1; /* one dimensional array */
|
|
|
|
ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
|
|
|
|
ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
|
|
|
|
/* fill the array */
|
|
|
|
i = 0;
|
|
|
|
foreach(item, newlist)
|
|
|
|
((int *) ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert the new tuple with the updated user list
|
|
|
|
*/
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record[Anum_pg_group_groname - 1] =
|
|
|
|
DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
|
|
|
|
new_record_nulls[Anum_pg_group_groname - 1] = ' ';
|
2000-04-12 19:17:23 +02:00
|
|
|
new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record_nulls[Anum_pg_group_grosysid - 1] = null ? 'n' : ' ';
|
2000-04-12 19:17:23 +02:00
|
|
|
new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
|
2001-07-11 00:09:29 +02:00
|
|
|
new_record_nulls[Anum_pg_group_grolist - 1] = newarray ? ' ' : 'n';
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
|
2001-01-23 05:32:23 +01:00
|
|
|
simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
} /* endif group not null */
|
|
|
|
} /* endif alter group drop user */
|
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(group_tuple);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2002-04-04 06:25:54 +02:00
|
|
|
/*
|
|
|
|
* Write the updated pg_shadow and pg_group data to the flat files.
|
|
|
|
*/
|
2001-06-12 07:55:50 +02:00
|
|
|
heap_close(pg_group_rel, NoLock);
|
2002-04-04 06:25:54 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the updated pg_shadow and pg_group data to the flat file.
|
|
|
|
*/
|
|
|
|
update_pg_pwd_and_pg_group(NULL);
|
1999-12-16 18:24:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-01-14 23:11:38 +01:00
|
|
|
/*
|
|
|
|
* DROP GROUP
|
|
|
|
*/
|
1999-12-16 18:24:19 +01:00
|
|
|
void
|
2000-01-14 23:11:38 +01:00
|
|
|
DropGroup(DropGroupStmt *stmt)
|
1999-12-16 18:24:19 +01:00
|
|
|
{
|
|
|
|
Relation pg_group_rel;
|
|
|
|
HeapScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
2000-04-12 19:17:23 +02:00
|
|
|
TupleDesc pg_group_dsc;
|
|
|
|
bool gro_exists = false;
|
1999-12-16 18:24:19 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
1999-12-16 18:24:19 +01:00
|
|
|
* Make sure the user can do this.
|
|
|
|
*/
|
2000-01-14 23:11:38 +01:00
|
|
|
if (!superuser())
|
|
|
|
elog(ERROR, "DROP GROUP: permission denied");
|
1999-12-16 18:24:19 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Scan the pg_group table and delete all matching groups.
|
|
|
|
*/
|
2001-06-12 07:55:50 +02:00
|
|
|
pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
|
1999-12-16 18:24:19 +01:00
|
|
|
pg_group_dsc = RelationGetDescr(pg_group_rel);
|
|
|
|
scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
|
|
|
|
|
|
|
|
while (HeapTupleIsValid(tuple = heap_getnext(scan, false)))
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
Datum datum;
|
|
|
|
bool null;
|
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
datum = heap_getattr(tuple, Anum_pg_group_groname,
|
|
|
|
pg_group_dsc, &null);
|
2002-04-18 23:16:16 +02:00
|
|
|
if (!null && strcmp(NameStr(*DatumGetName(datum)), stmt->name) == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
gro_exists = true;
|
2001-01-23 05:32:23 +01:00
|
|
|
simple_heap_delete(pg_group_rel, &tuple->t_self);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1999-12-16 18:24:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
heap_endscan(scan);
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Did we find any?
|
|
|
|
*/
|
|
|
|
if (!gro_exists)
|
2000-01-14 23:11:38 +01:00
|
|
|
elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name);
|
1999-12-16 18:24:19 +01:00
|
|
|
|
2001-06-12 07:55:50 +02:00
|
|
|
heap_close(pg_group_rel, NoLock);
|
2002-04-04 06:25:54 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the updated pg_shadow and pg_group data to the flat file.
|
|
|
|
*/
|
|
|
|
update_pg_pwd_and_pg_group(NULL);
|
1999-12-16 18:24:19 +01:00
|
|
|
}
|