On Mon, Dec 27, 2010 at 16:40, Magnus Hagander <mag...@hagander.net> wrote:
> On Mon, Dec 27, 2010 at 16:33, Tom Lane <t...@sss.pgh.pa.us> wrote:
>> Magnus Hagander <mag...@hagander.net> writes:
>>> On Mon, Dec 27, 2010 at 10:53, Magnus Hagander <mag...@hagander.net> wrote:
>>>> We could quite easily make a replication role *never* be able to
>>>> connect to a non-walsender backend. That would mean that if you set
>>>> your role to WITH REPLICATION, it can *only* be used for replication
>>>> and nothing else (well, you could still SET ROLE to it, but given that
>>>> it's not a superuser (anymore), that doesn't have any security
>>>> implications.
>>
>>> Actually, having implemented that and tested it, I realize that's a
>>> pretty bad idea.
>>
>> OK, so if we're not going to recommend that REPLICATION roles be
>> NOLOGIN, we're back to the original question: should the REPLICATION
>> bit give any other special privileges?  I can see the point of allowing
>> such a user to issue pg_start_backup and pg_stop_backup.
>
> Yes, those would definitely be useful.

Updated patch, still pending docs, but otherwise updated: allow
start/stop backup, make sure only superuser can turn on/off the flag,
include in system views, show properly in psql.


-- 
 Magnus Hagander
 Me: http://www.hagander.net/
 Work: http://www.redpill-linpro.com/
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 8301,8310 **** pg_start_backup(PG_FUNCTION_ARGS)
  	struct stat stat_buf;
  	FILE	   *fp;
  
! 	if (!superuser())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser to run a backup")));
  
  	if (RecoveryInProgress())
  		ereport(ERROR,
--- 8301,8310 ----
  	struct stat stat_buf;
  	FILE	   *fp;
  
! 	if (!superuser() && !is_authenticated_user_replication_role())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("must be superuser or replication role to run a backup")));
  
  	if (RecoveryInProgress())
  		ereport(ERROR,
***************
*** 8493,8502 **** pg_stop_backup(PG_FUNCTION_ARGS)
  	int			waits = 0;
  	bool		reported_waiting = false;
  
! 	if (!superuser())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to run a backup"))));
  
  	if (RecoveryInProgress())
  		ereport(ERROR,
--- 8493,8502 ----
  	int			waits = 0;
  	bool		reported_waiting = false;
  
! 	if (!superuser() && !is_authenticated_user_replication_role())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser or replication role to run a backup"))));
  
  	if (RecoveryInProgress())
  		ereport(ERROR,
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 15,20 **** CREATE VIEW pg_roles AS
--- 15,21 ----
          rolcreatedb,
          rolcatupdate,
          rolcanlogin,
+ 	rolreplication,
          rolconnlimit,
          '********'::text as rolpassword,
          rolvaliduntil,
***************
*** 30,35 **** CREATE VIEW pg_shadow AS
--- 31,37 ----
          rolcreatedb AS usecreatedb,
          rolsuper AS usesuper,
          rolcatupdate AS usecatupd,
+ 	rolreplication AS userepl,
          rolpassword AS passwd,
          rolvaliduntil::abstime AS valuntil,
          setconfig AS useconfig
***************
*** 54,59 **** CREATE VIEW pg_user AS
--- 56,62 ----
          usecreatedb,
          usesuper,
          usecatupd,
+ 	userepl,
          '********'::text as passwd,
          valuntil,
          useconfig
*** a/src/backend/commands/user.c
--- b/src/backend/commands/user.c
***************
*** 94,99 **** CreateRole(CreateRoleStmt *stmt)
--- 94,100 ----
  	bool		createrole = false;		/* Can this user create roles? */
  	bool		createdb = false;		/* Can the user create databases? */
  	bool		canlogin = false;		/* Can this user login? */
+ 	bool		isreplication = false; /* Is this a replication role? */
  	int			connlimit = -1; /* maximum connections allowed */
  	List	   *addroleto = NIL;	/* roles to make this a member of */
  	List	   *rolemembers = NIL;		/* roles to be members of this role */
***************
*** 107,112 **** CreateRole(CreateRoleStmt *stmt)
--- 108,114 ----
  	DefElem    *dcreaterole = NULL;
  	DefElem    *dcreatedb = NULL;
  	DefElem    *dcanlogin = NULL;
+ 	DefElem	   *disreplication = NULL;
  	DefElem    *dconnlimit = NULL;
  	DefElem    *daddroleto = NULL;
  	DefElem    *drolemembers = NULL;
***************
*** 190,195 **** CreateRole(CreateRoleStmt *stmt)
--- 192,205 ----
  						 errmsg("conflicting or redundant options")));
  			dcanlogin = defel;
  		}
