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