diff options
author | Karl MacMillan <kmacmill@redhat.com> | 2007-12-11 12:42:13 -0500 |
---|---|---|
committer | Karl MacMillan <kmacmill@redhat.com> | 2007-12-11 12:42:13 -0500 |
commit | d2378f13d0ce867175952346302d42c7a9a9fb2b (patch) | |
tree | adbff5e6e1715f855d08aea20e995c188dcdc248 /ipa-server/ipa-gui | |
parent | d53915954e68ad2fa1625ed016e7e65cd6f4e4e0 (diff) | |
parent | b75d735b7e15198fbc0e7baad582696a97f0d5ec (diff) | |
download | freeipa-d2378f13d0ce867175952346302d42c7a9a9fb2b.tar.gz freeipa-d2378f13d0ce867175952346302d42c7a9a9fb2b.tar.xz freeipa-d2378f13d0ce867175952346302d42c7a9a9fb2b.zip |
Merge.
Diffstat (limited to 'ipa-server/ipa-gui')
27 files changed, 712 insertions, 78 deletions
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/Makefile.am b/ipa-server/ipa-gui/ipagui/forms/Makefile.am index 4f1f72d22..a7f3c7623 100644 --- a/ipa-server/ipa-gui/ipagui/forms/Makefile.am +++ b/ipa-server/ipa-gui/ipagui/forms/Makefile.am @@ -7,6 +7,7 @@ app_PYTHON = \ ipapolicy.py \ user.py \ delegate.py \ + principal.py \ $(NULL) EXTRA_DIST = \ diff --git a/ipa-server/ipa-gui/ipagui/forms/group.py b/ipa-server/ipa-gui/ipagui/forms/group.py index fa3a0988a..afb63073e 100644 --- a/ipa-server/ipa-gui/ipagui/forms/group.py +++ b/ipa-server/ipa-gui/ipagui/forms/group.py @@ -41,11 +41,12 @@ class GroupNewForm(widgets.Form): class GroupEditValidator(validators.Schema): - cn = validators.String(not_empty=True) + cn = validators.String(not_empty=False) gidnumber = validators.Int(not_empty=False) description = validators.String(not_empty=False) pre_validators = [ + validators.RequireIfPresent(required='cn', present='editprotected'), validators.RequireIfPresent(required='gidnumber', present='editprotected'), ] diff --git a/ipa-server/ipa-gui/ipagui/forms/ipapolicy.py b/ipa-server/ipa-gui/ipagui/forms/ipapolicy.py index 78acac664..c48b8ced4 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,6 +13,11 @@ 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]) + ipadefaultemaildomain = widgets.TextField(name="ipadefaultemaildomain", label="Default E-mail Domain", attrs=dict(size=20)) ipapolicy_orig = widgets.HiddenField(name="ipapolicy_orig") @@ -34,6 +40,10 @@ 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)) + ipadefaultemaildomain = 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/forms/user.py b/ipa-server/ipa-gui/ipagui/forms/user.py index f1bf48365..b0c4d0aa7 100644 --- a/ipa-server/ipa-gui/ipagui/forms/user.py +++ b/ipa-server/ipa-gui/ipagui/forms/user.py @@ -11,7 +11,7 @@ class UserFields(object): displayname = widgets.TextField(name="displayname", label="Display Name") initials = widgets.TextField(name="initials", label="Initials") - uid = widgets.TextField(name="uid", label="Login") + uid = widgets.TextField(name="uid", label="Login", attrs=dict(onchange="warnRDN(this.id)")) userpassword = widgets.PasswordField(name="userpassword", label="Password") userpassword_confirm = widgets.PasswordField(name="userpassword_confirm", label="Confirm Password") @@ -56,9 +56,7 @@ class UserFields(object): label="Account Status", options = [("", "active"), ("true", "inactive")]) - uid_hidden = widgets.HiddenField(name="uid") - uidnumber_hidden = widgets.HiddenField(name="uidnumber") - gidnumber_hidden = widgets.HiddenField(name="gidnumber") + uid_hidden = widgets.HiddenField(name="uid_hidden") krbPasswordExpiration_hidden = widgets.HiddenField(name="krbPasswordExpiration") editprotected_hidden = widgets.HiddenField(name="editprotected") @@ -111,11 +109,12 @@ class UserEditValidator(validators.Schema): givenname = validators.String(not_empty=True) sn = validators.String(not_empty=True) cn = validators.ForEach(validators.String(not_empty=True)) - mail = validators.Email(not_empty=True) + mail = validators.Email(not_empty=False) uidnumber = validators.Int(not_empty=False) gidnumber = validators.Int(not_empty=False) pre_validators = [ + validators.RequireIfPresent(required='uid', present='editprotected'), validators.RequireIfPresent(required='uidnumber', present='editprotected'), validators.RequireIfPresent(required='gidnumber', present='editprotected'), ] 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/static/css/style.css b/ipa-server/ipa-gui/ipagui/static/css/style.css index 1a7cbb1fb..6d68e8e37 100644 --- a/ipa-server/ipa-gui/ipagui/static/css/style.css +++ b/ipa-server/ipa-gui/ipagui/static/css/style.css @@ -339,14 +339,18 @@ table.formtable td input[type="text"], input#criteria { border: 1px inset #dcdcdc; font-size: medium; padding: 2px 1px; +/* background-color: #f5faff; +*/ } table.formtable td select { border: 1px inset #dcdcdc; font-size: small; padding: 2px 1px; +/* background-color: #f5faff; +*/ } p.empty-message { @@ -402,3 +406,20 @@ ul.checkboxlist li input { .sortdesc { background-image: url(/static/images/down.gif) !important; } + +.warning_message { + font-size: 120%; +/* + color: #ee0000; +*/ + font-weight: bolder; +} + +.fielderror { + color: red !important; + font-weight: bold; +} + +.requiredfield { + background-color: #eebbbb !important; +} diff --git a/ipa-server/ipa-gui/ipagui/subcontrollers/Makefile.am b/ipa-server/ipa-gui/ipagui/subcontrollers/Makefile.am index a0c6393fe..4a7ff58df 100644 --- a/ipa-server/ipa-gui/ipagui/subcontrollers/Makefile.am +++ b/ipa-server/ipa-gui/ipagui/subcontrollers/Makefile.am @@ -9,6 +9,7 @@ app_PYTHON = \ policy.py \ user.py \ delegation.py \ + principal.py \ $(NULL) EXTRA_DIST = \ diff --git a/ipa-server/ipa-gui/ipagui/subcontrollers/group.py b/ipa-server/ipa-gui/ipagui/subcontrollers/group.py index dbcc77b9a..cc2944b22 100644 --- a/ipa-server/ipa-gui/ipagui/subcontrollers/group.py +++ b/ipa-server/ipa-gui/ipagui/subcontrollers/group.py @@ -271,14 +271,6 @@ class GroupController(IPAController): rv = client.update_group(new_group) # # If the group update succeeds, but below operations fail, we - if new_group.cn != kw.get('cn'): - group_modified = True - new_group.setValue('cn', kw['cn']) - - if group_modified: - rv = client.update_group(new_group) - # - # If the group update succeeds, but below operations fail, we # need to make sure a subsequent submit doesn't try to update # the group again. # @@ -313,7 +305,7 @@ class GroupController(IPAController): kw['dnadd'] = failed_adds group_modified = True except ipaerror.IPAError, e: - turbogears.flash("Group update failed: " + str(e) + "<br/>" + e.detail[0]['desc']) + turbogears.flash("Updating group membership failed: " + str(e) + "<br/>" + e.detail[0]['desc']) return dict(form=group_edit_form, group=kw, members=member_dicts, tg_template='ipagui.templates.groupedit') @@ -331,7 +323,7 @@ class GroupController(IPAController): kw['dndel'] = failed_dels group_modified = True except ipaerror.IPAError, e: - turbogears.flash("Group update failed: " + str(e) + "<br/>" + e.detail[0]['desc']) + turbogears.flash("Updating group membership failed: " + str(e) + "<br/>" + e.detail[0]['desc']) return dict(form=group_edit_form, group=kw, members=member_dicts, tg_template='ipagui.templates.groupedit') diff --git a/ipa-server/ipa-gui/ipagui/subcontrollers/ipapolicy.py b/ipa-server/ipa-gui/ipagui/subcontrollers/ipapolicy.py index a82b98888..267f9d3e1 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) + "<br/>" + 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.<br/>" + @@ -132,6 +146,15 @@ 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 new_ipapolicy.ipadefaultemaildomain != kw.get('ipadefaultemaildomain'): + policy_modified = True + new_ipapolicy.setValue('ipadefaultemaildomain', kw.get('ipadefaultemaildomain')) 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.<br/>" + + "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.<br/>" + + "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) + "<br/>" + 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.<br />" + + "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) + "<br/>" + 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) + "<br/>" + 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..952278a08 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__) @@ -28,14 +29,20 @@ user_edit_form = ipagui.forms.user.UserEditForm() user_fields = ['*', 'nsAccountLock'] -email_domain = ipa.config.config.default_realm.lower() - class UserController(IPAController): def __init__(self, *args, **kw): super(UserController,self).__init__(*args, **kw) # self.load_custom_fields() + def get_email_domain(self): + client = self.get_ipaclient() + + conf = client.get_ipa_config() + email_domain = conf.ipadefaultemaildomain + + return email_domain + def load_custom_fields(self): client = self.get_ipaclient() @@ -83,36 +90,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 +119,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.<br/>" + @@ -325,32 +302,34 @@ 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'): del(user_dict['userpassword']) + user_dict['uid_hidden'] = user_dict.get('uid') + user_groups = client.get_groups_by_member(user.dn, ['dn', 'cn']) user_groups.sort(self.sort_by_cn) user_groups_dicts = map(lambda group: group.toDict(), user_groups) @@ -398,17 +377,20 @@ class UserController(IPAController): self.restrict_post() client = self.get_ipaclient() + if not kw.get('uid'): + kw['uid'] = kw.get('uid_hidden') + if kw.get('submit') == 'Cancel Edit': turbogears.flash("Edit user cancelled") 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 @@ -430,6 +412,12 @@ class UserController(IPAController): user_groups=user_groups_dicts, tg_template='ipagui.templates.useredit') + # We don't want to inadvertantly add this to a record + try: + del kw['uid_hidden'] + except KeyError: + pass + password_change = False user_modified = False @@ -488,6 +476,7 @@ class UserController(IPAController): new_user.setValue('uidnumber', str(kw.get('uidnumber'))) new_user.setValue('gidnumber', str(kw.get('gidnumber'))) new_user.setValue('homedirectory', str(kw.get('homedirectory'))) + new_user.setValue('uid', str(kw.get('uid'))) for custom_field in user_edit_form.custom_fields: new_user.setValue(custom_field.name, @@ -750,13 +739,13 @@ class UserController(IPAController): givenname = givenname.lower() sn = sn.lower() - email = "%s.%s@%s" % (givenname, sn, email_domain) + email = "%s.%s@%s" % (givenname, sn, self.get_email_domain()) try: client.get_user_by_email(email) except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): return email - email = "%s@%s" % (self.suggest_uid(givenname, sn), email_domain) + email = "%s@%s" % (self.suggest_uid(givenname, sn), self.get_email_domain()) try: client.get_user_by_email(email) except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): diff --git a/ipa-server/ipa-gui/ipagui/templates/Makefile.am b/ipa-server/ipa-gui/ipagui/templates/Makefile.am index 4bf8f142d..279b13d43 100644 --- a/ipa-server/ipa-gui/ipagui/templates/Makefile.am +++ b/ipa-server/ipa-gui/ipagui/templates/Makefile.am @@ -27,6 +27,10 @@ app_DATA = \ master.kid \ policyindex.kid \ policylayout.kid \ + principallayout.kid \ + principallist.kid \ + principalnewform.kid \ + principalnew.kid \ usereditform.kid \ useredit.kid \ userlayout.kid \ 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 @@ </script> </form> + + + <script type="text/javascript"> + document.getElementById("form_name").focus(); + </script> + </div> 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 @@ -98,6 +98,10 @@ from ipagui.helpers import ipahelper </form> <script type="text/javascript"> + document.getElementById("form_cn").focus(); + </script> + + <script type="text/javascript"> /* * This section restores the contents of the add and remove lists * dynamically if we have to refresh the page diff --git a/ipa-server/ipa-gui/ipagui/templates/ipapolicyeditform.kid b/ipa-server/ipa-gui/ipagui/templates/ipapolicyeditform.kid index 106657636..5114943c0 100644 --- a/ipa-server/ipa-gui/ipagui/templates/ipapolicyeditform.kid +++ b/ipa-server/ipa-gui/ipagui/templates/ipapolicyeditform.kid @@ -15,6 +15,8 @@ from ipagui.helpers import ipahelper <script type="text/javascript" charset="utf-8" src="${tg.url('/static/javascript/dynamicedit.js')}"></script> + <script type="text/javascript" charset="utf-8" + src="${tg.url('/tg_widgets/tg_expanding_form_widget/javascript/expanding_form.js')}"></script> <div py:for="field in hidden_fields" py:replace="field.display(value_for(field), **params_for(field))" @@ -170,7 +172,90 @@ from ipagui.helpers import ipahelper py:content="tg.errors.get('ipadefaultprimarygroup')" /> </td> </tr> + <tr> + <th> + <label class="fieldlabel" py:content="ipapolicy_fields.ipadefaultemaildomain.label" />: + </th> + <td> + <span py:replace="ipapolicy_fields.ipadefaultemaildomain.display(value_for(ipapolicy_fields.ipadefaultemaildomain))" /> + <span py:if="tg.errors.get('ipadefaultemaildomain')" class="fielderror" + py:content="tg.errors.get('ipadefaultemaildomain')" /> + </td> + </tr> + <tr> + <th> + <label class="fieldlabel" for="${ipapolicy_fields.userobjectclasses.field_id}" + py:content="ipapolicy_fields.userobjectclasses.label" />: + </th> + <td colspan="3"> + <table class="formtable" cellpadding="2" cellspacing="0" border="0" id="${ipapolicy_fields.userobjectclasses.field_id}"> + <tbody> + <?python repetition = 0 + fld_index = 0 + fld_error = tg.errors.get('ipauserobjectclasses') + ?> + <tr py:for="fld in value_for(ipapolicy_fields.ipauserobjectclasses)" + id="${ipapolicy_fields.userobjectclasses.field_id}_${repetition}" + class="${ipapolicy_fields.userobjectclasses.field_class}"> + + <td py:for="field in ipapolicy_fields.userobjectclasses.fields"> + <span><input class="textfield" type="text" id="${ipapolicy_fields.userobjectclasses.field_id}_${repetition}_ipauserobjectclasses" name="userobjectclasses-${repetition}.ipauserobjectclasses" value="${fld}"/></span> + <span py:if="fld_error and fld_error[fld_index]" class="fielderror" + py:content="tg.errors.get('ipauserobjectclasses')" /> + </td> + <?python fld_index = fld_index + 1 ?> + <td> + <a + href="javascript:ExpandingForm.removeItem('${ipapolicy_fields.userobjectclasses.field_id}_${repetition}')">Remove</a> + </td> + <?python repetition = repetition + 1?> + </tr> + </tbody> + </table> + <a id="${ipapolicy_fields.userobjectclasses.field_id}_doclink" href="javascript:ExpandingForm.addItem('${ipapolicy_fields.userobjectclasses.field_id}');">Add User Object Class</a> + </td> + </tr> + <tr> + <th> + <label class="fieldlabel" for="${ipapolicy_fields.groupobjectclasses.field_id}" + py:content="ipapolicy_fields.groupobjectclasses.label" />: + </th> + <td colspan="3"> + <table class="formtable" cellpadding="2" cellspacing="0" border="0" id="${ipapolicy_fields.groupobjectclasses.field_id}"> + <tbody> + <?python repetition = 0 + fld_index = 0 + fld_error = tg.errors.get('ipagroupobjectclasses') + ?> + <tr py:for="fld in value_for(ipapolicy_fields.ipagroupobjectclasses)" + id="${ipapolicy_fields.groupobjectclasses.field_id}_${repetition}" + class="${ipapolicy_fields.groupobjectclasses.field_class}"> + + <td py:for="field in ipapolicy_fields.groupobjectclasses.fields"> + <span><input class="textfield" type="text" id="${ipapolicy_fields.groupobjectclasses.field_id}_${repetition}_ipagroupobjectclasses" name="groupobjectclasses-${repetition}.ipagroupobjectclasses" value="${fld}"/></span> + <span py:if="fld_error and fld_error[fld_index]" class="fielderror" + py:content="tg.errors.get('ipagroupobjectclasses')" /> + </td> + <?python fld_index = fld_index + 1 ?> + <td> + <a + href="javascript:ExpandingForm.removeItem('${ipapolicy_fields.groupobjectclasses.field_id}_${repetition}')">Remove</a> + </td> + <?python repetition = repetition + 1?> + </tr> + </tbody> + </table> + <a id="${ipapolicy_fields.groupobjectclasses.field_id}_doclink" href="javascript:ExpandingForm.addItem('${ipapolicy_fields.groupobjectclasses.field_id}');">Add Group Object Class</a> + </td> + </tr> </table> + + <hr/> + + <input type="submit" class="submitbutton" name="submit" + value="Update Policy"/> + <input type="submit" class="submitbutton" name="submit" + value="Cancel Edit" /> </form> </div> diff --git a/ipa-server/ipa-gui/ipagui/templates/ipapolicyshow.kid b/ipa-server/ipa-gui/ipagui/templates/ipapolicyshow.kid index 089fb494e..26621eed6 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') <script type="text/javascript" charset="utf-8" src="${tg.url('/static/javascript/tablekit.js')}"></script> <h1>Manage IPA Policy</h1> + <input class="submitbutton" type="button" + onclick="document.location.href='${edit_url}'" + value="Edit Policy" /> <h2 class="formsection">Search</h2> <table class="formtable" cellpadding="2" cellspacing="0" border="0"> @@ -109,6 +112,52 @@ edit_url = tg.url('/ipapolicy/edit') </th> <td>${ipapolicy.get("ipadefaultprimarygroup")}</td> </tr> + <tr> + <th> + <label class="fieldlabel" py:content="fields.ipadefaultemaildomain.label" />: + </th> + <td>${ipapolicy.get("ipadefaultemaildomain")}</td> + </tr> + <tr> + <th> + <label class="fieldlabel" py:content="fields.ipauserobjectclasses.label" />: + </th> + <td> + <table cellpadding="2" cellspacing="0" border="0"> + <tbody> + <?python + index = 0 + values = ipapolicy.get("ipauserobjectclasses", '') + if isinstance(values, str): + values = [values] + ?> + <tr py:for="index in range(len(values))"> + <td>${values[index]}</td> + </tr> + </tbody> + </table> + </td> + </tr> + <tr> + <th> + <label class="fieldlabel" py:content="fields.ipagroupobjectclasses.label" />: + </th> + <td> + <table cellpadding="2" cellspacing="0" border="0"> + <tbody> + <?python + index = 0 + values = ipapolicy.get("ipagroupobjectclasses", '') + if isinstance(values, str): + values = [values] + ?> + <tr py:for="index in range(len(values))"> + <td>${values[index]}</td> + </tr> + </tbody> + </table> + </td> + </tr> </table> <hr /> <input class="submitbutton" type="button" diff --git a/ipa-server/ipa-gui/ipagui/templates/master.kid b/ipa-server/ipa-gui/ipagui/templates/master.kid index 12e54fa1d..e11497546 100644 --- a/ipa-server/ipa-gui/ipagui/templates/master.kid +++ b/ipa-server/ipa-gui/ipagui/templates/master.kid @@ -78,10 +78,14 @@ <li><a href="${tg.url('/group/list')}">Find Groups</a></li> </ul> <ul py:if="'admins' in tg.identity.groups"> + <li><a href="${tg.url('/principal/new')}">Add Service Principal</a></li> + <li><a href="${tg.url('/principal/list')}">Find Service Principal</a></li> + </ul> + <ul py:if="'admins' in tg.identity.groups"> <li><a href="${tg.url('/policy/index')}">Manage Policy</a></li> </ul> <ul> - <li><a href="${tg.url('/user/edit/', principal=tg.identity.user.display_name)}">Self Service</a></li> + <li py:if="not tg.identity.anonymous"><a href="${tg.url('/user/edit/', principal=tg.identity.user.display_name)}">Self Service</a></li> </ul> <ul py:if="'admins' in tg.identity.groups"> <li><a href="${tg.url('/delegate/list')}">Delegations</a></li> diff --git a/ipa-server/ipa-gui/ipagui/templates/principallayout.kid b/ipa-server/ipa-gui/ipagui/templates/principallayout.kid new file mode 100644 index 000000000..1e7bef2e7 --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/templates/principallayout.kid @@ -0,0 +1,19 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" + py:extends="'master.kid'"> +<head> +</head> + +<body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'" py:attrs="item.items()"> + <div id="main_content"> + <div id="details"> + <div id="alertbox" py:if="value_of('tg_flash', None)"> + <p py:content="XML(tg_flash)"></p></div> + + <div py:replace="[item.text]+item[:]"></div> + </div> + + </div> +</body> + +</html> diff --git a/ipa-server/ipa-gui/ipagui/templates/principallist.kid b/ipa-server/ipa-gui/ipagui/templates/principallist.kid new file mode 100644 index 000000000..dcd9dd4b8 --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/templates/principallist.kid @@ -0,0 +1,64 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" + py:extends="'principallayout.kid'"> +<head> +<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/> +<title>Find Service Principals</title> +</head> +<body> + <h1>Find Service Principals</h1> + <script type="text/javascript" charset="utf-8" src="${tg.url('/static/javascript/tablekit.js')}"></script> + <div id="search"> + <form action="${tg.url('/principal/list')}" method="get"> + <input id="hostname" type="text" name="hostname" value="${hostname}" /> + <input class="searchbutton" type="submit" value="Find Hosts"/> + </form> + <script type="text/javascript"> + document.getElementById("hostname").focus(); + </script> + </div> + <div py:if='(principals != None) and (len(principals) > 0)'> + <h2>${len(principals)} results returned:</h2> + <table id="resultstable" class="details sortable resizable" cellspacing="0"> + <thead> + <tr> + <th> + Hostname + </th> + <th> + Service + </th> + </tr> + </thead> + <tbody> + <tr py:for="principal in principals"> + <td> + <a href="${tg.url('/principal/show',principal=principal.krbprincipalname)}" + >${principal.hostname}</a> + </td> + <td> + ${principal.service} + </td> + </tr> + </tbody> + </table> + </div> + <div id="alertbox" py:if='(principals != None) and (len(principals) == 0)'> + <p id="alertbox">No results found for "${hostname}"</p> + </div> + + <div class="instructions" py:if='principals == None'> + <p> + Exact matches are listed first, followed by partial matches. If your search + is too broad, you will get a warning that the search returned too many + results. Try being more specific. + </p> + <p> + The results that come back are sortable. Simply click on a column + header to sort on that header. A triangle will indicate the sorted + column, along with its direction. Clicking and dragging between headers + will allow you to resize the header. + </p> + </div> +</body> +</html> diff --git a/ipa-server/ipa-gui/ipagui/templates/principalnew.kid b/ipa-server/ipa-gui/ipagui/templates/principalnew.kid new file mode 100644 index 000000000..b4ba3179c --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/templates/principalnew.kid @@ -0,0 +1,13 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" + py:extends="'principallayout.kid'"> +<head> + <meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/> + <title>Add Service Principal</title> +</head> +<body> + <h1>Add Service Principal</h1> + + ${form.display(action=tg.url('/principal/create'), value=principal)} +</body> +</html> diff --git a/ipa-server/ipa-gui/ipagui/templates/principalnewform.kid b/ipa-server/ipa-gui/ipagui/templates/principalnewform.kid new file mode 100644 index 000000000..25b54621e --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/templates/principalnewform.kid @@ -0,0 +1,102 @@ +<div xmlns:py="http://purl.org/kid/ns#" + class="simpleroster"> + <form action="${action}" name="${name}" method="${method}" class="tableform" + onsubmit="preSubmit()" > + + <input type="submit" class="submitbutton" name="submit" value="Add Principal"/> + +<?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('/principal/edit_search') ?> + + <script type="text/javascript"> + function toggleOther(field) { + otherField = document.getElementById('form_other'); + var e=document.getElementById(field).value; + if ( e == "other") { + otherField.disabled = false; + } else { + otherField.disabled =true; + } + } + + function doSearch() { + $('searchresults').update("Searching..."); + new Ajax.Updater('searchresults', + '${searchurl}', + { asynchronous:true, + parameters: { criteria: $('criteria').value }, + evalScripts: true }); + return false; + } + </script> + + <div py:for="field in hidden_fields" + py:replace="field.display(value_for(field), **params_for(field))" + /> + + <h2 class="formsection">Service Principal Details</h2> + <table class="formtable" cellpadding="2" cellspacing="0" border="0"> + <tr> + <th> + <label class="fieldlabel" for="${principal_fields.hostname.field_id}" + py:content="principal_fields.hostname.label" />: + </th> + <td> + <span py:replace="principal_fields.hostname.display(value_for(principal_fields.hostname))" /> + <span py:if="tg.errors.get('hostname')" class="fielderror" + py:content="tg.errors.get('hostname')" /> + + </td> + </tr> + + <tr> + <th> + <label class="fieldlabel" for="${principal_fields.service.field_id}" + py:content="principal_fields.service.label" />: + </th> + <td> + <span py:replace="principal_fields.service.display(value_for(principal_fields.service))" /> + <span py:if="tg.errors.get('service')" class="fielderror" + py:content="tg.errors.get('service')" /> + + </td> + </tr> + + <tr> + <th> + <label class="fieldlabel" for="${principal_fields.other.field_id}" + py:content="principal_fields.other.label" />: + </th> + <td> + <span py:replace="principal_fields.other.display(value_for(principal_fields.other))" /> + <span py:if="tg.errors.get('other')" class="fielderror" + py:content="tg.errors.get('other')" /> + <script type="text/javascript"> + var e=document.getElementById('form_service').value; + if ( e != "other") { + document.getElementById('form_other').disabled = true; + } + </script> + + </td> + </tr> + + </table> + +<hr /> + + <input type="submit" class="submitbutton" name="submit" value="Add Principal"/> + + </form> + + <script type="text/javascript"> + document.getElementById("form_hostname").focus(); + </script> + +</div> diff --git a/ipa-server/ipa-gui/ipagui/templates/usereditform.kid b/ipa-server/ipa-gui/ipagui/templates/usereditform.kid index 88b778d8c..5bf533432 100644 --- a/ipa-server/ipa-gui/ipagui/templates/usereditform.kid +++ b/ipa-server/ipa-gui/ipagui/templates/usereditform.kid @@ -38,12 +38,14 @@ from ipagui.helpers import ipahelper function toggleProtectedFields(checkbox) { passwordField = document.getElementById('form_userpassword'); passwordConfirmField = document.getElementById('form_userpassword_confirm'); + uidField = document.getElementById('form_uid'); uidnumberField = document.getElementById('form_uidnumber'); gidnumberField = document.getElementById('form_gidnumber'); homedirectoryField = document.getElementById('form_homedirectory'); if (checkbox.checked) { passwordField.disabled = false; passwordConfirmField.disabled = false; + uidField.disabled = false; uidnumberField.disabled = false; gidnumberField.disabled = false; homedirectoryField.disabled = false; @@ -51,6 +53,7 @@ from ipagui.helpers import ipahelper } else { passwordField.disabled = true; passwordConfirmField.disabled = true; + uidField.disabled = true; uidnumberField.disabled = true; gidnumberField.disabled = true; homedirectoryField.disabled = true; @@ -58,6 +61,13 @@ from ipagui.helpers import ipahelper } } + function warnRDN() { + if (confirm("Are you sure you want to change the login name?<br/>This can have unexpected results. A password change is required.")) { + return true; + } + return false; + } + function doSearch() { $('searchresults').update("Searching..."); new Ajax.Updater('searchresults', @@ -215,13 +225,21 @@ from ipagui.helpers import ipahelper py:content="tg.errors.get('nsAccountLock')" /> </td> </tr> + <tr> <th> <label class="fieldlabel" for="${user_fields.uid.field_id}" py:content="user_fields.uid.label" />: </th> <td> - ${value_for(user_fields.uid)} + <span py:replace="user_fields.uid.display( + value_for(user_fields.uid))" /> + <span py:if="tg.errors.get('uid')" class="fielderror" + py:content="tg.errors.get('uid')" /> + + <script type="text/javascript"> + document.getElementById('form_uid').disabled = true; + </script> </td> </tr> diff --git a/ipa-server/ipa-gui/ipagui/templates/usernewform.kid b/ipa-server/ipa-gui/ipagui/templates/usernewform.kid index 97be52732..83dc64634 100644 --- a/ipa-server/ipa-gui/ipagui/templates/usernewform.kid +++ b/ipa-server/ipa-gui/ipagui/templates/usernewform.kid @@ -791,6 +791,10 @@ from ipagui.helpers import ipahelper </form> <script type="text/javascript"> + document.getElementById("form_title").focus(); + </script> + + <script type="text/javascript"> /* * This section restores the contents of the add and remove lists * dynamically if we have to refresh the page diff --git a/ipa-server/ipa-gui/ipagui/templates/usershow.kid b/ipa-server/ipa-gui/ipagui/templates/usershow.kid index 614988729..053721230 100644 --- a/ipa-server/ipa-gui/ipagui/templates/usershow.kid +++ b/ipa-server/ipa-gui/ipagui/templates/usershow.kid @@ -11,7 +11,7 @@ edit_url = tg.url('/user/edit', uid=user.get('uid')) ?> <h1>View User</h1> - <input py:if="'editors' in tg.identity.groups or 'admins' in tg.identity.groups" + <input py:if="'editors' in tg.identity.groups or 'admins' in tg.identity.groups or tg.identity.display_name == user.get('uid')" class="submitbutton" type="button" onclick="document.location.href='${edit_url}'" value="Edit User" /> @@ -374,7 +374,7 @@ else: <br/> <hr /> - <input py:if="'editors' in tg.identity.groups or 'admins' in tg.identity.groups" + <input py:if="'editors' in tg.identity.groups or 'admins' in tg.identity.groups or tg.identity.display_name == user.get('uid')" class="submitbutton" type="button" onclick="document.location.href='${edit_url}'" value="Edit User" /> |