Fixed an issue where shared server entries not getting deleted from SQLite database if the user gets deleted. Fixes #6143

This commit is contained in:
Nikhil Mohite
2021-02-04 11:31:39 +05:30
committed by Akshay Joshi
parent 6683522491
commit 02c3863e8c
5 changed files with 752 additions and 345 deletions

View File

@@ -26,6 +26,7 @@ Bug fixes
| `Issue #5871 <https://redmine.postgresql.org/issues/5871>`_ - Ensure that username should be visible in the 'Connect to Server' popup when service and user name both specified.
| `Issue #6045 <https://redmine.postgresql.org/issues/6045>`_ - Fixed autocomplete issue where it is not showing any suggestions if the schema name contains escape characters.
| `Issue #6087 <https://redmine.postgresql.org/issues/6087>`_ - Fixed an issue where the dependencies tab showing multiple owners for the objects having shared dependencies.
| `Issue #6143 <https://redmine.postgresql.org/issues/6143>`_ - Fixed an issue where shared server entries not getting deleted from SQLite database if the user gets deleted.
| `Issue #6163 <https://redmine.postgresql.org/issues/6163>`_ - Fixed an issue where Zoom to fit button only works if the diagram is larger than the canvas.
| `Issue #6164 <https://redmine.postgresql.org/issues/6164>`_ - Ensure that the diagram should not vanish entirely if zooming out too far in ERD.
| `Issue #6177 <https://redmine.postgresql.org/issues/6177>`_ - Fixed an issue while downloading ERD images in Safari and Firefox.

View File

