This commit is contained in:
Karl MacMillan
-
17 changed files with 125 additions and 98 deletions

View File

@@ -62,28 +62,28 @@ def main():
if (len(args) != 2): if (len(args) != 2):
while (cont != True): while (cont != True):
cn = raw_input("Group name: ") cn = raw_input("Group name: ")
if (ipavalidate.plain(cn, notEmpty=True)): if (ipavalidate.String(cn, notEmpty=True)):
print "Field is required and must be letters or '." print "Please enter a value"
else: else:
cont = True cont = True
else: else:
cn = args[1] cn = args[1]
if (ipavalidate.plain(cn, notEmpty=True)): if (ipavalidate.ipastsring(cn, notEmpty=True)):
print "Group name is required and must be letters or '." print "Please enter a value"
return 1 return 1
cont = False cont = False
if not options.desc: if not options.desc:
while (cont != True): while (cont != True):
desc = raw_input("Description: ") desc = raw_input("Description: ")
if (ipavalidate.plain(desc, notEmpty=True)): if (ipavalidate.String(desc, notEmpty=True)):
print "Field is required and must be letters or '." print "Please enter a value"
else: else:
cont = True cont = True
else: else:
desc = options.desc desc = options.desc
if (ipavalidate.plain(desc, notEmpty=True)): if (ipavalidate.String(desc, notEmpty=True)):
print "First name is required and must be letters or '." print "Please enter a value"
return 1 return 1
if options.gid: if options.gid:

View File

@@ -92,42 +92,42 @@ def main():
if not options.gn: if not options.gn:
while (cont != True): while (cont != True):
givenname = raw_input("First name: ") givenname = raw_input("First name: ")
if (ipavalidate.plain(givenname, notEmpty=True)): if (ipavalidate.String(givenname, notEmpty=True)):
print "Field is required and must be letters or '" print "Please enter a value"
else: else:
cont = True cont = True
else: else:
givenname = options.gn givenname = options.gn
if (ipavalidate.plain(givenname, notEmpty=True)): if (ipavalidate.String(givenname, notEmpty=True)):
print "First name is required and must be letters or '" print "Please enter a value"
return 1 return 1
cont = False cont = False
if not options.sn: if not options.sn:
while (cont != True): while (cont != True):
lastname = raw_input("Last name: ") lastname = raw_input("Last name: ")
if (ipavalidate.plain(lastname, notEmpty=True)): if (ipavalidate.String(lastname, notEmpty=True)):
print "Field is required and must be letters or '" print "Please enter a value"
else: else:
cont = True cont = True
else: else:
lastname = options.sn lastname = options.sn
if (ipavalidate.plain(lastname, notEmpty=True)): if (ipavalidate.String(lastname, notEmpty=True)):
print "Last name is required and must be letters or '" print "Please enter a value"
return 1 return 1
cont = False cont = False
if (len(args) != 2): if (len(args) != 2):
while (cont != True): while (cont != True):
username = raw_input("Login name: ") username = raw_input("Login name: ")
if (ipavalidate.plain(username, notEmpty=True)): if (ipavalidate.Plain(username, notEmpty=True, allowSpaces=False)):
print "Field is required and must be letters or '" print "Please enter a value"
else: else:
cont = True cont = True
else: else:
username = args[1] username = args[1]
if (ipavalidate.plain(username, notEmpty=True)): if (ipavalidate.Plain(username, notEmpty=True, allowSpaces=False)):
print "Username is required and must be letters or '" print "Username is required and may only include letters and numbers"
return 1 return 1
if not options.password: if not options.password:
@@ -147,7 +147,7 @@ def main():
if options.mail: if options.mail:
mail = options.mail mail = options.mail
if (ipavalidate.email(mail)): if (ipavalidate.Email(mail)):
print "The email provided seem not a valid email." print "The email provided seem not a valid email."
return 1 return 1
@@ -158,8 +158,8 @@ def main():
if not options.gecos: if not options.gecos:
while (cont != True): while (cont != True):
gecos = raw_input("gecos []: ") gecos = raw_input("gecos []: ")
if (ipavalidate.plain(gecos, notEmpty=False)): if (ipavalidate.String(gecos, notEmpty=False)):
print "Must be letters, numbers, spaces or '" print "Please enter a value"
else: else:
cont = True cont = True
cont = False cont = False
@@ -168,8 +168,8 @@ def main():
directory = raw_input("home directory [/home/"+username+"]: ") directory = raw_input("home directory [/home/"+username+"]: ")
if directory == "": if directory == "":
directory = "/home/"+username directory = "/home/"+username
if (ipavalidate.path(directory, notEmpty=False)): if (ipavalidate.Path(directory, notEmpty=False)):
print "Must be letters, numbers, spaces or '" print "Please enter a value"
else: else:
cont = True cont = True
cont = False cont = False

