Esempio n. 1
0
 def updateLdapContact(self, id, vals, cursor, uid, context):
     """update an existing contact with the data of OpenERP"""
     conn = self.connectToLdap(cursor, uid, context={})
     try:
         old_contatc_obj = self.getLdapContact(conn, id)
     except ldap.NO_SUCH_OBJECT:
         self.saveLdapContact(id, vals, cursor, uid, context)
         return
     contact_obj = self.mappLdapObject(id, vals, cursor, uid, context)
     if conn.ACTIVDIR:
         modlist = []
         for key, val in contact_obj.items():
             if key in ('cn', 'uid', 'objectclass'):
                 continue
             if isinstance(val, list):
                 val = val[0]
             modlist.append((ldap.MOD_REPLACE, key, val))
     else:
         modlist = ldap.modlist.modifyModlist(old_contatc_obj[1],
                                              contact_obj)
     try:
         conn.connexion.modify_s(old_contatc_obj[0], modlist)
         conn.connexion.unbind_s()
     except Exception:
         raise
Esempio n. 2
0
    def modify_attributes(self, dn, attributes, create=False):
        """
        :param dn: new entry's full_dn
        :param attributes: attributes to be filled
        :param create: if create is False, the attributes will be MODIFIED,
            if it is true, they will be ADDED to the node
        :return: original ldap.modify_s returns query id, I think it's useless,
            therefore this method will return nothing
        """
        if create:
            operation = ldap.MOD_ADD
        else:
            operation = ldap.MOD_REPLACE

        modlist = []
        for k, v in attributes.iteritems():
            if isinstance(v, list):
                raise (RuntimeError, "This will not work with list values")
            if v:
                modlist.append((operation, k, v))

        log.debug("Modlist: %s" % modlist)
        try:
            log.debug('%s, %s' % (dn, type(dn)))
            self.connection.modify_s(dn, modlist)
        except ldap.TIMEOUT as ex:
            # Try once more, pass the exception to the caller if faled
            self.disconnect()
            self.connection = self.connect()
            self.connection.modify_s(dn, modlist)
        except ldap.INVALID_SYNTAX as ex:
            log.exception(ex)
            self.connection.modify_s(dn, modlist)
        log.debug("Modified attribute(s) of LDAP node {0}".format(dn))
def update(connection,
           base,
           data,
           scope=None,
           filter=None,
           attributes=None,
           partial=True):
    with helpers.ldap.SaslBindingContext(
            **binding_args(connection)) as context:
        old = search(context,
                     base,
                     scope,
                     filter,
                     attributes=None,
                     behavior=SearchBehavior.EXPECT_ONE)[0]
        old_dn = old[RESULT_KEY_DN]
        new_dn = old_dn
        old_attrs = old[RESULT_KEY_ATTRIBUTES]
        new_attrs = validate_attributes(data)
        old_attrs_ci = CaseInsensitiveDict(old_attrs)
        new_attrs_ci = CaseInsensitiveDict(new_attrs)
        if len(new_attrs) != len(new_attrs_ci):
            raise helpers.errors.BadRequestException(
                'Case collision in attribute keys')
        rdn_attr_key = old_dn.split(',')[0].split('=')[0]
        # check for rename
        if rdn_attr_key in old_attrs_ci and rdn_attr_key in new_attrs_ci:
            old_rdn = helpers.ad.format_attribute(rdn_attr_key,
                                                  old_attrs_ci[rdn_attr_key])
            new_rdn = helpers.ad.format_attribute(rdn_attr_key,
                                                  new_attrs_ci[rdn_attr_key])
            if old_rdn != new_rdn:
                new_rdn_formatted = '{}={}'.format(rdn_attr_key, new_rdn)
                new_dn = ','.join([
                    new_rdn_formatted if i == 0 else s
                    for i, s in enumerate(old_dn.split(','))
                ])
                del old_attrs_ci[rdn_attr_key]
                del new_attrs_ci[rdn_attr_key]
                #print(new_dn)
                context.rename(old_dn, new_rdn_formatted)
        # build mod list - only delete attributes if not partial (patch)
        modlist = []
        for k in list(new_attrs_ci) if partial else list(old_attrs_ci):
            if (not partial) and k in old_attrs_ci and k not in new_attrs_ci:
                # delete
                modlist.append((ldap.MOD_DELETE, k, None))
            elif k not in old_attrs_ci or old_attrs_ci[k] != new_attrs_ci[k]:
                # update / replace
                modlist.append((ldap.MOD_REPLACE, k, new_attrs_ci[k]))
        #print(modlist)
        if len(modlist) > 0:
            context.modify(new_dn, modlist)
        return externalize(
            search(context,
                   new_dn,
                   scope=SCOPE_BASE,
                   attributes=attributes,
                   behavior=SearchBehavior.EXPECT_ONE,
                   empty_results_error=ReloadAfterModError)[0])
Esempio n. 4
0
 def modUserGroup(self, uid, grupo, adddel):
     modlist = []
     usuario = "uid=%s,ou=People,dc=gonzalonazareno,dc=org" % uid
     if adddel == "add":
         modlist.append((ldap.MOD_ADD, "member", usuario.encode("utf-8")))
     elif adddel == "del":
         modlist.append(
             (ldap.MOD_DELETE, "member", usuario.encode("utf-8")))
     else:
         return self.con.modify_s(
             "cn=%s,ou=Group,dc=gonzalonazareno,dc=org" % grupo, modlist)
Esempio n. 5
0
 def _get_modlist(self, data, modtype=ldap.MOD_REPLACE):
     modlist = []
     for key in data.keys():
         if modtype == ldap.MOD_DELETE:
             val = None
         else:
             val = data[key]
         if modtype == ldap.MOD_ADD:
             ntup = tuple([key, val])
         else:
             ntup = tuple([modtype, key, val])
         modlist.append(ntup)
     return modlist
