Adding the Login/Group Role Node.

Also - includes:
* Added 'parent_id' in the 'generate_browser_node' function of the Nodes
  to allow them to move around the parents objects (specially through
  update/save functionality).

* Handles the issue related to adding the older object back to the
  collection (DataModel).

Author: Ashesh Vashi
Reviewed by: Neel Patel, and Akshay Joshi.
pull/3/head
Ashesh Vashi 2016-02-05 09:23:58 +05:30
parent 3ff77e9866
commit 91c0388a47
39 changed files with 2797 additions and 19 deletions

View File

@ -201,7 +201,7 @@ class BrowserPluginModule(PgAdminModule):
return scripts
def generate_browser_node(
self, node_id, label, icon, inode, node_type, **kwargs
self, node_id, parent_id, label, icon, inode, node_type, **kwargs
):
obj = {
"id": "%s/%s" % (node_type, node_id),
@ -210,6 +210,7 @@ class BrowserPluginModule(PgAdminModule):
"inode": inode,
"_type": node_type,
"_id": node_id,
"_pid": parent_id,
"module": 'pgadmin.node.%s' % node_type
}
for key in kwargs:

View File

@ -53,7 +53,7 @@ class CollectionNodeModule(PgAdminModule, PGChildModule):
return scripts
def generate_browser_node(
self, node_id, label, icon, **kwargs
self, node_id, parent_id, label, icon, **kwargs
):
obj = {
"id": "%s/%s" % (self.node_type, node_id),
@ -62,20 +62,22 @@ class CollectionNodeModule(PgAdminModule, PGChildModule):
"inode": self.node_inode,
"_type": self.node_type,
"_id": node_id,
"_pid": parent_id,
"module": 'pgadmin.node.%s' % self.node_type
}
for key in kwargs:
obj.setdefault(key, kwargs[key])
return obj
def generate_browser_collection_node(self, sid, **kwargs):
def generate_browser_collection_node(self, parent_id, **kwargs):
obj = {
"id": "coll-%s/%d" % (self.node_type, sid),
"id": "coll-%s/%d" % (self.node_type, parent_id),
"label": self.collection_label,
"icon": self.collection_icon,
"inode": True,
"_type": 'coll-%s' % (self.node_type),
"_id": sid,
"_id": parent_id,
"_pid": parent_id,
"module": 'pgadmin.node.%s' % self.node_type
}

View File