View File

@@ -65,6 +65,7 @@ def main():
if not(isinstance(aci_str_list,list) or isinstance(aci_str_list,tuple)): if not(isinstance(aci_str_list,list) or isinstance(aci_str_list,tuple)):
aci_str_list = [aci_str_list] aci_str_list = [aci_str_list]
acistr = None
for aci_str in aci_str_list: for aci_str in aci_str_list:
try: try:
aci = ipa.aci.ACI(aci_str) aci = ipa.aci.ACI(aci_str)
@@ -76,7 +77,7 @@ def main():
pass pass
if acistr is None: if acistr is None:
print "No delegation %s found." % args[1] print "No delegation '%s' found." % args[1]
return 2 return 2
old_aci_index = aci_str_list.index(acistr) old_aci_index = aci_str_list.index(acistr)
@@ -86,6 +87,7 @@ def main():
aci_entry.setValue('aci', new_aci_str_list) aci_entry.setValue('aci', new_aci_str_list)
client.update_entry(aci_entry) client.update_entry(aci_entry)
print "Delegation removed."
except xmlrpclib.Fault, fault: except xmlrpclib.Fault, fault:
if fault.faultCode == errno.ECONNREFUSED: if fault.faultCode == errno.ECONNREFUSED:
print "The IPA XML-RPC service is not responding." print "The IPA XML-RPC service is not responding."

View File

@@ -67,7 +67,7 @@ def main():
return 2 return 2
elif counter == -1: elif counter == -1:
print "These results are truncated." print "These results are truncated."
print "Please revine your search and try again." print "Please refine your search and try again."
for ent in groups: for ent in groups:
try: try:

View File

@@ -92,7 +92,7 @@ def main():
return 2 return 2
elif counter == -1: elif counter == -1:
print "These results are truncated." print "These results are truncated."
print "Please revine your search and try again." print "Please refine your search and try again."
for ent in users: for ent in users:
attr = ent.attrList() attr = ent.attrList()

View File

