Implement checking of USAGE rights on namespaces.

This commit is contained in:
Tom Lane 2002-04-29 22:15:07 +00:00
parent 88ef7067f7
commit ccfaf9067d
1 changed files with 197 additions and 107 deletions

View File

@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.14 2002/04/27 03:45:00 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.15 2002/04/29 22:15:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -37,9 +37,10 @@
#include "storage/backendid.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/catcache.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@ -66,10 +67,25 @@
* path is determined by GUC. The factory default path contains the PUBLIC
* namespace (if it exists), preceded by the user's personal namespace
* (if one exists).
*
* If namespaceSearchPathValid is false, then namespaceSearchPath (and the
* derived variables) need to be recomputed from namespace_search_path.
* We mark it invalid upon an assignment to namespace_search_path or receipt
* of a syscache invalidation event for pg_namespace. The recomputation
* is done during the next lookup attempt.
*
* Any namespaces mentioned in namespace_search_path that are not readable
* by the current user ID are simply left out of namespaceSearchPath; so
* we have to be willing to recompute the path when current userid changes.
* namespaceUser is the userid the path has been computed for.
*/
static List *namespaceSearchPath = NIL;
static bool namespaceSearchPathValid = true;
static Oid namespaceUser = InvalidOid;
/* this flag must be updated correctly when namespaceSearchPath is changed */
static bool pathContainsSystemNamespace = false;
@ -103,12 +119,14 @@ typedef struct DelConstraint
/* Local functions */
static void recomputeNamespacePath(void);
static Oid GetTempTableNamespace(void);
static void RemoveTempRelations(Oid tempNamespaceId);
static List *FindTempRelations(Oid tempNamespaceId);
static List *FindDeletionConstraints(List *relOids);
static List *TopoSortRels(List *relOids, List *constraintList);
static void RemoveTempRelationsCallback(void);
static void NamespaceCallback(Datum arg, Oid relid);
/*
@ -137,12 +155,18 @@ RangeVarGetRelid(const RangeVar *relation, bool failOK)
if (relation->schemaname)
{
/* use exact schema given */
AclResult aclresult;
namespaceId = GetSysCacheOid(NAMESPACENAME,
CStringGetDatum(relation->schemaname),
0, 0, 0);
if (!OidIsValid(namespaceId))
elog(ERROR, "Namespace \"%s\" does not exist",
relation->schemaname);
aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, relation->schemaname);
relId = get_relname_relid(relation->relname, namespaceId);
}
else
@ -210,11 +234,14 @@ RangeVarGetCreationNamespace(const RangeVar *newRelation)
else
{
/* use the default creation namespace */
recomputeNamespacePath();
namespaceId = defaultCreationNamespace;
if (!OidIsValid(namespaceId))
elog(ERROR, "No namespace has been selected to create in");
}
/* Note: callers will check for CREATE rights when appropriate */
return namespaceId;
}
@ -229,9 +256,11 @@ RelnameGetRelid(const char *relname)
Oid relid;
List *lptr;
recomputeNamespacePath();
/*
* If a TEMP-table namespace has been set up, it is implicitly first
* in the search path.
* in the search path. We do not need to check USAGE permission.
*/
if (OidIsValid(myTempNamespace))
{
@ -241,7 +270,8 @@ RelnameGetRelid(const char *relname)
}
/*
* If system namespace is not in path, implicitly search it before path
* If system namespace is not in path, implicitly search it before path.
* We do not check USAGE permission.
*/
if (!pathContainsSystemNamespace)
{
@ -281,6 +311,8 @@ TypenameGetTypid(const char *typname)
Oid typid;
List *lptr;
recomputeNamespacePath();
/*
* If system namespace is not in path, implicitly search it before path
*/
@ -327,6 +359,8 @@ OpclassnameGetOpcid(Oid amid, const char *opcname)
Oid opcid;
List *lptr;
recomputeNamespacePath();
/*
* If system namespace is not in path, implicitly search it before path
*/
@ -414,17 +448,23 @@ FuncnameGetCandidates(List *names, int nargs)
if (schemaname)
{
/* use exact schema given */
AclResult aclresult;
namespaceId = GetSysCacheOid(NAMESPACENAME,
CStringGetDatum(schemaname),
0, 0, 0);
if (!OidIsValid(namespaceId))
elog(ERROR, "Namespace \"%s\" does not exist",
schemaname);
aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, schemaname);
}
else
{
/* flag to indicate we need namespace search */
namespaceId = InvalidOid;
recomputeNamespacePath();
}
/* Search syscache by name and (optionally) nargs only */
@ -598,17 +638,23 @@ OpernameGetCandidates(List *names, char oprkind)
if (schemaname)
{
/* use exact schema given */
AclResult aclresult;
namespaceId = GetSysCacheOid(NAMESPACENAME,
CStringGetDatum(schemaname),
0, 0, 0);
if (!OidIsValid(namespaceId))
elog(ERROR, "Namespace \"%s\" does not exist",
schemaname);
aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, schemaname);
}
else
{
/* flag to indicate we need namespace search */
namespaceId = InvalidOid;
recomputeNamespacePath();
}
/* Search syscache by name only */
@ -736,6 +782,8 @@ OpclassGetCandidates(Oid amid)
CatCList *catlist;
int i;
recomputeNamespacePath();
/* Search syscache by AM OID only */
catlist = SearchSysCacheList(CLAAMNAMENSP, 1,
ObjectIdGetDatum(amid),
@ -891,11 +939,14 @@ QualifiedNameGetCreationNamespace(List *names, char **objname_p)
else
{
/* use the default creation namespace */
recomputeNamespacePath();
namespaceId = defaultCreationNamespace;
if (!OidIsValid(namespaceId))
elog(ERROR, "No namespace has been selected to create in");
}
/* Note: callers will check for CREATE rights when appropriate */
*objname_p = objname;
return namespaceId;
}
@ -965,6 +1016,118 @@ isTempNamespace(Oid namespaceId)
return false;
}
/*
* recomputeNamespacePath - recompute path derived variables if needed.
*/
static void
recomputeNamespacePath(void)
{
Oid userId = GetUserId();
char *rawname;
List *namelist;
List *oidlist;
List *newpath;
List *l;
MemoryContext oldcxt;
/*
* Do nothing if path is already valid.
*/
if (namespaceSearchPathValid && namespaceUser == userId)
return;
/* Need a modifiable copy of namespace_search_path string */
rawname = pstrdup(namespace_search_path);
/* Parse string into list of identifiers */
if (!SplitIdentifierString(rawname, ',', &namelist))
{
/* syntax error in name list */
/* this should not happen if GUC checked check_search_path */
elog(ERROR, "recomputeNamespacePath: invalid list syntax");
}
/*
* Convert the list of names to a list of OIDs. If any names are not
* recognizable or we don't have read access, just leave them out of
* the list. (We can't raise an error, since the search_path setting
* has already been accepted.)
*/
oidlist = NIL;
foreach(l, namelist)
{
char *curname = (char *) lfirst(l);
Oid namespaceId;
if (strcmp(curname, "$user") == 0)
{
/* $user --- substitute namespace matching user name, if any */
HeapTuple tuple;
tuple = SearchSysCache(SHADOWSYSID,
ObjectIdGetDatum(userId),
0, 0, 0);
if (HeapTupleIsValid(tuple))
{
char *uname;
uname = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
namespaceId = GetSysCacheOid(NAMESPACENAME,
CStringGetDatum(uname),
0, 0, 0);
ReleaseSysCache(tuple);
if (OidIsValid(namespaceId) &&
pg_namespace_aclcheck(namespaceId, userId,
ACL_USAGE) == ACLCHECK_OK)
oidlist = lappendi(oidlist, namespaceId);
}
}
else
{
/* normal namespace reference */
namespaceId = GetSysCacheOid(NAMESPACENAME,
CStringGetDatum(curname),
0, 0, 0);
if (OidIsValid(namespaceId) &&
pg_namespace_aclcheck(namespaceId, userId,
ACL_USAGE) == ACLCHECK_OK)
oidlist = lappendi(oidlist, namespaceId);
}
}
/*
* Now that we've successfully built the new list of namespace OIDs,
* save it in permanent storage.
*/
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
newpath = listCopy(oidlist);
MemoryContextSwitchTo(oldcxt);
/* Now safe to assign to state variable. */
freeList(namespaceSearchPath);
namespaceSearchPath = newpath;
/*
* Update info derived from search path.
*/
pathContainsSystemNamespace = intMember(PG_CATALOG_NAMESPACE,
namespaceSearchPath);
if (namespaceSearchPath == NIL)
defaultCreationNamespace = InvalidOid;
else
defaultCreationNamespace = (Oid) lfirsti(namespaceSearchPath);
/* Mark the path valid. */
namespaceSearchPathValid = true;
namespaceUser = userId;
/* Clean up. */
pfree(rawname);
freeList(namelist);
freeList(oidlist);
}
/*
* GetTempTableNamespace
* Initialize temp table namespace on first use in a particular backend
@ -979,8 +1142,12 @@ GetTempTableNamespace(void)
* First, do permission check to see if we are authorized to make
* temp tables. We use a nonstandard error message here since
* "databasename: permission denied" might be a tad cryptic.
*
* Note we apply the check to the session user, not the currently
* active userid, since we are not going to change our minds about
* temp table availability during the session.
*/
if (pg_database_aclcheck(MyDatabaseId, GetUserId(),
if (pg_database_aclcheck(MyDatabaseId, GetSessionUserId(),
ACL_CREATE_TEMP) != ACLCHECK_OK)
elog(ERROR, "%s: not authorized to create temp tables",
DatabaseName);
@ -1320,7 +1487,8 @@ check_search_path(const char *proposed)
/*
* Verify that all the names are either valid namespace names or "$user".
* (We do not require $user to correspond to a valid namespace; should we?)
* We do not require $user to correspond to a valid namespace.
* We do not check for USAGE rights, either; should we?
*/
foreach(l, namelist)
{
@ -1348,104 +1516,12 @@ check_search_path(const char *proposed)
void
assign_search_path(const char *newval)
{
char *rawname;
List *namelist;
List *oidlist;
List *newpath;
List *l;
MemoryContext oldcxt;
/*
* If we aren't inside a transaction, we cannot do database access so
* cannot look up the names. In this case, do nothing; the internal
* search path will be fixed later by InitializeSearchPath. (We assume
* this situation can only happen in the postmaster or early in backend
* startup.)
* We mark the path as needing recomputation, but don't do anything until
* it's needed. This avoids trying to do database access during GUC
* initialization.
*/
if (!IsTransactionState())
return;
/* Need a modifiable copy of string */
rawname = pstrdup(newval);
/* Parse string into list of identifiers */
if (!SplitIdentifierString(rawname, ',', &namelist))
{
/* syntax error in name list */
/* this should not happen if GUC checked check_search_path */
elog(ERROR, "assign_search_path: invalid list syntax");
}
/*
* Convert the list of names to a list of OIDs. If any names are not
* recognizable, just leave them out of the list. (This is our only
* reasonable recourse when the already-accepted default is bogus.)
*/
oidlist = NIL;
foreach(l, namelist)
{
char *curname = (char *) lfirst(l);
Oid namespaceId;
if (strcmp(curname, "$user") == 0)
{
/* $user --- substitute namespace matching user name, if any */
HeapTuple tuple;
tuple = SearchSysCache(SHADOWSYSID,
ObjectIdGetDatum(GetSessionUserId()),
0, 0, 0);
if (HeapTupleIsValid(tuple))
{
char *uname;
uname = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
namespaceId = GetSysCacheOid(NAMESPACENAME,
CStringGetDatum(uname),
0, 0, 0);
if (OidIsValid(namespaceId))
oidlist = lappendi(oidlist, namespaceId);
ReleaseSysCache(tuple);
}
}
else
{
/* normal namespace reference */
namespaceId = GetSysCacheOid(NAMESPACENAME,
CStringGetDatum(curname),
0, 0, 0);
if (OidIsValid(namespaceId))
oidlist = lappendi(oidlist, namespaceId);
}
}
/*
* Now that we've successfully built the new list of namespace OIDs,
* save it in permanent storage.
*/
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
newpath = listCopy(oidlist);
MemoryContextSwitchTo(oldcxt);
/* Now safe to assign to state variable. */
freeList(namespaceSearchPath);
namespaceSearchPath = newpath;
/*
* Update info derived from search path.
*/
pathContainsSystemNamespace = intMember(PG_CATALOG_NAMESPACE,
namespaceSearchPath);
if (namespaceSearchPath == NIL)
defaultCreationNamespace = InvalidOid;
else
defaultCreationNamespace = (Oid) lfirsti(namespaceSearchPath);
/* Clean up. */
pfree(rawname);
freeList(namelist);
freeList(oidlist);
namespaceSearchPathValid = false;
}
/*
@ -1469,19 +1545,32 @@ InitializeSearchPath(void)
MemoryContextSwitchTo(oldcxt);
pathContainsSystemNamespace = true;
defaultCreationNamespace = PG_CATALOG_NAMESPACE;
namespaceSearchPathValid = true;
namespaceUser = GetUserId();
}
else
{
/*
* If a search path setting was provided before we were able to
* execute lookups, establish the internal search path now.
* In normal mode, arrange for a callback on any syscache invalidation
* of pg_namespace rows.
*/
if (namespace_search_path && *namespace_search_path &&
namespaceSearchPath == NIL)
assign_search_path(namespace_search_path);
CacheRegisterSyscacheCallback(NAMESPACEOID,
NamespaceCallback,
(Datum) 0);
}
}
/*
* NamespaceCallback
* Syscache inval callback function
*/
static void
NamespaceCallback(Datum arg, Oid relid)
{
/* Force search path to be recomputed on next use */
namespaceSearchPathValid = false;
}
/*
* Fetch the active search path, expressed as a List of OIDs.
*
@ -1490,5 +1579,6 @@ InitializeSearchPath(void)
List *
fetch_search_path(void)
{
recomputeNamespacePath();
return namespaceSearchPath;
}