user-groups: added info, and new panel to control user group synchronization.

- allows to check status of user-group and if it's going to be sync-ed via
  external plugins.
- allows to set manual synchronization for already existing RhodeCode Groups.
This commit is contained in:
Marcin Kuzminski 2017-04-05 14:20:30 +02:00
parent 19511543cf
commit 98f97b18fd
6 changed files with 128 additions and 9 deletions

View file

@ -374,6 +374,10 @@ def make_map(config):
'/user_groups/{user_group_id}/edit/advanced',
action='edit_advanced', conditions={'method': ['GET']})
m.connect('edit_user_group_advanced_sync',
'/user_groups/{user_group_id}/edit/advanced/sync',
action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
m.connect('edit_user_group_members',
'/user_groups/{user_group_id}/edit/members', jsroute=True,
action='user_group_members', conditions={'method': ['GET']})

View file

@ -117,7 +117,7 @@ class UserGroupsController(BaseController):
def user_group_actions(user_group_id, user_group_name):
return _render("user_group_actions", user_group_id, user_group_name)
## json generate
# json generate
group_iter = UserGroupList(UserGroup.query().all(),
perm_set=['usergroup.admin'])
@ -129,6 +129,7 @@ class UserGroupsController(BaseController):
"group_name_raw": user_gr.users_group_name,
"desc": h.escape(user_gr.user_group_description),
"members": len(user_gr.members),
"sync": user_gr.group_data.get('extern_type'),
"active": h.bool2icon(user_gr.users_group_active),
"owner": h.escape(h.link_to_user(user_gr.user.username)),
"action": user_group_actions(
@ -431,7 +432,6 @@ class UserGroupsController(BaseController):
prefix_error=False,
encoding="UTF-8",
force_defaults=False)
except Exception:
log.exception("Exception during permissions saving")
h.flash(_('An error occurred during permissions saving'),
@ -458,6 +458,36 @@ class UserGroupsController(BaseController):
return render('admin/user_groups/user_group_edit.mako')
@HasUserGroupPermissionAnyDecorator('usergroup.admin')
def edit_advanced_set_synchronization(self, user_group_id):
user_group_id = safe_int(user_group_id)
user_group = UserGroup.get_or_404(user_group_id)
existing = user_group.group_data.get('extern_type')
if existing:
new_state = user_group.group_data
new_state['extern_type'] = None
else:
new_state = user_group.group_data
new_state['extern_type'] = 'manual'
new_state['extern_type_set_by'] = c.rhodecode_user.username
try:
user_group.group_data = new_state
Session().add(user_group)
Session().commit()
h.flash(_('User Group synchronization updated successfully'),
category='success')
except Exception:
log.exception("Exception during sync settings saving")
h.flash(_('An error occurred during synchronization update'),
category='error')
return redirect(
url('edit_user_group_advanced', user_group_id=user_group_id))
@HasUserGroupPermissionAnyDecorator('usergroup.admin')
@XHRRequired()
@jsonify

View file

@ -501,8 +501,8 @@ class UserGroupModel(BaseModel):
if not existing_group:
desc = 'Automatically created from plugin:%s' % extern_type
# we use first admin account to set the owner of the group
existing_group = UserGroupModel().create(gr, desc, owner,
group_data={'extern_type': extern_type})
existing_group = UserGroupModel().create(
gr, desc, owner, group_data={'extern_type': extern_type})
# we can only add users to special groups created via plugins
managed = 'extern_type' in existing_group.group_data
@ -511,8 +511,7 @@ class UserGroupModel(BaseModel):
UserGroupModel().add_user_to_group(existing_group, user)
else:
log.debug('Skipping addition to group %s since it is '
'not managed by auth plugins' % gr)
'not set to be automatically synchronized' % gr)
def change_groups(self, user, groups):
"""

View file

@ -6,6 +6,8 @@
(_('Created on'), h.format_date(c.user_group.created_on), '', '',),
(_('Members'), len(c.group_members_obj),'', [x for x in c.group_members_obj]),
(_('Automatic member sync'), 'Yes' if c.user_group.group_data.get('extern_type') else 'No', '', '',),
(_('Assigned to repositories'), len(c.group_to_repos),'', [x for x in c.group_to_repos]),
(_('Assigned to repo groups'), len(c.group_to_repo_groups), '', [x for x in c.group_to_repo_groups]),
@ -19,8 +21,55 @@
<div class="panel-body">
${base.dt_info_panel(elems)}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">${_('Group members sync')}</h3>
</div>
<div class="panel-body">
<% sync_type = c.user_group.group_data.get('extern_type') %>
% if sync_type:
<p>
${_('This group is set to be automatically synchronised.')}<br/>
${_('Each member will be added or removed from this groups once they interact with RhodeCode system.')}<br/>
${_('This group synchronization was set by')}: <strong>${sync_type}</strong>
</p>
% else:
<p>
${_('This group is not set to be automatically synchronised')}
</p>
% endif
<div>
${h.secure_form(h.url('edit_user_group_advanced_sync', user_group_id=c.user_group.users_group_id), method='post')}
<div class="field">
<button class="btn btn-default" type="submit">
%if sync_type:
${_('Disable synchronization')}
%else:
${_('Enable synchronization')}
%endif
</button>
</div>
<div class="field">
<span class="help-block">
%if sync_type:
${_('User group will no longer synchronize membership')}
%else:
${_('User group will start to synchronize membership')}
%endif
</span>
</div>
${h.end_form()}
</div>
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title">${_('Delete User Group')}</h3>

View file

@ -58,8 +58,10 @@ $(document).ready(function() {
{ data: {"_": "members",
"sort": "members",
"type": Number}, title: "${_('Members')}", className: "td-number" },
{ data: {"_": "sync",
"sort": "sync"}, title: "${_('Sync')}", className: "td-sync" },
{ data: {"_": "active",
"sort": "active"}, title: "${_('Active')}", className: "td-active", className: "td-number"},
"sort": "active"}, title: "${_('Active')}", className: "td-active" },
{ data: {"_": "owner",
"sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
{ data: {"_": "action",

View file

@ -21,7 +21,7 @@
import pytest
from rhodecode.tests import (
TestController, url, assert_session_flash, link_to)
TestController, url, assert_session_flash, link_to, TEST_USER_ADMIN_LOGIN)
from rhodecode.model.db import User, UserGroup
from rhodecode.model.meta import Session
from rhodecode.tests.fixture import Fixture
@ -56,6 +56,41 @@ class TestAdminUsersGroupsController(TestController):
response,
'Created user group %s' % user_group_link)
def test_set_synchronization(self):
self.log_user()
users_group_name = TEST_USER_GROUP + 'sync'
response = self.app.post(url('users_groups'), {
'users_group_name': users_group_name,
'user_group_description': 'DESC',
'active': True,
'csrf_token': self.csrf_token})
group = Session().query(UserGroup).filter(
UserGroup.users_group_name == users_group_name).one()
assert group.group_data.get('extern_type') is None
# enable
self.app.post(
url('edit_user_group_advanced_sync', user_group_id=group.users_group_id),
params={'csrf_token': self.csrf_token}, status=302)
group = Session().query(UserGroup).filter(
UserGroup.users_group_name == users_group_name).one()
assert group.group_data.get('extern_type') == 'manual'
assert group.group_data.get('extern_type_set_by') == TEST_USER_ADMIN_LOGIN
# disable
self.app.post(
url('edit_user_group_advanced_sync',
user_group_id=group.users_group_id),
params={'csrf_token': self.csrf_token}, status=302)
group = Session().query(UserGroup).filter(
UserGroup.users_group_name == users_group_name).one()
assert group.group_data.get('extern_type') is None
assert group.group_data.get('extern_type_set_by') == TEST_USER_ADMIN_LOGIN
def test_delete(self):
self.log_user()
users_group_name = TEST_USER_GROUP + 'another'
@ -77,7 +112,7 @@ class TestAdminUsersGroupsController(TestController):
group = Session().query(UserGroup).filter(
UserGroup.users_group_name == users_group_name).one()
response = self.app.post(
self.app.post(
url('delete_users_group', user_group_id=group.users_group_id),
params={'_method': 'delete', 'csrf_token': self.csrf_token})