mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Merge.
This commit is contained in:
commit
ca118de76c
@ -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:
|
||||
|
@ -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()
|
||||
|
@ -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'})
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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.")
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
|
39
ipa-server/ipa-gui/ipagui/forms/principal.py
Normal file
39
ipa-server/ipa-gui/ipagui/forms/principal.py
Normal file
@ -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)
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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,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)
|
||||
|
153
ipa-server/ipa-gui/ipagui/subcontrollers/principal.py
Normal file
153
ipa-server/ipa-gui/ipagui/subcontrollers/principal.py
Normal file
@ -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
|
@ -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.<br/>" +
|
||||
@ -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
|
||||
|
@ -180,4 +180,10 @@
|
||||
</script>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById("form_name").focus();
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
@ -97,6 +97,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
|
||||
|
@ -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,80 @@ from ipagui.helpers import ipahelper
|
||||
py:content="tg.errors.get('ipadefaultprimarygroup')" />
|
||||
</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>
|
||||
|
@ -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,46 @@ edit_url = tg.url('/ipapolicy/edit')
|
||||
</th>
|
||||
<td>${ipapolicy.get("ipadefaultprimarygroup")}</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"
|
||||
|
@ -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>
|
||||
|
19
ipa-server/ipa-gui/ipagui/templates/principallayout.kid
Normal file
19
ipa-server/ipa-gui/ipagui/templates/principallayout.kid
Normal file
@ -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>
|
64
ipa-server/ipa-gui/ipagui/templates/principallist.kid
Normal file
64
ipa-server/ipa-gui/ipagui/templates/principallist.kid
Normal file
@ -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>
|
13
ipa-server/ipa-gui/ipagui/templates/principalnew.kid
Normal file
13
ipa-server/ipa-gui/ipagui/templates/principalnew.kid
Normal file
@ -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>
|
102
ipa-server/ipa-gui/ipagui/templates/principalnewform.kid
Normal file
102
ipa-server/ipa-gui/ipagui/templates/principalnewform.kid
Normal file
@ -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>
|
@ -790,6 +790,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
|
||||
|
@ -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" />
|
||||
|
@ -27,11 +27,15 @@ attributetypes: ( 2.16.840.1.113730.3.8.1.7 NAME 'ipaDefaultLoginShell' EQUALITY
|
||||
attributetypes: ( 2.16.840.1.113730.3.8.1.8 NAME 'ipaDefaultPrimaryGroup' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE)
|
||||
## ipaMaxUsernameLength - maximum username length to allow in the UI
|
||||
attributetypes: ( 2.16.840.1.113730.3.8.1.9 NAME 'ipaMaxUsernameLength' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE)
|
||||
## ipaPwdExpAdvNotify - time in days to send out paswwrod expiration notification before passwpord actually expires
|
||||
## ipaPwdExpAdvNotify - time in days to send out paswword expiration notification before passwpord actually expires
|
||||
attributetypes: ( 2.16.840.1.113730.3.8.1.10 NAME 'ipaPwdExpAdvNotify' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE)
|
||||
# ipaUserObjectClasses - required objectclasses for users
|
||||
attributetypes: ( 2.16.840.1.113730.3.8.1.11 NAME 'ipaUserObjectClasses' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27)
|
||||
# ipaGroupObjectClasses - required objectclasses for groups
|
||||
attributetypes: ( 2.16.840.1.113730.3.8.1.12 NAME 'ipaGroupObjectClasses' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27)
|
||||
###############################################
|
||||
##
|
||||
## ObjectClasses
|
||||
##
|
||||
## ipaGuiConfig - GUI config parameters objectclass
|
||||
objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify ) )
|
||||
objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses) )
|
||||
|
@ -14,7 +14,7 @@ krbMinPwdLife: 3600
|
||||
krbPwdMinDiffChars: 0
|
||||
krbPwdMinLength: 8
|
||||
krbPwdHistoryLength: 0
|
||||
krbMaxPwdLife: 864000
|
||||
krbMaxPwdLife: 7776000
|
||||
|
||||
dn: cn=users,cn=accounts,$SUFFIX
|
||||
changetype: add
|
||||
|
@ -18,16 +18,15 @@
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
import subprocess
|
||||
import string
|
||||
import tempfile
|
||||
import shutil
|
||||
import logging
|
||||
import pwd
|
||||
import glob
|
||||
import sys
|
||||
import os
|
||||
|
||||
from ipa import ipautil
|
||||
|
||||
from ipa.ipautil import *
|
||||
import service
|
||||
import installutils
|
||||
|
||||
@ -36,7 +35,7 @@ SERVER_ROOT_32 = "/usr/lib/dirsrv"
|
||||
|
||||
def ldap_mod(fd, dn, pwd):
|
||||
args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", "-D", dn, "-w", pwd, "-f", fd.name]
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
|
||||
def realm_to_suffix(realm_name):
|
||||
s = realm_name.split(".")
|
||||
@ -44,7 +43,7 @@ def realm_to_suffix(realm_name):
|
||||
return ",".join(terms)
|
||||
|
||||
def find_server_root():
|
||||
if dir_exists(SERVER_ROOT_64):
|
||||
if ipautil.dir_exists(SERVER_ROOT_64):
|
||||
return SERVER_ROOT_64
|
||||
else:
|
||||
return SERVER_ROOT_32
|
||||
@ -83,7 +82,7 @@ def check_existing_installation():
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
run(["/sbin/service", "dirsrv", "stop"])
|
||||
ipautil.run(["/sbin/service", "dirsrv", "stop"])
|
||||
except:
|
||||
pass
|
||||
for d in dirs:
|
||||
@ -185,25 +184,25 @@ class DsInstance(service.Service):
|
||||
logging.debug("adding ds user %s" % self.ds_user)
|
||||
args = ["/usr/sbin/useradd", "-c", "DS System User", "-d", "/var/lib/dirsrv", "-M", "-r", "-s", "/sbin/nologin", self.ds_user]
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("done adding user")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("failed to add user %s" % e)
|
||||
|
||||
def __create_instance(self):
|
||||
self.step("creating directory server instance")
|
||||
inf_txt = template_str(INF_TEMPLATE, self.sub_dict)
|
||||
inf_txt = ipautil.template_str(INF_TEMPLATE, self.sub_dict)
|
||||
logging.debug(inf_txt)
|
||||
inf_fd = write_tmp_file(inf_txt)
|
||||
inf_fd = ipautil.write_tmp_file(inf_txt)
|
||||
logging.debug("writing inf template")
|
||||
if file_exists("/usr/sbin/setup-ds.pl"):
|
||||
if ipautil.file_exists("/usr/sbin/setup-ds.pl"):
|
||||
args = ["/usr/sbin/setup-ds.pl", "--silent", "--logfile", "-", "-f", inf_fd.name]
|
||||
logging.debug("calling setup-ds.pl")
|
||||
else:
|
||||
args = ["/usr/bin/ds_newinst.pl", inf_fd.name]
|
||||
logging.debug("calling ds_newinst.pl")
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("completed creating ds instance")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("failed to restart ds instance %s" % e)
|
||||
@ -217,19 +216,19 @@ class DsInstance(service.Service):
|
||||
|
||||
def __add_default_schemas(self):
|
||||
self.step("adding default schema")
|
||||
shutil.copyfile(SHARE_DIR + "60kerberos.ldif",
|
||||
shutil.copyfile(ipautil.SHARE_DIR + "60kerberos.ldif",
|
||||
schema_dirname(self.realm_name) + "60kerberos.ldif")
|
||||
shutil.copyfile(SHARE_DIR + "60samba.ldif",
|
||||
shutil.copyfile(ipautil.SHARE_DIR + "60samba.ldif",
|
||||
schema_dirname(self.realm_name) + "60samba.ldif")
|
||||
shutil.copyfile(SHARE_DIR + "60radius.ldif",
|
||||
shutil.copyfile(ipautil.SHARE_DIR + "60radius.ldif",
|
||||
schema_dirname(self.realm_name) + "60radius.ldif")
|
||||
shutil.copyfile(SHARE_DIR + "60ipaconfig.ldif",
|
||||
shutil.copyfile(ipautil.SHARE_DIR + "60ipaconfig.ldif",
|
||||
schema_dirname(self.realm_name) + "60ipaconfig.ldif")
|
||||
|
||||
def __add_memberof_module(self):
|
||||
self.step("enabling memboerof plugin")
|
||||
memberof_txt = template_file(SHARE_DIR + "memberof-conf.ldif", self.sub_dict)
|
||||
memberof_fd = write_tmp_file(memberof_txt)
|
||||
memberof_txt = ipautil.template_file(ipautil.SHARE_DIR + "memberof-conf.ldif", self.sub_dict)
|
||||
memberof_fd = ipautil.write_tmp_file(memberof_txt)
|
||||
try:
|
||||
ldap_mod(memberof_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -238,8 +237,8 @@ class DsInstance(service.Service):
|
||||
|
||||
def __init_memberof(self):
|
||||
self.step("initializing group membership")
|
||||
memberof_txt = template_file(SHARE_DIR + "memberof-task.ldif", self.sub_dict)
|
||||
memberof_fd = write_tmp_file(memberof_txt)
|
||||
memberof_txt = ipautil.template_file(ipautil.SHARE_DIR + "memberof-task.ldif", self.sub_dict)
|
||||
memberof_fd = ipautil.write_tmp_file(memberof_txt)
|
||||
try:
|
||||
ldap_mod(memberof_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -248,8 +247,8 @@ class DsInstance(service.Service):
|
||||
|
||||
def __add_referint_module(self):
|
||||
self.step("enabling referential integrity plugin")
|
||||
referint_txt = template_file(SHARE_DIR + "referint-conf.ldif", self.sub_dict)
|
||||
referint_fd = write_tmp_file(referint_txt)
|
||||
referint_txt = ipautil.template_file(ipautil.SHARE_DIR + "referint-conf.ldif", self.sub_dict)
|
||||
referint_fd = ipautil.write_tmp_file(referint_txt)
|
||||
try:
|
||||
ldap_mod(referint_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -258,8 +257,8 @@ class DsInstance(service.Service):
|
||||
|
||||
def __add_dna_module(self):
|
||||
self.step("enabling distributed numeric assignment plugin")
|
||||
dna_txt = template_file(SHARE_DIR + "dna-conf.ldif", self.sub_dict)
|
||||
dna_fd = write_tmp_file(dna_txt)
|
||||
dna_txt = ipautil.template_file(ipautil.SHARE_DIR + "dna-conf.ldif", self.sub_dict)
|
||||
dna_fd = ipautil.write_tmp_file(dna_txt)
|
||||
try:
|
||||
ldap_mod(dna_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -268,8 +267,8 @@ class DsInstance(service.Service):
|
||||
|
||||
def __config_uidgid_gen_first_master(self):
|
||||
self.step("configuring Posix uid/gid generation as first master")
|
||||
dna_txt = template_file(SHARE_DIR + "dna-posix.ldif", self.sub_dict)
|
||||
dna_fd = write_tmp_file(dna_txt)
|
||||
dna_txt = ipautil.template_file(ipautil.SHARE_DIR + "dna-posix.ldif", self.sub_dict)
|
||||
dna_fd = ipautil.write_tmp_file(dna_txt)
|
||||
try:
|
||||
ldap_mod(dna_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -278,8 +277,8 @@ class DsInstance(service.Service):
|
||||
|
||||
def __add_master_entry_first_master(self):
|
||||
self.step("adding master entry as first master")
|
||||
master_txt = template_file(SHARE_DIR + "master-entry.ldif", self.sub_dict)
|
||||
master_fd = write_tmp_file(master_txt)
|
||||
master_txt = ipautil.template_file(ipautil.SHARE_DIR + "master-entry.ldif", self.sub_dict)
|
||||
master_fd = ipautil.write_tmp_file(master_txt)
|
||||
try:
|
||||
ldap_mod(master_fd, "cn=Directory Manager", self.dm_password)
|
||||
except ipautil.CalledProcessError, e:
|
||||
@ -292,20 +291,20 @@ class DsInstance(service.Service):
|
||||
args = ["/usr/share/ipa/ipa-server-setupssl", self.dm_password,
|
||||
dirname, self.host_name]
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("done configuring ssl for ds instance")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("Failed to configure ssl in ds instance %s" % e)
|
||||
|
||||
def __add_default_layout(self):
|
||||
self.step("adding default layout")
|
||||
txt = template_file(SHARE_DIR + "bootstrap-template.ldif", self.sub_dict)
|
||||
inf_fd = write_tmp_file(txt)
|
||||
txt = ipautil.template_file(ipautil.SHARE_DIR + "bootstrap-template.ldif", self.sub_dict)
|
||||
inf_fd = ipautil.write_tmp_file(txt)
|
||||
logging.debug("adding default dfrom ipa.ipautil import *s layout")
|
||||
args = ["/usr/bin/ldapmodify", "-xv", "-D", "cn=Directory Manager",
|
||||
"-w", self.dm_password, "-f", inf_fd.name]
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("done adding default ds layout")
|
||||
except ipautil.CalledProcessError, e:
|
||||
print "Failed to add default ds layout", e
|
||||
@ -313,13 +312,13 @@ class DsInstance(service.Service):
|
||||
|
||||
def __create_indeces(self):
|
||||
self.step("creating indeces")
|
||||
txt = template_file(SHARE_DIR + "indeces.ldif", self.sub_dict)
|
||||
inf_fd = write_tmp_file(txt)
|
||||
txt = ipautil.template_file(ipautil.SHARE_DIR + "indeces.ldif", self.sub_dict)
|
||||
inf_fd = ipautil.write_tmp_file(txt)
|
||||
logging.debug("adding/updating indeces")
|
||||
args = ["/usr/bin/ldapmodify", "-xv", "-D", "cn=Directory Manager",
|
||||
"-w", self.dm_password, "-f", inf_fd.name]
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("done adding/updating indeces")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("Failed to add/update indeces %s" % str(e))
|
||||
@ -327,7 +326,7 @@ class DsInstance(service.Service):
|
||||
def __certmap_conf(self):
|
||||
self.step("configuring certmap.conf")
|
||||
dirname = config_dirname(self.realm_name)
|
||||
certmap_conf = template_file(SHARE_DIR+"certmap.conf.template", self.sub_dict)
|
||||
certmap_conf = ipautil.template_file(ipautil.SHARE_DIR + "certmap.conf.template", self.sub_dict)
|
||||
certmap_fd = open(dirname+"certmap.conf", "w+")
|
||||
certmap_fd.write(certmap_conf)
|
||||
certmap_fd.close()
|
||||
@ -335,7 +334,7 @@ class DsInstance(service.Service):
|
||||
def change_admin_password(self, password):
|
||||
logging.debug("Changing admin password")
|
||||
dirname = config_dirname(self.realm_name)
|
||||
if dir_exists("/usr/lib64/mozldap"):
|
||||
if ipautil.dir_exists("/usr/lib64/mozldap"):
|
||||
app = "/usr/lib64/mozldap/ldappasswd"
|
||||
else:
|
||||
app = "/usr/lib/mozldap/ldappasswd"
|
||||
@ -344,7 +343,7 @@ class DsInstance(service.Service):
|
||||
"-P", dirname+"/cert8.db", "-ZZZ", "-s", password,
|
||||
"uid=admin,cn=sysaccounts,cn=etc,"+self.suffix]
|
||||
try:
|
||||
run(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("ldappasswd done")
|
||||
except ipautil.CalledProcessError, e:
|
||||
print "Unable to set admin password", e
|
||||
|
@ -329,6 +329,32 @@ class IPAServer:
|
||||
|
||||
return (exact_match_filter, partial_match_filter)
|
||||
|
||||
def __get_schema(self, opts=None):
|
||||
"""Retrieves the current LDAP schema from the LDAP server."""
|
||||
|
||||
schema_entry = self.__get_base_entry("", "objectclass=*", ['dn','subschemasubentry'], opts)
|
||||
schema_cn = schema_entry.get('subschemasubentry')
|
||||
schema = self.__get_base_entry(schema_cn, "objectclass=*", ['*'], opts)
|
||||
|
||||
return schema
|
||||
|
||||
def __get_objectclasses(self, opts=None):
|
||||
"""Returns a list of available objectclasses that the LDAP
|
||||
server supports. This parses out the syntax, attributes, etc
|
||||
and JUST returns a lower-case list of the names."""
|
||||
|
||||
schema = self.__get_schema(opts)
|
||||
|
||||
objectclasses = schema.get('objectclasses')
|
||||
|
||||
# Convert this list into something more readable
|
||||
result = []
|
||||
for i in range(len(objectclasses)):
|
||||
oc = objectclasses[i].lower().split(" ")
|
||||
result.append(oc[3].replace("'",""))
|
||||
|
||||
return result
|
||||
|
||||
# Higher-level API
|
||||
|
||||
def get_aci_entry(self, sattrs, opts=None):
|
||||
@ -423,6 +449,19 @@ class IPAServer:
|
||||
if self.__is_user_unique(user['uid'], opts) == 0:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
|
||||
|
||||
# dn is set here, not by the user
|
||||
try:
|
||||
del user['dn']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# No need to set empty fields, and they can cause issues when they
|
||||
# get to LDAP, like:
|
||||
# TypeError: ('expected a string in the list', None)
|
||||
for k in user.keys():
|
||||
if not user[k] or len(user[k]) == 0 or (len(user[k]) == 1 and '' in user[k]):
|
||||
del user[k]
|
||||
|
||||
dn="uid=%s,%s,%s" % (ldap.dn.escape_dn_chars(user['uid']),
|
||||
user_container,self.basedn)
|
||||
entry = ipaserver.ipaldap.Entry(dn)
|
||||
@ -476,8 +515,16 @@ class IPAServer:
|
||||
|
||||
conn = self.getConnection(opts)
|
||||
try:
|
||||
res = conn.addEntry(entry)
|
||||
self.add_user_to_group(user.get('uid'), group_dn, opts)
|
||||
try:
|
||||
res = conn.addEntry(entry)
|
||||
except TypeError, e:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, "There is a problem with one of the data types.")
|
||||
except Exception, e:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, e)
|
||||
try:
|
||||
self.add_user_to_group(user.get('uid'), group_dn, opts)
|
||||
except Exception, e:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, "The user was created but adding to group %s failed" % group_dn)
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
return res
|
||||
@ -1332,7 +1379,78 @@ class IPAServer:
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
return res
|
||||
|
||||
|
||||
def find_service_principal(self, criteria, sattrs, searchlimit=-1,
|
||||
timelimit=-1, opts=None):
|
||||
"""Returns a list: counter followed by the results.
|
||||
If the results are truncated, counter will be set to -1."""
|
||||
|
||||
config = self.get_ipa_config(opts)
|
||||
if timelimit < 0:
|
||||
timelimit = float(config.get('ipasearchtimelimit'))
|
||||
if searchlimit < 0:
|
||||
searchlimit = float(config.get('ipasearchrecordslimit'))
|
||||
|
||||
search_fields = ["krbprincipalname"]
|
||||
|
||||
criteria = self.__safe_filter(criteria)
|
||||
criteria_words = re.split(r'\s+', criteria)
|
||||
criteria_words = filter(lambda value:value!="", criteria_words)
|
||||
if len(criteria_words) == 0:
|
||||
return [0]
|
||||
|
||||
(exact_match_filter, partial_match_filter) = self.__generate_match_filters(
|
||||
search_fields, criteria_words)
|
||||
|
||||
#
|
||||
# further constrain search to just the objectClass
|
||||
# TODO - need to parameterize this into generate_match_filters,
|
||||
# and work it into the field-specification search feature
|
||||
#
|
||||
exact_match_filter = "(&(objectclass=krbPrincipalAux)(!(objectClass=person))(!(krbprincipalname=kadmin/*))%s)" % exact_match_filter
|
||||
partial_match_filter = "(&(objectclass=krbPrincipalAux)(!(objectClass=person))(!(krbprincipalname=kadmin/*))%s)" % partial_match_filter
|
||||
print exact_match_filter
|
||||
print partial_match_filter
|
||||
|
||||
conn = self.getConnection(opts)
|
||||
try:
|
||||
try:
|
||||
exact_results = conn.getListAsync(self.basedn, self.scope,
|
||||
exact_match_filter, sattrs, 0, None, None, timelimit,
|
||||
searchlimit)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||
exact_results = [0]
|
||||
|
||||
try:
|
||||
partial_results = conn.getListAsync(self.basedn, self.scope,
|
||||
partial_match_filter, sattrs, 0, None, None, timelimit,
|
||||
searchlimit)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||
partial_results = [0]
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
|
||||
exact_counter = exact_results[0]
|
||||
partial_counter = partial_results[0]
|
||||
|
||||
exact_results = exact_results[1:]
|
||||
partial_results = partial_results[1:]
|
||||
|
||||
# Remove exact matches from the partial_match list
|
||||
exact_dns = set(map(lambda e: e.dn, exact_results))
|
||||
partial_results = filter(lambda e: e.dn not in exact_dns,
|
||||
partial_results)
|
||||
|
||||
if (exact_counter == -1) or (partial_counter == -1):
|
||||
counter = -1
|
||||
else:
|
||||
counter = len(exact_results) + len(partial_results)
|
||||
|
||||
entries = [counter]
|
||||
for e in exact_results + partial_results:
|
||||
entries.append(self.convert_entry(e))
|
||||
|
||||
return entries
|
||||
|
||||
def get_keytab(self, name, opts=None):
|
||||
"""get a keytab"""
|
||||
@ -1397,6 +1515,19 @@ class IPAServer:
|
||||
except:
|
||||
raise
|
||||
|
||||
# Run through the list of User and Group object classes to make
|
||||
# sure they are all valid. This doesn't handle dependencies but it
|
||||
# will at least catch typos.
|
||||
classes = self.__get_objectclasses(opts)
|
||||
oc = newconfig['ipauserobjectclasses']
|
||||
for i in range(len(oc)):
|
||||
if not oc[i].lower() in classes:
|
||||
raise ipaerror.gen_exception(ipaerror.CONFIG_INVALID_OC)
|
||||
oc = newconfig['ipagroupobjectclasses']
|
||||
for i in range(len(oc)):
|
||||
if not oc[i].lower() in classes:
|
||||
raise ipaerror.gen_exception(ipaerror.CONFIG_INVALID_OC)
|
||||
|
||||
return self.update_entry(oldconfig, newconfig, opts)
|
||||
|
||||
def get_password_policy(self, opts=None):
|
||||
@ -1406,6 +1537,10 @@ class IPAServer:
|
||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_NO_CONFIG)
|
||||
|
||||
# convert some values for display purposes
|
||||
policy['krbmaxpwdlife'] = str(int(policy.get('krbmaxpwdlife')) / 86400)
|
||||
policy['krbminpwdlife'] = str(int(policy.get('krbminpwdlife')) / 3600)
|
||||
|
||||
return policy
|
||||
|
||||
def update_password_policy(self, oldpolicy, newpolicy, opts=None):
|
||||
@ -1414,11 +1549,18 @@ class IPAServer:
|
||||
# The LDAP routines want strings, not ints, so convert a few
|
||||
# things. Otherwise it sees a string -> int conversion as a change.
|
||||
try:
|
||||
newpolicy['krbmaxpwdlife'] = str(newpolicy.get('krbmaxpwdlife'))
|
||||
newpolicy['krbminpwdlife'] = str(newpolicy.get('krbminpwdlife'))
|
||||
newpolicy['krbpwdhistorylength'] = str(newpolicy.get('krbpwdhistorylength'))
|
||||
newpolicy['krbpwdmindiffchars'] = str(newpolicy.get('krbpwdmindiffchars'))
|
||||
newpolicy['krbpwdminlength'] = str(newpolicy.get('krbpwdminlength'))
|
||||
for k in oldpolicy.iterkeys():
|
||||
if k.startswith("krb", 0, 3):
|
||||
oldpolicy[k] = str(oldpolicy[k])
|
||||
for k in newpolicy.iterkeys():
|
||||
if k.startswith("krb", 0, 3):
|
||||
newpolicy[k] = str(newpolicy[k])
|
||||
|
||||
# Convert hours and days to seconds
|
||||
oldpolicy['krbmaxpwdlife'] = str(int(oldpolicy.get('krbmaxpwdlife')) * 86400)
|
||||
oldpolicy['krbminpwdlife'] = str(int(oldpolicy.get('krbminpwdlife')) * 3600)
|
||||
newpolicy['krbmaxpwdlife'] = str(int(newpolicy.get('krbmaxpwdlife')) * 86400)
|
||||
newpolicy['krbminpwdlife'] = str(int(newpolicy.get('krbminpwdlife')) * 3600)
|
||||
except KeyError:
|
||||
# These should all be there but if not, let things proceed
|
||||
pass
|
||||
|
@ -360,6 +360,7 @@ def handler(req, profiling=False):
|
||||
h.register_function(f.get_password_policy)
|
||||
h.register_function(f.update_password_policy)
|
||||
h.register_function(f.add_service_principal)
|
||||
h.register_function(f.find_service_principal)
|
||||
h.register_function(f.get_keytab)
|
||||
h.handle_request(req)
|
||||
finally:
|
||||
|
Loading…
Reference in New Issue
Block a user