DROP PRIVILEGES OWNED BY

Started by Marko Tiikkajaabout 11 years ago5 messages
#1Marko Tiikkaja
marko@joh.to
1 attachment(s)

Hi,

This week I had a problem where I wanted to drop only the privileges a
certain role had in the system, while keeping all the objects. I
couldn't figure out a reasonable way to do that, so I've attached a
patch for this to this email. Please consider it for inclusion into
9.5. The syntax is:

DROP PRIVILEGES OWNED BY role [, ...]

I at some point decided to implement it as a new command instead of
changing DropOwnedStmt, and I think that might have been a mistake. It
might have made more sense to instead teach DROP OWNED to accept a
specification of which things to drop. But the proposal is more
important than such details, I think.

.marko

Attachments:

drop_privileges_owned.v0.patchtext/plain; charset=UTF-8; name=drop_privileges_owned.v0.patch; x-mac-creator=0; x-mac-type=0Download
*** a/doc/src/sgml/ref/drop_owned.sgml
--- b/doc/src/sgml/ref/drop_owned.sgml
***************
*** 111,116 **** DROP OWNED BY <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCAD
--- 111,117 ----
    <title>See Also</title>
  
    <simplelist type="inline">
+    <member><xref linkend="sql-drop-privileges-owned"></member>
     <member><xref linkend="sql-reassign-owned"></member>
     <member><xref linkend="sql-droprole"></member>
    </simplelist>
*** /dev/null
--- b/doc/src/sgml/ref/drop_privileges_owned.sgml
***************
*** 0 ****
--- 1,72 ----
+ <!--
+ doc/src/sgml/ref/drop_privileges_owned.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-DROP-OWNED">
+  <indexterm zone="sql-drop-privileges-owned">
+   <primary>DROP PRIVILEGES OWNED</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>DROP PRIVILEGES OWNED</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>DROP PRIVILEGES OWNED</refname>
+   <refpurpose>remove privileges granted to a database role</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ DROP PRIVILEGES OWNED BY <replaceable class="PARAMETER">name</replaceable> [, ...]
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>DROP PRIVILEGES OWNED</command> revokes all privileges granted to
+    the given roles on objects in the current database and on shared objects
+    (databases, tablespaces).
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><replaceable class="PARAMETER">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of a role whose privileges will be revoked.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    The <command>DROP PRIVILEGES OWNED</command> statement is a
+    <productname>PostgreSQL</productname> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-drop-owned"></member>
+    <member><xref linkend="sql-reassign-owned"></member>
+    <member><xref linkend="sql-droprole"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
*** a/src/backend/catalog/pg_shdepend.c
--- b/src/backend/catalog/pg_shdepend.c
***************
*** 1162,1174 **** isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
   * interdependent objects in the wrong order.
   */
  void
! shdepDropOwned(List *roleids, DropBehavior behavior)
  {
  	Relation	sdepRel;
  	ListCell   *cell;
! 	ObjectAddresses *deleteobjs;
  
! 	deleteobjs = new_object_addresses();
  
  	/*
  	 * We don't need this strong a lock here, but we'll call routines that
--- 1162,1175 ----
   * interdependent objects in the wrong order.
   */
  void
! shdepDropOwned(List *roleids, DropBehavior behavior, bool privilegesOnly)
  {
  	Relation	sdepRel;
  	ListCell   *cell;
! 	ObjectAddresses *deleteobjs = NULL;
  
! 	if (!privilegesOnly)
! 		deleteobjs = new_object_addresses();
  
  	/*
  	 * We don't need this strong a lock here, but we'll call routines that
***************
*** 1243,1249 **** shdepDropOwned(List *roleids, DropBehavior behavior)
  					break;
  				case SHARED_DEPENDENCY_OWNER:
  					/* If a local object, save it for deletion below */
! 					if (sdepForm->dbid == MyDatabaseId)
  					{
  						obj.classId = sdepForm->classid;
  						obj.objectId = sdepForm->objid;
--- 1244,1250 ----
  					break;
  				case SHARED_DEPENDENCY_OWNER:
  					/* If a local object, save it for deletion below */
! 					if (!privilegesOnly && sdepForm->dbid == MyDatabaseId)
  					{
  						obj.classId = sdepForm->classid;
  						obj.objectId = sdepForm->objid;
***************
*** 1257,1268 **** shdepDropOwned(List *roleids, DropBehavior behavior)
  		systable_endscan(scan);
  	}
  
! 	/* the dependency mechanism does the actual work */
! 	performMultipleDeletions(deleteobjs, behavior, 0);
  
  	heap_close(sdepRel, RowExclusiveLock);
  
! 	free_object_addresses(deleteobjs);
  }
  
  /*
--- 1258,1274 ----
  		systable_endscan(scan);
  	}
  
! 	/*
! 	 * Unless we were asked not to drop objects, now is the time to let the
! 	 * dependency mechanism do the actual work of dropping them.
! 	 */
! 	if (!privilegesOnly)
! 		performMultipleDeletions(deleteobjs, behavior, 0);
  
  	heap_close(sdepRel, RowExclusiveLock);
  
! 	if (deleteobjs)
! 		free_object_addresses(deleteobjs);
  }
  
  /*
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
***************
*** 264,269 **** check_ddl_tag(const char *tag)
--- 264,270 ----
  		pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
  		pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
  		pg_strcasecmp(tag, "DROP OWNED") == 0 ||
+ 		pg_strcasecmp(tag, "DROP PRIVILEGES OWNED") == 0 ||
  		pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0)
  		return EVENT_TRIGGER_COMMAND_TAG_OK;
  
*** a/src/backend/commands/user.c
--- b/src/backend/commands/user.c
***************
*** 1308,1314 **** DropOwnedObjects(DropOwnedStmt *stmt)
  	}
  
  	/* Ok, do it */
! 	shdepDropOwned(role_ids, stmt->behavior);
  }
  
  /*
--- 1308,1340 ----
  	}
  
  	/* Ok, do it */
