public inbox for [email protected]
help / color / mirror / Atom feedFrom: Nikhil Mohite <[email protected]>
To: pgadmin-hackers <[email protected]>
Subject: Re: [pgAdmin][RM-6143]: Shared server entries not getting deleted.
Date: Wed, 3 Feb 2021 14:21:40 +0530
Message-ID: <CAOBg0AMA7tYj7zgOO0A4NVCPedq9ef9bWrg819ACFoTDgkhu-Q@mail.gmail.com> (raw)
In-Reply-To: <CA+OCxoygL=vjY73=sRd9c_aKcqfTTFw4OUJsGtD=0qJAZCTpXg@mail.gmail.com>
References: <CAOBg0APfo=oZvAotP9dpDf8_CveOnP9EpwsV=zn1uYPWgqPZ-Q@mail.gmail.com>
<CANxoLDfTspipwkMcJnNrnOWvguOMeHqNestMJ1dBqLGoowTeqQ@mail.gmail.com>
<CA+OCxowLS7AuXSmrfZ+PkOiVAiXU0RFPiJo8qz1b97yXG48mvA@mail.gmail.com>
<CANxoLDfN2AehHnBPDLPO1vR+yw-16T8dcRx04--UX-69UmCzqQ@mail.gmail.com>
<CAOBg0AM3ycNtnMwJ2Rk1Ty-qCW4dCsq11Zz6Eaai3bRWDb3LMg@mail.gmail.com>
<CA+OCxozsMp+p62THjyi=UX9j-4ZmsDz75OLYQPsPFWzQ6cEXQA@mail.gmail.com>
<CAOBg0ANns7UBMCXkuYQ-Rzsz4rtgQTCLOPke=ZMb77hkRq2fLg@mail.gmail.com>
<CA+OCxoygL=vjY73=sRd9c_aKcqfTTFw4OUJsGtD=0qJAZCTpXg@mail.gmail.com>
Hi Team,
Please find the updated patch for RM-6143.
Regards,
Nikhil Mohite.
On Mon, Feb 1, 2021 at 2:52 PM Dave Page <[email protected]> wrote:
> Hi
>
> On Mon, Feb 1, 2021 at 7:51 AM Nikhil Mohite <
> [email protected]> wrote:
>
>> Hi Dave/ Team,
>>
>> As per the suggestions I have created a sample UI for change ownership of
>> shared servers before deleting the user(only for admin user).
>> [image: shared_server_change_ownsership.png]
>> The user list will contain all admin users. (This alert will get display
>> if the admin user has created any shared servers.)
>>
>
> The UI is fine, but the wording needs work. A couple of general hints: An
> entire sense would never be in parentheses, and there should always be a
> space following a full stop.
>
> "Select the user that will take ownership of the shared servers created by
> <user name>. <num servers> shared servers are currently owned by this user."
>
> "Note: If no user is selected, the shared servers will be deleted."
>
> I'd also suggest that if the user does not select a new owner, a message
> box should ask for confirmation before continuing:
>
> "The shared servers owned by <user name> will be deleted. Do you wish to
> continue?".
>
>
>>
>> Any suggestions or if I missed anything please let me know.
>>
>> Regards,
>> Nikhil Mohite
>>
>> On Thu, Jan 21, 2021 at 4:07 PM Dave Page <[email protected]> wrote:
>>
>>> Hi
>>>
>>> On Thu, Jan 21, 2021 at 10:33 AM Nikhil Mohite <
>>> [email protected]> wrote:
>>>
>>>> Hi Dave,
>>>>
>>>> On Thu, Jan 21, 2021 at 3:24 PM Akshay Joshi <
>>>> [email protected]> wrote:
>>>>
>>>>> Reverted the commit.
>>>>>
>>>>> On Thu, Jan 21, 2021 at 3:13 PM Dave Page <[email protected]> wrote:
>>>>>
>>>>>> This seems like a very bad idea. What if the user that has left was
>>>>>> the user that setup 50 connections used by everyone else?
>>>>>>
>>>>>> Deleting those shared entries is (I would guess) most likely *not*
>>>>>> what the majority of users would want, and the current behaviour is
>>>>>> definitely safest.
>>>>>>
>>>>> In the current implementation when the admin user gets deleted all "*Server
>>>> groups*" created by that user are getting deleted, so if that admin
>>>> has created any *Shared server* other users are not able to access it
>>>> as its server group is not present in the database.
>>>>
>>>
>>> That seems bad. I would suggest we only delete the group if there are no
>>> shared servers left in it that would become orphaned. Of course, in that
>>> case we'll also need to reassign ownership of the group.
>>>
>>>
>>>>
>>>>>> We should make this optional; i.e. ask the use if they want shared
>>>>>> servers created by the user to be deleted. If they say no, they should be
>>>>>> reassigned to another user; either the admin that's deleting the user, or
>>>>>> their choice of user (a little more complex of course, but more flexible).
>>>>>>
>>>>> In the shared server table, we are creating entries per user, for
>>>> deletion of non-admin user we can delete the shared server tables entries
>>>> as it will not affect any other users. (because only admin users can mark
>>>> the server as shared.)
>>>> In case of admin user deletion, will add an extra check as suggested.
>>>>
>>>
>>> Sounds good - thanks!
>>>
>>>
>>>>
>>>>>>
>>>>>
>>>>>> Please revert this, until the deletion is made optional.
>>>>>>
>>>>>> On Thu, Jan 21, 2021 at 9:23 AM Akshay Joshi <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Thanks, patch applied.
>>>>>>>
>>>>>>> On Thu, Jan 21, 2021 at 12:18 PM Nikhil Mohite <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Team,
>>>>>>>>
>>>>>>>> Please find the attached patch for RM-6143
>>>>>>>> <https://redmine.postgresql.org/issues/6143;: Shared server
>>>>>>>> entries not getting deleted.
>>>>>>>> Added code to delete shared server entries if the admin deletes the
>>>>>>>> user from user management.
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> *Thanks & Regards,*
>>>>>>>> *Nikhil Mohite*
>>>>>>>> *Software Engineer.*
>>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> *Thanks & Regards*
>>>>>>> *Akshay Joshi*
>>>>>>> *pgAdmin Hacker | Principal Software Architect*
>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>
>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>>> Dave Page
>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>> Twitter: @pgsnake
>>>>>>
>>>>>> EDB: http://www.enterprisedb.com
>>>>>>
>>>>>>
>>>>>
>>>>> --
>>>>> *Thanks & Regards*
>>>>> *Akshay Joshi*
>>>>> *pgAdmin Hacker | Principal Software Architect*
>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>
>>>>> *Mobile: +91 976-788-8246*
>>>>>
>>>>
>>>> Regards,
>>>> Nikhil Mohite.
>>>>
>>>
>>>
>>> --
>>> Dave Page
>>> Blog: http://pgsnake.blogspot.com
>>> Twitter: @pgsnake
>>>
>>> EDB: http://www.enterprisedb.com
>>>
>>>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EDB: http://www.enterprisedb.com
>
>
Attachments:
[image/png] shared_server_change_ownsership.png (90.0K, 3-shared_server_change_ownsership.png)
download | view image
[application/octet-stream] RM_6143_v2.patch (47.9K, 4-RM_6143_v2.patch)
download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/__init__.py b/web/pgadmin/browser/server_groups/__init__.py
index 476a4e66..56177263 100644
--- a/web/pgadmin/browser/server_groups/__init__.py
+++ b/web/pgadmin/browser/server_groups/__init__.py
@@ -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,
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
index de1d0f21..4f282da8 100644
--- a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
@@ -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,
+ },
+ 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;
},
- changeIcon: function(data) {
- let span = this.$el.find('.select2-selection .select2-selection__rendered span.wcTabIcon'),
- selSpan = this.$el.find('option:selected');
+ 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 (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');
+ 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');
- 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';
+ 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');
- span.removeClass('icon-database-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';
- 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;
+ span.removeClass('icon-database-not-connected');
+ span.addClass(icon);
+ span.attr('data-connected', 'connected');
- if(alertify.connectServer) {
- delete alertify.connectServer;
- }
+ 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;
- 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(alertify.connectServer) {
+ delete alertify.connectServer;
+ }
- loadingDiv.removeClass('d-none');
- $.ajax({
- type: 'POST',
- timeout: 30000,
- url: _url,
- data: $('#frmPassword').serialize(),
- })
- .done(function(res) {
+ 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});
- 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.removeClass('d-none');
+ $.ajax({
+ type: 'POST',
+ timeout: 30000,
+ url: _url,
+ data: $('#frmPassword').serialize(),
+ })
+ .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 {
+ 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');
- alertify.connectServer().destroy();
- })
- .fail(function(xhr) {
+ } else {
+ response.password = $('#password').val();
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');
- } 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());
- });
-
- },
- }),
- },
- {
- 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);
+ 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);
}
- }, 10);
- return false;
- }
+ 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);
- return true;
- },
- 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%',
+ 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());
+ });
+
+ },
+ }),
},
- control: NewConnectionSelect2Control,
- url: 'sqleditor.get_new_connection_user',
- disabled: function(m) {
- let self_local = this;
- if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
+ {
+ 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('user', self_local.options[0].value);
- }
- }, 10);
- return false;
- }
- return true;
+ setTimeout(function() {
+ if(self_local.options.length) {
+ m.set('database', self_local.options[0].value);
+ }
+ }, 10);
+ return false;
+ }
+
+ return true;
+ },
+ 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: 'role',
- name: 'role',
- label: gettext('Role'),
- type: 'text',
- editable: true,
- deps: ['server'],
- select2: {
- allowClear: false,
- width: '100%',
- first_empty: true,
+ {
+ 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,
},
- control: NewConnectionSelect2Control,
- url: 'sqleditor.get_new_connection_role',
- disabled: false,
- },
],
validate: function() {
let msg = null;
diff --git a/web/pgadmin/tools/user_management/__init__.py b/web/pgadmin/tools/user_management/__init__.py
index ce280a3d..f202f898 100644
--- a/web/pgadmin/tools/user_management/__init__.py
+++ b/web/pgadmin/tools/user_management/__init__.py
@@ -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))
[email protected]('/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')
+
+
[email protected](
+ '/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'
+# )
[email protected](
+ '/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):
diff --git a/web/pgadmin/tools/user_management/static/js/user_management.js b/web/pgadmin/tools/user_management/static/js/user_management.js
index c3aca881..531c8c59 100644
--- a/web/pgadmin/tools/user_management/static/js/user_management.js
+++ b/web/pgadmin/tools/user_management/static/js/user_management.js
@@ -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(
view thread (12+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [pgAdmin][RM-6143]: Shared server entries not getting deleted.
In-Reply-To: <CAOBg0AMA7tYj7zgOO0A4NVCPedq9ef9bWrg819ACFoTDgkhu-Q@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox