Implement the password policy UI and finish IPA policy UI

This includes a default password policy
Custom fields are now read from LDAP. The format is a list of
  dicts with keys: label, field, required.
The LDAP-based configuration now specifies:
    ipaUserSearchFields: uid,givenName,sn,telephoneNumber,ou,title
    ipaGroupSearchFields: cn,description
    ipaSearchTimeLimit: 2
    ipaSearchRecordsLimit: 0
    ipaCustomFields:
    ipaHomesRootDir: /home
    ipaDefaultLoginShell: /bin/sh
    ipaDefaultPrimaryGroup: ipausers
    ipaMaxUsernameLength: 8
    ipaPwdExpAdvNotify: 4
This could use some optimization.
This commit is contained in:
Rob Crittenden
2007-11-16 12:59:32 -05:00
parent 0a3ed69746
commit 1967aafa39
14 changed files with 610 additions and 156 deletions

View File

@@ -205,8 +205,6 @@ def main():
user.setValue('homedirectory', directory) user.setValue('homedirectory', directory)
if shell: if shell:
user.setValue('loginshell', shell) user.setValue('loginshell', shell)
else:
user.setValue('loginshell', "/bin/sh")
try: try:
client = ipaclient.IPAClient() client = ipaclient.IPAClient()

View File

@@ -134,10 +134,14 @@ class IPAClient:
return all_users return all_users
def get_add_schema(self): def get_custom_fields(self):
"""Prototype for the GUI. Specify in the directory fields to """Get custom user fields"""
be displayed and what data to get for new users.""" result = self.transport.get_custom_fields()
result = self.transport.get_add_schema() return result
def set_custom_fields(self, schema):
"""Set custom user fields"""
result = self.transport.set_custom_fields(schema)
return result return result
def find_users(self, criteria, sattrs=None, searchlimit=0, timelimit=-1): def find_users(self, criteria, sattrs=None, searchlimit=0, timelimit=-1):
@@ -331,3 +335,29 @@ class IPAClient:
entries.append(user.User(e)) entries.append(user.User(e))
return entries return entries
def get_ipa_config(self):
"""Get the IPA configuration"""
result = self.transport.get_ipa_config()
return entity.Entity(result)
def update_ipa_config(self, config):
"""Updates the IPA configuration.
config is an Entity object.
"""
result = self.transport.update_ipa_config(config.origDataDict(), config.toDict())
return result
def get_password_policy(self):
"""Get the IPA password policy"""
result = self.transport.get_password_policy()
return entity.Entity(result)
def update_password_policy(self, policy):
"""Updates the IPA password policy.
policy is an Entity object.
"""
result = self.transport.update_password_policy(policy.origDataDict(), policy.toDict())
return result

View File

@@ -123,6 +123,11 @@ LDAP_EMPTY_MODLIST = gen_error_code(
0x0006, 0x0006,
"No modifications to be performed") "No modifications to be performed")
LDAP_NO_CONFIG = gen_error_code(
LDAP_CATEGORY,
0x0007,
"IPA configuration not found")
# #
# Input errors (sample - replace me) # Input errors (sample - replace me)
# #

View File

@@ -218,23 +218,32 @@ class RPCClient:
return ipautil.unwrap_binary_data(result) return ipautil.unwrap_binary_data(result)
def get_add_schema(self): def get_custom_fields(self):
"""Get the list of attributes we need to ask when adding a new """Get custom user fields."""
user.
"""
server = self.setup_server() server = self.setup_server()
# FIXME: Hardcoded and designed for the TurboGears GUI. Do we want
# this for the CLI as well?
try: try:
result = server.get_add_schema() result = server.get_custom_fields()
except xmlrpclib.Fault, fault: except xmlrpclib.Fault, fault:
raise ipaerror.gen_exception(fault.faultCode, fault.faultString) raise ipaerror.gen_exception(fault.faultCode, fault.faultString)
except socket.error, (value, msg): except socket.error, (value, msg):
raise xmlrpclib.Fault(value, msg) raise xmlrpclib.Fault(value, msg)
return ipautil.unwrap_binary_data(result) return ipautil.unwrap_binary_data(result)
def set_custom_fields(self, schema):
"""Set custom user fields."""
server = self.setup_server()
try:
result = server.set_custom_fields(schema)
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_all_users (self): def get_all_users (self):
"""Return a list containing a User object for each existing user.""" """Return a list containing a User object for each existing user."""
@@ -591,3 +600,51 @@ class RPCClient:
raise xmlrpclib.Fault(value, msg) raise xmlrpclib.Fault(value, msg)
return ipautil.unwrap_binary_data(result) return ipautil.unwrap_binary_data(result)
def get_ipa_config(self):
"""Get the IPA configuration"""
server = self.setup_server()
try:
result = server.get_ipa_config()
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 update_ipa_config(self, oldconfig, newconfig):
"""Update the IPA configuration"""
server = self.setup_server()
try:
result = server.update_ipa_config(oldconfig, newconfig)
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_password_policy(self):
"""Get the IPA password policy"""
server = self.setup_server()
try:
result = server.get_password_policy()
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 update_password_policy(self, oldpolicy, newpolicy):
"""Update the IPA password policy"""
server = self.setup_server()
try:
result = server.update_password_policy(oldpolicy, newpolicy)
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)

View File

@@ -2,25 +2,49 @@ import turbogears
from turbogears import validators, widgets from turbogears import validators, widgets
class IPAPolicyFields(): class IPAPolicyFields():
searchlimit = widgets.TextField(name="searchlimit", label="Search Time Limit (sec.)", attrs=dict(size=6,maxlength=6)) # From cn=ipaConfig
maxuidlength = widgets.TextField(name="maxuidlength", label="Max. UID Length", attrs=dict(size=3,maxlength=3)) ipausersearchfields = widgets.TextField(name="ipausersearchfields", label="User Search Fields")
passwordnotif = widgets.TextField(name="passwordnotif", label="Password Expiration Notification (days)", attrs=dict(size=3,maxlength=3)) ipagroupsearchfields = widgets.TextField(name="ipagroupsearchfields", label="Group Search Fields")
homedir = widgets.TextField(name="homedir", label="Root for Home Directories") ipasearchtimelimit = widgets.TextField(name="ipasearchtimelimit", label="Search Time Limit (sec.)", attrs=dict(size=6,maxlength=6))
defaultshell = widgets.TextField(name="defaultshell", label="Default shell") ipasearchrecordslimit = widgets.TextField(name="ipasearchrecordslimit", label="Search Records Limit", attrs=dict(size=6,maxlength=6))
defaultgroup = widgets.TextField(name="defaultgroup", label="Default Users group") ipahomesrootdir = widgets.TextField(name="ipahomesrootdir", label="Root for Home Directories")
ipadefaultloginshell = widgets.TextField(name="ipadefaultloginshell", label="Default shell")
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))
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))
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))
password_orig = widgets.HiddenField(name="password_orig")
class IPAPolicyValidator(validators.Schema): class IPAPolicyValidator(validators.Schema):
searchlimit = validators.Number(not_empty=True) ipausersearchfields = validators.String(not_empty=True)
maxuidlength = validators.Number(not_empty=True) ipagroupsearchfields = validators.String(not_empty=True)
passwordnotif = validators.Number(not_empty=True) ipasearchtimelimit = validators.Number(not_empty=True)
homedir = validators.String(not_empty=True) ipasearchrecordslimit = validators.Number(not_empty=True)
defaultshell = validators.String(not_empty=True) ipamaxusernamelength = validators.Number(not_empty=True)
defaultgroup = validators.String(not_empty=True) ipapwdexpadvnotify = validators.Number(not_empty=True)
ipahomesrootdir = validators.String(not_empty=True)
ipadefaultloginshell = validators.String(not_empty=True)
ipadefaultprimarygroup = validators.String(not_empty=True)
krbmaxpwdlife = validators.Number(not_empty=True)
krbminpwdlife = validators.Number(not_empty=True)
krbpwdmindiffchars = validators.Number(not_empty=True)
krbpwdminlength = validators.Number(not_empty=True)
krbpwdhistorylength = validators.Number(not_empty=True)
class IPAPolicyForm(widgets.Form): class IPAPolicyForm(widgets.Form):
params = ['ipapolicy_fields'] params = ['ipapolicy_fields']
hidden_fields = [ hidden_fields = [
IPAPolicyFields.ipapolicy_orig, IPAPolicyFields.password_orig
] ]
validator = IPAPolicyValidator() validator = IPAPolicyValidator()

View File

@@ -15,6 +15,7 @@ from turbogears import identity
from ipacontroller import IPAController from ipacontroller import IPAController
from ipa.entity import utf8_encode_values from ipa.entity import utf8_encode_values
from ipa import ipaerror from ipa import ipaerror
import ipa.entity
import ipagui.forms.ipapolicy import ipagui.forms.ipapolicy
import ldap.dn import ldap.dn
@@ -34,16 +35,14 @@ class IPAPolicyController(IPAController):
@identity.require(identity.in_group("admins")) @identity.require(identity.in_group("admins"))
def show(self, tg_errors=None): def show(self, tg_errors=None):
"""Displays the one policy page""" """Displays the one policy page"""
client = self.get_ipaclient()
config = client.get_ipa_config()
ipapolicy = config.toDict()
# TODO: Get this dict from LDAP ppolicy = client.get_password_policy()
ipapolicy = {} password = ppolicy.toDict()
ipapolicy['searchlimit'] = 2
ipapolicy['maxuidlength'] = 3 return dict(ipapolicy=ipapolicy,password=password,fields=ipagui.forms.ipapolicy.IPAPolicyFields())
ipapolicy['passwordnotif'] = 4
ipapolicy['homedir'] = "/home"
ipapolicy['defaultgroup'] = "ipausers"
ipapolicy['defaultshell'] = "/bin/bash"
return dict(ipapolicy=ipapolicy,fields=ipagui.forms.ipapolicy.IPAPolicyFields())
@expose("ipagui.templates.ipapolicyedit") @expose("ipagui.templates.ipapolicyedit")
@identity.require(identity.in_group("admins")) @identity.require(identity.in_group("admins"))
@@ -54,18 +53,28 @@ class IPAPolicyController(IPAController):
"Please see the messages below for details.") "Please see the messages below for details.")
try: try:
# TODO: Get this dict from LDAP client = self.get_ipaclient()
ipapolicy_dict = {} config = client.get_ipa_config()
ipapolicy_dict['searchlimit'] = 2 ipapolicy_dict = config.toDict()
ipapolicy_dict['maxuidlength'] = 3
ipapolicy_dict['passwordnotif'] = 4 ppolicy = client.get_password_policy()
ipapolicy_dict['homedir'] = "/home" password_dict = ppolicy.toDict()
ipapolicy_dict['defaultgroup'] = "ipausers"
ipapolicy_dict['defaultshell'] = "/bin/bash" # store a copy of the original policy for the update later
ipapolicy_data = b64encode(dumps(ipapolicy_dict))
ipapolicy_dict['ipapolicy_orig'] = ipapolicy_data
# store a copy of the original policy for the update later
password_data = b64encode(dumps(password_dict))
password_dict['password_orig'] = password_data
# Combine the 2 dicts to make the form easier
ipapolicy_dict.update(password_dict)
return dict(form=ipapolicy_edit_form, ipapolicy=ipapolicy_dict) return dict(form=ipapolicy_edit_form, ipapolicy=ipapolicy_dict)
except ipaerror.IPAError, e: except ipaerror.IPAError, e:
turbogears.flash("IPA Policy edit failed: " + str(e) + "<br/>" + str(e.detail)) turbogears.flash("IPA Policy edit failed: " + str(e) + "<br/>" + str(e.detail))
raise turbogears.redirect('/group/show', uid=cn) raise turbogears.redirect('/ipapolicy/show')
@expose() @expose()
@@ -86,16 +95,72 @@ class IPAPolicyController(IPAController):
return dict(form=ipapolicy_edit_form, ipapolicy=kw, return dict(form=ipapolicy_edit_form, ipapolicy=kw,
tg_template='ipagui.templates.ipapolicyedit') tg_template='ipagui.templates.ipapolicyedit')
try: policy_modified = False
password_modified = False
# TODO: Actually save the data try:
orig_ipapolicy_dict = loads(b64decode(kw.get('ipapolicy_orig')))
orig_password_dict = loads(b64decode(kw.get('password_orig')))
new_ipapolicy = ipa.entity.Entity(orig_ipapolicy_dict)
new_password = ipa.entity.Entity(orig_password_dict)
if str(new_ipapolicy.ipasearchtimelimit) != str(kw.get('ipasearchtimelimit')):
policy_modified = True
new_ipapolicy.setValue('ipasearchtimelimit', kw.get('ipasearchtimelimit'))
if str(new_ipapolicy.ipasearchrecordslimit) != str(kw.get('ipasearchrecordslimit')):
policy_modified = True
new_ipapolicy.setValue('ipasearchrecordslimit', kw.get('ipasearchrecordslimit'))
if new_ipapolicy.ipausersearchfields != kw.get('ipausersearchfields'):
policy_modified = True
new_ipapolicy.setValue('ipausersearchfields', kw.get('ipausersearchfields'))
if new_ipapolicy.ipagroupsearchfields != kw.get('ipagroupsearchfields'):
policy_modified = True
new_ipapolicy.setValue('ipagroupsearchfields', kw.get('ipagroupsearchfields'))
if str(new_ipapolicy.ipapwdexpadvnotify) != str(kw.get('ipapwdexpadvnotify')):
policy_modified = True
new_ipapolicy.setValue('ipapwdexpadvnotify', kw.get('ipapwdexpadvnotify'))
if str(new_ipapolicy.ipamaxusernamelength) != str(kw.get('ipamaxusernamelength')):
policy_modified = True
new_ipapolicy.setValue('ipamaxusernamelength', kw.get('ipamaxusernamelength'))
if new_ipapolicy.ipahomesrootdir != kw.get('ipahomesrootdir'):
policy_modified = True
new_ipapolicy.setValue('ipahomesrootdir', kw.get('ipahomesrootdir'))
if new_ipapolicy.ipadefaultloginshell != kw.get('ipadefaultloginshell'):
policy_modified = True
new_ipapolicy.setValue('ipadefaultloginshell', kw.get('ipadefaultloginshell'))
if new_ipapolicy.ipadefaultprimarygroup != kw.get('ipadefaultprimarygroup'):
policy_modified = True
new_ipapolicy.setValue('ipadefaultprimarygroup', kw.get('ipadefaultprimarygroup'))
if policy_modified:
rv = client.update_ipa_config(new_ipapolicy)
# Now check the password policy for updates
if str(new_password.krbmaxpwdlife) != str(kw.get('krbmaxpwdlife')):
password_modified = True
new_password.setValue('krbmaxpwdlife', str(kw.get('krbmaxpwdlife')))
if str(new_password.krbminpwdlife) != str(kw.get('krbminpwdlife')):
password_modified = True
new_password.setValue('krbminpwdlife', str(kw.get('krbminpwdlife')))
if str(new_password.krbpwdhistorylength) != str(kw.get('krbpwdhistorylength')):
password_modified = True
new_password.setValue('krbpwdhistorylength', str(kw.get('krbpwdhistorylength')))
if str(new_password.krbpwdmindiffchars) != str(kw.get('krbpwdmindiffchars')):
password_modified = True
new_password.setValue('krbpwdmindiffchars', str(kw.get('krbpwdmindiffchars')))
if str(new_password.krbpwdminlength) != str(kw.get('krbpwdminlength')):
password_modified = True
new_password.setValue('krbpwdminlength', str(kw.get('krbpwdminlength')))
if password_modified:
rv = client.update_password_policy(new_password)
turbogears.flash("IPA Policy updated") turbogears.flash("IPA Policy updated")
raise turbogears.redirect('/ipapolicy/show') raise turbogears.redirect('/ipapolicy/show')
except (SyntaxError, ipaerror.IPAError), e: except ipaerror.IPAError, e:
turbogears.flash("Policy update failed: " + str(e)) turbogears.flash("Policy update failed: " + str(e) + e.detail[0]['desc'])
return dict(form=policy_form, policy=kw, return dict(form=ipapolicy_edit_form, ipapolicy=kw,
tg_template='ipagui.templates.policyindex') tg_template='ipagui.templates.ipapolicyedit')
@validate(form=ipapolicy_edit_form) @validate(form=ipapolicy_edit_form)
@identity.require(identity.not_anonymous()) @identity.require(identity.not_anonymous())

View File

