diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2009-01-22 20:16:10 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2009-01-22 20:16:10 +0000 |
commit | 3cb5d6580a335e0b7fcf25da7fcebee3a776edb4 (patch) | |
tree | 53580564c946729c7f352b0dc26c7ee389a9d3a6 /src/backend/executor/execMain.c | |
parent | bf136cf6e376ae1a636341e5c8471c55299f9122 (diff) | |
download | postgresql-3cb5d6580a335e0b7fcf25da7fcebee3a776edb4.tar.gz postgresql-3cb5d6580a335e0b7fcf25da7fcebee3a776edb4.zip |
Support column-level privileges, as required by SQL standard.
Stephen Frost, with help from KaiGai Kohei and others
Diffstat (limited to 'src/backend/executor/execMain.c')
-rw-r--r-- | src/backend/executor/execMain.c | 115 |
1 files changed, 109 insertions, 6 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index bb05b5d4828..0352d9a5e43 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.320 2009/01/01 17:23:41 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.321 2009/01/22 20:16:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,6 +34,7 @@ #include "access/heapam.h" #include "access/reloptions.h" +#include "access/sysattr.h" #include "access/transam.h" #include "access/xact.h" #include "catalog/heap.h" @@ -453,8 +454,12 @@ static void ExecCheckRTEPerms(RangeTblEntry *rte) { AclMode requiredPerms; + AclMode relPerms; + AclMode remainingPerms; Oid relOid; Oid userid; + Bitmapset *tmpset; + int col; /* * Only plain-relation RTEs need to be checked here. Function RTEs are @@ -484,12 +489,110 @@ ExecCheckRTEPerms(RangeTblEntry *rte) userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* - * We must have *all* the requiredPerms bits, so use aclmask not aclcheck. + * We must have *all* the requiredPerms bits, but some of the bits can be + * satisfied from column-level rather than relation-level permissions. + * First, remove any bits that are satisfied by relation permissions. */ - if (pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL) - != requiredPerms) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); + relPerms = pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL); + remainingPerms = requiredPerms & ~relPerms; + if (remainingPerms != 0) + { + /* + * If we lack any permissions that exist only as relation permissions, + * we can fail straight away. + */ + if (remainingPerms & ~(ACL_SELECT | ACL_INSERT | ACL_UPDATE)) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + + /* + * Check to see if we have the needed privileges at column level. + * + * Note: failures just report a table-level error; it would be nicer + * to report a column-level error if we have some but not all of the + * column privileges. + */ + if (remainingPerms & ACL_SELECT) + { + /* + * When the query doesn't explicitly reference any columns (for + * example, SELECT COUNT(*) FROM table), allow the query if we + * have SELECT on any column of the rel, as per SQL spec. + */ + if (bms_is_empty(rte->selectedCols)) + { + if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, + ACLMASK_ANY) != ACLCHECK_OK) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + } + + tmpset = bms_copy(rte->selectedCols); + while ((col = bms_first_member(tmpset)) >= 0) + { + /* remove the column number offset */ + col += FirstLowInvalidHeapAttributeNumber; + if (col == InvalidAttrNumber) + { + /* Whole-row reference, must have priv on all cols */ + if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, + ACLMASK_ALL) != ACLCHECK_OK) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + } + else + { + if (pg_attribute_aclcheck(relOid, col, userid, ACL_SELECT) + != ACLCHECK_OK) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + } + } + bms_free(tmpset); + } + + /* + * Basically the same for the mod columns, with either INSERT or UPDATE + * privilege as specified by remainingPerms. + */ + remainingPerms &= ~ACL_SELECT; + if (remainingPerms != 0) + { + /* + * When the query doesn't explicitly change any columns, allow + * the query if we have permission on any column of the rel. This + * is to handle SELECT FOR UPDATE as well as possible corner cases + * in INSERT and UPDATE. + */ + if (bms_is_empty(rte->modifiedCols)) + { + if (pg_attribute_aclcheck_all(relOid, userid, remainingPerms, + ACLMASK_ANY) != ACLCHECK_OK) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + } + + tmpset = bms_copy(rte->modifiedCols); + while ((col = bms_first_member(tmpset)) >= 0) + { + /* remove the column number offset */ + col += FirstLowInvalidHeapAttributeNumber; + if (col == InvalidAttrNumber) + { + /* whole-row reference can't happen here */ + elog(ERROR, "whole-row update is not implemented"); + } + else + { + if (pg_attribute_aclcheck(relOid, col, userid, remainingPerms) + != ACLCHECK_OK) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + } + } + bms_free(tmpset); + } + } } /* |