Fix View privilege management. Fixes #1531
1. Fixed update privileges for views and materialized views. 2. Apart from this fixed wrong sql for privilege update. 3. Fixed: Error message was not got cleared even after removing entry with error on privilege tab.pull/3/head
parent
5ac2cceac7
commit
713c692ddd
|
@ -21,7 +21,8 @@ from pgadmin.browser.utils import PGChildNodeView
|
||||||
from pgadmin.utils.ajax import make_json_response, internal_server_error, \
|
from pgadmin.utils.ajax import make_json_response, internal_server_error, \
|
||||||
make_response as ajax_response, bad_request
|
make_response as ajax_response, bad_request
|
||||||
from pgadmin.utils.driver import get_driver
|
from pgadmin.utils.driver import get_driver
|
||||||
|
from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db,\
|
||||||
|
parse_priv_to_db
|
||||||
from config import PG_DEFAULT_DRIVER
|
from config import PG_DEFAULT_DRIVER
|
||||||
from pgadmin.utils.ajax import gone
|
from pgadmin.utils.ajax import gone
|
||||||
|
|
||||||
|
@ -230,7 +231,7 @@ class ViewNode(PGChildNodeView, VacuumSettings):
|
||||||
- This function is used to return modified SQL for the selected view
|
- This function is used to return modified SQL for the selected view
|
||||||
node.
|
node.
|
||||||
|
|
||||||
* get_sql(data, scid)
|
* getSQL(data, scid)
|
||||||
- This function will generate sql from model data
|
- This function will generate sql from model data
|
||||||
|
|
||||||
* sql(gid, sid, did, scid):
|
* sql(gid, sid, did, scid):
|
||||||
|
@ -396,33 +397,6 @@ class ViewNode(PGChildNodeView, VacuumSettings):
|
||||||
status=200
|
status=200
|
||||||
)
|
)
|
||||||
|
|
||||||
def parse_views_privileges(self, db_privileges):
|
|
||||||
"""
|
|
||||||
This function forms a separate list of grantable
|
|
||||||
and non grantable privileges.
|
|
||||||
"""
|
|
||||||
acl = {'grantor': db_privileges['grantor'],
|
|
||||||
'grantee': db_privileges['grantee'],
|
|
||||||
'privileges': []
|
|
||||||
}
|
|
||||||
|
|
||||||
privileges = []
|
|
||||||
for idx, priv in enumerate(db_privileges['privileges']):
|
|
||||||
if db_privileges['grantable'][idx]:
|
|
||||||
privileges.append({"privilege_type": priv,
|
|
||||||
"privilege": True,
|
|
||||||
"with_grant": True
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
privileges.append({"privilege_type": priv,
|
|
||||||
"privilege": True,
|
|
||||||
"with_grant": False
|
|
||||||
})
|
|
||||||
|
|
||||||
acl['privileges'] = privileges
|
|
||||||
|
|
||||||
return acl
|
|
||||||
|
|
||||||
@check_precondition
|
@check_precondition
|
||||||
def properties(self, gid, sid, did, scid, vid):
|
def properties(self, gid, sid, did, scid, vid):
|
||||||
"""
|
"""
|
||||||
|
@ -446,11 +420,8 @@ class ViewNode(PGChildNodeView, VacuumSettings):
|
||||||
return internal_server_error(errormsg=res)
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
for row in dataclres['rows']:
|
for row in dataclres['rows']:
|
||||||
priv = self.parse_views_privileges(row)
|
priv = parse_priv_from_db(row)
|
||||||
if row['deftype'] in res['rows'][0]:
|
res['rows'][0].setdefault(row['deftype'], []).append(priv)
|
||||||
res['rows'][0][row['deftype']].append(priv)
|
|
||||||
else:
|
|
||||||
res['rows'][0][row['deftype']] = [priv]
|
|
||||||
|
|
||||||
result = res['rows'][0]
|
result = res['rows'][0]
|
||||||
|
|
||||||
|
@ -678,49 +649,6 @@ class ViewNode(PGChildNodeView, VacuumSettings):
|
||||||
status=200
|
status=200
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def parse_privileges(str_privileges, object_type='VIEW'):
|
|
||||||
"""Parse Privileges."""
|
|
||||||
db_privileges = {
|
|
||||||
'a': 'INSERT',
|
|
||||||
'r': 'SELECT',
|
|
||||||
'w': 'UPDATE',
|
|
||||||
'd': 'DELETE',
|
|
||||||
'D': 'TRUNCATE',
|
|
||||||
'x': 'REFERENCES',
|
|
||||||
't': 'TRIGGER'
|
|
||||||
}
|
|
||||||
privileges = []
|
|
||||||
for priv in str_privileges:
|
|
||||||
priv_with_grant = []
|
|
||||||
priv_without_grant = []
|
|
||||||
for privilege in priv['privileges']:
|
|
||||||
if privilege['with_grant']:
|
|
||||||
priv_with_grant.append(
|
|
||||||
db_privileges[privilege['privilege_type']])
|
|
||||||
elif privilege['privilege']:
|
|
||||||
priv_without_grant.append(
|
|
||||||
db_privileges[privilege['privilege_type']])
|
|
||||||
|
|
||||||
# If we have all acl then just return all
|
|
||||||
if len(priv_with_grant) == len(db_privileges):
|
|
||||||
priv_with_grant = ['ALL']
|
|
||||||
if len(priv_without_grant) == len(db_privileges):
|
|
||||||
priv_without_grant = ['ALL']
|
|
||||||
|
|
||||||
# Server Level validation
|
|
||||||
if 'grantee' in priv:
|
|
||||||
privileges.append(
|
|
||||||
{
|
|
||||||
'grantee': priv['grantee'] if 'grantee' in priv else '',
|
|
||||||
'with_grant': priv_with_grant,
|
|
||||||
'without_grant': priv_without_grant
|
|
||||||
}
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
return privileges
|
|
||||||
|
|
||||||
def getSQL(self, gid, sid, data, vid=None):
|
def getSQL(self, gid, sid, data, vid=None):
|
||||||
"""
|
"""
|
||||||
This function will generate sql from model data
|
This function will generate sql from model data
|
||||||
|
@ -742,17 +670,26 @@ class ViewNode(PGChildNodeView, VacuumSettings):
|
||||||
if 'schema' not in data:
|
if 'schema' not in data:
|
||||||
data['schema'] = res['rows'][0]['schema']
|
data['schema'] = res['rows'][0]['schema']
|
||||||
|
|
||||||
key = 'datacl'
|
acls = []
|
||||||
if key in data and data[key] is not None:
|
try:
|
||||||
if 'added' in data[key]:
|
acls = render_template(
|
||||||
data[key]['added'] = self.parse_privileges(
|
"/".join([self.template_path, 'sql/allowed_privs.json'])
|
||||||
data[key]['added'])
|
)
|
||||||
if 'changed' in data[key]:
|
acls = json.loads(acls, encoding='utf-8')
|
||||||
data[key]['changed'] = self.parse_privileges(
|
except Exception as e:
|
||||||
data[key]['changed'])
|
current_app.logger.exception(e)
|
||||||
if 'deleted' in data[key]:
|
|
||||||
data[key]['deleted'] = self.parse_privileges(
|
# Privileges
|
||||||
data[key]['deleted'])
|
for aclcol in acls:
|
||||||
|
if aclcol in data:
|
||||||
|
allowedacl = acls[aclcol]
|
||||||
|
|
||||||
|
for key in ['added', 'changed', 'deleted']:
|
||||||
|
if key in data[aclcol]:
|
||||||
|
data[aclcol][key] = parse_priv_to_db(
|
||||||
|
data[aclcol][key], allowedacl['acl']
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
SQL = render_template("/".join(
|
SQL = render_template("/".join(
|
||||||
[self.template_path, 'sql/update.sql']), data=data,
|
[self.template_path, 'sql/update.sql']), data=data,
|
||||||
|
@ -777,8 +714,23 @@ class ViewNode(PGChildNodeView, VacuumSettings):
|
||||||
if 'schema' in data and isinstance(data['schema'], int):
|
if 'schema' in data and isinstance(data['schema'], int):
|
||||||
data['schema'] = self._get_schema(data['schema'])
|
data['schema'] = self._get_schema(data['schema'])
|
||||||
|
|
||||||
if 'datacl' in data and data['datacl'] is not None:
|
acls = []
|
||||||
data['datacl'] = self.parse_privileges(data['datacl'])
|
try:
|
||||||
|
acls = render_template(
|
||||||
|
"/".join([self.template_path, 'sql/allowed_privs.json'])
|
||||||
|
)
|
||||||
|
acls = json.loads(acls, encoding='utf-8')
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.exception(e)
|
||||||
|
|
||||||
|
# Privileges
|
||||||
|
for aclcol in acls:
|
||||||
|
if aclcol in data:
|
||||||
|
allowedacl = acls[aclcol]
|
||||||
|
data[aclcol] = parse_priv_to_db(
|
||||||
|
data[aclcol], allowedacl['acl']
|
||||||
|
)
|
||||||
|
|
||||||
SQL = render_template("/".join(
|
SQL = render_template("/".join(
|
||||||
[self.template_path, 'sql/create.sql']), data=data)
|
[self.template_path, 'sql/create.sql']), data=data)
|
||||||
if data['definition']:
|
if data['definition']:
|
||||||
|
@ -1024,15 +976,27 @@ class ViewNode(PGChildNodeView, VacuumSettings):
|
||||||
return internal_server_error(errormsg=res)
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
for row in dataclres['rows']:
|
for row in dataclres['rows']:
|
||||||
priv = self.parse_views_privileges(row)
|
priv = parse_priv_from_db(row)
|
||||||
if row['deftype'] in res['rows'][0]:
|
res['rows'][0].setdefault(row['deftype'], []).append(priv)
|
||||||
res['rows'][0]['datacl'].append(priv)
|
|
||||||
else:
|
|
||||||
res['rows'][0]['datacl'] = [priv]
|
|
||||||
|
|
||||||
result.update(res['rows'][0])
|
result.update(res['rows'][0])
|
||||||
if 'datacl' in result:
|
|
||||||
result['datacl'] = self.parse_privileges(result['datacl'])
|
acls = []
|
||||||
|
try:
|
||||||
|
acls = render_template(
|
||||||
|
"/".join([self.template_path, 'sql/allowed_privs.json'])
|
||||||
|
)
|
||||||
|
acls = json.loads(acls, encoding='utf-8')
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.exception(e)
|
||||||
|
|
||||||
|
# Privileges
|
||||||
|
for aclcol in acls:
|
||||||
|
if aclcol in result:
|
||||||
|
allowedacl = acls[aclcol]
|
||||||
|
result[aclcol] = parse_priv_to_db(
|
||||||
|
result[aclcol], allowedacl['acl']
|
||||||
|
)
|
||||||
|
|
||||||
SQL = render_template("/".join(
|
SQL = render_template("/".join(
|
||||||
[self.template_path, 'sql/create.sql']),
|
[self.template_path, 'sql/create.sql']),
|
||||||
|
@ -1264,7 +1228,7 @@ class MViewNode(ViewNode, VacuumSettings):
|
||||||
* delete(self, gid, sid, scid):
|
* delete(self, gid, sid, scid):
|
||||||
- Raise an error - we can not delete a material view.
|
- Raise an error - we can not delete a material view.
|
||||||
|
|
||||||
* get_sql(data, scid)
|
* getSQL(data, scid)
|
||||||
- This function will generate sql from model data
|
- This function will generate sql from model data
|
||||||
|
|
||||||
* refresh_data(gid, sid, did, scid, vid):
|
* refresh_data(gid, sid, did, scid, vid):
|
||||||
|
@ -1299,18 +1263,9 @@ class MViewNode(ViewNode, VacuumSettings):
|
||||||
'9.3_plus'
|
'9.3_plus'
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_sql(self, gid, sid, data, scid, vid=None):
|
|
||||||
"""
|
|
||||||
This function will generate sql from model data
|
|
||||||
"""
|
|
||||||
if scid is None:
|
|
||||||
return bad_request('Cannot create a View!')
|
|
||||||
|
|
||||||
return super(MViewNode, self).get_sql(gid, sid, data, scid, vid)
|
|
||||||
|
|
||||||
def getSQL(self, gid, sid, data, vid=None):
|
def getSQL(self, gid, sid, data, vid=None):
|
||||||
"""
|
"""
|
||||||
This function will genrate sql from model data
|
This function will generate sql from model data
|
||||||
"""
|
"""
|
||||||
if vid is not None:
|
if vid is not None:
|
||||||
SQL = render_template("/".join(
|
SQL = render_template("/".join(
|
||||||
|
@ -1387,17 +1342,25 @@ class MViewNode(ViewNode, VacuumSettings):
|
||||||
{'name': 'toast.autovacuum_enabled',
|
{'name': 'toast.autovacuum_enabled',
|
||||||
'value': data['toast_autovacuum_enabled']})
|
'value': data['toast_autovacuum_enabled']})
|
||||||
|
|
||||||
key = 'datacl'
|
acls = []
|
||||||
if key in data and data[key] is not None:
|
try:
|
||||||
if 'added' in data[key]:
|
acls = render_template(
|
||||||
data[key]['added'] = self.parse_privileges(
|
"/".join([self.template_path, 'sql/allowed_privs.json'])
|
||||||
data[key]['added'])
|
)
|
||||||
if 'changed' in data[key]:
|
acls = json.loads(acls, encoding='utf-8')
|
||||||
data[key]['changed'] = self.parse_privileges(
|
except Exception as e:
|
||||||
data[key]['changed'])
|
current_app.logger.exception(e)
|
||||||
if 'deleted' in data[key]:
|
|
||||||
data[key]['deleted'] = self.parse_privileges(
|
# Privileges
|
||||||
data[key]['deleted'])
|
for aclcol in acls:
|
||||||
|
if aclcol in data:
|
||||||
|
allowedacl = acls[aclcol]
|
||||||
|
|
||||||
|
for key in ['added', 'changed', 'deleted']:
|
||||||
|
if key in data[aclcol]:
|
||||||
|
data[aclcol][key] = parse_priv_to_db(
|
||||||
|
data[aclcol][key], allowedacl['acl']
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
SQL = render_template("/".join(
|
SQL = render_template("/".join(
|
||||||
|
@ -1459,8 +1422,23 @@ class MViewNode(ViewNode, VacuumSettings):
|
||||||
data['toast_autovacuum'] is True
|
data['toast_autovacuum'] is True
|
||||||
) else [])
|
) else [])
|
||||||
|
|
||||||
if 'datacl' in data and data['datacl'] is not None:
|
acls = []
|
||||||
data['datacl'] = self.parse_privileges(data['datacl'])
|
try:
|
||||||
|
acls = render_template(
|
||||||
|
"/".join([self.template_path, 'sql/allowed_privs.json'])
|
||||||
|
)
|
||||||
|
acls = json.loads(acls, encoding='utf-8')
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.exception(e)
|
||||||
|
|
||||||
|
# Privileges
|
||||||
|
for aclcol in acls:
|
||||||
|
if aclcol in data:
|
||||||
|
allowedacl = acls[aclcol]
|
||||||
|
data[aclcol] = parse_priv_to_db(
|
||||||
|
data[aclcol], allowedacl['acl']
|
||||||
|
)
|
||||||
|
|
||||||
SQL = render_template("/".join(
|
SQL = render_template("/".join(
|
||||||
[self.template_path, 'sql/create.sql']), data=data)
|
[self.template_path, 'sql/create.sql']), data=data)
|
||||||
if data['definition']:
|
if data['definition']:
|
||||||
|
@ -1521,15 +1499,27 @@ class MViewNode(ViewNode, VacuumSettings):
|
||||||
return internal_server_error(errormsg=res)
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
for row in dataclres['rows']:
|
for row in dataclres['rows']:
|
||||||
priv = self.parse_views_privileges(row)
|
priv = parse_priv_from_db(row)
|
||||||
if row['deftype'] in res['rows'][0]:
|
res['rows'][0].setdefault(row['deftype'], []).append(priv)
|
||||||
res['rows'][0]['datacl'].append(priv)
|
|
||||||
else:
|
|
||||||
res['rows'][0]['datacl'] = [priv]
|
|
||||||
|
|
||||||
result.update(res['rows'][0])
|
result.update(res['rows'][0])
|
||||||
if 'datacl' in result:
|
|
||||||
result['datacl'] = self.parse_privileges(result['datacl'])
|
acls = []
|
||||||
|
try:
|
||||||
|
acls = render_template(
|
||||||
|
"/".join([self.template_path, 'sql/allowed_privs.json'])
|
||||||
|
)
|
||||||
|
acls = json.loads(acls, encoding='utf-8')
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.exception(e)
|
||||||
|
|
||||||
|
# Privileges
|
||||||
|
for aclcol in acls:
|
||||||
|
if aclcol in result:
|
||||||
|
allowedacl = acls[aclcol]
|
||||||
|
result[aclcol] = parse_priv_to_db(
|
||||||
|
result[aclcol], allowedacl['acl']
|
||||||
|
)
|
||||||
|
|
||||||
SQL = render_template("/".join(
|
SQL = render_template("/".join(
|
||||||
[self.template_path, 'sql/create.sql']),
|
[self.template_path, 'sql/create.sql']),
|
||||||
|
@ -1605,11 +1595,8 @@ class MViewNode(ViewNode, VacuumSettings):
|
||||||
return internal_server_error(errormsg=res)
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
for row in dataclres['rows']:
|
for row in dataclres['rows']:
|
||||||
priv = self.parse_views_privileges(row)
|
priv = parse_priv_from_db(row)
|
||||||
if row['deftype'] in res['rows'][0]:
|
res['rows'][0].setdefault(row['deftype'], []).append(priv)
|
||||||
res['rows'][0][row['deftype']].append(priv)
|
|
||||||
else:
|
|
||||||
res['rows'][0][row['deftype']] = [priv]
|
|
||||||
|
|
||||||
result = res['rows'][0]
|
result = res['rows'][0]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"datacl": {
|
||||||
|
"type": "MVIEW",
|
||||||
|
"acl": ["a", "r", "w", "d", "D", "x", "t"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"datacl": {
|
||||||
|
"type": "MVIEW",
|
||||||
|
"acl": ["a", "r", "w", "d", "D", "x", "t"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"datacl": {
|
||||||
|
"type": "MVIEW",
|
||||||
|
"acl": ["a", "r", "w", "d", "D", "x", "t"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"datacl": {
|
||||||
|
"type": "VIEW",
|
||||||
|
"acl": ["a", "r", "w", "d", "D", "x", "t"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"datacl": {
|
||||||
|
"type": "VIEW",
|
||||||
|
"acl": ["a", "r", "w", "d", "D", "x", "t"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"datacl": {
|
||||||
|
"type": "VIEW",
|
||||||
|
"acl": ["a", "r", "w", "d", "D", "x", "t"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"datacl": {
|
||||||
|
"type": "VIEW",
|
||||||
|
"acl": ["a", "r", "w", "d", "D", "x", "t"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"datacl": {
|
||||||
|
"type": "VIEW",
|
||||||
|
"acl": ["a", "r", "w", "d", "D", "x", "t"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"datacl": {
|
||||||
|
"type": "VIEW",
|
||||||
|
"acl": ["a", "r", "w", "d", "D", "x", "t"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"datacl": {
|
||||||
|
"type": "VIEW",
|
||||||
|
"acl": ["a", "r", "w", "d", "D", "x", "t"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"datacl": {
|
||||||
|
"type": "VIEW",
|
||||||
|
"acl": ["a", "r", "w", "d", "D", "x", "t"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -249,10 +249,6 @@
|
||||||
|
|
||||||
toJSON: function(session) {
|
toJSON: function(session) {
|
||||||
|
|
||||||
if (session) {
|
|
||||||
return pgNode.Model.prototype.toJSON.apply(this, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
var privileges = [];
|
var privileges = [];
|
||||||
|
|
||||||
if (this.attributes &&
|
if (this.attributes &&
|
||||||
|
|
|
@ -932,6 +932,11 @@ function(_, pgAdmin, $, Backbone) {
|
||||||
if (!this.trackChanges)
|
if (!this.trackChanges)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
/* Once model is removed from collection clear its errorModel as it's no longer relevant
|
||||||
|
* for us. Otherwise it creates problem in 'clearInvalidSessionIfModelValid' function.
|
||||||
|
*/
|
||||||
|
obj.errorModel.clear();
|
||||||
|
|
||||||
var self = this,
|
var self = this,
|
||||||
invalidModels = self.sessAttrs['invalid'],
|
invalidModels = self.sessAttrs['invalid'],
|
||||||
copy = _.clone(obj),
|
copy = _.clone(obj),
|
||||||
|
@ -994,7 +999,7 @@ function(_, pgAdmin, $, Backbone) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Hmm..
|
// Hmm..
|
||||||
// How come - you have been assinged in invalid list.
|
// How come - you have been assigned in invalid list.
|
||||||
// I will make a list of it, and remove it later.
|
// I will make a list of it, and remove it later.
|
||||||
validModels.push(key);
|
validModels.push(key);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue