Add group management to the user edit page.

Added a couple more API calls to make the inverse operations easier.
This commit is contained in:
Kevin McCarthy 2007-09-28 16:01:42 -07:00
parent 0cfccd0f8c
commit dbf8c1aeb9
8 changed files with 373 additions and 8 deletions

View File

@ -266,6 +266,21 @@ class IPAClient:
return self.transport.remove_users_from_group(user_uids, group_cn)
def add_groups_to_user(self, group_dns, user_dn):
"""Given a list of group dn's add them to the user.
Returns a list of the group dns that were not added.
"""
return self.transport.add_groups_to_user(group_dns, user_dn)
def remove_groups_from_user(self, group_dns, user_dn):
"""Given a list of group dn's remove them from the user.
Returns a list of the group dns that were not removed.
"""
return self.transport.remove_groups_from_user(group_dns, user_dn)
def update_group(self,group):
"""Update a group entry."""

View File

@ -441,6 +441,36 @@ class RPCClient:
return ipautil.unwrap_binary_data(result)
def add_groups_to_user(self, group_dns, user_dn):
"""Given a list of group dn's add them to the user.
Returns a list of the group dns that were not added.
"""
server = self.setup_server()
try:
result = server.add_groups_to_user(group_dns, user_dn)
except xmlrpclib.Fault, fault:
raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
except socket.error, (value, msg):
raise xmlrpclib.Fault(value, msg)
return ipautil.unwrap_binary_data(result)
def remove_groups_from_user(self, group_dns, user_dn):
"""Given a list of group dn's remove them from the user.
Returns a list of the group dns that were not removed.
"""
server = self.setup_server()
try:
result = server.remove_groups_from_user(group_dns, user_dn)
except xmlrpclib.Fault, fault:
raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
except socket.error, (value, msg):
raise xmlrpclib.Fault(value, msg)
return ipautil.unwrap_binary_data(result)
def update_group(self,oldgroup,newgroup):
"""Update an existing group. oldgroup and newgroup are dicts of attributes"""
server = self.setup_server()

View File