@@ -177,6 +177,17 @@ class ServerGroupView(NodeView):
# if server group id is 1 we won't delete it.
sg = groups.first()
shared_servers = Server.query.filter_by(servergroup_id=sg.id,
shared=True).all()
if shared_servers:
return make_json_response(
status=417,
success=0,
errormsg=gettext(
'The specified server group cannot be deleted.'
)
)
if sg.id == gid:
return make_json_response(
status=417,

View File

@@ -100,7 +100,8 @@ export default function newConnectionDialogModel(response, sgid, sid, handler, c
server_name: server_name,
database_name: database_name,
},
schema: [{
schema: [
{
id: 'server',
name: 'server',
label: gettext('Server'),

View File

@@ -28,7 +28,7 @@ from pgadmin.utils.constants import MIMETYPE_APP_JS, INTERNAL,\
SUPPORTED_AUTH_SOURCES, KERBEROS
from pgadmin.utils.validation_utils import validate_email
from pgadmin.model import db, Role, User, UserPreference, Server, \
ServerGroup, Process, Setting
ServerGroup, Process, Setting, roles_users, SharedServer
# set template path for sql scripts
MODULE_NAME = 'user_management'
@@ -78,7 +78,9 @@ class UserManagementModule(PgAdminModule):
'user_management.update_user', 'user_management.delete_user',
'user_management.create_user', 'user_management.users',
'user_management.user', current_app.login_manager.login_view,
'user_management.auth_sources', 'user_management.auth_sources'
'user_management.auth_sources', 'user_management.auth_sources',
'user_management.shared_servers', 'user_management.admin_users',
'user_management.change_owner',
]
@@ -336,6 +338,8 @@ def delete(uid):
abort(404)
try:
server_groups = ServerGroup.query.filter_by(user_id=uid).all()
sg = [server_group.id for server_group in server_groups]
Setting.query.filter_by(user_id=uid).delete()
@@ -346,6 +350,11 @@ def delete(uid):
ServerGroup.query.filter_by(user_id=uid).delete()
Process.query.filter_by(user_id=uid).delete()
# Delete Shared servers for current user.
SharedServer.query.filter_by(user_id=uid).delete()
SharedServer.query.filter(SharedServer.servergroup_id.in_(sg)).delete(
synchronize_session=False)
# Finally delete user
db.session.delete(usr)
@@ -361,6 +370,174 @@ def delete(uid):
return internal_server_error(errormsg=str(e))
@blueprint.route('/change_owner', methods=['POST'], endpoint='change_owner')
@roles_required('Administrator')
def change_owner():
"""
Returns:
"""
data = request.form if request.form else json.loads(
request.data, encoding='utf-8'
)
try:
old_user_servers = Server.query.filter_by(shared=True, user_id=data[
'old_owner']).all()
server_group_ids = [server.servergroup_id for server in
old_user_servers]
server_groups = ServerGroup.query.filter(
ServerGroup.id.in_(server_group_ids)).all()
new_owner_sg = ServerGroup.query.filter_by(
user_id=data['new_owner']).all()
old_owner_sg = ServerGroup.query.filter_by(
user_id=data['old_owner']).all()
sg_data = {sg.name: sg.id for sg in new_owner_sg}
old_sg_data = {sg.id: sg.name for sg in old_owner_sg}
deleted_sg = []
# Change server user.
for server in old_user_servers:
if old_sg_data[server.servergroup_id] in sg_data:
sh_servers = SharedServer.query.filter_by(
servergroup_id=server.servergroup_id).all()
for sh in sh_servers:
sh.servergroup_id = sg_data[
old_sg_data[server.servergroup_id]]
# Update Server user and server group to prevent deleting
# shared server associated with deleting user.
Server.query.filter_by(
servergroup_id=server.servergroup_id, shared=True,
user_id=data['old_owner']
).update(
{
'servergroup_id': sg_data[old_sg_data[
server.servergroup_id]],
'user_id': data['new_owner']
}
)
ServerGroup.query.filter_by(id=server.servergroup_id).delete()
deleted_sg.append(server.servergroup_id)
else:
server.user_id = data['new_owner']
# Change server group user.
for server_group in server_groups:
if server_group.id not in deleted_sg:
server_group.user_id = data['new_owner']
db.session.commit()
# Delete old owner records.
delete(data['old_owner'])
return make_json_response(
success=1,
info=_("Owner changed successfully."),
data={}
)
except Exception as e:
return internal_server_error(
errormsg='Unable to update shared server owner')
@blueprint.route(
'/shared_servers/<int:uid>', methods=['GET'], endpoint='shared_servers'
)
@roles_required('Administrator')
def get_shared_servers(uid):
"""
Args:
uid:
Returns:
"""
usr = User.query.get(uid)
if not usr:
abort(404)
try:
shared_servers_count = 0
admin_role = Role.query.filter_by(name='Administrator')[0]
# Check user has admin role.
for role in usr.roles:
if role.id == admin_role.id:
# get all server created by user.
servers = Server.query.filter_by(user_id=usr.id).all()
for server in servers:
if server.shared:
shared_servers_count += 1
break
if shared_servers_count:
return make_json_response(
success=1,
info=_(
"{0} Shared servers are associated with this user."
"".format(shared_servers_count)
),
data={
'shared_servers': shared_servers_count
}
)
return make_json_response(
success=1,
info=_("No shared servers found"),
data={'shared_servers': 0}
)
except Exception as e:
return internal_server_error(errormsg=str(e))
# @blueprint.route(
# '/admin_users', methods=['GET'], endpoint='admin_users'
# )
@blueprint.route(
'/admin_users/<int:uid>', methods=['GET'], endpoint='admin_users'
)
@roles_required('Administrator')
def admin_users(uid=None):
"""
Args:
uid:
Returns:
"""
admin_role = Role.query.filter_by(name='Administrator')[0]
admin_users = db.session.query(roles_users).filter_by(
role_id=admin_role.id).all()
if uid:
admin_users = [user[0] for user in admin_users if user[0] != uid]
else:
admin_users = [user[0] for user in admin_users]
admin_list = User.query.filter(User.id.in_(admin_users)).all()
user_list = [{'value': admin.id, 'label': admin.username} for admin in
admin_list]
return make_json_response(
success=1,
info=_("No shared servers found"),
data={
'status': 'success',
'msg': 'Admin user list',
'result': {
'data': user_list,
}
}
)
@blueprint.route('/user/<int:uid>', methods=['PUT'], endpoint='update_user')
@roles_required('Administrator')
def update(uid):

View File

@@ -604,7 +604,223 @@ define([
}),
gridSchema = Backform.generateGridColumnsFromModel(
null, UserModel, 'edit'),
deleteUserCell = Backgrid.Extension.DeleteCell.extend({
changeOwnership: function(res, uid) {
let self = this;
let ownershipSelect2Control = Backform.Select2Control.extend({
fetchData: function(){
let self = this;
let url = self.field.get('url');
url = url_for(url, {'uid': uid});
$.ajax({
url: url,
headers: {
'Cache-Control' : 'no-cache',
},
}).done(function (res) {
var transform = self.field.get('transform');
if(res.data.status){
let data = res.data.result.data;
if (transform && _.isFunction(transform)) {
self.field.set('options', transform.bind(self, data));
} else {
self.field.set('options', data);
}
} else {
if (transform && _.isFunction(transform)) {
self.field.set('options', transform.bind(self, []));
} else {
self.field.set('options', []);
}
}
Backform.Select2Control.prototype.render.apply(self, arguments);
}).fail(function(e){
let msg = '';
if(e.status == 404) {
msg = 'Unable to find url.';
} else {
msg = e.responseJSON.errormsg;
}
alertify.error(msg);
});
},
render: function() {
this.fetchData();
return Backform.Select2Control.prototype.render.apply(this, arguments);
},
onChange: function() {
Backform.Select2Control.prototype.onChange.apply(this, arguments);
},
});
let ownershipModel = pgBrowser.DataModel.extend({
schema: [
{
id: 'note_text_ch_owner',
control: Backform.NoteControl,
text: 'Select the user that will take ownership of the shared servers created by <b>' + self.model.get('username') + '</b>. <b>' + res['data'].shared_servers + '</b> shared servers are currently owned by this user.',
group: gettext('General'),
},
{
id: 'user',
name: 'user',
label: gettext('User'),
type: 'text',
editable: true,
select2: {
allowClear: true,
width: '100%',
first_empty: true,
},
control: ownershipSelect2Control,
url: 'user_management.admin_users',
helpMessage: gettext('Note: If no user is selected, the shared servers will be deleted.'),
}],
});
// Change shared server ownership before deleting the admin user
if (!alertify.changeOwnershipDialog) {
alertify.dialog('changeOwnershipDialog', function factory() {
let $container = $('<div class=\'change-ownership\'></div>');
return {
main: function(message) {
this.msg = message;
},
build: function() {
this.elements.content.appendChild($container.get(0));
alertify.pgDialogBuild.apply(this);
},
setup: function(){
return {
buttons: [
{
text: gettext('Cancel'),
key: 27,
className: 'btn btn-secondary fa fa-times pg-alertify-button',
'data-btn-name': 'cancel',
}, {
text: gettext('OK'),
key: 13,
className: 'btn btn-primary fa fa-check pg-alertify-button',
'data-btn-name': 'ok',
},
],
// Set options for dialog
options: {
title: 'Change ownership',
//disable both padding and overflow control.
padding: !1,
overflow: !1,
model: 0,
resizable: true,
maximizable: false,
pinnable: false,
closableByDimmer: false,
modal: false,
autoReset: false,
closable: true,
},
};
},
prepare: function() {
let self = this;
$container.html('');
self.ownershipModel = new ownershipModel();
let fields = pgBackform.generateViewSchema(null, self.ownershipModel, 'create', null, null, true, null);
let view = this.view = new pgBackform.Dialog({
el: '<div></div>',
model: self.ownershipModel,
schema: fields,
});
//Render change ownership dialog.
$container.append(view.render().$el[0]);
},
callback: function(e) {
if(e.button['data-btn-name'] === 'ok') {
e.cancel = true; // Do not close dialog
let ownershipModel = this.ownershipModel.toJSON();
if (ownershipModel.user == '' || ownershipModel.user == undefined) {
alertify.confirm(
gettext('Delete user?'),
gettext('The shared servers owned by <b>'+ self.model.get('username') +'</b> will be deleted. Do you wish to continue?'),
function() {
self.model.destroy({
wait: true,
success: function() {
alertify.success(gettext('User deleted.'));
alertify.changeOwnershipDialog().destroy();
alertify.UserManagement().destroy();
},
error: function() {
alertify.error(
gettext('Error during deleting user.')
);
},
});
alertify.changeOwnershipDialog().destroy();
},
function() {
return true;
}
);
} else {
self.changeOwner(ownershipModel.user, uid);
}
} else {
alertify.changeOwnershipDialog().destroy();
}
},
};
});
}
alertify.changeOwnershipDialog('Change ownership').resizeTo(pgBrowser.stdW.md, pgBrowser.stdH.md);
},
changeOwner: function(user_id, old_user) {
$.ajax({
url: url_for('user_management.change_owner'),
method: 'POST',
data:{'new_owner': user_id, 'old_owner': old_user},
})
.done(function(res) {
alertify.changeOwnershipDialog().destroy();
alertify.UserManagement().destroy();
alertify.success(gettext(res.info));
})
.fail(function() {
alertify.error(gettext('Unable to change owner.'));
});
},
deleteUser: function() {
let self = this;
alertify.confirm(
gettext('Delete user?'),
gettext('Are you sure you wish to delete this user?'),
function() {
self.model.destroy({
wait: true,
success: function() {
alertify.success(gettext('User deleted.'));
},
error: function() {
alertify.error(
gettext('Error during deleting user.')
);
},
});
},
function() {
return true;
}
);
},
deleteRow: function(e) {
var self = this;
e.preventDefault();
@@ -629,26 +845,27 @@ define([
if (self.model.isNew()) {
self.model.destroy();
} else {
alertify.confirm(
gettext('Delete user?'),
gettext('Are you sure you wish to delete this user?'),
function() {
self.model.destroy({
wait: true,
success: function() {
alertify.success(gettext('User deleted.'));
},
error: function() {
alertify.error(
gettext('Error during deleting user.')
);
},
});
},
function() {
return true;
if(self.model.get('role') == 1){
$.ajax({
url: url_for('user_management.shared_servers', {'uid': self.model.get('id'),
}),
method: 'GET',
async: false,
})
.done(function(res) {
if(res['data'].shared_servers > 0) {
self.changeOwnership(res, self.model.get('id'));
} else {
self.deleteUser();
}
);
})
.fail(function() {
self.deleteUser();
});
} else {
self.deleteUser();
}
}
} else {
alertify.alert(