@ -32,7 +32,7 @@ class ServerGroupModule(BrowserPluginModule):
groups = ServerGroup.query.filter_by(user_id=current_user.id)
for group in groups:
yield self.generate_browser_node(
"%d" % (group.id),
"%d" % (group.id), None,
group.name,
"icon-%s" % self.node_type,
True,
@ -184,7 +184,7 @@ class ServerGroupView(NodeView):
return jsonify(
node=self.blueprint.generate_browser_node(
"%d" % (sg.id),
"%d" % (sg.id), None,
sg.name,
"icon-%s" % self.node_type,
True,
@ -241,7 +241,7 @@ class ServerGroupView(NodeView):
for group in groups:
nodes.append(
self.generate_browser_node(
"%d" % (group.id),
"%d" % (group.id), None,
group.name,
"icon-%s" % self.node_type,
True,

View File

@ -74,6 +74,7 @@ class ServerModule(sg.ServerGroupPluginModule):
yield self.generate_browser_node(
"%d" % (server.id),
gid,
server.name,
"icon-server-not-connected" if not connected else
"icon-{0}".format(manager.server_type),
@ -196,6 +197,7 @@ class ServerNode(PGChildNodeView):
res.append(
self.blueprint.generate_browser_node(
"%d" % (server.id),
gid,
server.name,
"icon-server-not-connected" if not connected else
"icon-{0}".format(manager.server_type),
@ -236,6 +238,7 @@ class ServerNode(PGChildNodeView):
return make_json_response(
result=self.blueprint.generate_browser_node(
"%d" % (server.id),
gid,
server.name,
"icon-server-not-connected" if not connected else
"icon-{0}".format(manager.server_type),
@ -490,7 +493,7 @@ class ServerNode(PGChildNodeView):
return jsonify(
node=self.blueprint.generate_browser_node(
"%d" % (server.id),
"%d" % (server.id), gid,
server.name,
"icon-server-not-connected",
True,

View File

@ -0,0 +1,908 @@
##########################################################################
#
# 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 node_inode(self):
"""
Override this property to make the node as leaf node.
"""
return False
@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:
seclabels:{
'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'provider' 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'provider' not in s):
return precondition_required(msg)
if u'changed' in seclabels:
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'provider' 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_dict(
render_template(
self.sql_path + 'permission.sql',
rid=kwargs['rid'],
conn=self.conn
)
)
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['rolname']
self.rolCanLogin = row['rolcanlogin']
self.rolCatUpdate = row['rolcatupdate']
self.rolSuper = row['rolsuper']
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'], sid,
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'], sid,
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']
row['rolpassword'] = ''
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')
@validate_request
def create(self, gid, sid):
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, sid,
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, sid,
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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

View File

@ -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;
}

View File

@ -0,0 +1,512 @@
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',
columns: [
'rolname', 'rolvaliduntil', 'rolconnlimit', 'rolcanlogin',
'rolsuper', 'rolcreaterole', 'rolcreatedb', 'rolcatupdate',
'rolinherit', 'rolreplication'
]
});
};
var SecurityModel = pgAdmin.Browser.SecurityModel =
pgAdmin.Browser.SecurityModel || pgAdmin.Browser.Node.Model.extend({
defaults: {
provider: null,
label: null
},
schema: [{
id: 'provider', label: '{{ _('Provider') }}',
type: 'text', disabled: false
},{
id: 'label', label: '{{ _('Security Label') }}',
type: 'text', disabled: false
}],
validate: function() {
var err = {},
errmsg = null,
data = this.toJSON();
if (_.isUndefined(data.provider) ||
_.isNull(data.provider) ||
String(data.provider).replace(/^\s+|\s+$/g, '') == '') {
var msg = '{{ _('Please specify the value for all the security providers.') }}';
this.errorModel.set('provider', msg);
return msg;
} else {
this.errorModel.unset('provider');
}
if (_.isUndefined(data.label) ||
_.isNull(data.label) ||
String(data.label).replace(/^\s+|\s+$/g, '') == '') {
var msg = '{{ _('Please specify the value for all the security providers.') }}' ;
this.errorModel.set('label', msg);
return msg;
} else {
this.errorModel.unset('label');
}
return null;
}
});
var switchOptions = {
'onText': 'Yes', 'offText': 'No',
'onColor': 'warning', 'offColor': 'primary',
'size': 'mini'
};
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")),
selectionTemplate: _.template([
'<span>',
' <span class="wcTabIcon <%= optimage %>"></span>',
' <span><%= opttext %><span>',
' <% if (checkbox) { %>',
' <input type="checkbox" <%=disabled ? "disabled=\'disabled\'" : ""%>/>',
' <% } %>',
'</span>'
].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() {
var field = _.defaults(this.field.toJSON(), this.defaults),
attributes = this.model.toJSON(),
attrArr = field.name.split('.'),
name = attrArr.shift(),
path = attrArr.join('.'),
rawValue = this.keyPathAccessor(attributes[name], path),
data = _.extend(field, {
rawValue: rawValue,
value: this.formatter.fromRaw(rawValue, this.model),
attributes: attributes,
formatter: this.formatter
}),
evalF = function(f, d, m) {
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
};
// Evaluate the disabled, visible, and required option
_.extend(data, {
disabled: evalF(data.disabled, data, this.model),
visible: evalF(data.visible, data, this.model),
required: evalF(data.required, data, this.model)
});
// Evaluation the options
if (_.isFunction(data.options)) {
try {
data.options = data.options.apply(this)
} catch(e) {
// Do nothing
data.options = []
this.model.trigger('pgadmin-view:transform:error', m, self.field, e);
}
}
// Clean up first
this.$el.removeClass(Backform.hiddenClassname);
if (!data.visible)
this.$el.addClass(Backform.hiddenClassname);
this.$el.html(this.template(data)).addClass(field.name);
this.updateInvalid();
var self = this,
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 {
var d = _.extend(
{}, data, {
'opttext': opt.text,
'optimage': optimage,
'checkbox': false
});
return $(self.selectionTemplate(d));
}
},
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 d = _.extend(
{}, data, {
'opttext': opt.text,
'optimage': optimage,
'checkbox': true
}),
j = $(self.selectionTemplate(d));
// Update the checkbox lazy
setTimeout(
function() {
_.each(collection.where({role: opt.id}), function(m) {
j.find('input').prop('checked', m.get('admin'));
});
}, 200);
(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() {
});
});
var msg = '{{ _('Selection of checkbox represents the permission to give grant to other roles.') }}';
this.$el.find('div.note').html("<span>NOTE:<br/>" + msg + "</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: ['properties', '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:'{{ _('Superuser') }}', 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:'{{ _('Create roles?') }}',
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:'{{ _('Create databases?') }}',
group: '{{ _('Role Privileges') }}', type: 'switch',
options: switchOptions, disabled: 'readonly'
},{
id: 'rolcatupdate', label:'{{ _('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', disabled: 'readonly',
mode: ['properties', 'edit', 'create'],
control: RoleMembersControl, model: pgBrowser.Node.Model.extend({
keys: ['role'],
defaults: {
role: undefined,
admin: false
}
}),
filter: function(d) {
return this.model.isNew() || (this.model.get('rolname') != d.label);
}
},{
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 not 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'];
});

View File

@ -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 %}

View File

@ -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

View File

@ -0,0 +1,5 @@
SELECT
rolname, rolcanlogin, rolcatupdate, rolsuper
FROM
pg_roles
WHERE oid = {{ rid }}::OID

View File

@ -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

View File

@ -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

View File

@ -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 %}

View File

@ -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;

View File

@ -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 %}

View File

@ -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

View File

@ -0,0 +1,5 @@
SELECT
rolname, rolcanlogin, rolcatupdate, rolsuper
FROM
pg_roles
WHERE oid = {{ rid }}::OID

View File

@ -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

View File

@ -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

View File

@ -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 %}

View File

@ -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;

View File

@ -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 %}

View File

@ -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

View File

@ -0,0 +1,5 @@
SELECT
rolname, rolcanlogin, rolcatupdate, rolsuper
FROM
pg_roles
WHERE oid = {{ rid }}::OID

View File

@ -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

View File

@ -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

View File

@ -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 %}

View File

@ -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;

View File

@ -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 %}

View File

@ -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

View File

@ -0,0 +1,5 @@
SELECT
rolname, rolcanlogin, rolsuper AS rolcatupdate, rolsuper
FROM
pg_roles
WHERE oid = {{ rid }}::OID

View File

@ -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

View File

@ -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

View File

@ -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 %}

View File

@ -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;

View File

@ -218,7 +218,12 @@ function(_, pgAdmin, $, Backbone) {
set: function(key, val, options) {
var opts = _.isObject(key) ? val : options;
this._changing = true;
this._previousAttributes = _.clone(this.attributes);
this.changed = {};
var res = Backbone.Model.prototype.set.call(this, key, val, options);
this._changing = false;
if ((opts&& opts.intenal) || !this.trackChanges) {
return true;
@ -233,7 +238,7 @@ function(_, pgAdmin, $, Backbone) {
return;
}
attrs[k] = v;
if (self.origSessAttrs[k] == v) {
if (_.isEqual(self.origSessAttrs[k], v)) {
delete self.sessAttrs[k];
} else {
self.sessAttrs[k] = v;
@ -248,7 +253,9 @@ function(_, pgAdmin, $, Backbone) {
}
self.trigger('pgadmin-session:set', self, attrs);
if (!options || !options.silent) {
self.trigger('change', self, options);
}
if ('validate' in self && typeof(self['validate']) === 'function') {
var msg = self.validate(_.keys(attrs));
@ -770,17 +777,15 @@ function(_, pgAdmin, $, Backbone) {
isAlreadyInvalid = (_.size(self.sessAttrs['invalid']) != 0),
idx = self.objFindInSession(obj, 'deleted');
if ('validate' in obj && typeof(obj.validate) === 'function') {
msg = obj.validate();
if (msg) {
self.sessAttrs['invalid'].push(obj);
}
}
// Hmm.. - it was originally deleted from this collection, we should
// remove it from the 'deleted' list.
if (idx >= 0) {
var origObj = self.sessAttrs['deleted'][idx];
obj.origSessAttrs = _.clone(origObj.origSessAttrs);
obj.attributes = _.extend(obj.attributes, origObj.attributes);
obj.sessAttrs = _.clone(origObj.sessAttrs);
self.sessAttrs['deleted'].splice(idx, 1);
// It has been changed originally!
@ -790,6 +795,14 @@ function(_, pgAdmin, $, Backbone) {
(self.handler || self).trigger('pgadmin-session:added', self, obj);
if ('validate' in obj && typeof(obj.validate) === 'function') {
msg = obj.validate();
if (msg) {
self.sessAttrs['invalid'].push(obj);
}
}
/*
* If the collection was already invalid, we don't need to inform the
* parent, or raise the event for the invalid status.
@ -804,6 +817,13 @@ function(_, pgAdmin, $, Backbone) {
return true;
}
if ('validate' in obj && typeof(obj.validate) === 'function') {
msg = obj.validate();
if (msg) {
self.sessAttrs['invalid'].push(obj);
}
}
self.sessAttrs['added'].push(obj);
/*
@ -840,6 +860,7 @@ function(_, pgAdmin, $, Backbone) {
}
idx = self.objFindInSession(obj, 'added');
// Hmm - it was newly added, we can safely remove it.
if (idx >= 0) {
self.sessAttrs['added'].splice(idx, 1);