@@ -124,8 +124,8 @@ def main():
if not options.gn: if not options.gn:
while (cont != True): while (cont != True):
givenname = raw_input("First name: [%s] " % user.getValue('givenname')) givenname = raw_input("First name: [%s] " % user.getValue('givenname'))
if (ipavalidate.plain(givenname, notEmpty=False)): if (ipavalidate.String(givenname, notEmpty=False)):
print "Must be letters or '" print "Please enter a value"
else: else:
cont = True cont = True
if len(givenname) < 1: if len(givenname) < 1:
@@ -133,16 +133,16 @@ def main():
cont = True cont = True
else: else:
givenname = options.gn givenname = options.gn
if (ipavalidate.plain(givenname, notEmpty=True)): if (ipavalidate.String(givenname, notEmpty=True)):
print "First name must be letters or '" print "Please enter a value"
return 1 return 1
cont = False cont = False
if not options.sn: if not options.sn:
while (cont != True): while (cont != True):
lastname = raw_input(" Last name: [%s] " % user.getValue('sn')) lastname = raw_input(" Last name: [%s] " % user.getValue('sn'))
if (ipavalidate.plain(lastname, notEmpty=False)): if (ipavalidate.String(lastname, notEmpty=False)):
print "Must be letters or '" print "Please enter a value"
else: else:
cont = True cont = True
if len(lastname) < 1: if len(lastname) < 1:
@@ -150,21 +150,21 @@ def main():
cont = True cont = True
else: else:
lastname = options.sn lastname = options.sn
if (ipavalidate.plain(lastname, notEmpty=True)): if (ipavalidate.String(lastname, notEmpty=True)):
print "Last name must be letters or '" print "Please enter a value"
return 1 return 1
cont = False cont = False
if not options.mail: if not options.mail:
while (cont != True): while (cont != True):
mail = raw_input("E-mail addr: [%s]" % user.getValue('mail')) mail = raw_input("E-mail addr: [%s]" % user.getValue('mail'))
if (ipavalidate.email(mail, notEmpty=False)): if (ipavalidate.Email(mail, notEmpty=False)):
print "Must include a user and domain name" print "E-mail must include a user and domain name"
else: else:
cont = True cont = True
else: else:
mail = options.mail mail = options.mail
if (ipavalidate.email(mail)): if (ipavalidate.Email(mail)):
print "E-mail must include a user and domain name" print "E-mail must include a user and domain name"
return 1 return 1
@@ -174,8 +174,8 @@ def main():
if not options.gecos: if not options.gecos:
while (cont != True): while (cont != True):
gecos = raw_input("gecos: [%s] " % user.getValue('gecos')) gecos = raw_input("gecos: [%s] " % user.getValue('gecos'))
if (ipavalidate.plain(gecos, notEmpty=False)): if (ipavalidate.String(gecos, notEmpty=False)):
print "Must be letters, numbers, spaces or '" print "Please enter a value"
else: else:
cont = True cont = True
@@ -183,8 +183,8 @@ def main():
if not options.directory: if not options.directory:
while (cont != True): while (cont != True):
directory = raw_input("home directory: [%s] " % user.getValue('homeDirectory')) directory = raw_input("home directory: [%s] " % user.getValue('homeDirectory'))
if (ipavalidate.path(gecos, notEmpty=False)): if (ipavalidate.Path(gecos, notEmpty=False)):
print "Must be letters, numbers, spaces or '" print "Valid path is required"
else: else:
cont = True cont = True
cont = False cont = False

View File

@@ -3,7 +3,6 @@ NULL =
appdir = $(IPA_DATA_DIR)/ipaclient appdir = $(IPA_DATA_DIR)/ipaclient
app_PYTHON = \ app_PYTHON = \
__init__.py \ __init__.py \
dnsclient.py \
ipachangeconf.py \ ipachangeconf.py \
ipadiscovery.py \ ipadiscovery.py \
ntpconf.py \ ntpconf.py \

View File

@@ -18,5 +18,5 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__all__ = ["ipadiscovery", "ipachangeconf", "dnsclient"] __all__ = ["ipadiscovery", "ipachangeconf"]

View File