@ -79,6 +79,15 @@ def sort_group_member(a, b):
else:
return 1
def sort_by_cn(a, b):
"""Comparator function used for sorting groups."""
if a.get('cn', '') == b.get('cn', ''):
return 0
elif a.get('cn', '') < b.get('cn', ''):
return -1
else:
return 1
class Root(controllers.RootController):
@expose(template="ipagui.templates.welcome")
@ -144,6 +153,28 @@ class Root(controllers.RootController):
turbogears.flash("User add failed: " + str(e))
return dict(form=user_new_form, tg_template='ipagui.templates.usernew')
@expose("ipagui.templates.dynamiceditsearch")
@identity.require(identity.not_anonymous())
def useredit_search(self, **kw):
"""Searches for groups and displays list of results in a table.
This method is used for the ajax search on the user edit page."""
client.set_krbccache(os.environ["KRB5CCNAME"])
groups = []
counter = 0
searchlimit = 100
criteria = kw.get('criteria')
if criteria != None and len(criteria) > 0:
try:
groups = client.find_groups(criteria.encode('utf-8'), None,
searchlimit)
groups_counter = groups[0]
groups = groups[1:]
except ipaerror.IPAError, e:
turbogears.flash("search failed: " + str(e))
return dict(users=None, groups=groups, criteria=criteria,
counter=groups_counter)
@expose("ipagui.templates.useredit")
@identity.require(identity.not_anonymous())
@ -152,18 +183,26 @@ class Root(controllers.RootController):
if tg_errors:
turbogears.flash("There was a problem with the form!")
client.set_krbccache(os.environ["KRB5CCNAME"])
try:
client.set_krbccache(os.environ["KRB5CCNAME"])
user = client.get_user_by_uid(uid, user_fields)
user_dict = user.toDict()
# Edit shouldn't fill in the password field.
if user_dict.has_key('userpassword'):
del(user_dict['userpassword'])
user_groups = client.get_groups_by_member(user.dn, ['dn', 'cn'])
user_groups_dicts = map(lambda group: group.toDict(), user_groups)
user_groups_dicts.sort(sort_by_cn)
user_groups_data = b64encode(dumps(user_groups_dicts))
# store a copy of the original user for the update later
user_data = b64encode(dumps(user_dict))
user_dict['user_orig'] = user_data
return dict(form=user_edit_form, user=user_dict)
user_dict['user_groups_data'] = user_groups_data
return dict(form=user_edit_form, user=user_dict,
user_groups=user_groups_dicts)
except ipaerror.IPAError, e:
turbogears.flash("User edit failed: " + str(e))
raise turbogears.redirect('/usershow', uid=kw.get('uid'))
@ -178,12 +217,20 @@ class Root(controllers.RootController):
turbogears.flash("Edit user cancelled")
raise turbogears.redirect('/usershow', uid=kw.get('uid'))
# Decode the group data, in case we need to round trip
user_groups_dicts = loads(b64decode(kw.get('user_groups_data')))
tg_errors, kw = self.userupdatevalidate(**kw)
if tg_errors:
return dict(form=user_edit_form, user=kw,
user_groups=user_groups_dicts,
tg_template='ipagui.templates.useredit')
password_change = False
#
# Update the user itself
#
try:
orig_user_dict = loads(b64decode(kw.get('user_orig')))
@ -210,22 +257,77 @@ class Root(controllers.RootController):
new_user.getValue('sn')))
rv = client.update_user(new_user)
#
# If the user update succeeds, but below operations fail, we
# need to make sure a subsequent submit doesn't try to update
# the user again.
#
kw['user_orig'] = b64encode(dumps(new_user.toDict()))
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST), e:
if not password_change:
turbogears.flash("User update failed: " + str(e))
return dict(form=user_edit_form, user=kw,
tg_template='ipagui.templates.useredit')
# could be a password change
# could be groups change
# too much work to figure out unless someone really screams
pass
except ipaerror.IPAError, e:
turbogears.flash("User update failed: " + str(e))
return dict(form=user_edit_form, user=kw,
user_groups=user_groups_dicts,
tg_template='ipagui.templates.useredit')
#
# Password change
#
try:
if password_change:
rv = client.modifyPassword(kw['uid'], "", kw.get('userpassword'))
except ipaerror.IPAError, e:
turbogears.flash("User password change failed: " + str(e))
return dict(form=user_edit_form, user=kw,
user_groups=user_groups_dicts,
tg_template='ipagui.templates.useredit')
#
# Add groups
#
failed_adds = []
try:
dnadds = kw.get('dnadd')
if dnadds != None:
if not(isinstance(dnadds,list) or isinstance(dnadds,tuple)):
dnadds = [dnadds]
failed_adds = client.add_groups_to_user(
utf8_encode_values(dnadds), new_user.dn)
kw['dnadd'] = failed_adds
except ipaerror.IPAError, e:
turbogears.flash("Group update failed: " + str(e))
return dict(form=user_edit_form, user=kw,
user_groups=user_groups_dicts,
tg_template='ipagui.templates.useredit')
#
# Remove groups
#
failed_dels = []
try:
dndels = kw.get('dndel')
if dndels != None:
if not(isinstance(dndels,list) or isinstance(dndels,tuple)):
dndels = [dndels]
failed_dels = client.remove_groups_from_user(
utf8_encode_values(dndels), new_user.dn)
kw['dndel'] = failed_dels
except ipaerror.IPAError, e:
turbogears.flash("Group update failed: " + str(e))
return dict(form=user_edit_form, user=kw,
user_groups=user_groups_dicts,
tg_template='ipagui.templates.useredit')
if (len(failed_adds) > 0) or (len(failed_dels) > 0):
message = "There was an error updating groups.<br />"
message += "Failures have been preserved in the add/remove lists."
turbogears.flash(message)
return dict(form=user_edit_form, user=kw,
user_groups=user_groups_dicts,
tg_template='ipagui.templates.useredit')
turbogears.flash("%s updated!" % kw['uid'])