@@ -34,26 +34,48 @@ class UserController(IPAController):
def __init__(self, *args, **kw): def __init__(self, *args, **kw):
super(UserController,self).__init__(*args, **kw) super(UserController,self).__init__(*args, **kw)
self.load_custom_fields() # self.load_custom_fields()
def load_custom_fields(self): def load_custom_fields(self):
# client = self.get_ipaclient()
# schema = client.get_user_custom_schema() client = self.get_ipaclient()
schema = [ schema = client.get_custom_fields()
{ 'label': 'See Also',
'field': 'seeAlso', # FIXME: Don't load from LDAP every single time it is called
'required': 'true', } ,
{ 'label': 'O O O', # FIXME: Is removing the attributes on the fly thread-safe? Do we
'field': 'o', # need to lock here?
'required': 'false', } ,
]
for s in schema: for s in schema:
required=False required=False
if (s['required'] == "true"): if (s['required'].lower() == "true"):
required=True required=True
field = widgets.TextField(name=s['field'],label=s['label']) field = widgets.TextField(name=s['field'],label=s['label'])
validator = validators.String(not_empty=required) validator = validators.String(not_empty=required)
# Don't allow dupes on the new form
try:
for i in range(len(user_new_form.custom_fields)):
if user_new_form.custom_fields[i].name == s['field']:
user_new_form.custom_fields.pop(i)
except:
pass
# Don't allow dupes on the edit form
try:
for i in range(len(user_edit_form.custom_fields)):
if user_edit_form.custom_fields[i].name == s['field']:
user_edit_form.custom_fields.pop(i)
except:
pass
# Don't allow dupes in the list of user fields
try:
for i in range(len(ipagui.forms.user.UserFields.custom_fields)):
if ipagui.forms.user.UserFields.custom_fields[i].name == s['field']:
ipagui.forms.user.UserFields.custom_fields.pop(i)
except:
pass
ipagui.forms.user.UserFields.custom_fields.append(field) ipagui.forms.user.UserFields.custom_fields.append(field)
user_new_form.custom_fields.append(field) user_new_form.custom_fields.append(field)
user_edit_form.custom_fields.append(field) user_edit_form.custom_fields.append(field)
@@ -99,6 +121,7 @@ class UserController(IPAController):
@identity.require(identity.in_any_group("admins","editors")) @identity.require(identity.in_any_group("admins","editors"))
def new(self, tg_errors=None): def new(self, tg_errors=None):
"""Displays the new user form""" """Displays the new user form"""
self.load_custom_fields()
if tg_errors: if tg_errors:
turbogears.flash("There were validation errors.<br/>" + turbogears.flash("There were validation errors.<br/>" +
"Please see the messages below for details.") "Please see the messages below for details.")
@@ -281,6 +304,7 @@ class UserController(IPAController):
@identity.require(identity.not_anonymous()) @identity.require(identity.not_anonymous())
def edit(self, uid=None, principal=None, tg_errors=None): def edit(self, uid=None, principal=None, tg_errors=None):
"""Displays the edit user form""" """Displays the edit user form"""
self.load_custom_fields()
if tg_errors: if tg_errors:
turbogears.flash("There were validation errors.<br/>" + turbogears.flash("There were validation errors.<br/>" +
"Please see the messages below for details.") "Please see the messages below for details.")
@@ -581,6 +605,7 @@ class UserController(IPAController):
def show(self, uid): def show(self, uid):
"""Retrieve a single user for display""" """Retrieve a single user for display"""
client = self.get_ipaclient() client = self.get_ipaclient()
self.load_custom_fields()
try: try:
user = client.get_user_by_uid(uid, user_fields) user = client.get_user_by_uid(uid, user_fields)

View File

@@ -24,12 +24,42 @@ from ipagui.helpers import ipahelper
<table class="formtable" cellpadding="2" cellspacing="0" border="0"> <table class="formtable" cellpadding="2" cellspacing="0" border="0">
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="ipapolicy_fields.searchlimit.label" />: <label class="fieldlabel" py:content="ipapolicy_fields.ipasearchtimelimit.label" />:
</th> </th>
<td> <td>
<span py:replace="ipapolicy_fields.searchlimit.display(value_for(ipapolicy_fields.searchlimit))" /> <span py:replace="ipapolicy_fields.ipasearchtimelimit.display(value_for(ipapolicy_fields.ipasearchtimelimit))" />
<span py:if="tg.errors.get('searchlimit')" class="fielderror" <span py:if="tg.errors.get('ipasearchtimelimit')" class="fielderror"
py:content="tg.errors.get('searchlimit')" /> py:content="tg.errors.get('ipasearchtimelimit')" />
</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="ipapolicy_fields.ipasearchrecordslimit.label" />:
</th>
<td>
<span py:replace="ipapolicy_fields.ipasearchrecordslimit.display(value_for(ipapolicy_fields.ipasearchrecordslimit))" />
<span py:if="tg.errors.get('ipasearchrecordslimit')" class="fielderror"
py:content="tg.errors.get('ipasearchrecordslimit')" />
</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="ipapolicy_fields.ipausersearchfields.label" />:
</th>
<td>
<span py:replace="ipapolicy_fields.ipausersearchfields.display(value_for(ipapolicy_fields.ipausersearchfields))" />
<span py:if="tg.errors.get('ipausersearchfields')" class="fielderror"
py:content="tg.errors.get('ipausersearchfields')" />
</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="ipapolicy_fields.ipagroupsearchfields.label" />:
</th>
<td>
<span py:replace="ipapolicy_fields.ipagroupsearchfields.display(value_for(ipapolicy_fields.ipagroupsearchfields))" />
<span py:if="tg.errors.get('ipagroupsearchfields')" class="fielderror"
py:content="tg.errors.get('ipagroupsearchfields')" />
</td> </td>
</tr> </tr>
</table> </table>
@@ -38,56 +68,106 @@ from ipagui.helpers import ipahelper
<table class="formtable" cellpadding="2" cellspacing="0" border="0"> <table class="formtable" cellpadding="2" cellspacing="0" border="0">
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="ipapolicy_fields.passwordnotif.label" />: <label class="fieldlabel" py:content="ipapolicy_fields.ipapwdexpadvnotify.label" />:
</th> </th>
<td> <td>
<span py:replace="ipapolicy_fields.passwordnotif.display(value_for(ipapolicy_fields.passwordnotif))" /> <span py:replace="ipapolicy_fields.ipapwdexpadvnotify.display(value_for(ipapolicy_fields.ipapwdexpadvnotify))" />
<span py:if="tg.errors.get('passwordnotif')" class="fielderror" <span py:if="tg.errors.get('ipapwdexpadvnotify')" class="fielderror"
py:content="tg.errors.get('passwordnotif')" /> py:content="tg.errors.get('ipapwdexpadvnotify')" />
</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="ipapolicy_fields.krbminpwdlife.label" />:
</th>
<td>
<span py:replace="ipapolicy_fields.krbminpwdlife.display(value_for(ipapolicy_fields.krbminpwdlife))" />
<span py:if="tg.errors.get('krbminpwdlife')" class="fielderror"
py:content="tg.errors.get('krbminpwdlife')" />
</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="ipapolicy_fields.krbmaxpwdlife.label" />:
</th>
<td>
<span py:replace="ipapolicy_fields.krbmaxpwdlife.display(value_for(ipapolicy_fields.krbmaxpwdlife))" />
<span py:if="tg.errors.get('krbmaxpwdlife')" class="fielderror"
py:content="tg.errors.get('krbmaxpwdlife')" />
</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="ipapolicy_fields.krbpwdmindiffchars.label" />:
</th>
<td>
<span py:replace="ipapolicy_fields.krbpwdmindiffchars.display(value_for(ipapolicy_fields.krbpwdmindiffchars))" />
<span py:if="tg.errors.get('krbpwdmindiffchars')" class="fielderror"
py:content="tg.errors.get('krbpwdmindiffchars')" />
</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="ipapolicy_fields.krbpwdminlength.label" />:
</th>
<td>
<span py:replace="ipapolicy_fields.krbpwdminlength.display(value_for(ipapolicy_fields.krbpwdminlength))" />
<span py:if="tg.errors.get('krbpwdminlength')" class="fielderror"
py:content="tg.errors.get('krbpwdminlength')" />
</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="ipapolicy_fields.krbpwdhistorylength.label" />:
</th>
<td>
<span py:replace="ipapolicy_fields.krbpwdhistorylength.display(value_for(ipapolicy_fields.krbpwdhistorylength))" />
<span py:if="tg.errors.get('krbpwdhistorylength')" class="fielderror"
py:content="tg.errors.get('krbpwdhistorylength')" />
</td> </td>
</tr> </tr>
</table> </table>
<h2 class="formsection">Password Policy</h2> <h2 class="formsection">User Settings</h2>
<table class="formtable" cellpadding="2" cellspacing="0" border="0"> <table class="formtable" cellpadding="2" cellspacing="0" border="0">
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="ipapolicy_fields.maxuidlength.label" />: <label class="fieldlabel" py:content="ipapolicy_fields.ipamaxusernamelength.label" />:
</th> </th>
<td> <td>
<span py:replace="ipapolicy_fields.maxuidlength.display(value_for(ipapolicy_fields.maxuidlength))" /> <span py:replace="ipapolicy_fields.ipamaxusernamelength.display(value_for(ipapolicy_fields.ipamaxusernamelength))" />
<span py:if="tg.errors.get('maxuidlength')" class="fielderror" <span py:if="tg.errors.get('ipamaxusernamelength')" class="fielderror"
py:content="tg.errors.get('maxuidlength')" /> py:content="tg.errors.get('ipamaxusernamelength')" />
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="ipapolicy_fields.homedir.label" />: <label class="fieldlabel" py:content="ipapolicy_fields.ipahomesrootdir.label" />:
</th> </th>
<td> <td>
<span py:replace="ipapolicy_fields.homedir.display(value_for(ipapolicy_fields.homedir))" /> <span py:replace="ipapolicy_fields.ipahomesrootdir.display(value_for(ipapolicy_fields.ipahomesrootdir))" />
<span py:if="tg.errors.get('homedir')" class="fielderror" <span py:if="tg.errors.get('ipahomesrootdir')" class="fielderror"
py:content="tg.errors.get('homedir')" /> py:content="tg.errors.get('ipahomesrootdir')" />
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="ipapolicy_fields.defaultshell.label" />: <label class="fieldlabel" py:content="ipapolicy_fields.ipadefaultloginshell.label" />:
</th> </th>
<td> <td>
<span py:replace="ipapolicy_fields.defaultshell.display(value_for(ipapolicy_fields.defaultshell))" /> <span py:replace="ipapolicy_fields.ipadefaultloginshell.display(value_for(ipapolicy_fields.ipadefaultloginshell))" />
<span py:if="tg.errors.get('defaultshell')" class="fielderror" <span py:if="tg.errors.get('ipadefaultloginshell')" class="fielderror"
py:content="tg.errors.get('defaultshell')" /> py:content="tg.errors.get('ipadefaultloginshell')" />
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="ipapolicy_fields.defaultgroup.label" />: <label class="fieldlabel" py:content="ipapolicy_fields.ipadefaultprimarygroup.label" />:
</th> </th>
<td> <td>
<span py:replace="ipapolicy_fields.defaultgroup.display(value_for(ipapolicy_fields.defaultgroup))" /> <span py:replace="ipapolicy_fields.ipadefaultprimarygroup.display(value_for(ipapolicy_fields.ipadefaultprimarygroup))" />
<span py:if="tg.errors.get('defaultgroup')" class="fielderror" <span py:if="tg.errors.get('ipadefaultprimarygroup')" class="fielderror"
py:content="tg.errors.get('defaultgroup')" /> py:content="tg.errors.get('ipadefaultprimarygroup')" />
</td> </td>
</tr> </tr>
</table> </table>

View File

