Orgs: allow users to leave an organization

This commit is contained in:
Sybren A. Stüvel 2017-08-24 11:35:09 +02:00
parent 0445c3bd86
commit be12bd7d99
3 changed files with 96 additions and 60 deletions

View File

@ -104,16 +104,18 @@ class OrganizationPatchHandler(patch_handler.AbstractPatchHandler):
The calling user must be admin of the organization.
"""
self._assert_is_admin(org_id)
# Do some basic validation.
email = patch.get('email') or None
user_id = patch.get('user_id')
user_oid = str2id(user_id) if user_id else None
log.info('User %s uses PATCH to remove user from organization %s',
current_user().user_id, org_id)
# Users require admin rights on the org, except when removing themselves.
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)
return jsonify(org_doc)

View File

@ -115,7 +115,7 @@
| {% endif %}
span.sharing-users-extra {{ member['username'] }}
.sharing-users-action
| {% if can_edit %}
| {% if can_edit or current_user.objectid == member['_id'] %}
| {% if current_user.objectid == member['_id'] %}
button.user-remove(title="Leave as member of this organization")
i.pi-trash
@ -180,28 +180,6 @@
| {% if can_edit %}
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() {
$('#user-select').userSearch(
'{{config.ALGOLIA_USER}}',
@ -232,13 +210,6 @@ script.
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) {
@ -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) {
if (!user_id || user_id.length == 0) {
toastr.error('Please select a user from the list');
@ -382,4 +328,66 @@ script.
}
| {% 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 %}

View File

@ -331,6 +331,32 @@ class OrganizationPatchTest(AbstractPillarTest):
self.assertEqual([], db_org['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):
self.enter_app_context()
om = self.app.org_manager