+ 		else if (strcmp(defel->defname, "isreplication") == 0)
+ 		{
+ 			if (disreplication)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			disreplication = defel;
+ 		}
  		else if (strcmp(defel->defname, "connectionlimit") == 0)
  		{
  			if (dconnlimit)
***************
*** 247,252 **** CreateRole(CreateRoleStmt *stmt)
--- 257,264 ----
  		createdb = intVal(dcreatedb->arg) != 0;
  	if (dcanlogin)
  		canlogin = intVal(dcanlogin->arg) != 0;
+ 	if (disreplication)
+ 		isreplication = intVal(disreplication->arg) != 0;
  	if (dconnlimit)
  	{
  		connlimit = intVal(dconnlimit->arg);
***************
*** 272,277 **** CreateRole(CreateRoleStmt *stmt)
--- 284,296 ----
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to create superusers")));
  	}
+ 	else if (isreplication)
+ 	{
+ 		if (!superuser())
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 					 errmsg("must be superuser to create replication users")));
+ 	}
  	else
  	{
  		if (!have_createrole_privilege())
***************
*** 341,346 **** CreateRole(CreateRoleStmt *stmt)
--- 360,366 ----
  	/* superuser gets catupdate right by default */
  	new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
  	new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
+ 	new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
  	new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
  
  	if (password)
***************
*** 439,444 **** AlterRole(AlterRoleStmt *stmt)
--- 459,465 ----
  	int			createrole = -1;	/* Can this user create roles? */
  	int			createdb = -1;	/* Can the user create databases? */
  	int			canlogin = -1;	/* Can this user login? */
+ 	int			isreplication = -1; /* Is this a replication role? */
  	int			connlimit = -1; /* maximum connections allowed */
  	List	   *rolemembers = NIL;		/* roles to be added/removed */
  	char	   *validUntil = NULL;		/* time the login is valid until */
***************
*** 450,455 **** AlterRole(AlterRoleStmt *stmt)
--- 471,477 ----
  	DefElem    *dcreaterole = NULL;
  	DefElem    *dcreatedb = NULL;
  	DefElem    *dcanlogin = NULL;
+ 	DefElem	   *disreplication = NULL;
  	DefElem    *dconnlimit = NULL;
  	DefElem    *drolemembers = NULL;
  	DefElem    *dvalidUntil = NULL;
***************
*** 514,519 **** AlterRole(AlterRoleStmt *stmt)
--- 536,549 ----
  						 errmsg("conflicting or redundant options")));
  			dcanlogin = defel;
  		}
+ 		else if (strcmp(defel->defname, "isreplication") == 0)
+ 		{
+ 			if (disreplication)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			disreplication = defel;
+ 		}
  		else if (strcmp(defel->defname, "connectionlimit") == 0)
  		{
  			if (dconnlimit)
***************
*** 556,561 **** AlterRole(AlterRoleStmt *stmt)
--- 586,593 ----
  		createdb = intVal(dcreatedb->arg);
  	if (dcanlogin)
  		canlogin = intVal(dcanlogin->arg);
+ 	if (disreplication)
+ 		isreplication = intVal(disreplication->arg);
  	if (dconnlimit)
  	{
  		connlimit = intVal(dconnlimit->arg);
***************
*** 594,605 **** AlterRole(AlterRoleStmt *stmt)
--- 626,645 ----
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to alter superusers")));
  	}
+ 	else if (((Form_pg_authid) GETSTRUCT(tuple))->rolreplication || isreplication >= 0)
+ 	{
+ 		if (!superuser())
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 					 errmsg("must be superuser to alter replication users")));
+ 	}
  	else if (!have_createrole_privilege())
  	{
  		if (!(inherit < 0 &&
  			  createrole < 0 &&
  			  createdb < 0 &&
  			  canlogin < 0 &&
+ 			  isreplication < 0 &&
  			  !dconnlimit &&
  			  !rolemembers &&
  			  !validUntil &&
***************
*** 685,690 **** AlterRole(AlterRoleStmt *stmt)
--- 725,736 ----
  		new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
  	}
  
+ 	if (isreplication >= 0)
+ 	{
+ 		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
+ 		new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
+ 	}
+ 
  	if (dconnlimit)
  	{
  		new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 510,517 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
  	MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NOCREATEDB
! 	NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
! 	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC
  
  	OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
  	ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
--- 510,518 ----
  	MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NOCREATEDB
! 	NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOREPLICATION_P
! 	NOSUPERUSER NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
! 	NULLS_P NUMERIC
  
  	OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
  	ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
***************
*** 523,530 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
  	QUOTE
  
  	RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REINDEX
! 	RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
! 	RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
  
  	SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
  	SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
--- 524,532 ----
  	QUOTE
  
  	RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REINDEX
! 	RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA REPLICATION_P
! 	RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK
! 	ROW ROWS RULE
  
  	SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
  	SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
***************
*** 864,869 **** AlterOptRoleElem:
--- 866,879 ----
  				{
  					$$ = makeDefElem("canlogin", (Node *)makeInteger(FALSE));
  				}
+ 			| REPLICATION_P
+ 				{
+ 					$$ = makeDefElem("isreplication", (Node *)makeInteger(TRUE));
+ 				}
+ 			| NOREPLICATION_P
+ 				{
+ 					$$ = makeDefElem("isreplication", (Node *)makeInteger(FALSE));
+ 				}
  			| CONNECTION LIMIT SignedIconst
  				{
  					$$ = makeDefElem("connectionlimit", (Node *)makeInteger($3));
***************
*** 11288,11293 **** unreserved_keyword:
--- 11298,11304 ----
  			| NOCREATEUSER
  			| NOINHERIT
  			| NOLOGIN_P
+ 			| NOREPLICATION_P
  			| NOSUPERUSER
  			| NOTHING
  			| NOTIFY
***************
*** 11330,11335 **** unreserved_keyword:
--- 11341,11347 ----
  			| REPEATABLE
  			| REPLACE
  			| REPLICA
+ 			| REPLICATION_P
  			| RESET
  			| RESTART
  			| RESTRICT
*** a/src/backend/utils/init/miscinit.c
--- b/src/backend/utils/init/miscinit.c
***************
*** 231,236 **** static int	SecurityRestrictionContext = 0;
--- 231,240 ----
  static bool SetRoleIsActive = false;
  
  
+ /* Remember if the authenticated user is a replication role */
+ static bool AuthenticatedUserIsReplicationRole = false;
+ 
+ 
  /*
   * GetUserId - get the current effective user ID.
   *
***************
*** 389,394 **** SetUserIdAndContext(Oid userid, bool sec_def_context)
--- 393,407 ----
  
  
  /*
+  * Check if the authenticated user is a replication role
+  */
+ bool
+ is_authenticated_user_replication_role(void)
+ {
+ 	return AuthenticatedUserIsReplicationRole;
+ }
+ 
+ /*
   * Initialize user identity during normal backend startup
   */
  void
***************
*** 418,423 **** InitializeSessionUserId(const char *rolename)
--- 431,437 ----
  
  	AuthenticatedUserId = roleid;
  	AuthenticatedUserIsSuperuser = rform->rolsuper;
+ 	AuthenticatedUserIsReplicationRole = rform->rolreplication;
  
  	/* This sets OuterUserId/CurrentUserId too */
  	SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
***************
*** 658,668 **** InitPostgres(const char *in_dbname, Oid dboid, const char *username,
  	{
  		Assert(!bootstrap);
  
! 		/* must have authenticated as a superuser */
! 		if (!am_superuser)
  			ereport(FATAL,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 					 errmsg("must be superuser to start walsender")));
  
  		/* process any options passed in the startup packet */
  		if (MyProcPort != NULL)
--- 658,668 ----
  	{
  		Assert(!bootstrap);
  
! 		/* must have authenticated as a replication role */
! 		if (!is_authenticated_user_replication_role())
  			ereport(FATAL,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 					 errmsg("must be replication role to start walsender")));
  
  		/* process any options passed in the startup packet */
  		if (MyProcPort != NULL)
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 2195,2200 **** describeRoles(const char *pattern, bool verbose)
--- 2195,2204 ----
  			appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
  			ncols++;
  		}
+ 		if (pset.sversion >= 90100)
+ 		{
+ 			appendPQExpBufferStr(&buf,"\n, r.rolreplication");
+ 		}
  
  		appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
  
***************
*** 2254,2259 **** describeRoles(const char *pattern, bool verbose)
--- 2258,2267 ----
  		if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
  			add_role_attribute(&buf, _("Cannot login"));
  
+ 		if (pset.sversion >= 90100)
+ 			if (strcmp(PQgetvalue(res, i, 8), "t") == 0)
+ 				add_role_attribute(&buf, _("Replication"));
+ 
  		conns = atoi(PQgetvalue(res, i, 6));
  		if (conns >= 0)
  		{
*** a/src/include/catalog/pg_authid.h
--- b/src/include/catalog/pg_authid.h
***************
*** 51,56 **** CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MAC
--- 51,57 ----
  	bool		rolcreatedb;	/* allowed to create databases? */
  	bool		rolcatupdate;	/* allowed to alter catalogs manually? */
  	bool		rolcanlogin;	/* allowed to log in as session user? */
+ 	bool        rolreplication; /* role used for streaming replication */
  	int4		rolconnlimit;	/* max connections allowed (-1=no limit) */
  
  	/* remaining fields may be null; use heap_getattr to read them! */
***************
*** 72,78 **** typedef FormData_pg_authid *Form_pg_authid;
   *		compiler constants for pg_authid
   * ----------------
   */
! #define Natts_pg_authid					10
  #define Anum_pg_authid_rolname			1
  #define Anum_pg_authid_rolsuper			2
  #define Anum_pg_authid_rolinherit		3
--- 73,79 ----
   *		compiler constants for pg_authid
   * ----------------
   */
! #define Natts_pg_authid					11
  #define Anum_pg_authid_rolname			1
  #define Anum_pg_authid_rolsuper			2
  #define Anum_pg_authid_rolinherit		3
***************
*** 80,88 **** typedef FormData_pg_authid *Form_pg_authid;
  #define Anum_pg_authid_rolcreatedb		5
  #define Anum_pg_authid_rolcatupdate		6
  #define Anum_pg_authid_rolcanlogin		7
! #define Anum_pg_authid_rolconnlimit		8
! #define Anum_pg_authid_rolpassword		9
! #define Anum_pg_authid_rolvaliduntil	10
  
  /* ----------------
   *		initial contents of pg_authid
--- 81,90 ----
  #define Anum_pg_authid_rolcreatedb		5
  #define Anum_pg_authid_rolcatupdate		6
  #define Anum_pg_authid_rolcanlogin		7
! #define Anum_pg_authid_rolreplication	8
! #define Anum_pg_authid_rolconnlimit		9
! #define Anum_pg_authid_rolpassword		10
! #define Anum_pg_authid_rolvaliduntil	11
  
  /* ----------------
   *		initial contents of pg_authid
***************
*** 91,97 **** typedef FormData_pg_authid *Form_pg_authid;
   * user choices.
   * ----------------
   */
! DATA(insert OID = 10 ( "POSTGRES" t t t t t t -1 _null_ _null_ ));
  
  #define BOOTSTRAP_SUPERUSERID 10
  
--- 93,99 ----
   * user choices.
   * ----------------
   */
! DATA(insert OID = 10 ( "POSTGRES" t t t t t t f -1 _null_ _null_ ));
  
  #define BOOTSTRAP_SUPERUSERID 10
  
*** a/src/include/miscadmin.h
--- b/src/include/miscadmin.h
***************
*** 357,362 **** extern void ValidatePgVersion(const char *path);
--- 357,363 ----
  extern void process_shared_preload_libraries(void);
  extern void process_local_preload_libraries(void);
  extern void pg_bindtextdomain(const char *domain);
+ extern bool is_authenticated_user_replication_role(void);
  
  /* in access/transam/xlog.c */
  extern bool BackupInProgress(void);
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 250,255 **** PG_KEYWORD("nocreateuser", NOCREATEUSER, UNRESERVED_KEYWORD)
--- 250,256 ----
  PG_KEYWORD("noinherit", NOINHERIT, UNRESERVED_KEYWORD)
  PG_KEYWORD("nologin", NOLOGIN_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("none", NONE, COL_NAME_KEYWORD)
+ PG_KEYWORD("noreplication", NOREPLICATION_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("nosuperuser", NOSUPERUSER, UNRESERVED_KEYWORD)
  PG_KEYWORD("not", NOT, RESERVED_KEYWORD)
  PG_KEYWORD("nothing", NOTHING, UNRESERVED_KEYWORD)
***************
*** 313,318 **** PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD)
--- 314,320 ----
  PG_KEYWORD("repeatable", REPEATABLE, UNRESERVED_KEYWORD)
  PG_KEYWORD("replace", REPLACE, UNRESERVED_KEYWORD)
  PG_KEYWORD("replica", REPLICA, UNRESERVED_KEYWORD)
+ PG_KEYWORD("replication", REPLICATION_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("reset", RESET, UNRESERVED_KEYWORD)
  PG_KEYWORD("restart", RESTART, UNRESERVED_KEYWORD)
  PG_KEYWORD("restrict", RESTRICT, UNRESERVED_KEYWORD)
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to