@@ -20,9 +20,27 @@ edit_url = tg.url('/ipapolicy/edit')
<table class="formtable" cellpadding="2" cellspacing="0" border="0"> <table class="formtable" cellpadding="2" cellspacing="0" border="0">
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="fields.searchlimit.label" />: <label class="fieldlabel" py:content="fields.ipasearchtimelimit.label" />:
</th> </th>
<td>${ipapolicy.get("searchlimit")}</td> <td>${ipapolicy.get("ipasearchtimelimit")}</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="fields.ipasearchrecordslimit.label" />:
</th>
<td>${ipapolicy.get("ipasearchrecordslimit")}</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="fields.ipausersearchfields.label" />:
</th>
<td>${ipapolicy.get("ipausersearchfields")}</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="fields.ipagroupsearchfields.label" />:
</th>
<td>${ipapolicy.get("ipagroupsearchfields")}</td>
</tr> </tr>
</table> </table>
@@ -30,36 +48,66 @@ edit_url = tg.url('/ipapolicy/edit')
<table class="formtable" cellpadding="2" cellspacing="0" border="0"> <table class="formtable" cellpadding="2" cellspacing="0" border="0">
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="fields.passwordnotif.label" />: <label class="fieldlabel" py:content="fields.ipapwdexpadvnotify.label" />:
</th> </th>
<td>${ipapolicy.get("passwordnotif")}</td> <td>${ipapolicy.get("ipapwdexpadvnotify")}</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="fields.krbminpwdlife.label" />:
</th>
<td>${password.get("krbminpwdlife")}</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="fields.krbmaxpwdlife.label" />:
</th>
<td>${password.get("krbmaxpwdlife")}</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="fields.krbpwdmindiffchars.label" />:
</th>
<td>${password.get("krbpwdmindiffchars")}</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="fields.krbpwdminlength.label" />:
</th>
<td>${password.get("krbpwdminlength")}</td>
</tr>
<tr>
<th>
<label class="fieldlabel" py:content="fields.krbpwdhistorylength.label" />:
</th>
<td>${password.get("krbpwdhistorylength")}</td>
</tr> </tr>
</table> </table>
<h2 class="formsection">User Settings</h2> <h2 class="formsection">User Settings</h2>
<table class="formtable" cellpadding="2" cellspacing="0" border="0"> <table class="formtable" cellpadding="2" cellspacing="0" border="0">
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="fields.maxuidlength.label" />: <label class="fieldlabel" py:content="fields.ipamaxusernamelength.label" />:
</th> </th>
<td>${ipapolicy.get("maxuidlength")}</td> <td>${ipapolicy.get("ipamaxusernamelength")}</td>
</tr> </tr>
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="fields.homedir.label" />: <label class="fieldlabel" py:content="fields.ipahomesrootdir.label" />:
</th> </th>
<td>${ipapolicy.get("homedir")}</td> <td>${ipapolicy.get("ipahomesrootdir")}</td>
</tr> </tr>
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="fields.defaultshell.label" />: <label class="fieldlabel" py:content="fields.ipadefaultloginshell.label" />:
</th> </th>
<td>${ipapolicy.get("defaultshell")}</td> <td>${ipapolicy.get("ipadefaultloginshell")}</td>
</tr> </tr>
<tr> <tr>
<th> <th>
<label class="fieldlabel" py:content="fields.defaultgroup.label" />: <label class="fieldlabel" py:content="fields.ipadefaultprimarygroup.label" />:
</th> </th>
<td>${ipapolicy.get("defaultgroup")}</td> <td>${ipapolicy.get("ipadefaultprimarygroup")}</td>
</tr> </tr>
</table> </table>
<hr /> <hr />

View File

@@ -345,7 +345,7 @@ else:
</table> </table>
<div py:if='len(fields.custom_fields) &gt; 0'> <div py:if='len(fields.custom_fields) &gt; 0'>
<div class="formsection" >Custom Fields</div> <h2 class="formsection">Custom Fields</h2>
<table class="formtable" cellpadding="2" cellspacing="0" border="0"> <table class="formtable" cellpadding="2" cellspacing="0" border="0">
<tr py:for='custom_field in fields.custom_fields'> <tr py:for='custom_field in fields.custom_fields'>
<th> <th>

View File

@@ -9,6 +9,11 @@ changetype: add
objectClass: top objectClass: top
objectClass: nsContainer objectClass: nsContainer
cn: accounts cn: accounts
krbMinPwdLife: 3600
krbPwdMinDiffChars: 0
krbPwdMinLength: 8
krbPwdHistoryLength: 0
krbMaxPwdLife: 864000
dn: cn=users,cn=accounts,$SUFFIX dn: cn=users,cn=accounts,$SUFFIX
changetype: add changetype: add
@@ -95,3 +100,19 @@ objectClass: posixGroup
gidNumber: 1003 gidNumber: 1003
description: Limited admins who can edit other users description: Limited admins who can edit other users
cn: editors cn: editors
dn: cn=ipaConfig,cn=etc,dc=greyoak,dc=com
changetype: add
objectClass: nsContainer
objectClass: top
objectClass: ipaGuiConfig
ipaUserSearchFields: uid,givenName,sn,telephoneNumber,ou,title
ipaGroupSearchFields: cn,description
ipaSearchTimeLimit: 2
ipaSearchRecordsLimit: 0
ipaCustomFields:
ipaHomesRootDir: /home
ipaDefaultLoginShell: /bin/sh
ipaDefaultPrimaryGroup: ipausers
ipaMaxUsernameLength: 8
ipaPwdExpAdvNotify: 4

View File