Esempio n. 6
0
    def modify_group_members(self, group, uid_to_add = [], uid_to_delete = []):
        """Add and/or remove members from a group"""

        modlist = []
        dn = "cn=%s,%s" % (group, self.__groups_ou)

        # Generate change list
        if uid_to_add != []:
            modlist.append((ldap.MOD_ADD, "memberUid", uid_to_add ))
        if uid_to_delete != []:
            modlist.append((ldap.MOD_DELETE, "memberUid", uid_to_delete))
        
        self.__ldap.modify_s(dn, modlist)
Esempio n. 7
0
    def modify_obj(self,
                   prop,
                   prop_value,
                   new,
                   objectClass=None,
                   throw_error=False):

        try:
            search_str = '({0}={1})'.format(prop, prop_value)

            if (objectClass != None):
                search_str = '(&{0}(objectClass={1}))'.format(
                    search_str, objectClass)

            r = self.conn.search_s(LDAP_VIRTUE_DN, ldap.SCOPE_SUBTREE, \
                    search_str)

            if (len(r) != 1):
                # We only want to change it if there's no
                # possibility that we're referring to the wrong object.
                return 22  # Error EINVAL

            dn = r[0][0]
            curr = r[0][1]

            # modlist = ldap.modlist.modifyModlist( r[0][1], new )
            modlist = []
            for k in new:
                if (curr.get(k) == None and new.get(k) != None):
                    modlist.append((0, k, new[k]))
                elif (curr.get(k) != None and new.get(k) != None
                      and new[k] != curr[k]):
                    modlist.append((2, k, new[k]))

            self.conn.modify_s(dn, modlist)

            return 0  # Success!

        except Exception as e:
            print("Error: {0}".format(e))
            # Send error to calling function if indicated
            if (throw_error):
                raise e
            return None
Esempio n. 8
0
    def update(self):
        """ If self.dn exist, returns a callable that will update it. """
        def _update():
            self.connection.modify_s(self.dn, modlist)
            return modlist

        modlist = []
        for (attr_name, attr_values) in self.attrs.items():
            ldap_attr = LdapAttr(self.module, self.connection, self.dn,
                                 attr_name, attr_values)
            op = ldap_attr.update()
            if op:
                modlist.append(op)

        action = None
        if modlist:
            action = _update

        return action
