diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2005-06-28 05:09:14 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2005-06-28 05:09:14 +0000 |
commit | 7762619e95272974f90a38d8d85aafbe0e94add5 (patch) | |
tree | d7f756687beb883406489d59d13f722995fd7660 /src/backend/commands | |
parent | 977530d8da2683dff036c2994395ab518527b93e (diff) | |
download | postgresql-7762619e95272974f90a38d8d85aafbe0e94add5.tar.gz postgresql-7762619e95272974f90a38d8d85aafbe0e94add5.zip |
Replace pg_shadow and pg_group by new role-capable catalogs pg_authid
and pg_auth_members. There are still many loose ends to finish in this
patch (no documentation, no regression tests, no pg_dump support for
instance). But I'm going to commit it now anyway so that Alvaro can
make some progress on shared dependencies. The catalog changes should
be pretty much done.
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/aggregatecmds.c | 8 | ||||
-rw-r--r-- | src/backend/commands/alter.c | 16 | ||||
-rw-r--r-- | src/backend/commands/conversioncmds.c | 8 | ||||
-rw-r--r-- | src/backend/commands/copy.c | 3 | ||||
-rw-r--r-- | src/backend/commands/dbcommands.c | 50 | ||||
-rw-r--r-- | src/backend/commands/functioncmds.c | 10 | ||||
-rw-r--r-- | src/backend/commands/opclasscmds.c | 10 | ||||
-rw-r--r-- | src/backend/commands/operatorcmds.c | 8 | ||||
-rw-r--r-- | src/backend/commands/schemacmds.c | 33 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 28 | ||||
-rw-r--r-- | src/backend/commands/tablespace.c | 30 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 8 | ||||
-rw-r--r-- | src/backend/commands/user.c | 1335 | ||||
-rw-r--r-- | src/backend/commands/variable.c | 56 |
14 files changed, 711 insertions, 892 deletions
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index 9833937b705..6335757981e 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.26 2005/04/14 20:03:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.27 2005/06/28 05:08:53 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -295,7 +295,7 @@ RenameAggregate(List *name, TypeName *basetype, const char *newname) * Change aggregate owner */ void -AlterAggregateOwner(List *name, TypeName *basetype, AclId newOwnerSysId) +AlterAggregateOwner(List *name, TypeName *basetype, Oid newOwnerId) { Oid basetypeOid; Oid procOid; @@ -329,7 +329,7 @@ AlterAggregateOwner(List *name, TypeName *basetype, AclId newOwnerSysId) * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ - if (procForm->proowner != newOwnerSysId) + if (procForm->proowner != newOwnerId) { /* Otherwise, must be superuser to change object ownership */ if (!superuser()) @@ -341,7 +341,7 @@ AlterAggregateOwner(List *name, TypeName *basetype, AclId newOwnerSysId) * Modify the owner --- okay to scribble on tup because it's a * copy */ - procForm->proowner = newOwnerSysId; + procForm->proowner = newOwnerId; simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 9bb40f35164..a19b500152d 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.12 2004/12/31 21:59:41 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.13 2005/06/28 05:08:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -64,10 +64,6 @@ ExecRenameStmt(RenameStmt *stmt) RenameFunction(stmt->object, stmt->objarg, stmt->newname); break; - case OBJECT_GROUP: - RenameGroup(stmt->subname, stmt->newname); - break; - case OBJECT_LANGUAGE: RenameLanguage(stmt->subname, stmt->newname); break; @@ -76,6 +72,10 @@ ExecRenameStmt(RenameStmt *stmt) RenameOpClass(stmt->object, stmt->subname, stmt->newname); break; + case OBJECT_ROLE: + RenameRole(stmt->subname, stmt->newname); + break; + case OBJECT_SCHEMA: RenameSchema(stmt->subname, stmt->newname); break; @@ -84,10 +84,6 @@ ExecRenameStmt(RenameStmt *stmt) RenameTableSpace(stmt->subname, stmt->newname); break; - case OBJECT_USER: - RenameUser(stmt->subname, stmt->newname); - break; - case OBJECT_TABLE: case OBJECT_INDEX: case OBJECT_COLUMN: @@ -153,7 +149,7 @@ ExecRenameStmt(RenameStmt *stmt) void ExecAlterOwnerStmt(AlterOwnerStmt *stmt) { - AclId newowner = get_usesysid(stmt->newowner); + Oid newowner = get_roleid_checked(stmt->newowner); switch (stmt->objectType) { diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c index fb71bf59f92..c2aa48614e6 100644 --- a/src/backend/commands/conversioncmds.c +++ b/src/backend/commands/conversioncmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.18 2005/05/03 19:17:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.19 2005/06/28 05:08:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -175,7 +175,7 @@ RenameConversion(List *name, const char *newname) * Change conversion owner */ void -AlterConversionOwner(List *name, AclId newOwnerSysId) +AlterConversionOwner(List *name, Oid newOwnerId) { Oid conversionOid; HeapTuple tup; @@ -203,7 +203,7 @@ AlterConversionOwner(List *name, AclId newOwnerSysId) * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ - if (convForm->conowner != newOwnerSysId) + if (convForm->conowner != newOwnerId) { /* Otherwise, must be superuser to change object ownership */ if (!superuser()) @@ -215,7 +215,7 @@ AlterConversionOwner(List *name, AclId newOwnerSysId) * Modify the owner --- okay to scribble on tup because it's a * copy */ - convForm->conowner = newOwnerSysId; + convForm->conowner = newOwnerId; simple_heap_update(rel, &tup->t_self, tup); diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 30eeefded8e..10e68684b8e 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.245 2005/06/02 01:21:22 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.246 2005/06/28 05:08:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,7 +25,6 @@ #include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_index.h" -#include "catalog/pg_shadow.h" #include "catalog/pg_type.h" #include "commands/copy.h" #include "commands/trigger.h" diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 6fa6aa5b44b..1dac14ead2e 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.161 2005/06/25 22:47:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.162 2005/06/28 05:08:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,10 +28,10 @@ #include "access/genam.h" #include "access/heapam.h" #include "catalog/catalog.h" +#include "catalog/indexing.h" +#include "catalog/pg_authid.h" #include "catalog/pg_database.h" -#include "catalog/pg_shadow.h" #include "catalog/pg_tablespace.h" -#include "catalog/indexing.h" #include "commands/comment.h" #include "commands/dbcommands.h" #include "commands/tablespace.h" @@ -52,7 +52,7 @@ /* non-export function prototypes */ -static bool get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP, +static bool get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP, int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP, Oid *dbLastSysOidP, TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP, @@ -70,7 +70,7 @@ createdb(const CreatedbStmt *stmt) HeapScanDesc scan; Relation rel; Oid src_dboid; - AclId src_owner; + Oid src_owner; int src_encoding; bool src_istemplate; bool src_allowconn; @@ -85,7 +85,7 @@ createdb(const CreatedbStmt *stmt) Datum new_record[Natts_pg_database]; char new_record_nulls[Natts_pg_database]; Oid dboid; - AclId datdba; + Oid datdba; ListCell *option; DefElem *dtablespacename = NULL; DefElem *downer = NULL; @@ -186,13 +186,13 @@ createdb(const CreatedbStmt *stmt) nodeTag(dencoding->arg)); } - /* obtain sysid of proposed owner */ + /* obtain OID of proposed owner */ if (dbowner) - datdba = get_usesysid(dbowner); /* will ereport if no such user */ + datdba = get_roleid_checked(dbowner); else datdba = GetUserId(); - if (datdba == GetUserId()) + if (is_member_of_role(GetUserId(), datdba)) { /* creating database for self: can be superuser or createdb */ if (!superuser() && !have_createdb_privilege()) @@ -243,7 +243,7 @@ createdb(const CreatedbStmt *stmt) */ if (!src_istemplate) { - if (!superuser() && GetUserId() != src_owner) + if (!pg_database_ownercheck(src_dboid, GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to copy database \"%s\"", @@ -483,7 +483,7 @@ createdb(const CreatedbStmt *stmt) new_record[Anum_pg_database_datname - 1] = DirectFunctionCall1(namein, CStringGetDatum(dbname)); - new_record[Anum_pg_database_datdba - 1] = Int32GetDatum(datdba); + new_record[Anum_pg_database_datdba - 1] = ObjectIdGetDatum(datdba); new_record[Anum_pg_database_encoding - 1] = Int32GetDatum(encoding); new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(false); new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true); @@ -557,9 +557,8 @@ createdb(const CreatedbStmt *stmt) void dropdb(const char *dbname) { - int4 db_owner; - bool db_istemplate; Oid db_id; + bool db_istemplate; Relation pgdbrel; SysScanDesc pgdbscan; ScanKeyData key; @@ -588,13 +587,13 @@ dropdb(const char *dbname) */ pgdbrel = heap_open(DatabaseRelationId, ExclusiveLock); - if (!get_db_info(dbname, &db_id, &db_owner, NULL, + if (!get_db_info(dbname, &db_id, NULL, NULL, &db_istemplate, NULL, NULL, NULL, NULL, NULL)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", dbname))); - if (GetUserId() != db_owner && !superuser()) + if (!pg_database_ownercheck(db_id, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); @@ -818,8 +817,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", stmt->dbname))); - if (!(superuser() - || ((Form_pg_database) GETSTRUCT(tuple))->datdba == GetUserId())) + if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, stmt->dbname); @@ -878,7 +876,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) * ALTER DATABASE name OWNER TO newowner */ void -AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId) +AlterDatabaseOwner(const char *dbname, Oid newOwnerId) { HeapTuple tuple; Relation rel; @@ -910,7 +908,7 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId) * command to have succeeded. This is to be consistent with other * objects. */ - if (datForm->datdba != newOwnerSysId) + if (datForm->datdba != newOwnerId) { Datum repl_val[Natts_pg_database]; char repl_null[Natts_pg_database]; @@ -930,7 +928,7 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId) memset(repl_repl, ' ', sizeof(repl_repl)); repl_repl[Anum_pg_database_datdba - 1] = 'r'; - repl_val[Anum_pg_database_datdba - 1] = Int32GetDatum(newOwnerSysId); + repl_val[Anum_pg_database_datdba - 1] = ObjectIdGetDatum(newOwnerId); /* * Determine the modified ACL for the new owner. This is only @@ -943,7 +941,7 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId) if (!isNull) { newAcl = aclnewowner(DatumGetAclP(aclDatum), - datForm->datdba, newOwnerSysId); + datForm->datdba, newOwnerId); repl_repl[Anum_pg_database_datacl - 1] = 'r'; repl_val[Anum_pg_database_datacl - 1] = PointerGetDatum(newAcl); } @@ -972,7 +970,7 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId) */ static bool -get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP, +get_db_info(const char *name, Oid *dbIdP, Oid *ownerIdP, int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP, Oid *dbLastSysOidP, TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP, @@ -1007,7 +1005,7 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP, /* oid of the database */ if (dbIdP) *dbIdP = HeapTupleGetOid(tuple); - /* sysid of the owner */ + /* oid of the owner */ if (ownerIdP) *ownerIdP = dbform->datdba; /* character encoding */ @@ -1046,12 +1044,12 @@ have_createdb_privilege(void) bool result = false; HeapTuple utup; - utup = SearchSysCache(SHADOWSYSID, - Int32GetDatum(GetUserId()), + utup = SearchSysCache(AUTHOID, + ObjectIdGetDatum(GetUserId()), 0, 0, 0); if (HeapTupleIsValid(utup)) { - result = ((Form_pg_shadow) GETSTRUCT(utup))->usecreatedb; + result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreatedb; ReleaseSysCache(utup); } return result; diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index ea3c3811837..3329822fe62 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.61 2005/04/14 20:03:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.62 2005/06/28 05:08:53 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -853,7 +853,7 @@ RenameFunction(List *name, List *argtypes, const char *newname) * Change function owner */ void -AlterFunctionOwner(List *name, List *argtypes, AclId newOwnerSysId) +AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) { Oid procOid; HeapTuple tup; @@ -882,7 +882,7 @@ AlterFunctionOwner(List *name, List *argtypes, AclId newOwnerSysId) * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ - if (procForm->proowner != newOwnerSysId) + if (procForm->proowner != newOwnerId) { Datum repl_val[Natts_pg_proc]; char repl_null[Natts_pg_proc]; @@ -902,7 +902,7 @@ AlterFunctionOwner(List *name, List *argtypes, AclId newOwnerSysId) memset(repl_repl, ' ', sizeof(repl_repl)); repl_repl[Anum_pg_proc_proowner - 1] = 'r'; - repl_val[Anum_pg_proc_proowner - 1] = Int32GetDatum(newOwnerSysId); + repl_val[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(newOwnerId); /* * Determine the modified ACL for the new owner. This is only @@ -914,7 +914,7 @@ AlterFunctionOwner(List *name, List *argtypes, AclId newOwnerSysId) if (!isNull) { newAcl = aclnewowner(DatumGetAclP(aclDatum), - procForm->proowner, newOwnerSysId); + procForm->proowner, newOwnerId); repl_repl[Anum_pg_proc_proacl - 1] = 'r'; repl_val[Anum_pg_proc_proacl - 1] = PointerGetDatum(newAcl); } diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index e64924c43a7..3610269644a 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.32 2005/04/14 20:03:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.33 2005/06/28 05:08:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -321,7 +321,7 @@ DefineOpClass(CreateOpClassStmt *stmt) namestrcpy(&opcName, opcname); values[i++] = NameGetDatum(&opcName); /* opcname */ values[i++] = ObjectIdGetDatum(namespaceoid); /* opcnamespace */ - values[i++] = Int32GetDatum(GetUserId()); /* opcowner */ + values[i++] = ObjectIdGetDatum(GetUserId()); /* opcowner */ values[i++] = ObjectIdGetDatum(typeoid); /* opcintype */ values[i++] = BoolGetDatum(stmt->isDefault); /* opcdefault */ values[i++] = ObjectIdGetDatum(storageoid); /* opckeytype */ @@ -880,7 +880,7 @@ RenameOpClass(List *name, const char *access_method, const char *newname) * Change opclass owner */ void -AlterOpClassOwner(List *name, const char *access_method, AclId newOwnerSysId) +AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId) { Oid opcOid; Oid amOid; @@ -945,7 +945,7 @@ AlterOpClassOwner(List *name, const char *access_method, AclId newOwnerSysId) * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ - if (opcForm->opcowner != newOwnerSysId) + if (opcForm->opcowner != newOwnerId) { /* Otherwise, must be superuser to change object ownership */ if (!superuser()) @@ -957,7 +957,7 @@ AlterOpClassOwner(List *name, const char *access_method, AclId newOwnerSysId) * Modify the owner --- okay to scribble on tup because it's a * copy */ - opcForm->opcowner = newOwnerSysId; + opcForm->opcowner = newOwnerId; simple_heap_update(rel, &tup->t_self, tup); diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index 45dc1eafeaa..adea1b2b5d7 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.21 2005/04/14 20:03:24 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.22 2005/06/28 05:08:54 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -269,7 +269,7 @@ RemoveOperatorById(Oid operOid) */ void AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, - AclId newOwnerSysId) + Oid newOwnerId) { Oid operOid; HeapTuple tup; @@ -293,7 +293,7 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ - if (oprForm->oprowner != newOwnerSysId) + if (oprForm->oprowner != newOwnerId) { /* Otherwise, must be superuser to change object ownership */ if (!superuser()) @@ -305,7 +305,7 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, * Modify the owner --- okay to scribble on tup because it's a * copy */ - oprForm->oprowner = newOwnerSysId; + oprForm->oprowner = newOwnerId; simple_heap_update(rel, &tup->t_self, tup); diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index 5754c1dfcb4..d92812f1fcf 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.30 2005/06/21 00:58:15 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.31 2005/06/28 05:08:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -42,11 +42,11 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) Oid namespaceId; List *parsetree_list; ListCell *parsetree_item; - AclId owner_userid; - AclId saved_userid; + Oid owner_uid; + Oid saved_uid; AclResult aclresult; - saved_userid = GetUserId(); + saved_uid = GetUserId(); /* * Figure out user identities. @@ -54,12 +54,11 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) if (!authId) { - owner_userid = saved_userid; + owner_uid = saved_uid; } else if (superuser()) { - /* The following will error out if user does not exist */ - owner_userid = get_usesysid(authId); + owner_uid = get_roleid_checked(authId); /* * Set the current user to the requested authorization so that @@ -67,15 +66,15 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) * (This will revert to session user on error or at the end of * this routine.) */ - SetUserId(owner_userid); + SetUserId(owner_uid); } else { const char *owner_name; /* not superuser */ - owner_userid = saved_userid; - owner_name = GetUserNameFromId(owner_userid); + owner_uid = saved_uid; + owner_name = GetUserNameFromId(owner_uid); if (strcmp(authId, owner_name) != 0) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -87,7 +86,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) /* * Permissions checks. */ - aclresult = pg_database_aclcheck(MyDatabaseId, saved_userid, ACL_CREATE); + aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); @@ -99,7 +98,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) errdetail("The prefix \"pg_\" is reserved for system schemas."))); /* Create the schema's namespace */ - namespaceId = NamespaceCreate(schemaName, owner_userid); + namespaceId = NamespaceCreate(schemaName, owner_uid); /* Advance cmd counter to make the namespace visible */ CommandCounterIncrement(); @@ -149,7 +148,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) PopSpecialNamespace(namespaceId); /* Reset current user */ - SetUserId(saved_userid); + SetUserId(saved_uid); } @@ -279,7 +278,7 @@ RenameSchema(const char *oldname, const char *newname) * Change schema owner */ void -AlterSchemaOwner(const char *name, AclId newOwnerSysId) +AlterSchemaOwner(const char *name, Oid newOwnerId) { HeapTuple tup; Relation rel; @@ -300,7 +299,7 @@ AlterSchemaOwner(const char *name, AclId newOwnerSysId) * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ - if (nspForm->nspowner != newOwnerSysId) + if (nspForm->nspowner != newOwnerId) { Datum repl_val[Natts_pg_namespace]; char repl_null[Natts_pg_namespace]; @@ -320,7 +319,7 @@ AlterSchemaOwner(const char *name, AclId newOwnerSysId) memset(repl_repl, ' ', sizeof(repl_repl)); repl_repl[Anum_pg_namespace_nspowner - 1] = 'r'; - repl_val[Anum_pg_namespace_nspowner - 1] = Int32GetDatum(newOwnerSysId); + repl_val[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(newOwnerId); /* * Determine the modified ACL for the new owner. This is only @@ -332,7 +331,7 @@ AlterSchemaOwner(const char *name, AclId newOwnerSysId) if (!isNull) { newAcl = aclnewowner(DatumGetAclP(aclDatum), - nspForm->nspowner, newOwnerSysId); + nspForm->nspowner, newOwnerId); repl_repl[Anum_pg_namespace_nspacl - 1] = 'r'; repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 9981129c0eb..dda9532baf8 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.161 2005/06/06 20:22:57 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.162 2005/06/28 05:08:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -231,9 +231,9 @@ static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, const char *colName, TypeName *typename); static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab); static void ATPostAlterTypeParse(char *cmd, List **wqueue); -static void ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId); +static void ATExecChangeOwner(Oid relationOid, Oid newOwnerId); static void change_owner_recurse_to_sequences(Oid relationOid, - int32 newOwnerSysId); + Oid newOwnerId); static void ATExecClusterOn(Relation rel, const char *indexName); static void ATExecDropCluster(Relation rel); static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, @@ -2133,8 +2133,8 @@ ATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd) AlterTableCreateToastTable(RelationGetRelid(rel), false); break; case AT_ChangeOwner: /* ALTER OWNER */ - /* get_usesysid raises an error if no such user */ - ATExecChangeOwner(RelationGetRelid(rel), get_usesysid(cmd->name)); + ATExecChangeOwner(RelationGetRelid(rel), + get_roleid_checked(cmd->name)); break; case AT_ClusterOn: /* CLUSTER ON */ ATExecClusterOn(rel, cmd->name); @@ -5233,7 +5233,7 @@ ATPostAlterTypeParse(char *cmd, List **wqueue) * ALTER TABLE OWNER */ static void -ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId) +ATExecChangeOwner(Oid relationOid, Oid newOwnerId) { Relation target_rel; Relation class_rel; @@ -5277,7 +5277,7 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId) * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ - if (tuple_class->relowner != newOwnerSysId) + if (tuple_class->relowner != newOwnerId) { Datum repl_val[Natts_pg_class]; char repl_null[Natts_pg_class]; @@ -5297,7 +5297,7 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId) memset(repl_repl, ' ', sizeof(repl_repl)); repl_repl[Anum_pg_class_relowner - 1] = 'r'; - repl_val[Anum_pg_class_relowner - 1] = Int32GetDatum(newOwnerSysId); + repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId); /* * Determine the modified ACL for the new owner. This is only @@ -5309,7 +5309,7 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId) if (!isNull) { newAcl = aclnewowner(DatumGetAclP(aclDatum), - tuple_class->relowner, newOwnerSysId); + tuple_class->relowner, newOwnerId); repl_repl[Anum_pg_class_relacl - 1] = 'r'; repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl); } @@ -5337,7 +5337,7 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId) /* For each index, recursively change its ownership */ foreach(i, index_oid_list) - ATExecChangeOwner(lfirst_oid(i), newOwnerSysId); + ATExecChangeOwner(lfirst_oid(i), newOwnerId); list_free(index_oid_list); } @@ -5346,10 +5346,10 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId) { /* If it has a toast table, recurse to change its ownership */ if (tuple_class->reltoastrelid != InvalidOid) - ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerSysId); + ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId); /* If it has dependent sequences, recurse to change them too */ - change_owner_recurse_to_sequences(relationOid, newOwnerSysId); + change_owner_recurse_to_sequences(relationOid, newOwnerId); } } @@ -5366,7 +5366,7 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId) * ownership. */ static void -change_owner_recurse_to_sequences(Oid relationOid, int32 newOwnerSysId) +change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId) { Relation depRel; SysScanDesc scan; @@ -5416,7 +5416,7 @@ change_owner_recurse_to_sequences(Oid relationOid, int32 newOwnerSysId) } /* We don't need to close the sequence while we alter it. */ - ATExecChangeOwner(depForm->objid, newOwnerSysId); + ATExecChangeOwner(depForm->objid, newOwnerId); /* Now we can close it. Keep the lock till end of transaction. */ relation_close(seqRel, NoLock); diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index a469a8fa349..15a263b8efd 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.22 2005/06/19 21:34:01 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.23 2005/06/28 05:08:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -208,7 +208,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) Oid tablespaceoid; char *location; char *linkloc; - AclId ownerid; + Oid ownerId; /* validate */ @@ -225,12 +225,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) /* However, the eventual owner of the tablespace need not be */ if (stmt->owner) - { - /* No need to check result, get_usesysid() does that */ - ownerid = get_usesysid(stmt->owner); - } + ownerId = get_roleid_checked(stmt->owner); else - ownerid = GetUserId(); + ownerId = GetUserId(); /* Unix-ify the offered path, and strip any trailing slashes */ location = pstrdup(stmt->location); @@ -297,7 +294,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) values[Anum_pg_tablespace_spcname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename)); values[Anum_pg_tablespace_spcowner - 1] = - Int32GetDatum(ownerid); + ObjectIdGetDatum(ownerId); values[Anum_pg_tablespace_spclocation - 1] = DirectFunctionCall1(textin, CStringGetDatum(location)); nulls[Anum_pg_tablespace_spcacl - 1] = 'n'; @@ -426,9 +423,8 @@ DropTableSpace(DropTableSpaceStmt *stmt) tablespaceoid = HeapTupleGetOid(tuple); - /* Must be superuser or owner */ - if (GetUserId() != ((Form_pg_tablespace) GETSTRUCT(tuple))->spcowner && - !superuser()) + /* Must be tablespace owner */ + if (!pg_tablespace_ownercheck(tablespaceoid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, tablespacename); @@ -711,8 +707,8 @@ RenameTableSpace(const char *oldname, const char *newname) heap_endscan(scan); - /* Must be owner or superuser */ - if (newform->spcowner != GetUserId() && !superuser()) + /* Must be owner */ + if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId())) aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, oldname); /* Validate new name */ @@ -750,7 +746,7 @@ RenameTableSpace(const char *oldname, const char *newname) * Change tablespace owner */ void -AlterTableSpaceOwner(const char *name, AclId newOwnerSysId) +AlterTableSpaceOwner(const char *name, Oid newOwnerId) { Relation rel; ScanKeyData entry[1]; @@ -778,7 +774,7 @@ AlterTableSpaceOwner(const char *name, AclId newOwnerSysId) * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ - if (spcForm->spcowner != newOwnerSysId) + if (spcForm->spcowner != newOwnerId) { Datum repl_val[Natts_pg_tablespace]; char repl_null[Natts_pg_tablespace]; @@ -798,7 +794,7 @@ AlterTableSpaceOwner(const char *name, AclId newOwnerSysId) memset(repl_repl, ' ', sizeof(repl_repl)); repl_repl[Anum_pg_tablespace_spcowner - 1] = 'r'; - repl_val[Anum_pg_tablespace_spcowner - 1] = Int32GetDatum(newOwnerSysId); + repl_val[Anum_pg_tablespace_spcowner - 1] = ObjectIdGetDatum(newOwnerId); /* * Determine the modified ACL for the new owner. This is only @@ -811,7 +807,7 @@ AlterTableSpaceOwner(const char *name, AclId newOwnerSysId) if (!isNull) { newAcl = aclnewowner(DatumGetAclP(aclDatum), - spcForm->spcowner, newOwnerSysId); + spcForm->spcowner, newOwnerId); repl_repl[Anum_pg_tablespace_spcacl - 1] = 'r'; repl_val[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(newAcl); } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 280022feaa1..53570c77409 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.72 2005/05/06 17:24:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.73 2005/06/28 05:08:54 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -2016,7 +2016,7 @@ GetDomainConstraints(Oid typeOid) * Change the owner of a type. */ void -AlterTypeOwner(List *names, AclId newOwnerSysId) +AlterTypeOwner(List *names, Oid newOwnerId) { TypeName *typename; Oid typeOid; @@ -2063,7 +2063,7 @@ AlterTypeOwner(List *names, AclId newOwnerSysId) * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ - if (typTup->typowner != newOwnerSysId) + if (typTup->typowner != newOwnerId) { /* Otherwise, must be superuser to change object ownership */ if (!superuser()) @@ -2075,7 +2075,7 @@ AlterTypeOwner(List *names, AclId newOwnerSysId) * Modify the owner --- okay to scribble on typTup because it's a * copy */ - typTup->typowner = newOwnerSysId; + typTup->typowner = newOwnerId; simple_heap_update(rel, &tup->t_self, tup); diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 3c70c579785..131f1896f92 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -1,12 +1,12 @@ /*------------------------------------------------------------------------- * * user.c - * Commands for manipulating users and groups. + * Commands for manipulating roles (formerly called users). * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.151 2005/04/14 20:03:24 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.152 2005/06/28 05:08:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -14,15 +14,14 @@ #include "access/heapam.h" #include "catalog/indexing.h" +#include "catalog/pg_auth_members.h" +#include "catalog/pg_authid.h" #include "catalog/pg_database.h" -#include "catalog/pg_group.h" -#include "catalog/pg_shadow.h" -#include "catalog/pg_type.h" #include "commands/user.h" #include "libpq/crypt.h" #include "miscadmin.h" -#include "utils/acl.h" #include "utils/builtins.h" +#include "utils/catcache.h" #include "utils/flatfiles.h" #include "utils/fmgroids.h" #include "utils/guc.h" @@ -32,46 +31,46 @@ extern bool Password_encryption; - -static void CheckPgUserAclNotNull(void); -static void UpdateGroupMembership(Relation group_rel, HeapTuple group_tuple, - List *members); -static IdList *IdListToArray(List *members); -static List *IdArrayToList(IdList *oldarray); +static List *roleNamesToIds(List *memberNames); +static void AddRoleMems(const char *rolename, Oid roleid, + List *memberNames, List *memberIds, + Oid grantorId, bool admin_opt); +static void DelRoleMems(const char *rolename, Oid roleid, + List *memberNames, List *memberIds, + bool admin_opt); /* - * CREATE USER + * CREATE ROLE */ void -CreateUser(CreateUserStmt *stmt) +CreateRole(CreateRoleStmt *stmt) { - Relation pg_shadow_rel; - TupleDesc pg_shadow_dsc; - HeapScanDesc scan; + Relation pg_authid_rel; + TupleDesc pg_authid_dsc; HeapTuple tuple; - Datum new_record[Natts_pg_shadow]; - char new_record_nulls[Natts_pg_shadow]; - bool user_exists = false, - sysid_exists = false, - havesysid = false; - int max_id; + Datum new_record[Natts_pg_authid]; + char new_record_nulls[Natts_pg_authid]; + Oid roleid; ListCell *item; ListCell *option; - char *password = NULL; /* PostgreSQL user password */ + char *password = NULL; /* user password */ bool encrypt_password = Password_encryption; /* encrypt password? */ char encrypted_password[MD5_PASSWD_LEN + 1]; - int sysid = 0; /* PgSQL system id (valid if havesysid) */ + bool issuper = false; /* Make the user a superuser? */ + bool createrole = false; /* Can this user create roles? */ bool createdb = false; /* Can the user create databases? */ - bool createuser = false; /* Can this user create users? */ - List *groupElts = NIL; /* The groups the user is a member of */ + bool canlogin = false; /* Can this user login? */ + List *roleElts = NIL; /* roles the user is a member of */ + List *rolememElts = NIL; /* roles which will be members of this role */ char *validUntil = NULL; /* The time the login is valid * until */ DefElem *dpassword = NULL; - DefElem *dsysid = NULL; DefElem *dcreatedb = NULL; - DefElem *dcreateuser = NULL; - DefElem *dgroupElts = NULL; + DefElem *dcreaterole = NULL; + DefElem *dcanlogin = NULL; + DefElem *droleElts = NULL; + DefElem *drolememElts = NULL; DefElem *dvalidUntil = NULL; /* Extract options from the statement node tree */ @@ -95,11 +94,16 @@ CreateUser(CreateUserStmt *stmt) } else if (strcmp(defel->defname, "sysid") == 0) { - if (dsysid) + ereport(WARNING, + (errmsg("SYSID can no longer be specified"))); + } + else if (strcmp(defel->defname, "createrole") == 0) + { + if (dcreaterole) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); - dsysid = defel; + dcreaterole = defel; } else if (strcmp(defel->defname, "createdb") == 0) { @@ -109,21 +113,29 @@ CreateUser(CreateUserStmt *stmt) errmsg("conflicting or redundant options"))); dcreatedb = defel; } - else if (strcmp(defel->defname, "createuser") == 0) + else if (strcmp(defel->defname, "canlogin") == 0) { - if (dcreateuser) + if (dcanlogin) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); - dcreateuser = defel; + dcanlogin = defel; } - else if (strcmp(defel->defname, "groupElts") == 0) + else if (strcmp(defel->defname, "roleElts") == 0) { - if (dgroupElts) + if (droleElts) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); - dgroupElts = defel; + droleElts = defel; + } + else if (strcmp(defel->defname, "rolememElts") == 0) + { + if (drolememElts) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + drolememElts = defel; } else if (strcmp(defel->defname, "validUntil") == 0) { @@ -140,83 +152,51 @@ CreateUser(CreateUserStmt *stmt) if (dcreatedb) createdb = intVal(dcreatedb->arg) != 0; - if (dcreateuser) - createuser = intVal(dcreateuser->arg) != 0; - if (dsysid) + if (dcreaterole) { - sysid = intVal(dsysid->arg); - if (sysid <= 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("user ID must be positive"))); - havesysid = true; + createrole = intVal(dcreaterole->arg) != 0; + /* XXX issuper is implied by createrole for now */ + issuper = createrole; } + if (dcanlogin) + canlogin = intVal(dcanlogin->arg) != 0; if (dvalidUntil) validUntil = strVal(dvalidUntil->arg); if (dpassword) password = strVal(dpassword->arg); - if (dgroupElts) - groupElts = (List *) dgroupElts->arg; + if (droleElts) + roleElts = (List *) droleElts->arg; + if (drolememElts) + rolememElts = (List *) drolememElts->arg; /* Check some permissions first */ - if (password) - CheckPgUserAclNotNull(); - if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to create users"))); + errmsg("must be superuser to create roles"))); - if (strcmp(stmt->user, "public") == 0) + if (strcmp(stmt->role, "public") == 0) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), - errmsg("user name \"%s\" is reserved", - stmt->user))); + errmsg("role name \"%s\" is reserved", + stmt->role))); /* - * 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 eventual update of the flat password file. + * Check the pg_authid relation to be certain the role doesn't + * already exist. Note we secure exclusive lock because + * we need to protect our eventual update of the flat auth file. */ - pg_shadow_rel = heap_open(ShadowRelationId, ExclusiveLock); - pg_shadow_dsc = RelationGetDescr(pg_shadow_rel); - - scan = heap_beginscan(pg_shadow_rel, SnapshotNow, 0, NULL); - max_id = 99; /* start auto-assigned ids at 100 */ - while (!user_exists && !sysid_exists && - (tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) - { - Form_pg_shadow shadow_form = (Form_pg_shadow) GETSTRUCT(tuple); - int32 this_sysid; - - user_exists = (strcmp(NameStr(shadow_form->usename), stmt->user) == 0); - - this_sysid = shadow_form->usesysid; - if (havesysid) /* customized id wanted */ - sysid_exists = (this_sysid == sysid); - else - { - /* pick 1 + max */ - if (this_sysid > max_id) - max_id = this_sysid; - } - } - heap_endscan(scan); + pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock); + pg_authid_dsc = RelationGetDescr(pg_authid_rel); - if (user_exists) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("user \"%s\" already exists", - stmt->user))); - if (sysid_exists) + tuple = SearchSysCache(AUTHNAME, + PointerGetDatum(stmt->role), + 0, 0, 0); + if (HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("user ID %d is already assigned", sysid))); - - /* If no sysid given, use max existing id + 1 */ - if (!havesysid) - sysid = max_id + 1; + errmsg("role \"%s\" already exists", + stmt->role))); /* * Build a tuple to insert @@ -224,105 +204,123 @@ CreateUser(CreateUserStmt *stmt) MemSet(new_record, 0, sizeof(new_record)); MemSet(new_record_nulls, ' ', sizeof(new_record_nulls)); - new_record[Anum_pg_shadow_usename - 1] = - DirectFunctionCall1(namein, CStringGetDatum(stmt->user)); - new_record[Anum_pg_shadow_usesysid - 1] = Int32GetDatum(sysid); - AssertState(BoolIsValid(createdb)); - new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb); - AssertState(BoolIsValid(createuser)); - new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser); - /* superuser gets catupd right by default */ - new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser); + new_record[Anum_pg_authid_rolname - 1] = + DirectFunctionCall1(namein, CStringGetDatum(stmt->role)); + + new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper); + new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole); + new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb); + /* superuser gets catupdate right by default */ + new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper); + new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin); if (password) { if (!encrypt_password || isMD5(password)) - new_record[Anum_pg_shadow_passwd - 1] = + new_record[Anum_pg_authid_rolpassword - 1] = DirectFunctionCall1(textin, CStringGetDatum(password)); else { - if (!EncryptMD5(password, stmt->user, strlen(stmt->user), + if (!EncryptMD5(password, stmt->role, strlen(stmt->role), encrypted_password)) elog(ERROR, "password encryption failed"); - new_record[Anum_pg_shadow_passwd - 1] = + new_record[Anum_pg_authid_rolpassword - 1] = DirectFunctionCall1(textin, CStringGetDatum(encrypted_password)); } } else - new_record_nulls[Anum_pg_shadow_passwd - 1] = 'n'; + new_record_nulls[Anum_pg_authid_rolpassword - 1] = 'n'; if (validUntil) - new_record[Anum_pg_shadow_valuntil - 1] = - DirectFunctionCall1(abstimein, CStringGetDatum(validUntil)); + new_record[Anum_pg_authid_rolvaliduntil - 1] = + DirectFunctionCall3(timestamptz_in, + CStringGetDatum(validUntil), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + else - new_record_nulls[Anum_pg_shadow_valuntil - 1] = 'n'; + new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = 'n'; - new_record_nulls[Anum_pg_shadow_useconfig - 1] = 'n'; + new_record_nulls[Anum_pg_authid_rolconfig - 1] = 'n'; - tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls); + tuple = heap_formtuple(pg_authid_dsc, new_record, new_record_nulls); /* - * Insert new record in the pg_shadow table + * Insert new record in the pg_authid table */ - simple_heap_insert(pg_shadow_rel, tuple); + roleid = simple_heap_insert(pg_authid_rel, tuple); + Assert(OidIsValid(roleid)); /* Update indexes */ - CatalogUpdateIndexes(pg_shadow_rel, tuple); + CatalogUpdateIndexes(pg_authid_rel, tuple); /* - * Add the user to the groups specified. We'll just call the below - * AlterGroup for this. + * Add the new role to the specified existing roles. */ - foreach(item, groupElts) + foreach(item, roleElts) { - AlterGroupStmt ags; + char *oldrolename = strVal(lfirst(item)); + Oid oldroleid = get_roleid_checked(oldrolename); - ags.name = strVal(lfirst(item)); /* the group name to add - * this in */ - ags.action = +1; - ags.listUsers = list_make1(makeInteger(sysid)); - AlterGroup(&ags, "CREATE USER"); + AddRoleMems(oldrolename, oldroleid, + list_make1(makeString(stmt->role)), + list_make1_oid(roleid), + GetUserId(), false); } /* + * Add the specified members to this new role. + */ + AddRoleMems(stmt->role, roleid, + rolememElts, roleNamesToIds(rolememElts), + GetUserId(), false); + + /* * Now we can clean up; but keep lock until commit (to avoid possible * deadlock when commit code tries to acquire lock). */ - heap_close(pg_shadow_rel, NoLock); + heap_close(pg_authid_rel, NoLock); /* - * Set flag to update flat password file at commit. + * Set flag to update flat auth file at commit. */ - user_file_update_needed(); + auth_file_update_needed(); } - /* - * ALTER USER + * ALTER ROLE */ void -AlterUser(AlterUserStmt *stmt) +AlterRole(AlterRoleStmt *stmt) { - Datum new_record[Natts_pg_shadow]; - char new_record_nulls[Natts_pg_shadow]; - char new_record_repl[Natts_pg_shadow]; - Relation pg_shadow_rel; - TupleDesc pg_shadow_dsc; + Datum new_record[Natts_pg_authid]; + char new_record_nulls[Natts_pg_authid]; + char new_record_repl[Natts_pg_authid]; + Relation pg_authid_rel; + TupleDesc pg_authid_dsc; HeapTuple tuple, new_tuple; ListCell *option; - char *password = NULL; /* PostgreSQL user password */ + char *password = NULL; /* user password */ bool encrypt_password = Password_encryption; /* encrypt password? */ char encrypted_password[MD5_PASSWD_LEN + 1]; - int createdb = -1; /* Can the user create databases? */ - int createuser = -1; /* Can this user create users? */ + int issuper = -1; /* Make the user a superuser? */ + int createrole = -1; /* Can this user create roles? */ + int createdb = -1; /* Can the user create databases? */ + int canlogin = -1; /* Can this user login? */ + int adminopt = 0; /* Can this user grant this role to others? */ + List *rolememElts = NIL; /* The roles which will be added/removed to this role */ char *validUntil = NULL; /* The time the login is valid * until */ DefElem *dpassword = NULL; DefElem *dcreatedb = NULL; - DefElem *dcreateuser = NULL; + DefElem *dcreaterole = NULL; + DefElem *dcanlogin = NULL; + DefElem *dadminopt = NULL; DefElem *dvalidUntil = NULL; + DefElem *drolememElts = NULL; + Oid roleid; /* Extract options from the statement node tree */ foreach(option, stmt->options) @@ -351,13 +349,29 @@ AlterUser(AlterUserStmt *stmt) errmsg("conflicting or redundant options"))); dcreatedb = defel; } - else if (strcmp(defel->defname, "createuser") == 0) + else if (strcmp(defel->defname, "createrole") == 0) { - if (dcreateuser) + if (dcreaterole) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); - dcreateuser = defel; + dcreaterole = defel; + } + else if (strcmp(defel->defname, "canlogin") == 0) + { + if (dcanlogin) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dcanlogin = defel; + } + else if (strcmp(defel->defname, "adminopt") == 0) + { + if (dadminopt) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dadminopt = defel; } else if (strcmp(defel->defname, "validUntil") == 0) { @@ -367,6 +381,14 @@ AlterUser(AlterUserStmt *stmt) errmsg("conflicting or redundant options"))); dvalidUntil = defel; } + else if (strcmp(defel->defname, "rolememElts") == 0 && stmt->action != 0) + { + if (drolememElts) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + drolememElts = defel; + } else elog(ERROR, "option \"%s\" not recognized", defel->defname); @@ -374,42 +396,54 @@ AlterUser(AlterUserStmt *stmt) if (dcreatedb) createdb = intVal(dcreatedb->arg); - if (dcreateuser) - createuser = intVal(dcreateuser->arg); + if (dcreaterole) + { + createrole = intVal(dcreaterole->arg); + /* XXX createrole implies issuper for now */ + issuper = createrole; + } + if (dcanlogin) + canlogin = intVal(dcanlogin->arg); + if (dadminopt) + adminopt = intVal(dadminopt->arg); if (dvalidUntil) validUntil = strVal(dvalidUntil->arg); if (dpassword) password = strVal(dpassword->arg); - - if (password) - CheckPgUserAclNotNull(); + if (drolememElts) + rolememElts = (List *) drolememElts->arg; /* must be superuser or just want to change your own password */ if (!superuser() && - !(createdb < 0 && - createuser < 0 && + !(issuper < 0 && + createrole < 0 && + createdb < 0 && + canlogin < 0 && !validUntil && + !rolememElts && + !adminopt && password && - strcmp(GetUserNameFromId(GetUserId()), stmt->user) == 0)) + strcmp(GetUserNameFromId(GetUserId()), stmt->role) == 0)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied"))); /* - * 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. + * Scan the pg_authid relation to be certain the user exists. Note we + * secure exclusive lock to protect our update of the flat auth file. */ - pg_shadow_rel = heap_open(ShadowRelationId, ExclusiveLock); - pg_shadow_dsc = RelationGetDescr(pg_shadow_rel); + pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock); + pg_authid_dsc = RelationGetDescr(pg_authid_rel); - tuple = SearchSysCache(SHADOWNAME, - PointerGetDatum(stmt->user), + tuple = SearchSysCache(AUTHNAME, + PointerGetDatum(stmt->role), 0, 0, 0); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("user \"%s\" does not exist", stmt->user))); + errmsg("role \"%s\" does not exist", stmt->role))); + + roleid = HeapTupleGetOid(tuple); /* * Build an updated tuple, perusing the information just obtained @@ -418,65 +452,79 @@ AlterUser(AlterUserStmt *stmt) MemSet(new_record_nulls, ' ', sizeof(new_record_nulls)); MemSet(new_record_repl, ' ', sizeof(new_record_repl)); - new_record[Anum_pg_shadow_usename - 1] = DirectFunctionCall1(namein, - CStringGetDatum(stmt->user)); - new_record_repl[Anum_pg_shadow_usename - 1] = 'r'; - - /* createdb */ - if (createdb >= 0) - { - new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb > 0); - new_record_repl[Anum_pg_shadow_usecreatedb - 1] = 'r'; - } + new_record[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein, + CStringGetDatum(stmt->role)); + new_record_repl[Anum_pg_authid_rolname - 1] = 'r'; /* - * createuser (superuser) and catupd + * issuper/createrole/catupdate/etc * - * XXX It's rather unclear how to handle catupd. It's probably best to + * XXX It's rather unclear how to handle catupdate. 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! + * catalogs, including pg_authid! */ - if (createuser >= 0) + if (issuper >= 0) { - new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser > 0); - new_record_repl[Anum_pg_shadow_usesuper - 1] = 'r'; + new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0); + new_record_repl[Anum_pg_authid_rolsuper - 1] = 'r'; - new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser > 0); - new_record_repl[Anum_pg_shadow_usecatupd - 1] = 'r'; + new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0); + new_record_repl[Anum_pg_authid_rolcatupdate - 1] = 'r'; + } + + if (createrole >= 0) + { + new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0); + new_record_repl[Anum_pg_authid_rolcreaterole - 1] = 'r'; + } + + if (createdb >= 0) + { + new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0); + new_record_repl[Anum_pg_authid_rolcreatedb - 1] = 'r'; + } + + if (canlogin >= 0) + { + new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0); + new_record_repl[Anum_pg_authid_rolcanlogin - 1] = 'r'; } /* password */ if (password) { if (!encrypt_password || isMD5(password)) - new_record[Anum_pg_shadow_passwd - 1] = + new_record[Anum_pg_authid_rolpassword - 1] = DirectFunctionCall1(textin, CStringGetDatum(password)); else { - if (!EncryptMD5(password, stmt->user, strlen(stmt->user), + if (!EncryptMD5(password, stmt->role, strlen(stmt->role), encrypted_password)) elog(ERROR, "password encryption failed"); - new_record[Anum_pg_shadow_passwd - 1] = + new_record[Anum_pg_authid_rolpassword - 1] = DirectFunctionCall1(textin, CStringGetDatum(encrypted_password)); } - new_record_repl[Anum_pg_shadow_passwd - 1] = 'r'; + new_record_repl[Anum_pg_authid_rolpassword - 1] = 'r'; } /* valid until */ if (validUntil) { - new_record[Anum_pg_shadow_valuntil - 1] = - DirectFunctionCall1(abstimein, CStringGetDatum(validUntil)); - new_record_repl[Anum_pg_shadow_valuntil - 1] = 'r'; + new_record[Anum_pg_authid_rolvaliduntil - 1] = + DirectFunctionCall3(timestamptz_in, + CStringGetDatum(validUntil), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = 'r'; } - new_tuple = heap_modifytuple(tuple, pg_shadow_dsc, new_record, + new_tuple = heap_modifytuple(tuple, pg_authid_dsc, new_record, new_record_nulls, new_record_repl); - simple_heap_update(pg_shadow_rel, &tuple->t_self, new_tuple); + simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple); /* Update indexes */ - CatalogUpdateIndexes(pg_shadow_rel, new_tuple); + CatalogUpdateIndexes(pg_authid_rel, new_tuple); ReleaseSysCache(tuple); heap_freetuple(new_tuple); @@ -485,59 +533,68 @@ AlterUser(AlterUserStmt *stmt) * Now we can clean up; but keep lock until commit (to avoid possible * deadlock when commit code tries to acquire lock). */ - heap_close(pg_shadow_rel, NoLock); + heap_close(pg_authid_rel, NoLock); + + if (stmt->action == +1) /* add members to role */ + AddRoleMems(stmt->role, roleid, + rolememElts, roleNamesToIds(rolememElts), + GetUserId(), adminopt); + else if (stmt->action == -1) /* drop members from role */ + DelRoleMems(stmt->role, roleid, + rolememElts, roleNamesToIds(rolememElts), + adminopt); /* - * Set flag to update flat password file at commit. + * Set flag to update flat auth file at commit. */ - user_file_update_needed(); + auth_file_update_needed(); } /* - * ALTER USER ... SET + * ALTER ROLE ... SET */ void -AlterUserSet(AlterUserSetStmt *stmt) +AlterRoleSet(AlterRoleSetStmt *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]; + Datum repl_val[Natts_pg_authid]; + char repl_null[Natts_pg_authid]; + char repl_repl[Natts_pg_authid]; int i; valuestr = flatten_set_variable_args(stmt->variable, stmt->value); /* * RowExclusiveLock is sufficient, because we don't need to update the - * flat password file. + * flat auth file. */ - rel = heap_open(ShadowRelationId, RowExclusiveLock); - oldtuple = SearchSysCache(SHADOWNAME, - PointerGetDatum(stmt->user), + rel = heap_open(AuthIdRelationId, RowExclusiveLock); + oldtuple = SearchSysCache(AUTHNAME, + PointerGetDatum(stmt->role), 0, 0, 0); if (!HeapTupleIsValid(oldtuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("user \"%s\" does not exist", stmt->user))); + errmsg("role \"%s\" does not exist", stmt->role))); if (!(superuser() || - ((Form_pg_shadow) GETSTRUCT(oldtuple))->usesysid == GetUserId())) + (HeapTupleGetOid(oldtuple) == GetUserId()))) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied"))); - for (i = 0; i < Natts_pg_shadow; i++) + for (i = 0; i < Natts_pg_authid; i++) repl_repl[i] = ' '; - repl_repl[Anum_pg_shadow_useconfig - 1] = 'r'; + repl_repl[Anum_pg_authid_rolconfig - 1] = 'r'; if (strcmp(stmt->variable, "all") == 0 && valuestr == NULL) { /* RESET ALL */ - repl_null[Anum_pg_shadow_useconfig - 1] = 'n'; + repl_null[Anum_pg_authid_rolconfig - 1] = 'n'; } else { @@ -545,10 +602,10 @@ AlterUserSet(AlterUserSetStmt *stmt) bool isnull; ArrayType *array; - repl_null[Anum_pg_shadow_useconfig - 1] = ' '; + repl_null[Anum_pg_authid_rolconfig - 1] = ' '; - datum = SysCacheGetAttr(SHADOWNAME, oldtuple, - Anum_pg_shadow_useconfig, &isnull); + datum = SysCacheGetAttr(AUTHNAME, oldtuple, + Anum_pg_authid_rolconfig, &isnull); array = isnull ? NULL : DatumGetArrayTypeP(datum); @@ -558,9 +615,9 @@ AlterUserSet(AlterUserSetStmt *stmt) array = GUCArrayDelete(array, stmt->variable); if (array) - repl_val[Anum_pg_shadow_useconfig - 1] = PointerGetDatum(array); + repl_val[Anum_pg_authid_rolconfig - 1] = PointerGetDatum(array); else - repl_null[Anum_pg_shadow_useconfig - 1] = 'n'; + repl_null[Anum_pg_authid_rolconfig - 1] = 'n'; } newtuple = heap_modifytuple(oldtuple, RelationGetDescr(rel), repl_val, repl_null, repl_repl); @@ -574,60 +631,61 @@ AlterUserSet(AlterUserSetStmt *stmt) /* - * DROP USER + * DROP ROLE */ void -DropUser(DropUserStmt *stmt) +DropRole(DropRoleStmt *stmt) { - Relation pg_shadow_rel; - TupleDesc pg_shadow_dsc; + Relation pg_authid_rel, pg_auth_members_rel; ListCell *item; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to drop users"))); + errmsg("must be superuser to drop roles"))); /* - * Scan the pg_shadow relation to find the usesysid of the user to be + * Scan the pg_authid relation to find the Oid of the role to be * deleted. Note we secure exclusive lock, because we need to protect - * our update of the flat password file. + * our update of the flat auth file. */ - pg_shadow_rel = heap_open(ShadowRelationId, ExclusiveLock); - pg_shadow_dsc = RelationGetDescr(pg_shadow_rel); + pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock); + pg_auth_members_rel = heap_open(AuthMemRelationId, ExclusiveLock); - foreach(item, stmt->users) + foreach(item, stmt->roles) { - const char *user = strVal(lfirst(item)); + const char *role = strVal(lfirst(item)); HeapTuple tuple, tmp_tuple; Relation pg_rel; TupleDesc pg_dsc; ScanKeyData scankey; HeapScanDesc scan; - AclId usesysid; + CatCList *auth_mem_list; + Oid roleid; + int i; - tuple = SearchSysCache(SHADOWNAME, - PointerGetDatum(user), + tuple = SearchSysCache(AUTHNAME, + PointerGetDatum(role), 0, 0, 0); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("user \"%s\" does not exist", user))); + errmsg("role \"%s\" does not exist", role))); - usesysid = ((Form_pg_shadow) GETSTRUCT(tuple))->usesysid; + roleid = HeapTupleGetOid(tuple); - if (usesysid == GetUserId()) + if (roleid == GetUserId()) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), - errmsg("current user cannot be dropped"))); - if (usesysid == GetSessionUserId()) + errmsg("current role cannot be dropped"))); + if (roleid == GetSessionUserId()) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), - errmsg("session user cannot be dropped"))); + errmsg("session role cannot be dropped"))); /* - * Check if user still owns a database. If so, error out. + * Check if role 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 @@ -639,8 +697,8 @@ DropUser(DropUserStmt *stmt) ScanKeyInit(&scankey, Anum_pg_database_datdba, - BTEqualStrategyNumber, F_INT4EQ, - Int32GetDatum(usesysid)); + BTEqualStrategyNumber, F_OIDEQ, + roleid); scan = heap_beginscan(pg_rel, SnapshotNow, 1, &scankey); @@ -651,8 +709,8 @@ DropUser(DropUserStmt *stmt) dbname = NameStr(((Form_pg_database) GETSTRUCT(tmp_tuple))->datname); ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), - errmsg("user \"%s\" cannot be dropped", user), - errdetail("The user owns database \"%s\".", dbname))); + errmsg("role \"%s\" cannot be dropped", role), + errdetail("The role owns database \"%s\".", dbname))); } heap_endscan(scan); @@ -660,66 +718,64 @@ DropUser(DropUserStmt *stmt) /* * 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 + * role 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 + * Remove the role from the pg_authid table */ - simple_heap_delete(pg_shadow_rel, &tuple->t_self); + simple_heap_delete(pg_authid_rel, &tuple->t_self); ReleaseSysCache(tuple); /* - * Remove user from groups + * Remove role from roles * - * try calling alter group drop user for every group + * scan pg_auth_members and remove tuples which have + * roleid == member or roleid == role */ - pg_rel = heap_open(GroupRelationId, ExclusiveLock); - pg_dsc = RelationGetDescr(pg_rel); - scan = heap_beginscan(pg_rel, SnapshotNow, 0, NULL); - while ((tmp_tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) - { - AlterGroupStmt ags; + auth_mem_list = SearchSysCacheList(AUTHMEMROLEMEM, 1, + ObjectIdGetDatum(roleid), + 0, 0, 0); - /* the group name from which to try to drop the user: */ - ags.name = pstrdup(NameStr(((Form_pg_group) GETSTRUCT(tmp_tuple))->groname)); - ags.action = -1; - ags.listUsers = list_make1(makeInteger(usesysid)); - AlterGroup(&ags, "DROP USER"); + for (i = 0; i < auth_mem_list->n_members; i++) + { + HeapTuple authmemtup = &auth_mem_list->members[i]->tuple; + simple_heap_delete(pg_auth_members_rel, &authmemtup->t_self); } - heap_endscan(scan); - heap_close(pg_rel, ExclusiveLock); + ReleaseSysCacheList(auth_mem_list); - /* - * 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(); + auth_mem_list = SearchSysCacheList(AUTHMEMMEMROLE, 1, + ObjectIdGetDatum(roleid), + 0, 0, 0); + + for (i = 0; i < auth_mem_list->n_members; i++) + { + HeapTuple authmemtup = &auth_mem_list->members[i]->tuple; + simple_heap_delete(pg_auth_members_rel, &authmemtup->t_self); + } + ReleaseSysCacheList(auth_mem_list); } /* * Now we can clean up; but keep lock until commit (to avoid possible * deadlock when commit code tries to acquire lock). */ - heap_close(pg_shadow_rel, NoLock); + heap_close(pg_auth_members_rel, NoLock); + heap_close(pg_authid_rel, NoLock); /* - * Set flag to update flat password file at commit. + * Set flag to update flat auth file at commit. */ - user_file_update_needed(); + auth_file_update_needed(); } - /* - * Rename user + * Rename role */ void -RenameUser(const char *oldname, const char *newname) +RenameRole(const char *oldname, const char *newname) { HeapTuple oldtuple, newtuple; @@ -727,22 +783,23 @@ RenameUser(const char *oldname, const char *newname) Relation rel; Datum datum; bool isnull; - Datum repl_val[Natts_pg_shadow]; - char repl_null[Natts_pg_shadow]; - char repl_repl[Natts_pg_shadow]; + Datum repl_val[Natts_pg_authid]; + char repl_null[Natts_pg_authid]; + char repl_repl[Natts_pg_authid]; int i; + Oid roleid; /* ExclusiveLock because we need to update the password file */ - rel = heap_open(ShadowRelationId, ExclusiveLock); + rel = heap_open(AuthIdRelationId, ExclusiveLock); dsc = RelationGetDescr(rel); - oldtuple = SearchSysCache(SHADOWNAME, + oldtuple = SearchSysCache(AUTHNAME, CStringGetDatum(oldname), 0, 0, 0); if (!HeapTupleIsValid(oldtuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("user \"%s\" does not exist", oldname))); + errmsg("role \"%s\" does not exist", oldname))); /* * XXX Client applications probably store the session user somewhere, @@ -750,43 +807,46 @@ RenameUser(const char *oldname, const char *newname) * not be an actual problem besides a little confusion, so think about * this and decide. */ - if (((Form_pg_shadow) GETSTRUCT(oldtuple))->usesysid == GetSessionUserId()) + + roleid = HeapTupleGetOid(oldtuple); + + if (roleid == GetSessionUserId()) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("session user may not be renamed"))); + errmsg("session role may not be renamed"))); /* make sure the new name doesn't exist */ - if (SearchSysCacheExists(SHADOWNAME, + if (SearchSysCacheExists(AUTHNAME, CStringGetDatum(newname), 0, 0, 0)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("user \"%s\" already exists", newname))); + errmsg("role \"%s\" already exists", newname))); /* must be superuser */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to rename users"))); + errmsg("must be superuser to rename roles"))); - for (i = 0; i < Natts_pg_shadow; i++) + for (i = 0; i < Natts_pg_authid; i++) repl_repl[i] = ' '; - repl_repl[Anum_pg_shadow_usename - 1] = 'r'; - repl_val[Anum_pg_shadow_usename - 1] = DirectFunctionCall1(namein, + repl_repl[Anum_pg_authid_rolname - 1] = 'r'; + repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein, CStringGetDatum(newname)); - repl_null[Anum_pg_shadow_usename - 1] = ' '; + repl_null[Anum_pg_authid_rolname - 1] = ' '; - datum = heap_getattr(oldtuple, Anum_pg_shadow_passwd, dsc, &isnull); + datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull); if (!isnull && isMD5(DatumGetCString(DirectFunctionCall1(textout, datum)))) { /* MD5 uses the username as salt, so just clear it on a rename */ - repl_repl[Anum_pg_shadow_passwd - 1] = 'r'; - repl_null[Anum_pg_shadow_passwd - 1] = 'n'; + repl_repl[Anum_pg_authid_rolpassword - 1] = 'r'; + repl_null[Anum_pg_authid_rolpassword - 1] = 'n'; ereport(NOTICE, - (errmsg("MD5 password cleared because of user rename"))); + (errmsg("MD5 password cleared because of role rename"))); } newtuple = heap_modifytuple(oldtuple, dsc, repl_val, repl_null, repl_repl); @@ -797,551 +857,322 @@ RenameUser(const char *oldname, const char *newname) ReleaseSysCache(oldtuple); heap_close(rel, NoLock); - user_file_update_needed(); + auth_file_update_needed(); } - /* - * CheckPgUserAclNotNull + * GrantRoleStmt * - * check to see if there is an ACL on pg_shadow - */ -static void -CheckPgUserAclNotNull(void) -{ - HeapTuple htup; - - htup = SearchSysCache(RELOID, - ObjectIdGetDatum(ShadowRelationId), - 0, 0, 0); - if (!HeapTupleIsValid(htup)) /* should not happen, we hope */ - elog(ERROR, "cache lookup failed for relation %u", ShadowRelationId); - - if (heap_attisnull(htup, Anum_pg_class_relacl)) - { - Form_pg_class classForm = (Form_pg_class) GETSTRUCT(htup); - - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("before using passwords you must revoke privileges on %s", - NameStr(classForm->relname)), - errdetail("This restriction is to prevent unprivileged users from reading the passwords."), - errhint("Try REVOKE ALL ON \"%s\" FROM PUBLIC.", - NameStr(classForm->relname)))); - } - - ReleaseSysCache(htup); -} - - -/* - * CREATE GROUP + * Grant/Revoke roles to/from roles */ void -CreateGroup(CreateGroupStmt *stmt) +GrantRole(GrantRoleStmt *stmt) { - Relation pg_group_rel; - HeapScanDesc scan; - HeapTuple tuple; - TupleDesc pg_group_dsc; - bool group_exists = false, - sysid_exists = false, - havesysid = false; - int max_id; - Datum new_record[Natts_pg_group]; - char new_record_nulls[Natts_pg_group]; + Oid grantor; + List *grantee_ids; ListCell *item; - ListCell *option; - List *newlist = NIL; - IdList *grolist; - int sysid = 0; - List *userElts = NIL; - DefElem *dsysid = NULL; - DefElem *duserElts = NULL; - - foreach(option, stmt->options) - { - DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "sysid") == 0) - { - if (dsysid) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("conflicting or redundant options"))); - dsysid = defel; - } - else if (strcmp(defel->defname, "userElts") == 0) - { - if (duserElts) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("conflicting or redundant options"))); - duserElts = defel; - } - else - elog(ERROR, "option \"%s\" not recognized", - defel->defname); - } - - if (dsysid) - { - sysid = intVal(dsysid->arg); - if (sysid <= 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("group ID must be positive"))); - havesysid = true; - } + if (stmt->grantor) + grantor = get_roleid_checked(stmt->grantor); + else + grantor = GetUserId(); - if (duserElts) - userElts = (List *) duserElts->arg; + grantee_ids = roleNamesToIds(stmt->grantee_roles); /* - * Make sure the user can do this. - */ - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to create groups"))); - - if (strcmp(stmt->name, "public") == 0) - ereport(ERROR, - (errcode(ERRCODE_RESERVED_NAME), - errmsg("group name \"%s\" is reserved", - stmt->name))); - - /* - * Scan the pg_group relation to be certain the group or id doesn't - * already exist. Note we secure exclusive lock, because we also need - * to be sure of what the next grosysid should be, and we need to - * protect our eventual update of the flat group file. + * Step through all of the granted roles and add/remove + * entries for the grantees, or, if admin_opt is set, then + * just add/remove the admin option. + * + * Note: Permissions checking is done by AddRoleMems/DelRoleMems */ - pg_group_rel = heap_open(GroupRelationId, ExclusiveLock); - pg_group_dsc = RelationGetDescr(pg_group_rel); - - scan = heap_beginscan(pg_group_rel, SnapshotNow, 0, NULL); - max_id = 99; /* start auto-assigned ids at 100 */ - while (!group_exists && !sysid_exists && - (tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + foreach(item, stmt->granted_roles) { - Form_pg_group group_form = (Form_pg_group) GETSTRUCT(tuple); - int32 this_sysid; + char *rolename = strVal(lfirst(item)); + Oid roleid = get_roleid_checked(rolename); - group_exists = (strcmp(NameStr(group_form->groname), stmt->name) == 0); - - this_sysid = group_form->grosysid; - if (havesysid) /* customized id wanted */ - sysid_exists = (this_sysid == sysid); + if (stmt->is_grant) + AddRoleMems(rolename, roleid, + stmt->grantee_roles, grantee_ids, + grantor, stmt->admin_opt); else - { - /* pick 1 + max */ - if (this_sysid > max_id) - max_id = this_sysid; - } + DelRoleMems(rolename, roleid, + stmt->grantee_roles, grantee_ids, + stmt->admin_opt); } - heap_endscan(scan); - - if (group_exists) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("group \"%s\" already exists", - stmt->name))); - if (sysid_exists) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("group ID %d is already assigned", sysid))); +} - /* If no sysid given, use max existing id + 1 */ - if (!havesysid) - sysid = max_id + 1; +/* + * roleNamesToIds + * + * Given a list of role names (as String nodes), generate a list of role OIDs + * in the same order. + */ +static List * +roleNamesToIds(List *memberNames) +{ + List *result = NIL; + ListCell *l; - /* - * Translate the given user names to ids - */ - foreach(item, userElts) + foreach(l, memberNames) { - const char *groupuser = strVal(lfirst(item)); - int32 userid = get_usesysid(groupuser); + char *rolename = strVal(lfirst(l)); + Oid roleid = get_roleid_checked(rolename); - if (!list_member_int(newlist, userid)) - newlist = lappend_int(newlist, userid); + result = lappend_oid(result, roleid); } - - /* build an array to insert */ - if (newlist) - grolist = IdListToArray(newlist); - else - grolist = NULL; - - /* - * Form a tuple to insert - */ - new_record[Anum_pg_group_groname - 1] = - DirectFunctionCall1(namein, CStringGetDatum(stmt->name)); - new_record[Anum_pg_group_grosysid - 1] = Int32GetDatum(sysid); - new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(grolist); - - new_record_nulls[Anum_pg_group_groname - 1] = ' '; - new_record_nulls[Anum_pg_group_grosysid - 1] = ' '; - new_record_nulls[Anum_pg_group_grolist - 1] = grolist ? ' ' : 'n'; - - tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls); - - /* - * Insert a new record in the pg_group table - */ - simple_heap_insert(pg_group_rel, tuple); - - /* Update indexes */ - CatalogUpdateIndexes(pg_group_rel, tuple); - - /* - * Now we can clean up; but keep lock until commit (to avoid possible - * deadlock when commit code tries to acquire lock). - */ - heap_close(pg_group_rel, NoLock); - - /* - * Set flag to update flat group file at commit. - */ - group_file_update_needed(); + return result; } - /* - * ALTER GROUP + * AddRoleMems -- Add given members to the specified role + * + * rolename: name of role to add to (used only for error messages) + * roleid: OID of role to add to + * memberNames: list of names of roles to add (used only for error messages) + * memberIds: OIDs of roles to add + * grantorId: who is granting the membership + * admin_opt: granting admin option? */ -void -AlterGroup(AlterGroupStmt *stmt, const char *tag) +static void +AddRoleMems(const char *rolename, Oid roleid, + List *memberNames, List *memberIds, + Oid grantorId, bool admin_opt) { - Relation pg_group_rel; - TupleDesc pg_group_dsc; - HeapTuple group_tuple; - IdList *oldarray; - Datum datum; - bool null; - List *newlist; - ListCell *item; + Relation pg_authmem_rel; + TupleDesc pg_authmem_dsc; + ListCell *nameitem; + ListCell *iditem; - /* - * Make sure the user can do this. - */ - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to alter groups"))); + Assert(list_length(memberNames) == list_length(memberIds)); - /* - * Secure exclusive lock to protect our update of the flat group file. - */ - pg_group_rel = heap_open(GroupRelationId, ExclusiveLock); - pg_group_dsc = RelationGetDescr(pg_group_rel); + /* Skip permission check if nothing to do */ + if (!memberIds) + return; /* - * Fetch existing tuple for group. + * Check permissions: must be superuser or have admin option on the + * role to be changed. + * + * XXX: The admin option is not considered to be inherited through + * multiple roles, unlike normal 'is_member_of_role' privilege checks. */ - group_tuple = SearchSysCache(GRONAME, - PointerGetDatum(stmt->name), - 0, 0, 0); - if (!HeapTupleIsValid(group_tuple)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("group \"%s\" does not exist", stmt->name))); + if (!superuser()) + { + HeapTuple authmem_chk_tuple; + Form_pg_auth_members authmem_chk; - /* Fetch old group membership. */ - datum = heap_getattr(group_tuple, Anum_pg_group_grolist, - pg_group_dsc, &null); - oldarray = null ? NULL : DatumGetIdListP(datum); + if (grantorId != GetUserId()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to set grantor ID"))); + + authmem_chk_tuple = SearchSysCache(AUTHMEMROLEMEM, + ObjectIdGetDatum(roleid), + ObjectIdGetDatum(grantorId), + 0, 0); + if (!HeapTupleIsValid(authmem_chk_tuple)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser or have admin option on role \"%s\"", + rolename))); - /* initialize list with old array contents */ - newlist = IdArrayToList(oldarray); + authmem_chk = (Form_pg_auth_members) GETSTRUCT(authmem_chk_tuple); + if (!authmem_chk->admin_option) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser or have admin option on role \"%s\"", + rolename))); + ReleaseSysCache(authmem_chk_tuple); + } /* - * Now decide what to do. + * Secure exclusive lock to protect our update of the flat auth file. */ - AssertState(stmt->action == +1 || stmt->action == -1); + pg_authmem_rel = heap_open(AuthMemRelationId, ExclusiveLock); + pg_authmem_dsc = RelationGetDescr(pg_authmem_rel); - if (stmt->action == +1) /* add users, might also be invoked by - * create user */ + forboth(nameitem, memberNames, iditem, memberIds) { + const char *membername = strVal(lfirst(nameitem)); + Oid memberid = lfirst_oid(iditem); + HeapTuple authmem_tuple; + HeapTuple tuple; + Datum new_record[Natts_pg_auth_members]; + char new_record_nulls[Natts_pg_auth_members]; + char new_record_repl[Natts_pg_auth_members]; + /* - * convert the to be added usernames to sysids and add them to the - * list + * Check if entry for this role/member already exists; + * if so, give warning unless we are adding admin option. */ - foreach(item, stmt->listUsers) + authmem_tuple = SearchSysCache(AUTHMEMROLEMEM, + ObjectIdGetDatum(roleid), + ObjectIdGetDatum(memberid), + 0, 0); + if (HeapTupleIsValid(authmem_tuple) && !admin_opt) { - int32 sysid; - - if (strcmp(tag, "ALTER GROUP") == 0) - { - /* Get the uid of the proposed user to add. */ - sysid = get_usesysid(strVal(lfirst(item))); - } - 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 - */ - sysid = intVal(lfirst(item)); - } - else - { - elog(ERROR, "unexpected tag: \"%s\"", tag); - sysid = 0; /* keep compiler quiet */ - } - - if (!list_member_int(newlist, sysid)) - newlist = lappend_int(newlist, sysid); + ereport(NOTICE, + (errmsg("role \"%s\" is already a member of role \"%s\"", + membername, rolename))); + ReleaseSysCache(authmem_tuple); + continue; } - /* Do the update */ - UpdateGroupMembership(pg_group_rel, group_tuple, newlist); - } /* endif alter group add user */ + /* Build a tuple to insert or update */ + MemSet(new_record, 0, sizeof(new_record)); + MemSet(new_record_nulls, ' ', sizeof(new_record_nulls)); + MemSet(new_record_repl, ' ', sizeof(new_record_repl)); - else if (stmt->action == -1) /* drop users from group */ - { - bool is_dropuser = strcmp(tag, "DROP USER") == 0; + new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid); + new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid); + new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId); + new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt); - if (newlist == NIL) + if (HeapTupleIsValid(authmem_tuple)) { - if (!is_dropuser) - ereport(WARNING, - (errcode(ERRCODE_WARNING), - errmsg("group \"%s\" does not have any members", - stmt->name))); + new_record_repl[Anum_pg_auth_members_grantor - 1] = 'r'; + new_record_repl[Anum_pg_auth_members_admin_option - 1] = 'r'; + tuple = heap_modifytuple(authmem_tuple, pg_authmem_dsc, + new_record, + new_record_nulls, new_record_repl); + simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple); + CatalogUpdateIndexes(pg_authmem_rel, tuple); + ReleaseSysCache(authmem_tuple); } else { - /* - * convert the to be dropped usernames to sysids and remove - * them from the list - */ - foreach(item, stmt->listUsers) - { - int32 sysid; - - if (!is_dropuser) - { - /* Get the uid of the proposed user to drop. */ - sysid = get_usesysid(strVal(lfirst(item))); - } - else - { - /* for dropuser we already know the uid */ - sysid = intVal(lfirst(item)); - } - if (list_member_int(newlist, sysid)) - newlist = list_delete_int(newlist, sysid); - else if (!is_dropuser) - ereport(WARNING, - (errcode(ERRCODE_WARNING), - errmsg("user \"%s\" is not in group \"%s\"", - strVal(lfirst(item)), stmt->name))); - } - - /* Do the update */ - UpdateGroupMembership(pg_group_rel, group_tuple, newlist); - } /* endif group not null */ - } /* endif alter group drop user */ - - ReleaseSysCache(group_tuple); + tuple = heap_formtuple(pg_authmem_dsc, + new_record, new_record_nulls); + simple_heap_insert(pg_authmem_rel, tuple); + CatalogUpdateIndexes(pg_authmem_rel, tuple); + } + } /* * Now we can clean up; but keep lock until commit (to avoid possible * deadlock when commit code tries to acquire lock). */ - heap_close(pg_group_rel, NoLock); - - /* - * Set flag to update flat group file at commit. - */ - group_file_update_needed(); + heap_close(pg_authmem_rel, NoLock); } /* - * Subroutine for AlterGroup: given a pg_group tuple and a desired new - * membership (expressed as an integer list), form and write an updated tuple. - * The pg_group relation must be open and locked already. + * DelRoleMems -- Remove given members from the specified role + * + * rolename: name of role to del from (used only for error messages) + * roleid: OID of role to del from + * memberNames: list of names of roles to del (used only for error messages) + * memberIds: OIDs of roles to del + * admin_opt: remove admin option only? */ static void -UpdateGroupMembership(Relation group_rel, HeapTuple group_tuple, - List *members) +DelRoleMems(const char *rolename, Oid roleid, + List *memberNames, List *memberIds, + bool admin_opt) { - IdList *newarray; - Datum new_record[Natts_pg_group]; - char new_record_nulls[Natts_pg_group]; - char new_record_repl[Natts_pg_group]; - HeapTuple tuple; + Relation pg_authmem_rel; + TupleDesc pg_authmem_dsc; + ListCell *nameitem; + ListCell *iditem; - newarray = IdListToArray(members); + Assert(list_length(memberNames) == list_length(memberIds)); + + /* Skip permission check if nothing to do */ + if (!memberIds) + return; /* - * Form an updated tuple with the new array and write it back. + * Check permissions: must be superuser or have admin option on the + * role to be changed. + * + * XXX: The admin option is not considered to be inherited through + * multiple roles, unlike normal 'is_member_of_role' privilege checks. */ - MemSet(new_record, 0, sizeof(new_record)); - MemSet(new_record_nulls, ' ', sizeof(new_record_nulls)); - MemSet(new_record_repl, ' ', sizeof(new_record_repl)); - - new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray); - new_record_repl[Anum_pg_group_grolist - 1] = 'r'; - - tuple = heap_modifytuple(group_tuple, RelationGetDescr(group_rel), - new_record, new_record_nulls, new_record_repl); - - simple_heap_update(group_rel, &group_tuple->t_self, tuple); - - /* Update indexes */ - CatalogUpdateIndexes(group_rel, tuple); -} - - -/* - * Convert an integer list of sysids to an array. - */ -static IdList * -IdListToArray(List *members) -{ - int nmembers = list_length(members); - IdList *newarray; - ListCell *item; - int i; - - newarray = palloc(ARR_OVERHEAD(1) + nmembers * sizeof(int32)); - newarray->size = ARR_OVERHEAD(1) + nmembers * sizeof(int32); - newarray->flags = 0; - newarray->elemtype = INT4OID; - ARR_NDIM(newarray) = 1; /* one dimensional array */ - ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */ - ARR_DIMS(newarray)[0] = nmembers; /* axis is this long */ - i = 0; - foreach(item, members) - ((int *) ARR_DATA_PTR(newarray))[i++] = lfirst_int(item); - - return newarray; -} - -/* - * Convert an array of sysids to an integer list. - */ -static List * -IdArrayToList(IdList *oldarray) -{ - List *newlist = NIL; - int hibound, - i; - - if (oldarray == NULL) - return NIL; - - Assert(ARR_NDIM(oldarray) == 1); - Assert(ARR_ELEMTYPE(oldarray) == INT4OID); - - hibound = ARR_DIMS(oldarray)[0]; - - for (i = 0; i < hibound; i++) + if (!superuser()) { - int32 sysid; + HeapTuple authmem_chk_tuple; + Form_pg_auth_members authmem_chk; + + authmem_chk_tuple = SearchSysCache(AUTHMEMROLEMEM, + ObjectIdGetDatum(roleid), + ObjectIdGetDatum(GetUserId()), + 0, 0); + if (!HeapTupleIsValid(authmem_chk_tuple)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser or have admin option on role \"%s\"", + rolename))); - sysid = ((int32 *) ARR_DATA_PTR(oldarray))[i]; - /* filter out any duplicates --- probably a waste of time */ - if (!list_member_int(newlist, sysid)) - newlist = lappend_int(newlist, sysid); + authmem_chk = (Form_pg_auth_members) GETSTRUCT(authmem_chk_tuple); + if (!authmem_chk->admin_option) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser or have admin option on role \"%s\"", + rolename))); + ReleaseSysCache(authmem_chk_tuple); } - return newlist; -} - - -/* - * DROP GROUP - */ -void -DropGroup(DropGroupStmt *stmt) -{ - Relation pg_group_rel; - HeapTuple tuple; - /* - * Make sure the user can do this. + * Secure exclusive lock to protect our update of the flat auth file. */ - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to drop groups"))); + pg_authmem_rel = heap_open(AuthMemRelationId, ExclusiveLock); + pg_authmem_dsc = RelationGetDescr(pg_authmem_rel); - /* - * Secure exclusive lock to protect our update of the flat group file. - */ - pg_group_rel = heap_open(GroupRelationId, ExclusiveLock); + forboth(nameitem, memberNames, iditem, memberIds) + { + const char *membername = strVal(lfirst(nameitem)); + Oid memberid = lfirst_oid(iditem); + HeapTuple authmem_tuple; - /* Find and delete the group. */ + /* + * Find entry for this role/member + */ + authmem_tuple = SearchSysCache(AUTHMEMROLEMEM, + ObjectIdGetDatum(roleid), + ObjectIdGetDatum(memberid), + 0, 0); + if (!HeapTupleIsValid(authmem_tuple)) + { + ereport(WARNING, + (errmsg("role \"%s\" is not a member of role \"%s\"", + membername, rolename))); + continue; + } - tuple = SearchSysCacheCopy(GRONAME, - PointerGetDatum(stmt->name), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("group \"%s\" does not exist", stmt->name))); + if (!admin_opt) + { + /* Remove the entry altogether */ + simple_heap_delete(pg_authmem_rel, &authmem_tuple->t_self); + } + else + { + /* Just turn off the admin option */ + HeapTuple tuple; + Datum new_record[Natts_pg_auth_members]; + char new_record_nulls[Natts_pg_auth_members]; + char new_record_repl[Natts_pg_auth_members]; + + /* Build a tuple to update with */ + MemSet(new_record, 0, sizeof(new_record)); + MemSet(new_record_nulls, ' ', sizeof(new_record_nulls)); + MemSet(new_record_repl, ' ', sizeof(new_record_repl)); + + new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false); + new_record_repl[Anum_pg_auth_members_admin_option - 1] = 'r'; + + tuple = heap_modifytuple(authmem_tuple, pg_authmem_dsc, + new_record, + new_record_nulls, new_record_repl); + simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple); + CatalogUpdateIndexes(pg_authmem_rel, tuple); + } - simple_heap_delete(pg_group_rel, &tuple->t_self); + ReleaseSysCache(authmem_tuple); + } /* * Now we can clean up; but keep lock until commit (to avoid possible * deadlock when commit code tries to acquire lock). */ - heap_close(pg_group_rel, NoLock); - - /* - * Set flag to update flat group file at commit. - */ - group_file_update_needed(); -} - - -/* - * Rename group - */ -void -RenameGroup(const char *oldname, const char *newname) -{ - HeapTuple tup; - Relation rel; - - /* ExclusiveLock because we need to update the flat group file */ - rel = heap_open(GroupRelationId, ExclusiveLock); - - tup = SearchSysCacheCopy(GRONAME, - CStringGetDatum(oldname), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("group \"%s\" does not exist", oldname))); - - /* make sure the new name doesn't exist */ - if (SearchSysCacheExists(GRONAME, - CStringGetDatum(newname), - 0, 0, 0)) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("group \"%s\" already exists", newname))); - - /* must be superuser */ - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to rename groups"))); - - /* rename */ - namestrcpy(&(((Form_pg_group) GETSTRUCT(tup))->groname), newname); - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); - - heap_close(rel, NoLock); - heap_freetuple(tup); - - group_file_update_needed(); + heap_close(pg_authmem_rel, NoLock); } diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index 048a3e41562..7df2a92a6c6 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.108 2005/06/09 21:52:07 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.109 2005/06/28 05:08:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,7 +19,7 @@ #include <ctype.h> #include "access/xact.h" -#include "catalog/pg_shadow.h" +#include "catalog/pg_authid.h" #include "commands/variable.h" #include "miscadmin.h" #include "parser/scansup.h" @@ -567,46 +567,46 @@ assign_client_encoding(const char *value, bool doit, GucSource source) * SET SESSION AUTHORIZATION * * When resetting session auth after an error, we can't expect to do catalog - * lookups. Hence, the stored form of the value must provide a numeric userid + * lookups. Hence, the stored form of the value must provide a numeric oid * that can be re-used directly. We store the string in the form of * NAMEDATALEN 'x's, followed by T or F to indicate superuserness, followed - * by the numeric userid, followed by a comma, followed by the user name. - * This cannot be confused with a plain user name because of the NAMEDATALEN + * by the numeric oid, followed by a comma, followed by the role name. + * This cannot be confused with a plain role name because of the NAMEDATALEN * limit on names, so we can tell whether we're being passed an initial - * username or a saved/restored value. + * role name or a saved/restored value. */ extern char *session_authorization_string; /* in guc.c */ const char * assign_session_authorization(const char *value, bool doit, GucSource source) { - AclId usesysid = 0; + Oid roleid = InvalidOid; bool is_superuser = false; - const char *actual_username = NULL; + const char *actual_rolename = NULL; char *result; if (strspn(value, "x") == NAMEDATALEN && (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F')) { /* might be a saved userid string */ - AclId savedsysid; + Oid savedoid; char *endptr; - savedsysid = (AclId) strtoul(value + NAMEDATALEN + 1, &endptr, 10); + savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10); if (endptr != value + NAMEDATALEN + 1 && *endptr == ',') { /* syntactically valid, so break out the data */ - usesysid = savedsysid; + roleid = savedoid; is_superuser = (value[NAMEDATALEN] == 'T'); - actual_username = endptr + 1; + actual_rolename = endptr + 1; } } - if (usesysid == 0) + if (roleid == InvalidOid) { /* not a saved ID, so look it up */ - HeapTuple userTup; + HeapTuple roleTup; if (!IsTransactionState()) { @@ -618,38 +618,38 @@ assign_session_authorization(const char *value, bool doit, GucSource source) return NULL; } - userTup = SearchSysCache(SHADOWNAME, + roleTup = SearchSysCache(AUTHNAME, PointerGetDatum(value), 0, 0, 0); - if (!HeapTupleIsValid(userTup)) + if (!HeapTupleIsValid(roleTup)) { if (source >= PGC_S_INTERACTIVE) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("user \"%s\" does not exist", value))); + errmsg("role \"%s\" does not exist", value))); return NULL; } - usesysid = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid; - is_superuser = ((Form_pg_shadow) GETSTRUCT(userTup))->usesuper; - actual_username = value; + roleid = HeapTupleGetOid(roleTup); + is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper; + actual_rolename = value; - ReleaseSysCache(userTup); + ReleaseSysCache(roleTup); } if (doit) - SetSessionAuthorization(usesysid, is_superuser); + SetSessionAuthorization(roleid, is_superuser); - result = (char *) malloc(NAMEDATALEN + 32 + strlen(actual_username)); + result = (char *) malloc(NAMEDATALEN + 32 + strlen(actual_rolename)); if (!result) return NULL; memset(result, 'x', NAMEDATALEN); - sprintf(result + NAMEDATALEN, "%c%lu,%s", + sprintf(result + NAMEDATALEN, "%c%u,%s", is_superuser ? 'T' : 'F', - (unsigned long) usesysid, - actual_username); + roleid, + actual_rolename); return result; } @@ -662,13 +662,13 @@ show_session_authorization(void) * assign_session_authorization */ const char *value = session_authorization_string; - AclId savedsysid; + Oid savedoid; char *endptr; Assert(strspn(value, "x") == NAMEDATALEN && (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F')); - savedsysid = (AclId) strtoul(value + NAMEDATALEN + 1, &endptr, 10); + savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10); Assert(endptr != value + NAMEDATALEN + 1 && *endptr == ','); |