@@ -9,3 +9,14 @@ aci: (targetattr="krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCo
aci: (targetattr="userPassword || krbPrincipalKey ||sambaLMPassword || sambaNTPassword || krbPasswordExpiration || krbPwdHistory || krbLastPwdChange")(version 3.0; acl "Kpasswd access to passowrd hashes for passowrd changes"; allow (read, write) userdn="ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";) aci: (targetattr="userPassword || krbPrincipalKey ||sambaLMPassword || sambaNTPassword || krbPasswordExpiration || krbPwdHistory || krbLastPwdChange")(version 3.0; acl "Kpasswd access to passowrd hashes for passowrd changes"; allow (read, write) userdn="ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";)
aci: (targetfilter="(|(objectClass=person)(objectClass=krbPrincipalAux)(objectClass=posixAccount)(objectClass=groupOfUniqueNames)(objectClass=posixGroup))")(targetattr="*")(version 3.0; acl "Account Admins can manage Users and Groups"; allow (add,delete,read,write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";) aci: (targetfilter="(|(objectClass=person)(objectClass=krbPrincipalAux)(objectClass=posixAccount)(objectClass=groupOfUniqueNames)(objectClass=posixGroup))")(targetattr="*")(version 3.0; acl "Account Admins can manage Users and Groups"; allow (add,delete,read,write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
aci: (targetattr = "givenName || sn || cn || displayName || initials || loginShell || homePhone || mobile || pager || facsimileTelephoneNumber || telephoneNumber || street || roomNumber || l || st || postalCode || manager || description || carLicense || labeledURI || inetUserHTTPURL || seeAlso || userPassword")(version 3.0;acl "Self service";allow (write) userdn="ldap:///self";) aci: (targetattr = "givenName || sn || cn || displayName || initials || loginShell || homePhone || mobile || pager || facsimileTelephoneNumber || telephoneNumber || street || roomNumber || l || st || postalCode || manager || description || carLicense || labeledURI || inetUserHTTPURL || seeAlso || userPassword")(version 3.0;acl "Self service";allow (write) userdn="ldap:///self";)
dn: cn=ipaConfig,cn=etc,dc=greyoak,dc=com
changetype: modify
add: aci
aci: (targetattr = "ipaUserSearchFields || ipaGroupSearchFields || ipaSearchTimeLimit || ipaSearchRecordsLimit || ipaCustomFields || ipaHomesRootDir || ipaDefaultLoginShell || ipaDefaultPrimaryGroup || ipaMaxUsernameLength || ipaPwdExpAdvNotify")(version 3.0;acl "Admins can write IPA policy"; allow (write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
replace: aci
dn: cn=accounts,$SUFFIX
changetype: modify
add: aci
aci: (targetattr = "krbMaxPwdLife || krbMinPwdLife || krbPwdMinDiffChars || krbPwdMinLength || krbPwdHistoryLength")(version 3.0;acl "Admins can write password policy"; allow (write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)

View File

@@ -30,6 +30,7 @@ import xmlrpclib
import copy import copy
import attrs import attrs
from ipa import ipaerror from ipa import ipaerror
from urllib import quote,unquote
import string import string
from types import * from types import *
@@ -420,17 +421,23 @@ class IPAServer:
# FIXME: This should be dynamic and can include just about anything # FIXME: This should be dynamic and can include just about anything
# Get our configuration
config = self.get_ipa_config(opts)
# Let us add in some missing attributes # Let us add in some missing attributes
if user.get('homedirectory') is None: if user.get('homedirectory') is None:
user['homedirectory'] = '/home/%s' % user.get('uid') user['homedirectory'] = '%s/%s' % (config.get('ipahomesrootdir'), user.get('uid'))
user['homedirectory'] = user['homedirectory'].replace('//', '/')
user['homedirectory'] = user['homedirectory'].rstrip('/')
if user.get('loginshell') is None:
user['loginshell'] = config.get('ipadefaultloginshell')
if user.get('gecos') is None: if user.get('gecos') is None:
user['gecos'] = user['uid'] user['gecos'] = user['uid']
# If uidnumber is blank the the FDS dna_plugin will automatically # If uidnumber is blank the the FDS dna_plugin will automatically
# assign the next value. So we don't have to do anything with it. # assign the next value. So we don't have to do anything with it.
# FIXME: put the default group in a config file group_dn="cn=%s,%s,%s" % (config.get('ipadefaultprimarygroup'), DefaultGroupContainer, self.basedn)
group_dn="cn=%s,%s,%s" % ("ipausers", DefaultGroupContainer, self.basedn)
try: try:
default_group = self.get_entry_by_dn(group_dn, ['dn','gidNumber'], opts) default_group = self.get_entry_by_dn(group_dn, ['dn','gidNumber'], opts)
if default_group: if default_group:
@@ -467,50 +474,67 @@ class IPAServer:
self.releaseConnection(conn) self.releaseConnection(conn)
return res return res
def get_add_schema (self): def get_custom_fields (self, opts=None):
"""Get the list of fields to be used when adding users in the GUI.""" """Get the list of custom user fields.
# FIXME: this needs to be pulled from LDAP A schema is a list of dict's of the form:
fields = [] label: The label dispayed to the user
field: the attribute name
field1 = { required: true/false
"name": "uid" ,
"label": "Login:", It is displayed to the user in the order of the list.
"type": "text", """
"validator": "text",
"required": "true" config = self.get_ipa_config(opts)
}
fields.append(field1) fields = config.get('ipacustomfields')
field1 = { if fields is None or fields == '':
"name": "givenName" , return []
"label": "First name:",
"type": "text", fl = fields.split('$')
"validator": "string", schema = []
"required": "true" for x in range(len(fl)):
} vals = fl[x].split(',')
fields.append(field1) if len(vals) != 3:
# Raise?
field1 = { print "Invalid field, skipping"
"name": "sn" , d = dict(label=unquote(vals[0]), field=unquote(vals[1]), required=unquote(vals[2]))
"label": "Last name:", schema.append(d)
"type": "text",
"validator": "string", return schema
"required": "true"
}
fields.append(field1)
field1 = {
"name": "mail" ,
"label": "E-mail address:",
"type": "text",
"validator": "email",
"required": "false"
}
fields.append(field1)
return fields
def set_custom_fields (self, schema, opts=None):
"""Set the list of custom user fields.
A schema is a list of dict's of the form:
label: The label dispayed to the user
field: the attribute name
required: true/false
It is displayed to the user in the order of the list.
"""
config = self.get_ipa_config(opts)
# The schema is stored as:
# label,field,required$label,field,required$...
# quote() from urilib is used to ensure that it is easy to unparse
stored_schema = ""
for i in range(len(schema)):
entry = schema[i]
entry = quote(entry.get('label')) + "," + quote(entry.get('field')) + "," + quote(entry.get('required'))
if stored_schema != "":
stored_schema = stored_schema + "$" + entry
else:
stored_schema = entry
new_config = copy.deepcopy(config)
new_config['ipacustomfields'] = stored_schema
return self.update_entry(config, new_config, opts)
def get_all_users (self, args=None, opts=None): def get_all_users (self, args=None, opts=None):
"""Return a list containing a User object for each """Return a list containing a User object for each
existing user. existing user.
@@ -529,18 +553,21 @@ class IPAServer:
return users return users
def find_users (self, criteria, sattrs=None, searchlimit=0, timelimit=-1, def find_users (self, criteria, sattrs=None, searchlimit=-1, timelimit=-1,
opts=None): opts=None):
"""Returns a list: counter followed by the results. """Returns a list: counter followed by the results.
If the results are truncated, counter will be set to -1.""" If the results are truncated, counter will be set to -1."""
# TODO - retrieve from config config = self.get_ipa_config(opts)
timelimit = 2 if timelimit < 0:
timelimit = float(config.get('ipasearchtimelimit'))
if searchlimit < 0:
searchlimit = float(config.get('ipasearchrecordslimit'))
# Assume the list of fields to search will come from a central # Assume the list of fields to search will come from a central
# configuration repository. A good format for that would be # configuration repository. A good format for that would be
# a comma-separated list of fields # a comma-separated list of fields
search_fields_conf_str = "uid,givenName,sn,telephoneNumber,ou,title" search_fields_conf_str = config.get('ipausersearchfields')
search_fields = string.split(search_fields_conf_str, ",") search_fields = string.split(search_fields_conf_str, ",")
criteria = self.__safe_filter(criteria) criteria = self.__safe_filter(criteria)
@@ -763,16 +790,22 @@ class IPAServer:
finally: finally:
self.releaseConnection(conn) self.releaseConnection(conn)
def find_groups (self, criteria, sattrs=None, searchlimit=0, timelimit=-1, def find_groups (self, criteria, sattrs=None, searchlimit=-1, timelimit=-1,
opts=None): opts=None):
"""Return a list containing a User object for each """Return a list containing a User object for each
existing group that matches the criteria. existing group that matches the criteria.
""" """
config = self.get_ipa_config(opts)
if timelimit < 0:
timelimit = float(config.get('ipasearchtimelimit'))
if searchlimit < 0:
searchlimit = float(config.get('ipasearchrecordslimit'))
# Assume the list of fields to search will come from a central # Assume the list of fields to search will come from a central
# configuration repository. A good format for that would be # configuration repository. A good format for that would be
# a comma-separated list of fields # a comma-separated list of fields
search_fields_conf_str = "cn,description" search_fields_conf_str = config.get('ipagroupsearchfields')
search_fields = string.split(search_fields_conf_str, ",") search_fields = string.split(search_fields_conf_str, ",")
criteria = self.__safe_filter(criteria) criteria = self.__safe_filter(criteria)
@@ -1155,10 +1188,10 @@ class IPAServer:
"""Do a memberOf search of groupdn and return the attributes in """Do a memberOf search of groupdn and return the attributes in
attr_list (an empty list returns everything).""" attr_list (an empty list returns everything)."""
# TODO - retrieve from config config = self.get_ipa_config(opts)
timelimit = 2 timelimit = float(config.get('ipasearchtimelimit'))
searchlimit = 0 searchlimit = float(config.get('ipasearchrecordslimit'))
groupdn = self.__safe_filter(groupdn) groupdn = self.__safe_filter(groupdn)
filter = "(memberOf=%s)" % groupdn filter = "(memberOf=%s)" % groupdn
@@ -1182,6 +1215,58 @@ class IPAServer:
return entries return entries
# Configuration support
def get_ipa_config(self, opts=None):
"""Retrieve the IPA configuration"""
try:
config = self.get_entry_by_cn("ipaconfig", None, opts)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
raise ipaerror.gen_exception(ipaerror.LDAP_NO_CONFIG)
return config
def update_ipa_config(self, oldconfig, newconfig, opts=None):
"""Update the IPA configuration"""
# The LDAP routines want strings, not ints, so convert a few
# things. Otherwise it sees a string -> int conversion as a change.
try:
newconfig['krbmaxpwdlife'] = str(newconfig.get('krbmaxpwdlife'))
newconfig['krbminpwdlife'] = str(newconfig.get('krbminpwdlife'))
newconfig['krbpwdmindiffchars'] = str(newconfig.get('krbpwdmindiffchars'))
newconfig['krbpwdminlength'] = str(newconfig.get('krbpwdminlength'))
newconfig['krbpwdhistorylength'] = str(newconfig.get('krbpwdhistorylength'))
except KeyError:
# These should all be there but if not, let things proceed
pass
return self.update_entry(oldconfig, newconfig, opts)
def get_password_policy(self, opts=None):
"""Retrieve the IPA password policy"""
try:
policy = self.get_entry_by_cn("accounts", None, opts)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
raise ipaerror.gen_exception(ipaerror.LDAP_NO_CONFIG)
return policy
def update_password_policy(self, oldpolicy, newpolicy, opts=None):
"""Update the IPA configuration"""
# 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'))
except KeyError:
# These should all be there but if not, let things proceed
pass
return self.update_entry(oldpolicy, newpolicy, opts)
def ldap_search_escape(match): def ldap_search_escape(match):
"""Escapes out nasty characters from the ldap search. """Escapes out nasty characters from the ldap search.
See RFC 2254.""" See RFC 2254."""

View File

@@ -326,7 +326,8 @@ def handler(req, profiling=False):
h.register_function(f.get_user_by_email) h.register_function(f.get_user_by_email)
h.register_function(f.get_users_by_manager) h.register_function(f.get_users_by_manager)
h.register_function(f.add_user) h.register_function(f.add_user)
h.register_function(f.get_add_schema) h.register_function(f.get_custom_fields)
h.register_function(f.set_custom_fields)
h.register_function(f.get_all_users) h.register_function(f.get_all_users)
h.register_function(f.find_users) h.register_function(f.find_users)
h.register_function(f.update_user) h.register_function(f.update_user)
@@ -351,6 +352,10 @@ def handler(req, profiling=False):
h.register_function(f.delete_group) h.register_function(f.delete_group)
h.register_function(f.attrs_to_labels) h.register_function(f.attrs_to_labels)
h.register_function(f.group_members) h.register_function(f.group_members)
h.register_function(f.get_ipa_config)
h.register_function(f.update_ipa_config)
h.register_function(f.get_password_policy)
h.register_function(f.update_password_policy)
h.handle_request(req) h.handle_request(req)
finally: finally:
pass pass