! 	shdepDropOwned(role_ids, stmt->behavior, false);
! }
! 
! /*
!  * DropOwnedPrivileges
!  *
!  * Revoke privileges granted to a given list of roles.
!  */
! void
! DropOwnedPrivileges(DropPrivilegesOwnedStmt *stmt)
! {
! 	List	   *role_ids = roleNamesToIds(stmt->roles);
! 	ListCell   *cell;
! 
! 	/* Check privileges */
! 	foreach(cell, role_ids)
! 	{
! 		Oid			roleid = lfirst_oid(cell);
! 
! 		if (!has_privs_of_role(GetUserId(), roleid))
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 					 errmsg("permission denied to drop privileges")));
! 	}
! 
! 	/* Ok, do it */
! 	shdepDropOwned(role_ids, DROP_RESTRICT, true);
  }
  
  /*
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3844,3849 **** _copyDropOwnedStmt(const DropOwnedStmt *from)
--- 3844,3859 ----
  	return newnode;
  }
  
+ static DropPrivilegesOwnedStmt *
+ _copyDropPrivilegesOwnedStmt(const DropPrivilegesOwnedStmt *from)
+ {
+ 	DropPrivilegesOwnedStmt *newnode = makeNode(DropPrivilegesOwnedStmt);
+ 
+ 	COPY_NODE_FIELD(roles);
+ 
+ 	return newnode;
+ }
+ 
  static ReassignOwnedStmt *
  _copyReassignOwnedStmt(const ReassignOwnedStmt *from)
  {
***************
*** 4616,4621 **** copyObject(const void *from)
--- 4626,4634 ----
  		case T_DropOwnedStmt:
  			retval = _copyDropOwnedStmt(from);
  			break;
+ 		case T_DropPrivilegesOwnedStmt:
+ 			retval = _copyDropPrivilegesOwnedStmt(from);
+ 			break;
  		case T_ReassignOwnedStmt:
  			retval = _copyReassignOwnedStmt(from);
  			break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1978,1983 **** _equalDropOwnedStmt(const DropOwnedStmt *a, const DropOwnedStmt *b)
--- 1978,1991 ----
  }
  
  static bool
+ _equalDropPrivilegesOwnedStmt(const DropPrivilegesOwnedStmt *a, const DropPrivilegesOwnedStmt *b)
+ {
+ 	COMPARE_NODE_FIELD(roles);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalReassignOwnedStmt(const ReassignOwnedStmt *a, const ReassignOwnedStmt *b)
  {
  	COMPARE_NODE_FIELD(roles);
***************
*** 3043,3048 **** equal(const void *a, const void *b)
--- 3051,3059 ----
  		case T_DropOwnedStmt:
  			retval = _equalDropOwnedStmt(a, b);
  			break;
+ 		case T_DropPrivilegesOwnedStmt:
+ 			retval = _equalDropPrivilegesOwnedStmt(a, b);
+ 			break;
  		case T_ReassignOwnedStmt:
  			retval = _equalReassignOwnedStmt(a, b);
  			break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 256,262 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  		VariableResetStmt VariableSetStmt VariableShowStmt
  		ViewStmt CheckPointStmt CreateConversionStmt
  		DeallocateStmt PrepareStmt ExecuteStmt
! 		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
  		CreateMatViewStmt RefreshMatViewStmt
  
--- 256,262 ----
  		VariableResetStmt VariableSetStmt VariableShowStmt
  		ViewStmt CheckPointStmt CreateConversionStmt
  		DeallocateStmt PrepareStmt ExecuteStmt
! 		DropOwnedStmt DropPrivilegesOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
  		CreateMatViewStmt RefreshMatViewStmt
  
***************
*** 807,812 **** stmt :
--- 807,813 ----
  			| DropOpFamilyStmt
  			| DropOwnedStmt
  			| DropPolicyStmt
+ 			| DropPrivilegesOwnedStmt
  			| DropPLangStmt
  			| DropRuleStmt
  			| DropStmt
***************
*** 5389,5394 **** DropOpFamilyStmt:
--- 5390,5396 ----
   *		QUERY:
   *
   *		DROP OWNED BY username [, username ...] [ RESTRICT | CASCADE ]
+  *		DROP PRIVILEGES OWNED BY username [, username ...]
   *		REASSIGN OWNED BY username [, username ...] TO username
   *
   *****************************************************************************/
***************
*** 5400,5405 **** DropOwnedStmt:
--- 5402,5416 ----
  					n->behavior = $5;
  					$$ = (Node *)n;
  				}
+ 			;
+ 
+ DropPrivilegesOwnedStmt:
+ 			DROP PRIVILEGES OWNED BY role_list
+ 				{
+ 					DropPrivilegesOwnedStmt *n = makeNode(DropPrivilegesOwnedStmt);
+ 					n->roles = $5;
+ 					$$ = (Node *)n;
+ 				}
  		;
  
  ReassignOwnedStmt:
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 189,194 **** check_xact_readonly(Node *parsetree)
--- 189,195 ----
  		case T_AlterDefaultPrivilegesStmt:
  		case T_TruncateStmt:
  		case T_DropOwnedStmt:
+ 		case T_DropPrivilegesOwnedStmt:
  		case T_ReassignOwnedStmt:
  		case T_AlterTSDictionaryStmt:
  		case T_AlterTSConfigurationStmt:
***************
*** 1319,1324 **** ProcessUtilitySlow(Node *parsetree,
--- 1320,1329 ----
  				DropOwnedObjects((DropOwnedStmt *) parsetree);
  				break;
  
+ 			case T_DropPrivilegesOwnedStmt:
+ 				DropOwnedPrivileges((DropPrivilegesOwnedStmt *) parsetree);
+ 				break;
+ 
  			case T_AlterDefaultPrivilegesStmt:
  				ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt *) parsetree);
  				break;
***************
*** 2256,2261 **** CreateCommandTag(Node *parsetree)
--- 2261,2270 ----
  			tag = "DROP OWNED";
  			break;
  
+ 		case T_DropPrivilegesOwnedStmt:
+ 			tag = "DROP PRIVILEGES OWNED";
+ 			break;
+ 
  		case T_ReassignOwnedStmt:
  			tag = "REASSIGN OWNED";
  			break;