@@ -20,7 +20,7 @@
import socket import socket
import logging import logging
import dnsclient import ipa.dnsclient
import ldap import ldap
from ldap import LDAPError from ldap import LDAPError
@@ -191,10 +191,10 @@ class IPADiscovery:
# terminate the name # terminate the name
if not qname.endswith("."): if not qname.endswith("."):
qname += "." qname += "."
results = dnsclient.query(qname, dnsclient.DNS_C_IN, dnsclient.DNS_T_SRV) results = ipa.dnsclient.query(qname, ipa.dnsclient.DNS_C_IN, ipa.dnsclient.DNS_T_SRV)
for result in results: for result in results:
if result.dns_type == dnsclient.DNS_T_SRV: if result.dns_type == ipa.dnsclient.DNS_T_SRV:
rserver = result.rdata.server.rstrip(".") rserver = result.rdata.server.rstrip(".")
if result.rdata.port and result.rdata.port != 389: if result.rdata.port and result.rdata.port != 389:
rserver += ":" + str(result.rdata.port) rserver += ":" + str(result.rdata.port)
@@ -214,10 +214,10 @@ class IPADiscovery:
# terminate the name # terminate the name
if not qname.endswith("."): if not qname.endswith("."):
qname += "." qname += "."
results = dnsclient.query(qname, dnsclient.DNS_C_IN, dnsclient.DNS_T_TXT) results = ipa.dnsclient.query(qname, ipa.dnsclient.DNS_C_IN, ipa.dnsclient.DNS_T_TXT)
for result in results: for result in results:
if result.dns_type == dnsclient.DNS_T_TXT: if result.dns_type == ipa.dnsclient.DNS_T_TXT:
realm = result.rdata.data realm = result.rdata.data
if realm: if realm:
break break
@@ -228,9 +228,9 @@ class IPADiscovery:
# terminate the name # terminate the name
if not qname.endswith("."): if not qname.endswith("."):
qname += "." qname += "."
results = dnsclient.query(qname, dnsclient.DNS_C_IN, dnsclient.DNS_T_SRV) results = ipa.dnsclient.query(qname, ipa.dnsclient.DNS_C_IN, ipa.dnsclient.DNS_T_SRV)
for result in results: for result in results:
if result.dns_type == dnsclient.DNS_T_SRV: if result.dns_type == ipa.dnsclient.DNS_T_SRV:
qname = result.rdata.server.rstrip(".") qname = result.rdata.server.rstrip(".")
if result.rdata.port and result.rdata.port != 88: if result.rdata.port and result.rdata.port != 88:
qname += ":" + str(result.rdata.port) qname += ":" + str(result.rdata.port)

View File

@@ -1,3 +1,3 @@
include *.conf include *.conf
include freeipa-python.spec* include ipa-python.spec*

View File

@@ -19,7 +19,7 @@
import re import re
def email(mail, notEmpty=True): def Email(mail, notEmpty=True):
"""Do some basic validation of an e-mail address. """Do some basic validation of an e-mail address.
Return 0 if ok Return 0 if ok
Return 1 if not Return 1 if not
@@ -49,7 +49,7 @@ def email(mail, notEmpty=True):
return 0 return 0
def plain(text, notEmpty=False): def Plain(text, notEmpty=False, allowSpaces=True):
"""Do some basic validation of a plain text field """Do some basic validation of a plain text field
Return 0 if ok Return 0 if ok
Return 1 if not Return 1 if not
@@ -57,23 +57,33 @@ def plain(text, notEmpty=False):
If notEmpty is True the this will return an error if the field If notEmpty is True the this will return an error if the field
is "" or None. is "" or None.
""" """
textRE = re.compile(r"^[a-zA-Z_\-0-9\'\ ]*$") if (text is None) or (not text.strip()):
if not text and notEmpty is True:
return 1
if text is None:
if notEmpty is True: if notEmpty is True:
return 1 return 1
else: else:
return 0 return 0
if allowSpaces:
textRE = re.compile(r"^[a-zA-Z_\-0-9\'\ ]*$")
else:
textRE = re.compile(r"^[a-zA-Z_\-0-9\']*$")
if not textRE.search(text): if not textRE.search(text):
return 1 return 1
return 0 return 0
def path(text, notEmpty=False): def String(text, notEmpty=False):
"""A string type. This is much looser in what it allows than plain"""
if text is None or not text.strip():
if notEmpty is True:
return 1
else:
return 0
return 0
def Path(text, notEmpty=False):
"""Do some basic validation of a path """Do some basic validation of a path
Return 0 if ok Return 0 if ok
Return 1 if not Return 1 if not

View File

@@ -31,39 +31,54 @@ class TestValidate(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
def test_validemail(self): def test_validEmail(self):
self.assertEqual(0, ipavalidate.email("test@freeipa.org")) self.assertEqual(0, ipavalidate.Email("test@freeipa.org"))
self.assertEqual(0, ipavalidate.email("", notEmpty=False)) self.assertEqual(0, ipavalidate.Email("", notEmpty=False))
def test_invalidemail(self): def test_invalidEmail(self):
self.assertEqual(1, ipavalidate.email("test")) self.assertEqual(1, ipavalidate.Email("test"))
self.assertEqual(1, ipavalidate.email("test@freeipa")) self.assertEqual(1, ipavalidate.Email("test@freeipa"))
self.assertEqual(1, ipavalidate.email("test@.com")) self.assertEqual(1, ipavalidate.Email("test@.com"))
self.assertEqual(1, ipavalidate.email("")) self.assertEqual(1, ipavalidate.Email(""))
self.assertEqual(1, ipavalidate.email(None)) self.assertEqual(1, ipavalidate.Email(None))
def test_validplain(self): def test_validPlain(self):
self.assertEqual(0, ipavalidate.plain("Joe User")) self.assertEqual(0, ipavalidate.Plain("Joe User"))
self.assertEqual(0, ipavalidate.plain("Joe O'Malley")) self.assertEqual(0, ipavalidate.Plain("Joe O'Malley"))
self.assertEqual(0, ipavalidate.plain("", notEmpty=False)) self.assertEqual(0, ipavalidate.Plain("", notEmpty=False))
self.assertEqual(0, ipavalidate.plain(None, notEmpty=False)) self.assertEqual(0, ipavalidate.Plain(None, notEmpty=False))
self.assertEqual(0, ipavalidate.Plain("JoeUser", allowSpaces=False))
self.assertEqual(0, ipavalidate.Plain("JoeUser", allowSpaces=True))
def test_invalidplain(self): def test_invalidPlain(self):
self.assertEqual(1, ipavalidate.plain("Joe (User)")) self.assertEqual(1, ipavalidate.Plain("Joe (User)"))
self.assertEqual(1, ipavalidate.plain("", notEmpty=True)) self.assertEqual(1, ipavalidate.Plain("Joe C. User"))
self.assertEqual(1, ipavalidate.plain(None, notEmpty=True)) self.assertEqual(1, ipavalidate.Plain("", notEmpty=True))
self.assertEqual(1, ipavalidate.Plain(None, notEmpty=True))
self.assertEqual(1, ipavalidate.Plain("Joe User", allowSpaces=False))
def test_validpath(self): def test_validString(self):
self.assertEqual(0, ipavalidate.path("/")) self.assertEqual(0, ipavalidate.String("Joe User"))
self.assertEqual(0, ipavalidate.path("/home/user")) self.assertEqual(0, ipavalidate.String("Joe O'Malley"))
self.assertEqual(0, ipavalidate.path("../home/user")) self.assertEqual(1, ipavalidate.Plain("Joe C. User"))
self.assertEqual(0, ipavalidate.path("", notEmpty=False)) self.assertEqual(0, ipavalidate.String("", notEmpty=False))
self.assertEqual(0, ipavalidate.path(None, notEmpty=False)) self.assertEqual(0, ipavalidate.String(None, notEmpty=False))
def test_invalidpath(self): def test_invalidString(self):
self.assertEqual(1, ipavalidate.path("(foo)")) self.assertEqual(1, ipavalidate.String("", notEmpty=True))
self.assertEqual(1, ipavalidate.path("", notEmpty=True)) self.assertEqual(1, ipavalidate.String(None, notEmpty=True))
self.assertEqual(1, ipavalidate.path(None, notEmpty=True))
def test_validPath(self):
self.assertEqual(0, ipavalidate.Path("/"))
self.assertEqual(0, ipavalidate.Path("/home/user"))
self.assertEqual(0, ipavalidate.Path("../home/user"))
self.assertEqual(0, ipavalidate.Path("", notEmpty=False))
self.assertEqual(0, ipavalidate.Path(None, notEmpty=False))
def test_invalidPath(self):
self.assertEqual(1, ipavalidate.Path("(foo)"))
self.assertEqual(1, ipavalidate.Path("", notEmpty=True))
self.assertEqual(1, ipavalidate.Path(None, notEmpty=True))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@@ -9,8 +9,8 @@ class IPAPolicyFields(object):
ipasearchtimelimit = widgets.TextField(name="ipasearchtimelimit", label="Search Time Limit (sec.)", attrs=dict(size=6,maxlength=6)) ipasearchtimelimit = widgets.TextField(name="ipasearchtimelimit", label="Search Time Limit (sec.)", attrs=dict(size=6,maxlength=6))
ipasearchrecordslimit = widgets.TextField(name="ipasearchrecordslimit", label="Search Records Limit", attrs=dict(size=6,maxlength=6)) ipasearchrecordslimit = widgets.TextField(name="ipasearchrecordslimit", label="Search Records Limit", attrs=dict(size=6,maxlength=6))
ipahomesrootdir = widgets.TextField(name="ipahomesrootdir", label="Root for Home Directories") ipahomesrootdir = widgets.TextField(name="ipahomesrootdir", label="Root for Home Directories")
ipadefaultloginshell = widgets.TextField(name="ipadefaultloginshell", label="Default shell") ipadefaultloginshell = widgets.TextField(name="ipadefaultloginshell", label="Default Shell")
ipadefaultprimarygroup = widgets.TextField(name="ipadefaultprimarygroup", label="Default Users group") ipadefaultprimarygroup = widgets.TextField(name="ipadefaultprimarygroup", label="Default User Group")
ipamaxusernamelength = widgets.TextField(name="ipamaxusernamelength", label="Max. Username Length", attrs=dict(size=3,maxlength=3)) 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)) 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)) ipauserobjectclasses = widgets.TextField(name="ipauserobjectclasses", label="Default User Object Classes", attrs=dict(size=50))
@@ -24,9 +24,9 @@ class IPAPolicyFields(object):
# From cn=accounts # From cn=accounts
krbmaxpwdlife = widgets.TextField(name="krbmaxpwdlife", label="Max. Password Lifetime (days)", 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)) 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)) 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)) 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)) krbpwdhistorylength = widgets.TextField(name="krbpwdhistorylength", label="Password History Size", attrs=dict(size=3,maxlength=3))
password_orig = widgets.HiddenField(name="password_orig") password_orig = widgets.HiddenField(name="password_orig")

