def test_001_user_rename(self): """ Rename user "Doe, John" to "Sixpack, Joe" and verify the recipient policy is applied, and the IMAP INBOX folder for the user is renamed. """ auth = Auth() auth.connect() recipient = auth.find_recipient('*****@*****.**') user_info = wap_client.user_info(recipient) if 'mailhost' not in user_info: from tests.functional.synchronize import synchronize_once synchronize_once() imap = IMAP() imap.connect() folders = imap.lm('user/[email protected]') self.assertEqual(len(folders), 1) auth = Auth() auth.connect() recipient = auth.find_recipient("%(local)s@%(domain)s" % (self.user)) user_info = wap_client.user_info(recipient) user_info['sn'] = 'Sixpack' user_info['givenname'] = 'Joe' user_info['uid'] = 'sixpack' user_edit = wap_client.user_edit(recipient, user_info) time.sleep(2) print imap.lm() user_info = wap_client.user_info('uid=sixpack,ou=People,dc=example,dc=org') if not user_info['mail'] == '*****@*****.**': from tests.functional.synchronize import synchronize_once synchronize_once() user_info = wap_client.user_info('uid=sixpack,ou=People,dc=example,dc=org') self.assertEqual(user_info['mail'], '*****@*****.**') print imap.lm() folders = imap.lm('user/[email protected]') self.assertEqual(len(folders), 0, "INBOX for john.doe still exists") folders = imap.lm('user/[email protected]') self.assertEqual(len(folders), 1, "INBOX for joe.sixpack does not exist")
def execute(*args, **kw): """ List deleted mailboxes """ try: domain = conf.cli_args.pop(0) except: domain = utils.ask_question(_("Domain")) imap = IMAP() imap.connect() auth = Auth() auth.connect() domains = auth.list_domains() folders = [] for primary,secondaries in domains: if not domain == primary and not domain in secondaries: continue folders.extend(imap.lm("user/%%@%s" % (primary))) for secondary in secondaries: folders.extend(imap.lm("user/%%@%s" % (secondary))) print "Deleted folders:" for folder in folders: if not conf.raw: print imap_utf7.decode(folder) else: print folder
def test_001_two_johns(self): from tests.functional.user_add import user_add user_add("John", "Doe") user_add("John", "Doe") time.sleep(3) auth = Auth() auth.connect() max_tries = 20 while max_tries > 0: recipient1 = auth.find_recipient('*****@*****.**') recipient2 = auth.find_recipient('*****@*****.**') if not recipient1 or not recipient2: time.sleep(1) max_tries -= 1 else: break imap = IMAP() imap.connect() folders = imap.lm('user/[email protected]') self.assertEqual(len(folders), 1, "No INBOX found for first John") folders = imap.lm('user/[email protected]') self.assertEqual(len(folders), 1, "No INBOX found for second John")
def test_002_user_recipient_policy_duplicate(self): from tests.functional.user_add import user_add user = {'local': 'jane.doe', 'domain': 'example.org'} user_add("Jane", "Doe") time.sleep(3) auth = Auth() auth.connect() recipient = auth.find_recipient("%(local)s@%(domain)s" % (user)) if hasattr(self, 'assertIsInstance'): self.assertIsInstance(recipient, str) self.assertEqual(recipient, "uid=doe2,ou=People,dc=example,dc=org") result = wap_client.user_info(recipient) if 'mailhost' not in result: from tests.functional.synchronize import synchronize_once synchronize_once() result = wap_client.user_info(recipient) self.assertEqual(result['mail'], '*****@*****.**') self.assertEqual(result['alias'], ['*****@*****.**', '*****@*****.**'])
def test_002_resource_collection(self): auth = Auth() auth.connect() attrs = auth.get_entry_attributes(None, self.cars['dn'], ['*']) self.assertIn('groupofuniquenames', attrs['objectclass']) self.assertEqual(len(attrs['uniquemember']), 3) self.assertEqual(attrs['kolabinvitationpolicy'], 'ACT_ACCEPT')
def execute(*args, **kw): """ List deleted mailboxes """ imap = IMAP() imap.connect() auth = Auth() auth.connect() domains = auth.list_domains() folders = [] for domain in list(set(domains.keys())): folders.extend(imap.lm("DELETED/*@%s" % (domain))) folders.extend(imap.lm("DELETED/*")) print "Deleted folders:" for folder in folders: utf8_folder = imap_utf7.decode(folder).encode('utf-8') mbox_parts = imap.parse_mailfolder(utf8_folder) ts = datetime.datetime.fromtimestamp(int(mbox_parts['hex_timestamp'], 16)) if not conf.raw: print "%s (Deleted at %s)" % (utf8_folder, ts) else: print "%s (Deleted at %s)" % (folder, ts)
def test_001_resource_created(self): auth = Auth() auth.connect() resource = auth.find_resource(self.audi['mail']) self.assertEqual(resource, self.audi['dn']) collection = auth.find_resource(self.cars['mail']) self.assertEqual(collection, self.cars['dn'])
def test_002_fr_FR_user_recipient_policy(self): auth = Auth() auth.connect() recipient = auth.find_recipient("%(local)s@%(domain)s" % (self.user)) if hasattr(self, 'assertIsInstance'): self.assertIsInstance(recipient, str) self.assertEqual(recipient, "uid=fuentes,ou=People,dc=example,dc=org") result = wap_client.user_info(recipient) self.assertEqual(result['mail'], '*****@*****.**') self.assertEqual(sorted(result['alias']), ['*****@*****.**', '*****@*****.**'])
def test_001_user_recipient_policy(self): auth = Auth() auth.connect() recipient = auth.find_recipient("%(local)s@%(domain)s" % (self.user)) if hasattr(self, 'assertIsInstance'): self.assertIsInstance(recipient, str) self.assertEqual(recipient, "uid=doe,ou=People,dc=example,dc=org") result = wap_client.user_info(recipient) self.assertEqual(result['mail'], '*****@*****.**') self.assertEqual(result['alias'], ['*****@*****.**', '*****@*****.**'])
def execute(*args, **kw): global imap, pool auth = Auth() if conf.domain == "all": log.debug(_("Listing domains..."), level=5) start_time = time.time() domains = auth.list_domains() end_time = time.time() log.debug(_("Found %d domains in %d seconds") % (len(domains), (end_time - start_time)), level=8) else: domains = {} domains[conf.domain] = conf.domain if version.StrictVersion(sys.version[:3]) >= version.StrictVersion("2.7"): pool = multiprocessing.Pool(conf.threads, worker_process, (), 1) else: pool = multiprocessing.Pool(conf.threads, worker_process, ()) for primary_domain in list(set(domains.values())): log.debug(_("Running for domain %s") % (primary_domain), level=8) auth = Auth(primary_domain) auth.connect(primary_domain) start_time = time.time() auth.synchronize(mode='_paged_search', callback=queue_add) end_time = time.time() log.info( _("Synchronizing users for %s took %d seconds") % (primary_domain, (end_time - start_time))) while not pool._taskqueue.empty(): time.sleep(1)
def test_001_default(self): from tests.functional.user_add import user_add user_add("John", "Doe") from tests.functional.synchronize import synchronize_once synchronize_once() auth = Auth() auth.connect() user = auth.find_recipient('*****@*****.**') user_info = wap_client.user_info(user) self.assertEqual(user_info['uid'], "doe") from tests.functional.purge_users import purge_users purge_users()
def test_003_givenname_fc_dot_surname(self): self.set('example.org', 'policy_uid', "'%(givenname)s'[0:1].%(surname)s") from tests.functional.user_add import user_add user_add("John", "Doe") from tests.functional.synchronize import synchronize_once synchronize_once() auth = Auth() auth.connect() user = auth.find_recipient('*****@*****.**') user_info = wap_client.user_info(user) self.assertEqual(user_info['uid'], "J.Doe") from tests.functional.purge_users import purge_users purge_users()
def execute(*args, **kw): """ List deleted mailboxes """ imap = IMAP() imap.connect() auth = Auth() auth.connect() domains = auth.list_domains() folders = [] for domain in domains.keys(): print "%s: %d" % (domain, len(imap.lm("user/%%@%s" % (domain)))) null_realm = len(imap.lm("user/%%")) if null_realm > 0: print "null: %d" % (null_realm)
def synchronize(self, domain): log.debug(_("Synchronizing for domain %s") % (domain), level=8) sync_interval = conf.get('kolab', 'sync_interval') if sync_interval is None or sync_interval == 0: sync_interval = 300 else: sync_interval = (int)(sync_interval) while True: try: auth = Auth(domain) auth.connect(domain) auth.synchronize() time.sleep(sync_interval) except KeyboardInterrupt: break except Exception, errmsg: log.error( _("Error in process %r, terminating:\n\t%r") % (self.name, errmsg)) import traceback traceback.print_exc() time.sleep(1)
def execute(*args, **kw): """ Transfer mailbox """ if len(conf.cli_args) > 1: mailfolder = conf.cli_args.pop(0) target_server = conf.cli_args.pop(0) if len(conf.cli_args) > 0: target_partition = conf.cli_args.pop(0) imap = IMAP() imap.connect() mbox_parts = imap.parse_mailfolder(mailfolder) if mbox_parts['domain'] == None: domain = conf.get('kolab', 'primary_domain') user_identifier = mbox_parts['path_parts'][1] else: domain = mbox_parts['domain'] user_identifier = "%s@%s" % (mbox_parts['path_parts'][1], mbox_parts['domain']) auth = Auth(domain=domain) auth.connect() user = auth.find_recipient(user_identifier) source_server = imap.user_mailbox_server(mailfolder) imap.connect(server=source_server) imap.imap.xfer(mailfolder, target_server) if not user == None and not len(user) < 1: auth.set_entry_attributes(domain, user, {'mailhost': target_server})
_("No recipients for '%s' (use --delete to delete)!") % (r_folder)) for primary in list(set(domains.values())): secondaries = [x for x in domains.keys() if domains[x] == primary] folders = [] folders.extend(imap.lm('shared/%%@%s' % (primary))) folders.extend(imap.lm('user/%%@%s' % (primary))) for secondary in secondaries: folders.extend(imap.lm('shared/%%@%s' % (secondary))) folders.extend(imap.lm('user/%%@%s' % (secondary))) auth = Auth(domain=primary) auth.connect() for folder in folders: server = imap.user_mailbox_server(folder) r_folder = folder if folder.startswith('shared/'): recipient = auth.find_folder_resource(folder) else: r_folder = '/'.join(folder.split('/')[1:]) recipient = auth.find_recipient( r_folder, search_attrs=[result_attribute]) if (isinstance(recipient, list)): if len(recipient) > 1:
def execute(*args, **kw): try: address = conf.cli_args.pop(0) except: address = utils.ask_question(_("Email Address")) auth = Auth() auth.connect() user = auth.find_recipient(address) # Get the main, default backend backend = conf.get('kolab', 'imap_backend') if len(address.split('@')) > 1: domain = address.split('@')[1] else: domain = conf.get('kolab', 'primary_domain') if conf.has_section(domain) and conf.has_option(domain, 'imap_backend'): backend = conf.get(domain, 'imap_backend') if conf.has_section(domain) and conf.has_option(domain, 'imap_uri'): uri = conf.get(domain, 'imap_uri') else: uri = conf.get(backend, 'uri') hostname = None port = None result = urlparse(uri) if hasattr(result, 'hostname'): hostname = result.hostname else: scheme = uri.split(':')[0] (hostname, port) = uri.split('/')[2].split(':') port = 4190 # Get the credentials admin_login = conf.get(backend, 'admin_login') admin_password = conf.get(backend, 'admin_password') import sievelib.managesieve sieveclient = sievelib.managesieve.Client(hostname, port, conf.debuglevel > 8) sieveclient.connect(None, None, True) sieveclient._plain_authentication(admin_login, admin_password, address) sieveclient.authenticated = True result = sieveclient.listscripts() if result == None: active = None scripts = [] else: active, scripts = result log.debug(_("Found the following scripts for user %s: %s") % (address, ','.join(scripts)), level=8) log.debug(_("And the following script is active for user %s: %s") % (address, active), level=8) mgmt_required_extensions = [] mgmt_script = """# # MANAGEMENT # """ user = auth.get_entry_attributes(domain, user, ['*']) # # Vacation settings (a.k.a. Out of Office) # vacation_active = None vacation_text = None vacation_uce = None vacation_noreact_domains = None vacation_react_domains = None vacation_active_attr = conf.get('sieve', 'vacation_active_attr') vacation_text_attr = conf.get('sieve', 'vacation_text_attr') vacation_uce_attr = conf.get('sieve', 'vacation_uce_attr') vacation_noreact_domains_attr = conf.get('sieve', 'vacation_noreact_domains_attr') vacation_react_domains_attr = conf.get('sieve', 'vacation_react_domains_attr') if not vacation_text_attr == None: if user.has_key(vacation_active_attr): vacation_active = utils.true_or_false(user[vacation_active_attr]) else: vacation_active = False if user.has_key(vacation_text_attr): vacation_text = user[vacation_text_attr] else: vacation_active = False if user.has_key(vacation_uce_attr): vacation_uce = utils.true_or_false(user[vacation_uce_attr]) else: vacation_uce = False if user.has_key(vacation_react_domains_attr): if isinstance(user[vacation_react_domains_attr], list): vacation_react_domains = user[vacation_react_domains_attr] else: vacation_react_domains = [user[vacation_react_domains_attr]] else: if user.has_key(vacation_noreact_domains_attr): if isinstance(user[vacation_noreact_domains_attr], list): vacation_noreact_domains = user[ vacation_noreact_domains_attr] else: vacation_noreact_domains = [ user[vacation_noreact_domains_attr] ] else: vacation_noreact_domains = [] # # Delivery to Folder # dtf_active_attr = conf.get('sieve', 'deliver_to_folder_active') if not dtf_active_attr == None: if user.has_key(dtf_active_attr): dtf_active = utils.true_or_false(user[dtf_active_attr]) else: dtf_active = False else: # TODO: Not necessarily de-activated, the *Active attributes are # not supposed to charge this - check the deliver_to_folder_attr # attribute value for a value. dtf_active = False if dtf_active: dtf_folder_name_attr = conf.get('sieve', 'deliver_to_folder_attr') if not dtf_folder_name_attr == None: if user.has_key(dtf_folder_name_attr): dtf_folder = user[dtf_folder_name_attr] else: log.warning( _("Delivery to folder active, but no folder name attribute available for user %r" ) % (user)) dtf_active = False else: log.error( _("Delivery to folder active, but no folder name attribute configured" )) dtf_active = False # # Folder name to delivery spam to. # # Global or local. # sdf_filter = True sdf = conf.get('sieve', 'spam_global_folder') if sdf == None: sdf = conf.get('sieve', 'spam_personal_folder') if sdf == None: sdf_filter = False # # Mail forwarding # forward_active = None forward_addresses = [] forward_keepcopy = None forward_uce = None forward_active_attr = conf.get('sieve', 'forward_address_active') if not forward_active_attr == None: if user.has_key(forward_active_attr): forward_active = utils.true_or_false(user[forward_active_attr]) else: forward_active = False if not forward_active == False: forward_address_attr = conf.get('sieve', 'forward_address_attr') if user.has_key(forward_address_attr): if isinstance(user[forward_address_attr], basestring): forward_addresses = [user[forward_address_attr]] elif isinstance(user[forward_address_attr], str): forward_addresses = [user[forward_address_attr]] else: forward_addresses = user[forward_address_attr] if len(forward_addresses) == 0: forward_active = False forward_keepcopy_attr = conf.get('sieve', 'forward_keepcopy_active') if not forward_keepcopy_attr == None: if user.has_key(forward_keepcopy_attr): forward_keepcopy = utils.true_or_false( user[forward_keepcopy_attr]) else: forward_keepcopy = False forward_uce_attr = conf.get('sieve', 'forward_uce_active') if not forward_uce_attr == None: if user.has_key(forward_uce_attr): forward_uce = utils.true_or_false(user[forward_uce_attr]) else: forward_uce = False if vacation_active: mgmt_required_extensions.append('vacation') mgmt_required_extensions.append('envelope') if dtf_active: mgmt_required_extensions.append('fileinto') if forward_active and (len(forward_addresses) > 1 or forward_keepcopy): mgmt_required_extensions.append('copy') if sdf_filter: mgmt_required_extensions.append('fileinto') import sievelib.factory mgmt_script = sievelib.factory.FiltersSet("MANAGEMENT") for required_extension in mgmt_required_extensions: mgmt_script.require(required_extension) mgmt_script.require('fileinto') if vacation_active: if not vacation_react_domains == None and len( vacation_react_domains) > 0: mgmt_script.addfilter( 'vacation', [('envelope', ':domain', ":is", "from", vacation_react_domains) ], [( "vacation", ":days", 1, ":subject", "Out of Office", # ":handle", see http://tools.ietf.org/html/rfc5230#page-4 # ":mime", to indicate the reason is in fact MIME vacation_text)]) elif not vacation_noreact_domains == None and len( vacation_noreact_domains) > 0: mgmt_script.addfilter( 'vacation', [('not', ('envelope', ':domain', ":is", "from", vacation_noreact_domains))], [( "vacation", ":days", 1, ":subject", "Out of Office", # ":handle", see http://tools.ietf.org/html/rfc5230#page-4 # ":mime", to indicate the reason is in fact MIME vacation_text)]) else: mgmt_script.addfilter( 'vacation', [('true', )], [( "vacation", ":days", 1, ":subject", "Out of Office", # ":handle", see http://tools.ietf.org/html/rfc5230#page-4 # ":mime", to indicate the reason is in fact MIME vacation_text)]) if forward_active: forward_rules = [] # Principle can be demonstrated by: # # python -c "print ','.join(['a','b','c'][:-1])" # for forward_copy in forward_addresses[:-1]: forward_rules.append(("redirect", ":copy", forward_copy)) if forward_keepcopy: # Principle can be demonstrated by: # # python -c "print ','.join(['a','b','c'][-1])" # if forward_uce: rule_name = 'forward-uce-keepcopy' else: rule_name = 'forward-keepcopy' forward_rules.append(("redirect", ":copy", forward_addresses[-1])) else: if forward_uce: rule_name = 'forward-uce' else: rule_name = 'forward' forward_rules.append(("redirect", forward_addresses[-1])) forward_rules.append(("stop")) if forward_uce: mgmt_script.addfilter(rule_name, ['true'], forward_rules) else: # NOTE: Messages with no X-Spam-Status header need to be matched # too, and this does exactly that. mgmt_script.addfilter(rule_name, [("not", ("X-Spam-Status", ":matches", "Yes,*"))], forward_rules) if sdf_filter: mgmt_script.addfilter('spam_delivery_folder', [("X-Spam-Status", ":matches", "Yes,*")], [("fileinto", "INBOX/Spam"), ("stop")]) if dtf_active: mgmt_script.addfilter('delivery_to_folder', ['true'], [("fileinto", dtf_folder)]) mgmt_script = mgmt_script.__str__() log.debug(_("MANAGEMENT script for user %s contents: %r") % (address, mgmt_script), level=9) result = sieveclient.putscript("MANAGEMENT", mgmt_script) if not result: log.error( _("Uploading script MANAGEMENT failed for user %s") % (address)) else: log.debug(_("Uploading script MANAGEMENT for user %s succeeded") % (address), level=8) user_script = """# # User # require ["include"]; """ for script in scripts: if not script in ["MASTER", "MANAGEMENT", "USER"]: log.debug(_("Including script %s in USER (for user %s)") % (script, address), level=8) user_script = """%s include :personal "%s"; """ % (user_script, script) result = sieveclient.putscript("USER", user_script) if not result: log.error(_("Uploading script USER failed for user %s") % (address)) else: log.debug(_("Uploading script USER for user %s succeeded") % (address), level=8) result = sieveclient.putscript( "MASTER", """# # MASTER # # This file is authoritative for your system and MUST BE KEPT ACTIVE. # # Altering it is likely to render your account dysfunctional and may # be violating your organizational or corporate policies. # # For more information on the mechanism and the conventions behind # this script, see http://wiki.kolab.org/KEP:14 # require ["include"]; # OPTIONAL: Includes for all or a group of users # include :global "all-users"; # include :global "this-group-of-users"; # The script maintained by the general management system include :personal "MANAGEMENT"; # The script(s) maintained by one or more editors available to the user include :personal "USER"; """) if not result: log.error(_("Uploading script MASTER failed for user %s") % (address)) else: log.debug(_("Uploading script MASTER for user %s succeeded") % (address), level=8) sieveclient.setactive("MASTER")
def do_sync(self): domain_auth = {} pid = os.getpid() primary_domain = conf.get('kolab', 'primary_domain') while 1: primary_auth = Auth(primary_domain) connected = False while not connected: try: primary_auth.connect() connected = True except Exception, errmsg: connected = False log.error(_("Could not connect to LDAP, is it running?")) time.sleep(5) log.debug(_("Listing domains..."), level=5) start = time.time() try: domains = primary_auth.list_domains() except: time.sleep(60) continue if isinstance(domains, list) and len(domains) < 1: log.error(_("No domains. Not syncing")) time.sleep(5) continue # domains now is a list of key-valye pairs in the format of # {'secondary': 'primary'}, we want the primaries primaries = list(set(domains.values())) # Store the naming contexts for the domains as # # {'domain': 'naming context'} # # and the domain root dns as # # {'domain': 'domain root dn'} # domain_root_dns = {} naming_contexts = {} for primary in primaries: naming_context = primary_auth.domain_naming_context(primary) domain_root_dn = primary_auth._auth._kolab_domain_root_dn( primary) log.debug(_("Domain %r naming context: %r, root dn: %r") % (primary, naming_context, domain_root_dn), level=8) domain_root_dns[primary] = domain_root_dn naming_contexts[primary] = naming_context log.debug(_("Naming contexts to synchronize: %r") % (list(set(naming_contexts.values()))), level=8) # Find however many naming contexts we have, and what the # corresponding domain name is for them. primary_domains = [ x for x, y in naming_contexts.iteritems() if domain_root_dns[x] == y ] # Now we can check if any changes happened. added_domains = [] removed_domains = [] # Combine the domains from LDAP with the domain processes # accounted for locally. all_domains = list(set(primary_domains + domain_auth.keys())) log.debug(_("Result set of domains: %r") % (all_domains), level=8) for domain in all_domains: log.debug(_("Checking for domain %s") % (domain), level=8) if domain in domain_auth.keys() and domain in primary_domains: if not domain_auth[domain].is_alive(): log.debug(_("Domain %s isn't alive anymore.") % (domain), level=8) domain_auth[domain].terminate() added_domains.append(domain) else: log.debug(_("Domain %s already there and alive.") % (domain), level=8) continue elif domain in domain_auth.keys(): log.debug(_("Domain %s should not exist any longer.") % (domain), level=8) removed_domains.append(domain) else: log.debug(_("Domain %s does not have a process yet.") % (domain), level=8) added_domains.append(domain) if len(removed_domains) == 0 and len(added_domains) == 0: try: sleep_between_domain_operations_in_seconds = (float)( conf.get('kolab', 'domain_sync_interval')) time.sleep(sleep_between_domain_operations_in_seconds) except ValueError: time.sleep(600) log.debug(_("added domains: %r, removed domains: %r") % (added_domains, removed_domains), level=8) for domain in added_domains: domain_auth[domain] = Process(domain) domain_auth[domain].start() # Pause or hammer your LDAP server to death if len(added_domains) >= 5: time.sleep(10) for domain in removed_domains: domain_auth[domain].terminate() del domain_auth[domain]
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 user_mailbox_create(self, mailbox_base_name, server=None): """ Create a user mailbox. Returns the full path to the new mailbox folder. """ # TODO: Whether or not to lowercase the mailbox name is really up to the # IMAP server setting username_tolower (normalize_uid, lmtp_downcase_rcpt). self.connect() if not mailbox_base_name == mailbox_base_name.lower(): log.warning(_("Downcasing mailbox name %r") % (mailbox_base_name)) mailbox_base_name = mailbox_base_name.lower() folder_name = "user%s%s" % (self.get_separator(), mailbox_base_name) log.info(_("Creating new mailbox for user %s") %(mailbox_base_name)) max_tries = 10 success = False while not success and max_tries > 0: success = self.create_folder(folder_name, server) if not success: self.disconnect() max_tries -= 1 time.sleep(1) self.connect() if not success: log.error(_("Could not create the mailbox for user %s, aborting." % (mailbox_base_name))) return False # In a Cyrus IMAP Murder topology, wait for the murder to have settled if self.imap_murder(): self.disconnect() self.connect() created = False last_log = time.time() while not created: created = self.has_folder(folder_name) if not created: if time.time() - last_log > 5: log.info(_("Waiting for the Cyrus IMAP Murder to settle...")) last_log = time.time() time.sleep(0.5) _additional_folders = None if not hasattr(self, 'domain'): self.domain = None if self.domain == None and len(mailbox_base_name.split('@')) > 1: self.domain = mailbox_base_name.split('@')[1] if not self.domain == None: if conf.has_option(self.domain, "autocreate_folders"): _additional_folders = conf.get_raw( self.domain, "autocreate_folders" ) else: from pykolab.auth import Auth auth = Auth() auth.connect() domains = auth.list_domains(self.domain) auth.disconnect() if len(domains.keys()) > 0: if domains.has_key(self.domain): primary = domains[self.domain] if conf.has_option(primary, "autocreate_folders"): _additional_folders = conf.get_raw( primary, "autocreate_folders" ) if _additional_folders == None: if conf.has_option('kolab', "autocreate_folders"): _additional_folders = conf.get_raw( 'kolab', "autocreate_folders" ) additional_folders = conf.plugins.exec_hook( "create_user_folders", kw={ 'folder': folder_name, 'additional_folders': _additional_folders } ) if not additional_folders == None: self.user_mailbox_create_additional_folders( mailbox_base_name, additional_folders ) if not self.domain == None: if conf.has_option(self.domain, "sieve_mgmt"): sieve_mgmt_enabled = conf.get(self.domain, 'sieve_mgmt') if utils.true_or_false(sieve_mgmt_enabled): conf.plugins.exec_hook( 'sieve_mgmt_refresh', kw={ 'user': mailbox_base_name } ) return folder_name
def handler(*args, **kw): auth = Auth() auth.connect() if len(args) == 4: # moddn, not relevant for Sieve Script Management pass elif len(args) == 3: dn = args[0] new = utils.normalize(args[1]) old = utils.normalize(args[2]) if isinstance(old, dict) and len(old.keys()) > 0: # Either the entry changed or was deleted if isinstance(new, dict) and len(new.keys()) > 0: # The entry was modified. result_attr = conf.get('cyrus-sasl', 'result_attribute') if result_attr not in new: log.error("Entry %r does not have attribute %r" % (dn, result_attr)) return # See if the mailserver_attribute exists mailserver_attribute = conf.get( 'ldap', 'mailserver_attribute').lower() if mailserver_attribute is None: log.error("Mail server attribute is not set") # TODO: Perhaps, query for IMAP servers. If there is only one, # we know what to do. return if mailserver_attribute in new: if not new[mailserver_attribute] == constants.fqdn: log.info( "The mail server for user %r is set, and it is not me (%r)" % (dn, new[mailserver_attribute])) return else: log.error("Entry %r does not have a mail server set" % (dn)) return conf.plugins.exec_hook('sieve_mgmt_refresh', kw={'user': new[result_attr]}) else: # The entry was deleted. This is irrelevant for # Sieve Script Management return elif isinstance(new, dict) and len(new.keys()) > 0: # Old is not a dict (or empty), so the entry is just created # See if the mailserver_attribute exists mailserver_attribute = conf.get('ldap', 'mailserver_attribute').lower() result_attr = conf.get('cyrus-sasl', 'result_attribute').lower() if mailserver_attribute is None: log.error("Mail server attribute is not set") # TODO: Perhaps, query for IMAP servers. If there is only one, # we know what to do. return if mailserver_attribute in new: if not new[mailserver_attribute] == constants.fqdn: log.info( "The mail server for user %r is set, and it is not me (%r)" % (dn, new[mailserver_attribute])) return conf.plugins.exec_hook('sieve_mgmt_refresh', kw={'user': new[result_attr]}) else: log.info("entry %r changed, but no new or old attributes" % (dn))
def synchronize_once(): auth = Auth() auth.connect() auth.synchronize(mode='_paged_search')
def execute(*args, **kw): try: address = conf.cli_args.pop(0) except: address = utils.ask_question(_("Email Address")) auth = Auth() auth.connect() user = auth.find_recipient(address) # Get the main, default backend backend = conf.get('kolab', 'imap_backend') if len(address.split('@')) > 1: domain = address.split('@')[1] else: domain = conf.get('kolab', 'primary_domain') if conf.has_section(domain) and conf.has_option(domain, 'imap_backend'): backend = conf.get(domain, 'imap_backend') if conf.has_section(domain) and conf.has_option(domain, 'imap_uri'): uri = conf.get(domain, 'imap_uri') else: uri = conf.get(backend, 'uri') hostname = None port = None result = urlparse(uri) if hasattr(result, 'hostname'): hostname = result.hostname else: scheme = uri.split(':')[0] (hostname, port) = uri.split('/')[2].split(':') port = 4190 # Get the credentials admin_login = conf.get(backend, 'admin_login') admin_password = conf.get(backend, 'admin_password') import sievelib.managesieve sieveclient = sievelib.managesieve.Client(hostname, port, conf.debuglevel > 8) sieveclient.connect(None, None, True) result = sieveclient._plain_authentication(admin_login, admin_password, address) if not result: print "LOGIN FAILED??" sieveclient.authenticated = True result = sieveclient.listscripts() if result == None: print "No scripts" sys.exit(0) (active, scripts) = result print "%s (active)" % (active) for script in scripts: print script
def do_saslauthd(self): """ Create the actual listener socket, and handle the authentication. The actual authentication handling is passed on to the appropriate backend authentication classes through the more generic Auth(). """ import binascii import socket import struct s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) # TODO: The saslauthd socket path could be a setting. try: os.remove(conf.socketfile) except: # TODO: Do the "could not remove, could not start" dance pass s.bind(conf.socketfile) os.chmod(conf.socketfile, 0777) s.listen(5) while 1: max_tries = 20 cur_tries = 0 bound = False while not bound: cur_tries += 1 try: (clientsocket, address) = s.accept() bound = True except Exception, errmsg: log.error( _("kolab-saslauthd could not accept " + "connections on socket: %r") % (errmsg)) if cur_tries >= max_tries: log.fatal(_("Maximum tries exceeded, exiting")) sys.exit(1) time.sleep(1) received = clientsocket.recv(4096) login = [] start = 0 end = 2 while end < len(received): (length, ) = struct.unpack("!H", received[start:end]) start += 2 end += length (value, ) = struct.unpack("!%ds" % (length), received[start:end]) start += length end = start + 2 login.append(value) if len(login) == 4: realm = login[3] elif len(login[0].split('@')) > 1: realm = login[0].split('@')[1] else: realm = conf.get('kolab', 'primary_domain') auth = Auth(domain=realm) auth.connect() success = False try: success = auth.authenticate(login) except: success = False if success: # #1170: Catch broken pipe error (incomplete authentication request) try: clientsocket.send(struct.pack("!H2s", 2, "OK")) except: pass else: # #1170: Catch broken pipe error (incomplete authentication request) try: clientsocket.send(struct.pack("!H2s", 2, "NO")) except: pass clientsocket.close() auth.disconnect()
def execute(*args, **kw): try: primary_rcpt_address = conf.cli_args.pop(0) try: secondary_rcpt_address = conf.cli_args.pop(0) except: print >> sys.stderr, _("Specify the (new) alias address") sys.exit(1) except: print >> sys.stderr, _("Specify the existing recipient address") sys.exit(1) if len(primary_rcpt_address.split('@')) > 1: primary_rcpt_domain = primary_rcpt_address.split('@')[-1] else: primary_rcpt_domain = conf.get('kolab', 'primary_domain') auth = Auth(domain=primary_rcpt_domain) domains = auth.list_domains() #print domains if len(secondary_rcpt_address.split('@')) > 1: secondary_rcpt_domain = secondary_rcpt_address.split('@')[-1] else: secondary_rcpt_domain = conf.get('kolab', 'primary_domain') # Check if either is in fact a domain if not primary_rcpt_domain.lower() in domains.keys(): print >> sys.stderr, _("Domain %r is not a local domain") % ( primary_rcpt_domain) sys.exit(1) if not secondary_rcpt_domain.lower() in domains.keys(): print >> sys.stderr, _("Domain %r is not a local domain") % ( secondary_rcpt_domain) sys.exit(1) if not primary_rcpt_domain == secondary_rcpt_domain: if not domains[primary_rcpt_domain] == domains[secondary_rcpt_domain]: print >> sys.stderr, _( "Primary and secondary domain do not have the same parent domain" ) sys.exit(1) primary_recipient_dn = auth.find_recipient(primary_rcpt_address) if primary_recipient_dn == [] or len(primary_recipient_dn) == 0: print >> sys.stderr, _("No such recipient %r") % (primary_rcpt_address) sys.exit(1) secondary_recipient_dn = auth.find_recipient(secondary_rcpt_address) if not secondary_recipient_dn == [] and not len( secondary_recipient_dn) == 0: print >> sys.stderr, _("Recipient for alias %r already exists") % ( secondary_rcpt_address) sys.exit(1) rcpt_attrs = conf.get_list('ldap', 'mail_attributes') primary_rcpt_attr = rcpt_attrs[0] if len(rcpt_attrs) >= 2: secondary_rcpt_attr = rcpt_attrs[1] else: print >> sys.stderr, _("Environment is not configured for " + \ "users to hold secondary mail attributes") sys.exit(1) primary_recipient = auth.get_entry_attributes(primary_rcpt_domain, primary_recipient_dn, rcpt_attrs) if not primary_recipient.has_key(primary_rcpt_attr): print >> sys.stderr, _( "Recipient %r is not the primary recipient for address %r") % ( primary_recipient, primary_rcpt_address) sys.exit(1) if not primary_recipient.has_key(secondary_rcpt_attr): auth.set_entry_attributes( primary_rcpt_domain, primary_recipient_dn, {secondary_rcpt_attr: [secondary_rcpt_address]}) else: if isinstance(primary_recipient[secondary_rcpt_attr], basestring): new_secondary_rcpt_attrs = [ primary_recipient[secondary_rcpt_attr], secondary_rcpt_address ] else: new_secondary_rcpt_attrs = \ primary_recipient[secondary_rcpt_attr] + \ [ secondary_rcpt_address ] auth.set_entry_attributes( primary_rcpt_domain, primary_recipient_dn, {secondary_rcpt_attr: new_secondary_rcpt_attrs})
def execute(*args, **kw): """ Synchronize or display changes """ imap = IMAP() if not conf.connect_server == None: imap.connect(server=conf.connect_server) else: imap.connect() auth = Auth() auth.connect() result_attribute = conf.get('cyrus-sasl', 'result_attribute') if result_attribute is None: result_attribute = 'mail' domains = auth.list_domains() folders = imap.lm() imap_domains_not_domains = [] for folder in folders: if len(folder.split('@')) > 1 and not folder.startswith('DELETED'): _folder_domain = folder.split('@')[-1] if not _folder_domain in list( set(domains.keys() + domains.values())): imap_domains_not_domains.append(folder.split('@')[-1]) imap_domains_not_domains = list(set(imap_domains_not_domains)) log.debug(_("Domains in IMAP not in LDAP: %r") % (imap_domains_not_domains), level=8) if len(imap_domains_not_domains) > 0: for domain in imap_domains_not_domains: folders = [] folders.extend(imap.lm('shared/%%@%s' % (domain))) folders.extend(imap.lm('user/%%@%s' % (domain))) for folder in folders: r_folder = folder if not folder.startswith('shared/'): r_folder = '/'.join(folder.split('/')[1:]) if conf.delete: if conf.dry_run: if not folder.startswith('shared/'): log.warning( _("No recipients for '%s' (would have deleted the mailbox if not for --dry-run)!" ) % (r_folder)) else: continue else: if not folder.startswith('shared/'): log.info( _("Deleting mailbox '%s' because it has no recipients" ) % (folder)) try: imap.dm(folder) except Exception, errmsg: log.error( _("An error occurred removing mailbox %r: %r" ) % (folder, errmsg)) else: log.info( _("Not automatically deleting shared folder '%s'" ) % (folder)) else: log.warning( _("No recipients for '%s' (use --delete to delete)!") % (r_folder))
from pykolab.auth import Auth from pykolab.constants import KOLAB_LIB_PATH from pykolab import telemetry from pykolab.translate import _ # TODO: Figure out how to make our logger do some syslogging as well. log = pykolab.getLogger('pykolab.parse_telemetry') # TODO: Removing the stdout handler would mean one can no longer test by # means of manual execution in debug mode. #log.remove_stdout_handler() conf = pykolab.getConf() conf.finalize_conf() auth = Auth() db = telemetry.init_db() while True: try: log_file = conf.cli_args.pop(0) except: # TODO: More verbose failing or parse all in /var/lib/imap/log/ or # options? break telemetry_log = telemetry.TelemetryLog(log_file) telemetry.expire_sessions()
def handler(*args, **kw): log.info("kolab.handler(args(%d): %r, kw: %r)" % (len(args), args, kw)) auth = Auth() auth.connect() if len(args) == 4: # moddn dn = args[0] new = utils.normalize(args[1]) old = utils.normalize(args[2]) command = args[4] pass elif len(args) == 3: dn = args[0] new = utils.normalize(args[1]) old = utils.normalize(args[2]) if isinstance(old, dict) and len(old.keys()) > 0: # Two options: # - entry changed # - entry deleted log.info("user %r, old is dict" % (dn)) if isinstance(new, dict) and len(new.keys()) > 0: log.info("Modify entry %r" % (dn)) mailserver_attribute = conf.get( 'ldap', 'mailserver_attribute').lower() if mailserver_attribute is None: log.error("Mail server attribute is not set") return if mailserver_attribute in old: log.info( "Modified entry %r has mail server attribute %s: %r" % (dn, mailserver_attribute, new[mailserver_attribute])) if not old[mailserver_attribute] == constants.fqdn: # Even though the new mailserver can be us, it is the # *current* mail server that needs to push for the XFER. log.info( "The mail server for user %r is set, and it is not me (%r)" % (dn, old[mailserver_attribute])) return else: # If old has no mailserver attribute, but new does, we need to create # the user locally. if mailserver_attribute in new: if not new[mailserver_attribute] == constants.fqdn: log.info( "The mail server for user %r is set (in new, not old), but it is not me (%r)" % (dn, new[mailserver_attribute])) return else: log.info( "Entry %r does not have a mail server attribute." % (dn)) return auth._auth._synchronize_callback(change_type='modify', previous_dn=None, change_number=None, dn=dn, entry=new) else: log.info("Delete entry %r" % (dn)) # See if the mailserver_attribute exists mailserver_attribute = conf.get( 'ldap', 'mailserver_attribute').lower() if mailserver_attribute is None: log.error("Mail server attribute is not set") # TODO: Perhaps, query for IMAP servers. If there is only one, # we know what to do. return if mailserver_attribute in old: log.info( "Deleted entry %r has mail server attribute %s: %r" % (dn, mailserver_attribute, old[mailserver_attribute])) if not old[mailserver_attribute] == constants.fqdn: log.info( "The mail server for user %r is set, and it is not me (%r)" % (dn, old[mailserver_attribute])) return else: log.info( "Entry deletion notification for %r does not have a mail server attribute specified." % (dn)) cfg = ConfigRegistry() cfg.load() if cfg.is_true('mail/cyrus/mailbox/delete', True): auth._auth._synchronize_callback(change_type='delete', previous_dn=None, change_number=None, dn=dn, entry=old) elif isinstance(new, dict) and len(new.keys()) > 0: # Old is not a dict (or empty), so the entry is just created log.info("Add entry %r" % (dn)) # See if the mailserver_attribute exists mailserver_attribute = conf.get('ldap', 'mailserver_attribute').lower() if mailserver_attribute is None: log.error("Mail server attribute is not set") # TODO: Perhaps, query for IMAP servers. If there is only one, # we know what to do. return if mailserver_attribute in new: log.info("Added entry %r has mail server attribute %s: %r" % (dn, mailserver_attribute, new[mailserver_attribute])) if not new[mailserver_attribute] == constants.fqdn: log.info( "The mail server for user %r is set, and it is not me (%r)" % (dn, new[mailserver_attribute])) return else: log.info( "Added entry %r does not have a mail server attribute set." % (dn)) return auth._auth._synchronize_callback(change_type='add', previous_dn=None, change_number=None, dn=dn, entry=new) else: log.info("entry %r changed, but no new or old attributes" % (dn))
def execute(*args, **kw): """ List mailboxes """ auth = Auth() domains = auth.list_domains() imap = IMAP() imap.connect() domain_folders = {} subjects = [] # Placeholder for subjects that would have already been deleted subjects_deleted = [] for domain in domains.keys(): domain_folders[domain] = imap.lm("user/%%@%s" % (domain)) for domain in domain_folders.keys(): auth = Auth(domain=domain) auth.connect(domain) for folder in domain_folders[domain]: user = folder.replace('user/', '') try: recipient = auth.find_recipient(user) except ldap.NO_SUCH_OBJECT, errmsg: if not user in subjects_deleted and conf.dryrun: subjects_deleted.append(user) if conf.dryrun: log.info( _("Would have deleted folder 'user/%s' (dryrun)") % (user)) else: log.info(_("Deleting folder 'user/%s'") % (user)) continue if len(recipient) == 0 or recipient == []: if not user in subjects_deleted and conf.dryrun: subjects_deleted.append(user) if conf.dryrun: log.info( _("Would have deleted folder 'user/%s' (dryrun)") % (user)) else: log.info(_("Deleting folder 'user/%s'") % (user)) try: imap.dm(folder) except: log.error( _("Error deleting folder 'user/%s'") % (user)) else: log.debug(_("Valid recipient found for 'user/%s'") % (user), level=6) if not user in subjects: subjects.append(user)
def execute(*args, **kw): try: address = conf.cli_args.pop(0) except: address = utils.ask_question(_("Email Address")) auth = Auth() auth.connect() user = auth.find_recipient(address) # Get the main, default backend backend = conf.get('kolab', 'imap_backend') if len(address.split('@')) > 1: domain = address.split('@')[1] else: domain = conf.get('kolab', 'primary_domain') if conf.has_section(domain) and conf.has_option(domain, 'imap_backend'): backend = conf.get(domain, 'imap_backend') if conf.has_section(domain) and conf.has_option(domain, 'imap_uri'): uri = conf.get(domain, 'imap_uri') else: uri = conf.get(backend, 'uri') hostname = None port = None result = urlparse(uri) if hasattr(result, 'hostname'): hostname = result.hostname else: scheme = uri.split(':')[0] (hostname, port) = uri.split('/')[2].split(':') port = 4190 # Get the credentials admin_login = conf.get(backend, 'admin_login') admin_password = conf.get(backend, 'admin_password') import sievelib.managesieve sieveclient = sievelib.managesieve.Client(hostname, port, True) sieveclient.connect(None, None, True) sieveclient._plain_authentication(admin_login, admin_password, address) sieveclient.authenticated = True active, scripts = sieveclient.listscripts() print "%s (active)" % (active) _all_scripts = [active] + scripts _used_scripts = [active] _included_scripts = [] _a_script = sieveclient.getscript(active) print _a_script import sievelib.parser _a_parser = sievelib.parser.Parser(debug=True) _a_parsed = _a_parser.parse(_a_script) #print "%r" % (_a_parsed) if not _a_parsed: print _a_parser.error print "%r" % (_a_parser.result) for _a_command in _a_parser.result: print _a_command.name, _a_command.arguments if len(_a_command.children) > 0: for _a_child in _a_command.children: print " ", _a_child.name, _a_child.arguments if _a_command.name == "include": if _a_command.arguments["script"].strip('"') in scripts: print "OK" _used_scripts.append(_a_command.arguments["script"].strip('"')) else: print "Not OK" for script in scripts: print script