Esempio n. 9
0
def execute(*args, **kw):
    ask_questions = True

    if not conf.config_file == conf.defaults.config_file:
        ask_questions = False

    _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
            )

        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")

    else:
        _input['admin_pass'] = conf.get('ldap', 'bind_pw')
        _input['dirmgr_pass'] = conf.get('ldap', 'bind_pw')
        try:
            pw = pwd.getpwnam("dirsrv")
        except:
            _input['userid'] = "nobody"
            _input['group'] = "nobody"
        else:
            _input['userid'] = "dirsrv"
            _input['group'] = "dirsrv"

    # TODO: Verify the user and group exist.

    # TODO: This takes the system fqdn, domainname and hostname, rather then
    # the desired fqdn, domainname and hostname.
    #
    # 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'])

    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.
                    """)
            )

        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.
                                """)
                        )

        _input['nodotdomain'] = _input['domain'].replace('.','_')
        _input['rootdn'] = utils.standard_root_dn(_input['domain'])

        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.
                    """)
            )

        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.
                                """)
                        )

    # TODO: Loudly complain if the fqdn does not resolve back to this system.

    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)

    if os.path.isfile("/usr/sbin/setup-ds-admin.pl"):
        setup_ds_admin = "/usr/sbin/setup-ds-admin.pl"
    elif os.path.isfile("/usr/sbin/setup-ds"):
        setup_ds_admin = "/usr/sbin/setup-ds"
    else:
        log.error(_("No directory server setup tool available."))

    command = [
            setup_ds_admin,
            '--debug',
            '--silent',
            '--force',
            '--file=%s' % (filename)
        ]

    print >> sys.stderr, utils.multiline_message(
            _("""
                    Setup is now going to set up the 389 Directory Server. This
                    may take a little while (during which period there is no
                    output and no progress indication).
                """)
        )

    log.info(_("Setting up 389 Directory Server"))

    setup_389 = subprocess.Popen(
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE
        )

    (stdoutdata, stderrdata) = setup_389.communicate()

    # TODO: Get the return code and display output if not successful.

    log.debug(_("Setup DS stdout:"), level=8)
    log.debug(stdoutdata, level=8)

    log.debug(_("Setup DS stderr:"), level=8)
    log.debug(stderrdata, level=8)

    # TODO: Fails when ran a second time.

    # TODO: When fail, fail gracefully.

    # Find the kolab schema. It's installed as %doc in the kolab-schema package.
    # TODO: Chown nobody, nobody, chmod 440
    schema_file = None
    for root, directories, filenames in os.walk('/usr/share/doc/'):
        for filename in filenames:
            if filename.startswith('kolab') and filename.endswith('.ldif') and schema_file == None:
                schema_file = os.path.join(root,filename)

    if not schema_file == None:
        try:
            shutil.copy(
                    schema_file,
                    '/etc/dirsrv/slapd-%s/schema/99%s' % (
                            _input['hostname'],
                            os.path.basename(schema_file)
                        )
                )
            schema_error = False
        except:
            log.error(_("Could not copy the LDAP extensions for Kolab"))
            schema_error = True
    else:
        log.error(_("Could not find the ldap Kolab schema file"))
        schema_error = True

    if os.path.isfile('/bin/systemctl'):
        subprocess.call(['/bin/systemctl', 'restart', 'dirsrv.target'])
    elif os.path.isfile('/sbin/service'):
        subprocess.call(['/sbin/service', 'dirsrv', 'restart'])
    elif os.path.isfile('/usr/sbin/service'):
        subprocess.call(['/usr/sbin/service','dirsrv','stop'])
        time.sleep(20)
        subprocess.call(['/usr/sbin/service','dirsrv','start'])
    else:
        log.error(_("Could not start the directory server service."))

    if os.path.isfile('/bin/systemctl'):
        subprocess.call(['/bin/systemctl', 'enable', 'dirsrv.target'])
    elif os.path.isfile('/sbin/chkconfig'):
        subprocess.call(['/sbin/chkconfig', 'dirsrv', 'on'])
    elif os.path.isfile('/usr/sbin/update-rc.d'):
        subprocess.call(['/usr/sbin/update-rc.d', 'dirsrv', 'defaults'])
    else:
        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')

    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', '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'])

    fp = open(conf.defaults.config_file, "w+")
    conf.cfg_parser.write(fp)
    fp.close()

    log.info(_("Inserting service users into LDAP."))

    # Insert service users
    auth = Auth(_input['domain'])
    auth.connect()
    auth._auth.connect()
    auth._auth._bind()

    dn = 'uid=%s,ou=Special Users,%s' % (conf.get('cyrus-imap', 'admin_login'), _input['rootdn'])

    # A dict to help build the "body" of the object
    attrs = {}
    attrs['objectclass'] = ['top','person','inetorgperson','organizationalperson']
    attrs['uid'] = conf.get('cyrus-imap', 'admin_login')
    attrs['givenname'] = "Cyrus"
    attrs['surname'] = "Administrator"
    attrs['cn'] = "Cyrus Administrator"
    attrs['userPassword'] = _input['cyrus_admin_pass']

    # Convert our dict to nice syntax for the add-function using modlist-module
    ldif = ldap.modlist.addModlist(attrs)

    # 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'])

    dn = 'uid=kolab-service,ou=Special Users,%s' % (_input['rootdn'])

    # A dict to help build the "body" of the object
    attrs = {}
    attrs['objectclass'] = ['top','person','inetorgperson','organizationalperson']
    attrs['uid'] = "kolab-service"
    attrs['givenname'] = "Kolab"
    attrs['surname'] = "Service"
    attrs['cn'] = "Kolab Service"
    attrs['userPassword'] = _input['kolab_service_pass']
    attrs['nslookthroughlimit'] = '-1'
    attrs['nssizelimit'] = '-1'
    attrs['nstimelimit'] = '-1'
    attrs['nsidletimeout'] = '-1'

    # Convert our dict to nice syntax for the add-function using modlist-module
    ldif = ldap.modlist.addModlist(attrs)

    # Do the actual synchronous add-operation to the ldapserver
    auth._auth.ldap.add_s(dn, ldif)

    dn = 'ou=Resources,%s' % (_input['rootdn'])

    # A dict to help build the "body" of the object
    attrs = {}
    attrs['objectclass'] = ['top','organizationalunit']
    attrs['ou'] = "Resources"

    # Convert our dict to nice syntax for the add-function using modlist-module
    ldif = ldap.modlist.addModlist(attrs)

    # Do the actual synchronous add-operation to the ldapserver
    auth._auth.ldap.add_s(dn, ldif)

    dn = 'ou=Shared Folders,%s' % (_input['rootdn'])

    # A dict to help build the "body" of the object
    attrs = {}
    attrs['objectclass'] = ['top','organizationalunit']
    attrs['ou'] = "Shared Folders"

    # Convert our dict to nice syntax for the add-function using modlist-module
    ldif = ldap.modlist.addModlist(attrs)

    # Do the actual synchronous add-operation to the ldapserver
    auth._auth.ldap.add_s(dn, ldif)

    log.info(_("Writing out cn=kolab,cn=config"))

    dn = 'cn=kolab,cn=config'

    # A dict to help build the "body" of the object
    attrs = {}
    attrs['objectclass'] = ['top','extensibleobject']
    attrs['cn'] = "kolab"

    # Convert our dict to nice syntax for the add-function using modlist-module
    ldif = ldap.modlist.addModlist(attrs)

    # Do the actual synchronous add-operation to the ldapserver
    auth._auth.ldap.add_s(dn, ldif)

    auth._auth.set_entry_attribute(
            dn,
            'aci',
            '(targetattr = "*") (version 3.0;acl "Kolab Services";allow (read,compare,search)(userdn = "ldap:///%s");)' % ('uid=kolab-service,ou=Special Users,%s' % (_input['rootdn']))
        )

    # TODO: Add kolab-admin role
    # TODO: Assign kolab-admin admin ACLs

    log.info(_("Adding domain %s to list of domains for this deployment") % (_input['domain']))
    dn = "associateddomain=%s,cn=kolab,cn=config" % (_input['domain'])
    attrs = {}
    attrs['objectclass'] = ['top','domainrelatedobject']
    attrs['associateddomain'] = '%s' % (_input['domain'])
    attrs['aci'] = '(targetattr = "*") (version 3.0;acl "Read Access for %(domain)s Users";allow (read,compare,search)(userdn = "ldap:///%(rootdn)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']):
        attrs['objectclass'].append('inetdomain')
        attrs['inetdomainbasedn'] = _input['rootdn']

    ldif = ldap.modlist.addModlist(attrs)
    auth._auth.ldap.add_s(dn, ldif)

    if not conf.anonymous:
        log.info(_("Disabling anonymous binds"))
        dn = "cn=config"
        modlist = []
        modlist.append((ldap.MOD_REPLACE, "nsslapd-allow-anonymous-access", "off"))
        auth._auth.ldap.modify_s(dn, modlist)

    # TODO: Ensure the uid attribute is unique
    # TODO^2: Consider renaming the general "attribute uniqueness to "uid attribute uniqueness"
    log.info(_("Enabling attribute uniqueness plugin"))
    dn = "cn=attribute uniqueness,cn=plugins,cn=config"
    modlist = []
    modlist.append((ldap.MOD_REPLACE, "nsslapd-pluginEnabled", "on"))
    auth._auth.ldap.modify_s(dn, modlist)

    log.info(_("Enabling referential integrity plugin"))
    dn = "cn=referential integrity postoperation,cn=plugins,cn=config"
    modlist = []
    modlist.append((ldap.MOD_REPLACE, "nsslapd-pluginEnabled", "on"))
    auth._auth.ldap.modify_s(dn, modlist)

    log.info(_("Enabling and configuring account policy plugin"))
    dn = "cn=Account Policy Plugin,cn=plugins,cn=config"
    modlist = []
    modlist.append((ldap.MOD_REPLACE, "nsslapd-pluginEnabled", "on"))
    modlist.append((ldap.MOD_ADD, "nsslapd-pluginarg0", "cn=config,cn=Account Policy Plugin,cn=plugins,cn=config"))
    auth._auth.ldap.modify_s(dn, modlist)

    dn = "cn=config,cn=Account Policy Plugin,cn=plugins,cn=config"
    modlist = []
    modlist.append((ldap.MOD_REPLACE, "alwaysrecordlogin", "yes"))
    modlist.append((ldap.MOD_ADD, "stateattrname", "lastLoginTime"))
    modlist.append((ldap.MOD_ADD, "altstateattrname", "createTimestamp"))
    auth._auth.ldap.modify_s(dn, modlist)

    # TODO: Add kolab-admin role
    log.info(_("Adding the kolab-admin role"))
    dn = "cn=kolab-admin,%s" % (_input['rootdn'])
    attrs = {}
    attrs['description'] = "Kolab Administrator"
    attrs['objectClass'] = ['top','ldapsubentry','nsroledefinition','nssimpleroledefinition','nsmanagedroledefinition']
    attrs['cn'] = "kolab-admin"
    ldif = ldap.modlist.addModlist(attrs)

    auth._auth.ldap.add_s(dn, ldif)

    # TODO: User writeable attributes on root_dn
    log.info(_("Setting access control to %s") % (_input['rootdn']))
    dn = _input['rootdn']
    aci = []

    if not schema_error:
        aci.append('(targetattr = "homePhone || preferredDeliveryMethod || jpegPhoto || postalAddress || carLicense || userPassword || mobile || displayName || description || labeledURI || homePostalAddress || postOfficeBox || registeredAddress || postalCode || photo || title || street || pager || o || l || initials || telephoneNumber || preferredLanguage || facsimileTelephoneNumber") (version 3.0;acl "Enable self write for common attributes";allow (read,compare,search,write)(userdn = "ldap:///self");)')
    else:
        aci.append('(targetattr = "homePhone || preferredDeliveryMethod || jpegPhoto || postalAddress || carLicense || userPassword || mobile || kolabAllowSMTPRecipient || displayName || kolabDelegate || description || labeledURI || homePostalAddress || postOfficeBox || registeredAddress || postalCode || photo || title || street || kolabInvitationPolicy || pager || o || l || initials || kolabAllowSMTPSender || telephoneNumber || preferredLanguage || facsimileTelephoneNumber") (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 "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))
    aci.append('(targetattr = "*") (version 3.0;acl "Search Access";allow (read,compare,search)(userdn = "ldap:///all");)')
    modlist = []
    modlist.append((ldap.MOD_REPLACE, "aci", aci))
    auth._auth.ldap.modify_s(dn, modlist)

    if os.path.isfile('/bin/systemctl'):
        subprocess.call(['/bin/systemctl', 'enable', 'dirsrv-admin.service'])
    elif os.path.isfile('/sbin/chkconfig'):
        subprocess.call(['/sbin/chkconfig', 'dirsrv-admin', 'on'])
    elif os.path.isfile('/usr/sbin/update-rc.d'):
        subprocess.call(['/usr/sbin/update-rc.d', 'dirsrv-admin', 'defaults'])
    else:
        log.error(_("Could not start and configure to start on boot, the " + \
                "directory server admin service."))
Esempio n. 10
0
	def save(self):
		'''Method to create or modify the LDAP object in Active Directory
		
			If adOBJ is a New object then create LDAP object in Active Directory
			If adOBJ is not a new object then modify LDAP object in Active Directory
		'''
		modlist = []
		attrs = self.__dict__.copy()
		del attrs['adldap']
		del attrs['adcomplete']
		isNew = attrs.get('is_new',False)
		try:
			del attrs['is_new']
		except:
			pass

#		if attrs.get('dn',None).value == None:
#			raise self.adldap.NotDNDefinied("Not DN Definied")

		if (attrs.get('cn',None).value == None) or (attrs.get('container',None).value == None):
			raise self.adldap.NotDNDefinied("CN and/or CONTAINER field can not be None")

		if not isNew: #is modify action
			if attrs['dn'].__dict__.get('is_modified',False):
				raise self.adldap.ForbidChangeDN("Can not change DN field. You must use CN and/or CONTAINER attribute. Remove field.")
			cn = False
			cnt = False
			if attrs['cn'].__dict__.get('is_modified',False):
				cn = attrs['cn'].value
				del attrs['cn']
	
			if attrs['container'].__dict__.get('is_modified',False):
				cnt = attrs['container'].value
				del attrs['container']

			for attr in attrs:
				modtype= ldap.MOD_REPLACE
				if attrs[attr].__dict__.get('is_modified',False):
					if attrs.get(attr,None) == None:
						modtype = ldap.MOD_DELETE
					modlist.append((modtype,attr,attrs[attr].raw))
					if (attr.lower() == 'unicodepwd'): #Two times is needed for update password in AD
						modlist.append((modtype,attr,attrs[attr].raw))

			if len(modlist) > 0:
				self.adldap.ldapConnection.modify_s(self.dn.value,modlist)

			if cn:
				self.adldap.ldapConnection.modrdn_s(self.dn.value,'cn='+str(cn),True)
				newdn = 'cn='+str(cn)+','+','.join(self.dn.value.split(',')[1:])
				self.dn = newdn
				del self.dn.is_modified
				del self.cn.is_modified

			if cnt:
				self.adldap.ldapConnection.rename_s(self.dn.value,'cn='+self.cn.value, cnt)
				newdn = 'cn='+self.cn.value+','+cnt
				self.dn = newdn
				del self.dn.is_modified
				del self.container.is_modified

		else:
			cnt = attrs['container'].value
			del attrs['container']
			add = {}
			
			try:
				if (self.is_enable) and (self.__dict__.get("unicodePwd",None) == None):
					raise Exception("Not set Password to new enable object")
			except self.adldap.ObjectNotHaveEnable:
				pass

			for attr in attrs:
				if (attr in self.newobj) and (attrs[attr].value == None):
					raise self.adldap.EmptyAttrNewObj("Empty attributes for new Object "+str(self.newobj))
				if attrs[attr].value != None:
					add[attr] = attrs[attr].value
			addlist= ldap.modlist.addModlist(add)
			dn = 'CN='+attrs['cn'].value+','+cnt
			self.adldap.ldapConnection.add_s(dn,addlist)
Esempio n. 11
0
    def s_edit(self, parent_ou, new_data: dict={}):
        """Server side edition of user's information on LDAP (only if modified)

        Args:
            new_data (dict): List of properties to change. Default is `{}`.
        """
        modlist = []
        if new_data.get('login'):
            modlist.append(get_modify_item('samAccountName', self.login, new_data.get('login')))
            modlist.append(get_modify_item('userPrincipalName',
                f"{self.login.decode('utf-8')}@{cm().ldap.domain}".encode('utf-8'),
                new_data.get('login') + f"@{cm().ldap.domain}"
            ))
        if new_data.get('description') is not None:
            if new_data.get('description') == "":
                # Empty description
                modlist.append((ldap.MOD_DELETE, 'description', None))
            else:
                modlist.append(get_modify_item('description', self.description, new_data.get('description')))
        if new_data.get('display_name'):
            modlist.append(get_modify_item('displayName', self.display_name, new_data.get('display_name')))
        if new_data.get('password'):
            # Reset password
            password = new_data.get('password')
            modlist.append(get_modify_item('unicodePwd',
                None, f'"{password}"', encoding="utf-16-le"
            ))
        modlist = list(filter(None.__ne__, modlist)) # remove empty changes
        logger.info(f"There is {len(modlist)} changes to make on the user object.")
        if len(modlist) > 0:
            logger.trivia(modlist)
            con = get_ldap_connect()
            try:
                con.modify_s(self.base, modlist)
                logger.info(f"User {self.login} is edited.")
            except Exception as e:
                logger.error(f"Cannot edit user {self.login}: {str(e)}")
                return "500: Server side issue on editing user."
        else:
            logger.debug("Nothing to edit.")
        return get_user_in_ou(parent_ou, new_data.get('login'), as_dict=True)
Esempio n. 12
0
def main():
    usage = "usage: %prog [options]"
    parser = optparse.OptionParser(usage=usage, description=__doc__)
    parser.add_option(
        "-f",
        "--filter",
        help=
        "resync objects from master found by this filter. Default: (uid=<hostname>$)"
    )
    parser.add_option("-r",
                      "--remove",
                      action="store_true",
                      help="remove objects in local database before resync")
    parser.add_option("-s",
                      "--simulate",
                      action="store_true",
                      help="dry run, do not remove or add")
    parser.add_option("-u",
                      "--update",
                      action="store_true",
                      help="update/modify existing objects")
    opts, args = parser.parse_args()

    ucr = univention.config_registry.ConfigRegistry()
    ucr.load()
    base = ucr.get("ldap/base")
    binddn = "cn=update,%s" % base
    with open("/etc/ldap/rootpw.conf", "r") as fh:
        for line in fh:
            line = line.strip()
            if line.startswith('rootpw '):
                bindpw = line[7:].strip('"')
                break
        else:
            exit(1)

    if not opts.filter:
        opts.filter = '(uid=%s$)' % ucr['hostname']

    # get local and master connection
    local = uldap.access(binddn=binddn,
                         bindpw=bindpw,
                         start_tls=0,
                         host="localhost",
                         port=389)
    if ucr.get("server/role", "") == "domaincontroller_backup":
        master = uldap.getAdminConnection()
    else:
        master = uldap.getMachineConnection(ldap_master=True)

    # delete local
    if opts.remove:
        res = local.search(base=base, filter=opts.filter)
        for dn, data in res:
            print("remove from local: %s" % (dn, ))
            if not opts.simulate:
                local.delete(dn)

    # resync from master
    res = master.search(base=base, filter=opts.filter)
    for dn, data in res:
        print("resync from master: %s" % (dn, ))
        local_res = local.search(base=dn)
        if not local_res or not opts.update:
            print('  ==> adding object')
            if not opts.simulate:
                local.add(dn, ldap.modlist.addModlist(data))
        else:
            modlist = []
            local_data = local_res[0][1]
            for key in set(data.keys()) | set(local_data.keys()):
                if local_data.get(key, []) != data.get(key, []):
                    modlist.append(
                        [key, local_data.get(key, []),
                         data.get(key, [])])
            if not modlist:
                print('  ==> no change')
            else:
                print('  ==> modifying object')
                if not opts.simulate:
                    local.modify(dn, modlist)
Esempio n. 13
0
def execute(*args, **kw):
    ask_questions = True

    if not conf.config_file == conf.defaults.config_file:
        ask_questions = False

    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', 'unique_attribute', 'entryuuid')

        fp = open(conf.defaults.config_file, "w+")
        conf.cfg_parser.write(fp)
        fp.close()

        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')

        # 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')

        return

    elif conf.with_ad and conf.with_openldap:
        print >> sys.stderr, utils.multiline_message(
            _("""
                        You can not configure Kolab to run against OpenLDAP
                        and Active Directory simultaneously.
                    """))

        sys.exit(1)

    # Pre-execution checks
    for path, directories, files in os.walk('/etc/dirsrv/'):
        for direct in directories:
            if direct.startswith('slapd-'):
                print >> sys.stderr, utils.multiline_message(
                    _("""
                                It seems 389 Directory Server has an existing
                                instance configured. This setup script does not
                                intend to destroy or overwrite your data. Please
                                make sure /etc/dirsrv/ and /var/lib/dirsrv/ are
                                clean so that this setup does not have to worry.
                            """))

                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")

    else:
        _input['admin_pass'] = conf.get('ldap', 'bind_pw')
        _input['dirmgr_pass'] = conf.get('ldap', 'bind_pw')
        try:
            pw = pwd.getpwnam("dirsrv")
        except:
            _input['userid'] = "nobody"
            _input['group'] = "nobody"
        else:
            _input['userid'] = "dirsrv"
            _input['group'] = "dirsrv"

    # TODO: Verify the user and group exist.

    # TODO: This takes the system fqdn, domainname and hostname, rather then
    # the desired fqdn, domainname and hostname.
    #
    # 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'])

    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.
                    """))

        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.
                                """))

        _input['nodotdomain'] = _input['domain'].replace('.', '_')
        _input['rootdn'] = utils.standard_root_dn(_input['domain'])

        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.
                    """))

        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.
                                """))

    # TODO: Loudly complain if the fqdn does not resolve back to this system.

    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)

    if os.path.isfile("/usr/sbin/setup-ds-admin.pl"):
        setup_ds_admin = "/usr/sbin/setup-ds-admin.pl"
    #elif os.path.isfile("/usr/sbin/setup-ds-admin"):
    #setup_ds_admin = "/usr/sbin/setup-ds-admin"
    elif os.path.isfile("/usr/sbin/setup-ds.pl"):
        setup_ds_admin = "/usr/sbin/setup-ds.pl"
    elif os.path.isfile("/usr/sbin/setup-ds"):
        setup_ds_admin = "/usr/sbin/setup-ds"
    else:
        log.error(_("No directory server setup tool available."))
        sys.exit(1)

    command = [
        setup_ds_admin, '--debug', '--silent', '--force',
        '--file=%s' % (filename)
    ]

    print >> sys.stderr, utils.multiline_message(
        _("""
                    Setup is now going to set up the 389 Directory Server. This
                    may take a little while (during which period there is no
                    output and no progress indication).
                """))

    log.info(_("Setting up 389 Directory Server"))

    setup_389 = subprocess.Popen(command,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)

    (stdoutdata, stderrdata) = setup_389.communicate()

    if not setup_389.returncode == 0:
        print >> sys.stderr, utils.multiline_message(
            _("""
                        An error was detected in the setup procedure for 389
                        Directory Server. This setup will write out stderr and
                        stdout to /var/log/kolab/setup.error.log and
                        /var/log/kolab/setup.out.log respectively, before it
                        exits.
                    """))

        fp = open('/var/log/kolab/setup.error.log', 'w')
        fp.write(stderrdata)
        fp.close()

        fp = open('/var/log/kolab/setup.out.log', 'w')
        fp.write(stdoutdata)
        fp.close()

    log.debug(_("Setup DS stdout:"), level=8)
    log.debug(stdoutdata, level=8)

    log.debug(_("Setup DS stderr:"), level=8)
    log.debug(stderrdata, level=8)

    if not setup_389.returncode == 0:
        sys.exit(1)

    # Find the kolab schema. It's installed as %doc in the kolab-schema package.
    # TODO: Chown nobody, nobody, chmod 440
    schema_file = None
    for root, directories, filenames in os.walk('/usr/share/doc/'):
        for filename in filenames:
            if filename.startswith('kolab') and filename.endswith(
                    '.ldif') and schema_file == None:
                schema_file = os.path.join(root, filename)

    if not schema_file == None:
        try:
            shutil.copy(
                schema_file, '/etc/dirsrv/slapd-%s/schema/99%s' %
                (_input['hostname'], os.path.basename(schema_file)))

            schema_error = False
        except:
            log.error(_("Could not copy the LDAP extensions for Kolab"))
            schema_error = True
    else:
        log.error(_("Could not find the ldap Kolab schema file"))
        schema_error = True

    if os.path.isfile('/bin/systemctl'):
        subprocess.call(['/bin/systemctl', 'restart', 'dirsrv.target'])
        time.sleep(20)
    elif os.path.isfile('/sbin/service'):
        subprocess.call(['/sbin/service', 'dirsrv', 'restart'])
    elif os.path.isfile('/usr/sbin/service'):
        subprocess.call(['/usr/sbin/service', 'dirsrv', 'stop'])
        time.sleep(20)
        subprocess.call(['/usr/sbin/service', 'dirsrv', 'start'])
    else:
        log.error(_("Could not start the directory server service."))

    if os.path.isfile('/bin/systemctl'):
        subprocess.call(['/bin/systemctl', 'enable', 'dirsrv.target'])
    elif os.path.isfile('/sbin/chkconfig'):
        subprocess.call(['/sbin/chkconfig', 'dirsrv', 'on'])
    elif os.path.isfile('/usr/sbin/update-rc.d'):
        subprocess.call(['/usr/sbin/update-rc.d', 'dirsrv', 'defaults'])
    else:
        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')

    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', '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'])

    fp = open(conf.defaults.config_file, "w+")
    conf.cfg_parser.write(fp)
    fp.close()

    log.info(_("Inserting service users into LDAP."))

    # Insert service users
    auth = Auth(_input['domain'])
    auth.connect()
    auth._auth.connect()
    auth._auth._bind(bind_dn='cn=Directory Manager',
                     bind_pw=_input['dirmgr_pass'])

    dn = 'uid=%s,ou=Special Users,%s' % (conf.get(
        'cyrus-imap', 'admin_login'), _input['rootdn'])

    # A dict to help build the "body" of the object
    attrs = {}
    attrs['objectclass'] = [
        'top', 'person', 'inetorgperson', 'organizationalperson'
    ]
    attrs['uid'] = conf.get('cyrus-imap', 'admin_login')
    attrs['givenname'] = "Cyrus"
    attrs['surname'] = "Administrator"
    attrs['cn'] = "Cyrus Administrator"
    attrs['userPassword'] = _input['cyrus_admin_pass']

    # Convert our dict to nice syntax for the add-function using modlist-module
    ldif = ldap.modlist.addModlist(attrs)

    # 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'])

    dn = 'uid=kolab-service,ou=Special Users,%s' % (_input['rootdn'])

    # A dict to help build the "body" of the object
    attrs = {}
    attrs['objectclass'] = [
        'top', 'person', 'inetorgperson', 'organizationalperson'
    ]
    attrs['uid'] = "kolab-service"
    attrs['givenname'] = "Kolab"
    attrs['surname'] = "Service"
    attrs['cn'] = "Kolab Service"
    attrs['userPassword'] = _input['kolab_service_pass']
    attrs['nslookthroughlimit'] = '-1'
    attrs['nssizelimit'] = '-1'
    attrs['nstimelimit'] = '-1'
    attrs['nsidletimeout'] = '-1'

    # Convert our dict to nice syntax for the add-function using modlist-module
    ldif = ldap.modlist.addModlist(attrs)

    # Do the actual synchronous add-operation to the ldapserver
    auth._auth.ldap.add_s(dn, ldif)

    dn = 'ou=Resources,%s' % (_input['rootdn'])

    # A dict to help build the "body" of the object
    attrs = {}
    attrs['objectclass'] = ['top', 'organizationalunit']
    attrs['ou'] = "Resources"

    # Convert our dict to nice syntax for the add-function using modlist-module
    ldif = ldap.modlist.addModlist(attrs)

    # Do the actual synchronous add-operation to the ldapserver
    auth._auth.ldap.add_s(dn, ldif)

    dn = 'ou=Shared Folders,%s' % (_input['rootdn'])

    # A dict to help build the "body" of the object
    attrs = {}
    attrs['objectclass'] = ['top', 'organizationalunit']
    attrs['ou'] = "Shared Folders"

    # Convert our dict to nice syntax for the add-function using modlist-module
    ldif = ldap.modlist.addModlist(attrs)

    # Do the actual synchronous add-operation to the ldapserver
    auth._auth.ldap.add_s(dn, ldif)

    log.info(_("Writing out cn=kolab,cn=config"))

    dn = 'cn=kolab,cn=config'

    # A dict to help build the "body" of the object
    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'])

    # Convert our dict to nice syntax for the add-function using modlist-module
    ldif = ldap.modlist.addModlist(attrs)

    # Do the actual synchronous add-operation to the ldapserver
    auth._auth.ldap.add_s(dn, ldif)

    log.info(
        _("Adding domain %s to list of domains for this deployment") %
        (_input['domain']))
    dn = "associateddomain=%s,cn=kolab,cn=config" % (_input['domain'])
    attrs = {}
    attrs['objectclass'] = ['top', 'domainrelatedobject']
    attrs['associateddomain'] = [
        '%s' % (_input['domain']),
        '%s' % (_input['fqdn']), 'localhost.localdomain', 'localhost'
    ]

    # De-duplicate attribute values before attempting to insert the object (#2205)
    attrs['associateddomain'] = list(set(attrs['associateddomain']))
    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)

    # 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']):
        attrs['objectclass'].append('inetdomain')
        attrs['inetdomainbasedn'] = _input['rootdn']

    ldif = ldap.modlist.addModlist(attrs)
    auth._auth.ldap.add_s(dn, ldif)

    if not conf.anonymous:
        log.info(_("Disabling anonymous binds"))
        dn = "cn=config"
        modlist = []
        modlist.append(
            (ldap.MOD_REPLACE, "nsslapd-allow-anonymous-access", "off"))
        auth._auth.ldap.modify_s(dn, modlist)

    # TODO: Ensure the uid attribute is unique
    # TODO^2: Consider renaming the general "attribute uniqueness to "uid attribute uniqueness"
    log.info(_("Enabling attribute uniqueness plugin"))
    dn = "cn=attribute uniqueness,cn=plugins,cn=config"
    modlist = []
    modlist.append((ldap.MOD_REPLACE, "nsslapd-pluginEnabled", "on"))
    auth._auth.ldap.modify_s(dn, modlist)

    log.info(_("Enabling referential integrity plugin"))
    dn = "cn=referential integrity postoperation,cn=plugins,cn=config"
    modlist = []
    modlist.append((ldap.MOD_REPLACE, "nsslapd-pluginEnabled", "on"))
    auth._auth.ldap.modify_s(dn, modlist)

    log.info(_("Enabling and configuring account policy plugin"))
    dn = "cn=Account Policy Plugin,cn=plugins,cn=config"
    modlist = []
    modlist.append((ldap.MOD_REPLACE, "nsslapd-pluginEnabled", "on"))
    modlist.append((ldap.MOD_ADD, "nsslapd-pluginarg0",
                    "cn=config,cn=Account Policy Plugin,cn=plugins,cn=config"))
    auth._auth.ldap.modify_s(dn, modlist)

    dn = "cn=config,cn=Account Policy Plugin,cn=plugins,cn=config"
    modlist = []
    modlist.append((ldap.MOD_REPLACE, "alwaysrecordlogin", "yes"))
    modlist.append((ldap.MOD_ADD, "stateattrname", "lastLoginTime"))
    modlist.append((ldap.MOD_ADD, "altstateattrname", "createTimestamp"))
    auth._auth.ldap.modify_s(dn, modlist)

    # Add kolab-admin role
    log.info(_("Adding the kolab-admin role"))
    dn = "cn=kolab-admin,%s" % (_input['rootdn'])
    attrs = {}
    attrs['description'] = "Kolab Administrator"
    attrs['objectClass'] = [
        'top', 'ldapsubentry', 'nsroledefinition', 'nssimpleroledefinition',
        'nsmanagedroledefinition'
    ]
    attrs['cn'] = "kolab-admin"
    ldif = ldap.modlist.addModlist(attrs)

    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']
    aci = []

    if schema_error:
        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") (version 3.0; acl "Enable self write for common attributes"; allow (read,compare,search,write)(userdn = "ldap:///self");)'
        )
    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 "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))
    aci.append(
        '(targetattr != "userPassword") (version 3.0;acl "Search Access";allow (read,compare,search)(userdn = "ldap:///all");)'
    )
    modlist = []
    modlist.append((ldap.MOD_REPLACE, "aci", aci))
    auth._auth.ldap.modify_s(dn, modlist)

    if os.path.isfile('/bin/systemctl'):
        if not os.path.isfile('/usr/lib/systemd/system/dirsrv-admin.service'):
            log.info(_("directory server admin service not available"))
        else:
            subprocess.call(
                ['/bin/systemctl', 'enable', 'dirsrv-admin.service'])
    elif os.path.isfile('/sbin/chkconfig'):
        subprocess.call(['/sbin/chkconfig', 'dirsrv-admin', 'on'])
    elif os.path.isfile('/usr/sbin/update-rc.d'):
        subprocess.call(['/usr/sbin/update-rc.d', 'dirsrv-admin', 'defaults'])
    else:
        log.error(_("Could not start and configure to start on boot, the " + \
                "directory server admin service."))
