From dbf8c1aeb98c730b7f4a83bfc15062040e331083 Mon Sep 17 00:00:00 2001 From: Kevin McCarthy Date: Fri, 28 Sep 2007 16:01:42 -0700 Subject: [PATCH] Add group management to the user edit page. Added a couple more API calls to make the inverse operations easier. --- ipa-python/ipaclient.py | 15 ++ ipa-python/rpcclient.py | 30 ++++ ipa-server/ipa-gui/ipagui/controllers.py | 114 +++++++++++- ipa-server/ipa-gui/ipagui/forms/user.py | 4 + .../ipa-gui/ipagui/templates/useredit.kid | 2 +- .../ipa-gui/ipagui/templates/usereditform.kid | 164 +++++++++++++++++- ipa-server/xmlrpc-server/funcs.py | 50 ++++++ ipa-server/xmlrpc-server/ipaxmlrpc.py | 2 + 8 files changed, 373 insertions(+), 8 deletions(-) diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py index d047f7717..27ad1c246 100644 --- a/ipa-python/ipaclient.py +++ b/ipa-python/ipaclient.py @@ -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.""" diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py index cfeb3d20c..9f02b374f 100644 --- a/ipa-python/rpcclient.py +++ b/ipa-python/rpcclient.py @@ -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() diff --git a/ipa-server/ipa-gui/ipagui/controllers.py b/ipa-server/ipa-gui/ipagui/controllers.py index 45a94e023..c770f5c02 100644 --- a/ipa-server/ipa-gui/ipagui/controllers.py +++ b/ipa-server/ipa-gui/ipagui/controllers.py @@ -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.
" + 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']) diff --git a/ipa-server/ipa-gui/ipagui/forms/user.py b/ipa-server/ipa-gui/ipagui/forms/user.py index 037205a17..561c7ef18 100644 --- a/ipa-server/ipa-gui/ipagui/forms/user.py +++ b/ipa-server/ipa-gui/ipagui/forms/user.py @@ -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() diff --git a/ipa-server/ipa-gui/ipagui/templates/useredit.kid b/ipa-server/ipa-gui/ipagui/templates/useredit.kid index b5e8447d2..e4d492b0a 100644 --- a/ipa-server/ipa-gui/ipagui/templates/useredit.kid +++ b/ipa-server/ipa-gui/ipagui/templates/useredit.kid @@ -35,6 +35,6 @@ else: Password has expired - ${form.display(action="userupdate", value=user)} + ${form.display(action="userupdate", value=user, user_groups=user_groups)} diff --git a/ipa-server/ipa-gui/ipagui/templates/usereditform.kid b/ipa-server/ipa-gui/ipagui/templates/usereditform.kid index 3f7db50d7..36c65fb16 100644 --- a/ipa-server/ipa-gui/ipagui/templates/usereditform.kid +++ b/ipa-server/ipa-gui/ipagui/templates/usereditform.kid @@ -1,6 +1,16 @@
-
+ + + + + + + @@ -213,6 +257,81 @@ +
+
Groups
+ +
+
To Remove:
+
+
+
+ +
+ +
+ + + + remove + + +
+
+ +
+ +
+
Add Groups
+ +
+
To Add:
+
+
+
+ +
+ +
+
+
+
+ + + +
@@ -232,9 +351,52 @@ + + + + + + + diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py index 838f05f12..e4e2f40e2 100644 --- a/ipa-server/xmlrpc-server/funcs.py +++ b/ipa-server/xmlrpc-server/funcs.py @@ -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) diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py index 69b2740fc..96d9299c2 100644 --- a/ipa-server/xmlrpc-server/ipaxmlrpc.py +++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py @@ -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)