From b0bcf8aab2da6710ff8842b86fed93571e143cc8 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 21 Apr 2002 00:26:44 +0000 Subject: [PATCH] Restructure AclItem representation so that we can have more than eight different privilege bits (might as well make use of the space we were wasting on padding). EXECUTE and USAGE bits for procedures, languages now are separate privileges instead of being overlaid on SELECT. Add privileges for namespaces and databases. The GRANT and REVOKE commands work for these object types, but we don't actually enforce the privileges yet... --- doc/src/sgml/catalogs.sgml | 11 +- doc/src/sgml/ref/grant.sgml | 54 ++- doc/src/sgml/ref/revoke.sgml | 12 +- src/backend/catalog/aclchk.c | 668 +++++++++++++++++++++------- src/backend/commands/dbcommands.c | 15 +- src/backend/commands/functioncmds.c | 4 +- src/backend/parser/gram.y | 52 ++- src/backend/parser/keywords.c | 35 +- src/backend/utils/adt/acl.c | 289 +++++------- src/backend/utils/cache/fcache.c | 4 +- src/bin/initdb/initdb.sh | 6 +- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_attribute.h | 3 +- src/include/catalog/pg_class.h | 4 +- src/include/catalog/pg_database.h | 10 +- src/include/catalog/pg_namespace.h | 6 +- src/include/catalog/pg_type.h | 8 +- src/include/nodes/parsenodes.h | 42 +- src/include/parser/keywords.h | 3 +- src/include/utils/acl.h | 112 ++--- 20 files changed, 832 insertions(+), 510 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 66fedbce36..980c215536 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,6 +1,6 @@ @@ -825,7 +825,7 @@ If true then this database can be used in the TEMPLATE clause of CREATE - DATABASE to create the new database as a clone of + DATABASE to create a new database as a clone of this one. @@ -890,6 +890,13 @@ Session defaults for run-time configuration variables + + + datacl + aclitem[] + + Access permissions + diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml index 720c553957..70e9d581c8 100644 --- a/doc/src/sgml/ref/grant.sgml +++ b/doc/src/sgml/ref/grant.sgml @@ -1,5 +1,5 @@ @@ -18,7 +18,11 @@ PostgreSQL documentation GRANT { { SELECT | INSERT | UPDATE | DELETE | RULE | REFERENCES | TRIGGER } [,...] | ALL [ PRIVILEGES ] } - ON [ TABLE ] objectname [, ...] + ON [ TABLE ] tablename [, ...] + TO { username | GROUP groupname | PUBLIC } [, ...] + +GRANT { { CREATE | TEMPORARY | TEMP } [,...] | ALL [ PRIVILEGES ] } + ON DATABASE dbname [, ...] TO { username | GROUP groupname | PUBLIC } [, ...] GRANT { EXECUTE | ALL [ PRIVILEGES ] } @@ -28,6 +32,10 @@ GRANT { EXECUTE | ALL [ PRIVILEGES ] } GRANT { USAGE | ALL [ PRIVILEGES ] } ON LANGUAGE langname [, ...] TO { username | GROUP groupname | PUBLIC } [, ...] + +GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] } + ON SCHEMA schemaname [, ...] + TO { username | GROUP groupname | PUBLIC } [, ...] @@ -36,7 +44,8 @@ GRANT { USAGE | ALL [ PRIVILEGES ] } The GRANT command gives specific permissions on - an object (table, view, sequence, function, procedural language) to + an object (table, view, sequence, database, function, procedural language, + or schema) to one or more users or groups of users. These permissions are added to those already granted, if any. @@ -144,6 +153,29 @@ GRANT { USAGE | ALL [ PRIVILEGES ] } + + CREATE + + + For databases, allows new schemas to be created in the database. + + + For schemas, allows new objects to be created within the specified + schema. + + + + + + TEMPORARY + TEMP + + + Allows temporary tables to be created while using the database. + + + + EXECUTE @@ -159,10 +191,16 @@ GRANT { USAGE | ALL [ PRIVILEGES ] } USAGE - Allows the use of the specified procedural language for the - creation of functions in that language. This is the only type + For procedural languages, allows the use of the specified language for + the creation of functions in that language. This is the only type of privilege that is applicable to procedural languages. + + For schemas, allows the use of objects contained in the specified + schema (assuming that the objects' own privilege requirements are + met). Essentially this allows the grantee to look up + objects within the schema. + @@ -226,7 +264,11 @@ GRANT { USAGE | ALL [ PRIVILEGES ] } R -- RULE x -- REFERENCES t -- TRIGGER - arwdRxt -- ALL PRIVILEGES + X -- EXECUTE + U -- USAGE + C -- CREATE + T -- TEMPORARY + arwdRxt -- ALL PRIVILEGES (for tables) diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml index 60c31a37c8..3bc30cfd49 100644 --- a/doc/src/sgml/ref/revoke.sgml +++ b/doc/src/sgml/ref/revoke.sgml @@ -1,5 +1,5 @@ @@ -18,7 +18,11 @@ PostgreSQL documentation REVOKE { { SELECT | INSERT | UPDATE | DELETE | RULE | REFERENCES | TRIGGER } [,...] | ALL [ PRIVILEGES ] } - ON [ TABLE ] object [, ...] + ON [ TABLE ] tablename [, ...] + FROM { username | GROUP groupname | PUBLIC } [, ...] + +REVOKE { { CREATE | TEMPORARY | TEMP } [,...] | ALL [ PRIVILEGES ] } + ON DATABASE dbname [, ...] FROM { username | GROUP groupname | PUBLIC } [, ...] REVOKE { EXECUTE | ALL [ PRIVILEGES ] } @@ -28,6 +32,10 @@ REVOKE { EXECUTE | ALL [ PRIVILEGES ] } REVOKE { USAGE | ALL [ PRIVILEGES ] } ON LANGUAGE langname [, ...] FROM { username | GROUP groupname | PUBLIC } [, ...] + +REVOKE { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] } + ON SCHEMA schemaname [, ...] + FROM { username | GROUP groupname | PUBLIC } [, ...] diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 5f223aab9d..48cca5fcb8 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.65 2002/04/12 20:38:17 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.66 2002/04/21 00:26:42 tgl Exp $ * * NOTES * See acl.h. @@ -18,37 +18,35 @@ #include "postgres.h" #include "access/heapam.h" -#include "access/transam.h" #include "catalog/catalog.h" #include "catalog/catname.h" #include "catalog/indexing.h" #include "catalog/namespace.h" -#include "catalog/pg_aggregate.h" +#include "catalog/pg_database.h" #include "catalog/pg_group.h" #include "catalog/pg_language.h" +#include "catalog/pg_namespace.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" #include "catalog/pg_shadow.h" #include "catalog/pg_type.h" #include "miscadmin.h" -#include "nodes/parsenodes.h" -#include "parser/keywords.h" -#include "parser/parse.h" -#include "parser/parse_agg.h" #include "parser/parse_func.h" -#include "parser/parse_expr.h" -#include "parser/parse_type.h" #include "utils/acl.h" +#include "utils/fmgroids.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" -static void ExecuteGrantStmt_Table(GrantStmt *stmt); +static void ExecuteGrantStmt_Relation(GrantStmt *stmt); +static void ExecuteGrantStmt_Database(GrantStmt *stmt); static void ExecuteGrantStmt_Function(GrantStmt *stmt); -static void ExecuteGrantStmt_Lang(GrantStmt *stmt); +static void ExecuteGrantStmt_Language(GrantStmt *stmt); +static void ExecuteGrantStmt_Namespace(GrantStmt *stmt); -static const char *privilege_token_string(int token); +static const char *privilege_to_string(AclMode privilege); -static int32 aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode); +static int32 aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode); /* warning messages, now more explicit. */ /* MUST correspond to the order of the ACLCHECK_* result codes in acl.h. */ @@ -83,12 +81,16 @@ dumpacl(Acl *acl) * grantees to the existing old_acl. If is_grant is false, the * privileges for the given grantees are removed from old_acl. */ -static Acl* -merge_acl_with_grant(Acl *old_acl, bool is_grant, List *grantees, char *privileges) +static Acl * +merge_acl_with_grant(Acl *old_acl, bool is_grant, + List *grantees, AclMode privileges) { + unsigned modechg; List *j; Acl *new_acl; + modechg = is_grant ? ACL_MODECHG_ADD : ACL_MODECHG_DEL; + #ifdef ACLDEBUG dumpacl(old_acl); #endif @@ -97,23 +99,27 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant, List *grantees, char *privileg foreach(j, grantees) { PrivGrantee *grantee = (PrivGrantee *) lfirst(j); - char *granteeString; - char *aclString; - AclItem aclitem; - unsigned modechg; + AclItem aclitem; + uint32 idtype; if (grantee->username) - granteeString = aclmakeuser("U", grantee->username); + { + aclitem.ai_id = get_usesysid(grantee->username); + idtype = ACL_IDTYPE_UID; + } else if (grantee->groupname) - granteeString = aclmakeuser("G", grantee->groupname); + { + aclitem.ai_id = get_grosysid(grantee->groupname); + idtype = ACL_IDTYPE_GID; + } else - granteeString = aclmakeuser("A", ""); + { + aclitem.ai_id = ACL_ID_WORLD; + idtype = ACL_IDTYPE_WORLD; + } - aclString = makeAclString(privileges, granteeString, - is_grant ? '+' : '-'); + ACLITEM_SET_PRIVS_IDTYPE(aclitem, privileges, idtype); - /* Convert string ACL spec into internal form */ - aclparse(aclString, &aclitem, &modechg); new_acl = aclinsert3(new_acl, &aclitem, modechg); #ifdef ACLDEBUG @@ -131,74 +137,50 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant, List *grantees, char *privileg void ExecuteGrantStmt(GrantStmt *stmt) { - /* see comment in pg_type.h */ - Assert(ACLITEMSIZE == sizeof(AclItem)); - - switch(stmt->objtype) + switch (stmt->objtype) { - case TABLE: - ExecuteGrantStmt_Table(stmt); + case ACL_OBJECT_RELATION: + ExecuteGrantStmt_Relation(stmt); + break; + case ACL_OBJECT_DATABASE: + ExecuteGrantStmt_Database(stmt); break; - case FUNCTION: + case ACL_OBJECT_FUNCTION: ExecuteGrantStmt_Function(stmt); break; - case LANGUAGE: - ExecuteGrantStmt_Lang(stmt); + case ACL_OBJECT_LANGUAGE: + ExecuteGrantStmt_Language(stmt); + break; + case ACL_OBJECT_NAMESPACE: + ExecuteGrantStmt_Namespace(stmt); break; default: - elog(ERROR, "bogus GrantStmt.objtype %d", stmt->objtype); + elog(ERROR, "bogus GrantStmt.objtype %d", (int) stmt->objtype); } } - static void -ExecuteGrantStmt_Table(GrantStmt *stmt) +ExecuteGrantStmt_Relation(GrantStmt *stmt) { + AclMode privileges; List *i; - char *privstring; - if (lfirsti(stmt->privileges) == ALL) - privstring = aclmakepriv(ACL_MODE_STR, 0); + if (lfirsti(stmt->privileges) == ACL_ALL_RIGHTS) + privileges = ACL_ALL_RIGHTS_RELATION; else { - privstring = ""; + privileges = ACL_NO_RIGHTS; foreach(i, stmt->privileges) { - int c = 0; + AclMode priv = lfirsti(i); - switch(lfirsti(i)) - { - case SELECT: - c = ACL_MODE_SELECT_CHR; - break; - case INSERT: - c = ACL_MODE_INSERT_CHR; - break; - case UPDATE: - c = ACL_MODE_UPDATE_CHR; - break; - case DELETE: - c = ACL_MODE_DELETE_CHR; - break; - case RULE: - c = ACL_MODE_RULE_CHR; - break; - case REFERENCES: - c = ACL_MODE_REFERENCES_CHR; - break; - case TRIGGER: - c = ACL_MODE_TRIGGER_CHR; - break; - default: - elog(ERROR, "invalid privilege type %s for table object", - privilege_token_string(lfirsti(i))); - } - - privstring = aclmakepriv(privstring, c); + if (priv & ~((AclMode) ACL_ALL_RIGHTS_RELATION)) + elog(ERROR, "invalid privilege type %s for table object", + privilege_to_string(priv)); + privileges |= priv; } } - foreach(i, stmt->objects) { RangeVar *relvar = (RangeVar *) lfirst(i); @@ -210,7 +192,6 @@ ExecuteGrantStmt_Table(GrantStmt *stmt) bool isNull; Acl *old_acl; Acl *new_acl; - unsigned i; HeapTuple newtuple; Datum values[Natts_pg_class]; char nulls[Natts_pg_class]; @@ -241,24 +222,23 @@ ExecuteGrantStmt_Table(GrantStmt *stmt) aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl, &isNull); if (isNull) - old_acl = acldefault(pg_class_tuple->relowner); + old_acl = acldefault(ACL_OBJECT_RELATION, + pg_class_tuple->relowner); else - /* get a detoasted copy of the rel's ACL */ + /* get a detoasted copy of the ACL */ old_acl = DatumGetAclPCopy(aclDatum); new_acl = merge_acl_with_grant(old_acl, stmt->is_grant, - stmt->grantees, privstring); + stmt->grantees, privileges); /* finished building new ACL value, now insert it */ - for (i = 0; i < Natts_pg_class; ++i) - { - replaces[i] = ' '; - nulls[i] = ' '; /* ignored if replaces[i]==' ' anyway */ - values[i] = (Datum) NULL; /* ignored if replaces[i]==' ' - * anyway */ - } + MemSet(values, 0, sizeof(values)); + MemSet(nulls, ' ', sizeof(nulls)); + MemSet(replaces, ' ', sizeof(replaces)); + replaces[Anum_pg_class_relacl - 1] = 'r'; values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl); + newtuple = heap_modifytuple(tuple, relation, values, nulls, replaces); ReleaseSysCache(tuple); @@ -282,25 +262,124 @@ ExecuteGrantStmt_Table(GrantStmt *stmt) } } +static void +ExecuteGrantStmt_Database(GrantStmt *stmt) +{ + AclMode privileges; + List *i; + + if (lfirsti(stmt->privileges) == ACL_ALL_RIGHTS) + privileges = ACL_ALL_RIGHTS_DATABASE; + else + { + privileges = ACL_NO_RIGHTS; + foreach(i, stmt->privileges) + { + AclMode priv = lfirsti(i); + + if (priv & ~((AclMode) ACL_ALL_RIGHTS_DATABASE)) + elog(ERROR, "invalid privilege type %s for database object", + privilege_to_string(priv)); + privileges |= priv; + } + } + + foreach(i, stmt->objects) + { + char *dbname = strVal(lfirst(i)); + Relation relation; + ScanKeyData entry[1]; + HeapScanDesc scan; + HeapTuple tuple; + Form_pg_database pg_database_tuple; + Datum aclDatum; + bool isNull; + Acl *old_acl; + Acl *new_acl; + HeapTuple newtuple; + Datum values[Natts_pg_database]; + char nulls[Natts_pg_database]; + char replaces[Natts_pg_database]; + + relation = heap_openr(DatabaseRelationName, RowExclusiveLock); + ScanKeyEntryInitialize(&entry[0], 0, + Anum_pg_database_datname, F_NAMEEQ, + CStringGetDatum(dbname)); + scan = heap_beginscan(relation, 0, SnapshotNow, 1, entry); + tuple = heap_getnext(scan, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "database \"%s\" not found", dbname); + pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple); + + if (!superuser() && pg_database_tuple->datdba != GetUserId()) + elog(ERROR, "permission denied"); + + /* + * If there's no ACL, create a default. + */ + aclDatum = heap_getattr(tuple, Anum_pg_database_datacl, + RelationGetDescr(relation), &isNull); + if (isNull) + old_acl = acldefault(ACL_OBJECT_DATABASE, + pg_database_tuple->datdba); + else + /* get a detoasted copy of the ACL */ + old_acl = DatumGetAclPCopy(aclDatum); + + new_acl = merge_acl_with_grant(old_acl, stmt->is_grant, + stmt->grantees, privileges); + + /* finished building new ACL value, now insert it */ + MemSet(values, 0, sizeof(values)); + MemSet(nulls, ' ', sizeof(nulls)); + MemSet(replaces, ' ', sizeof(replaces)); + + replaces[Anum_pg_database_datacl - 1] = 'r'; + values[Anum_pg_database_datacl - 1] = PointerGetDatum(new_acl); + + newtuple = heap_modifytuple(tuple, relation, values, nulls, replaces); + + simple_heap_update(relation, &newtuple->t_self, newtuple); + + { + /* keep the catalog indexes up to date */ + Relation idescs[Num_pg_database_indices]; + + CatalogOpenIndices(Num_pg_database_indices, Name_pg_database_indices, + idescs); + CatalogIndexInsert(idescs, Num_pg_database_indices, relation, newtuple); + CatalogCloseIndices(Num_pg_database_indices, idescs); + } + + pfree(old_acl); + pfree(new_acl); + + heap_endscan(scan); + + heap_close(relation, RowExclusiveLock); + } +} static void ExecuteGrantStmt_Function(GrantStmt *stmt) { + AclMode privileges; List *i; - char *privstring = NULL; - if (lfirsti(stmt->privileges) == ALL) - privstring = aclmakepriv("", ACL_MODE_SELECT_CHR); + if (lfirsti(stmt->privileges) == ACL_ALL_RIGHTS) + privileges = ACL_ALL_RIGHTS_FUNCTION; else { + privileges = ACL_NO_RIGHTS; foreach(i, stmt->privileges) { - if (lfirsti(i) != EXECUTE) + AclMode priv = lfirsti(i); + + if (priv & ~((AclMode) ACL_ALL_RIGHTS_FUNCTION)) elog(ERROR, "invalid privilege type %s for function object", - privilege_token_string(lfirsti(i))); + privilege_to_string(priv)); + privileges |= priv; } - - privstring = aclmakepriv("", ACL_MODE_SELECT_CHR); } foreach(i, stmt->objects) @@ -314,7 +393,6 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) bool isNull; Acl *old_acl; Acl *new_acl; - unsigned i; HeapTuple newtuple; Datum values[Natts_pg_proc]; char nulls[Natts_pg_proc]; @@ -323,15 +401,14 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) oid = LookupFuncNameTypeNames(func->funcname, func->funcargs, true, "GRANT"); relation = heap_openr(ProcedureRelationName, RowExclusiveLock); - tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(oid), 0, 0, 0); + tuple = SearchSysCache(PROCOID, + ObjectIdGetDatum(oid), + 0, 0, 0); if (!HeapTupleIsValid(tuple)) - { - heap_close(relation, RowExclusiveLock); elog(ERROR, "function %u not found", oid); - } pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple); - if (pg_proc_tuple->proowner != GetUserId()) + if (!pg_proc_ownercheck(oid, GetUserId())) elog(ERROR, "permission denied"); /* @@ -341,24 +418,23 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl, &isNull); if (isNull) - old_acl = acldefault(pg_proc_tuple->proowner); + old_acl = acldefault(ACL_OBJECT_FUNCTION, + pg_proc_tuple->proowner); else - /* get a detoasted copy of the rel's ACL */ + /* get a detoasted copy of the ACL */ old_acl = DatumGetAclPCopy(aclDatum); new_acl = merge_acl_with_grant(old_acl, stmt->is_grant, - stmt->grantees, privstring); + stmt->grantees, privileges); /* finished building new ACL value, now insert it */ - for (i = 0; i < Natts_pg_proc; ++i) - { - replaces[i] = ' '; - nulls[i] = ' '; /* ignored if replaces[i]==' ' anyway */ - values[i] = (Datum) NULL; /* ignored if replaces[i]==' ' - * anyway */ - } + MemSet(values, 0, sizeof(values)); + MemSet(nulls, ' ', sizeof(nulls)); + MemSet(replaces, ' ', sizeof(replaces)); + replaces[Anum_pg_proc_proacl - 1] = 'r'; values[Anum_pg_proc_proacl - 1] = PointerGetDatum(new_acl); + newtuple = heap_modifytuple(tuple, relation, values, nulls, replaces); ReleaseSysCache(tuple); @@ -382,25 +458,26 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) } } - static void -ExecuteGrantStmt_Lang(GrantStmt *stmt) +ExecuteGrantStmt_Language(GrantStmt *stmt) { + AclMode privileges; List *i; - char *privstring = NULL; - if (lfirsti(stmt->privileges) == ALL) - privstring = aclmakepriv("", ACL_MODE_SELECT_CHR); + if (lfirsti(stmt->privileges) == ACL_ALL_RIGHTS) + privileges = ACL_ALL_RIGHTS_LANGUAGE; else { + privileges = ACL_NO_RIGHTS; foreach(i, stmt->privileges) { - if (lfirsti(i) != USAGE) + AclMode priv = lfirsti(i); + + if (priv & ~((AclMode) ACL_ALL_RIGHTS_LANGUAGE)) elog(ERROR, "invalid privilege type %s for language object", - privilege_token_string(lfirsti(i))); + privilege_to_string(priv)); + privileges |= priv; } - - privstring = aclmakepriv("", ACL_MODE_SELECT_CHR); } foreach(i, stmt->objects) @@ -413,7 +490,6 @@ ExecuteGrantStmt_Lang(GrantStmt *stmt) bool isNull; Acl *old_acl; Acl *new_acl; - unsigned i; HeapTuple newtuple; Datum values[Natts_pg_language]; char nulls[Natts_pg_language]; @@ -423,19 +499,15 @@ ExecuteGrantStmt_Lang(GrantStmt *stmt) elog(ERROR, "permission denied"); relation = heap_openr(LanguageRelationName, RowExclusiveLock); - tuple = SearchSysCache(LANGNAME, PointerGetDatum(langname), 0, 0, 0); + tuple = SearchSysCache(LANGNAME, + PointerGetDatum(langname), + 0, 0, 0); if (!HeapTupleIsValid(tuple)) - { - heap_close(relation, RowExclusiveLock); elog(ERROR, "language \"%s\" not found", langname); - } pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple); if (!pg_language_tuple->lanpltrusted) - { - heap_close(relation, RowExclusiveLock); elog(ERROR, "language \"%s\" is not trusted", langname); - } /* * If there's no ACL, create a default. @@ -443,24 +515,23 @@ ExecuteGrantStmt_Lang(GrantStmt *stmt) aclDatum = SysCacheGetAttr(LANGNAME, tuple, Anum_pg_language_lanacl, &isNull); if (isNull) - old_acl = acldefault(InvalidOid); + old_acl = acldefault(ACL_OBJECT_LANGUAGE, + InvalidOid); else - /* get a detoasted copy of the rel's ACL */ + /* get a detoasted copy of the ACL */ old_acl = DatumGetAclPCopy(aclDatum); new_acl = merge_acl_with_grant(old_acl, stmt->is_grant, - stmt->grantees, privstring); + stmt->grantees, privileges); /* finished building new ACL value, now insert it */ - for (i = 0; i < Natts_pg_language; ++i) - { - replaces[i] = ' '; - nulls[i] = ' '; /* ignored if replaces[i]==' ' anyway */ - values[i] = (Datum) NULL; /* ignored if replaces[i]==' ' - * anyway */ - } + MemSet(values, 0, sizeof(values)); + MemSet(nulls, ' ', sizeof(nulls)); + MemSet(replaces, ' ', sizeof(replaces)); + replaces[Anum_pg_language_lanacl - 1] = 'r'; values[Anum_pg_language_lanacl - 1] = PointerGetDatum(new_acl); + newtuple = heap_modifytuple(tuple, relation, values, nulls, replaces); ReleaseSysCache(tuple); @@ -484,21 +555,137 @@ ExecuteGrantStmt_Lang(GrantStmt *stmt) } } - - -static const char * -privilege_token_string(int token) +static void +ExecuteGrantStmt_Namespace(GrantStmt *stmt) { - const char *s = TokenString(token); + AclMode privileges; + List *i; - if (s) - return s; + if (lfirsti(stmt->privileges) == ACL_ALL_RIGHTS) + privileges = ACL_ALL_RIGHTS_NAMESPACE; else - elog(ERROR, "privilege_token_string: invalid token number"); - return NULL; /* appease compiler */ + { + privileges = ACL_NO_RIGHTS; + foreach(i, stmt->privileges) + { + AclMode priv = lfirsti(i); + + if (priv & ~((AclMode) ACL_ALL_RIGHTS_NAMESPACE)) + elog(ERROR, "invalid privilege type %s for namespace object", + privilege_to_string(priv)); + privileges |= priv; + } + } + + foreach(i, stmt->objects) + { + char *nspname = strVal(lfirst(i)); + Relation relation; + HeapTuple tuple; + Form_pg_namespace pg_namespace_tuple; + Datum aclDatum; + bool isNull; + Acl *old_acl; + Acl *new_acl; + HeapTuple newtuple; + Datum values[Natts_pg_namespace]; + char nulls[Natts_pg_namespace]; + char replaces[Natts_pg_namespace]; + + relation = heap_openr(NamespaceRelationName, RowExclusiveLock); + tuple = SearchSysCache(NAMESPACENAME, + CStringGetDatum(nspname), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "namespace \"%s\" not found", nspname); + pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple); + + if (!pg_namespace_ownercheck(tuple->t_data->t_oid, GetUserId())) + elog(ERROR, "permission denied"); + + /* + * If there's no ACL, create a default using the pg_namespace.nspowner + * field. + */ + aclDatum = SysCacheGetAttr(NAMESPACENAME, tuple, + Anum_pg_namespace_nspacl, + &isNull); + if (isNull) + old_acl = acldefault(ACL_OBJECT_NAMESPACE, + pg_namespace_tuple->nspowner); + else + /* get a detoasted copy of the ACL */ + old_acl = DatumGetAclPCopy(aclDatum); + + new_acl = merge_acl_with_grant(old_acl, stmt->is_grant, + stmt->grantees, privileges); + + /* finished building new ACL value, now insert it */ + MemSet(values, 0, sizeof(values)); + MemSet(nulls, ' ', sizeof(nulls)); + MemSet(replaces, ' ', sizeof(replaces)); + + replaces[Anum_pg_namespace_nspacl - 1] = 'r'; + values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(new_acl); + + newtuple = heap_modifytuple(tuple, relation, values, nulls, replaces); + + ReleaseSysCache(tuple); + + simple_heap_update(relation, &newtuple->t_self, newtuple); + + { + /* keep the catalog indexes up to date */ + Relation idescs[Num_pg_namespace_indices]; + + CatalogOpenIndices(Num_pg_namespace_indices, Name_pg_namespace_indices, + idescs); + CatalogIndexInsert(idescs, Num_pg_namespace_indices, relation, newtuple); + CatalogCloseIndices(Num_pg_namespace_indices, idescs); + } + + pfree(old_acl); + pfree(new_acl); + + heap_close(relation, RowExclusiveLock); + } } +static const char * +privilege_to_string(AclMode privilege) +{ + switch (privilege) + { + case ACL_INSERT: + return "INSERT"; + case ACL_SELECT: + return "SELECT"; + case ACL_UPDATE: + return "UPDATE"; + case ACL_DELETE: + return "DELETE"; + case ACL_RULE: + return "RULE"; + case ACL_REFERENCES: + return "REFERENCES"; + case ACL_TRIGGER: + return "TRIGGER"; + case ACL_EXECUTE: + return "EXECUTE"; + case ACL_USAGE: + return "USAGE"; + case ACL_CREATE: + return "CREATE"; + case ACL_CREATE_TEMP: + return "TEMP"; + default: + elog(ERROR, "privilege_to_string: unrecognized privilege %d", + privilege); + } + return NULL; /* appease compiler */ +} + AclId get_grosysid(char *groname) @@ -599,7 +786,7 @@ in_group(AclId uid, AclId gid) * The ACL list is expected to be sorted in standard order. */ static int32 -aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode) +aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode) { AclItem *aip, *aidat; @@ -635,12 +822,12 @@ aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode) * "World" rights are applicable regardless of the passed-in ID, and * since they're much the cheapest to check, check 'em first. */ - if (aidat->ai_idtype != ACL_IDTYPE_WORLD) + if (ACLITEM_GET_IDTYPE(*aidat) != ACL_IDTYPE_WORLD) elog(ERROR, "aclcheck: first entry in ACL is not 'world' entry"); - if (aidat->ai_mode & mode) + if (aidat->ai_privs & mode) { #ifdef ACLDEBUG - elog(DEBUG1, "aclcheck: using world=%d", aidat->ai_mode); + elog(DEBUG1, "aclcheck: using world=%d", ACLITEM_GET_PRIVS(*aidat)); #endif return ACLCHECK_OK; } @@ -650,31 +837,31 @@ aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode) case ACL_IDTYPE_UID: /* See if permission is granted directly to user */ for (i = 1, aip = aidat + 1; /* skip world entry */ - i < num && aip->ai_idtype == ACL_IDTYPE_UID; + i < num && ACLITEM_GET_IDTYPE(*aip) == ACL_IDTYPE_UID; ++i, ++aip) { if (aip->ai_id == id) { #ifdef ACLDEBUG elog(DEBUG1, "aclcheck: found user %u/%d", - aip->ai_id, aip->ai_mode); + aip->ai_id, ACLITEM_GET_PRIVS(*aip)); #endif - if (aip->ai_mode & mode) + if (aip->ai_privs & mode) return ACLCHECK_OK; } } /* See if he has the permission via any group */ for (; - i < num && aip->ai_idtype == ACL_IDTYPE_GID; + i < num && ACLITEM_GET_IDTYPE(*aip) == ACL_IDTYPE_GID; ++i, ++aip) { - if (aip->ai_mode & mode) + if (aip->ai_privs & mode) { if (in_group(id, aip->ai_id)) { #ifdef ACLDEBUG elog(DEBUG1, "aclcheck: found group %u/%d", - aip->ai_id, aip->ai_mode); + aip->ai_id, ACLITEM_GET_PRIVS(*aip)); #endif return ACLCHECK_OK; } @@ -684,20 +871,20 @@ aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode) case ACL_IDTYPE_GID: /* Look for this group ID */ for (i = 1, aip = aidat + 1; /* skip world entry */ - i < num && aip->ai_idtype == ACL_IDTYPE_UID; + i < num && ACLITEM_GET_IDTYPE(*aip) == ACL_IDTYPE_UID; ++i, ++aip) /* skip UID entry */ ; for (; - i < num && aip->ai_idtype == ACL_IDTYPE_GID; + i < num && ACLITEM_GET_IDTYPE(*aip) == ACL_IDTYPE_GID; ++i, ++aip) { if (aip->ai_id == id) { #ifdef ACLDEBUG elog(DEBUG1, "aclcheck: found group %u/%d", - aip->ai_id, aip->ai_mode); + aip->ai_id, ACLITEM_GET_PRIVS(*aip)); #endif - if (aip->ai_mode & mode) + if (aip->ai_privs & mode) return ACLCHECK_OK; } } @@ -795,7 +982,7 @@ pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode) AclId ownerId; ownerId = ((Form_pg_class) GETSTRUCT(tuple))->relowner; - acl = acldefault(ownerId); + acl = acldefault(ACL_OBJECT_RELATION, ownerId); aclDatum = (Datum) 0; } else @@ -804,7 +991,7 @@ pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode) acl = DatumGetAclP(aclDatum); } - result = aclcheck(acl, userid, (AclIdType) ACL_IDTYPE_UID, mode); + result = aclcheck(acl, userid, ACL_IDTYPE_UID, mode); /* if we have a detoasted copy, free it */ if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) @@ -815,13 +1002,78 @@ pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode) return result; } +/* + * Exported routine for checking a user's access privileges to a database + * + * Returns an ACLCHECK_* result code. + */ +int32 +pg_database_aclcheck(Oid db_oid, Oid userid, AclMode mode) +{ + int32 result; + Relation pg_database; + ScanKeyData entry[1]; + HeapScanDesc scan; + HeapTuple tuple; + Datum aclDatum; + bool isNull; + Acl *acl; + + /* Superusers bypass all permission checking. */ + if (superuser_arg(userid)) + return ACLCHECK_OK; + + /* + * Get the database's ACL from pg_database + * + * There's no syscache for pg_database, so must look the hard way + */ + pg_database = heap_openr(DatabaseRelationName, AccessShareLock); + ScanKeyEntryInitialize(&entry[0], 0x0, + ObjectIdAttributeNumber, F_OIDEQ, + ObjectIdGetDatum(db_oid)); + scan = heap_beginscan(pg_database, 0, SnapshotNow, 1, entry); + tuple = heap_getnext(scan, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "pg_database_aclcheck: database %u not found", db_oid); + + aclDatum = heap_getattr(tuple, Anum_pg_database_datacl, + RelationGetDescr(pg_database), &isNull); + + if (isNull) + { + /* No ACL, so build default ACL */ + AclId ownerId; + + ownerId = ((Form_pg_database) GETSTRUCT(tuple))->datdba; + acl = acldefault(ACL_OBJECT_DATABASE, ownerId); + aclDatum = (Datum) 0; + } + else + { + /* detoast ACL if necessary */ + acl = DatumGetAclP(aclDatum); + } + + result = aclcheck(acl, userid, ACL_IDTYPE_UID, mode); + + /* if we have a detoasted copy, free it */ + if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) + pfree(acl); + + heap_endscan(scan); + heap_close(pg_database, AccessShareLock); + + return result; +} + /* * Exported routine for checking a user's access privileges to a function * * Returns an ACLCHECK_* result code. */ int32 -pg_proc_aclcheck(Oid proc_oid, Oid userid) +pg_proc_aclcheck(Oid proc_oid, Oid userid, AclMode mode) { int32 result; HeapTuple tuple; @@ -850,7 +1102,7 @@ pg_proc_aclcheck(Oid proc_oid, Oid userid) AclId ownerId; ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner; - acl = acldefault(ownerId); + acl = acldefault(ACL_OBJECT_FUNCTION, ownerId); aclDatum = (Datum) 0; } else @@ -859,11 +1111,7 @@ pg_proc_aclcheck(Oid proc_oid, Oid userid) acl = DatumGetAclP(aclDatum); } - /* - * Functions only have one kind of privilege, which is encoded as - * "SELECT" here. - */ - result = aclcheck(acl, userid, (AclIdType) ACL_IDTYPE_UID, ACL_SELECT); + result = aclcheck(acl, userid, ACL_IDTYPE_UID, mode); /* if we have a detoasted copy, free it */ if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) @@ -880,7 +1128,7 @@ pg_proc_aclcheck(Oid proc_oid, Oid userid) * Returns an ACLCHECK_* result code. */ int32 -pg_language_aclcheck(Oid lang_oid, Oid userid) +pg_language_aclcheck(Oid lang_oid, Oid userid, AclMode mode) { int32 result; HeapTuple tuple; @@ -906,7 +1154,7 @@ pg_language_aclcheck(Oid lang_oid, Oid userid) if (isNull) { /* No ACL, so build default ACL */ - acl = acldefault(InvalidOid); + acl = acldefault(ACL_OBJECT_LANGUAGE, InvalidOid); aclDatum = (Datum) 0; } else @@ -915,11 +1163,62 @@ pg_language_aclcheck(Oid lang_oid, Oid userid) acl = DatumGetAclP(aclDatum); } + result = aclcheck(acl, userid, ACL_IDTYPE_UID, mode); + + /* if we have a detoasted copy, free it */ + if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) + pfree(acl); + + ReleaseSysCache(tuple); + + return result; +} + +/* + * Exported routine for checking a user's access privileges to a namespace + * + * Returns an ACLCHECK_* result code. + */ +int32 +pg_namespace_aclcheck(Oid nsp_oid, Oid userid, AclMode mode) +{ + int32 result; + HeapTuple tuple; + Datum aclDatum; + bool isNull; + Acl *acl; + + /* Superusers bypass all permission checking. */ + if (superuser_arg(userid)) + return ACLCHECK_OK; + /* - * Languages only have one kind of privilege, which is encoded as - * "SELECT" here. + * Get the function's ACL from pg_namespace */ - result = aclcheck(acl, userid, (AclIdType) ACL_IDTYPE_UID, ACL_SELECT); + tuple = SearchSysCache(NAMESPACEOID, + ObjectIdGetDatum(nsp_oid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "pg_namespace_aclcheck: namespace %u not found", nsp_oid); + + aclDatum = SysCacheGetAttr(NAMESPACEOID, tuple, Anum_pg_namespace_nspacl, + &isNull); + if (isNull) + { + /* No ACL, so build default ACL */ + AclId ownerId; + + ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner; + acl = acldefault(ACL_OBJECT_NAMESPACE, ownerId); + aclDatum = (Datum) 0; + } + else + { + /* detoast ACL if necessary */ + acl = DatumGetAclP(aclDatum); + } + + result = aclcheck(acl, userid, ACL_IDTYPE_UID, mode); /* if we have a detoasted copy, free it */ if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) @@ -1034,3 +1333,30 @@ pg_proc_ownercheck(Oid proc_oid, Oid userid) return userid == owner_id; } + +/* + * Ownership check for a namespace (specified by OID). + */ +bool +pg_namespace_ownercheck(Oid nsp_oid, Oid userid) +{ + HeapTuple tuple; + AclId owner_id; + + /* Superusers bypass all permission checking. */ + if (superuser_arg(userid)) + return true; + + tuple = SearchSysCache(NAMESPACEOID, + ObjectIdGetDatum(nsp_oid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "pg_namespace_ownercheck: namespace %u not found", + nsp_oid); + + owner_id = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner; + + ReleaseSysCache(tuple); + + return userid == owner_id; +} diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index a6072be07a..9b5c47969a 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.86 2002/04/11 05:32:03 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.87 2002/04/21 00:26:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -269,6 +269,9 @@ createdb(const char *dbname, const char *dbowner, pg_database_dsc = RelationGetDescr(pg_database_rel); /* Form tuple */ + MemSet(new_record, 0, sizeof(new_record)); + MemSet(new_record_nulls, ' ', sizeof(new_record_nulls)); + new_record[Anum_pg_database_datname - 1] = DirectFunctionCall1(namein, CStringGetDatum(dbname)); new_record[Anum_pg_database_datdba - 1] = Int32GetDatum(datdba); @@ -278,12 +281,12 @@ createdb(const char *dbname, const char *dbowner, new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid); new_record[Anum_pg_database_datvacuumxid - 1] = TransactionIdGetDatum(src_vacuumxid); new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid); - /* no nulls here, GetRawDatabaseInfo doesn't like them */ + /* do not set datpath to null, GetRawDatabaseInfo won't cope */ new_record[Anum_pg_database_datpath - 1] = DirectFunctionCall1(textin, CStringGetDatum(dbpath ? dbpath : "")); - memset(new_record_nulls, ' ', sizeof(new_record_nulls)); new_record_nulls[Anum_pg_database_datconfig - 1] = 'n'; + new_record_nulls[Anum_pg_database_datacl - 1] = 'n'; tuple = heap_formtuple(pg_database_dsc, new_record, new_record_nulls); @@ -454,7 +457,6 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) Datum repl_val[Natts_pg_database]; char repl_null[Natts_pg_database]; char repl_repl[Natts_pg_database]; - int i; valuestr = (stmt->value ? ((A_Const *) lfirst(stmt->value))->val.val.str @@ -472,8 +474,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) || ((Form_pg_database) GETSTRUCT(tuple))->datdba == GetUserId())) elog(ERROR, "permission denied"); - for (i = 0; i < Natts_pg_database; i++) - repl_repl[i] = ' '; + MemSet(repl_repl, ' ', sizeof(repl_repl)); repl_repl[Anum_pg_database_datconfig-1] = 'r'; if (strcmp(stmt->variable, "all")==0 && stmt->value == NULL) @@ -605,7 +606,7 @@ have_createdb_privilege(void) 0, 0, 0); if (!HeapTupleIsValid(utup)) - retval = true; + retval = false; else retval = ((Form_pg_shadow) GETSTRUCT(utup))->usecreatedb; diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 0bee7cdced..86b7cd50ec 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.2 2002/04/21 00:26:42 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -325,7 +325,7 @@ CreateFunction(ProcedureStmt *stmt) languageStruct = (Form_pg_language) GETSTRUCT(languageTuple); if (!((languageStruct->lanpltrusted - && pg_language_aclcheck(languageOid, GetUserId()) == ACLCHECK_OK) + && pg_language_aclcheck(languageOid, GetUserId(), ACL_USAGE) == ACLCHECK_OK) || superuser())) elog(ERROR, "permission denied"); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1f03ce42db..9ee7a66a42 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.305 2002/04/18 21:16:16 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.306 2002/04/21 00:26:43 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2473,8 +2473,8 @@ RevokeStmt: REVOKE opt_revoke_grant_option privileges ON privilege_target FROM /* either ALL [PRIVILEGES] or a list of individual privileges */ privileges: privilege_list { $$ = $1; } - | ALL { $$ = makeListi1(ALL); } - | ALL PRIVILEGES { $$ = makeListi1(ALL); } + | ALL { $$ = makeListi1(ACL_ALL_RIGHTS); } + | ALL PRIVILEGES { $$ = makeListi1(ACL_ALL_RIGHTS); } ; privilege_list: privilege { $$ = makeListi1($1); } @@ -2482,16 +2482,20 @@ privilege_list: privilege { $$ = makeListi1($1); } ; /* Not all of these privilege types apply to all objects, but that - gets sorted out later. */ -privilege: SELECT { $$ = SELECT; } - | INSERT { $$ = INSERT; } - | UPDATE { $$ = UPDATE; } - | DELETE { $$ = DELETE; } - | RULE { $$ = RULE; } - | REFERENCES { $$ = REFERENCES; } - | TRIGGER { $$ = TRIGGER; } - | EXECUTE { $$ = EXECUTE; } - | USAGE { $$ = USAGE; } + * gets sorted out later. + */ +privilege: SELECT { $$ = ACL_SELECT; } + | INSERT { $$ = ACL_INSERT; } + | UPDATE { $$ = ACL_UPDATE; } + | DELETE { $$ = ACL_DELETE; } + | RULE { $$ = ACL_RULE; } + | REFERENCES { $$ = ACL_REFERENCES; } + | TRIGGER { $$ = ACL_TRIGGER; } + | EXECUTE { $$ = ACL_EXECUTE; } + | USAGE { $$ = ACL_USAGE; } + | CREATE { $$ = ACL_CREATE; } + | TEMPORARY { $$ = ACL_CREATE_TEMP; } + | TEMP { $$ = ACL_CREATE_TEMP; } ; @@ -2500,28 +2504,42 @@ privilege: SELECT { $$ = SELECT; } privilege_target: qualified_name_list { PrivTarget *n = makeNode(PrivTarget); - n->objtype = TABLE; + n->objtype = ACL_OBJECT_RELATION; n->objs = $1; $$ = n; } | TABLE qualified_name_list { PrivTarget *n = makeNode(PrivTarget); - n->objtype = TABLE; + n->objtype = ACL_OBJECT_RELATION; n->objs = $2; $$ = n; } | FUNCTION function_with_argtypes_list { PrivTarget *n = makeNode(PrivTarget); - n->objtype = FUNCTION; + n->objtype = ACL_OBJECT_FUNCTION; + n->objs = $2; + $$ = n; + } + | DATABASE name_list + { + PrivTarget *n = makeNode(PrivTarget); + n->objtype = ACL_OBJECT_DATABASE; n->objs = $2; $$ = n; } | LANGUAGE name_list { PrivTarget *n = makeNode(PrivTarget); - n->objtype = LANGUAGE; + n->objtype = ACL_OBJECT_LANGUAGE; + n->objs = $2; + $$ = n; + } + | SCHEMA name_list + { + PrivTarget *n = makeNode(PrivTarget); + n->objtype = ACL_OBJECT_NAMESPACE; n->objs = $2; $$ = n; } diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index ab08ae04f7..eee0e1b9b2 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.105 2002/04/18 21:16:16 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.106 2002/04/21 00:26:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -356,36 +356,3 @@ ScanKeywordLookup(char *text) return NULL; } - - -/* - * This does the reverse mapping from token number to string. - */ -const char * -TokenString(int token) -{ - int i = 0; - static char buf[NAMEDATALEN]; - - while (i < sizeof(ScanKeywords)) - { - if (ScanKeywords[i].value == token) - { - int k; - - /* uppercase */ - for (k = 0; k < NAMEDATALEN; k++) - if (ScanKeywords[i].name[k] >= 'a' - && ScanKeywords[i].name[k] <= 'z') - buf[k] = ScanKeywords[i].name[k] + ('A' - 'a'); - else - buf[k] = ScanKeywords[i].name[k]; - - return buf; - } - - i++; - } - - return NULL; -} diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 1db296f5e0..ed175429f2 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.70 2002/03/26 19:16:05 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.71 2002/04/21 00:26:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,16 +16,11 @@ #include -#include "access/heapam.h" -#include "catalog/catalog.h" #include "catalog/namespace.h" #include "catalog/pg_shadow.h" -#include "catalog/pg_type.h" -#include "lib/stringinfo.h" #include "miscadmin.h" #include "utils/acl.h" #include "utils/builtins.h" -#include "utils/memutils.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -34,6 +29,8 @@ #define ACL_IDTYPE_UID_KEYWORD "user" static const char *getid(const char *s, char *n); +static Acl *makeacl(int n); +static const char *aclparse(const char *s, AclItem *aip, unsigned *modechg); static bool aclitemeq(const AclItem *a1, const AclItem *a2); static bool aclitemgt(const AclItem *a1, const AclItem *a2); @@ -115,9 +112,11 @@ getid(const char *s, char *n) * UID/GID, id type identifier and mode type values. * - loads 'modechg' with the mode change flag. */ -const char * +static const char * aclparse(const char *s, AclItem *aip, unsigned *modechg) { + AclMode privs; + uint32 idtype; char name[NAMEDATALEN]; Assert(s && aip && modechg); @@ -125,23 +124,23 @@ aclparse(const char *s, AclItem *aip, unsigned *modechg) #ifdef ACLDEBUG elog(LOG, "aclparse: input = '%s'", s); #endif - aip->ai_idtype = ACL_IDTYPE_UID; + idtype = ACL_IDTYPE_UID; s = getid(s, name); if (*s != ACL_MODECHG_ADD_CHR && *s != ACL_MODECHG_DEL_CHR && *s != ACL_MODECHG_EQL_CHR) { /* we just read a keyword, not a name */ - if (!strcmp(name, ACL_IDTYPE_GID_KEYWORD)) - aip->ai_idtype = ACL_IDTYPE_GID; - else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD)) + if (strcmp(name, ACL_IDTYPE_GID_KEYWORD) == 0) + idtype = ACL_IDTYPE_GID; + else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD) != 0) elog(ERROR, "aclparse: bad keyword, must be [group|user]"); s = getid(s, name); /* move s to the name beyond the keyword */ if (name[0] == '\0') elog(ERROR, "aclparse: a name must follow the [group|user] keyword"); } if (name[0] == '\0') - aip->ai_idtype = ACL_IDTYPE_WORLD; + idtype = ACL_IDTYPE_WORLD; switch (*s) { @@ -155,43 +154,58 @@ aclparse(const char *s, AclItem *aip, unsigned *modechg) *modechg = ACL_MODECHG_EQL; break; default: - elog(ERROR, "aclparse: mode change flag must use \"%s\"", - ACL_MODECHG_STR); + elog(ERROR, "aclparse: mode change flag must use \"%c%c%c\"", + ACL_MODECHG_ADD_CHR, + ACL_MODECHG_DEL_CHR, + ACL_MODECHG_EQL_CHR); } - aip->ai_mode = ACL_NO; + privs = ACL_NO_RIGHTS; + while (isalpha((unsigned char) *++s)) { switch (*s) { - case ACL_MODE_INSERT_CHR: - aip->ai_mode |= ACL_INSERT; + case ACL_INSERT_CHR: + privs |= ACL_INSERT; + break; + case ACL_SELECT_CHR: + privs |= ACL_SELECT; + break; + case ACL_UPDATE_CHR: + privs |= ACL_UPDATE; + break; + case ACL_DELETE_CHR: + privs |= ACL_DELETE; break; - case ACL_MODE_SELECT_CHR: - aip->ai_mode |= ACL_SELECT; + case ACL_RULE_CHR: + privs |= ACL_RULE; break; - case ACL_MODE_UPDATE_CHR: - aip->ai_mode |= ACL_UPDATE; + case ACL_REFERENCES_CHR: + privs |= ACL_REFERENCES; break; - case ACL_MODE_DELETE_CHR: - aip->ai_mode |= ACL_DELETE; + case ACL_TRIGGER_CHR: + privs |= ACL_TRIGGER; break; - case ACL_MODE_RULE_CHR: - aip->ai_mode |= ACL_RULE; + case ACL_EXECUTE_CHR: + privs |= ACL_EXECUTE; break; - case ACL_MODE_REFERENCES_CHR: - aip->ai_mode |= ACL_REFERENCES; + case ACL_USAGE_CHR: + privs |= ACL_USAGE; break; - case ACL_MODE_TRIGGER_CHR: - aip->ai_mode |= ACL_TRIGGER; + case ACL_CREATE_CHR: + privs |= ACL_CREATE; + break; + case ACL_CREATE_TEMP_CHR: + privs |= ACL_CREATE_TEMP; break; default: elog(ERROR, "aclparse: mode flags must use \"%s\"", - ACL_MODE_STR); + ACL_ALL_RIGHTS_STR); } } - switch (aip->ai_idtype) + switch (idtype) { case ACL_IDTYPE_UID: aip->ai_id = get_usesysid(name); @@ -204,9 +218,11 @@ aclparse(const char *s, AclItem *aip, unsigned *modechg) break; } + ACLITEM_SET_PRIVS_IDTYPE(*aip, privs, idtype); + #ifdef ACLDEBUG elog(LOG, "aclparse: correctly read [%x %d %x], modechg=%x", - aip->ai_idtype, aip->ai_id, aip->ai_mode, *modechg); + idtype, aip->ai_id, privs, *modechg); #endif return s; } @@ -218,7 +234,7 @@ aclparse(const char *s, AclItem *aip, unsigned *modechg) * RETURNS: * the new Acl */ -Acl * +static Acl * makeacl(int n) { Acl *new_acl; @@ -281,10 +297,10 @@ aclitemout(PG_FUNCTION_ARGS) unsigned i; char *tmpname; - p = out = palloc(strlen("group =" ACL_MODE_STR " ") + 1 + NAMEDATALEN); + p = out = palloc(strlen("group = ") + N_ACL_RIGHTS + NAMEDATALEN + 1); *p = '\0'; - switch (aip->ai_idtype) + switch (ACLITEM_GET_IDTYPE(*aip)) { case ACL_IDTYPE_UID: htup = SearchSysCache(SHADOWSYSID, @@ -327,15 +343,16 @@ aclitemout(PG_FUNCTION_ARGS) case ACL_IDTYPE_WORLD: break; default: - elog(ERROR, "aclitemout: bad ai_idtype: %d", aip->ai_idtype); + elog(ERROR, "aclitemout: bad idtype: %d", + ACLITEM_GET_IDTYPE(*aip)); break; } while (*p) ++p; *p++ = '='; - for (i = 0; i < N_ACL_MODES; ++i) - if ((aip->ai_mode >> i) & 01) - *p++ = ACL_MODE_STR[i]; + for (i = 0; i < N_ACL_RIGHTS; ++i) + if (aip->ai_privs & (1 << i)) + *p++ = ACL_ALL_RIGHTS_STR[i]; *p = '\0'; PG_RETURN_CSTRING(out); @@ -345,8 +362,8 @@ aclitemout(PG_FUNCTION_ARGS) * aclitemeq * aclitemgt * AclItem equality and greater-than comparison routines. - * Two AclItems are considered equal iff they have the - * same identifier (and identifier type); the mode is ignored. + * Two AclItems are considered equal iff they have the same + * identifier (and identifier type); the privileges are ignored. * Note that these routines are really only useful for sorting * AclItems into identifier order. * @@ -356,14 +373,16 @@ aclitemout(PG_FUNCTION_ARGS) static bool aclitemeq(const AclItem *a1, const AclItem *a2) { - return a1->ai_idtype == a2->ai_idtype && a1->ai_id == a2->ai_id; + return ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) && + a1->ai_id == a2->ai_id; } static bool aclitemgt(const AclItem *a1, const AclItem *a2) { - return ((a1->ai_idtype > a2->ai_idtype) || - (a1->ai_idtype == a2->ai_idtype && a1->ai_id > a2->ai_id)); + return ((ACLITEM_GET_IDTYPE(*a1) > ACLITEM_GET_IDTYPE(*a2)) || + (ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) && + a1->ai_id > a2->ai_id)); } @@ -374,26 +393,53 @@ aclitemgt(const AclItem *a1, const AclItem *a2) * newly-created tables (or any table with a NULL acl entry in pg_class) */ Acl * -acldefault(AclId ownerid) +acldefault(GrantObjectType objtype, AclId ownerid) { + AclMode world_default; + AclMode owner_default; Acl *acl; AclItem *aip; -#define ACL_WORLD_DEFAULT (ACL_NO) -#define ACL_OWNER_DEFAULT (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_DELETE|ACL_RULE|ACL_REFERENCES|ACL_TRIGGER) + switch (objtype) + { + case ACL_OBJECT_RELATION: + world_default = ACL_NO_RIGHTS; + owner_default = ACL_ALL_RIGHTS_RELATION; + break; + case ACL_OBJECT_DATABASE: + world_default = ACL_NO_RIGHTS; + owner_default = ACL_ALL_RIGHTS_DATABASE; + break; + case ACL_OBJECT_FUNCTION: + world_default = ACL_NO_RIGHTS; + owner_default = ACL_ALL_RIGHTS_FUNCTION; + break; + case ACL_OBJECT_LANGUAGE: + world_default = ACL_NO_RIGHTS; + owner_default = ACL_ALL_RIGHTS_LANGUAGE; + break; + case ACL_OBJECT_NAMESPACE: + world_default = ACL_NO_RIGHTS; + owner_default = ACL_ALL_RIGHTS_NAMESPACE; + break; + default: + elog(ERROR, "acldefault: bogus objtype %d", (int) objtype); + world_default = ACL_NO_RIGHTS; /* keep compiler quiet */ + owner_default = ACL_NO_RIGHTS; + break; + } acl = makeacl(ownerid ? 2 : 1); aip = ACL_DAT(acl); - aip[0].ai_idtype = ACL_IDTYPE_WORLD; + aip[0].ai_id = ACL_ID_WORLD; - aip[0].ai_mode = ACL_WORLD_DEFAULT; - /* FIXME: The owner's default should vary with the object type. */ + ACLITEM_SET_PRIVS_IDTYPE(aip[0], world_default, ACL_IDTYPE_WORLD); if (ownerid) { - aip[1].ai_idtype = ACL_IDTYPE_UID; aip[1].ai_id = ownerid; - aip[1].ai_mode = ACL_OWNER_DEFAULT; + ACLITEM_SET_PRIVS_IDTYPE(aip[1], owner_default, ACL_IDTYPE_UID); } + return acl; } @@ -469,8 +515,8 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg) } /* initialize the new entry with no permissions */ new_aip[dst].ai_id = mod_aip->ai_id; - new_aip[dst].ai_idtype = mod_aip->ai_idtype; - new_aip[dst].ai_mode = 0; + ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst], ACL_NO_RIGHTS, + ACLITEM_GET_IDTYPE(*mod_aip)); num++; /* set num to the size of new_acl */ } @@ -478,13 +524,15 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg) switch (modechg) { case ACL_MODECHG_ADD: - new_aip[dst].ai_mode |= mod_aip->ai_mode; + new_aip[dst].ai_privs |= ACLITEM_GET_PRIVS(*mod_aip); break; case ACL_MODECHG_DEL: - new_aip[dst].ai_mode &= ~mod_aip->ai_mode; + new_aip[dst].ai_privs &= ~ACLITEM_GET_PRIVS(*mod_aip); break; case ACL_MODECHG_EQL: - new_aip[dst].ai_mode = mod_aip->ai_mode; + ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst], + ACLITEM_GET_PRIVS(*mod_aip), + ACLITEM_GET_IDTYPE(new_aip[dst])); break; } @@ -493,7 +541,7 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg) * For example, this helps in removing entries for users who no longer * exist. EXCEPTION: never remove the world entry. */ - if (new_aip[dst].ai_mode == 0 && dst > 0) + if (ACLITEM_GET_PRIVS(new_aip[dst]) == ACL_NO_RIGHTS && dst > 0) { memmove((char *) (new_aip + dst), (char *) (new_aip + dst + 1), @@ -594,133 +642,14 @@ aclcontains(PG_FUNCTION_ARGS) aidat = ACL_DAT(acl); for (i = 0; i < num; ++i) { - /* Note that aclitemeq only considers id, not mode */ - if (aclitemeq(aip, aidat + i) && - aip->ai_mode == aidat[i].ai_mode) + if (aip->ai_id == aidat[i].ai_id && + aip->ai_privs == aidat[i].ai_privs) PG_RETURN_BOOL(true); } PG_RETURN_BOOL(false); } -/* - * Parser support routines for ACL-related statements. - * - * XXX CAUTION: these are called from gram.y, which is not allowed to - * do any table accesses. Therefore, it is not kosher to do things - * like trying to translate usernames to user IDs here. Keep it all - * in string form until statement execution time. - */ - -/* - * aclmakepriv - * make a acl privilege string out of an existing privilege string - * and a new privilege - * - * does not add duplicate privileges - */ -char * -aclmakepriv(const char *old_privlist, char new_priv) -{ - char *priv; - int i; - int l; - - Assert(strlen(old_privlist) <= strlen(ACL_MODE_STR)); - priv = palloc(strlen(ACL_MODE_STR) + 1); - - if (old_privlist == NULL || old_privlist[0] == '\0') - { - priv[0] = new_priv; - priv[1] = '\0'; - return priv; - } - - strcpy(priv, old_privlist); - - l = strlen(old_privlist); - - if (l == strlen(ACL_MODE_STR)) - { /* can't add any more privileges */ - return priv; - } - - /* check to see if the new privilege is already in the old string */ - for (i = 0; i < l; i++) - { - if (priv[i] == new_priv) - break; - } - if (i == l) - { /* we really have a new privilege */ - priv[l] = new_priv; - priv[l + 1] = '\0'; - } - - return priv; -} - -/* - * aclmakeuser - * user_type must be "A" - all users - * "G" - group - * "U" - user - * - * Just concatenates the two strings together with a space in between. - * Per above comments, we can't try to resolve a user or group name here. - */ -char * -aclmakeuser(const char *user_type, const char *user) -{ - char *user_list; - - user_list = palloc(strlen(user_type) + strlen(user) + 2); - sprintf(user_list, "%s %s", user_type, user); - return user_list; -} - - -/* - * makeAclString: We take in the privileges and grantee as well as a - * single character '+' or '-' to indicate grant or revoke. - * - * We convert the information to the same external form recognized by - * aclitemin (see aclparse) and return that string. Conversion to - * internal form happens when the statement is executed. - */ -char * -makeAclString(const char *privileges, const char *grantee, char grant_or_revoke) -{ - StringInfoData str; - char *ret; - - initStringInfo(&str); - - /* the grantee string is "G ", "U ", or "ALL" */ - if (grantee[0] == 'G') /* group permissions */ - { - appendStringInfo(&str, "%s \"%s\"%c%s", - ACL_IDTYPE_GID_KEYWORD, - grantee + 2, grant_or_revoke, privileges); - } - else if (grantee[0] == 'U') /* user permission */ - { - appendStringInfo(&str, "%s \"%s\"%c%s", - ACL_IDTYPE_UID_KEYWORD, - grantee + 2, grant_or_revoke, privileges); - } - else - { - /* all permission */ - appendStringInfo(&str, "%c%s", - grant_or_revoke, privileges); - } - ret = pstrdup(str.data); - pfree(str.data); - return ret; -} - - /* * has_table_privilege_name_name * Check user privileges on a relation given @@ -949,7 +878,7 @@ convert_priv_string(text *priv_type_text) /* * We should never get here, but stop the compiler from complaining */ - return ACL_NO; + return ACL_NO_RIGHTS; } /* diff --git a/src/backend/utils/cache/fcache.c b/src/backend/utils/cache/fcache.c index 96fee81349..cb11cbe4e2 100644 --- a/src/backend/utils/cache/fcache.c +++ b/src/backend/utils/cache/fcache.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.42 2002/02/18 23:11:25 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.43 2002/04/21 00:26:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -42,7 +42,7 @@ init_fcache(Oid foid, int nargs, MemoryContext fcacheCxt) /* Initialize additional info */ retval->setArgsValid = false; - retval->permission_ok = pg_proc_aclcheck(foid, GetUserId()) == ACLCHECK_OK; + retval->permission_ok = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE) == ACLCHECK_OK; return retval; } diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index 4074c9cf61..195e4f8a83 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -27,7 +27,7 @@ # Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.150 2002/04/19 23:13:54 tgl Exp $ +# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.151 2002/04/21 00:26:43 tgl Exp $ # #------------------------------------------------------------------------- @@ -949,9 +949,9 @@ $ECHO_N "setting privileges on built-in objects... "$ECHO_C cat <> 30) +#define ACLITEM_SET_PRIVS_IDTYPE(item,privs,idtype) \ + ((item).ai_privs = ((privs) & 0x3FFFFFFF) | ((idtype) << 30)) /* @@ -144,20 +130,39 @@ typedef ArrayType IdList; #define ACL_MODECHG_DEL 2 #define ACL_MODECHG_EQL 3 -/* mode indicators for I/O */ -#define ACL_MODECHG_STR "+-=" /* list of valid characters */ +/* external representation of mode indicators for I/O */ #define ACL_MODECHG_ADD_CHR '+' #define ACL_MODECHG_DEL_CHR '-' #define ACL_MODECHG_EQL_CHR '=' -#define ACL_MODE_STR "arwdRxt" /* list of valid - * characters */ -#define ACL_MODE_INSERT_CHR 'a' /* formerly known as "append" */ -#define ACL_MODE_SELECT_CHR 'r' /* formerly known as "read" */ -#define ACL_MODE_UPDATE_CHR 'w' /* formerly known as "write" */ -#define ACL_MODE_DELETE_CHR 'd' -#define ACL_MODE_RULE_CHR 'R' -#define ACL_MODE_REFERENCES_CHR 'x' -#define ACL_MODE_TRIGGER_CHR 't' + +/* + * External representations of the privilege bits --- aclitemin/aclitemout + * represent each possible privilege bit with a distinct 1-character code + */ +#define ACL_INSERT_CHR 'a' /* formerly known as "append" */ +#define ACL_SELECT_CHR 'r' /* formerly known as "read" */ +#define ACL_UPDATE_CHR 'w' /* formerly known as "write" */ +#define ACL_DELETE_CHR 'd' +#define ACL_RULE_CHR 'R' +#define ACL_REFERENCES_CHR 'x' +#define ACL_TRIGGER_CHR 't' +#define ACL_EXECUTE_CHR 'X' +#define ACL_USAGE_CHR 'U' +#define ACL_CREATE_CHR 'C' +#define ACL_CREATE_TEMP_CHR 'T' + +/* string holding all privilege code chars, in order by bitmask position */ +#define ACL_ALL_RIGHTS_STR "arwdRxtXUCT" + +/* + * Bitmasks defining "all rights" for each supported object type + */ +#define ACL_ALL_RIGHTS_RELATION (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_DELETE|ACL_RULE|ACL_REFERENCES|ACL_TRIGGER) +#define ACL_ALL_RIGHTS_DATABASE (ACL_CREATE|ACL_CREATE_TEMP) +#define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE) +#define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE) +#define ACL_ALL_RIGHTS_NAMESPACE (ACL_USAGE|ACL_CREATE) + /* result codes for pg_*_aclcheck */ #define ACLCHECK_OK 0 @@ -171,26 +176,18 @@ extern const char * const aclcheck_error_strings[]; /* * routines used internally */ -extern Acl *acldefault(AclId ownerid); -extern Acl *aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg); - -/* - * routines used by the parser - */ -extern char *aclmakepriv(const char *old_privlist, char new_priv); -extern char *aclmakeuser(const char *user_type, const char *user); +extern Acl *acldefault(GrantObjectType objtype, AclId ownerid); +extern Acl *aclinsert3(const Acl *old_acl, const AclItem *mod_aip, + unsigned modechg); /* * exported routines (from acl.c) */ -extern Acl *makeacl(int n); extern Datum aclitemin(PG_FUNCTION_ARGS); extern Datum aclitemout(PG_FUNCTION_ARGS); extern Datum aclinsert(PG_FUNCTION_ARGS); extern Datum aclremove(PG_FUNCTION_ARGS); extern Datum aclcontains(PG_FUNCTION_ARGS); -extern const char *aclparse(const char *s, AclItem *aip, unsigned *modechg); -extern char *makeAclString(const char *privileges, const char *grantee, char grant_or_revoke); /* * prototypes for functions in aclchk.c @@ -201,13 +198,16 @@ extern char *get_groname(AclId grosysid); /* these return ACLCHECK_* result codes */ extern int32 pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode); -extern int32 pg_proc_aclcheck(Oid proc_oid, Oid userid); -extern int32 pg_language_aclcheck(Oid lang_oid, Oid userid); +extern int32 pg_database_aclcheck(Oid db_oid, Oid userid, AclMode mode); +extern int32 pg_proc_aclcheck(Oid proc_oid, Oid userid, AclMode mode); +extern int32 pg_language_aclcheck(Oid lang_oid, Oid userid, AclMode mode); +extern int32 pg_namespace_aclcheck(Oid nsp_oid, Oid userid, AclMode mode); /* ownercheck routines just return true (owner) or false (not) */ extern bool pg_class_ownercheck(Oid class_oid, Oid userid); extern bool pg_type_ownercheck(Oid type_oid, Oid userid); extern bool pg_oper_ownercheck(Oid oper_oid, Oid userid); extern bool pg_proc_ownercheck(Oid proc_oid, Oid userid); +extern bool pg_namespace_ownercheck(Oid nsp_oid, Oid userid); #endif /* ACL_H */ -- GitLab