***************
*** 2813,2818 **** GetCommandLogLevel(Node *parsetree)
--- 2822,2831 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_DropPrivilegesOwnedStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  		case T_ReassignOwnedStmt:
  			lev = LOGSTMT_DDL;
  			break;
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 255,261 **** extern void copyTemplateDependencies(Oid templateDbId, Oid newDbId);
  
  extern void dropDatabaseDependencies(Oid databaseId);
  
! extern void shdepDropOwned(List *relids, DropBehavior behavior);
  
  extern void shdepReassignOwned(List *relids, Oid newrole);
  
--- 255,261 ----
  
  extern void dropDatabaseDependencies(Oid databaseId);
  
! extern void shdepDropOwned(List *relids, DropBehavior behavior, bool privilegesOnly);
  
  extern void shdepReassignOwned(List *relids, Oid newrole);
  
*** a/src/include/commands/user.h
--- b/src/include/commands/user.h
***************
*** 29,34 **** extern void DropRole(DropRoleStmt *stmt);
--- 29,35 ----
  extern void GrantRole(GrantRoleStmt *stmt);
  extern Oid	RenameRole(const char *oldname, const char *newname);
  extern void DropOwnedObjects(DropOwnedStmt *stmt);
+ extern void DropOwnedPrivileges(DropPrivilegesOwnedStmt *stmt);
  extern void ReassignOwnedObjects(ReassignOwnedStmt *stmt);
  extern List *roleNamesToIds(List *memberNames);
  
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 342,347 **** typedef enum NodeTag
--- 342,348 ----
  	T_AlterObjectSchemaStmt,
  	T_AlterOwnerStmt,
  	T_DropOwnedStmt,
+ 	T_DropPrivilegesOwnedStmt,
  	T_ReassignOwnedStmt,
  	T_CompositeTypeStmt,
  	T_CreateEnumStmt,
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2816,2821 **** typedef struct DropOwnedStmt
--- 2816,2830 ----
  } DropOwnedStmt;
  
  /*
+  *		DROP PRIVILEGES OWNED statement
+  */
+ typedef struct DropPrivilegesOwnedStmt
+ {
+ 	NodeTag		type;
+ 	List	   *roles;
+ } DropPrivilegesOwnedStmt;
+ 
+ /*
   *		REASSIGN OWNED statement
   */
  typedef struct ReassignOwnedStmt
*** a/src/test/regress/expected/dependency.out
--- b/src/test/regress/expected/dependency.out
***************
*** 61,67 **** GRANT ALL ON deptest1 TO regression_user1 WITH GRANT OPTION;
--- 61,84 ----
  SET SESSION AUTHORIZATION regression_user1;
  CREATE TABLE deptest (a serial primary key, b text);
  GRANT ALL ON deptest1 TO regression_user2;
+ GRANT ALL ON deptest TO regression_user2;
  RESET SESSION AUTHORIZATION;
+ \z deptest
+                                           Access privileges
+  Schema |  Name   | Type  |             Access privileges             | Column privileges | Policies 
+ --------+---------+-------+-------------------------------------------+-------------------+----------
+  public | deptest | table | regression_user1=arwdDxt/regression_user1+|                   | 
+         |         |       | regression_user2=arwdDxt/regression_user1 |                   | 
+ (1 row)
+ 
+ DROP PRIVILEGES OWNED BY regression_user2;
+ \z deptest
+                                           Access privileges
+  Schema |  Name   | Type  |             Access privileges             | Column privileges | Policies 
+ --------+---------+-------+-------------------------------------------+-------------------+----------
+  public | deptest | table | regression_user1=arwdDxt/regression_user1 |                   | 
+ (1 row)
+ 
  \z deptest1
                                                Access privileges
   Schema |   Name   | Type  |                Access privileges                 | Column privileges | Policies 
*** a/src/test/regress/sql/dependency.sql
--- b/src/test/regress/sql/dependency.sql
***************
*** 63,71 **** GRANT ALL ON deptest1 TO regression_user1 WITH GRANT OPTION;
  SET SESSION AUTHORIZATION regression_user1;
  CREATE TABLE deptest (a serial primary key, b text);
  GRANT ALL ON deptest1 TO regression_user2;
  RESET SESSION AUTHORIZATION;
