diff --git a/.arclint b/.arclint --- a/.arclint +++ b/.arclint @@ -19,11 +19,13 @@ "E131": "disabled", "E201": "disabled", "E202": "disabled", + "E221": "disabled", "E225": "disabled", "E231": "disabled", "E251": "disabled", "E261": "disabled", "E265": "disabled", + "E266": "disabled", "E302": "disabled", "E303": "disabled", "E402": "disabled", diff --git a/pykolab/auth/__init__.py b/pykolab/auth/__init__.py --- a/pykolab/auth/__init__.py +++ b/pykolab/auth/__init__.py @@ -49,7 +49,6 @@ Login is a simple list of username, password, service and, optionally, the realm. """ - if len(login) == 3: # The realm has not been specified. See if we know whether or not # to use virtual_domains, as this may be a cause for the realm not @@ -58,12 +57,12 @@ # TODO: Insert debug statements #if use_virtual_domains == "userid": - #print "# Derive domain from login[0]" + # print "# Derive domain from login[0]" #elif not use_virtual_domains: - #print "# Explicitly do not user virtual domains??" + # print "# Explicitly do not user virtual domains??" #else: - ## Do use virtual domains, derive domain from login[0] - #print "# Derive domain from login[0]" + # ## Do use virtual domains, derive domain from login[0] + # print "# Derive domain from login[0]" if len(login[0].split('@')) > 1: domain = login[0].split('@')[1] @@ -86,7 +85,7 @@ back to the primary domain specified by the configuration. """ - log.debug(_("Called for domain %r") % (domain), level=8) + log.debug(_("Called for domain %r") % (domain), level=5) if not self._auth == None: return @@ -99,14 +98,10 @@ section = 'kolab' domain = conf.get('kolab', 'primary_domain') else: + log.debug(_("Getting list of domains for %s ...") % (domain), level=5) self.list_domains(domain) section = domain - log.debug( - _("Using section %s and domain %s") % (section,domain), - level=8 - ) - if not self.domains == None and self.domains.has_key(domain): section = self.domains[domain] domain = self.domains[domain] @@ -116,13 +111,6 @@ level=8 ) - log.debug( - _("Connecting to Authentication backend for domain %s") % ( - domain - ), - level=8 - ) - if not conf.has_section(section): section = 'kolab' @@ -142,21 +130,32 @@ level=8 ) + _auth_mechanism = conf.get(section, 'auth_mechanism') + # Get the actual authentication and authorization backend. - if conf.get(section, 'auth_mechanism') == 'ldap': - log.debug(_("Starting LDAP..."), level=8) + if _auth_mechanism == 'ldap': + log.debug(_("Initializing LDAP..."), level=8) from pykolab.auth import ldap self._auth = ldap.LDAP(self.domain) - elif conf.get(section, 'auth_mechanism') == 'sql': + elif _auth_mechanism == 'sql': + log.debug(_("Initializing SQL..."), level=8) from pykolab.auth import sql self._auth = sql.SQL(self.domain) else: - log.debug(_("Starting LDAP..."), level=8) + log.debug(_("Fallback to LDAP. Initializing ..."), level=5) from pykolab.auth import ldap self._auth = ldap.LDAP(self.domain) + log.debug( + _("Connecting to Authentication %s backend for domain %s") % ( + _auth_mechanism, + domain + ), + level=5 + ) + self._auth.connect() def disconnect(self, domain=None): @@ -277,6 +276,8 @@ for secondary in secondaries: self.domains[secondary.lower()] = primary.lower() + log.debug(_("List of domains for %s is: %s") % (domain, ", ".join(self.domains)), level=8) + return self.domains def synchronize(self, mode=0, callback=None): @@ -291,6 +292,9 @@ def primary_domain_for_naming_context(self, domain): return self._auth._primary_domain_for_naming_context(domain) + def add_entry(self, domain, entry): + return self._auth._add_entry(entry) + def get_entry_attribute(self, domain, entry, attribute): return self._auth.get_entry_attribute(entry, attribute) diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py --- a/pykolab/auth/ldap/__init__.py +++ b/pykolab/auth/ldap/__init__.py @@ -1525,6 +1525,26 @@ log.debug(_("bind_priv() called but already bound"), level=8) return True + def _add_entry(self, entry): + """ + Add generic type of entry. + + Initialy created for setup-kolab to be able to add AD schema + by parsing Kolab AD schema ldif file + + entry - dictionary, same as search results + """ + self._bind() + + entry_dn, entry_attrs = entry + + log.debug(_("Entry DN: %s, with attributes: %s") % (entry_dn, entry_attrs), level=8) + + self.ldap.add_s( + entry_dn, + entry_attrs + ) + def _change_add_group(self, entry, change): """ An entry of type group was added. diff --git a/pykolab/conf/__init__.py b/pykolab/conf/__init__.py --- a/pykolab/conf/__init__.py +++ b/pykolab/conf/__init__.py @@ -329,7 +329,7 @@ if self.cli_args: if len(self.cli_args) >= 1: if hasattr(self,"command_%s" % self.cli_args[0].replace('-','_')): - exec("self.command_%s(%r)" % (self.cli_args[0].replace('-','_'), self.cli_args[1:])) + exec("self.command_%s(%r)" % (self.cli_args[0].replace('-','_'), self.cli_args[1:])) else: print >> sys.stderr, _("No command supplied") @@ -367,7 +367,7 @@ continue for key in keys: - print "%s_%s = %s" % (mode, key ,self.cfg_parser.get(mode,key)) + print "%s_%s = %s" % (mode, key, self.cfg_parser.get(mode,key)) def read_config(self, value=None): """ @@ -385,7 +385,7 @@ if hasattr(self, 'cli_keywords') and hasattr(self.cli_keywords, 'config_file'): self.cli_keywords.config_file = value - self.defaults.config_file = value + self.config_file = value def command_get(self, *args, **kw): diff --git a/pykolab/setup/setup_ldap.py b/pykolab/setup/setup_ldap.py --- a/pykolab/setup/setup_ldap.py +++ b/pykolab/setup/setup_ldap.py @@ -17,11 +17,15 @@ # along with this program. If not, see . # +import base64 import ldap import ldap.modlist +import ldap.dn +import ldif import os import pwd import shutil +import socket import subprocess import tempfile import time @@ -35,6 +39,9 @@ from pykolab.constants import * from pykolab.translate import _ +from StringIO import StringIO +from jinja2 import Template + log = pykolab.getLogger('pykolab.setup') conf = pykolab.getConf() @@ -95,20 +102,461 @@ def description(): return _("Setup LDAP.") +def get_answers(): + + answers = {} + + if conf.fqdn: + answers['fqdn'] = conf.fqdn + answers['hostname'] = conf.fqdn.split('.')[0] + answers['domain'] = '.'.join(conf.fqdn.split('.')[1:]) + else: + answers['fqdn'] = fqdn + answers['hostname'] = hostname.split('.')[0] + answers['domain'] = domainname + + print >> sys.stderr, utils.multiline_message( + _(""" + This setup procedure plans to set up Kolab Groupware for + the following domain name space. This domain name is + obtained from the reverse DNS entry on your network + interface. Please confirm this is the appropriate domain + name space. + """) + ) + + answers['domain'] = utils.ask_question( + _("Domain name to use"), + default = answers['domain'] + ) + + answers['nodotdomain'] = answers['domain'].replace('.','_') + + print >> sys.stderr, utils.multiline_message( + _(""" + The standard root dn we composed for you follows. Please + confirm this is the root dn you wish to use. + """) + ) + + answers['base_dn'] = utils.ask_question( + _("LDAP base DN"), + default = utils.standard_root_dn(answers['domain']) + ) + + if conf.with_ad: + print >> sys.stderr, utils.multiline_message( + _(""" + Please supply Active Directory Administrator bind DN. + This will be used to bind to Active Directory LDAP to + perform various administrative tasks. This user should + have necessary privileges in Active Directory already. + """) + ) + + answers['bind_dn'] = utils.ask_question( + _("AD Administrator bind DN"), + default="CN=Administrator,CN=Users,%s" % answers['base_dn'] + ) + elif not conf.with_ad and not conf.with_openldap: + # Assuming Dirsrv + answers['bind_dn'] = "cn=Directory Manager" + log.info(_("Default administrative bind dn for Directory Server: %s") % answers['bind_dn']) + + # Get cn=Directory Manger password + if conf.directory_manager_pwd is not None: + # password was set with command line options + answers['bind_pw'] = conf.directory_manager_pwd + else: + # Ask for password + if conf.with_ad: + print >> sys.stderr, utils.multiline_message( + _(""" + Please supply a password for the Active Directory Administrator user. + """) + ) + _short_text = _("Active Directory administrator password") + else: + print >> sys.stderr, utils.multiline_message( + _(""" + Please supply a password for the LDAP Directory Manager + user, which is the administrator user you will be using + to at least initially log in to the Web Admin, and that + Kolab uses to perform administrative tasks. + """) + ) + _short_text = _("Directory Manager password") + + answers['bind_pw'] = utils.ask_question( + _short_text, + default=utils.generate_password(), + password=True, + confirm=True + ) + + # Ask only if LDAP is DS + if not conf.with_openldap and not conf.with_ad: + # Get Dirsrv Admin server admin user pass + print >> sys.stderr, utils.multiline_message( + _(""" + Please supply a password for the LDAP administrator user + 'admin', used to login to the graphical console of 389 + Directory server. + """) + ) + + answers['admin_pass'] = utils.ask_question( + _("Administrator password"), + default=utils.generate_password(), + password=True, + confirm=True + ) + + + # Set users for DS LDAP + print >> sys.stderr, utils.multiline_message( + _(""" + Please choose the system user and group the service + should use to run under. These should be existing, + unprivileged, local system POSIX accounts with no shell. + """) + ) + + try: + pw = pwd.getpwnam("dirsrv") + except: + answers['userid'] = utils.ask_question(_("User"), default="nobody") + answers['group'] = utils.ask_question(_("Group"), default="nobody") + else: + answers['userid'] = utils.ask_question(_("User"), default="dirsrv") + answers['group'] = utils.ask_question(_("Group"), default="dirsrv") + + # Ask if Active Directory + elif conf.with_ad and not conf.with_openldap: + # Get answers for Active Directory setup + print >> sys.stderr, utils.multiline_message( + _(""" + Specify basic Active Directory connection information. + """) + ) + + print >> sys.stderr, utils.multiline_message( + _(""" + Specify LDAP tree DN for Kolab related data. Kolab specific + LDAP records such as Kolab domain information, special users + for Kolab, shared folders and resources LDAP information. + """) + ) + + answers['kolab_tree_dn'] = utils.ask_question( + _("AD LDAP tree for Kolab:"), + default="OU=Kolab,%s" % answers['base_dn'] + ) + + answers['specialuser_base_dn'] = "OU=Special Users,%s" % answers['kolab_tree_dn'] + answers['cyrus_admin_dn'] = "CN=Cyrus Admin,%s" % answers['specialuser_base_dn'] + answers['service_bind_dn'] = "CN=Kolab Service,%s" % answers['specialuser_base_dn'] + answers['domain_base_dn'] = "OU=Domains,%s" % answers['kolab_tree_dn'] + answers['group_base_dn'] = "OU=Groups,%s" % answers['kolab_tree_dn'] + answers['resource_base_dn'] = "OU=Resources,%s" % answers['kolab_tree_dn'] + answers['sharedfolder_base_dn'] = "OU=Shared Folders,%s" % answers['kolab_tree_dn'] + answers['user_base_dn'] = "CN=Users,%s" % answers['base_dn'] + answers['kolab_user_base_dn'] = answers['user_base_dn'] + + print >> sys.stderr, utils.multiline_message( + _(""" + Please provide Active Directory server URI. Setup will use it to load + Kolab schema into AD and then will configure Kolab to run with AD as + LDAP server. Schema can be loaded to schema master server only wherefore + this needs to be URI specifying AD schema master. You can change AD server + URI later, in kolab.conf file. + """) + ) + + #answers['ldap_uri'] = utils.ask_question( + # _("AD server URI"), + # default="ldap://adserver.domain:389" + # ) + answers['ldap_uri'] = utils.ask_question( + _("AD server URI"), + default="ldaps://w2k16-1.ad-lab.loc:636" + ) + + print >> sys.stderr, utils.multiline_message( + _(""" + Please supply a Cyrus Administrator password. This + password is used by Kolab to execute administrative + tasks in Cyrus IMAP. You may also need the password + yourself to troubleshoot Cyrus IMAP and/or perform + other administrative tasks against Cyrus IMAP directly. + """) + ) + + answers['cyrus_admin_password'] = utils.ask_question( + _("Cyrus Administrator password"), + default=utils.generate_password(), + password=True, + confirm=True + ) + + print >> sys.stderr, utils.multiline_message( + _(""" + Please supply a Kolab Service account password. This + account is used by various services such as Postfix, + and Roundcube, as anonymous binds to the LDAP server + will not be allowed. + """) + ) + + answers['service_bind_pw'] = utils.ask_question( + _("Kolab Service password"), + default=utils.generate_password(), + password=True, + confirm=True + ) + + return answers + +def find_template(file_name=None): + """ + Function to find template file. + """ + + if os.path.isfile('/etc/kolab/templates/%s' % file_name): + template_file = '/etc/kolab/templates/%s' % file_name + elif os.path.isfile('/usr/share/kolab/templates/%s' % file_name): + template_file = '/usr/share/kolab/templates/%s' % file_name + elif os.path.isfile(os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', template ))): + template_file = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', template )) + else: + log.error(_("Template %s not found.")) + return None + + return template_file + +def ad_schema_import(*args, **kwargs): + """ + Function to load Kolab schema to Active Directory + Does checks if ldap_uri points to Schema master + """ + conf.ldap_uri = kwargs['ldap_uri'] + + # Create LDAP connection + auth = Auth(kwargs['domain']) + auth.connect() + auth._auth.connect() + auth._auth._bind(kwargs['bind_dn'], kwargs['bind_pw']) + + ad_schema_naming_context = auth._auth.get_entry_attribute("", "schemaNamingContext") + + log.debug("AD Schema Naming context is %s" % ad_schema_naming_context, level=8) + + # Find out AD schema master server. Only this server has writable copy of + # AD schema. First need to get fsmoRoleOwner attribute value. + ad_fsmo_attribute = auth._auth.get_entry_attribute(ad_schema_naming_context, "fSMORoleOwner") + + log.debug(_("AD FSMO Role Owner entry is: %s") % ad_fsmo_attribute, level=8) + + # Now get parent of fsmoRoleOwner + if ad_fsmo_attribute is not None: + ad_fsmo_dn = ldap.dn.explode_dn(ad_fsmo_attribute) + ad_fsmo_parent = ','.join(ad_fsmo_dn[1:]) + + log.debug(_("AD FSMO entry parent is: %s") % ad_fsmo_parent, level=8) + + # Look for dNSHostName attribute of the parent + if ad_fsmo_parent is not None: + ad_schema_master = auth._auth.get_entry_attribute(ad_fsmo_parent, "dNSHostName") + log.debug(_("AD schema master server is: %s") % ad_schema_master, level=8) + + ( + _ad_protocol, + _ad_server, + _ad_port, + _ad_base_dn, + _ad_attr, + _ad_scope, + _ad_filter + ) = utils.parse_ldap_uri(conf.ldap_uri) + + if not _ad_server == ad_schema_master: + log.debug(_("AD schema master %s is not the same as %s.") % (ad_schema_master, _ad_server), level=5) + log.error(_("Cannot install Kolab schema on AD server which is not schema master")) + sys.exit(1) + + template_file = find_template(file_name='kolab3-ad-schema.ldif.j2') + + if not template_file == None: + log.debug(_("Using template file %r") % (template_file), level=8) + fp = open(template_file, 'r') + template_definition = fp.read() + fp.close() + + schema_template = Template(template_definition) + + log.debug( + _("Successfully compiled template %r, ready to add schema to AD server") % (template_file), + level=8 + ) + + schema_ldif = StringIO( + schema_template.render(ad_schema_naming_context=ad_schema_naming_context).__str__() + ) + + parser = ldif.LDIFRecordList(schema_ldif) + parser.parse() + + # If silent install or install without asking questions + # when no need to ask confirmation + if kwargs['confirm']: + + print >> sys.stderr, utils.multiline_message( + _(""" + Setup is going to add Kolab schema elements to + Active Directory. This is ireversable operation. + """) + ) + + confirmed = utils.ask_confirmation( + _("Modify Active Directory schema?"), + default="y" + ) + + else: + # this is silent install + confirmed = True + + if confirmed: + for dn, attributes in parser.all_records: + log.debug("Adding DN: %s" % dn, level=5) + try: + add_attributes = ldap.modlist.addModlist(attributes) + auth._auth.ldap.add_s(dn, add_attributes) + except ldap.ALREADY_EXISTS: + log.warning(_("Schema element already exist: %s") % dn) + continue + except ldap.NO_SUCH_ATTRIBUTE: + log.warning(_("Could not add %s schema element. Force update and retry.") % dn) + try: + auth._auth.set_entry_attribute("", "schemaUpdateNow", "1") + time.sleep(2) + auth._auth.ldap.add_s(dn, add_attributes) + except: + log.error(_("Failed to add %s Kolab schema element.") % dn) + sys.exit(1) + except Exception, errorMsg: + log.error(_("Could not add schema element: %s") % dn) + log.error(_("Error: %s") % errorMsg ) + sys.exit(1) + else: + log.error(_("Kolab schema must be installed before you continue.")) + sys.exit(1) + +def ldap_data_load(*args, **kwargs): + """ + Function to setup LDAP tree + """ + conf.ldap_uri = kwargs['ldap_uri'] + + # Create LDAP connection + auth = Auth(kwargs['domain']) + auth.connect() + auth._auth.connect() + auth._auth._bind(kwargs['bind_dn'], kwargs['bind_pw']) + + ( + _protocol, + _server, + _port, + _base_dn, + _attr, + _scope, + _filter + ) = utils.parse_ldap_uri(conf.ldap_uri) + + ad_ldaps = False + if _protocol == 'ldap' and conf.with_ad: + log.info(_("Active Directory passwords cannot be set over %s protocol.") % (_protocol)) + elif _protocol == "ldaps" and conf.with_ad: + ad_ldaps = True + + if not kwargs['template'] == None: + log.debug(_("Using template file %r") % (kwargs['template']), level=8) + fp = open(kwargs['template'], 'r') + template_definition = fp.read() + fp.close() + + _template_vars = kwargs + _template_vars['ad_ldaps'] = ad_ldaps + _template_vars['cyrus_admin_password_base64'] = base64.b64encode( + unicode("\"" + kwargs['cyrus_admin_password'] + "\"", "iso-8859-1").encode('utf-16-le') + ) + _template_vars['service_bind_pw_base64'] = base64.b64encode( + unicode("\"" + kwargs['service_bind_pw'] + "\"", "iso-8859-1").encode('utf-16-le') + ) + + compiled_template = Template(template_definition) + + log.debug(_("Successfully compiled template %r") % (kwargs['template']), level=1) + + rendered_template = compiled_template.render(**_template_vars).__str__() + + log.debug(_("Importing LDIF %s") % rendered_template, level=8) + + template_ldif = StringIO(rendered_template) + + parser = ldif.LDIFRecordList(template_ldif) + parser.parse() + + for dn, attributes in parser.all_records: + log.debug("Adding DN: %s" % dn, level=5) + try: + add_attributes = ldap.modlist.addModlist(attributes) + # Do the actual synchronous add-operation to the ldapserver + auth._auth.ldap.add_s(dn, add_attributes) + except ldap.ALREADY_EXISTS: + log.warning(_("Object already exist: %s") % dn) + continue + except Exception, errorMsg: + log.error(_("Could not add object: %s") % dn) + log.error(_("Error: %s") % errorMsg ) + sys.exit(1) + + def execute(*args, **kw): ask_questions = True + _input = {} + if not conf.config_file == conf.defaults.config_file: + log.debug(_("Using supplied config file to answer all questions"), level=5) ask_questions = False + if conf.fqdn is not None: + log.debug(_("FQDN specified. Setting other dependend values"), level=8) + _input['fqdn'] = conf.fqdn + _input['hostname'] = conf.fqdn.split('.')[0] + _input['domain'] = '.'.join(conf.fqdn.split('.')[1:]) + else: + log.debug(_("FQDN not specified. Derriving from defalts"), level=8) + _input['fqdn'] = fqdn + _input['hostname'] = hostname.split('.')[0] + + if conf.get('kolab', 'primary_domain') is not None: + _input['domain'] = conf.get('kolab', 'primary_domain') + else: + _input['domain'] = domainname + + if conf.get('ldap', 'base_dn') is not None: + _input['base_dn'] = conf.get('ldap', 'base_dn') + else: + _input['base_dn'] = utils.standard_root_dn(_input['domain']) if conf.without_ldap: print >> sys.stderr, _("Skipping setup of LDAP, as specified") return - _input = {} - if conf.with_openldap and not conf.with_ad: - + conf.command_set('ldap', 'type', 'openldap') conf.command_set('ldap', 'unique_attribute', 'entryuuid') fp = open(conf.defaults.config_file, "w+") @@ -118,14 +566,69 @@ return elif conf.with_ad and not conf.with_openldap: - conf.command_set('ldap', 'auth_attributes', 'samaccountname') - conf.command_set('ldap', 'modifytimestamp_format', '%%Y%%m%%d%%H%%M%%S.0Z') - conf.command_set('ldap', 'unique_attribute', 'userprincipalname') + + if ask_questions: + _input = get_answers() + else: + # Get all the information from config file supplied as CLI -c param + _input['ldap_uri'] = conf.get('ldap', 'ldap_uri') + _input['bind_dn'] = conf.get('ldap', 'bind_dn') + _input['bind_pw'] = conf.get('ldap', 'bind_pw') + _input['service_bind_dn'] = conf.get('ldap', 'service_bind_dn') + _input['service_bind_pw'] = conf.get('ldap', 'service_bind_pw') + _input['user_base_dn'] = conf.get('ldap', 'user_base_dn') + _input['kolab_user_base_dn'] = conf.get('ldap', 'kolab_user_base_dn') + _input['group_base_dn'] = conf.get('ldap', 'group_base_dn') + _input['sharedfolder_base_dn'] = conf.get('ldap', 'sharedfolder_base_dn') + _input['resource_base_dn'] = conf.get('ldap', 'resource_base_dn') + _input['domain_base_dn'] = conf.get('ldap', 'domain_base_dn') + _input['cyrus_admin_password'] = conf.get('cyrus-imap', 'admin_password') + + # Things which are specific to AD and are not in config file + _input['kolab_tree_dn'] = "OU=Kolab,%s" % _input['base_dn'] + _input['specialuser_base_dn'] = "OU=Special Users,%s" % _input['kolab_tree_dn'] + _input['cyrus_admin_dn'] = "CN=Cyrus Admin,%s" % _input['specialuser_base_dn'] + + conf.command_set('ldap', 'type', 'ad') + conf.command_set('ldap', 'auth_attributes', 'mail, kolabAlias, uid, samaccountname') + conf.command_set('ldap', 'mail_attributes', 'mail, kolabAlias') + conf.command_set('ldap', 'modifytimestamp_format', '%Y%m%d%H%M%S.0Z') + conf.command_set('ldap', 'unique_attribute', 'objectguid') + conf.command_set('ldap', 'sharedfolder_acl_entry_attribute', 'kolabImapACL') + conf.command_set('ldap', 'mailserver_attribute', 'kolabMailhost') + conf.command_set('ldap', 'quota_attribute', 'kolabMailquota') # TODO: These attributes need to be checked - conf.command_set('ldap', 'mail_attributes', 'mail') - conf.command_set('ldap', 'mailserver_attributes', 'mailhost') - conf.command_set('ldap', 'quota_attribute', 'mailquota') + conf.command_set('ldap', 'ldap_uri', _input['ldap_uri']) + conf.command_set('ldap', 'base_dn', _input['base_dn']) + conf.command_set('ldap', 'bind_dn', _input['bind_dn']) + conf.command_set('ldap', 'bind_pw', _input['bind_pw']) + conf.command_set('ldap', 'service_bind_dn', _input['service_bind_dn']) + conf.command_set('ldap', 'service_bind_pw', _input['service_bind_pw']) + conf.command_set('ldap', 'user_base_dn', _input['user_base_dn']) + conf.command_set('ldap', 'kolab_user_base_dn', _input['kolab_user_base_dn']) + conf.command_set('ldap', 'group_base_dn', _input['group_base_dn']) + conf.command_set('ldap', 'sharedfolder_base_dn', _input['sharedfolder_base_dn']) + conf.command_set('ldap', 'resource_base_dn', _input['resource_base_dn']) + conf.command_set('ldap', 'domain_base_dn', _input['domain_base_dn']) + + conf.command_set('kolab', 'primary_domain', _input['domain']) + conf.command_set('cyrus-imap', 'admin_password', _input['cyrus_admin_password']) + + log.error("%r" % _input) + + _input['confirm'] = ask_questions + + ad_schema_import(**_input) + + templates = ['kolab-ad-tree.ldif.j2', 'kolab-ad-special-users.ldif.j2', 'kolab-ad-domain.ldif.j2'] + for template in templates: + _input['template'] = find_template(file_name=template) + + if _input['template'] is not None: + ldap_data_load(**_input) + else: + continue return @@ -139,6 +642,7 @@ sys.exit(1) + # This is a point where setup starts cofigure local Directory Server # Pre-execution checks for path, directories, files in os.walk('/etc/dirsrv/'): for direct in directories: @@ -155,63 +659,11 @@ sys.exit(1) - _input = {} - if ask_questions: - print >> sys.stderr, utils.multiline_message( - _(""" - Please supply a password for the LDAP administrator user - 'admin', used to login to the graphical console of 389 - Directory server. - """) - ) - - _input['admin_pass'] = utils.ask_question( - _("Administrator password"), - default=utils.generate_password(), - password=True, - confirm=True - ) - - if conf.directory_manager_pwd is not None: - _input['dirmgr_pass'] = conf.directory_manager_pwd - else: - print >> sys.stderr, utils.multiline_message( - _(""" - Please supply a password for the LDAP Directory Manager - user, which is the administrator user you will be using - to at least initially log in to the Web Admin, and that - Kolab uses to perform administrative tasks. - """) - ) - - _input['dirmgr_pass'] = utils.ask_question( - _("Directory Manager password"), - default=utils.generate_password(), - password=True, - confirm=True - ) - - print >> sys.stderr, utils.multiline_message( - _(""" - Please choose the system user and group the service - should use to run under. These should be existing, - unprivileged, local system POSIX accounts with no shell. - """) - ) - - try: - pw = pwd.getpwnam("dirsrv") - except: - _input['userid'] = utils.ask_question(_("User"), default="nobody") - _input['group'] = utils.ask_question(_("Group"), default="nobody") - else: - _input['userid'] = utils.ask_question(_("User"), default="dirsrv") - _input['group'] = utils.ask_question(_("Group"), default="dirsrv") - + _input = get_answers() else: _input['admin_pass'] = conf.get('ldap', 'bind_pw') - _input['dirmgr_pass'] = conf.get('ldap', 'bind_pw') + _input['bind_pw'] = conf.get('ldap', 'bind_pw') try: pw = pwd.getpwnam("dirsrv") except: @@ -228,101 +680,33 @@ # # TODO^2: This should be confirmed. - if conf.fqdn: - _input['fqdn'] = conf.fqdn - _input['hostname'] = conf.fqdn.split('.')[0] - _input['domain'] = '.'.join(conf.fqdn.split('.')[1:]) - else: - _input['fqdn'] = fqdn - _input['hostname'] = hostname.split('.')[0] - _input['domain'] = domainname _input['nodotdomain'] = _input['domain'].replace('.','_') - _input['rootdn'] = utils.standard_root_dn(_input['domain']) + _input['ldap_uri'] = 'ldap://localhost:389/' - if ask_questions: - print >> sys.stderr, utils.multiline_message( - _(""" - This setup procedure plans to set up Kolab Groupware for - the following domain name space. This domain name is - obtained from the reverse DNS entry on your network - interface. Please confirm this is the appropriate domain - name space. - """) - ) + # TODO: Loudly complain if the fqdn does not resolve back to this system. - answer = utils.ask_confirmation("%s" % (_input['domain'])) - - if not answer: - positive_answer = False - while not positive_answer: - _input['domain'] = utils.ask_question(_("Domain name to use")) - if not _input['domain'] == None and not _input['domain'] == "": - positive_answer = True - else: - print >> sys.stderr, utils.multiline_message( - _(""" - Invalid input. Please try again. - """) - ) + template_file = find_template(file_name='kolab-ds-setup.inf.j2') - _input['nodotdomain'] = _input['domain'].replace('.','_') - _input['rootdn'] = utils.standard_root_dn(_input['domain']) + if template_file is not None: + log.debug(_("Setup Directory server using %s template") % (template_file), level=8) - print >> sys.stderr, utils.multiline_message( - _(""" - The standard root dn we composed for you follows. Please - confirm this is the root dn you wish to use. - """) - ) + fpr = open(template_file, 'r') + template_definition = fpr.read() + fpr.close() - answer = utils.ask_confirmation("%s" % (_input['rootdn'])) - - if not answer: - positive_answer = False - while not positive_answer: - _input['rootdn'] = utils.ask_question(_("Root DN to use")) - if not _input['rootdn'] == None and not _input['rootdn'] == "": - positive_answer = True - else: - print >> sys.stderr, utils.multiline_message( - _(""" - Invalid input. Please try again. - """) - ) + setup_inf_template = Template(template_definition) - # TODO: Loudly complain if the fqdn does not resolve back to this system. + log.debug( + _("Successfully compiled template %r, ready to add schema to AD server") % (template_file), + level=8 + ) + + data = setup_inf_template.render(**_input).__str__() - data = """ -[General] -FullMachineName = %(fqdn)s -SuiteSpotUserID = %(userid)s -SuiteSpotGroup = %(group)s -AdminDomain = %(domain)s -ConfigDirectoryLdapURL = ldap://%(fqdn)s:389/o=NetscapeRoot -ConfigDirectoryAdminID = admin -ConfigDirectoryAdminPwd = %(admin_pass)s - -[slapd] -SlapdConfigForMC = Yes -UseExistingMC = 0 -ServerPort = 389 -ServerIdentifier = %(hostname)s -Suffix = %(rootdn)s -RootDN = cn=Directory Manager -RootDNPwd = %(dirmgr_pass)s -ds_bename = %(nodotdomain)s -AddSampleEntries = No - -[admin] -Port = 9830 -ServerAdminID = admin -ServerAdminPwd = %(admin_pass)s -""" % (_input) - - (fp, filename) = tempfile.mkstemp(dir="/tmp/") - os.write(fp, data) - os.close(fp) + (fpw, filename) = tempfile.mkstemp(dir="/tmp/") + os.write(fpw, data) + os.close(fpw) if os.path.isfile("/usr/sbin/setup-ds-admin.pl"): setup_ds_admin = "/usr/sbin/setup-ds-admin.pl" @@ -438,53 +822,20 @@ log.error(_("Could not configure to start on boot, the " + \ "directory server service.")) - if ask_questions: - print >> sys.stderr, utils.multiline_message( - _(""" - Please supply a Cyrus Administrator password. This - password is used by Kolab to execute administrative - tasks in Cyrus IMAP. You may also need the password - yourself to troubleshoot Cyrus IMAP and/or perform - other administrative tasks against Cyrus IMAP directly. - """) - ) - - _input['cyrus_admin_pass'] = utils.ask_question( - _("Cyrus Administrator password"), - default=utils.generate_password(), - password=True, - confirm=True - ) - - print >> sys.stderr, utils.multiline_message( - _(""" - Please supply a Kolab Service account password. This - account is used by various services such as Postfix, - and Roundcube, as anonymous binds to the LDAP server - will not be allowed. - """) - ) - - _input['kolab_service_pass'] = utils.ask_question( - _("Kolab Service password"), - default=utils.generate_password(), - password=True, - confirm=True - ) - - else: - _input['cyrus_admin_pass'] = conf.get('cyrus-imap', 'admin_password') - _input['kolab_service_pass'] = conf.get('ldap', 'service_bind_pw') + if not ask_questions: + _input['cyrus_admin_password'] = conf.get('cyrus-imap', 'admin_password') + _input['service_bind_pw'] = conf.get('ldap', 'service_bind_pw') log.info(_("Writing out configuration to kolab.conf")) # Write out kolab configuration conf.command_set('kolab', 'primary_domain', _input['domain']) - conf.command_set('ldap', 'base_dn', _input['rootdn']) + conf.command_set('ldap', 'ldap_uri', _input['ldap_uri']) + conf.command_set('ldap', 'base_dn', _input['base_dn']) conf.command_set('ldap', 'bind_dn', 'cn=Directory Manager') - conf.command_set('ldap', 'bind_pw', _input['dirmgr_pass']) - conf.command_set('ldap', 'service_bind_dn', 'uid=kolab-service,ou=Special Users,%s' % (_input['rootdn'])) - conf.command_set('ldap', 'service_bind_pw', _input['kolab_service_pass']) + conf.command_set('ldap', 'bind_pw', _input['bind_pw']) + conf.command_set('ldap', 'service_bind_dn', 'uid=kolab-service,ou=Special Users,%s' % (_input['base_dn'])) + conf.command_set('ldap', 'service_bind_pw', _input['service_bind_pw']) fp = open(conf.defaults.config_file, "w+") conf.cfg_parser.write(fp) @@ -496,9 +847,9 @@ auth = Auth(_input['domain']) auth.connect() auth._auth.connect() - auth._auth._bind(bind_dn='cn=Directory Manager', bind_pw=_input['dirmgr_pass']) + auth._auth._bind(bind_dn='cn=Directory Manager', bind_pw=_input['bind_pw']) - dn = 'uid=%s,ou=Special Users,%s' % (conf.get('cyrus-imap', 'admin_login'), _input['rootdn']) + dn = 'uid=%s,ou=Special Users,%s' % (conf.get('cyrus-imap', 'admin_login'), _input['base_dn']) # A dict to help build the "body" of the object attrs = {} @@ -507,7 +858,7 @@ attrs['givenname'] = "Cyrus" attrs['surname'] = "Administrator" attrs['cn'] = "Cyrus Administrator" - attrs['userPassword'] = _input['cyrus_admin_pass'] + attrs['userPassword'] = _input['cyrus_admin_password'] # Convert our dict to nice syntax for the add-function using modlist-module ldif = ldap.modlist.addModlist(attrs) @@ -515,9 +866,9 @@ # Do the actual synchronous add-operation to the ldapserver auth._auth.ldap.add_s(dn, ldif) - conf.command_set('cyrus-imap', 'admin_password', _input['cyrus_admin_pass']) + conf.command_set('cyrus-imap', 'admin_password', _input['cyrus_admin_password']) - dn = 'uid=kolab-service,ou=Special Users,%s' % (_input['rootdn']) + dn = 'uid=kolab-service,ou=Special Users,%s' % (_input['base_dn']) # A dict to help build the "body" of the object attrs = {} @@ -526,7 +877,7 @@ attrs['givenname'] = "Kolab" attrs['surname'] = "Service" attrs['cn'] = "Kolab Service" - attrs['userPassword'] = _input['kolab_service_pass'] + attrs['userPassword'] = _input['service_bind_pw'] attrs['nslookthroughlimit'] = '-1' attrs['nssizelimit'] = '-1' attrs['nstimelimit'] = '-1' @@ -538,7 +889,7 @@ # Do the actual synchronous add-operation to the ldapserver auth._auth.ldap.add_s(dn, ldif) - dn = 'ou=Resources,%s' % (_input['rootdn']) + dn = 'ou=Resources,%s' % (_input['base_dn']) # A dict to help build the "body" of the object attrs = {} @@ -551,7 +902,7 @@ # Do the actual synchronous add-operation to the ldapserver auth._auth.ldap.add_s(dn, ldif) - dn = 'ou=Shared Folders,%s' % (_input['rootdn']) + dn = 'ou=Shared Folders,%s' % (_input['base_dn']) # A dict to help build the "body" of the object attrs = {} @@ -572,7 +923,7 @@ attrs = {} attrs['objectclass'] = ['top','extensibleobject'] attrs['cn'] = "kolab" - attrs['aci'] = '(targetattr = "*") (version 3.0;acl "Kolab Services";allow (read,compare,search)(userdn = "ldap:///uid=kolab-service,ou=Special Users,%s");)' % (_input['rootdn']) + attrs['aci'] = '(targetattr = "*") (version 3.0;acl "Kolab Services";allow (read,compare,search)(userdn = "ldap:///uid=kolab-service,ou=Special Users,%s");)' % (_input['base_dn']) # Convert our dict to nice syntax for the add-function using modlist-module ldif = ldap.modlist.addModlist(attrs) @@ -596,13 +947,13 @@ attrs['associateddomain'].pop(attrs['associateddomain'].index(_input['domain'])) attrs['associateddomain'] = [ _input['domain'] ] + attrs['associateddomain'] - attrs['aci'] = '(targetattr = "*") (version 3.0;acl "Read Access for %(domain)s Users";allow (read,compare,search)(userdn = "ldap:///%(rootdn)s??sub?(objectclass=*)");)' % (_input) + attrs['aci'] = '(targetattr = "*") (version 3.0;acl "Read Access for %(domain)s Users";allow (read,compare,search)(userdn = "ldap:///%(base_dn)s??sub?(objectclass=*)");)' % (_input) # Add inetdomainbasedn in case the configured root dn is not the same as the # standard root dn for the domain name configured - if not _input['rootdn'] == utils.standard_root_dn(_input['domain']): + if not _input['base_dn'] == utils.standard_root_dn(_input['domain']): attrs['objectclass'].append('inetdomain') - attrs['inetdomainbasedn'] = _input['rootdn'] + attrs['inetdomainbasedn'] = _input['base_dn'] ldif = ldap.modlist.addModlist(attrs) auth._auth.ldap.add_s(dn, ldif) @@ -644,7 +995,7 @@ # Add kolab-admin role log.info(_("Adding the kolab-admin role")) - dn = "cn=kolab-admin,%s" % (_input['rootdn']) + dn = "cn=kolab-admin,%s" % (_input['base_dn']) attrs = {} attrs['description'] = "Kolab Administrator" attrs['objectClass'] = ['top','ldapsubentry','nsroledefinition','nssimpleroledefinition','nsmanagedroledefinition'] @@ -654,8 +1005,8 @@ auth._auth.ldap.add_s(dn, ldif) # User writeable attributes on root_dn - log.info(_("Setting access control to %s") % (_input['rootdn'])) - dn = _input['rootdn'] + log.info(_("Setting access control to %s") % (_input['base_dn'])) + dn = _input['base_dn'] aci = [] if schema_error: @@ -663,7 +1014,7 @@ else: aci.append('(targetattr = "carLicense || description || displayName || facsimileTelephoneNumber || homePhone || homePostalAddress || initials || jpegPhoto || l || labeledURI || mobile || o || pager || photo || postOfficeBox || postalAddress || postalCode || preferredDeliveryMethod || preferredLanguage || registeredAddress || roomNumber || secretary || seeAlso || st || street || telephoneNumber || telexNumber || title || userCertificate || userPassword || userSMIMECertificate || x500UniqueIdentifier || kolabDelegate || kolabInvitationPolicy || kolabAllowSMTPSender") (version 3.0; acl "Enable self write for common attributes"; allow (read,compare,search,write)(userdn = "ldap:///self");)') - aci.append('(targetattr = "*") (version 3.0;acl "Directory Administrators Group";allow (all)(groupdn = "ldap:///cn=Directory Administrators,%(rootdn)s" or roledn = "ldap:///cn=kolab-admin,%(rootdn)s");)' % (_input)) + aci.append('(targetattr = "*") (version 3.0;acl "Directory Administrators Group";allow (all)(groupdn = "ldap:///cn=Directory Administrators,%(base_dn)s" or roledn = "ldap:///cn=kolab-admin,%(base_dn)s");)' % (_input)) aci.append('(targetattr="*")(version 3.0; acl "Configuration Administrators Group"; allow (all) groupdn="ldap:///cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot";)') aci.append('(targetattr="*")(version 3.0; acl "Configuration Administrator"; allow (all) userdn="ldap:///uid=admin,ou=Administrators,ou=TopologyManagement,o=NetscapeRoot";)') aci.append('(targetattr = "*")(version 3.0; acl "SIE Group"; allow (all) groupdn = "ldap:///cn=slapd-%(hostname)s,cn=389 Directory Server,cn=Server Group,cn=%(fqdn)s,ou=%(domain)s,o=NetscapeRoot";)' % (_input)) diff --git a/templates/kolab-ad-domain.ldif.j2 b/templates/kolab-ad-domain.ldif.j2 new file mode 100644 --- /dev/null +++ b/templates/kolab-ad-domain.ldif.j2 @@ -0,0 +1,13 @@ +dn: CN={{ domain }},{{ domain_base_dn }} +changetype: add +objectClass: top +objectClass: domainRelatedObject +objectClass: document +cn: {{ domain }} +distinguishedName: CN={{ domain }},{{ domain_base_dn }} +instanceType: 4 +showInAdvancedViewOnly: TRUE +name: {{ domain }} +objectCategory: CN=document,CN=Schema,CN=Configuration,{{ base_dn }} +associatedDomain: {{ domain }} + diff --git a/templates/kolab-ad-special-users.ldif.j2 b/templates/kolab-ad-special-users.ldif.j2 new file mode 100644 --- /dev/null +++ b/templates/kolab-ad-special-users.ldif.j2 @@ -0,0 +1,50 @@ +dn: {{ cyrus_admin_dn }} +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: inetOrgPerson +cn: Cyrus Admin +sn: Administrator +description: User for Cyrus IMAP to connect to LDAP +givenName: Cyrus +distinguishedName: {{ cyrus_admin_dn }} +displayName: Cyrus Administrator +name: Cyrus Admin +codePage: 0 +countryCode: 0 +sAMAccountName: cyrus-admin +userPrincipalName: cyrus-admin@{{ domain }} +objectCategory: CN=Person,CN=Schema,CN=Configuration,{{ base_dn }} +uid: cyrus-admin +{%- if ad_ldaps %} +unicodePwd:: {{ cyrus_admin_password_base64 }} +userAccountControl: 66048 +{% endif %} + +dn: {{ service_bind_dn }} +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectClass: inetOrgPerson +cn: Kolab Service +sn: Service +description: User for Kolab to connect to LDAP +givenName: Kolab +distinguishedName: {{ service_bind_dn }} +displayName: Kolab Service +name: Kolab Service +codePage: 0 +countryCode: 0 +sAMAccountName: kolab-service +userPrincipalName: kolab-service@{{ domain }} +objectCategory: CN=Person,CN=Schema,CN=Configuration,{{ base_dn }} +uid: kolab-service +{%- if ad_ldaps %} +unicodePwd:: {{ service_bind_pw_base64 }} +userAccountControl: 66048 +{% endif %} + diff --git a/templates/kolab-ad-tree.ldif.j2 b/templates/kolab-ad-tree.ldif.j2 new file mode 100644 --- /dev/null +++ b/templates/kolab-ad-tree.ldif.j2 @@ -0,0 +1,66 @@ +dn: {{ kolab_tree_dn }} +changetype: add +objectClass: top +objectClass: organizationalUnit +ou: Kolab +description: LDAP tree part for Kolab related data +distinguishedName: {{ kolab_tree_dn }} +instanceType: 4 +name: Kolab +objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }} + +dn: {{ domain_base_dn }} +changetype: add +objectClass: top +objectClass: organizationalUnit +ou: Domains +description: Kolab hosted domains +distinguishedName: {{ domain_base_dn }} +instanceType: 4 +name: Domains +objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }} + +dn: {{ group_base_dn }} +changetype: add +objectClass: top +objectClass: organizationalUnit +ou: Groups +description: Kolab groups +distinguishedName: {{ group_base_dn }} +instanceType: 4 +name: Groups +objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }} + +dn: {{ resource_base_dn }} +changetype: add +objectClass: top +objectClass: organizationalUnit +ou: Resources +description: Kolab resources +distinguishedName: {{ resource_base_dn }} +instanceType: 4 +name: Resources +objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }} + +dn: {{ sharedfolder_base_dn }} +changetype: add +objectClass: top +objectClass: organizationalUnit +ou: Shared Folders +description: Kolab shared folders +distinguishedName: {{ sharedfolder_base_dn }} +instanceType: 4 +name: Shared Folders +objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }} + +dn: {{ specialuser_base_dn }} +changetype: add +objectClass: top +objectClass: organizationalUnit +ou: Special Users +description: Kolab special users +distinguishedName: ${{ specialuser_base_dn }} +instanceType: 4 +name: Special Users +objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }} + diff --git a/templates/kolab-ds-setup.inf.j2 b/templates/kolab-ds-setup.inf.j2 new file mode 100644 --- /dev/null +++ b/templates/kolab-ds-setup.inf.j2 @@ -0,0 +1,24 @@ +[General] +FullMachineName = {{ fqdn }} +SuiteSpotUserID = {{ userid }} +SuiteSpotGroup = {{ group }} +AdminDomain = {{ domain }} +ConfigDirectoryLdapURL = ldap://{{ fqdn }}:389/o=NetscapeRoot +ConfigDirectoryAdminID = admin +ConfigDirectoryAdminPwd = {{ admin_pass }} + +[slapd] +SlapdConfigForMC = Yes +UseExistingMC = 0 +ServerPort = 389 +ServerIdentifier = {{ hostname }} +Suffix = {{ base_dn }} +RootDN = cn=Directory Manager +RootDNPwd = {{ bind_pw }} +ds_bename = {{ nodotdomain }} +AddSampleEntries = No + +[admin] +Port = 9830 +ServerAdminID = admin +ServerAdminPwd = {{ admin_pass }} diff --git a/templates/kolab3-ad-schema.ldif.j2 b/templates/kolab3-ad-schema.ldif.j2 new file mode 100644 --- /dev/null +++ b/templates/kolab3-ad-schema.ldif.j2 @@ -0,0 +1,440 @@ +dn: CN=kolabAlias,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabAlias +distinguishedName: CN=kolabAlias,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.2.1.3 +attributeSyntax: 2.5.5.5 +isSingleValued: FALSE +rangeLower: 0 +rangeUpper: 256 +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabAlias +adminDescription: RFC1274: RFC822 Mailbox +oMSyntax: 22 +lDAPDisplayName: kolabAlias +name: kolabAlias +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabDelegate,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabDelegate +distinguishedName: CN=kolabDelegate,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.1.1.1.3 +attributeSyntax: 2.5.5.5 +isSingleValued: FALSE +rangeUpper: 256 +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabDelegate +adminDescription: + Kolab user allowed to act as delegates - RFC822 Mailbox/Alias +oMSyntax: 22 +lDAPDisplayName: kolabDelegate +name: kolabDelegate +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabInvitationPolicy,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabInvitationPolicy +distinguishedName: + CN=kolabInvitationPolicy,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.1.1.1.4 +attributeSyntax: 2.5.5.5 +isSingleValued: FALSE +rangeUpper: 256 +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabInvitationPolicy +adminDescription: Defines how to respond to invitations +oMSyntax: 22 +lDAPDisplayName: kolabInvitationPolicy +name: kolabInvitationPolicy +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabVacationBeginDateTime,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabVacationBeginDateTime +distinguishedName: + CN=kolabVacationBeginDateTime,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.1.1.1.8 +attributeSyntax: 2.5.5.11 +isSingleValued: TRUE +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabVacationBeginDateTime +adminDescription: Begin date of vacation +oMSyntax: 24 +lDAPDisplayName: kolabVacationBeginDateTime +name: kolabVacationBeginDateTime +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabVacationEndDateTime,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabVacationEndDateTime +distinguishedName: + CN=kolabVacationEndDateTime,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.1.1.1.9 +attributeSyntax: 2.5.5.11 +isSingleValued: TRUE +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabVacationEndDateTime +adminDescription: Specifies the end of vacation time +oMSyntax: 24 +lDAPDisplayName: kolabVacationEndDateTime +name: kolabVacationEndDateTime +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabVacationResendInterval,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabVacationResendInterval +distinguishedName: + CN=kolabVacationResendInterval,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.1.1.1.10 +attributeSyntax: 2.5.5.9 +isSingleValued: TRUE +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabVacationResendInterval +adminDescription: Vacation notice interval in days +oMSyntax: 2 +lDAPDisplayName: kolabVacationResendInterval +name: kolabVacationResendInterval +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabVacationAddress,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabVacationAddress +distinguishedName: + CN=kolabVacationAddress,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.1.1.1.11 +attributeSyntax: 2.5.5.5 +isSingleValued: TRUE +rangeUpper: 256 +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabVacationAddress +adminDescription: Email address for vacation to response upon +oMSyntax: 22 +lDAPDisplayName: kolabVacationAddress +name: kolabVacationAddress +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabForwardUCE,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabForwardUCE +distinguishedName: CN=kolabForwardUCE,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.1.1.1.16 +attributeSyntax: 2.5.5.8 +isSingleValued: TRUE +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabForwardUCE +adminDescription: Enable forwarding of mails known as UCE +oMSyntax: 1 +lDAPDisplayName: kolabForwardUCE +name: kolabForwardUCE +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabAllowSMTPRecipient,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabAllowSMTPRecipient +distinguishedName: + CN=kolabAllowSMTPRecipient,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.1.1.1.18 +attributeSyntax: 2.5.5.5 +isSingleValued: FALSE +rangeUpper: 512 +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabAllowSMTPRecipient +adminDescription: SMTP address allowed for destination +oMSyntax: 22 +lDAPDisplayName: kolabAllowSMTPRecipient +name: kolabAllowSMTPRecipient +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabAllowSMTPSender,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabAllowSMTPSender +distinguishedName: + CN=kolabAllowSMTPSender,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.1.1.1.43 +attributeSyntax: 2.5.5.5 +isSingleValued: FALSE +rangeUpper: 512 +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabAllowSMTPSender +adminDescription: SMTP envelope sender address accepted for delivery +oMSyntax: 22 +lDAPDisplayName: kolabAllowSMTPSender +name: kolabAllowSMTPSender +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabFolderType,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabFolderType +distinguishedName: CN=kolabFolderType,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.2.1.7 +attributeSyntax: 2.5.5.5 +isSingleValued: TRUE +rangeUpper: 256 +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabFolderType +adminDescription: Type of a kolab folder +oMSyntax: 22 +lDAPDisplayName: kolabFolderType +name: kolabFolderType +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabTargetFolder,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabTargetFolder +distinguishedName: + CN=kolabTargetFolder,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.2.1.8 +attributeSyntax: 2.5.5.12 +isSingleValued: TRUE +rangeUpper: 512 +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabTargetFolder +adminDescription: Target for a Kolab Shared Folder delivery +oMSyntax: 64 +lDAPDisplayName: kolabTargetFolder +name: kolabTargetFolder +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabImapACL,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabImapACL +distinguishedName: CN=kolabImapACL,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.2.1.651 +attributeSyntax: 2.5.5.5 +isSingleValued: FALSE +rangeUpper: 256 +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabImapACL +adminDescription: Cyrus IMAP access control lists +oMSyntax: 22 +lDAPDisplayName: kolabImapACL +name: kolabImapACL +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabDescAttribute,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabDescAttribute +distinguishedName: + CN=kolabDescAttribute,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.3.1.1 +attributeSyntax: 2.5.5.5 +isSingleValued: TRUE +rangeUpper: 256 +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabDescAttribute +adminDescription: Descriptive attribute or parameter for a Resource +oMSyntax: 22 +lDAPDisplayName: kolabDescAttribute +name: kolabDescAttribute +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabMailHost,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabMailHost +distinguishedName: CN=kolabMailHost,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.3.1.2 +attributeSyntax: 2.5.5.4 +isSingleValued: TRUE +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabMailHost +adminDescription:: + TWFpbCBob3N0IHdoZXJlIEtvbGFiIHVzZXIsIHNoYXJlZCBmb2xkZXIgLyByZXNvdXJjZSByZXNpZG + VzIG9uIA== +oMSyntax: 20 +lDAPDisplayName: kolabMailHost +name: kolabMailHost +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabMailQuota,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: attributeSchema +cn: kolabMailQuota +distinguishedName: CN=kolabMailQuota,{{ ad_schema_naming_context }} +instanceType: 4 +attributeID: 1.3.6.1.4.1.19414.3.1.3 +attributeSyntax: 2.5.5.4 +isSingleValued: TRUE +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabMailQuota +adminDescription: Kolab mailbox quota limit. +oMSyntax: 20 +lDAPDisplayName: kolabMailQuota +name: kolabMailQuota +objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }} + +dn: CN=kolabSharedFolder,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: classSchema +cn: kolabSharedFolder +distinguishedName: + CN=kolabSharedFolder,{{ ad_schema_naming_context }} +instanceType: 4 +subClassOf: top +governsID: 1.3.6.1.4.1.19414.2.2.9 +mustContain: cn +mayContain: owner +mayContain: kolabImapAcl +mayContain: kolabAlias +mayContain: kolabAllowSMTPRecipient +mayContain: kolabAllowSMTPSender +mayContain: kolabDelegate +mayContain: kolabFolderType +mayContain: kolabMailHost +mayContain: kolabTargetFolder +rDNAttID: cn +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabSharedFolder +adminDescription: Kolab public shared folder +objectClassCategory: 3 +lDAPDisplayName: kolabSharedFolder +name: kolabSharedFolder +systemOnly: FALSE +defaultSecurityDescriptor: + D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY) + (A;;RPLCLORC;;;AU) +objectCategory: CN=Class-Schema,{{ ad_schema_naming_context }} +defaultObjectCategory: + CN=kolabSharedFolder,{{ ad_schema_naming_context }} + +dn: CN=kolabInetOrgPerson,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: classSchema +cn: kolabInetOrgPerson +distinguishedName: + CN=kolabInetOrgPerson,{{ ad_schema_naming_context }} +instanceType: 4 +subClassOf: top +governsID: 1.3.6.1.4.1.19414.3.2.2 +mayContain: kolabAlias +mayContain: kolabAllowSMTPRecipient +mayContain: kolabAllowSMTPSender +mayContain: kolabDelegate +mayContain: kolabForwardUCE +mayContain: kolabInvitationPolicy +mayContain: kolabMailHost +mayContain: kolabMailQuota +mayContain: kolabVacationAddress +mayContain: kolabVacationBeginDateTime +mayContain: kolabVacationEndDateTime +mayContain: kolabVacationResendInterval +rDNAttID: cn +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabInetOrgPerson +adminDescription: Kolab Internet Organizational Person +objectClassCategory: 3 +lDAPDisplayName: kolabInetOrgPerson +name: kolabInetOrgPerson +systemOnly: FALSE +defaultSecurityDescriptor: + D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY) + (A;;RPLCLORC;;;AU) +objectCategory: CN=Class-Schema,{{ ad_schema_naming_context }} +defaultObjectCategory: + CN=kolabInetOrgPerson,{{ ad_schema_naming_context }} + +dn: CN=kolabGroupOfUniqueNames,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: classSchema +cn: kolabGroupOfUniqueNames +distinguishedName: + CN=kolabGroupOfUniqueNames,{{ ad_schema_naming_context }} +instanceType: 4 +subClassOf: top +governsID: 1.3.6.1.4.1.19414.3.2.8 +mayContain: kolabAlias +mayContain: kolabAllowSMTPRecipient +mayContain: kolabAllowSMTPSender +mayContain: kolabDelegate +rDNAttID: cn +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabGroupOfUniqueNames +adminDescription: Kolab group of names (DNs) derived from RFC2256 +objectClassCategory: 3 +lDAPDisplayName: kolabGroupOfUniqueNames +name: kolabGroupOfUniqueNames +systemOnly: FALSE +defaultSecurityDescriptor: + D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY) + (A;;RPLCLORC;;;AU) +objectCategory: CN=Class-Schema,{{ ad_schema_naming_context }} +defaultObjectCategory: + CN=kolabGroupOfUniqueNames,{{ ad_schema_naming_context }} + +dn: CN=kolabResource,{{ ad_schema_naming_context }} +changetype: add +objectClass: top +objectClass: classSchema +cn: kolabResource +distinguishedName: CN=kolabResource,{{ ad_schema_naming_context }} +instanceType: 4 +subClassOf: top +governsID: 1.3.6.1.4.1.19414.3.2.9 +mayContain: description +mayContain: kolabDescAttribute +mayContain: kolabInvitationPolicy +mayContain: owner +rDNAttID: cn +showInAdvancedViewOnly: TRUE +adminDisplayName: kolabResource +adminDescription: Kolab resource +objectClassCategory: 3 +lDAPDisplayName: kolabResource +name: kolabResource +systemOnly: FALSE +defaultSecurityDescriptor: + D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY) + (A;;RPLCLORC;;;AU) +objectCategory: CN=Class-Schema,{{ ad_schema_naming_context }} +defaultObjectCategory: + CN=kolabResource,{{ ad_schema_naming_context }} +-