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,352 +100,353 @@ export default function newConnectionDialogModel(response, sgid, sid, handler, c
server_name: server_name,
database_name: database_name,
},
schema: [{
id: 'server',
name: 'server',
label: gettext('Server'),
type: 'text',
editable: true,
disabled: false,
select2: {
allowClear: false,
width: 'style',
templateResult: formatNode,
templateSelection: formatNode,
},
events: {
'focus select': 'clearInvalid',
'keydown :input': 'processTab',
'select2:select': 'onSelect',
'select2:selecting': 'beforeSelect',
'select2:clear': 'onChange',
},
transform: function(data) {
let group_template_options = [];
for (let key in data) {
if (data.hasOwnProperty(key)) {
group_template_options.push({'group': key, 'optval': data[key]});
}
}
return group_template_options;
},
control: Backform.Select2Control.extend({
template: _.template([
'<% if(label == false) {} else {%>',
' <label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
'<% }%>',
'<div class="<%=controlsClassName%>">',
' <select class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>"',
' name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%>',
' <%=required ? "required" : ""%><%= select2.multiple ? " multiple>" : ">" %>',
' <%=select2.first_empty ? " <option></option>" : ""%>',
' <% for (var i=0; i < options.length; i++) {%>',
' <% if (options[i].group) { %>',
' <% var group = options[i].group; %>',
' <% if (options[i].optval) { %> <% var option_length = options[i].optval.length; %>',
' <optgroup label="<%=group%>">',
' <% for (var subindex=0; subindex < option_length; subindex++) {%>',
' <% var option = options[i].optval[subindex]; %>',
' <option ',
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
' <% if (option.connected) { %> data-connected=connected <%}%>',
' value=<%- formatter.fromRaw(option.value) %>',
' <% if (option.selected) {%>selected="selected"<%} else {%>',
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
' <%}%>',
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
' <%}%>',
' </optgroup>',
' <%}%>',
' <%} else {%>',
' <% var option = options[i]; %>',
' <option ',
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
' <% if (option.connected) { %> data-connected=connected <%}%>',
' value=<%- formatter.fromRaw(option.value) %>',
' <% if (option.selected) {%>selected="selected"<%} else {%>',
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
' <%}%>',
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
' <%}%>',
' <%}%>',
' </select>',
' <% if (helpMessage && helpMessage.length) { %>',
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
' <% } %>',
'</div>',
].join('\n')),
beforeSelect: function() {
var selVal = arguments[0].params.args.data.id;
if(this.field.get('connect') && this.$el.find('option[value="'+selVal+'"]').attr('data-connected') !== 'connected') {
this.field.get('connect').apply(this, [selVal, this.changeIcon.bind(this)]);
} else {
$(this.$sel).trigger('change');
setTimeout(function(){ this.onChange.apply(this); }.bind(this), 200);
}
schema: [
{
id: 'server',
name: 'server',
label: gettext('Server'),
type: 'text',
editable: true,
disabled: false,
select2: {
allowClear: false,
width: 'style',
templateResult: formatNode,
templateSelection: formatNode,
},
changeIcon: function(data) {
let span = this.$el.find('.select2-selection .select2-selection__rendered span.wcTabIcon'),
selSpan = this.$el.find('option:selected');
if (span.hasClass('icon-server-not-connected') || span.hasClass('icon-shared-server-not-connected')) {
let icon = (data.icon) ? data.icon : 'icon-pg';
span.removeClass('icon-server-not-connected');
span.addClass(icon);
span.attr('data-connected', 'connected');
selSpan.data().image = icon;
selSpan.attr('data-connected', 'connected');
alertify.connectServer().destroy();
this.onChange.apply(this);
}
else if (span.hasClass('icon-database-not-connected')) {
let icon = (data.icon) ? data.icon : 'pg-icon-database';
span.removeClass('icon-database-not-connected');
span.addClass(icon);
span.attr('data-connected', 'connected');
selSpan.removeClass('icon-database-not-connected');
selSpan.data().image = icon;
selSpan.attr('data-connected', 'connected');
alertify.connectServer().destroy();
this.onChange.apply(this);
}
events: {
'focus select': 'clearInvalid',
'keydown :input': 'processTab',
'select2:select': 'onSelect',
'select2:selecting': 'beforeSelect',
'select2:clear': 'onChange',
},
connect: function(self) {
let local_self = self;
if(alertify.connectServer) {
delete alertify.connectServer;
transform: function(data) {
let group_template_options = [];
for (let key in data) {
if (data.hasOwnProperty(key)) {
group_template_options.push({'group': key, 'optval': data[key]});
}
}
return group_template_options;
},
control: Backform.Select2Control.extend({
template: _.template([
'<% if(label == false) {} else {%>',
' <label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
'<% }%>',
'<div class="<%=controlsClassName%>">',
' <select class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>"',
' name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%>',
' <%=required ? "required" : ""%><%= select2.multiple ? " multiple>" : ">" %>',
' <%=select2.first_empty ? " <option></option>" : ""%>',
' <% for (var i=0; i < options.length; i++) {%>',
' <% if (options[i].group) { %>',
' <% var group = options[i].group; %>',
' <% if (options[i].optval) { %> <% var option_length = options[i].optval.length; %>',
' <optgroup label="<%=group%>">',
' <% for (var subindex=0; subindex < option_length; subindex++) {%>',
' <% var option = options[i].optval[subindex]; %>',
' <option ',
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
' <% if (option.connected) { %> data-connected=connected <%}%>',
' value=<%- formatter.fromRaw(option.value) %>',
' <% if (option.selected) {%>selected="selected"<%} else {%>',
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
' <%}%>',
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
' <%}%>',
' </optgroup>',
' <%}%>',
' <%} else {%>',
' <% var option = options[i]; %>',
' <option ',
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
' <% if (option.connected) { %> data-connected=connected <%}%>',
' value=<%- formatter.fromRaw(option.value) %>',
' <% if (option.selected) {%>selected="selected"<%} else {%>',
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
' <%}%>',
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
' <%}%>',
' <%}%>',
' </select>',
' <% if (helpMessage && helpMessage.length) { %>',
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
' <% } %>',
'</div>',
].join('\n')),
beforeSelect: function() {
var selVal = arguments[0].params.args.data.id;
alertify.dialog('connectServer', function factory() {
return {
main: function(
title, message, server_id, submit_password=true, connect_server=null,
) {
this.set('title', title);
this.message = message;
this.server_id = server_id;
this.submit_password = submit_password;
this.connect_server = connect_server;
},
setup:function() {
return {
buttons:[{
text: gettext('Cancel'), className: 'btn btn-secondary fa fa-times pg-alertify-button',
key: 27,
},{
text: gettext('OK'), key: 13, className: 'btn btn-primary fa fa-check pg-alertify-button',
}],
focus: {element: '#password', select: true},
options: {
modal: 0, resizable: false, maximizable: false, pinnable: false,
},
};
},
build:function() {
},
prepare:function() {
this.setContent(this.message);
},
callback: function(closeEvent) {
var loadingDiv = $('#show_filter_progress');
if (closeEvent.button.text == gettext('OK')) {
if(this.submit_password) {
var _url = url_for('sqleditor.connect_server', {'sid': this.server_id});
if(this.field.get('connect') && this.$el.find('option[value="'+selVal+'"]').attr('data-connected') !== 'connected') {
this.field.get('connect').apply(this, [selVal, this.changeIcon.bind(this)]);
} else {
$(this.$sel).trigger('change');
setTimeout(function(){ this.onChange.apply(this); }.bind(this), 200);
}
},
changeIcon: function(data) {
let span = this.$el.find('.select2-selection .select2-selection__rendered span.wcTabIcon'),
selSpan = this.$el.find('option:selected');
loadingDiv.removeClass('d-none');
$.ajax({
type: 'POST',
timeout: 30000,
url: _url,
data: $('#frmPassword').serialize(),
})
.done(function(res) {
if (span.hasClass('icon-server-not-connected') || span.hasClass('icon-shared-server-not-connected')) {
let icon = (data.icon) ? data.icon : 'icon-pg';
span.removeClass('icon-server-not-connected');
span.addClass(icon);
span.attr('data-connected', 'connected');
local_self.model.attributes.database = null;
local_self.changeIcon(res.data);
local_self.model.attributes.user = null;
local_self.model.attributes.role = null;
Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
Object.keys(response.server_list).forEach(key => {
response.server_list[key].forEach(option => {
if (option.value == local_self.getValueFromDOM()) {
response.server_name = option.label;
}
});
});
selSpan.data().image = icon;
selSpan.attr('data-connected', 'connected');
alertify.connectServer().destroy();
this.onChange.apply(this);
}
else if (span.hasClass('icon-database-not-connected')) {
let icon = (data.icon) ? data.icon : 'pg-icon-database';
loadingDiv.addClass('d-none');
alertify.connectServer().destroy();
span.removeClass('icon-database-not-connected');
span.addClass(icon);
span.attr('data-connected', 'connected');
selSpan.removeClass('icon-database-not-connected');
selSpan.data().image = icon;
selSpan.attr('data-connected', 'connected');
alertify.connectServer().destroy();
this.onChange.apply(this);
}
},
connect: function(self) {
let local_self = self;
if(alertify.connectServer) {
delete alertify.connectServer;
}
alertify.dialog('connectServer', function factory() {
return {
main: function(
title, message, server_id, submit_password=true, connect_server=null,
) {
this.set('title', title);
this.message = message;
this.server_id = server_id;
this.submit_password = submit_password;
this.connect_server = connect_server;
},
setup:function() {
return {
buttons:[{
text: gettext('Cancel'), className: 'btn btn-secondary fa fa-times pg-alertify-button',
key: 27,
},{
text: gettext('OK'), key: 13, className: 'btn btn-primary fa fa-check pg-alertify-button',
}],
focus: {element: '#password', select: true},
options: {
modal: 0, resizable: false, maximizable: false, pinnable: false,
},
};
},
build:function() {
},
prepare:function() {
this.setContent(this.message);
},
callback: function(closeEvent) {
var loadingDiv = $('#show_filter_progress');
if (closeEvent.button.text == gettext('OK')) {
if(this.submit_password) {
var _url = url_for('sqleditor.connect_server', {'sid': this.server_id});
loadingDiv.removeClass('d-none');
$.ajax({
type: 'POST',
timeout: 30000,
url: _url,
data: $('#frmPassword').serialize(),
})
.fail(function(xhr) {
loadingDiv.addClass('d-none');
alertify.connectServer().destroy();
alertify.connectServer('Connect to server', xhr.responseJSON.result, local_self.getValueFromDOM());
});
} else {
if(Object.keys(this.connect_server).length > 0) {
this.connect_server['password'] = $('#password').val();
this.connect_server['is_selected'] = false;
handler.gridView.on_change_connection(this.connect_server, conn_self, false);
loadingDiv = $('#fetching_data');
loadingDiv.addClass('d-none');
.done(function(res) {
local_self.model.attributes.database = null;
local_self.changeIcon(res.data);
local_self.model.attributes.user = null;
local_self.model.attributes.role = null;
Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
Object.keys(response.server_list).forEach(key => {
response.server_list[key].forEach(option => {
if (option.value == local_self.getValueFromDOM()) {
response.server_name = option.label;
}
});
});
loadingDiv.addClass('d-none');
alertify.connectServer().destroy();
})
.fail(function(xhr) {
loadingDiv.addClass('d-none');
alertify.connectServer().destroy();
alertify.connectServer('Connect to server', xhr.responseJSON.result, local_self.getValueFromDOM());
});
} else {
response.password = $('#password').val();
loadingDiv.addClass('d-none');
if(Object.keys(this.connect_server).length > 0) {
this.connect_server['password'] = $('#password').val();
this.connect_server['is_selected'] = false;
handler.gridView.on_change_connection(this.connect_server, conn_self, false);
loadingDiv = $('#fetching_data');
loadingDiv.addClass('d-none');
} else {
response.password = $('#password').val();
loadingDiv.addClass('d-none');
}
}
} else {
local_self.model.attributes.database = null;
local_self.model.attributes.user = null;
local_self.model.attributes.role = null;
Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
loadingDiv.addClass('d-none');
alertify.connectServer().destroy();
}
} else {
local_self.model.attributes.database = null;
local_self.model.attributes.user = null;
local_self.model.attributes.role = null;
Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
loadingDiv.addClass('d-none');
alertify.connectServer().destroy();
}
closeEvent.close = true;
},
};
});
},
render: function() {
let self = this;
self.connect(self);
Object.keys(response.server_list).forEach(key => {
response.server_list[key].forEach(option => {
if (option.value == parseInt(sid)) {
response.server_name = option.label;
}
closeEvent.close = true;
},
};
});
});
var transform = self.field.get('transform') || self.defaults.transform;
if (transform && _.isFunction(transform)) {
self.field.set('options', transform.bind(self, response.server_list));
} else {
self.field.set('options', response.server_list);
}
return Backform.Select2Control.prototype.render.apply(self, arguments);
},
onChange: function() {
this.model.attributes.database = null;
this.model.attributes.user = null;
let self = this;
self.connect(self);
let url = url_for('sqleditor.connect_server', {
'sid': self.getValueFromDOM(),
'usr': self.model.attributes.user,
});
var loadingDiv = $('#show_filter_progress');
loadingDiv.removeClass('d-none');
$.ajax({
url: url,
type: 'POST',
headers: {
'Cache-Control' : 'no-cache',
},
}).done(function () {
Backform.Select2Control.prototype.onChange.apply(self, arguments);
},
render: function() {
let self = this;
self.connect(self);
Object.keys(response.server_list).forEach(key => {
response.server_list[key].forEach(option => {
if (option.value == self.getValueFromDOM()) {
if (option.value == parseInt(sid)) {
response.server_name = option.label;
}
});
});
loadingDiv.addClass('d-none');
}).fail(function(xhr){
loadingDiv.addClass('d-none');
alertify.connectServer('Connect to server', xhr.responseJSON.result, self.getValueFromDOM());
});
var transform = self.field.get('transform') || self.defaults.transform;
if (transform && _.isFunction(transform)) {
self.field.set('options', transform.bind(self, response.server_list));
} else {
self.field.set('options', response.server_list);
}
return Backform.Select2Control.prototype.render.apply(self, arguments);
},
onChange: function() {
this.model.attributes.database = null;
this.model.attributes.user = null;
let self = this;
self.connect(self);
let url = url_for('sqleditor.connect_server', {
'sid': self.getValueFromDOM(),
'usr': self.model.attributes.user,
});
var loadingDiv = $('#show_filter_progress');
loadingDiv.removeClass('d-none');
$.ajax({
url: url,
type: 'POST',
headers: {
'Cache-Control' : 'no-cache',
},
}).done(function () {
Backform.Select2Control.prototype.onChange.apply(self, arguments);
Object.keys(response.server_list).forEach(key => {
response.server_list[key].forEach(option => {
if (option.value == self.getValueFromDOM()) {
response.server_name = option.label;
}
});
});
loadingDiv.addClass('d-none');
}).fail(function(xhr){
loadingDiv.addClass('d-none');
alertify.connectServer('Connect to server', xhr.responseJSON.result, self.getValueFromDOM());
});
},
}),
},
{
id: 'database',
name: 'database',
label: gettext('Database'),
type: 'text',
editable: true,
disabled: function(m) {
let self_local = this;
if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
&& m.get('server') !== '') {
setTimeout(function() {
if(self_local.options.length) {
m.set('database', self_local.options[0].value);
}
}, 10);
return false;
}
return true;
},
}),
},
{
id: 'database',
name: 'database',
label: gettext('Database'),
type: 'text',
editable: true,
disabled: function(m) {
let self_local = this;
if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
deps: ['server'],
url: 'sqleditor.get_new_connection_database',
select2: {
allowClear: false,
width: '100%',
first_empty: true,
select_first: false,
},
extraClasses:['new-connection-dialog-style'],
control: NewConnectionSelect2Control,
transform: function(data) {
response.database_list = data;
return data;
},
},
{
id: 'user',
name: 'user',
label: gettext('User'),
type: 'text',
editable: true,
deps: ['server'],
select2: {
allowClear: false,
width: '100%',
},
control: NewConnectionSelect2Control,
url: 'sqleditor.get_new_connection_user',
disabled: function(m) {
let self_local = this;
if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
&& m.get('server') !== '') {
setTimeout(function() {
if(self_local.options.length) {
m.set('database', self_local.options[0].value);
}
}, 10);
return false;
}
return true;
setTimeout(function() {
if(self_local.options.length) {
m.set('user', self_local.options[0].value);
}
}, 10);
return false;
}
return true;
},
},{
id: 'role',
name: 'role',
label: gettext('Role'),
type: 'text',
editable: true,
deps: ['server'],
select2: {
allowClear: false,
width: '100%',
first_empty: true,
},
control: NewConnectionSelect2Control,
url: 'sqleditor.get_new_connection_role',
disabled: false,
},
deps: ['server'],
url: 'sqleditor.get_new_connection_database',
select2: {
allowClear: false,
width: '100%',
first_empty: true,
select_first: false,
},
extraClasses:['new-connection-dialog-style'],
control: NewConnectionSelect2Control,
transform: function(data) {
response.database_list = data;
return data;
},
},
{
id: 'user',
name: 'user',
label: gettext('User'),
type: 'text',
editable: true,
deps: ['server'],
select2: {
allowClear: false,
width: '100%',
},
control: NewConnectionSelect2Control,
url: 'sqleditor.get_new_connection_user',
disabled: function(m) {
let self_local = this;
if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
&& m.get('server') !== '') {
setTimeout(function() {
if(self_local.options.length) {
m.set('user', self_local.options[0].value);
}
}, 10);
return false;
}
return true;
},
},{
id: 'role',
name: 'role',
label: gettext('Role'),
type: 'text',
editable: true,
deps: ['server'],
select2: {
allowClear: false,
width: '100%',
first_empty: true,
},
control: NewConnectionSelect2Control,
url: 'sqleditor.get_new_connection_role',
disabled: false,
},
],
validate: function() {
let msg = null;

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.')
);
},
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();
});
},
function() {
return true;
}
);
} else {
self.deleteUser();
}
}
} else {
alertify.alert(