def main():
    usage = "usage: %prog [options]"
    parser = optparse.OptionParser(usage=usage, description=__doc__)
    parser.add_option(
        "-f",
        "--filter",
        help=
        "resync objects from master found by this filter. Default: (uid=<hostname>$)"
    )
    parser.add_option("-r",
                      "--remove",
                      action="store_true",
                      help="remove objects in local database before resync")
    parser.add_option("-s",
                      "--simulate",
                      action="store_true",
                      help="dry run, do not remove or add")
    parser.add_option("-u",
                      "--update",
                      action="store_true",
                      help="update/modify existing objects")
    opts, args = parser.parse_args()

    ucr = univention.config_registry.ConfigRegistry()
    ucr.load()
    base = ucr.get("ldap/base")
    server_role = ucr.get("server/role", "")
    if server_role == 'domaincontroller_master':
        print('local ldap is master server, nothing todo')
        return
    if server_role not in [
            'domaincontroller_backup', 'domaincontroller_slave'
    ]:
        print(
            'server role ("{}") has no ldap, nothing todo'.format(server_role))
        return

    if not opts.filter:
        opts.filter = '(uid=%s$)' % ucr['hostname']

    # get local and master connection
    local = uldap.getRootDnConnection()
    if server_role == "domaincontroller_backup":
        master = uldap.getAdminConnection()
    else:
        master = uldap.getMachineConnection(ldap_master=True)

    # delete local
    if opts.remove:
        res = local.search(base=base, filter=opts.filter)
        if not res:
            print('object does not exist local')
        for dn, data in res:
            print("remove from local: %s" % (dn, ))
            if not opts.simulate:
                local.delete(dn)

    # resync from master
    res = master.search(base=base, filter=opts.filter)
    if not res:
        print('object does not exist on master')
    for dn, data in res:
        print("resync from master: %s" % (dn, ))
        try:
            local_res = local.search(base=dn)
        except ldap.NO_SUCH_OBJECT:
            local_res = None
        if local_res and opts.remove and opts.simulate:
            local_res = None
        if not local_res and not opts.update:
            print('  ==> adding object')
            if not opts.simulate:
                local.add(dn, ldap.modlist.addModlist(data))
        elif not local_res and opts.update:
            print('  ==> object does not exist, can not update')
        elif local_res and opts.update:
            modlist = []
            local_data = local_res[0][1]
            for key in set(data.keys()) | set(local_data.keys()):
                if set(local_data.get(key, [])).symmetric_difference(
                        set(data.get(key, []))):
                    modlist.append(
                        [key, local_data.get(key, []),
                         data.get(key, [])])
            if not modlist:
                print('  ==> no change')
            else:
                print('  ==> modifying object')
                if not opts.simulate:
                    local.modify(dn, modlist)
        elif local_res and not opts.update:
            print('  ==> object does exist, can not create')