Update files for the schema compatibility plugin and RFC4876 profiles

Also handle syntax errors a bit more gracefully and allow the updater to
work on more than one file at a time.

Adjust to new config.py and use a custom exception class for syntax errors.

Also fix a error in parsing the separate files

Include slapi-nis in Requires

Includes work provided by Martin Nagy

460055
This commit is contained in:
Rob Crittenden 2008-09-10 15:56:11 -04:00
parent ec57bc3e44
commit 1eec34393b
9 changed files with 366 additions and 29 deletions

View File

@ -247,6 +247,7 @@ AC_CONFIG_FILES([
ipa-gui/ipa_gui.egg-info/Makefile
ipa-install/Makefile
ipa-install/share/Makefile
ipa-install/updates/Makefile
ipa-kpasswd/Makefile
ipaserver/Makefile
ipa-slapi-plugins/Makefile

View File

@ -2,6 +2,7 @@ NULL =
SUBDIRS = \
share \
updates \
$(NULL)
sbin_SCRIPTS = \

View File

@ -0,0 +1,17 @@
NULL =
appdir = $(IPA_DATA_DIR)/updates
app_DATA = \
RFC4876.update \
RFC2307bis.update \
schema_compatibility.update \
nss_ldap.update \
$(NULL)
EXTRA_DIST = \
$(app_DATA) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

View File

@ -0,0 +1,65 @@
#
# Schema derived from RFC 2307bis:
# "An Approach for Using LDAP as a Network Information Service"
#
dn: cn=schema
add: attributeTypes:
( 1.3.6.1.1.1.1.28 NAME 'nisPublickey'
DESC 'nisPublickey'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
X-ORIGIN 'RFC2307bis' )
add:attributeTypes:
( 1.3.6.1.1.1.1.29 NAME 'nisSecretkey'
DESC 'nisSecretkey'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
X-ORIGIN 'RFC2307bis' )
add:attributeTypes:
( 1.3.6.1.4.1.1.1.1.12 SUP name NAME 'nisDomain'
DESC 'NIS domain'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
X-ORIGIN 'RFC2307bis' )
add:attributeTypes:
( 2.16.840.1.113730.3.1.30 NAME 'mgrpRFC822MailMember'
DESC 'mgrpRFC822MailMember'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
X-ORIGIN 'RFC2307bis' )
add:attributeTypes:
( 1.3.6.1.4.1.42.2.27.1.1.12 NAME 'nisNetIdUser'
DESC 'nisNetIdUser'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
X-ORIGIN 'RFC2307bis' )
add:attributeTypes:
( 1.3.6.1.4.1.42.2.27.1.1.13 NAME 'nisNetIdGroup'
DESC 'nisNetIdGroup'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
X-ORIGIN 'RFC2307bis' )
add:attributeTypes:
( 1.3.6.1.4.1.42.2.27.1.1.14 NAME 'nisNetIdHost'
DESC 'nisNetIdHost'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
X-ORIGIN 'RFC2307bis' )
add:objectClasses:
( 1.3.6.1.1.1.2.14 NAME 'nisKeyObject'
DESC 'nisKeyObject' SUP top
MUST ( cn $ nisPublickey $ nisSecretkey )
MAY ( uidNumber $ description ) )
add:objectClasses:
( 1.3.1.6.1.1.1.2.15 NAME 'nisDomainObject'
DESC 'nisDomainObject' SUP top AUXILIARY
MUST ( nisDomain ) )
add:objectClasses:
( 2.16.840.1.113730.3.2.4 NAME 'mailGroup'
DESC 'mailGroup' SUP top
MUST ( mail )
MAY ( cn $ mgrpRFC822MailMember ) )
add:objectClasses:
( 1.3.6.1.4.1.42.2.27.1.2.6 NAME 'nisNetId'
DESC 'nisNetId' SUP top
MUST ( cn )
MAY ( nisNetIdUser $ nisNetIdGroup $ nisNetIdHost ) )

View File

@ -0,0 +1,146 @@
#
# Schema more or less verbatim from RFC 4876:
# "A Configuration Profile Schema for Lightweight Directory Access
# Protocol (LDAP)-Based Agents"
#
dn: cn=schema
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.0 NAME 'defaultServerList'
DESC 'List of default servers'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.1 NAME 'defaultSearchBase'
DESC 'Default base for searches'
EQUALITY distinguishedNameMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
SINGLE-VALUE
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.2 NAME 'preferredServerList'
DESC 'List of preferred servers'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.3 NAME 'searchTimeLimit'
DESC 'Maximum time an agent or service allows for a
search to complete'
EQUALITY integerMatch
ORDERING integerOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.4 NAME 'bindTimeLimit'
DESC 'Maximum time an agent or service allows for a
bind operation to complete'
EQUALITY integerMatch
ORDERING integerOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.5 NAME 'followReferrals'
DESC 'An agent or service does or should follow referrals'
EQUALITY booleanMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
SINGLE-VALUE
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.6 NAME 'authenticationMethod'
DESC 'Identifies the types of authentication methods either
used, required, or provided by a service or peer'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.7 NAME 'profileTTL'
DESC 'Time to live, in seconds, before a profile is
considered stale'
EQUALITY integerMatch
ORDERING integerOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.9 NAME 'attributeMap'
DESC 'Attribute mappings used, required, or supported by an
agent or service'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.10 NAME 'credentialLevel'
DESC 'Identifies type of credentials either used, required,
or supported by an agent or service'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
SINGLE-VALUE
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.11 NAME 'objectclassMap'
DESC 'Object class mappings used, required, or supported by
an agent or service'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.12 NAME 'defaultSearchScope'
DESC 'Default scope used when performing a search'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
SINGLE-VALUE
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.13 NAME 'serviceCredentialLevel'
DESC 'Specifies the type of credentials either used, required,
or supported by a specific service'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.14 NAME 'serviceSearchDescriptor'
DESC 'Specifies search descriptors required, used, or
supported by a particular service or agent'
EQUALITY caseExactMatch
SUBSTR caseExactSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.15 NAME 'serviceAuthenticationMethod'
DESC 'Specifies types authentication methods either
used, required, or supported by a particular service'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
X-ORIGIN 'RFC4876' )
add:attributeTypes:
( 1.3.6.1.4.1.11.1.3.1.1.16 NAME 'dereferenceAliases'
DESC 'Specifies if a service or agent either requires,
supports, or uses dereferencing of aliases.'
EQUALITY booleanMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
SINGLE-VALUE
X-ORIGIN 'RFC4876' )
add:objectClasses:
( 1.3.6.1.4.1.11.1.3.1.2.5 NAME 'DUAConfigProfile'
SUP top STRUCTURAL
DESC 'Abstraction of a base configuration for a DUA'
MUST ( cn )
MAY ( defaultServerList $ preferredServerList $
defaultSearchBase $ defaultSearchScope $
searchTimeLimit $ bindTimeLimit $
credentialLevel $ authenticationMethod $
followReferrals $ dereferenceAliases $
serviceSearchDescriptor $ serviceCredentialLevel $
serviceAuthenticationMethod $ objectclassMap $
attributeMap $ profileTTL )
X-ORIGIN 'RFC4876' )

View File

@ -0,0 +1,33 @@
#
# Add profile for RFC 4876 agents (Solaris and HP/ux)
#
# Update the top-level entry
dn: $SUFFIX
add:objectClass: domain
add:objectClass: domainRelatedObject
add:objectClass: nisDomainObject
add:associatedDomain: $DOMAIN
add:nisDomain: $DOMAIN
# Add a place to store the nss_ldap default profile
dn: ou=profile,$SUFFIX
add: objectClass: top
add: objectClass: organizationalUnit
add: ou: profiles
# The DUA profile. On Solaris one can run:
# ldap_client init ipa.example.com
dn: cn=default,ou=profile,$SUFFIX
default:ObjectClass: top
default:ObjectClass: DUAConfigProfile
default:defaultServerList: $FQDN
default:defaultSearchBase: $SUFFIX
default:authenticationMethod: none
default:searchTimeLimit: 15
default:cn: default
default:serviceSearchDescriptor: passwd:cn=users,cn=accounts,$SUFFIX
default:serviceSearchDescriptor: group:cn=groups,cn=compat,$SUFFIX
default:bindTimeLimit: 5
default:objectClassMap: shadow:shadowAccount=posixAccount
default:followReferrals:TRUE

View File

@ -0,0 +1,50 @@
#
# Enable the Schema Compatibility plugin provided by slapi-nis.
#
# http://slapi-nis.fedorahosted.org/
#
dn: cn=Schema Compatibility, cn=plugins, cn=config
default:objectclass: top
default:objectclass: nsSlapdPlugin
default:objectclass: extensibleObject
default:cn: Schema Compatibility
default:nsslapd-pluginpath: /usr/lib$LIBARCH/dirsrv/plugins/schemacompat-plugin.so
default:nsslapd-plugininitfunc: schema_compat_plugin_init
default:nsslapd-plugintype: object
default:nsslapd-pluginenabled: on
default:nsslapd-pluginid: schema-compat-plugin
default:nsslapd-pluginversion: 0.8
default:nsslapd-pluginvendor: redhat.com
default:nsslapd-plugindescription: Schema Compatibility Plugin
dn: cn=users, cn=Schema Compatibility, cn=plugins, cn=config
default:objectClass: top
default:objectClass: extensibleObject
default:cn: users
default:schema-compat-container-group: cn=compat, $SUFFIX
default:schema-compat-container-rdn: cn=users
default:schema-compat-search-base: cn=users, cn=accounts, $SUFFIX
default:schema-compat-search-filter: objectclass=posixAccount
default:schema-compat-entry-rdn: uid=%{uid}
default:schema-compat-entry-attribute: objectclass=posixAccount
default:schema-compat-entry-attribute: gecos=%{cn}
default:schema-compat-entry-attribute: cn=%{cn}
default:schema-compat-entry-attribute: uidNumber=%{uidNumber}
default:schema-compat-entry-attribute: gidNumber=%{gidNumber}
default:schema-compat-entry-attribute: loginShell=%{loginShell}
default:schema-compat-entry-attribute: homeDirectory=%{homeDirectory}
dn: cn=groups, cn=Schema Compatibility, cn=plugins, cn=config
default:objectClass: top
default:objectClass: extensibleObject
default:cn: groups
default:schema-compat-container-group: cn=compat, $SUFFIX
default:schema-compat-container-rdn: cn=groups
default:schema-compat-search-base: cn=groups, cn=accounts, $SUFFIX
default:schema-compat-search-filter: objectclass=posixGroup
default:schema-compat-entry-rdn: cn=%{cn}
default:schema-compat-entry-attribute: objectclass=posixGroup
default:schema-compat-entry-attribute: gidNumber=%{gidNumber}
default:schema-compat-entry-attribute: memberUid=%{memberUid}
default:schema-compat-entry-attribute: memberUid=%deref("member","uid")
default:schema-compat-entry-attribute: memberUid=%referred("cn=users","memberOf","uid")

View File

@ -50,19 +50,26 @@ error was:
sub_dict = {}
live_run = True
class BadSyntax(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
def parse_options():
parser = OptionParser("%prog [options] input_file")
parser = OptionParser("%prog [options] input_file(s)")
parser.add_option("-d", "--debug", action="store_true", dest="debug",
help="Display debugging information about the update(s)")
parser.add_option("-t", "--test", action="store_true", dest="test",
help="Run through the update without changing anything")
args = config.init_config(sys.argv)
options, args = parser.parse_args(args)
config.add_standard_options(parser)
options, args = parser.parse_args()
if len(args) != 2:
if len(args) < 1:
parser.error("missing file operand")
config.init_config(options)
return options, args
@ -101,7 +108,7 @@ def detail_error(detail):
msg = msg + " " + info
return msg
def identify_arch():
"""On multi-arch systems some libraries may be in /lib64, /usr/lib64, etc.
Determine if a suffix is needed based on the current architecture.
@ -113,6 +120,14 @@ def identify_arch():
else:
return ""
def template_str(s):
global sub_dict
try:
return ipautil.template_str(s, sub_dict)
except KeyError, e:
raise BadSyntax("Unknown template keyword %s" % e)
def remove_quotes(line):
"""Remove leading and trailng double or single quotes"""
if line.startswith('"'):
@ -174,8 +189,6 @@ def entry_to_entity(ent):
def parse_update_file(conn, data):
"""Parse the update file into a dictonary of lists and apply the update
for each DN in the file."""
global sub_dict
valid_keywords = ["default", "add", "remove", "only"]
update = {}
dn = None
@ -194,10 +207,10 @@ def parse_update_file(conn, data):
update = {}
dn = line[3:].strip()
update['dn'] = ipautil.template_str(dn, sub_dict)
update['dn'] = template_str(dn)
else:
if dn is None:
raise SyntaxError, "dn is not defined in the update"
raise BadSyntax, "dn is not defined in the update"
if line.startswith(' '):
v = d[len(d) - 1]
@ -208,16 +221,16 @@ def parse_update_file(conn, data):
line = line.strip()
values = line.split(':', 2)
if len(values) != 3:
raise SyntaxError, "Bad formatting on line %d: %s" % (lcount,line)
raise BadSyntax, "Bad formatting on line %d: %s" % (lcount,line)
index = values[0].strip().lower()
if index not in valid_keywords:
raise SyntaxError, "Unknown keyword %s" % index
raise BadSyntax, "Unknown keyword %s" % index
attr = values[1].strip()
value = values[2].strip()
value = ipautil.template_str(value, sub_dict)
value = template_str(value)
new_value = ""
if index == "default":
@ -235,7 +248,7 @@ def parse_update_file(conn, data):
if dn is not None:
update_record(conn, update)
return
return
def create_index_task(conn, attribute):
"""Create a task to update an index for an attribute"""
@ -247,7 +260,7 @@ def create_index_task(conn, attribute):
# randomness for good measure.
sub_dict['TIME'] = int(time.time()) + r.randint(0,10000)
cn = ipautil.template_str("indextask_$TIME", sub_dict)
cn = template_str("indextask_$TIME")
dn = "cn=%s, cn=index, cn=tasks, cn=config" % cn
e = ipaldap.Entry(dn)
@ -256,7 +269,7 @@ def create_index_task(conn, attribute):
e.setValue('cn', cn)
e.setValue('nsInstance', 'userRoot')
e.setValues('nsIndexAttribute', attribute)
logging.info("Creating task to index attribute: %s", attribute)
logging.debug("Task id: %s", dn)
@ -426,8 +439,7 @@ def update_record(conn, update):
e = get_entry(conn, new_entry.dn)
if len(e) > 1:
# we should only ever get back one entry
# FIXME, wrong exception type
raise SyntaxError, "More than 1 entry returned on a dn search!? %s" % new_entry.dn
raise BadSyntax, "More than 1 entry returned on a dn search!? %s" % new_entry.dn
entry = entry_to_entity(e[0])
found = True
logging.info("Updating existing entry: %s", entry.dn)
@ -509,26 +521,32 @@ def main():
dirman_password = get_dirman_password(fqdn)
try:
data = read_file(args[1])
except Exception, e:
print e
sys.exit(1)
for a in args:
try:
logging.info("Parsing file %s" % a)
data = read_file(a)
except Exception, e:
print e
sys.exit(1)
conn = None
conn = None
try:
conn = ipaldap.IPAdmin(fqdn)
conn.do_simple_bind(bindpw=dirman_password)
parse_update_file(conn, data)
finally:
if conn: conn.unbind()
try:
conn = ipaldap.IPAdmin(fqdn)
conn.do_simple_bind(bindpw=dirman_password)
parse_update_file(conn, data)
finally:
if conn: conn.unbind()
return
try:
if __name__ == "__main__":
sys.exit(main())
except BadSyntax, e:
print "There is a syntax error in this update file:"
print " %s" % e
sys.exit(1)
except SystemExit, e:
sys.exit(e)
except KeyboardInterrupt, e:

View File

@ -41,6 +41,7 @@ Requires: python-tgexpandingformwidget
Requires: acl
Requires: python-pyasn1
Requires: libcap
Requires: slapi-nis
Conflicts: mod_ssl
@ -149,6 +150,8 @@ fi
%{_usr}/share/ipa/ipaserver/*
%dir %{_usr}/share/ipa/locales/
%{_usr}/share/ipa/locales/*
%dir %{_usr}/share/ipa/updates/
%{_usr}/share/ipa/updates/*
%dir %{python_sitelib}/ipaserver
%{python_sitelib}/ipaserver/*.py*
@ -173,6 +176,9 @@ fi
%{_mandir}/man1/ipa-server-install.1.gz
%changelog
* Tue Sep 9 2008 Rob Crittenden <rcritten@redhat.com> - 1.0.0-5
- Add updates directory
* Tue Sep 9 2008 Rob Crittenden <rcritten@redhat.com> - 1.0.0-4
- Add ipa-ldap-updater