- \z deptest1
  
  DROP OWNED BY regression_user1;
  -- all grants revoked
  \z deptest1
--- 63,76 ----
  SET SESSION AUTHORIZATION regression_user1;
  CREATE TABLE deptest (a serial primary key, b text);
  GRANT ALL ON deptest1 TO regression_user2;
+ GRANT ALL ON deptest TO regression_user2;
  RESET SESSION AUTHORIZATION;
  
+ \z deptest
+ DROP PRIVILEGES OWNED BY regression_user2;
+ \z deptest
+ 
+ \z deptest1
  DROP OWNED BY regression_user1;
  -- all grants revoked
  \z deptest1
#2Michael Paquier
michael.paquier@gmail.com
In reply to: Marko Tiikkaja (#1)
Re: DROP PRIVILEGES OWNED BY

On Mon, Dec 15, 2014 at 9:43 AM, Marko Tiikkaja <marko@joh.to> wrote:

Hi,

This week I had a problem where I wanted to drop only the privileges a
certain role had in the system, while keeping all the objects. I couldn't
figure out a reasonable way to do that, so I've attached a patch for this to
this email. Please consider it for inclusion into 9.5. The syntax is:

DROP PRIVILEGES OWNED BY role [, ...]

I at some point decided to implement it as a new command instead of changing
DropOwnedStmt, and I think that might have been a mistake. It might have
made more sense to instead teach DROP OWNED to accept a specification of
which things to drop. But the proposal is more important than such details,
I think.

You should consider adding it to the upcoming CF:
https://commitfest.postgresql.org/action/commitfest_view?id=25
Regards,
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Marko Tiikkaja (#1)
Re: DROP PRIVILEGES OWNED BY

On 12/15/2014 02:43 AM, Marko Tiikkaja wrote:

This week I had a problem where I wanted to drop only the privileges a
certain role had in the system, while keeping all the objects. I
couldn't figure out a reasonable way to do that, so I've attached a
patch for this to this email. Please consider it for inclusion into
9.5. The syntax is:

DROP PRIVILEGES OWNED BY role [, ...]

I at some point decided to implement it as a new command instead of
changing DropOwnedStmt, and I think that might have been a mistake. It
might have made more sense to instead teach DROP OWNED to accept a
specification of which things to drop. But the proposal is more
important than such details, I think.

DROP seems like the wrong verb here. DROP is used for deleting objects,
while REVOKE is used for removing permissions from them. REVOKE already
has something similar:

REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM heikki;

Following that style, how about making the syntax:

REVOKE ALL PRIVILEGES ON ALL OBJECTS FROM <role>

or just:

REVOKE ALL PRIVILEGES FROM <role>;

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Marko Tiikkaja
marko@joh.to
In reply to: Heikki Linnakangas (#3)
Re: DROP PRIVILEGES OWNED BY

On 12/17/14 5:37 PM, Heikki Linnakangas wrote:

On 12/15/2014 02:43 AM, Marko Tiikkaja wrote:

The syntax is:

DROP PRIVILEGES OWNED BY role [, ...]

DROP seems like the wrong verb here. DROP is used for deleting objects,
while REVOKE is used for removing permissions from them. REVOKE already
has something similar:

REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM heikki;

Following that style, how about making the syntax:

REVOKE ALL PRIVILEGES FROM <role>;

I don't have a problem with that. It would probably work, too, since
FROM is already fully reserved.

.marko

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Michael Paquier
michael.paquier@gmail.com
In reply to: Marko Tiikkaja (#4)
Re: DROP PRIVILEGES OWNED BY

On Thu, Dec 18, 2014 at 1:43 AM, Marko Tiikkaja <marko@joh.to> wrote:

I don't have a problem with that. It would probably work, too, since FROM
is already fully reserved.

Marking patch as returned with feedback as there has been no input
from Marko in the last couple of weeks.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers