Hi Team,
Please find the patch for Login/Group Role(s).
Akshay/Murtuza,
Can you please review it?
--
Thanks & Regards,
Ashesh Vashi
EnterpriseDB INDIA: Enterprise PostgreSQL Company
<http://www.enterprisedb.com>
*http://www.linkedin.com/in/asheshvashi*
<http://www.linkedin.com/in/asheshvashi>
diff --git a/web/pgadmin/browser/server_groups/servers/roles/__init__.py
b/web/pgadmin/browser/server_groups/servers/roles/__init__.py
new file mode 100644
index 0000000..9557d6c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/roles/__init__.py
@@ -0,0 +1,901 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+from flask import render_template, request, current_app, jsonify
+from flask.ext.babel import gettext as _
+from pgadmin.utils.ajax import make_json_response, \
+ make_response as ajax_response, precondition_required, \
+ internal_server_error, forbidden, \
+ not_implemented, success_return
+from pgadmin.browser.utils import NodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups as sg
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+import re
+import datetime
+from functools import wraps
+import simplejson as json
+
+
+class RoleModule(CollectionNodeModule):
+ NODE_TYPE = 'role'
+ COLLECTION_LABEL = _("Login/Group Roles")
+
+ def __init__(self, *args, **kwargs):
+ self.min_ver = None
+ self.max_ver = None
+
+ super(RoleModule, self).__init__(*args, **kwargs)
+
+ def get_nodes(self, gid, sid):
+ """
+ Generate the collection node
+ """
+
+ yield self.generate_browser_collection_node(sid)
+
+ @property
+ def script_load(self):
+ """
+ Load the module script for server, when any of the server-group node is
+ initialized.
+ """
+ return sg.ServerGroupModule.NODE_TYPE
+
+ @property
+ def csssnippets(self):
+ """
+ Returns a snippet of css to include in the page
+ """
+ snippets = [
+ render_template(
+ "browser/css/collection.css",
+ node_type=self.node_type
+ ),
+ render_template("role/css/role.css")]
+
+ for submodule in self.submodules:
+ snippets.extend(submodule.csssnippets)
+
+ return snippets
+
+
+blueprint = RoleModule(__name__)
+
+
+class RoleView(NodeView):
+ node_type = 'role'
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'rid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'drop', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+ 'sql': [{'get': 'sql'}],
+ 'msql': [{'get': 'msql'}, {'get': 'msql'}],
+ 'dependency': [{'get': 'dependencies'}],
+ 'dependent': [{'get': 'dependents'}],
+ 'children': [{'get': 'children'}],
+ 'module.js': [{}, {}, {'get': 'module_js'}],
+ 'vopts': [{}, {'get': 'voptions'}],
+ 'variables': [{'get': 'variables'}],
+ })
+
+ def validate_request(f):
+ @wraps(f)
+ def wrap(self, **kwargs):
+
+ data = None
+ if request.data:
+ data = json.loads(request.data)
+ else:
+ data = dict()
+ req = request.args or request.form
+
+ for key in req:
+
+ val = req[key]
+ if key in [
+ u'rolcanlogin', u'rolsuper', u'rolcreatedb',
+ u'rolcreaterole', u'rolinherit', u'rolreplication',
+ u'rolcatupdate', u'variables', u'rolmembership',
+ u'seclabels'
+ ]:
+ data[key] = json.loads(val)
+ else:
+ data[key] = val
+
+ if u'rid' not in kwargs or kwargs['rid'] == -1:
+ if u'rolname' not in data:
+ return precondition_required(
+ _("Name is not provided!")
+ )
+
+ if u'rolconnlimit' in data:
+ if data[u'rolconnlimit'] is not None:
+ data[u'rolconnlimit'] = int(data[u'rolconnlimit'])
+ if type(data[u'rolconnlimit']) != int or
data[u'rolconnlimit'] < -1:
+ return precondition_required(
+ _("Connection limit must be an integer value
or equals to -1!")
+ )
+
+ if u'rolmembership' in data:
+ if u'rid' not in kwargs or kwargs['rid'] == -1:
+ msg = _("""
+Role membership information must be passed as an array of JSON object in the
+following format:
+
+rolmembership:[{
+ role: [rolename],
+ admin: True/False
+ },
+ ...
+]""")
+ if type(data[u'rolmembership']) != list:
+ return precondition_required(msg)
+
+ data[u'members'] = []
+ data[u'admins'] = []
+
+ for r in data[u'rolmembership']:
+ if type(r) != dict or u'role' not in r or u'admin' not
in r:
+ return precondition_required(msg)
+ else:
+ if r[u'admin']:
+ data[u'admins'].append(r[u'role'])
+ else:
+ data[u'members'].append(r[u'role'])
+ else:
+ msg = _("""
+Role membership information must be passed a string representing an array of
+JSON object in the following format:
+rolmembership:{
+ 'added': [{
+ role: [rolename],
+ admin: True/False
+ },
+ ...
+ ],
+ 'deleted': [{
+ role: [rolename],
+ admin: True/False
+ },
+ ...
+ ],
+ 'updated': [{
+ role: [rolename],
+ admin: True/False
+ },
+ ...
+ ]
+""")
+ if type(data[u'rolmembership']) != dict:
+ return precondition_required(msg)
+
+ data[u'members'] = []
+ data[u'admins'] = []
+ data[u'revoked_admins'] = []
+ data[u'revoked'] = []
+
+ if u'added' in data[u'rolmembership']:
+ roles = (data[u'rolmembership'])[u'added']
+
+ if type(roles) != list:
+ return precondition_required(msg)
+
+ for r in roles:
+ if (type(r) != dict or u'role' not in r or
+ u'admin' not in r):
+ return precondition_required(msg)
+
+ if r[u'admin']:
+ data[u'admins'].append(r[u'role'])
+ else:
+ data[u'members'].append(r[u'role'])
+
+ if u'deleted' in data[u'rolmembership']:
+ roles = (data[u'rolmembership'])[u'deleted']
+
+ if type(roles) != list:
+ return precondition_required(msg)
+
+ for r in roles:
+ if type(r) != dict or u'role' not in r:
+ return precondition_required(msg)
+
+ data[u'revoked'].append(r[u'role'])
+
+ if u'changed' in data[u'rolmembership']:
+ roles = (data[u'rolmembership'])[u'changed']
+
+ if type(roles) != list:
+ return precondition_required(msg)
+
+ for r in roles:
+ if (type(r) != dict or u'role' not in r or
+ u'admin' not in r):
+ return precondition_required(msg)
+
+ if not r[u'admin']:
+ data[u'revoked_admins'].append(r[u'role'])
+ else:
+ data[u'admins'].append(r[u'role'])
+
+ if self.manager.version >= 90200:
+ if u'seclabels' in data:
+ if u'rid' not in kwargs or kwargs['rid'] == -1:
+ msg = _("""
+Security Label must be passed as an array of JSON object in the following
+format:
+seclabels:[{
+ provider: <provider>,
+ label: <label>
+ },
+ ...
+]""")
+ if type(data[u'seclabels']) != list:
+ return precondition_required(msg)
+
+ for s in data[u'seclabels']:
+ if (type(s) != dict or u'provider' not in s or
+ u'label' not in s):
+ return precondition_required(msg)
+ else:
+ msg = _("""
+Security Label must be passed as an array of JSON object in the following
+format:
+rolmembership:{
+ 'added': [{
+ provider: <provider>,
+ label: <label>
+ },
+ ...
+ ],
+ 'deleted': [{
+ provider: <provider>,
+ label: <label>
+ },
+ ...
+ ],
+ 'updated': [{
+ provider: <provider>,
+ label: <label>
+ },
+ ...
+ ]
+""")
+ seclabels = data[u'seclabels']
+ if type(seclabels) != dict:
+ return precondition_required(msg)
+
+ if u'added' in seclabels:
+ new_seclabels = seclabels[u'added']
+
+ if type(new_seclabels) != list:
+ return precondition_required(msg)
+
+ for s in new_seclabels:
+ if (type(s) != dict or u'provilder' not in s or
+ u'label' not in s):
+ return precondition_required(msg)
+
+ if u'deleted' in seclabels:
+ removed_seclabels = seclabels[u'deleted']
+
+ if type(removed_seclabels) != list:
+ return precondition_required(msg)
+
+ for s in removed_seclabels:
+ if (type(s) != dict or u'provilder' not in s):
+ return precondition_required(msg)
+
+
+ if u'changed' in variables:
+ changed_seclabels = seclabels[u'deleted']
+
+ if type(changed_seclabels) != list:
+ return precondition_required(msg)
+
+ for s in changed_seclabels:
+ if (type(s) != dict or u'provilder' not in s
+ and u'label' not in s):
+ return precondition_required(msg)
+
+
+ if u'variables' in data:
+ if u'rid' not in kwargs or kwargs['rid'] == -1:
+ msg = _("""
+Configuration parameters/variables must be passed as an array of JSON object in
+the following format (create mode):
+variables:[{
+ database: <database> or null,
+ name: <configuration>,
+ value: <value>
+ },
+ ...
+]""")
+ if type(data[u'variables']) != list:
+ return precondition_required(msg)
+
+ for r in data[u'variables']:
+ if (type(r) != dict or
+ u'name' not in r or
+ u'value' not in r):
+ return precondition_required(msg)
+ else:
+ msg = _("""
+Configuration parameters/variables must be passed as an array of JSON object in
+the following format (update mode):
+rolmembership:{
+ 'added': [{
+ database: <database> or null,
+ name: <configuration>,
+ value: <value>
+ },
+ ...
+ ],
+ 'deleted': [{
+ database: <database> or null,
+ name: <configuration>,
+ value: <value>
+ },
+ ...
+ ],
+ 'updated': [{
+ database: <database> or null,
+ name: <configuration>,
+ value: <value>
+ },
+ ...
+ ]
+""")
+ variables = data[u'variables']
+ if type(variables) != dict:
+ return precondition_required(msg)
+
+ if u'added' in variables:
+ new_vars = variables[u'added']
+
+ if type(new_vars) != list:
+ return precondition_required(msg)
+
+ for v in new_vars:
+ if (type(v) != dict or u'name' not in v or
+ u'value' not in v):
+ return precondition_required(msg)
+
+ if u'deleted' in variables:
+ delete_vars = variables[u'deleted']
+
+ if type(delete_vars) != list:
+ return precondition_required(msg)
+
+ for v in delete_vars:
+ if type(v) != dict or u'name' not in v:
+ return precondition_required(msg)
+
+ if u'changed' in variables:
+ new_vars = variables[u'changed']
+
+ if type(new_vars) != list:
+ return precondition_required(msg)
+
+ for v in new_vars:
+ if (type(v) != dict or u'name' not in v or
+ u'value' not in v):
+ return precondition_required(msg)
+
+ self.request = data
+
+ return f(self, **kwargs)
+ return wrap
+
+ def check_precondition(action=None):
+ """
+ This function will behave as a decorator which will checks the status
+ of the database connection for the maintainance database of the server,
+ beforeexecuting rest of the operation for the wrapped function. It will
+ also attach manager, conn (maintenance connection for the server) as
+ properties of the instance.
+ """
+ def wrap(f):
+ @wraps(f)
+ def wrapped(self, **kwargs):
+ self.manager = get_driver(
+ PG_DEFAULT_DRIVER
+ ).connection_manager(
+ kwargs['sid']
+ )
+ self.conn = self.manager.connection()
+
+ if not self.conn.connected():
+ return precondition_required(
+ _("Connection to the server has been lost!")
+ )
+
+ ver = self.manager.version
+
+ self.sql_path = 'role/sql/{0}/'.format(
+ 'post9_4' if ver >= 90500 else \
+ 'post9_1' if ver >= 90200 else \
+ 'post9_0' if ver >= 90100 else \
+ 'post8_4'
+ )
+
+ self.alterKeys = [
+ u'rolcanlogin', u'rolsuper', u'rolcreatedb',
+ u'rolcreaterole', u'rolinherit', u'rolreplication',
+ u'rolconnlimit', u'rolvaliduntil', u'rolpassword'
+ ] if ver >= 90200 else [
+ u'rolcanlogin', u'rolsuper', u'rolcreatedb',
+ u'rolcreaterole', u'rolinherit',
u'rolconnlimit',
+ u'rolvaliduntil', u'rolpassword'
+ ]
+
+ auth_tbl=False
+ check_permission=False
+ fetch_name=False
+ forbidden_msg = None
+
+ if action in ['list', 'properties']:
+ auth_tbl = True
+ elif action in ['drop', 'update']:
+ check_permission = True
+ fetch_name = True
+ if action == 'drop':
+ forbidden_msg = _(
+ "The current user does not have permission to
drop the role!"
+ )
+ else:
+ forbidden_msg = _(
+ "The current user does not have permission to
update the role!"
+ )
+ elif action == 'create':
+ check_permission = True
+ forbidden_msg = _(
+ "The current user does not have permission to
create the role!"
+ )
+ elif (action == 'msql' and
+ 'rid' in kwargs and kwargs['rid'] != -1):
+ fetch_name = True
+
+ if auth_tbl:
+ status, res = self.conn.execute_scalar(
+ "SELECT has_table_privilege('pg_authid', 'SELECT')"
+ )
+
+ if not status:
+ return internal_server_error(
+ _(
+ "Error checking the permission to the
pg_authid!\n{0}"
+ ).format(res)
+ )
+ self.role_tbl = 'pg_authid' if res else 'pg_roles'
+ else:
+ self.role_tbl = 'pg_roles'
+
+ if check_permission:
+ user = self.manager.user_info
+
+ if not user['is_superuser'] and not
user['can_create_role']:
+ if (action != 'update' or
+ 'rid' in kwargs and kwargs['rid'] != -1 and
+ user['id'] != rid):
+ return forbidden(forbidden_msg)
+
+ if fetch_name:
+ status, res = self.conn.execute_2darray(
+ """
+SELECT
+ rolname, rolcanlogin, rolcatupdate, rolsuper
+FROM
+ pg_roles
+WHERE oid=%(rid)s::OID""",
+ {'rid': kwargs['rid']}
+ )
+
+ if not status:
+ return internal_server_error(
+ _(
+ "ERROR: fetching the role
information!\n{0}"
+ ).format(res)
+ )
+
+ if len(res['rows']) == 0:
+ return gone(
+ _("Couldn't find the specific role in the
database server!")
+ )
+
+ row = res['rows'][0]
+
+ self.role = row[0]
+ self.rolCanLogin = row[1]
+ self.rolCatUpdate = row[2]
+ self.rolSuper = row[3]
+
+ return f(self, **kwargs)
+ return wrapped
+ return wrap
+
+ @check_precondition(action='list')
+ def list(self, gid, sid):
+ status, res = self.conn.execute_dict(
+ render_template(self.sql_path + 'properties.sql',
+ role_tbl=self.role_tbl
+ )
+ )
+
+ if not status:
+ return internal_server_error(
+ _(
+ "Error fetching the roles from the database
server!\n{0}"
+ ).format(res)
+ )
+
+ self.transform(res)
+
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition(action='nodes')
+ def nodes(self, gid, sid):
+
+ status, rset = self.conn.execute_2darray(
+ render_template(self.sql_path + 'nodes.sql',
+ role_tbl=self.role_tbl
+ )
+ )
+
+ if not status:
+ return internal_server_error(
+ _(
+ "Error fetching the roles information from the
database server!\n{0}"
+ ).format(res)
+ )
+
+ res = []
+ for row in rset['rows']:
+ res.append(
+ self.blueprint.generate_browser_node(
+ row['oid'],
+ row['rolname'],
+ 'icon-role' if row['rolcanlogin'] else 'icon-group',
+ can_login=row['rolcanlogin'],
+ is_superuser=row['rolsuper']
+ )
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ @check_precondition(action='node')
+ def node(self, gid, sid, rid):
+
+ status, rset = self.conn.execute_2darray(
+ render_template(self.sql_path + 'nodes.sql',
+ rid=rid,
+ role_tbl=self.role_tbl
+ )
+ )
+
+ if not status:
+ return internal_server_error(
+ _(
+ "Error fetching the roles information from the
database server!\n{0}"
+ ).format(res)
+ )
+
+ for row in rset['rows']:
+ return make_json_response(
+ data=self.blueprint.generate_browser_node(
+ row['oid'],
+ row['rolname'],
+ 'icon-role' if row['rolcanlogin'] else 'icon-group',
+ can_login=row['rolcanlogin'],
+ is_superuser=row['rolsuper']
+ ),
+ status=200
+ )
+
+ return gone(_("Couldn't find the role information!"))
+
+ def transform(self, rset):
+ for row in rset['rows']:
+ res = []
+ roles = row['rolmembership']
+ for role in roles:
+ role = re.search(r'([01])(.+)', role)
+ res.append({
+ 'role': role.group(2),
+ 'admin': True if role.group(1) == '1' else False
+ })
+ row['rolmembership'] = res
+ row['rolvaliduntil'] = row['rolvaliduntil'].isoformat() \
+ if isinstance(
+ row['rolvaliduntil'],
+ (datetime.date, datetime.datetime)
+ ) else None
+ if 'seclabels' in row and row['seclabels'] is not None:
+ res = []
+ for sec in row['seclabels']:
+ sec = re.search(r'([^=]+)=(.*$)', sec)
+ res.append({
+ 'provider': sec.group(1),
+ 'label': sec.group(2)
+ })
+
+ @check_precondition(action='properties')
+ def properties(self, gid, sid, rid):
+
+ status, res = self.conn.execute_dict(
+ render_template(self.sql_path + 'properties.sql',
+ role_tbl=self.role_tbl,
+ rid=rid
+ )
+ )
+
+ if not status:
+ return internal_server_error(
+ _(
+ "Error fetching the roles from the database
server!\n{0}"
+ ).format(res)
+ )
+
+ self.transform(res)
+ if len(res['rows']) == 0:
+ return gone(_("Couldn't find the role information!"))
+
+ return ajax_response(
+ response=res['rows'][0],
+ status=200
+ )
+
+ @check_precondition(action='drop')
+ def drop(self, gid, sid, rid):
+
+ status, res = self.conn.execute_2darray(
+ "DROP ROLE {0};".format(self.role)
+ )
+ if not status:
+ return internal_server_error(
+ _("ERROR: Couldn't drop the user!\n{0}").format(res)
+ )
+
+ return success_return()
+
+ @check_precondition()
+ def sql(self, gid, sid, rid):
+ status, res = self.conn.execute_scalar(
+ render_template(
+ self.sql_path + 'sql.sql',
+ role_tbl=self.role_tbl
+ ),
+ dict({'rid':rid})
+ )
+
+ if not status:
+ return internal_server_error(
+ _("ERROR: Couldn't generate reversed engineered Query for
the role/user!\n{0}").format(
+ res
+ )
+ )
+
+ if res is None:
+ return gone(
+ _("ERROR: Couldn't generate reversed engineered Query for
the role/user!")
+ )
+
+ return ajax_response(response=res)
+
+ @check_precondition(action='create')
+ def sql(self, gid, sid, rid):
+
+ sql = render_template(
+ self.sql_path + 'create.sql',
+ data=self.request,
+ dummy=False,
+ conn=self.conn
+ )
+
+ status, msg = self.conn.execute_dict(sql)
+
+ if not status:
+ return internal_server_error(
+ _("ERROR: Couldn't create the role!\n{0}").format(msg)
+ )
+
+ status, rid = self.conn.execute_scalar(
+ "SELECT oid FROM {0} WHERE rolname = %(rolname)s".format(
+ self.role_tbl
+ ),
+ {'rolname': self.request[u'rolname']}
+ )
+
+ if not status:
+ return internal_server_error(
+ _("ERROR: Couldn't fetch the role
information!\n{0}").format(msg)
+ )
+
+
+ status, rset = self.conn.execute_dict(
+ render_template(self.sql_path + 'nodes.sql',
+ rid=rid,
+ role_tbl=self.role_tbl
+ )
+ )
+
+ if not status:
+ return internal_server_error(
+ _(
+ "Error fetching the roles information from the
database server!\n{0}"
+ ).format(res)
+ )
+ for row in rset['rows']:
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ rid,
+ row['rolname'],
+ 'icon-role' if row['rolcanlogin'] else 'icon-group',
+ can_login=row['rolcanlogin']
+ )
+ )
+
+ return gone(_("Couldn't find the role information!"))
+
+ @check_precondition(action='update')
+ @validate_request
+ def update(self, gid, sid, rid):
+
+ sql = render_template(
+ self.sql_path + 'update.sql',
+ data=self.request,
+ dummy=False,
+ conn=self.conn,
+ role=self.role,
+ rolCanLogin=self.rolCanLogin,
+ rolCatUpdate=self.rolCatUpdate,
+ rolSuper=self.rolSuper,
+ alterKeys=self.alterKeys
+ )
+
+ status, msg = self.conn.execute_dict(sql)
+
+ if not status:
+ return internal_server_error(
+ _("ERROR: Couldn't create the role!\n{0}").format(msg)
+ )
+
+ status, rset = self.conn.execute_dict(
+ render_template(self.sql_path + 'nodes.sql',
+ rid=rid,
+ role_tbl=self.role_tbl
+ )
+ )
+
+ if not status:
+ return internal_server_error(
+ _(
+ "Error fetching the roles information from the
database server!\n{0}"
+ ).format(res)
+ )
+
+ for row in rset['rows']:
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ rid,
+ row['rolname'],
+ 'icon-role' if row['rolcanlogin'] else 'icon-group',
+ can_login=row['rolcanlogin'],
+ is_superuser=row['rolsuper']
+ )
+ )
+
+ return gone(_("Couldn't find the role information!"))
+
+ @check_precondition(action='msql')
+ @validate_request
+ def msql(self, gid, sid, rid=-1):
+ if rid == -1:
+ return make_json_response(
+ data=render_template(
+ self.sql_path + 'create.sql',
+ data=self.request,
+ dummy=True,
+ conn=self.conn
+ )
+ )
+ else:
+ return make_json_response(
+ data=render_template(
+ self.sql_path + 'update.sql',
+ data=self.request,
+ dummy=True,
+ conn=self.conn,
+ role=self.role,
+ rolCanLogin=self.rolCanLogin,
+ rolCatUpdate=self.rolCatUpdate,
+ rolSuper=self.rolSuper,
+ alterKeys=self.alterKeys
+ )
+ )
+
+ @check_precondition()
+ def dependencies(self, gid, sid, rid):
+ return not_implemented()
+
+ @check_precondition()
+ def dependents(self, gid, sid, rid):
+ return not_implemented()
+
+ @check_precondition()
+ def variables(self, gid, sid, rid):
+
+ status, rset = self.conn.execute_dict(
+ render_template(self.sql_path + 'variables.sql',
+ rid=rid
+ )
+ )
+
+ if not status:
+ return internal_server_error(
+ _(
+ "Error fetching the variable information for the given
role!\n{0}"
+ ).format(rset)
+ )
+
+ return make_json_response(
+ data=rset['rows']
+ )
+
+ @check_precondition()
+ def voptions(self, gid, sid):
+
+ status, res = self.conn.execute_dict(
+ """
+SELECT
+ name, vartype, min_val, max_val, enumvals
+FROM
+ (
+ SELECT
+ 'role'::text AS name, 'string'::text AS vartype,
+ NULL AS min_val, NULL AS max_val, NULL::text[] AS enumvals
+ UNION ALL
+ SELECT
+ name, vartype, min_val::numeric AS min_val, max_val::numeric AS
max_val, enumvals
+ FROM
+ pg_settings
+ WHERE
+ context in ('user', 'superuser')
+ ) a""")
+
+ if not status:
+ return internal_server_error(
+ _(
+ "Error fetching the variable options for role!\n{0}"
+ ).format(res)
+ )
+ return make_json_response(
+ data=res['rows']
+ )
+
+
+RoleView.register_node_view(blueprint)
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/static/img/coll-role.png
b/web/pgadmin/browser/server_groups/servers/roles/static/img/coll-role.png
new file mode 100644
index
0000000000000000000000000000000000000000..f5c20bfb9df3fcfd5637142b5edadee88d16f1dd
GIT binary patch
literal 472
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}dw@@fE08{V^k{v3eRq4)g`;~e
z9NpL5-umO!!|$)2oIiTx`>Pk{j~?H@e}8v-M|4a~ZF5V0aY<frNo8Y8R7}kF?b~~%
zPQUW%!-W?gPCfg0<nhO%nugrm+}X2dH#9VCIdWpw#y!!|(PtjN-uK{RZh1{fRdwTp
z=?fPw3=It}uB@KF`_#M*+jrdiwDJ0z+|K(gK&LR41o;IsI6S+N2IO!SctjQhX%8@V
zJDF_<WOR7CIEF}E_U*eV)?~oLdiew&*D0mYiA-HP{`~(w(TrnnzGvDwUIq>q#;_yj
z3T>;K`{Y|Vq&xEjj%G~k6hHR+h!THhnaxw_$SGHMPMfZMPi)i9%XhNulY>gPzF~iB
z|KgKQLWq9XH}ze6r2ic?sZII+S2M0acw&}`vH8UR{h3j<5wpVPAM$;3o^cn?p;iC4
zi2>cDTH+c}l9E`GYL#4+3Zxi}3=9o)4UKdS3`2|ztW1opOiZ;6OsxzI=DpKfg`y!h
mKP5A*5~9Jt608BJ+QiDh45H!G^w2dx4Gf;HelF{r5}E*Jd&X1%
literal 0
HcmV?d00001
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/static/img/group.png
b/web/pgadmin/browser/server_groups/servers/roles/static/img/group.png
new file mode 100644
index
0000000000000000000000000000000000000000..819ff810f49f937fc3fc6d1d85a01a2f4bd81277
GIT binary patch
literal 804
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47|R2ELR^8|RjXDN78WKY#xGqs
z@z#}{H?QnmvT)Luw`V_t(7Cy@d*WiE`Wp(j&uiH+zj;zq;iq@!rcLcQwWjyX+MaXk
zdM<A0nbK15<iX*^3nxWJM5HFhPG~GRysZ85rtSkvnvxO|mM@rj_u`4W7f&r)FefQ7
z@x-dOtDCzIEp6Pte}7U^{E|hJe!RN><JH6OuO5GY^=#RK`BPg8ZfxzE(N<8LmwI$l
z|EgK_QIX-_U%j|<@ltwHOhRnb^5x6d&TIbvVAcQo%L}s;zrT8Y=i-%!sIUpE;ujrB
z$}EbgsHnJeWaj_73s%jj{Qm0gvIUDy{mnS>J8i<M*r=$eU$>Y3zcY9Lin^r4q<Q-i
zkN-+tcRe9AH1y%&?wo>xRXcW1p1&wFH$NjcziZCY*_-yH=N4|hn{@O?@_|o@|8Gs7
zzIf@SmmkhP|9JB0$0LtF?tl1k_x+FEOJWXxOFZ;7;s5oCJ5OKOcK6eQ1DCSP!Vi9l
zpFMl_|7(3w5n-nuzukQM)4FT#A|gY-TX+irgPyS@$S;_|;n|HeAcwQSBeEDsdw{Xq
z$!t3y<B6w>V~E7%<OCo{NlHp_U^=6-Cg+XEp&lKXIWaMJZ1&XrVX{$RR0~sx59kPw
za0&5|S<=?o+wJWX<Q3%>=HoYyb4lx5Zzsn<&q&u$-&kgq1uK{8E?Kd7_453Lj1-<r
zNl960>sX#dM?^_RGygQ(R`!kWl*7KolQ(y-mS%3=-M)AEbgl{S-#&iy`g#6g0f85i
zB5Se|UOo{PdL=IQ?wr6wNfAj=Sz&4M`4eVLnIm*SKyZrG#A)*;&YU_|c>e6^@&^Sb
zMhOIRJQEk4{$#qF!4%gi;UP^wL$8`H6-?!E5L5{`#=v0l(|FM{-7kSa|EiX_MwFx^
zmZVxG7o`Fz1|tJQ16@NST?4}qBLgcFV=EI=Z39y)1A}?*^j4v0$jwj5Osj-wFt7w`
c0ID{zGBAT^I5j<V4NwDvr>mdKI;Vst0Cs_b9RL6T
literal 0
HcmV?d00001
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/static/img/role.png
b/web/pgadmin/browser/server_groups/servers/roles/static/img/role.png
new file mode 100644
index
0000000000000000000000000000000000000000..ef54e88ad5ed7543fe5cbc287ecd997a470026bd
GIT binary patch
literal 557
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf`2e2~S0Fue=FF_Dti5~p)>V~n
zU$^|pjdM?KT-dg5Rb5r(^)rWmyt@D6)x+<v9({lH<ocPDbyZd0Up@Q&>czHo>%PBw
z_4vlE@2}oGzH$5et9RShZM=5oLTyz|bWBWCOiWg8?y@c0w;Vm$K4Ef7dPZ`3Mswff
z75h)kT)RCrJ2xsSYVn56S6+R%`0~TK=O0c#`*{4x$0LtF9(?$5?}Lw%R&0rgif-=e
z*>&&J_Pd|TTYIKVnX+{A_APflO<1)fFE4NZ<(r#se_DL(c0@$PnWyhI-ukrR#)r_*
zP}ZdzDuG^NED7=pW^j0RBMr#mEbxdd2GSm2>~=ES4#=45>Eak7aoP3kOQC}XB5e;<
z?&j)-hPuA}y-&<_!tecRo<4o=?|(iw$CBe@qLaZd|I56WH_7rg%{wjLrFuR1SX;r)
zN2|4yyuz6mHL1pOhaT8gV%y~2r+ug6q4(v_%?4f3r`|5P{PrvR;fp_>pDZzp*9m`p
zwru<KhYM<K=C)qu{!!&!dE(uJ<HnC0_Z@i8|FkiXZ81+RqrZZf+3KyIRsbESTH+c}
zl9E`GYL#4+3Zxi}3=9o)4UKdS3`2|ztW1opOiZ;6OsxzI=DpKfg`y!hKP5A*5~9Jt
e608BJ+QiDh45H!G^w2dx4Gf;HelF{r5}E*2d<@$F
literal 0
HcmV?d00001
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/css/role.css
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/css/role.css
new file mode 100644
index 0000000..7145fec
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/css/role.css
@@ -0,0 +1,27 @@
+.icon-role {
+ background-image: url('{{ url_for('NODE-role.static',
filename='img/role.png')}}') !important;
+ background-repeat: no-repeat;
+ align-content: center;
+ vertical-align: middle;
+ height: 1.3em;
+}
+
+.icon-group {
+ background-image: url('{{ url_for('NODE-role.static',
filename='img/group.png')}}') !important;
+ background-repeat: no-repeat;
+ align-content: center;
+ vertical-align: middle;
+ height: 1.3em;
+}
+
+.pgadmin-node-select option[node="role"] {
+ background-image: url('{{ url_for('NODE-role.static',
filename='img/role.png')}}') !important;
+ background-repeat: no-repeat;
+ background-position: center left;
+}
+
+.pgadmin-node-select option[node="group"] {
+ background-image: url('{{ url_for('NODE-role.static',
filename='img/group.png')}}') !important;
+ background-repeat: no-repeat;
+ background-position: center left;
+}
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/js/role.js
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/js/role.js
new file mode 100644
index 0000000..dc12f0b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/roles/templates/role/js/role.js
@@ -0,0 +1,419 @@
+define(
+ [
+ 'jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser',
+ 'alertify', 'pgadmin.backform', 'select2', 'pgadmin.browser.collection',
+ 'pgadmin.browser.node.ui', 'pgadmin.browser.server.variable'
+ ],
+function($, _, S, pgAdmin, pgBrowser, alertify, Backform) {
+
+ if (!pgBrowser.Nodes['coll-role']) {
+ var role = pgAdmin.Browser.Nodes['coll-role'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'role',
+ type: 'coll-role'
+ });
+ };
+
+ var SecurityModel = Backform.SecurityModel =
pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ provider: null,
+ security_label: null
+ },
+ schema: [{
+ id: 'provider', label: '{{ _('Provider') }}',
+ type: 'text'
+ },{
+ id: 'security_label', label: '{{ _('Security Label') }}',
+ type: 'text'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(data.label) ||
+ _.isNull(data.label) ||
+ String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+ return _("Please specify the value for all the security providers.");
+ }
+ return null;
+ }
+ });
+
+ var switchOptions = {
+ 'onText': 'Yes', 'offText': 'No',
+ 'onColor': 'warning', 'offColor': 'primary',
+ 'size': 'small'
+ };
+
+ var RoleMembersControl = Backform.Control.extend({
+ defaults: _.defaults(
+ {extraClasses: ['col-xs-12 col-sm-12 col-md-12']},
+ Backform.NodeListByNameControl.prototype.defaults
+ ),
+ initialize: function() {
+ Backform.NodeListByNameControl.prototype.initialize.apply(this,
arguments);
+ },
+ formatter: {
+ fromRaw: function (rawData, model) {
+ var res = _.isObject(rawData) ? rawData : JSON.parse(rawData);
+
+ return _.pluck(res, 'role');
+ },
+ toRaw: function (formattedData, model) {
+ return formattedData;
+ }
+ },
+ template: _.template([
+ '<label class="control-label col-xs-12"><%=label%></label>',
+ '<div class="pgadmin-controls col-xs-12">',
+ ' <select multiple="multiple" style="width:100%;"
class="pgadmin-controls <%=extraClasses.join(\' \')%>" name="<%=name%>"
value="<%-JSON.stringify(value)%>" <%=disabled ? "disabled" : ""%> <%=required
? "required" : ""%>>',
+ ' <% for (var i=0; i < options.length; i++) { %>',
+ ' <% var option = options[i]; %>',
+ ' <option value=<%-option.value%> data-icon=<%-option.image%>
<%=value != null && _.indexOf(value, option.value) != -1 ? "selected" : ""%>
<%=option.disabled ? "disabled=\'disabled\'" : ""%>><%-option.label%></option>',
+ ' <% } %>',
+ ' </select>',
+ '</div>',
+ '<div class="note <%=Backform.helpMessageClassName%> col-xs-12"></div>',
+ ].join("\n")),
+ events: {"change select": "onChange"},
+ getValueFromDOM: function() {
+ var res = [];
+
+ this.$el.find("select").find(':selected').each(function() {
+ res.push($(this).attr('value'));
+ });
+
+ return res;
+ },
+ render: function() {
+ Backform.SelectControl.prototype.render.apply(this, arguments);
+ var collection = this.model.get(this.field.get('name')),
+ formatState = function(opt) {
+ if (!opt.id) {
+ return opt.text;
+ }
+
+ var optimage = $(opt.element).data('icon');
+
+ if(!optimage){
+ return opt.text;
+ } else {
+ return $(
+ '<span><span class="wcTabIcon ' + optimage + '"/>' +
opt.text + '</span>'
+ );
+ }
+ },
+ formatSelection = function (opt) {
+
+ if (!opt.id) {
+ return opt.text;
+ }
+
+ var optimage = $(opt.element).data('icon'),
+ grantUpdate = function(ev) {
+
+ _.each(collection.where({role: opt.id}), function(m) {
+ m.set('admin', $(ev.target).is(":checked"));
+ });
+
+ return false;
+ };
+
+ if(!optimage){
+ return opt.text;
+ } else {
+ var j = $('<span><span class="wcTabIcon ' + optimage + '"/>' +
opt.text + ' <span><input type="checkbox"/></span></span>');
+
+ _.each(collection.where({role: opt.id}), function(m) {
+ j.find('input').prop('checked', m.get('admin'));
+ });
+
+ (j.find('input')).on('change', grantUpdate);
+
+ return j;
+ }
+ };
+
+ this.$el.find('select').select2({
+ templateResult: formatState,
+ templateSelection: formatSelection,
+ multiple: true,
+ tags: true,
+ allowClear: true,
+ placeholder: "Select members",
+ width: 'style'
+ }).on("change", function(e) {
+ $(e.target).find(':selected').each(function() {
+ });
+ });
+
+ this.$el.find('div.note').html("<span>NOTE:<br/>In order to give grant
options select the checkbox next to the roles.</span>");
+
+ return this;
+ },
+ onChange: function(e) {
+ var model = this.model,
+ $el = $(e.target),
+ attrArr = this.field.get("name").split('.'),
+ name = attrArr.shift(),
+ path = attrArr.join('.'),
+ vals = this.getValueFromDOM(),
+ collection = model.get(name),
+ removed = [];
+
+ this.stopListening(this.model, "change:" + name, this.render);
+
+ /*
+ * Iterate through all the values, and find out how many are already
+ * present in the collection.
+ */
+ collection.each(function(m) {
+ var role = m.get('role'),
+ idx = _.indexOf(vals, role);
+
+ if (idx > -1) {
+ vals.splice(idx, 1);
+ } else {
+ removed.push(role);
+ }
+ });
+
+ /*
+ * Adding new values
+ */
+ _.each(vals, function(v) {
+ collection.add({role: v});
+ });
+
+ /*
+ * Removing unwanted!
+ */
+ _.each(removed, function(v) {
+ collection.remove(collection.where({role: v}));
+ });
+
+ this.listenTo(this.model, "change:" + name, this.render);
+ }
+ });
+
+ if (!pgBrowser.Nodes['role']) {
+ var role = pgAdmin.Browser.Nodes['role'] = pgAdmin.Browser.Node.extend({
+ parent_type: 'server',
+ type: 'role',
+ label: '{{ _('Login/Group Role') }}',
+ hasSQL: true,
+ canDrop: true,
+ node_label: function(r) {
+ if (!r.can_login) {
+ return r.label + '(GROUP)';
+ }
+ return r.label;
+ },
+ node_image: function(r) {
+ return (r.can_login ? 'icon-role' : 'icon-group');
+ },
+ title: function(d) {
+ if (!d) {
+ return this.label;
+ }
+ if (d.can_login) {
+ return '{{ _('Login Role') }}' + ' - ' + d.label;
+ }
+ return '{{ _('Group Role') }}' + ' - ' + d.label;
+ },
+ Init: function() {
+ /* Avoid mulitple registration of menus */
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ pgBrowser.add_menus([{
+ name: 'create_role_on_server', node: 'server', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Login/Group Role...')
}}',
+ icon: 'wcTabIcon icon-role', data: {action: 'create'},
+ enable: 'can_create_role'
+ },{
+ name: 'create_role_on_roles', node: 'coll-role', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Login/Group Role...')
}}',
+ icon: 'wcTabIcon icon-role', data: {action: 'create'},
+ enable: 'can_create_role'
+ },{
+ name: 'create_role', node: 'role', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Login/Group Role...')
}}',
+ icon: 'wcTabIcon icon-role', data: {action: 'create'},
+ enable: 'can_create_role'
+ }]);
+ },
+ can_create_role: function(node, item) {
+ var treeData = this.getTreeNodeHierarchy(item),
+ server = treeData['server'];
+
+ return server.connected && server.user.can_create_role;
+ },
+ model: pgAdmin.Browser.Node.Model.extend({
+ idAttribute: 'oid',
+ defaults: {
+ oid: null,
+ rolname: undefined,
+ rolcanlogin: false,
+ rolpassword: null,
+ rolconnlimit: -1,
+ rolsuper: false,
+ rolcreaterole: false,
+ rolcreatedb: false,
+ rolinherit: true,
+ rolcatupdate: false,
+ rolreplication: false,
+ rolmembership: [],
+ rolvaliduntil: null,
+ seclabels: [],
+ variables: []
+ },
+ schema: [{
+ id: 'rolname', label: '{{ _('Name') }}', type: 'text',
+ disabled: 'readonly'
+ },{
+ id: 'oid', label:'{{ _('Role ID') }}', cell: 'string',
+ editable: false, type: 'text', visible: true, disabled: true
+ },{
+ id: 'rolpassword', label: '{{ _('Password') }}', type: 'password',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ control: 'input', deps: ['rolcanlogin'], retype: true,
+ cell: 'string', disabled: function(m) {
+ if (!m.isNew()) {
+ var user = this.node_info.server.user;
+
+ return (!(user.is_superuser || user.can_create_role) &&
+ user.id != m.get('oid'));
+ }
+ return false;
+ }
+ },{
+ id: 'rolvaliduntil', disabled: 'readonly', type: 'date',
+ group: '{{ _('Definition') }}', label: '{{ _('Account Expires') }}',
+ cell: 'string', mode: ['edit', 'create'], retype: true,
+ deps: ['rolcanlogin'], options: {}
+ },{
+ id: 'rolconnlimit', type: 'int', group: '{{ _('Definition') }}',
+ label: '{{ _('Connection Limit') }}', cell: 'number',
+ mode: ['properties', 'edit', 'create'], disabled: 'readonly'
+ },{
+ id: 'rolcanlogin', label:'{{ _('Can Login?') }}', type: 'switch',
+ group: '{{ _('Role Privileges') }}', options: switchOptions,
+ disabled: 'readonly'
+ },{
+ id: 'rolsuper', label:'{{ _('Super User') }}', type: 'switch',
+ group: '{{ _('Role Privileges') }}', options: switchOptions,
+ control: Backform.SwitchControl.extend({
+ onChange: function() {
+ Backform.SwitchControl.prototype.onChange.apply(this, arguments);
+
+ this.model.set('rolcatupdate', this.model.get('rolsuper'));
+ this.model.set('rolcreaterole', this.model.get('rolsuper'));
+ this.model.set('rolcreatedb', this.model.get('rolsuper'));
+ }
+ }),
+ disabled: 'readonly'
+ },{
+ id: 'rolcreaterole', label:'{{ _('Can Create Role?') }}',
+ group: '{{ _('Role Privileges') }}', type: 'switch',
+ options: switchOptions, disabled: 'readonly'
+ },{
+ id: 'description', label:'{{ _('Comments') }}', type: 'multiline',
+ group: null, mode: ['properties', 'edit', 'create'],
+ options: switchOptions, disabled: 'readonly'
+ },{
+ id: 'rolcreatedb', label:'{{ _('Can create databases?') }}',
+ group: '{{ _('Role Privileges') }}', type: 'switch',
+ options: switchOptions, disabled: 'readonly'
+ },{
+ id: 'rolcatupdate', label:'{{ _('Can update catalog?') }}',
+ type: 'switch', max_version: 90500, options: switchOptions,
+ group: '{{ _('Role Privileges') }}', disabled: function(m) {
+ return (m.get('read_only') || (!m.get('rolsuper')));
+ }
+ },{
+ id: 'rolinherit', group: '{{ _('Role Privileges') }}',
+ label:'{{ _('Inherit rights from the parent roles?') }}',
+ type: 'switch', options: switchOptions, disabled: 'readonly'
+ },{
+ id: 'rolreplication', group: '{{ _('Role Privileges') }}',
+ label:'{{ _('Can initiate streaming replication and backups?') }}',
+ type: 'switch', min_version: 90100, options: switchOptions,
+ disabled: 'readonly'
+ },{
+ id: 'rolmembership', label: '{{ _('Role Membership') }}',
+ group: '{{ _('Role Privileges') }}', type: 'collection',
+ cell: 'string', mode: ['edit', 'create'], disabled: 'readonly',
+ control: RoleMembersControl, model: pgBrowser.Node.Model.extend({
+ idAttribute: 'role',
+ defaults: {
+ role: undefined,
+ admin: false
+ }
+ })
+ },{
+ id: 'variables', label: '{{ _('Variables') }}', type: 'collection',
+ group: '{{ _('Security') }}', hasDatabase: true, url: 'variables',
+ model: pgBrowser.Node.VariableModel, control: 'variable-collection',
+ mode: [ 'edit', 'create'], canAdd: true, canDelete: true,
+ url: "variables", disabled: 'readonly'
+ },{
+ id: 'seclabels', label: '{{ _('Security Labels') }}',
+ model: SecurityModel, editable: false, type: 'collection',
+ group: '{{ _('Security') }}', mode: ['edit', 'create'],
+ min_version: 90200, disabled: 'readonly', canAdd: true,
+ canEdit: false, canDelete: true, control: 'unique-col-collection'
+ }],
+ readonly: function(m) {
+ if (!m.has('read_only')) {
+ var user = this.node_info.server.user;
+
+ m.set('read_only', !(user.is_superuser || user.can_create_role));
+ }
+
+ return m.get('read_only');
+ },
+ validate: function()
+ {
+ var err = {},
+ errmsg,
+ seclabels = this.get('seclabels');
+
+ if (_.isUndefined(this.get('rolname')) ||
String(this.get('rolname')).replace(/^\s+|\s+$/g, '') == '') {
+ err['name'] = '{{ _('Name can be empty!') }}';
+ errmsg = errmsg || err['name'];
+ }
+
+ if (seclabels) {
+ var secLabelsErr;
+ for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++)
{
+ secLabelsErr =
(seclabels.models[i]).validate.apply(seclabels.models[i]);
+ if (secLabelsErr) {
+ err['seclabels'] = secLabelsErr;
+ errmsg = errmsg || secLabelsErr;
+ }
+ }
+ }
+
+ this.errorModel.clear().set(err);
+
+ if (_.size(err)) {
+ this.trigger('on-status', {msg: errmsg});
+ return errmsg;
+ }
+
+ return null;
+ }
+ })
+ }),
+ sg = pgBrowser.Nodes['server-group'];
+ }
+
+ return pgBrowser.Nodes['role'];
+});
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/create.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/create.sql
new file mode 100644
index 0000000..8ed6e17
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/create.sql
@@ -0,0 +1,49 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+CREATE {% if data.rolcanlogin %}USER{% else %}ROLE{% endif %} {{
conn|qtIdent(data.rolname) }} WITH{% if data.rolcanlogin and data.rolcanlogin
is sameas True %}
+
+ LOGIN{% else %}
+
+ NOLOGIN{% endif %}{% if data.rolsuper %}
+
+ SUPERUSER{% else %}
+
+ NOSUPERUSER{% endif %}{% if data.rolcreatedb %}
+
+ CREATEDB{% else %}
+
+ NOCREATEDB{% endif %}{% if data.rolcreaterole %}
+
+ CREATEROLE{% else %}
+
+ NOCREATEROLE{% endif %}{% if data.rolinherit is sameas true %}
+
+ INHERIT{% else %}
+
+ NOINHERIT{% endif %}{% if data.rolreplication %}
+
+ REPLICATION{% else %}
+
+ NOREPLICATION{% endif %}{% if 'rolconnlimit' in data and
data.rolconnlimit is number and data.rolconnlimit >= -1 %}
+
+ CONNECTION LIMIT {{ data.rolconnlimit }}{% endif %}{% if
data.rolvaliduntil and data.rolvaliduntil is not none %}
+
+ VALID UNTIL {{ data.rolvaliduntil|qtLiteral }} {% endif %}{% if
data.rolpassword %}
+
+ PASSWORD {% if data.rolpassword is none %}NULL{% else %}{% if dummy
%}'xxxxxx'{% else %} {{ data.rolpassword | qtLiteral }}{% endif %}{% endif %}{%
endif %};{% if data.rolsuper and data.rolcatupdate is sameas false %}
+
+
+UPDATE pg_authid SET rolcatupdate=false WHERE rolname = {{
data.rolname|qtLiteral }};{% endif %}{% if data.members and data.members|length
> 0 %}
+
+
+GRANT {{ conn|qtIdent(data.members)|join(', ') }} TO {{
conn|qtIdent(data.rolname) }};{% endif %}{% if data.admins and
data.admins|length > 0 %}
+
+GRANT {{ conn|qtIdent(data.admins)|join(', ') }} TO {{
conn|qtIdent(data.rolname) }} WITH ADMIN OPTION;{% endif %}{% if data.variables
%}
+
+{% for var in data.variables %}
+
+{{ VARIABLE.APPLY(conn, var.database, data.rolname, var.name, var.value) }}
+{% endfor %}{% endif %}{% if data.description %}
+
+COMMENT ON ROLE {{ conn|qtIdent(data.rolname) }} IS {{
data.description|qtLiteral }};
+{% endif %}
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/nodes.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/nodes.sql
new file mode 100644
index 0000000..fcd4e48
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/nodes.sql
@@ -0,0 +1,8 @@
+SELECT
+ r.oid, r.rolname, r.rolcanlogin, r.rolsuper
+FROM
+ {{ role_tbl }} r
+{% if rid %}
+WHERE r.oid = {{ rid }}::int
+{% endif %}
+ORDER BY r.rolcanlogin, r.rolname
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/properties.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/properties.sql
new file mode 100644
index 0000000..f993663
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/properties.sql
@@ -0,0 +1,16 @@
+SELECT
+ r.oid, r.*,
+ pg_catalog.shobj_description(r.oid, 'pg_authid') AS description,
+ ARRAY(
+ SELECT
+ CASE WHEN am.admin_option THEN '1' ELSE '0' END ||
rm.rolname
+ FROM
+ (SELECT * FROM pg_auth_members WHERE member = r.oid) am
+ LEFT JOIN pg_catalog.pg_roles rm ON (rm.oid = am.roleid)
+ ) rolmembership
+FROM
+ {{ role_tbl }} r
+{% if rid %}
+WHERE r.oid = {{ rid }}::int
+{% endif %}
+ORDER BY r.rolcanlogin, r.rolname
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/sql.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/sql.sql
new file mode 100644
index 0000000..6f920b7
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/sql.sql
@@ -0,0 +1,98 @@
+SELECT
+ array_to_string(array_agg(sql), E'\n\n')
+FROM
+(SELECT
+ CASE WHEN rolcanlogin THEN '-- User: ' ELSE '-- Role: ' END ||
+ pg_catalog.quote_ident(rolname) ||
+ E'\n-- DROP ' || CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE
' END ||
+ pg_catalog.quote_ident(rolname) || E'\n\nCREATE ' ||
+ CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE ' END ||
+ pg_catalog.quote_ident(rolname) || E' WITH\n ' ||
+ CASE WHEN rolcanlogin THEN 'LOGIN' ELSE 'NOLOGIN' END || E'\n
' ||
+ CASE WHEN rolcanlogin AND rolpassword LIKE 'md5%%' THEN
'ENCRYPTED PASSWORD ' || quote_literal(rolpassword) || E'\n ' ELSE '' END ||
+ CASE WHEN rolsuper THEN 'SUPERUSER' ELSE 'NOSUPERUSER' END ||
E'\n ' ||
+ CASE WHEN rolinherit THEN 'INHERIT' ELSE 'NOINHERIT' END ||
E'\n ' ||
+ CASE WHEN rolcreatedb THEN 'CREATEDB' ELSE 'NOCREATEDB' END ||
E'\n ' ||
+ CASE WHEN rolcreaterole THEN 'CREATEROLE' ELSE 'NOCREATEROLE'
END || E'\n ' ||
+ CASE WHEN rolconnlimit > 0 THEN E'\n CONNECTION LIMIT ' ||
rolconnlimit ELSE '' END ||
+ CASE WHEN rolvaliduntil IS NOT NULL THEN E'\n VALID UNTIL ' ||
quote_literal(rolvaliduntil::text) ELSE ';' END ||
+ -- PostgreSQL < 9.5
+ CASE WHEN rolsuper AND NOT rolcatupdate THEN E'\n\nUPDATE
pg_authid SET rolcatupdate=false WHERE rolname=' ||
pg_catalog.quote_literal(rolname) || ';' ELSE '' END AS sql
+FROM
+ {{ role_tbl }} r
+WHERE
+ r.oid=%(rid)s::OID
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n')
+FROM
+(SELECT
+ 'GRANT ' || array_to_string(array_agg(rolname), ', ') || ' TO ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) ||
+ CASE WHEN admin_option THEN ' WITH ADMIN OPTION;' ELSE ';' END sql
+FROM
+ (SELECT
+ quote_ident(r.rolname) AS rolname, m.admin_option AS
admin_option
+ FROM
+ pg_auth_members m
+ LEFT JOIN pg_roles r ON (m.roleid = r.oid)
+ WHERE
+ m.member=%(rid)s::OID
+ ORDER BY
+ r.rolname
+ ) a
+GROUP BY admin_option) s)
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n') sql
+FROM
+(SELECT
+ 'ALTER ' || CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE ' END ||
pg_catalog.quote_ident(rolname) || ' SET ' || param || ' TO ' || CASE WHEN
param IN ('search_path', 'temp_tablespaces') THEN value ELSE
quote_literal(value) END || ';' AS sql
+FROM
+(SELECT
+ rolcanlogin, rolname, split_part(rolconfig, '=', 1) AS param,
replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value
+FROM
+ (SELECT
+ unnest(rolconfig) AS rolconfig, rolcanlogin, rolname
+ FROM
+ pg_catalog.pg_roles
+ WHERE
+ oid=%(rid)s::OID
+ ) r
+) a) b)
+-- PostgreSQL >= 9.0
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n') AS sql
+FROM
+ (SELECT
+ 'ALTER ROLE ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) ||
+ ' IN DATABASE ' || pg_catalog.quote_ident(datname) ||
+ ' SET ' || param|| ' TO ' ||
+ CASE
+ WHEN param IN ('search_path', 'temp_tablespaces') THEN value
+ ELSE quote_literal(value)
+ END || ';' AS sql
+ FROM
+ (SELECT
+ datname, split_part(rolconfig, '=', 1) AS param,
replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value
+ FROM
+ (SELECT
+ d.datname, unnest(c.setconfig) AS rolconfig
+ FROM
+ (SELECT *
+ FROM
+ pg_catalog.pg_db_role_setting dr
+ WHERE
+ dr.setrole=%(rid)s::OID AND
dr.setdatabase!=0) c
+ LEFT JOIN pg_catalog.pg_database d ON (d.oid =
c.setdatabase)
+ ) a
+ ) b
+ ) d
+)
+UNION ALL
+(SELECT
+ 'COMMENT ON ROLE ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) || ' IS ' ||
pg_catalog.quote_literal(description) || ';' AS sql
+FROM
+ (SELECT pg_catalog.shobj_description(%(rid)s::OID, 'pg_authid') AS
description) a
+WHERE
+ description IS NOT NULL)) AS a
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/update.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/update.sql
new file mode 100644
index 0000000..316b5d0
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/update.sql
@@ -0,0 +1,110 @@
+{% import 'macros/variable.macros' as VARIABLE %}
+{% if 'rolname' in data %}
+{% set rolname=data.rolname %}
+ALTER{% if rolCanLogin %} USER {% else %} ROLE {% endif %}{{
conn|qtIdent(role) }}
+ RENAME TO {{ conn|qtIdent(rolname) }};
+
+{% else %}
+{% set rolname=role %}
+{% endif %}
+{% if data|hasAny(alterKeys) %}
+ALTER {% if rolCanLogin %}USER{% else %}ROLE{% endif %} {{
conn|qtIdent(rolname) }}{% if 'rolcanlogin' in data %}
+
+{% if data.rolcanlogin %}
+ LOGIN{% else %}
+ NOLOGIN{% endif %}{% endif %}{% if 'rolsuper' in data %}
+
+{% if data.rolsuper %}
+ SUPERUSER{% else %}
+ NOSUPERUSER{% endif %}{% endif %}{% if 'rolcreatedb' in data %}
+
+{% if data.rolcreatedb %}
+ CREATEDB{% else %}
+ NOCREATEDB{% endif %}{% endif %}{% if 'rolcreaterole' in data %}
+
+{% if data.rolcreaterole %}
+ CREATEROLE{% else %}
+ NOCREATEROLE{% endif %}{% endif %}{% if 'rolinherit' in data %}
+
+{% if data.rolinherit %}
+ INHERIT{% else %}
+ NOINHERIT{% endif %}{% endif %}{% if 'rolreplication' in data %}
+
+{% if data.rolreplication %}
+ REPLICATION{% else %}
+ NOREPLICATION{% endif %}{% endif %}{% if 'rolconnlimit' in data and
data.rolconnlimit is number and data.rolconnlimit >= -1 %}
+
+ CONNECTION LIMIT {{ data.rolconnlimit }}
+{% endif %}{% if 'rolvaliduntil' in data and data.rolvaliduntil %}
+
+ VALID UNTIL {% if data.rolvaliduntil is not none %}{{
data.rolvaliduntil|qtLiteral }}{% else %}'infinity'
+{% endif %}{% endif %}{% if 'rolpassword' in data %}
+
+ PASSWORD{% if data.rolpassword is none %} NULL{% else %}{% if dummy %}
'xxxxxx'{% else %} {{ data.rolpassword|qtLiteral }}{% endif %}{% endif %}{%
endif %};{% endif %}
+
+{% if
+ not rolSuper and
+ 'rolsuper' in data and
+ 'rolcatupdate' not in data and
+ data.rolsuper %}
+
+
+UPDATE pg_authid SET rolcatupdate=false WHERE rolname = {{ rolname|qtLiteral
}};
+
+{% elif
+ rolSuper and
+ 'rolsuper' not in data and
+ 'rolcatupdate' in data %}
+{% if data.rolcatupdate %}
+
+
+UPDATE pg_authid SET rolcatupdate=true WHERE rolname = {{ rolname|qtLiteral }};
+
+{% else %}
+
+
+UPDATE pg_authid SET rolcatupdate=false WHERE rolname = {{ rolname|qtLiteral
}};
+
+{% endif %}
+{% endif %}
+{% if 'revoked_admins' in data and
+ data.revoked_admins|length > 0
+%}
+
+-- Revoked the admin options from the members
+REVOKE ADMIN OPTION FOR {{ conn|qtIdent(data.revoked_admins)|join(', ') }}
FROM {{ conn|qtIdent(rolname) }};{% endif %}{% if 'revoked' in data and
data.revoked|length > 0 %}
+
+
+-- Following are no more the members
+REVOKE {{ conn|qtIdent(data.revoked)|join(', ') }} FROM {{
conn|qtIdent(rolname) }};{% endif %}{% if data.admins and data.admins|length >
0 %}
+
+-- Following are the new admins (or, existing members made admins)
+GRANT {{ conn|qtIdent(data.admins)|join(', ') }} TO {{ conn|qtIdent(rolname)
}} WITH ADMIN OPTION;{% endif %}{% if data.members and data.members|length > 0
%}
+
+
+-- Following are the new members
+GRANT {{ conn|qtIdent(data.members)|join(', ') }} TO {{ conn|qtIdent(rolname)
}};{% endif %}
+{% if 'variables' in data and data.variables|length > 0 %}
+{% set variables = data.variables %}
+{% if 'deleted' in variables and variables.deleted|length > 0 %}
+
+{% for var in variables.deleted %}
+{{ VARIABLE.RESET(conn, var.database, rolname, var.name) }}
+{% endfor %}{% endif %}
+{% if 'added' in variables and variables.added|length > 0 %}
+
+{% for var in variables.added %}
+{{ VARIABLE.APPLY(conn, var.database, rolname, var.name, var.value) }}
+{% endfor %}{% endif %}
+{% if 'changed' in variables and variables.changed|length > 0 %}
+
+{% for var in variables.changed %}
+{{ VARIABLE.APPLY(conn, var.database, rolname, var.name, var.value) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% if 'description' in data %}
+
+
+COMMENT ON ROLE {{ conn|qtIdent(rolname) }} IS {{ data.description|qtLiteral
}};
+{% endif %}
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/variables.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/variables.sql
new file mode 100644
index 0000000..b8620c1
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post8_4/variables.sql
@@ -0,0 +1,25 @@
+SELECT
+ split_part(rolconfig, '=', 1) AS name, replace(rolconfig,
split_part(rolconfig, '=', 1) || '=', '') AS value, NULL::text AS database
+FROM
+ (SELECT
+ unnest(rolconfig) AS rolconfig, rolcanlogin, rolname
+ FROM
+ pg_catalog.pg_roles
+ WHERE
+ oid={{ rid|qtLiteral }}::OID
+ ) r
+
+UNION ALL
+SELECT
+ split_part(rolconfig, '=', 1) AS name, replace(rolconfig,
split_part(rolconfig, '=', 1) || '=', '') AS value, datname AS database
+FROM
+ (SELECT
+ d.datname, unnest(c.setconfig) AS rolconfig
+ FROM
+ (SELECT *
+ FROM pg_catalog.pg_db_role_setting dr
+ WHERE
+ dr.setrole={{ rid|qtLiteral }}::OID AND dr.setdatabase!=0
+ ) c
+ LEFT JOIN pg_catalog.pg_database d ON (d.oid = c.setdatabase)
+ ) a;
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/create.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/create.sql
new file mode 100644
index 0000000..0feb5d0
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/create.sql
@@ -0,0 +1,45 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+CREATE {% if data.rolcanlogin %}USER{% else %}ROLE{% endif %} {{
conn|qtIdent(data.rolname) }} WITH{% if data.rolcanlogin and data.rolcanlogin
is sameas True %}
+
+ LOGIN{% else %}
+
+ NOLOGIN{% endif %}{% if data.rolsuper %}
+
+ SUPERUSER{% else %}
+
+ NOSUPERUSER{% endif %}{% if data.rolcreatedb %}
+
+ CREATEDB{% else %}
+
+ NOCREATEDB{% endif %}{% if data.rolcreaterole %}
+
+ CREATEROLE{% else %}
+
+ NOCREATEROLE{% endif %}{% if data.rolinherit is sameas true %}
+
+ INHERIT{% else %}
+
+ NOINHERIT{% endif %}{% if 'rolconnlimit' in data and data.rolconnlimit
is number and data.rolconnlimit >= -1 %}
+
+ CONNECTION LIMIT {{ data.rolconnlimit }}{% endif %}{% if
data.rolvaliduntil and data.rolvaliduntil is not none %}
+
+ VALID UNTIL {{ data.rolvaliduntil|qtLiteral }} {% endif %}{% if
data.rolpassword %}
+
+ PASSWORD {% if data.rolpassword is none %}NULL{% else %}{% if dummy
%}'xxxxxx'{% else %} {{ data.rolpassword | qtLiteral }}{% endif %}{% endif %}{%
endif %};{% if data.rolsuper and data.rolcatupdate is sameas false %}
+
+
+UPDATE pg_authid SET rolcatupdate=false WHERE rolname = {{
data.rolname|qtLiteral }};{% endif %}{% if data.members and data.members|length
> 0 %}
+
+
+GRANT {{ conn|qtIdent(data.members)|join(', ') }} TO {{
conn|qtIdent(data.rolname) }};{% endif %}{% if data.admins and
data.admins|length > 0 %}
+
+GRANT {{ conn|qtIdent(data.admins)|join(', ') }} TO {{
conn|qtIdent(data.rolname) }} WITH ADMIN OPTION;{% endif %}{% if data.variables
%}
+
+{% for var in data.variables %}
+
+{{ VARIABLE.APPLY(conn, var.database, data.rolname, var.name, var.value) }}
+{% endfor %}{% endif %}{% if data.description %}
+
+COMMENT ON ROLE {{ conn|qtIdent(data.rolname) }} IS {{
data.description|qtLiteral }};
+{% endif %}
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/nodes.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/nodes.sql
new file mode 100644
index 0000000..fcd4e48
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/nodes.sql
@@ -0,0 +1,8 @@
+SELECT
+ r.oid, r.rolname, r.rolcanlogin, r.rolsuper
+FROM
+ {{ role_tbl }} r
+{% if rid %}
+WHERE r.oid = {{ rid }}::int
+{% endif %}
+ORDER BY r.rolcanlogin, r.rolname
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/properties.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/properties.sql
new file mode 100644
index 0000000..f993663
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/properties.sql
@@ -0,0 +1,16 @@
+SELECT
+ r.oid, r.*,
+ pg_catalog.shobj_description(r.oid, 'pg_authid') AS description,
+ ARRAY(
+ SELECT
+ CASE WHEN am.admin_option THEN '1' ELSE '0' END ||
rm.rolname
+ FROM
+ (SELECT * FROM pg_auth_members WHERE member = r.oid) am
+ LEFT JOIN pg_catalog.pg_roles rm ON (rm.oid = am.roleid)
+ ) rolmembership
+FROM
+ {{ role_tbl }} r
+{% if rid %}
+WHERE r.oid = {{ rid }}::int
+{% endif %}
+ORDER BY r.rolcanlogin, r.rolname
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/sql.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/sql.sql
new file mode 100644
index 0000000..6f920b7
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/sql.sql
@@ -0,0 +1,98 @@
+SELECT
+ array_to_string(array_agg(sql), E'\n\n')
+FROM
+(SELECT
+ CASE WHEN rolcanlogin THEN '-- User: ' ELSE '-- Role: ' END ||
+ pg_catalog.quote_ident(rolname) ||
+ E'\n-- DROP ' || CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE
' END ||
+ pg_catalog.quote_ident(rolname) || E'\n\nCREATE ' ||
+ CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE ' END ||
+ pg_catalog.quote_ident(rolname) || E' WITH\n ' ||
+ CASE WHEN rolcanlogin THEN 'LOGIN' ELSE 'NOLOGIN' END || E'\n
' ||
+ CASE WHEN rolcanlogin AND rolpassword LIKE 'md5%%' THEN
'ENCRYPTED PASSWORD ' || quote_literal(rolpassword) || E'\n ' ELSE '' END ||
+ CASE WHEN rolsuper THEN 'SUPERUSER' ELSE 'NOSUPERUSER' END ||
E'\n ' ||
+ CASE WHEN rolinherit THEN 'INHERIT' ELSE 'NOINHERIT' END ||
E'\n ' ||
+ CASE WHEN rolcreatedb THEN 'CREATEDB' ELSE 'NOCREATEDB' END ||
E'\n ' ||
+ CASE WHEN rolcreaterole THEN 'CREATEROLE' ELSE 'NOCREATEROLE'
END || E'\n ' ||
+ CASE WHEN rolconnlimit > 0 THEN E'\n CONNECTION LIMIT ' ||
rolconnlimit ELSE '' END ||
+ CASE WHEN rolvaliduntil IS NOT NULL THEN E'\n VALID UNTIL ' ||
quote_literal(rolvaliduntil::text) ELSE ';' END ||
+ -- PostgreSQL < 9.5
+ CASE WHEN rolsuper AND NOT rolcatupdate THEN E'\n\nUPDATE
pg_authid SET rolcatupdate=false WHERE rolname=' ||
pg_catalog.quote_literal(rolname) || ';' ELSE '' END AS sql
+FROM
+ {{ role_tbl }} r
+WHERE
+ r.oid=%(rid)s::OID
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n')
+FROM
+(SELECT
+ 'GRANT ' || array_to_string(array_agg(rolname), ', ') || ' TO ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) ||
+ CASE WHEN admin_option THEN ' WITH ADMIN OPTION;' ELSE ';' END sql
+FROM
+ (SELECT
+ quote_ident(r.rolname) AS rolname, m.admin_option AS
admin_option
+ FROM
+ pg_auth_members m
+ LEFT JOIN pg_roles r ON (m.roleid = r.oid)
+ WHERE
+ m.member=%(rid)s::OID
+ ORDER BY
+ r.rolname
+ ) a
+GROUP BY admin_option) s)
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n') sql
+FROM
+(SELECT
+ 'ALTER ' || CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE ' END ||
pg_catalog.quote_ident(rolname) || ' SET ' || param || ' TO ' || CASE WHEN
param IN ('search_path', 'temp_tablespaces') THEN value ELSE
quote_literal(value) END || ';' AS sql
+FROM
+(SELECT
+ rolcanlogin, rolname, split_part(rolconfig, '=', 1) AS param,
replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value
+FROM
+ (SELECT
+ unnest(rolconfig) AS rolconfig, rolcanlogin, rolname
+ FROM
+ pg_catalog.pg_roles
+ WHERE
+ oid=%(rid)s::OID
+ ) r
+) a) b)
+-- PostgreSQL >= 9.0
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n') AS sql
+FROM
+ (SELECT
+ 'ALTER ROLE ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) ||
+ ' IN DATABASE ' || pg_catalog.quote_ident(datname) ||
+ ' SET ' || param|| ' TO ' ||
+ CASE
+ WHEN param IN ('search_path', 'temp_tablespaces') THEN value
+ ELSE quote_literal(value)
+ END || ';' AS sql
+ FROM
+ (SELECT
+ datname, split_part(rolconfig, '=', 1) AS param,
replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value
+ FROM
+ (SELECT
+ d.datname, unnest(c.setconfig) AS rolconfig
+ FROM
+ (SELECT *
+ FROM
+ pg_catalog.pg_db_role_setting dr
+ WHERE
+ dr.setrole=%(rid)s::OID AND
dr.setdatabase!=0) c
+ LEFT JOIN pg_catalog.pg_database d ON (d.oid =
c.setdatabase)
+ ) a
+ ) b
+ ) d
+)
+UNION ALL
+(SELECT
+ 'COMMENT ON ROLE ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) || ' IS ' ||
pg_catalog.quote_literal(description) || ';' AS sql
+FROM
+ (SELECT pg_catalog.shobj_description(%(rid)s::OID, 'pg_authid') AS
description) a
+WHERE
+ description IS NOT NULL)) AS a
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/update.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/update.sql
new file mode 100644
index 0000000..ba654eb
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/update.sql
@@ -0,0 +1,106 @@
+{% import 'macros/variable.macros' as VARIABLE %}
+{% if 'rolname' in data %}
+{% set rolname=data.rolname %}
+ALTER{% if rolCanLogin %} USER {% else %} ROLE {% endif %}{{
conn|qtIdent(role) }}
+ RENAME TO {{ conn|qtIdent(rolname) }};
+
+{% else %}
+{% set rolname=role %}
+{% endif %}
+{% if data|hasAny(alterKeys) %}
+ALTER {% if rolCanLogin %}USER{% else %}ROLE{% endif %} {{
conn|qtIdent(rolname) }}{% if 'rolcanlogin' in data %}
+
+{% if data.rolcanlogin %}
+ LOGIN{% else %}
+ NOLOGIN{% endif %}{% endif %}{% if 'rolsuper' in data %}
+
+{% if data.rolsuper %}
+ SUPERUSER{% else %}
+ NOSUPERUSER{% endif %}{% endif %}{% if 'rolcreatedb' in data %}
+
+{% if data.rolcreatedb %}
+ CREATEDB{% else %}
+ NOCREATEDB{% endif %}{% endif %}{% if 'rolcreaterole' in data %}
+
+{% if data.rolcreaterole %}
+ CREATEROLE{% else %}
+ NOCREATEROLE{% endif %}{% endif %}{% if 'rolinherit' in data %}
+
+{% if data.rolinherit %}
+ INHERIT{% else %}
+ NOINHERIT{% endif %}{% endif %}{% if 'rolconnlimit' in data and
data.rolconnlimit is number and data.rolconnlimit >= -1 %}
+
+ CONNECTION LIMIT {{ data.rolconnlimit }}
+{% endif %}{% if 'rolvaliduntil' in data and data.rolvaliduntil %}
+
+ VALID UNTIL {% if data.rolvaliduntil is not none %}{{
data.rolvaliduntil|qtLiteral }}{% else %}'infinity'
+{% endif %}{% endif %}{% if 'rolpassword' in data %}
+
+ PASSWORD{% if data.rolpassword is none %} NULL{% else %}{% if dummy %}
'xxxxxx'{% else %} {{ data.rolpassword|qtLiteral }}{% endif %}{% endif %}{%
endif %};{% endif %}
+
+{% if
+ not rolSuper and
+ 'rolsuper' in data and
+ 'rolcatupdate' not in data and
+ data.rolsuper %}
+
+
+UPDATE pg_authid SET rolcatupdate=false WHERE rolname = {{ rolname|qtLiteral
}};
+
+{% elif
+ rolSuper and
+ 'rolsuper' not in data and
+ 'rolcatupdate' in data %}
+{% if data.rolcatupdate %}
+
+
+UPDATE pg_authid SET rolcatupdate=true WHERE rolname = {{ rolname|qtLiteral }};
+
+{% else %}
+
+
+UPDATE pg_authid SET rolcatupdate=false WHERE rolname = {{ rolname|qtLiteral
}};
+
+{% endif %}
+{% endif %}
+{% if 'revoked_admins' in data and
+ data.revoked_admins|length > 0
+%}
+
+-- Revoked the admin options from the members
+REVOKE ADMIN OPTION FOR {{ conn|qtIdent(data.revoked_admins)|join(', ') }}
FROM {{ conn|qtIdent(rolname) }};{% endif %}{% if 'revoked' in data and
data.revoked|length > 0 %}
+
+
+-- Following are no more the members
+REVOKE {{ conn|qtIdent(data.revoked)|join(', ') }} FROM {{
conn|qtIdent(rolname) }};{% endif %}{% if data.admins and data.admins|length >
0 %}
+
+-- Following are the new admins (or, existing members made admins)
+GRANT {{ conn|qtIdent(data.admins)|join(', ') }} TO {{ conn|qtIdent(rolname)
}} WITH ADMIN OPTION;{% endif %}{% if data.members and data.members|length > 0
%}
+
+
+-- Following are the new members
+GRANT {{ conn|qtIdent(data.members)|join(', ') }} TO {{ conn|qtIdent(rolname)
}};{% endif %}
+{% if 'variables' in data and data.variables|length > 0 %}
+{% set variables = data.variables %}
+{% if 'deleted' in variables and variables.deleted|length > 0 %}
+
+{% for var in variables.deleted %}
+{{ VARIABLE.RESET(conn, var.database, rolname, var.name) }}
+{% endfor %}{% endif %}
+{% if 'added' in variables and variables.added|length > 0 %}
+
+{% for var in variables.added %}
+{{ VARIABLE.APPLY(conn, var.database, rolname, var.name, var.value) }}
+{% endfor %}{% endif %}
+{% if 'changed' in variables and variables.changed|length > 0 %}
+
+{% for var in variables.changed %}
+{{ VARIABLE.APPLY(conn, var.database, rolname, var.name, var.value) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% if 'description' in data %}
+
+
+COMMENT ON ROLE {{ conn|qtIdent(rolname) }} IS {{ data.description|qtLiteral
}};
+{% endif %}
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/variables.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/variables.sql
new file mode 100644
index 0000000..b8620c1
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_0/variables.sql
@@ -0,0 +1,25 @@
+SELECT
+ split_part(rolconfig, '=', 1) AS name, replace(rolconfig,
split_part(rolconfig, '=', 1) || '=', '') AS value, NULL::text AS database
+FROM
+ (SELECT
+ unnest(rolconfig) AS rolconfig, rolcanlogin, rolname
+ FROM
+ pg_catalog.pg_roles
+ WHERE
+ oid={{ rid|qtLiteral }}::OID
+ ) r
+
+UNION ALL
+SELECT
+ split_part(rolconfig, '=', 1) AS name, replace(rolconfig,
split_part(rolconfig, '=', 1) || '=', '') AS value, datname AS database
+FROM
+ (SELECT
+ d.datname, unnest(c.setconfig) AS rolconfig
+ FROM
+ (SELECT *
+ FROM pg_catalog.pg_db_role_setting dr
+ WHERE
+ dr.setrole={{ rid|qtLiteral }}::OID AND dr.setdatabase!=0
+ ) c
+ LEFT JOIN pg_catalog.pg_database d ON (d.oid = c.setdatabase)
+ ) a;
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/create.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/create.sql
new file mode 100644
index 0000000..3dc80fb
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/create.sql
@@ -0,0 +1,54 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+CREATE {% if data.rolcanlogin %}USER{% else %}ROLE{% endif %} {{
conn|qtIdent(data.rolname) }} WITH{% if data.rolcanlogin and data.rolcanlogin
is sameas True %}
+
+ LOGIN{% else %}
+
+ NOLOGIN{% endif %}{% if data.rolsuper %}
+
+ SUPERUSER{% else %}
+
+ NOSUPERUSER{% endif %}{% if data.rolcreatedb %}
+
+ CREATEDB{% else %}
+
+ NOCREATEDB{% endif %}{% if data.rolcreaterole %}
+
+ CREATEROLE{% else %}
+
+ NOCREATEROLE{% endif %}{% if data.rolinherit is sameas true %}
+
+ INHERIT{% else %}
+
+ NOINHERIT{% endif %}{% if data.rolreplication %}
+
+ REPLICATION{% else %}
+
+ NOREPLICATION{% endif %}{% if 'rolconnlimit' in data and
data.rolconnlimit is number and data.rolconnlimit >= -1 %}
+
+ CONNECTION LIMIT {{ data.rolconnlimit }}{% endif %}{% if
data.rolvaliduntil and data.rolvaliduntil is not none %}
+
+ VALID UNTIL {{ data.rolvaliduntil|qtLiteral }} {% endif %}{% if
data.rolpassword %}
+
+ PASSWORD {% if data.rolpassword is none %}NULL{% else %}{% if dummy
%}'xxxxxx'{% else %} {{ data.rolpassword | qtLiteral }}{% endif %}{% endif %}{%
endif %};{% if data.rolsuper and data.rolcatupdate is sameas false %}
+
+
+UPDATE pg_authid SET rolcatupdate=false WHERE rolname = {{
data.rolname|qtLiteral }};{% endif %}{% if data.members and data.members|length
> 0 %}
+
+
+GRANT {{ conn|qtIdent(data.members)|join(', ') }} TO {{
conn|qtIdent(data.rolname) }};{% endif %}{% if data.admins and
data.admins|length > 0 %}
+
+GRANT {{ conn|qtIdent(data.admins)|join(', ') }} TO {{
conn|qtIdent(data.rolname) }} WITH ADMIN OPTION;{% endif %}{% if data.seclabels
and data.seclabels|length > 0 %}
+
+{% for r in data.seclabels %}
+
+{{ SECLABLE.APPLY(conn, 'ROLE', data.rolname, r.provider, r.label) }}
+{% endfor %}{% endif %}{% if data.variables %}
+
+{% for var in data.variables %}
+
+{{ VARIABLE.APPLY(conn, var.database, data.rolname, var.name, var.value) }}
+{% endfor %}{% endif %}{% if data.description %}
+
+COMMENT ON ROLE {{ conn|qtIdent(data.rolname) }} IS {{
data.description|qtLiteral }};
+{% endif %}
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/nodes.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/nodes.sql
new file mode 100644
index 0000000..fcd4e48
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/nodes.sql
@@ -0,0 +1,8 @@
+SELECT
+ r.oid, r.rolname, r.rolcanlogin, r.rolsuper
+FROM
+ {{ role_tbl }} r
+{% if rid %}
+WHERE r.oid = {{ rid }}::int
+{% endif %}
+ORDER BY r.rolcanlogin, r.rolname
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/properties.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/properties.sql
new file mode 100644
index 0000000..bb56ec4
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/properties.sql
@@ -0,0 +1,17 @@
+SELECT
+ r.oid, r.*,
+ pg_catalog.shobj_description(r.oid, 'pg_authid') AS description,
+ ARRAY(
+ SELECT
+ CASE WHEN am.admin_option THEN '1' ELSE '0' END ||
rm.rolname
+ FROM
+ (SELECT * FROM pg_auth_members WHERE member = r.oid) am
+ LEFT JOIN pg_catalog.pg_roles rm ON (rm.oid = am.roleid)
+ ) rolmembership,
+ (SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1
WHERE sl1.objoid=r.oid) AS seclabels
+FROM
+ {{ role_tbl }} r
+{% if rid %}
+WHERE r.oid = {{ rid }}::int
+{% endif %}
+ORDER BY r.rolcanlogin, r.rolname
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/sql.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/sql.sql
new file mode 100644
index 0000000..8cc9c26
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/sql.sql
@@ -0,0 +1,119 @@
+SELECT
+ array_to_string(array_agg(sql), E'\n\n')
+FROM
+(SELECT
+ CASE WHEN rolcanlogin THEN '-- User: ' ELSE '-- Role: ' END ||
+ pg_catalog.quote_ident(rolname) ||
+ E'\n-- DROP ' || CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE
' END ||
+ pg_catalog.quote_ident(rolname) || E'\n\nCREATE ' ||
+ CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE ' END ||
+ pg_catalog.quote_ident(rolname) || E' WITH\n ' ||
+ CASE WHEN rolcanlogin THEN 'LOGIN' ELSE 'NOLOGIN' END || E'\n
' ||
+ CASE WHEN rolcanlogin AND rolpassword LIKE 'md5%%' THEN
'ENCRYPTED PASSWORD ' || quote_literal(rolpassword) || E'\n ' ELSE '' END ||
+ CASE WHEN rolsuper THEN 'SUPERUSER' ELSE 'NOSUPERUSER' END ||
E'\n ' ||
+ CASE WHEN rolinherit THEN 'INHERIT' ELSE 'NOINHERIT' END ||
E'\n ' ||
+ CASE WHEN rolcreatedb THEN 'CREATEDB' ELSE 'NOCREATEDB' END ||
E'\n ' ||
+ CASE WHEN rolcreaterole THEN 'CREATEROLE' ELSE 'NOCREATEROLE'
END || E'\n ' ||
+ -- PostgreSQL >= 9.1
+ CASE WHEN rolreplication THEN 'REPLICATION' ELSE
'NOREPLICATION' END ||
+ CASE WHEN rolconnlimit > 0 THEN E'\n CONNECTION LIMIT ' ||
rolconnlimit ELSE '' END ||
+ CASE WHEN rolvaliduntil IS NOT NULL THEN E'\n VALID UNTIL ' ||
quote_literal(rolvaliduntil::text) ELSE ';' END ||
+ -- PostgreSQL < 9.5
+ CASE WHEN rolsuper AND NOT rolcatupdate THEN E'\n\nUPDATE
pg_authid SET rolcatupdate=false WHERE rolname=' ||
pg_catalog.quote_literal(rolname) || ';' ELSE '' END AS sql
+FROM
+ {{ role_tbl }} r
+WHERE
+ r.oid=%(rid)s::OID
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n')
+FROM
+(SELECT
+ 'GRANT ' || array_to_string(array_agg(rolname), ', ') || ' TO ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) ||
+ CASE WHEN admin_option THEN ' WITH ADMIN OPTION;' ELSE ';' END sql
+FROM
+ (SELECT
+ quote_ident(r.rolname) AS rolname, m.admin_option AS
admin_option
+ FROM
+ pg_auth_members m
+ LEFT JOIN pg_roles r ON (m.roleid = r.oid)
+ WHERE
+ m.member=%(rid)s::OID
+ ORDER BY
+ r.rolname
+ ) a
+GROUP BY admin_option) s)
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n') sql
+FROM
+(SELECT
+ 'ALTER ' || CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE ' END ||
pg_catalog.quote_ident(rolname) || ' SET ' || param || ' TO ' || CASE WHEN
param IN ('search_path', 'temp_tablespaces') THEN value ELSE
quote_literal(value) END || ';' AS sql
+FROM
+(SELECT
+ rolcanlogin, rolname, split_part(rolconfig, '=', 1) AS param,
replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value
+FROM
+ (SELECT
+ unnest(rolconfig) AS rolconfig, rolcanlogin, rolname
+ FROM
+ pg_catalog.pg_roles
+ WHERE
+ oid=%(rid)s::OID
+ ) r
+) a) b)
+-- PostgreSQL >= 9.0
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n') AS sql
+FROM
+ (SELECT
+ 'ALTER ROLE ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) ||
+ ' IN DATABASE ' || pg_catalog.quote_ident(datname) ||
+ ' SET ' || param|| ' TO ' ||
+ CASE
+ WHEN param IN ('search_path', 'temp_tablespaces') THEN value
+ ELSE quote_literal(value)
+ END || ';' AS sql
+ FROM
+ (SELECT
+ datname, split_part(rolconfig, '=', 1) AS param,
replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value
+ FROM
+ (SELECT
+ d.datname, unnest(c.setconfig) AS rolconfig
+ FROM
+ (SELECT *
+ FROM
+ pg_catalog.pg_db_role_setting dr
+ WHERE
+ dr.setrole=%(rid)s::OID AND
dr.setdatabase!=0) c
+ LEFT JOIN pg_catalog.pg_database d ON (d.oid =
c.setdatabase)
+ ) a
+ ) b
+ ) d
+)
+UNION ALL
+(SELECT
+ 'COMMENT ON ROLE ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) || ' IS ' ||
pg_catalog.quote_literal(description) || ';' AS sql
+FROM
+ (SELECT pg_catalog.shobj_description(%(rid)s::OID, 'pg_authid') AS
description) a
+WHERE
+ description IS NOT NULL)
+-- PostgreSQL >= 9.2
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n') AS sql
+FROM
+ (SELECT
+ 'SECURITY LABEL FOR ' || provider ||
+ E'\n ON ROLE ' || pg_catalog.quote_ident(rolname) ||
+ E'\n IS ' || pg_catalog.quote_literal(label) || ';' AS sql
+ FROM
+ (SELECT
+ label, provider, rolname
+ FROM
+ (SELECT *
+ FROM
+ pg_shseclabel sl1
+ WHERE sl1.objoid=%(rid)s::OID) s
+ LEFT JOIN pg_catalog.pg_roles r ON (s.objoid=r.oid)) a)
b
+)) AS a
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/update.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/update.sql
new file mode 100644
index 0000000..8ee9bdd
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/update.sql
@@ -0,0 +1,132 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+{% if 'rolname' in data %}
+{% set rolname=data.rolname %}
+ALTER{% if rolCanLogin %} USER {% else %} ROLE {% endif %}{{
conn|qtIdent(role) }}
+ RENAME TO {{ conn|qtIdent(rolname) }};
+
+{% else %}
+{% set rolname=role %}
+{% endif %}
+{% if data|hasAny(alterKeys) %}
+ALTER {% if rolCanLogin %}USER{% else %}ROLE{% endif %} {{
conn|qtIdent(rolname) }}{% if 'rolcanlogin' in data %}
+
+{% if data.rolcanlogin %}
+ LOGIN{% else %}
+ NOLOGIN{% endif %}{% endif %}{% if 'rolsuper' in data %}
+
+{% if data.rolsuper %}
+ SUPERUSER{% else %}
+ NOSUPERUSER{% endif %}{% endif %}{% if 'rolcreatedb' in data %}
+
+{% if data.rolcreatedb %}
+ CREATEDB{% else %}
+ NOCREATEDB{% endif %}{% endif %}{% if 'rolcreaterole' in data %}
+
+{% if data.rolcreaterole %}
+ CREATEROLE{% else %}
+ NOCREATEROLE{% endif %}{% endif %}{% if 'rolinherit' in data %}
+
+{% if data.rolinherit %}
+ INHERIT{% else %}
+ NOINHERIT{% endif %}{% endif %}{% if 'rolreplication' in data %}
+
+{% if data.rolreplication %}
+ REPLICATION{% else %}
+ NOREPLICATION{% endif %}{% endif %}{% if 'rolconnlimit' in data and
data.rolconnlimit is number and data.rolconnlimit >= -1 %}
+
+ CONNECTION LIMIT {{ data.rolconnlimit }}
+{% endif %}{% if 'rolvaliduntil' in data and data.rolvaliduntil %}
+
+ VALID UNTIL {% if data.rolvaliduntil is not none %}{{
data.rolvaliduntil|qtLiteral }}{% else %}'infinity'
+{% endif %}{% endif %}{% if 'rolpassword' in data %}
+
+ PASSWORD{% if data.rolpassword is none %} NULL{% else %}{% if dummy %}
'xxxxxx'{% else %} {{ data.rolpassword|qtLiteral }}{% endif %}{% endif %}{%
endif %};{% endif %}
+
+{% if
+ not rolSuper and
+ 'rolsuper' in data and
+ 'rolcatupdate' not in data and
+ data.rolsuper %}
+
+
+UPDATE pg_authid SET rolcatupdate=false WHERE rolname = {{ rolname|qtLiteral
}};
+
+{% elif
+ rolSuper and
+ 'rolsuper' not in data and
+ 'rolcatupdate' in data %}
+{% if data.rolcatupdate %}
+
+
+UPDATE pg_authid SET rolcatupdate=true WHERE rolname = {{ rolname|qtLiteral }};
+
+{% else %}
+
+
+UPDATE pg_authid SET rolcatupdate=false WHERE rolname = {{ rolname|qtLiteral
}};
+
+{% endif %}
+{% endif %}
+{% if 'revoked_admins' in data and
+ data.revoked_admins|length > 0
+%}
+
+-- Revoked the admin options from the members
+REVOKE ADMIN OPTION FOR {{ conn|qtIdent(data.revoked_admins)|join(', ') }}
FROM {{ conn|qtIdent(rolname) }};{% endif %}{% if 'revoked' in data and
data.revoked|length > 0 %}
+
+
+-- Following are no more the members
+REVOKE {{ conn|qtIdent(data.revoked)|join(', ') }} FROM {{
conn|qtIdent(rolname) }};{% endif %}{% if data.admins and data.admins|length >
0 %}
+
+-- Following are the new admins (or, existing members made admins)
+GRANT {{ conn|qtIdent(data.admins)|join(', ') }} TO {{ conn|qtIdent(rolname)
}} WITH ADMIN OPTION;{% endif %}{% if data.members and data.members|length > 0
%}
+
+
+-- Following are the new members
+GRANT {{ conn|qtIdent(data.members)|join(', ') }} TO {{ conn|qtIdent(rolname)
}};{% endif %}{% if data.seclabels and
+ data.seclabels|length > 0
+%}{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'ROLE', data.rolname, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'ROLE', data.rolname, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'ROLE', data.rolname, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% if 'variables' in data and data.variables|length > 0 %}
+{% set variables = data.variables %}
+{% if 'deleted' in variables and variables.deleted|length > 0 %}
+
+{% for var in variables.deleted %}
+{{ VARIABLE.RESET(conn, var.database, rolname, var.name) }}
+{% endfor %}{% endif %}
+{% if 'added' in variables and variables.added|length > 0 %}
+
+{% for var in variables.added %}
+{{ VARIABLE.APPLY(conn, var.database, rolname, var.name, var.value) }}
+{% endfor %}{% endif %}
+{% if 'changed' in variables and variables.changed|length > 0 %}
+
+{% for var in variables.changed %}
+{{ VARIABLE.APPLY(conn, var.database, rolname, var.name, var.value) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% if 'description' in data %}
+
+
+COMMENT ON ROLE {{ conn|qtIdent(rolname) }} IS {{ data.description|qtLiteral
}};
+{% endif %}
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/variables.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/variables.sql
new file mode 100644
index 0000000..b8620c1
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_1/variables.sql
@@ -0,0 +1,25 @@
+SELECT
+ split_part(rolconfig, '=', 1) AS name, replace(rolconfig,
split_part(rolconfig, '=', 1) || '=', '') AS value, NULL::text AS database
+FROM
+ (SELECT
+ unnest(rolconfig) AS rolconfig, rolcanlogin, rolname
+ FROM
+ pg_catalog.pg_roles
+ WHERE
+ oid={{ rid|qtLiteral }}::OID
+ ) r
+
+UNION ALL
+SELECT
+ split_part(rolconfig, '=', 1) AS name, replace(rolconfig,
split_part(rolconfig, '=', 1) || '=', '') AS value, datname AS database
+FROM
+ (SELECT
+ d.datname, unnest(c.setconfig) AS rolconfig
+ FROM
+ (SELECT *
+ FROM pg_catalog.pg_db_role_setting dr
+ WHERE
+ dr.setrole={{ rid|qtLiteral }}::OID AND dr.setdatabase!=0
+ ) c
+ LEFT JOIN pg_catalog.pg_database d ON (d.oid = c.setdatabase)
+ ) a;
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/create.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/create.sql
new file mode 100644
index 0000000..ebfe6ce
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/create.sql
@@ -0,0 +1,51 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+CREATE {% if data.rolcanlogin %}USER{% else %}ROLE{% endif %} {{
conn|qtIdent(data.rolname) }} WITH{% if data.rolcanlogin and data.rolcanlogin
is sameas True %}
+
+ LOGIN{% else %}
+
+ NOLOGIN{% endif %}{% if data.rolsuper %}
+
+ SUPERUSER{% else %}
+
+ NOSUPERUSER{% endif %}{% if data.rolcreatedb %}
+
+ CREATEDB{% else %}
+
+ NOCREATEDB{% endif %}{% if data.rolcreaterole %}
+
+ CREATEROLE{% else %}
+
+ NOCREATEROLE{% endif %}{% if data.rolinherit is sameas true %}
+
+ INHERIT{% else %}
+
+ NOINHERIT{% endif %}{% if data.rolreplication %}
+
+ REPLICATION{% else %}
+
+ NOREPLICATION{% endif %}{% if 'rolconnlimit' in data and
data.rolconnlimit is number and data.rolconnlimit >= -1 %}
+
+ CONNECTION LIMIT {{ data.rolconnlimit }}{% endif %}{% if
data.rolvaliduntil and data.rolvaliduntil is not none %}
+
+ VALID UNTIL {{ data.rolvaliduntil|qtLiteral }} {% endif %}{% if
data.rolpassword %}
+
+ PASSWORD {% if data.rolpassword is none %}NULL{% else %}{% if dummy
%}'xxxxxx'{% else %} {{ data.rolpassword | qtLiteral }}{% endif %}{% endif %}{%
endif %};{% if data.members and data.members|length > 0 %}
+
+
+GRANT {{ conn|qtIdent(data.members)|join(', ') }} TO {{
conn|qtIdent(data.rolname) }};{% endif %}{% if data.admins and
data.admins|length > 0 %}
+
+GRANT {{ conn|qtIdent(data.admins)|join(', ') }} TO {{
conn|qtIdent(data.rolname) }} WITH ADMIN OPTION;{% endif %}{% if data.seclabels
and data.seclabels|length > 0 %}
+
+{% for r in data.seclabels %}
+
+{{ SECLABLE.APPLY(conn, 'ROLE', data.rolname, r.provider, r.label) }}
+{% endfor %}{% endif %}{% if data.variables %}
+
+{% for var in data.variables %}
+
+{{ VARIABLE.APPLY(conn, var.database, data.rolname, var.name, var.value) }}
+{% endfor %}{% endif %}{% if data.description %}
+
+COMMENT ON ROLE {{ conn|qtIdent(data.rolname) }} IS {{
data.description|qtLiteral }};
+{% endif %}
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/nodes.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/nodes.sql
new file mode 100644
index 0000000..fcd4e48
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/nodes.sql
@@ -0,0 +1,8 @@
+SELECT
+ r.oid, r.rolname, r.rolcanlogin, r.rolsuper
+FROM
+ {{ role_tbl }} r
+{% if rid %}
+WHERE r.oid = {{ rid }}::int
+{% endif %}
+ORDER BY r.rolcanlogin, r.rolname
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/properties.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/properties.sql
new file mode 100644
index 0000000..bb56ec4
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/properties.sql
@@ -0,0 +1,17 @@
+SELECT
+ r.oid, r.*,
+ pg_catalog.shobj_description(r.oid, 'pg_authid') AS description,
+ ARRAY(
+ SELECT
+ CASE WHEN am.admin_option THEN '1' ELSE '0' END ||
rm.rolname
+ FROM
+ (SELECT * FROM pg_auth_members WHERE member = r.oid) am
+ LEFT JOIN pg_catalog.pg_roles rm ON (rm.oid = am.roleid)
+ ) rolmembership,
+ (SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1
WHERE sl1.objoid=r.oid) AS seclabels
+FROM
+ {{ role_tbl }} r
+{% if rid %}
+WHERE r.oid = {{ rid }}::int
+{% endif %}
+ORDER BY r.rolcanlogin, r.rolname
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/sql.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/sql.sql
new file mode 100644
index 0000000..933ec2b
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/sql.sql
@@ -0,0 +1,117 @@
+SELECT
+ array_to_string(array_agg(sql), E'\n\n')
+FROM
+(SELECT
+ CASE WHEN rolcanlogin THEN '-- User: ' ELSE '-- Role: ' END ||
+ pg_catalog.quote_ident(rolname) ||
+ E'\n-- DROP ' || CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE
' END ||
+ pg_catalog.quote_ident(rolname) || E'\n\nCREATE ' ||
+ CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE ' END ||
+ pg_catalog.quote_ident(rolname) || E' WITH\n ' ||
+ CASE WHEN rolcanlogin THEN 'LOGIN' ELSE 'NOLOGIN' END || E'\n
' ||
+ CASE WHEN rolcanlogin AND rolpassword LIKE 'md5%%' THEN
'ENCRYPTED PASSWORD ' || quote_literal(rolpassword) || E'\n ' ELSE '' END ||
+ CASE WHEN rolsuper THEN 'SUPERUSER' ELSE 'NOSUPERUSER' END ||
E'\n ' ||
+ CASE WHEN rolinherit THEN 'INHERIT' ELSE 'NOINHERIT' END ||
E'\n ' ||
+ CASE WHEN rolcreatedb THEN 'CREATEDB' ELSE 'NOCREATEDB' END ||
E'\n ' ||
+ CASE WHEN rolcreaterole THEN 'CREATEROLE' ELSE 'NOCREATEROLE'
END || E'\n ' ||
+ -- PostgreSQL >= 9.1
+ CASE WHEN rolreplication THEN 'REPLICATION' ELSE
'NOREPLICATION' END ||
+ CASE WHEN rolconnlimit > 0 THEN E'\n CONNECTION LIMIT ' ||
rolconnlimit ELSE '' END ||
+ CASE WHEN rolvaliduntil IS NOT NULL THEN E'\n VALID UNTIL ' ||
quote_literal(rolvaliduntil::text) ELSE ';' END AS sql
+FROM
+ {{ role_tbl }} r
+WHERE
+ r.oid=%(rid)s::OID
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n')
+FROM
+(SELECT
+ 'GRANT ' || array_to_string(array_agg(rolname), ', ') || ' TO ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) ||
+ CASE WHEN admin_option THEN ' WITH ADMIN OPTION;' ELSE ';' END sql
+FROM
+ (SELECT
+ quote_ident(r.rolname) AS rolname, m.admin_option AS
admin_option
+ FROM
+ pg_auth_members m
+ LEFT JOIN pg_roles r ON (m.roleid = r.oid)
+ WHERE
+ m.member=%(rid)s::OID
+ ORDER BY
+ r.rolname
+ ) a
+GROUP BY admin_option) s)
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n') sql
+FROM
+(SELECT
+ 'ALTER ' || CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE ' END ||
pg_catalog.quote_ident(rolname) || ' SET ' || param || ' TO ' || CASE WHEN
param IN ('search_path', 'temp_tablespaces') THEN value ELSE
quote_literal(value) END || ';' AS sql
+FROM
+(SELECT
+ rolcanlogin, rolname, split_part(rolconfig, '=', 1) AS param,
replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value
+FROM
+ (SELECT
+ unnest(rolconfig) AS rolconfig, rolcanlogin, rolname
+ FROM
+ pg_catalog.pg_roles
+ WHERE
+ oid=%(rid)s::OID
+ ) r
+) a) b)
+-- PostgreSQL >= 9.0
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n') AS sql
+FROM
+ (SELECT
+ 'ALTER ROLE ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) ||
+ ' IN DATABASE ' || pg_catalog.quote_ident(datname) ||
+ ' SET ' || param|| ' TO ' ||
+ CASE
+ WHEN param IN ('search_path', 'temp_tablespaces') THEN value
+ ELSE quote_literal(value)
+ END || ';' AS sql
+ FROM
+ (SELECT
+ datname, split_part(rolconfig, '=', 1) AS param,
replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value
+ FROM
+ (SELECT
+ d.datname, unnest(c.setconfig) AS rolconfig
+ FROM
+ (SELECT *
+ FROM
+ pg_catalog.pg_db_role_setting dr
+ WHERE
+ dr.setrole=%(rid)s::OID AND
dr.setdatabase!=0) c
+ LEFT JOIN pg_catalog.pg_database d ON (d.oid =
c.setdatabase)
+ ) a
+ ) b
+ ) d
+)
+UNION ALL
+(SELECT
+ 'COMMENT ON ROLE ' ||
pg_catalog.quote_ident(pg_get_userbyid(%(rid)s::OID)) || ' IS ' ||
pg_catalog.quote_literal(description) || ';' AS sql
+FROM
+ (SELECT pg_catalog.shobj_description(%(rid)s::OID, 'pg_authid') AS
description) a
+WHERE
+ description IS NOT NULL)
+-- PostgreSQL >= 9.2
+UNION ALL
+(SELECT
+ array_to_string(array_agg(sql), E'\n') AS sql
+FROM
+ (SELECT
+ 'SECURITY LABEL FOR ' || provider ||
+ E'\n ON ROLE ' || pg_catalog.quote_ident(rolname) ||
+ E'\n IS ' || pg_catalog.quote_literal(label) || ';' AS sql
+ FROM
+ (SELECT
+ label, provider, rolname
+ FROM
+ (SELECT *
+ FROM
+ pg_shseclabel sl1
+ WHERE sl1.objoid=%(rid)s::OID) s
+ LEFT JOIN pg_catalog.pg_roles r ON (s.objoid=r.oid)) a)
b
+)) AS a
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/update.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/update.sql
new file mode 100644
index 0000000..7ad498f
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/update.sql
@@ -0,0 +1,107 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/variable.macros' as VARIABLE %}
+{% if 'rolname' in data %}
+{% set rolname=data.rolname %}
+ALTER{% if rolCanLogin %} USER {% else %} ROLE {% endif %}{{
conn|qtIdent(role) }}
+ RENAME TO {{ conn|qtIdent(rolname) }};
+
+{% else %}
+{% set rolname=role %}
+{% endif %}
+{% if data|hasAny(alterKeys) %}
+ALTER {% if rolCanLogin %}USER{% else %}ROLE{% endif %} {{
conn|qtIdent(rolname) }}{% if 'rolcanlogin' in data %}
+
+{% if data.rolcanlogin %}
+ LOGIN{% else %}
+ NOLOGIN{% endif %}{% endif %}{% if 'rolsuper' in data %}
+
+{% if data.rolsuper %}
+ SUPERUSER{% else %}
+ NOSUPERUSER{% endif %}{% endif %}{% if 'rolcreatedb' in data %}
+
+{% if data.rolcreatedb %}
+ CREATEDB{% else %}
+ NOCREATEDB{% endif %}{% endif %}{% if 'rolcreaterole' in data %}
+
+{% if data.rolcreaterole %}
+ CREATEROLE{% else %}
+ NOCREATEROLE{% endif %}{% endif %}{% if 'rolinherit' in data %}
+
+{% if data.rolinherit %}
+ INHERIT{% else %}
+ NOINHERIT{% endif %}{% endif %}{% if 'rolreplication' in data %}
+
+{% if data.rolreplication %}
+ REPLICATION{% else %}
+ NOREPLICATION{% endif %}{% endif %}{% if 'rolconnlimit' in data and
data.rolconnlimit is number and data.rolconnlimit >= -1 %}
+
+ CONNECTION LIMIT {{ data.rolconnlimit }}
+{% endif %}{% if 'rolvaliduntil' in data and data.rolvaliduntil %}
+
+ VALID UNTIL {% if data.rolvaliduntil is not none %}{{
data.rolvaliduntil|qtLiteral }}{% else %}'infinity'
+{% endif %}{% endif %}{% if 'rolpassword' in data %}
+
+ PASSWORD{% if data.rolpassword is none %} NULL{% else %}{% if dummy %}
'xxxxxx'{% else %} {{ data.rolpassword|qtLiteral }}{% endif %}{% endif %}{%
endif %};{% endif %}
+
+{% if 'revoked_admins' in data and
+ data.revoked_admins|length > 0
+%}
+
+-- Revoked the admin options from the members
+REVOKE ADMIN OPTION FOR {{ conn|qtIdent(data.revoked_admins)|join(', ') }}
FROM {{ conn|qtIdent(rolname) }};{% endif %}{% if 'revoked' in data and
data.revoked|length > 0 %}
+
+
+-- Following are no more the members
+REVOKE {{ conn|qtIdent(data.revoked)|join(', ') }} FROM {{
conn|qtIdent(rolname) }};{% endif %}{% if data.admins and data.admins|length >
0 %}
+
+-- Following are the new admins (or, existing members made admins)
+GRANT {{ conn|qtIdent(data.admins)|join(', ') }} TO {{ conn|qtIdent(rolname)
}} WITH ADMIN OPTION;{% endif %}{% if data.members and data.members|length > 0
%}
+
+
+-- Following are the new members
+GRANT {{ conn|qtIdent(data.members)|join(', ') }} TO {{ conn|qtIdent(rolname)
}};{% endif %}{% if data.seclabels and
+ data.seclabels|length > 0
+%}{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'ROLE', data.rolname, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'ROLE', data.rolname, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'ROLE', data.rolname, r.provider, r.label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% if 'variables' in data and data.variables|length > 0 %}
+{% set variables = data.variables %}
+{% if 'deleted' in variables and variables.deleted|length > 0 %}
+
+{% for var in variables.deleted %}
+{{ VARIABLE.RESET(conn, var.database, rolname, var.name) }}
+{% endfor %}{% endif %}
+{% if 'added' in variables and variables.added|length > 0 %}
+
+{% for var in variables.added %}
+{{ VARIABLE.APPLY(conn, var.database, rolname, var.name, var.value) }}
+{% endfor %}{% endif %}
+{% if 'changed' in variables and variables.changed|length > 0 %}
+
+{% for var in variables.changed %}
+{{ VARIABLE.APPLY(conn, var.database, rolname, var.name, var.value) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% if 'description' in data %}
+
+
+COMMENT ON ROLE {{ conn|qtIdent(rolname) }} IS {{ data.description|qtLiteral
}};
+{% endif %}
diff --git
a/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/variables.sql
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/variables.sql
new file mode 100644
index 0000000..b8620c1
--- /dev/null
+++
b/web/pgadmin/browser/server_groups/servers/roles/templates/role/sql/post9_4/variables.sql
@@ -0,0 +1,25 @@
+SELECT
+ split_part(rolconfig, '=', 1) AS name, replace(rolconfig,
split_part(rolconfig, '=', 1) || '=', '') AS value, NULL::text AS database
+FROM
+ (SELECT
+ unnest(rolconfig) AS rolconfig, rolcanlogin, rolname
+ FROM
+ pg_catalog.pg_roles
+ WHERE
+ oid={{ rid|qtLiteral }}::OID
+ ) r
+
+UNION ALL
+SELECT
+ split_part(rolconfig, '=', 1) AS name, replace(rolconfig,
split_part(rolconfig, '=', 1) || '=', '') AS value, datname AS database
+FROM
+ (SELECT
+ d.datname, unnest(c.setconfig) AS rolconfig
+ FROM
+ (SELECT *
+ FROM pg_catalog.pg_db_role_setting dr
+ WHERE
+ dr.setrole={{ rid|qtLiteral }}::OID AND dr.setdatabase!=0
+ ) c
+ LEFT JOIN pg_catalog.pg_database d ON (d.oid = c.setdatabase)
+ ) a;
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers