summaryrefslogtreecommitdiffstats
path: root/ipa-server/ipa-gui
diff options
context:
space:
mode:
authorKarl MacMillan <kmacmill@redhat.com>2007-12-11 12:42:13 -0500
committerKarl MacMillan <kmacmill@redhat.com>2007-12-11 12:42:13 -0500
commitd2378f13d0ce867175952346302d42c7a9a9fb2b (patch)
treeadbff5e6e1715f855d08aea20e995c188dcdc248 /ipa-server/ipa-gui
parentd53915954e68ad2fa1625ed016e7e65cd6f4e4e0 (diff)
parentb75d735b7e15198fbc0e7baad582696a97f0d5ec (diff)
downloadfreeipa-d2378f13d0ce867175952346302d42c7a9a9fb2b.tar.gz
freeipa-d2378f13d0ce867175952346302d42c7a9a9fb2b.tar.xz
freeipa-d2378f13d0ce867175952346302d42c7a9a9fb2b.zip
Merge.
Diffstat (limited to 'ipa-server/ipa-gui')
-rw-r--r--ipa-server/ipa-gui/ipagui/controllers.py2
-rw-r--r--ipa-server/ipa-gui/ipagui/forms/Makefile.am1
-rw-r--r--ipa-server/ipa-gui/ipagui/forms/group.py3
-rw-r--r--ipa-server/ipa-gui/ipagui/forms/ipapolicy.py10
-rw-r--r--ipa-server/ipa-gui/ipagui/forms/principal.py39
-rw-r--r--ipa-server/ipa-gui/ipagui/forms/user.py9
-rw-r--r--ipa-server/ipa-gui/ipagui/helpers/ipahelper.py31
-rw-r--r--ipa-server/ipa-gui/ipagui/proxyprovider.py2
-rw-r--r--ipa-server/ipa-gui/ipagui/static/css/style.css21
-rw-r--r--ipa-server/ipa-gui/ipagui/subcontrollers/Makefile.am1
-rw-r--r--ipa-server/ipa-gui/ipagui/subcontrollers/group.py12
-rw-r--r--ipa-server/ipa-gui/ipagui/subcontrollers/ipapolicy.py23
-rw-r--r--ipa-server/ipa-gui/ipagui/subcontrollers/principal.py153
-rw-r--r--ipa-server/ipa-gui/ipagui/subcontrollers/user.py103
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/Makefile.am4
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/delegateform.kid6
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/groupnewform.kid4
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/ipapolicyeditform.kid85
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/ipapolicyshow.kid49
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/master.kid6
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/principallayout.kid19
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/principallist.kid64
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/principalnew.kid13
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/principalnewform.kid102
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/usereditform.kid20
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/usernewform.kid4
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/usershow.kid4
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" />