aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/comment.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-03-04 16:08:24 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2011-03-04 16:08:53 -0500
commit8d3b421f5f7b955e7ac7d156aa74ee6a6fe4e9f6 (patch)
tree7c39d9ea30b748ea92b25b020dc0187ee3cf154c /src/backend/commands/comment.c
parent4442e1975d3c4c96a0b573b7abd864b0cbe26f9d (diff)
downloadpostgresql-8d3b421f5f7b955e7ac7d156aa74ee6a6fe4e9f6.tar.gz
postgresql-8d3b421f5f7b955e7ac7d156aa74ee6a6fe4e9f6.zip
Allow non-superusers to create (some) extensions.
Remove the unconditional superuser permissions check in CREATE EXTENSION, and instead define a "superuser" extension property, which when false (not the default) skips the superuser permissions check. In this case the calling user only needs enough permissions to execute the commands in the extension's installation script. The superuser property is also enforced in the same way for ALTER EXTENSION UPDATE cases. In other ALTER EXTENSION cases and DROP EXTENSION, test ownership of the extension rather than superuserness. ALTER EXTENSION ADD/DROP needs to insist on ownership of the target object as well; to do that without duplicating code, refactor comment.c's big switch for permissions checks into a separate function in objectaddress.c. I also removed the superuserness checks in pg_available_extensions and related functions; there's no strong reason why everybody shouldn't be able to see that info. Also invent an IF NOT EXISTS variant of CREATE EXTENSION, and use that in pg_dump, so that dumps won't fail for installed-by-default extensions. We don't have any of those yet, but we will soon. This is all per discussion of wrapping the standard procedural languages into extensions. I'll make those changes in a separate commit; this is just putting the core infrastructure in place.
Diffstat (limited to 'src/backend/commands/comment.c')
-rw-r--r--src/backend/commands/comment.c222
1 files changed, 23 insertions, 199 deletions
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index a0a561c144d..3fbeefa018b 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -22,25 +22,11 @@
#include "catalog/pg_shdescription.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
-#include "libpq/be-fsstubs.h"
#include "miscadmin.h"
-#include "parser/parse_func.h"
-#include "parser/parse_type.h"
-#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
-#include "utils/rel.h"
#include "utils/tqual.h"
-/*
- * For most object types, the permissions-checking logic is simple enough
- * that it makes sense to just include it in CommentObject(). However, a few
- * object types require something more complex; for those, we define helper
- * functions.
- */
-static void CheckAttributeComment(Relation relation);
-static void CheckCastComment(List *qualname, List *arguments);
-
/*
* CommentObject --
@@ -76,143 +62,41 @@ CommentObject(CommentStmt *stmt)
}
/*
- * Translate the parser representation which identifies this object into
- * an ObjectAddress. get_object_address() will throw an error if the
+ * Translate the parser representation that identifies this object into
+ * an ObjectAddress. get_object_address() will throw an error if the
* object does not exist, and will also acquire a lock on the target
* to guard against concurrent DROP operations.
*/
address = get_object_address(stmt->objtype, stmt->objname, stmt->objargs,
&relation, ShareUpdateExclusiveLock);
- /* Privilege and integrity checks. */
+ /* Require ownership of the target object. */
+ check_object_ownership(GetUserId(), stmt->objtype, address,
+ stmt->objname, stmt->objargs, relation);
+
+ /* Perform other integrity checks as needed. */
switch (stmt->objtype)
{
- case OBJECT_INDEX:
- case OBJECT_SEQUENCE:
- case OBJECT_TABLE:
- case OBJECT_VIEW:
- case OBJECT_FOREIGN_TABLE:
- if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
- RelationGetRelationName(relation));
- break;
case OBJECT_COLUMN:
- CheckAttributeComment(relation);
- break;
- case OBJECT_DATABASE:
- if (!pg_database_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
- strVal(linitial(stmt->objname)));
- break;
- case OBJECT_TYPE:
- case OBJECT_DOMAIN:
- if (!pg_type_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
- format_type_be(address.objectId));
- break;
- case OBJECT_AGGREGATE:
- case OBJECT_FUNCTION:
- if (!pg_proc_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
- NameListToString(stmt->objname));
- break;
- case OBJECT_OPERATOR:
- if (!pg_oper_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
- NameListToString(stmt->objname));
- break;
- case OBJECT_RULE:
- case OBJECT_TRIGGER:
- case OBJECT_CONSTRAINT:
- if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
- RelationGetRelationName(relation));
- break;
- case OBJECT_SCHEMA:
- if (!pg_namespace_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
- strVal(linitial(stmt->objname)));
- break;
- case OBJECT_COLLATION:
- if (!pg_collation_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION,
- NameListToString(stmt->objname));
- break;
- case OBJECT_CONVERSION:
- if (!pg_conversion_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
- NameListToString(stmt->objname));
- break;
- case OBJECT_LANGUAGE:
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to comment on procedural language")));
- break;
- case OBJECT_EXTENSION:
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to comment on extension")));
- break;
- case OBJECT_OPCLASS:
- if (!pg_opclass_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
- NameListToString(stmt->objname));
- break;
- case OBJECT_OPFAMILY:
- if (!pg_opfamily_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
- NameListToString(stmt->objname));
- break;
- case OBJECT_LARGEOBJECT:
- if (!lo_compat_privileges &&
- !pg_largeobject_ownercheck(address.objectId, GetUserId()))
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be owner of large object %u",
- address.objectId)));
- break;
- case OBJECT_CAST:
- CheckCastComment(stmt->objname, stmt->objargs);
- break;
- case OBJECT_TABLESPACE:
- if (!pg_tablespace_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE,
- strVal(linitial(stmt->objname)));
- break;
- case OBJECT_ROLE:
- if (!has_privs_of_role(GetUserId(), address.objectId))
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be member of role \"%s\" to comment upon it",
- strVal(linitial(stmt->objname)))));
- break;
- case OBJECT_TSPARSER:
- if (!superuser())
+ /*
+ * Allow comments only on columns of tables, views, composite
+ * types, and foreign tables (which are the only relkinds for
+ * which pg_dump will dump per-column comments). In particular we
+ * wish to disallow comments on index columns, because the naming
+ * of an index's columns may change across PG versions, so dumping
+ * per-column comments could create reload failures.
+ */
+ if (relation->rd_rel->relkind != RELKIND_RELATION &&
+ relation->rd_rel->relkind != RELKIND_VIEW &&
+ relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
+ relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to comment on text search parser")));
- break;
- case OBJECT_TSDICTIONARY:
- if (!pg_ts_dict_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
- NameListToString(stmt->objname));
- break;
- case OBJECT_TSTEMPLATE:
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to comment on text search template")));
- break;
- case OBJECT_TSCONFIGURATION:
- if (!pg_ts_config_ownercheck(address.objectId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
- NameListToString(stmt->objname));
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table, view, composite type, or foreign table",
+ RelationGetRelationName(relation))));
break;
default:
- elog(ERROR, "unrecognized object type: %d",
- (int) stmt->objtype);
+ break;
}
/*
@@ -574,63 +458,3 @@ GetComment(Oid oid, Oid classoid, int32 subid)
return comment;
}
-
-/*
- * Check whether the user is allowed to comment on an attribute of the
- * specified relation.
- */
-static void
-CheckAttributeComment(Relation relation)
-{
- if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
- RelationGetRelationName(relation));
-
- /*
- * Allow comments only on columns of tables, views, composite types, and
- * foreign tables (which are the only relkinds for which pg_dump will dump
- * per-column comments). In particular we wish to disallow comments on
- * index columns, because the naming of an index's columns may change
- * across PG versions, so dumping per-column comments could create reload
- * failures.
- */
- if (relation->rd_rel->relkind != RELKIND_RELATION &&
- relation->rd_rel->relkind != RELKIND_VIEW &&
- relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
- relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, composite type, or foreign table",
- RelationGetRelationName(relation))));
-}
-
-/*
- * Check whether the user is allowed to comment on the specified cast.
- */
-static void
-CheckCastComment(List *qualname, List *arguments)
-{
- TypeName *sourcetype;
- TypeName *targettype;
- Oid sourcetypeid;
- Oid targettypeid;
-
- Assert(list_length(qualname) == 1);
- sourcetype = (TypeName *) linitial(qualname);
- Assert(IsA(sourcetype, TypeName));
- Assert(list_length(arguments) == 1);
- targettype = (TypeName *) linitial(arguments);
- Assert(IsA(targettype, TypeName));
-
- sourcetypeid = typenameTypeId(NULL, sourcetype);
- targettypeid = typenameTypeId(NULL, targettype);
-
- /* Permission check */
- if (!pg_type_ownercheck(sourcetypeid, GetUserId())
- && !pg_type_ownercheck(targettypeid, GetUserId()))
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be owner of type %s or type %s",
- format_type_be(sourcetypeid),
- format_type_be(targettypeid))));
-}