diff --git a/ipa-admintools/ipa-findgroup b/ipa-admintools/ipa-findgroup index 9f809aa0b..d84a2c62f 100644 --- a/ipa-admintools/ipa-findgroup +++ b/ipa-admintools/ipa-findgroup @@ -65,6 +65,9 @@ def main(): if counter == 0: print "No entries found for", args[1] return 2 + elif counter == -1: + print "These results are truncated." + print "Please revine your search and try again." for ent in groups: try: diff --git a/ipa-admintools/ipa-finduser b/ipa-admintools/ipa-finduser index 6dc4d56c8..81e8898a2 100644 --- a/ipa-admintools/ipa-finduser +++ b/ipa-admintools/ipa-finduser @@ -90,6 +90,9 @@ def main(): if counter == 0: print "No entries found for", args[1] return 2 + elif counter == -1: + print "These results are truncated." + print "Please revine your search and try again." for ent in users: attr = ent.attrList() diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install index fccdac3bd..bc8e7c9c3 100644 --- a/ipa-client/ipa-install/ipa-client-install +++ b/ipa-client/ipa-install/ipa-client-install @@ -131,8 +131,10 @@ def main(): opts = [{'name':'comment', 'type':'comment', 'value':'File modified by ipa-client-install'}, {'name':'empty', 'type':'empty'}] - defopts.append({'name':'server', 'type':'option', 'value':ds.getServerName()}) - defopts.append({'name':'realm', 'type':'option', 'value':ds.getRealmName()}) + + #[defaults] + defopts = [{'name':'server', 'type':'option', 'value':ds.getServerName()}, + {'name':'realm', 'type':'option', 'value':ds.getRealmName()}] opts.append({'name':'defaults', 'type':'section', 'value':defopts}) opts.append({'name':'empty', 'type':'empty'}) diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py index c551f0435..426f66817 100644 --- a/ipa-python/ipaclient.py +++ b/ipa-python/ipaclient.py @@ -116,9 +116,6 @@ class IPAClient: user_dict = user.toDict() - # dn is set on the server-side - del user_dict['dn'] - # convert to a regular dict before sending result = self.transport.add_user(user_dict, user_container) return result @@ -385,6 +382,20 @@ class IPAClient: def add_service_principal(self, princ_name): return self.transport.add_service_principal(princ_name) + def find_service_principal(self, criteria, sattrs=None, searchlimit=0, timelimit=-1): + """Return a list: counter followed by a Entity object for each host that + matches the criteria. If the results are truncated, counter will + be set to -1""" + result = self.transport.find_service_principal(criteria, sattrs, searchlimit, timelimit) + counter = result[0] + + hosts = [counter] + for attrs in result[1:]: + if attrs is not None: + hosts.append(entity.Entity(attrs)) + + return hosts + def get_keytab(self, princ_name): return self.transport.get_keytab(princ_name) diff --git a/ipa-python/ipaerror.py b/ipa-python/ipaerror.py index 2f9a98363..e34963365 100644 --- a/ipa-python/ipaerror.py +++ b/ipa-python/ipaerror.py @@ -177,3 +177,8 @@ CONFIG_DEFAULT_GROUP = gen_error_code( CONFIGURATION_CATEGORY, 0x0002, "You cannot remove the default users group.") + +CONFIG_INVALID_OC = gen_error_code( + CONFIGURATION_CATEGORY, + 0x0003, + "Invalid object class.") diff --git a/ipa-python/ipautil.py b/ipa-python/ipautil.py index 9b8412d23..2dc9b0c9d 100644 --- a/ipa-python/ipautil.py +++ b/ipa-python/ipautil.py @@ -27,9 +27,7 @@ from random import Random from time import gmtime import os import stat -import socket -from string import lower import re import xmlrpclib import datetime @@ -79,7 +77,7 @@ def run(args, stdin=None): logging.info(stderr) if p.returncode != 0: - raise self.CalledProcessError(p.returncode, ' '.join(args)) + raise CalledProcessError(p.returncode, ' '.join(args)) def file_exists(filename): try: @@ -118,24 +116,24 @@ class CIDict(dict): self.update(default or {}) def __getitem__(self,key): - return super(CIDict,self).__getitem__(lower(key)) + return super(CIDict,self).__getitem__(string.lower(key)) def __setitem__(self,key,value): - lower_key = lower(key) + lower_key = string.lower(key) self._keys[lower_key] = key - return super(CIDict,self).__setitem__(lower(key),value) + return super(CIDict,self).__setitem__(string.lower(key),value) def __delitem__(self,key): - lower_key = lower(key) + lower_key = string.lower(key) del self._keys[lower_key] - return super(CIDict,self).__delitem__(lower(key)) + return super(CIDict,self).__delitem__(string.lower(key)) def update(self,dict): for key in dict.keys(): self[key] = dict[key] def has_key(self,key): - return super(CIDict, self).has_key(lower(key)) + return super(CIDict, self).has_key(string.lower(key)) def get(self,key,failobj=None): try: diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py index d7ff97405..de32e9beb 100644 --- a/ipa-python/rpcclient.py +++ b/ipa-python/rpcclient.py @@ -703,6 +703,24 @@ class RPCClient: return ipautil.unwrap_binary_data(result) + def find_service_principal (self, criteria, sattrs=None, searchlimit=0, timelimit=-1): + """Return a list: counter followed by a Entity object for each host that + matches the criteria. If the results are truncated, counter will + be set to -1""" + + server = self.setup_server() + try: + # None values are not allowed in XML-RPC + if sattrs is None: + sattrs = "__NONE__" + result = server.find_service_principal(criteria, sattrs, searchlimit, timelimit) + 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 get_keytab(self, princ_name): server = self.setup_server() diff --git a/ipa-server/ipa-gui/ipagui/controllers.py b/ipa-server/ipa-gui/ipagui/controllers.py index d1ee22e01..70a29246a 100644 --- a/ipa-server/ipa-gui/ipagui/controllers.py +++ b/ipa-server/ipa-gui/ipagui/controllers.py @@ -19,6 +19,7 @@ from subcontrollers.group import GroupController from subcontrollers.delegation import DelegationController from subcontrollers.policy import PolicyController from subcontrollers.ipapolicy import IPAPolicyController +from subcontrollers.principal import PrincipalController ipa.config.init_config() @@ -31,6 +32,7 @@ class Root(controllers.RootController): delegate = DelegationController() policy = PolicyController() ipapolicy = IPAPolicyController() + principal = PrincipalController() @expose(template="ipagui.templates.welcome") @identity.require(identity.not_anonymous()) diff --git a/ipa-server/ipa-gui/ipagui/forms/ipapolicy.py b/ipa-server/ipa-gui/ipagui/forms/ipapolicy.py index 6cd967a97..1d48f8f33 100644 --- a/ipa-server/ipa-gui/ipagui/forms/ipapolicy.py +++ b/ipa-server/ipa-gui/ipagui/forms/ipapolicy.py @@ -1,5 +1,6 @@ import turbogears from turbogears import validators, widgets +from tg_expanding_form_widget.tg_expanding_form_widget import ExpandingForm class IPAPolicyFields(object): # From cn=ipaConfig @@ -12,12 +13,16 @@ class IPAPolicyFields(object): ipadefaultprimarygroup = widgets.TextField(name="ipadefaultprimarygroup", label="Default Users group") ipamaxusernamelength = widgets.TextField(name="ipamaxusernamelength", label="Max. Username Length", attrs=dict(size=3,maxlength=3)) ipapwdexpadvnotify = widgets.TextField(name="ipapwdexpadvnotify", label="Password Expiration Notification (days)", attrs=dict(size=3,maxlength=3)) + ipauserobjectclasses = widgets.TextField(name="ipauserobjectclasses", label="Default User Object Classes", attrs=dict(size=50)) + userobjectclasses = ExpandingForm(name="userobjectclasses", label="Default User Object Classes", fields=[ipauserobjectclasses]) + ipagroupobjectclasses = widgets.TextField(name="ipagroupobjectclasses", label="Default Group Object Classes", attrs=dict(size=50)) + groupobjectclasses = ExpandingForm(name="groupobjectclasses", label="Default User Object Classes", fields=[ipagroupobjectclasses]) ipapolicy_orig = widgets.HiddenField(name="ipapolicy_orig") # From cn=accounts - krbmaxpwdlife = widgets.TextField(name="krbmaxpwdlife", label="Max. Password Lifetime", attrs=dict(size=3,maxlength=3)) - krbminpwdlife = widgets.TextField(name="krbminpwdlife", label="Min. Password Lifetime", attrs=dict(size=3,maxlength=3)) + krbmaxpwdlife = widgets.TextField(name="krbmaxpwdlife", label="Max. Password Lifetime (days)", attrs=dict(size=3,maxlength=3)) + krbminpwdlife = widgets.TextField(name="krbminpwdlife", label="Min. Password Lifetime (hours)", attrs=dict(size=3,maxlength=3)) krbpwdmindiffchars = widgets.TextField(name="krbpwdmindiffchars", label="Min. number of character classes", attrs=dict(size=3,maxlength=3)) krbpwdminlength = widgets.TextField(name="krbpwdminlength", label="Min. Length of password", attrs=dict(size=3,maxlength=3)) krbpwdhistorylength = widgets.TextField(name="krbpwdhistorylength", label="Password History size", attrs=dict(size=3,maxlength=3)) @@ -34,6 +39,9 @@ class IPAPolicyValidator(validators.Schema): ipahomesrootdir = validators.String(not_empty=True) ipadefaultloginshell = validators.String(not_empty=True) ipadefaultprimarygroup = validators.String(not_empty=True) + ipauserobjectclasses = validators.ForEach(validators.String(not_empty=True)) + ipagroupobjectclasses = validators.ForEach(validators.String(not_empty=True)) + krbmaxpwdlife = validators.Number(not_empty=True) krbminpwdlife = validators.Number(not_empty=True) krbpwdmindiffchars = validators.Number(not_empty=True) diff --git a/ipa-server/ipa-gui/ipagui/forms/principal.py b/ipa-server/ipa-gui/ipagui/forms/principal.py new file mode 100644 index 000000000..a830c8a34 --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/forms/principal.py @@ -0,0 +1,39 @@ +import turbogears +from turbogears import validators, widgets +from tg_expanding_form_widget.tg_expanding_form_widget import ExpandingForm + +class PrincipalFields(object): + hostname = widgets.TextField(name="hostname", label="Host Name") + service = widgets.SingleSelectField(name="service", + label="Service Type", + options = [ + ("cifs", "cifs"), + ("dhcp", "dhcp"), + ("dns", "dns"), + ("host", "host"), + ("HTTP", "HTTP"), + ("ldap", "ldap"), + ("other", "other"), + ("rpc", "rpc"), + ("snmp", "snmp") + ], + attrs=dict(onchange="toggleOther(this.id)")) + other = widgets.TextField(name="other", label="Other Service", attrs=dict(size=10)) + +class PrincipalNewValidator(validators.Schema): + hostname = validators.String(not_empty=True) + service = validators.String(not_empty=True) + other = validators.String(not_empty=False) + +class PrincipalNewForm(widgets.Form): + params = ['principal_fields'] + + validator = PrincipalNewValidator() + + def __init__(self, *args, **kw): + super(PrincipalNewForm,self).__init__(*args, **kw) + (self.template_c, self.template) = widgets.meta.load_kid_template("ipagui.templates.principalnewform") + self.principal_fields = PrincipalFields + + def update_params(self, params): + super(PrincipalNewForm,self).update_params(params) diff --git a/ipa-server/ipa-gui/ipagui/helpers/ipahelper.py b/ipa-server/ipa-gui/ipagui/helpers/ipahelper.py index 9ea6b48ab..e5c2bd378 100644 --- a/ipa-server/ipa-gui/ipagui/helpers/ipahelper.py +++ b/ipa-server/ipa-gui/ipagui/helpers/ipahelper.py @@ -7,3 +7,34 @@ def javascript_string_escape(input): return re.sub(r'[\'\"\\]', lambda match: "\\%s" % match.group(), input) + +def setup_mv_fields(field, fieldname): + """Given a field (must be a list) and field name, convert that + field into a list of dictionaries of the form: + [ { fieldname : v1}, { fieldname : v2 }, .. ] + + This is how we pre-fill values for multi-valued fields. + """ + mvlist = [] + if field: + for v in field: + if v: + mvlist.append({ fieldname : v } ) + if len(mvlist) == 0: + # We need to return an empty value so something can be + # displayed on the edit page. Otherwise only an Add link + # will show, not an empty field. + mvlist.append({ fieldname : '' } ) + return mvlist + +def fix_incoming_fields(fields, fieldname, multifieldname): + """This is called by the update() function. It takes the incoming + list of dictionaries and converts it into back into the original + field, then removes the multiple field. + """ + fields[fieldname] = [] + for i in range(len(fields[multifieldname])): + fields[fieldname].append(fields[multifieldname][i][fieldname]) + del(fields[multifieldname]) + + return fields diff --git a/ipa-server/ipa-gui/ipagui/proxyprovider.py b/ipa-server/ipa-gui/ipagui/proxyprovider.py index bd9cf87a8..485a0f3b8 100644 --- a/ipa-server/ipa-gui/ipagui/proxyprovider.py +++ b/ipa-server/ipa-gui/ipagui/proxyprovider.py @@ -83,7 +83,7 @@ class ProxyIdentity(object): return self._user.groups except AttributeError: # Groups haven't been computed yet - return None + return [] groups= property(_get_groups) def logout(self): diff --git a/ipa-server/ipa-gui/ipagui/subcontrollers/ipapolicy.py b/ipa-server/ipa-gui/ipagui/subcontrollers/ipapolicy.py index a82b98888..d8237331b 100644 --- a/ipa-server/ipa-gui/ipagui/subcontrollers/ipapolicy.py +++ b/ipa-server/ipa-gui/ipagui/subcontrollers/ipapolicy.py @@ -17,6 +17,7 @@ from ipa.entity import utf8_encode_values from ipa import ipaerror import ipa.entity import ipagui.forms.ipapolicy +from ipagui.helpers import ipahelper import ldap.dn @@ -71,6 +72,15 @@ class IPAPolicyController(IPAController): # Combine the 2 dicts to make the form easier ipapolicy_dict.update(password_dict) + # Load potential multi-valued fields + if isinstance(ipapolicy_dict.get('ipauserobjectclasses',''), str): + ipapolicy_dict['ipauserobjectclasses'] = [ipapolicy_dict.get('ipauserobjectclasses')] + ipapolicy_dict['userobjectclasses'] = ipahelper.setup_mv_fields(ipapolicy_dict.get('ipauserobjectclasses'), 'ipauserobjectclasses') + + if isinstance(ipapolicy_dict.get('ipagroupobjectclasses',''), str): + ipapolicy_dict['ipagroupobjectclasses'] = [ipapolicy_dict.get('ipagroupobjectclasses')] + ipapolicy_dict['groupobjectclasses'] = ipahelper.setup_mv_fields(ipapolicy_dict.get('ipagroupobjectclasses'), 'ipagroupobjectclasses') + return dict(form=ipapolicy_edit_form, ipapolicy=ipapolicy_dict) except ipaerror.IPAError, e: turbogears.flash("IPA Policy edit failed: " + str(e) + "
" + str(e.detail)) @@ -88,6 +98,10 @@ class IPAPolicyController(IPAController): turbogears.flash("Edit policy cancelled") raise turbogears.redirect('/ipapolicy/show') + # Fix incoming multi-valued fields we created for the form + kw = ipahelper.fix_incoming_fields(kw, 'ipauserobjectclasses', 'userobjectclasses') + kw = ipahelper.fix_incoming_fields(kw, 'ipagroupobjectclasses', 'groupobjectclasses') + tg_errors, kw = self.ipapolicyupdatevalidate(**kw) if tg_errors: turbogears.flash("There were validation errors.
" + @@ -132,6 +146,12 @@ class IPAPolicyController(IPAController): if new_ipapolicy.ipadefaultprimarygroup != kw.get('ipadefaultprimarygroup'): policy_modified = True new_ipapolicy.setValue('ipadefaultprimarygroup', kw.get('ipadefaultprimarygroup')) + if new_ipapolicy.ipauserobjectclasses != kw.get('ipauserobjectclasses'): + policy_modified = True + new_ipapolicy.setValue('ipauserobjectclasses', kw.get('ipauserobjectclasses')) + if new_ipapolicy.ipagroupobjectclasses != kw.get('ipagroupobjectclasses'): + policy_modified = True + new_ipapolicy.setValue('ipagroupobjectclasses', kw.get('ipagroupobjectclasses')) if policy_modified: rv = client.update_ipa_config(new_ipapolicy) diff --git a/ipa-server/ipa-gui/ipagui/subcontrollers/principal.py b/ipa-server/ipa-gui/ipagui/subcontrollers/principal.py new file mode 100644 index 000000000..1b2ad6942 --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/subcontrollers/principal.py @@ -0,0 +1,153 @@ +import os +from pickle import dumps, loads +from base64 import b64encode, b64decode +import copy +import logging + +import cherrypy +import turbogears +from turbogears import controllers, expose, flash +from turbogears import validators, validate +from turbogears import widgets, paginate +from turbogears import error_handler +from turbogears import identity + +from ipacontroller import IPAController +from ipa.entity import utf8_encode_values +from ipa import ipaerror +import ipagui.forms.principal + +import ldap.dn + +log = logging.getLogger(__name__) + +principal_new_form = ipagui.forms.principal.PrincipalNewForm() +principal_fields = ['*'] + +class PrincipalController(IPAController): + + @expose() + @identity.require(identity.in_group("admins")) + def index(self, tg_errors=None): + raise turbogears.redirect("/principal/list") + + @expose("ipagui.templates.principalnew") + @identity.require(identity.in_group("admins")) + def new(self, tg_errors=None): + """Displays the new service principal form""" + if tg_errors: + turbogears.flash("There were validation errors.
" + + "Please see the messages below for details.") + + client = self.get_ipaclient() + + return dict(form=principal_new_form, principal={}) + + @expose() + @identity.require(identity.in_group("admins")) + def create(self, **kw): + """Creates a service principal group""" + self.restrict_post() + client = self.get_ipaclient() + + if kw.get('submit') == 'Cancel': + turbogears.flash("Add principal cancelled") + raise turbogears.redirect('/') + + tg_errors, kw = self.principalcreatevalidate(**kw) + if tg_errors: + turbogears.flash("There were validation errors.
" + + "Please see the messages below for details.") + return dict(form=principal_new_form, principal=kw, + tg_template='ipagui.templates.principalnew') + + principal_name = "" + hostname = kw.get('hostname') + # + # Create the principal itself + # + try: + if kw.get('service') == "other": + service = kw.get('other') + if not service: + turbogears.flash("Service type must be provided") + return dict(form=principal_new_form, principal=kw, + tg_template='ipagui.templates.principalnew') + else: + service = kw.get('service') + + # The realm is added by add_service_principal + principal_name = utf8_encode_values(service + "/" + kw.get('hostname')) + + rv = client.add_service_principal(principal_name) + except ipaerror.exception_for(ipaerror.LDAP_DUPLICATE): + turbogears.flash("Service principal '%s' already exists" % + principal_name) + return dict(form=principal_new_form, principal=kw, + tg_template='ipagui.templates.principalnew') + except ipaerror.IPAError, e: + turbogears.flash("Service principal add failed: " + str(e) + "
" + e.detail[0]['desc']) + return dict(form=principal_new_form, principal=kw, + tg_template='ipagui.templates.principalnew') + + turbogears.flash("%s added!" % principal_name) + raise turbogears.redirect('/principal/list', hostname=hostname) + + @expose("ipagui.templates.principallist") + @identity.require(identity.not_anonymous()) + def list(self, **kw): + """Searches for service principals and displays list of results""" + client = self.get_ipaclient() + + principals = None + counter = 0 + hostname = kw.get('hostname') + if hostname != None and len(hostname) > 0: + try: + principals = client.find_service_principal(hostname.encode('utf-8'), principal_fields, 0, 2) + counter = principals[0] + principals = principals[1:] + + if counter == -1: + turbogears.flash("These results are truncated.
" + + "Please refine your search and try again.") + + # For each entry break out service type and hostname + for i in range(len(principals)): + (service,host) = principals[i].krbprincipalname.split('/') + h = host.split('@') + principals[i].setValue('service', service) + principals[i].setValue('hostname', h[0]) + + except ipaerror.IPAError, e: + turbogears.flash("principal list failed: " + str(e) + "
" + e.detail[0]['desc']) + raise turbogears.redirect("/principal/list") + + return dict(principals=principals, hostname=hostname, fields=ipagui.forms.principal.PrincipalFields()) + + @expose() + @identity.require(identity.not_anonymous()) + def show(self, **kw): + """Returns the keytab for a given principal""" + client = self.get_ipaclient() + + principal = kw.get('principal') + if principal != None and len(principal) > 0: + try: + p = principal.split('@') + keytab = client.get_keytab(p[0].encode('utf-8')) + + cherrypy.response.headers['Content-Type'] = "application/x-download" + cherrypy.response.headers['Content-Disposition'] = 'attachment; filename=krb5.keytab' + cherrypy.response.headers['Content-Length'] = len(keytab) + cherrypy.response.body = keytab + return cherrypy.response.body + except ipaerror.IPAError, e: + turbogears.flash("keytab retrieval failed: " + str(e) + "
" + e.detail[0]['desc']) + raise turbogears.redirect("/principal/list") + raise turbogears.redirect("/principal/list") + + @validate(form=principal_new_form) + @identity.require(identity.not_anonymous()) + def principalcreatevalidate(self, tg_errors=None, **kw): + return tg_errors, kw diff --git a/ipa-server/ipa-gui/ipagui/subcontrollers/user.py b/ipa-server/ipa-gui/ipagui/subcontrollers/user.py index 39343b595..eda0966bb 100644 --- a/ipa-server/ipa-gui/ipagui/subcontrollers/user.py +++ b/ipa-server/ipa-gui/ipagui/subcontrollers/user.py @@ -18,6 +18,7 @@ from ipa.entity import utf8_encode_values from ipa import ipaerror import ipagui.forms.user import ipa.config +from ipagui.helpers import ipahelper log = logging.getLogger(__name__) @@ -83,36 +84,6 @@ class UserController(IPAController): user_new_form.validator.add_field(s['field'], validator) user_edit_form.validator.add_field(s['field'], validator) - def setup_mv_fields(self, field, fieldname): - """Given a field (must be a list) and field name, convert that - field into a list of dictionaries of the form: - [ { fieldname : v1}, { fieldname : v2 }, .. ] - - This is how we pre-fill values for multi-valued fields. - """ - mvlist = [] - if field is not None: - for v in field: - mvlist.append({ fieldname : v } ) - else: - # We need to return an empty value so something can be - # displayed on the edit page. Otherwise only an Add link - # will show, not an empty field. - mvlist.append({ fieldname : '' } ) - return mvlist - - def fix_incoming_fields(self, fields, fieldname, multifieldname): - """This is called by the update() function. It takes the incoming - list of dictionaries and converts it into back into the original - field, then removes the multiple field. - """ - fields[fieldname] = [] - for i in range(len(fields[multifieldname])): - fields[fieldname].append(fields[multifieldname][i][fieldname]) - del(fields[multifieldname]) - - return fields - @expose() def index(self): raise turbogears.redirect("/user/list") @@ -142,12 +113,12 @@ class UserController(IPAController): tg_errors, kw = self.usercreatevalidate(**kw) # Fix incoming multi-valued fields we created for the form - kw = self.fix_incoming_fields(kw, 'cn', 'cns') - kw = self.fix_incoming_fields(kw, 'telephonenumber', 'telephonenumbers') - kw = self.fix_incoming_fields(kw, 'facsimiletelephonenumber', 'facsimiletelephonenumbers') - kw = self.fix_incoming_fields(kw, 'mobile', 'mobiles') - kw = self.fix_incoming_fields(kw, 'pager', 'pagers') - kw = self.fix_incoming_fields(kw, 'homephone', 'homephones') + kw = ipahelper.fix_incoming_fields(kw, 'cn', 'cns') + kw = ipahelper.fix_incoming_fields(kw, 'telephonenumber', 'telephonenumbers') + kw = ipahelper.fix_incoming_fields(kw, 'facsimiletelephonenumber', 'facsimiletelephonenumbers') + kw = ipahelper.fix_incoming_fields(kw, 'mobile', 'mobiles') + kw = ipahelper.fix_incoming_fields(kw, 'pager', 'pagers') + kw = ipahelper.fix_incoming_fields(kw, 'homephone', 'homephones') if tg_errors: turbogears.flash("There were validation errors.
" + @@ -325,27 +296,27 @@ class UserController(IPAController): # Load potential multi-valued fields if isinstance(user_dict['cn'], str): user_dict['cn'] = [user_dict['cn']] - user_dict['cns'] = self.setup_mv_fields(user_dict['cn'], 'cn') + user_dict['cns'] = ipahelper.setup_mv_fields(user_dict['cn'], 'cn') if isinstance(user_dict.get('telephonenumber',''), str): - user_dict['telephonenumber'] = [user_dict.get('telephonenumber'),''] - user_dict['telephonenumbers'] = self.setup_mv_fields(user_dict.get('telephonenumber'), 'telephonenumber') + user_dict['telephonenumber'] = [user_dict.get('telephonenumber')] + user_dict['telephonenumbers'] = ipahelper.setup_mv_fields(user_dict.get('telephonenumber'), 'telephonenumber') if isinstance(user_dict.get('facsimiletelephonenumber',''), str): - user_dict['facsimiletelephonenumber'] = [user_dict.get('facsimiletelephonenumber'),''] - user_dict['facsimiletelephonenumbers'] = self.setup_mv_fields(user_dict.get('facsimiletelephonenumber'), 'facsimiletelephonenumber') + user_dict['facsimiletelephonenumber'] = [user_dict.get('facsimiletelephonenumber')] + user_dict['facsimiletelephonenumbers'] = ipahelper.setup_mv_fields(user_dict.get('facsimiletelephonenumber'), 'facsimiletelephonenumber') if isinstance(user_dict.get('mobile',''), str): - user_dict['mobile'] = [user_dict.get('mobile'),''] - user_dict['mobiles'] = self.setup_mv_fields(user_dict.get('mobile'), 'mobile') + user_dict['mobile'] = [user_dict.get('mobile')] + user_dict['mobiles'] = ipahelper.setup_mv_fields(user_dict.get('mobile'), 'mobile') if isinstance(user_dict.get('pager',''), str): - user_dict['pager'] = [user_dict.get('pager'),''] - user_dict['pagers'] = self.setup_mv_fields(user_dict.get('pager'), 'pager') + user_dict['pager'] = [user_dict.get('pager')] + user_dict['pagers'] = ipahelper.setup_mv_fields(user_dict.get('pager'), 'pager') if isinstance(user_dict.get('homephone',''), str): - user_dict['homephone'] = [user_dict.get('homephone'),''] - user_dict['homephones'] = self.setup_mv_fields(user_dict.get('homephone'), 'homephone') + user_dict['homephone'] = [user_dict.get('homephone')] + user_dict['homephones'] = ipahelper.setup_mv_fields(user_dict.get('homephone'), 'homephone') # Edit shouldn't fill in the password field. if user_dict.has_key('userpassword'): @@ -403,12 +374,12 @@ class UserController(IPAController): raise turbogears.redirect('/user/show', uid=kw.get('uid')) # Fix incoming multi-valued fields we created for the form - kw = self.fix_incoming_fields(kw, 'cn', 'cns') - kw = self.fix_incoming_fields(kw, 'telephonenumber', 'telephonenumbers') - kw = self.fix_incoming_fields(kw, 'facsimiletelephonenumber', 'facsimiletelephonenumbers') - kw = self.fix_incoming_fields(kw, 'mobile', 'mobiles') - kw = self.fix_incoming_fields(kw, 'pager', 'pagers') - kw = self.fix_incoming_fields(kw, 'homephone', 'homephones') + kw = ipahelper.fix_incoming_fields(kw, 'cn', 'cns') + kw = ipahelper.fix_incoming_fields(kw, 'telephonenumber', 'telephonenumbers') + kw = ipahelper.fix_incoming_fields(kw, 'facsimiletelephonenumber', 'facsimiletelephonenumbers') + kw = ipahelper.fix_incoming_fields(kw, 'mobile', 'mobiles') + kw = ipahelper.fix_incoming_fields(kw, 'pager', 'pagers') + kw = ipahelper.fix_incoming_fields(kw, 'homephone', 'homephones') # admins and editors can update anybody. A user can only update # themselves. We need this check because it is very easy to guess diff --git a/ipa-server/ipa-gui/ipagui/templates/delegateform.kid b/ipa-server/ipa-gui/ipagui/templates/delegateform.kid index 62cc710f0..4eb846d53 100644 --- a/ipa-server/ipa-gui/ipagui/templates/delegateform.kid +++ b/ipa-server/ipa-gui/ipagui/templates/delegateform.kid @@ -180,4 +180,10 @@ + + + + diff --git a/ipa-server/ipa-gui/ipagui/templates/groupnewform.kid b/ipa-server/ipa-gui/ipagui/templates/groupnewform.kid index 2b6e2ebb2..04a9a9e70 100644 --- a/ipa-server/ipa-gui/ipagui/templates/groupnewform.kid +++ b/ipa-server/ipa-gui/ipagui/templates/groupnewform.kid @@ -97,6 +97,10 @@ from ipagui.helpers import ipahelper + + +
+ + +
diff --git a/ipa-server/ipa-gui/ipagui/templates/ipapolicyshow.kid b/ipa-server/ipa-gui/ipagui/templates/ipapolicyshow.kid index 089fb494e..50c7d6d8a 100644 --- a/ipa-server/ipa-gui/ipagui/templates/ipapolicyshow.kid +++ b/ipa-server/ipa-gui/ipagui/templates/ipapolicyshow.kid @@ -15,6 +15,9 @@ edit_url = tg.url('/ipapolicy/edit')

Manage IPA Policy

+

Search

@@ -109,6 +112,46 @@ edit_url = tg.url('/ipapolicy/edit') + + + + + + + +
${ipapolicy.get("ipadefaultprimarygroup")}
+ + + + + + + + +
${values[index]}
+
+ + + + + + + + +
${values[index]}
+

Find Groups +