From 1eec34393b8a7ccd420a7fa540462f5d8779977c Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Wed, 10 Sep 2008 15:56:11 -0400 Subject: 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 --- ipa-server/configure.ac | 1 + ipa-server/ipa-install/Makefile.am | 1 + ipa-server/ipa-install/updates/Makefile.am | 17 +++ ipa-server/ipa-install/updates/RFC2307bis.update | 65 +++++++++ ipa-server/ipa-install/updates/RFC4876.update | 146 +++++++++++++++++++++ ipa-server/ipa-install/updates/nss_ldap.update | 33 +++++ .../updates/schema_compatibility.update | 50 +++++++ ipa-server/ipa-ldap-updater | 76 +++++++---- ipa-server/ipa-server.spec.in | 6 + 9 files changed, 366 insertions(+), 29 deletions(-) create mode 100644 ipa-server/ipa-install/updates/Makefile.am create mode 100644 ipa-server/ipa-install/updates/RFC2307bis.update create mode 100644 ipa-server/ipa-install/updates/RFC4876.update create mode 100644 ipa-server/ipa-install/updates/nss_ldap.update create mode 100644 ipa-server/ipa-install/updates/schema_compatibility.update diff --git a/ipa-server/configure.ac b/ipa-server/configure.ac index 7260559c8..82ef5dcc9 100644 --- a/ipa-server/configure.ac +++ b/ipa-server/configure.ac @@ -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 diff --git a/ipa-server/ipa-install/Makefile.am b/ipa-server/ipa-install/Makefile.am index f82cd25e6..3f5661754 100644 --- a/ipa-server/ipa-install/Makefile.am +++ b/ipa-server/ipa-install/Makefile.am @@ -2,6 +2,7 @@ NULL = SUBDIRS = \ share \ + updates \ $(NULL) sbin_SCRIPTS = \ diff --git a/ipa-server/ipa-install/updates/Makefile.am b/ipa-server/ipa-install/updates/Makefile.am new file mode 100644 index 000000000..56ec3b25f --- /dev/null +++ b/ipa-server/ipa-install/updates/Makefile.am @@ -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 diff --git a/ipa-server/ipa-install/updates/RFC2307bis.update b/ipa-server/ipa-install/updates/RFC2307bis.update new file mode 100644 index 000000000..6d08d5786 --- /dev/null +++ b/ipa-server/ipa-install/updates/RFC2307bis.update @@ -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 ) ) diff --git a/ipa-server/ipa-install/updates/RFC4876.update b/ipa-server/ipa-install/updates/RFC4876.update new file mode 100644 index 000000000..5a372c201 --- /dev/null +++ b/ipa-server/ipa-install/updates/RFC4876.update @@ -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' ) diff --git a/ipa-server/ipa-install/updates/nss_ldap.update b/ipa-server/ipa-install/updates/nss_ldap.update new file mode 100644 index 000000000..e8c1e00f7 --- /dev/null +++ b/ipa-server/ipa-install/updates/nss_ldap.update @@ -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 diff --git a/ipa-server/ipa-install/updates/schema_compatibility.update b/ipa-server/ipa-install/updates/schema_compatibility.update new file mode 100644 index 000000000..71732c995 --- /dev/null +++ b/ipa-server/ipa-install/updates/schema_compatibility.update @@ -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") diff --git a/ipa-server/ipa-ldap-updater b/ipa-server/ipa-ldap-updater index df0a503c1..cd6cd2eee 100755 --- a/ipa-server/ipa-ldap-updater +++ b/ipa-server/ipa-ldap-updater @@ -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: diff --git a/ipa-server/ipa-server.spec.in b/ipa-server/ipa-server.spec.in index a33ea5c60..ecb27eae9 100644 --- a/ipa-server/ipa-server.spec.in +++ b/ipa-server/ipa-server.spec.in @@ -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 - 1.0.0-5 +- Add updates directory + * Tue Sep 9 2008 Rob Crittenden - 1.0.0-4 - Add ipa-ldap-updater -- cgit