View File

@ -24,6 +24,8 @@ class UserFields():
editprotected_hidden = widgets.HiddenField(name="editprotected")
user_orig = widgets.HiddenField(name="user_orig")
user_groups_data = widgets.HiddenField(name="user_groups_data")
dn_to_info_json = widgets.HiddenField(name="dn_to_info_json")
class UserNewValidator(validators.Schema):
uid = validators.PlainText(not_empty=True)
@ -88,6 +90,8 @@ class UserEditForm(widgets.Form):
UserFields.uidnumber, UserFields.gidnumber,
UserFields.krbPasswordExpiration_hidden,
UserFields.editprotected_hidden,
UserFields.user_groups_data,
UserFields.dn_to_info_json,
]
validator = UserEditValidator()

View File

@ -35,6 +35,6 @@ else:
Password has expired
</div>
${form.display(action="userupdate", value=user)}
${form.display(action="userupdate", value=user, user_groups=user_groups)}
</body>
</html>

View File

@ -1,6 +1,16 @@
<div xmlns:py="http://purl.org/kid/ns#"
class="simpleroster">
<form action="${action}" name="${name}" method="${method}" class="tableform">
<form action="${action}" name="${name}" method="${method}" class="tableform"
onsubmit="preSubmit()">
<?python
from ipagui.helpers import ipahelper
?>
<script type="text/javascript" charset="utf-8"
src="${tg.url('/static/javascript/dynamicedit.js')}"></script>
<?python searchurl = tg.url('/useredit_search') ?>
<script type="text/javascript">
function toggleProtectedFields(checkbox) {
@ -22,6 +32,40 @@
$('form_editprotected').value = '';
}
}
function enterDoSearch(e) {
var keyPressed;
if (window.event) {
keyPressed = window.event.keyCode;
} else {
keyPressed = e.which;
}
if (keyPressed == 13) {
return doSearch();
} else {
return true;
}
}
function doSearch() {
$('searchresults').update("Searching...");
new Ajax.Updater('searchresults',
'${searchurl}',
{ asynchronous:true,
parameters: { criteria: $('criteria').value },
evalScripts: true });
return false;
}
// override dynamicedit.js version
// we don't need to show [group] nor italize groups
function renderMemberInfo(newdiv, info) {
if (info.type == "group") {
newdiv.appendChild(document.createTextNode(
info.name.escapeHTML() + " "));
}
}
</script>
@ -213,6 +257,81 @@
</tr>
</table>
<div>
<div class="formsection">Groups</div>
<div class="floatlist">
<div class="floatheader">To Remove:</div>
<div id="delmembers">
</div>
</div>
<div>
<?python div_counter = 1 ?>
<div py:for="group in user_groups" id="member-${div_counter}">
<?python
group_dn = group.get('dn')
group_dn_esc = ipahelper.javascript_string_escape(group_dn)
group_name = group.get('cn')
group_descr = "[group]"
group_type = "group"
group_name_esc = ipahelper.javascript_string_escape(group_name)
group_descr_esc = ipahelper.javascript_string_escape(group_descr)
group_type_esc = ipahelper.javascript_string_escape(group_type)
?>
<span id="member-info-${div_counter}"></span>
<script type="text/javascript">
renderMemberInfo($('member-info-${div_counter}'),
new MemberDisplayInfo('${group_name_esc}',
'${group_descr_esc}',
'${group_type_esc}'));
</script>
<a href="#"
onclick="removememberHandler(this, '${group_dn_esc}',
new MemberDisplayInfo('${group_name_esc}',
'${group_descr_esc}',
'${group_type_esc}'));
return false;"
>remove</a>
<script type="text/javascript">
dn_to_member_div_id['${group_dn_esc}'] = "member-${div_counter}";
member_hash["${group_dn_esc}"] = 1;
</script>
<?python
div_counter = div_counter + 1
?>
</div>
</div>
</div>
<div style="clear:both">
<div class="formsection">Add Groups</div>
<div class="floatlist">
<div class="floatheader">To Add:</div>
<div id="newmembers">
</div>
</div>
<div>
<div id="search">
<input id="criteria" type="text" name="criteria"
onkeypress="return enterDoSearch(event);" />
<input type="button" value="Find"
onclick="return doSearch();"
/>
</div>
<div id="searchresults">
</div>
</div>
</div>
<table class="formtable" cellpadding="2" cellspacing="0" border="0">
<tr>
<th>
@ -232,9 +351,52 @@
</form>
<script type="text/javascript">
/*
* This section restores the contents of the add and remove lists
* dynamically if we have to refresh the page
*/
if ($('form_dn_to_info_json').value != "") {
dn_to_info_hash = new Hash($('form_dn_to_info_json').value.evalJSON());
}
if ($('form_editprotected').value != "") {
$('toggleprotected_checkbox').checked = true;
toggleProtectedFields($('toggleprotected_checkbox'));
}
</script>
<?python
dnadds = value.get('dnadd', [])
if not(isinstance(dnadds,list) or isinstance(dnadds,tuple)):
dnadds = [dnadds]
dndels = value.get('dndel', [])
if not(isinstance(dndels,list) or isinstance(dndels,tuple)):
dndels = [dndels]
?>
<script py:for="dnadd in dnadds">
<?python
dnadd_esc = ipahelper.javascript_string_escape(dnadd)
?>
var dn = "${dnadd_esc}";
var info = dn_to_info_hash[dn];
var newdiv = addmember(dn, info);
if (newdiv != null) {
newdiv.style.display = 'block';
}
</script>
<script py:for="dndel in dndels">
<?python
dndel_esc = ipahelper.javascript_string_escape(dndel)
?>
var dn = "${dndel_esc}";
var info = dn_to_info_hash[dn];
var newdiv = removemember(dn, info);
newdiv.style.display = 'block';
orig_div_id = dn_to_member_div_id[dn]
$(orig_div_id).style.display = 'none';
</script>
</div>

