Orgs: allow users to leave an organization
This commit is contained in:
@@ -104,16 +104,18 @@ class OrganizationPatchHandler(patch_handler.AbstractPatchHandler):
|
|||||||
The calling user must be admin of the organization.
|
The calling user must be admin of the organization.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._assert_is_admin(org_id)
|
|
||||||
|
|
||||||
# Do some basic validation.
|
# Do some basic validation.
|
||||||
email = patch.get('email') or None
|
email = patch.get('email') or None
|
||||||
user_id = patch.get('user_id')
|
user_id = patch.get('user_id')
|
||||||
|
|
||||||
user_oid = str2id(user_id) if user_id else None
|
user_oid = str2id(user_id) if user_id else None
|
||||||
|
|
||||||
log.info('User %s uses PATCH to remove user from organization %s',
|
# Users require admin rights on the org, except when removing themselves.
|
||||||
current_user().user_id, org_id)
|
current_user_id = current_user().user_id
|
||||||
|
if user_oid is None or user_oid != current_user_id:
|
||||||
|
self._assert_is_admin(org_id)
|
||||||
|
|
||||||
|
log.info('User %s uses PATCH to remove user %s from organization %s',
|
||||||
|
current_user_id, user_oid, org_id)
|
||||||
|
|
||||||
org_doc = current_app.org_manager.remove_user(org_id, user_id=user_oid, email=email)
|
org_doc = current_app.org_manager.remove_user(org_id, user_id=user_oid, email=email)
|
||||||
return jsonify(org_doc)
|
return jsonify(org_doc)
|
||||||
|
@@ -115,7 +115,7 @@
|
|||||||
| {% endif %}
|
| {% endif %}
|
||||||
span.sharing-users-extra {{ member['username'] }}
|
span.sharing-users-extra {{ member['username'] }}
|
||||||
.sharing-users-action
|
.sharing-users-action
|
||||||
| {% if can_edit %}
|
| {% if can_edit or current_user.objectid == member['_id'] %}
|
||||||
| {% if current_user.objectid == member['_id'] %}
|
| {% if current_user.objectid == member['_id'] %}
|
||||||
button.user-remove(title="Leave as member of this organization")
|
button.user-remove(title="Leave as member of this organization")
|
||||||
i.pi-trash
|
i.pi-trash
|
||||||
@@ -180,28 +180,6 @@
|
|||||||
|
|
||||||
| {% if can_edit %}
|
| {% if can_edit %}
|
||||||
script.
|
script.
|
||||||
|
|
||||||
function patchOrganization(patch) {
|
|
||||||
if (typeof patch == 'undefined') {
|
|
||||||
throw 'patchOrganization(undefined) called';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (console) console.log('patchOrganization', patch);
|
|
||||||
|
|
||||||
var promise = $.ajax({
|
|
||||||
url: '/api/organizations/{{ organization._id }}',
|
|
||||||
method: 'PATCH',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: JSON.stringify(patch),
|
|
||||||
})
|
|
||||||
.fail(function(err) {
|
|
||||||
if (console) console.log('Error patching: ', err);
|
|
||||||
})
|
|
||||||
;
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#user-select').userSearch(
|
$('#user-select').userSearch(
|
||||||
'{{config.ALGOLIA_USER}}',
|
'{{config.ALGOLIA_USER}}',
|
||||||
@@ -232,13 +210,6 @@ script.
|
|||||||
setAdmin(hit.objectID, hit.full_name);
|
setAdmin(hit.objectID, hit.full_name);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
$('body').off('click', '.user-remove'); // remove previous handlers.
|
|
||||||
$('body').on('click', '.user-remove', function(e) {
|
|
||||||
var user_id = $(this).closest('*[data-user-id]').data('user-id');
|
|
||||||
var user_email = $(this).closest('*[data-user-email]').data('user-email');
|
|
||||||
removeUser(user_id, user_email);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function addUser(userId) {
|
function addUser(userId) {
|
||||||
@@ -264,31 +235,6 @@ script.
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function removeUser(user_id, email) {
|
|
||||||
if (typeof user_id == 'undefined' && typeof email == 'undefined') {
|
|
||||||
throw "removeUser(undefined, undefined) called";
|
|
||||||
}
|
|
||||||
var organization_id = '{{ organization._id }}';
|
|
||||||
var patch = {op: 'remove-user'};
|
|
||||||
|
|
||||||
if (typeof user_id !== 'undefined') {
|
|
||||||
patch.user_id = user_id;
|
|
||||||
}
|
|
||||||
if (typeof email !== 'undefined') {
|
|
||||||
patch.email = String(email);
|
|
||||||
}
|
|
||||||
|
|
||||||
patchOrganization(patch)
|
|
||||||
.done(function() {
|
|
||||||
$("ul.sharing-users-list").find("[data-user-id='" + user_id + "']").remove();
|
|
||||||
item_open('{{ organization._id }}', false);
|
|
||||||
toastr.success('User removed from this organization');
|
|
||||||
}).fail(function (data) {
|
|
||||||
var msg = xhrErrorResponseMessage(data);
|
|
||||||
toastr.error('Error removing user: ' + msg);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAdmin(user_id, full_name) {
|
function setAdmin(user_id, full_name) {
|
||||||
if (!user_id || user_id.length == 0) {
|
if (!user_id || user_id.length == 0) {
|
||||||
toastr.error('Please select a user from the list');
|
toastr.error('Please select a user from the list');
|
||||||
@@ -382,4 +328,66 @@ script.
|
|||||||
}
|
}
|
||||||
|
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
script.
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('body').off('click', '.user-remove'); // remove previous handlers.
|
||||||
|
$('body').on('click', '.user-remove', function(e) {
|
||||||
|
var user_id = $(this).closest('*[data-user-id]').data('user-id');
|
||||||
|
var user_email = $(this).closest('*[data-user-email]').data('user-email');
|
||||||
|
removeUser(user_id, user_email);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function patchOrganization(patch) {
|
||||||
|
if (typeof patch == 'undefined') {
|
||||||
|
throw 'patchOrganization(undefined) called';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (console) console.log('patchOrganization', patch);
|
||||||
|
|
||||||
|
var promise = $.ajax({
|
||||||
|
url: '/api/organizations/{{ organization._id }}',
|
||||||
|
method: 'PATCH',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify(patch),
|
||||||
|
})
|
||||||
|
.fail(function(err) {
|
||||||
|
if (console) console.log('Error patching: ', err);
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeUser(user_id, email) {
|
||||||
|
if (typeof user_id == 'undefined' && typeof email == 'undefined') {
|
||||||
|
throw "removeUser(undefined, undefined) called";
|
||||||
|
}
|
||||||
|
var organization_id = '{{ organization._id }}';
|
||||||
|
var patch = {op: 'remove-user'};
|
||||||
|
|
||||||
|
if (typeof user_id !== 'undefined') {
|
||||||
|
patch.user_id = user_id;
|
||||||
|
}
|
||||||
|
if (typeof email !== 'undefined') {
|
||||||
|
patch.email = String(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
patchOrganization(patch)
|
||||||
|
.done(function() {
|
||||||
|
$("ul.sharing-users-list").find("[data-user-id='" + user_id + "']").remove();
|
||||||
|
if ('{{ current_user.user_id }}' == user_id) {
|
||||||
|
// User removed self, so we cannot open this organization again.
|
||||||
|
$('#organization-{{ organization._id}}').remove();
|
||||||
|
toastr.success('You left the organization.')
|
||||||
|
} else {
|
||||||
|
item_open('{{ organization._id }}', false);
|
||||||
|
toastr.success('User removed from this organization');
|
||||||
|
}
|
||||||
|
}).fail(function (data) {
|
||||||
|
var msg = xhrErrorResponseMessage(data);
|
||||||
|
toastr.error('Error removing user: ' + msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
@@ -331,6 +331,32 @@ class OrganizationPatchTest(AbstractPillarTest):
|
|||||||
self.assertEqual([], db_org['members'])
|
self.assertEqual([], db_org['members'])
|
||||||
self.assertEqual(['member2@example.com'], db_org['unknown_members'])
|
self.assertEqual(['member2@example.com'], db_org['unknown_members'])
|
||||||
|
|
||||||
|
def test_remove_self(self):
|
||||||
|
self.enter_app_context()
|
||||||
|
om = self.app.org_manager
|
||||||
|
|
||||||
|
admin_uid = self.create_user(24 * 'a', token='admin-token')
|
||||||
|
member_uid = self.create_user(24 * 'b', email='member1@example.com', token='member-token')
|
||||||
|
org_doc = om.create_new_org('Хакеры', admin_uid, 25)
|
||||||
|
org_id = org_doc['_id']
|
||||||
|
|
||||||
|
om.assign_users(org_id, ['member1@example.com'])
|
||||||
|
|
||||||
|
# Try the PATCH to remove self.
|
||||||
|
resp = self.patch(f'/api/organizations/{org_id}',
|
||||||
|
json={
|
||||||
|
'op': 'remove-user',
|
||||||
|
'user_id': str(member_uid),
|
||||||
|
},
|
||||||
|
auth_token='member-token')
|
||||||
|
new_org_doc = resp.get_json()
|
||||||
|
|
||||||
|
db = self.app.db('organizations')
|
||||||
|
db_org = db.find_one(org_id)
|
||||||
|
|
||||||
|
self.assertEqual([], db_org['members'])
|
||||||
|
self.assertEqual([], new_org_doc['members'])
|
||||||
|
|
||||||
def test_edit_from_web(self):
|
def test_edit_from_web(self):
|
||||||
self.enter_app_context()
|
self.enter_app_context()
|
||||||
om = self.app.org_manager
|
om = self.app.org_manager
|
||||||
|
Reference in New Issue
Block a user