View File

@@ -395,9 +395,9 @@ ul.checkboxlist li input {
.sortcol { .sortcol {
cursor: pointer; cursor: pointer;
padding-right: 20px !important; padding-left: 10px !important;
background-repeat: no-repeat !important; background-repeat: no-repeat !important;
background-position: right center !important; background-position: left center !important;
text-decoration: underline; text-decoration: underline;
} }
.sortasc { .sortasc {

View File

@@ -71,7 +71,7 @@ class DelegationController(IPAController):
new_aci.source_group = kw.get('source_group_dn') new_aci.source_group = kw.get('source_group_dn')
new_aci.dest_group = kw.get('dest_group_dn') new_aci.dest_group = kw.get('dest_group_dn')
new_aci.attrs = kw.get('attrs') new_aci.attrs = kw.get('attrs')
if (new_aci.attrs, str): if isinstance(new_aci.attrs, str):
new_aci.attrs = [new_aci.attrs] new_aci.attrs = [new_aci.attrs]
# Look for an existing ACI of the same name # Look for an existing ACI of the same name

View File

@@ -23,6 +23,7 @@ dn: cn=accounts,$SUFFIX
changetype: modify changetype: modify
add: aci 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";) 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";)
aci: (targetattr = "aci")(version 3.0;acl "Admins can manage delegations"; allow (write, delete) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
dn: cn=services,cn=accounts,$SUFFIX dn: cn=services,cn=accounts,$SUFFIX
changetype: modify changetype: modify