View File

@ -899,6 +899,56 @@ class IPAServer:
return failed
def add_groups_to_user(self, group_dns, user_dn, opts=None):
"""Given a list of group dn's add them to the user.
Returns a list of the group dns that were not added.
"""
failed = []
if (isinstance(group_dns, str)):
group_dns = [group_dns]
for group_dn in group_dns:
# TODO - change add_member_to_group to take a group_dn
try:
group = self.get_group_by_dn(group_dn, ['cn'], opts)
self.add_member_to_group(user_dn, group.get('cn'), opts)
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST):
# User is already in the group
failed.append(group_dn)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
# User or the group does not exist
failed.append(group_dn)
return failed
def remove_groups_from_user(self, group_dns, user_dn, opts=None):
"""Given a list of group dn's remove them from the user.
Returns a list of the group dns that were not removed.
"""
failed = []
if (isinstance(group_dns, str)):
group_dns = [group_dns]
for group_dn in group_dns:
# TODO - change remove_member_from_group to take a group_dn
try:
group = self.get_group_by_dn(group_dn, ['cn'], opts)
self.remove_member_from_group(user_dn, group.get('cn'), opts)
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST):
# User is not in the group
failed.append(group_dn)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
# User or the group does not exist
failed.append(group_dn)
return failed
def update_group (self, oldgroup, newgroup, opts=None):
"""Update a group in LDAP"""
return self.__update_entry(oldgroup, newgroup, opts)

View File

@ -342,6 +342,8 @@ def handler(req, profiling=False):
h.register_function(f.add_group_to_group)
h.register_function(f.remove_user_from_group)
h.register_function(f.remove_users_from_group)
h.register_function(f.add_groups_to_user)
h.register_function(f.remove_groups_from_user)
h.register_function(f.update_group)
h.register_function(f.delete_group)
h.handle_request(req)