def delete(mails, revoke_admin_privilege_from_user=True, conn=None): """ Delete standalone domain admin accounts, or revoke admin privilege from mail user which is domain admin. :param mails: list of domain admin email addresses :param revoke_admin_privilege_from_user: if @mails contains mail user which has domain admin privilege, we should revoke the privilege. :param conn: ldap connection cursor """ mails = [str(i).lower() for i in mails if iredutils.is_email(i)] if not mails: return (True, ) if not conn: _wrap = LDAPWrap() conn = _wrap.conn result = {} for mail in mails: # Get dn of admin account under o=domainAdmins dn = ldaputils.rdn_value_to_admin_dn(mail) try: conn.delete_s(dn) log_activity(msg="Delete admin: %s." % (mail), event='delete') except ldap.NO_SUCH_OBJECT: if revoke_admin_privilege_from_user: # This is a mail user admin dn = ldaputils.rdn_value_to_user_dn(mail) try: # Delete enabledService=domainadmin ldap_lib_general.remove_attr_values(dn=dn, attr='enabledService', values=['domainadmin'], conn=conn) # Delete domainGlobalAdmin=yes ldap_lib_general.remove_attr_values( dn=dn, attr='domainGlobalAdmin', values=['yes'], conn=conn) log_activity(msg="Revoke domain admin privilege: %s." % (mail), event='delete') except Exception as e: result[mail] = str(e) except ldap.LDAPError as e: result[mail] = str(e) if result == {}: return (True, ) else: return (False, repr(result))
def get_user_account_setting(mail, profile=None, conn=None): mail = str(mail).lower() if not iredutils.is_email(mail): return (False, 'INVALID_MAIL') if profile: dn = None else: dn = ldaputils.rdn_value_to_user_dn(mail) return __get_account_setting_by_dn(dn=dn, profile=profile, conn=conn)
def get_profile(mail, attributes=None, conn=None): """Get user profile. :param mail: full email address or '@domain.com' (catch-all account) :param attributes: list of LDAP attribute names :param conn: LDAP connection cursor Returned data: - (True, {'dn': '<full_dn>', 'ldif': dict}) - (False, <error_reason>) """ if not attributes: attributes = list(attrs.USER_ATTRS_ALL) if not conn: _wrap = LDAPWrap() conn = _wrap.conn try: dn = ldaputils.rdn_value_to_user_dn(mail) _filter = '(&(objectClass=mailUser)(mail=%s))' % (mail) qr = ldap_lib_general.get_profile_by_dn(dn=dn, query_filter=_filter, attributes=attributes, conn=conn) if not qr[0]: return qr _dn = qr[1]['dn'] _ldif = qr[1]['ldif'] # Normal domain admin is not allowed to view/update global admin profile if not session.get('is_global_admin'): if _ldif.get('domainGlobalAdmin', ['no']) == ['yes']: return (False, 'PERMISSION_DENIED_UPDATE_GLOBAL_ADMIN_PROFILE') # Sort some lists for k in ['shadowAddress', 'mailForwardingAddress']: if k in _ldif: _addrs = _ldif[k] _addrs.sort() _ldif[k] = _addrs return (True, {'dn': _dn, 'ldif': _ldif}) except ldap.NO_SUCH_OBJECT: return (False, 'NO_SUCH_ACCOUNT') except Exception as e: return (False, repr(e))
def reset_forwardings(mail, forwardings=None, conn=None): if forwardings: _addresses = [ str(v).lower() for v in forwardings if iredutils.is_email(v) ] else: _addresses = [] # Remove duplicate addresses if _addresses: _addresses = list(set(_addresses)) # Remove non-existing addresses in same domain. domain = mail.split('@', 1)[-1] addrs_in_domain = set() addrs_not_in_domain = set() for addr in _addresses: if addr.endswith('@' + domain): addrs_in_domain.add(addr) else: addrs_not_in_domain.add(addr) if not conn: _wrap = LDAPWrap() conn = _wrap.conn # Get existing addresses valid_addrs_in_domain = set() if addrs_in_domain: dn = ldaputils.rdn_value_to_domain_dn(domain) _qr = ldap_lib_general.filter_existing_emails(mails=addrs_in_domain, base_dn=dn, conn=conn) valid_addrs_in_domain = set(_qr['exist']) fwd_addrs = list(valid_addrs_in_domain | addrs_not_in_domain) if fwd_addrs: mod_attr = ldaputils.mod_replace('mailForwardingAddress', fwd_addrs) else: mod_attr = ldaputils.mod_replace('mailForwardingAddress', None) try: dn = ldaputils.rdn_value_to_user_dn(mail) conn.modify_s(dn, mod_attr) return (True, ) except Exception as e: return (False, repr(e))
def mark_unmark_as_admin(domain, mails, action, conn=None): domain = str(domain).lower() mails = [ str(v).lower() for v in mails if iredutils.is_email(v) and v.endswith('@' + domain) ] if not conn: _wrap = LDAPWrap() conn = _wrap.conn result = {} for mail in mails: dn = ldaputils.rdn_value_to_user_dn(mail) if action in ['markasadmin', 'unmarkasadmin']: update_attribute = 'enabledService' update_value = 'domainadmin' update_action = 'delete' if action == 'markasadmin': update_action = 'add' elif action in ['markasglobaladmin', 'unmarkasglobaladmin']: # Require global admin if not session.get('is_global_admin'): return (False, 'PERMISSION_DENIED') update_attribute = 'domainGlobalAdmin' update_value = 'yes' update_action = 'delete' if action == 'markasglobaladmin': update_action = 'add' try: ldap_lib_general.add_or_remove_attr_values(dn=dn, attr=update_attribute, values=[update_value], action=update_action, conn=conn) except Exception as e: result[mail] = str(e) if result == {}: return (True, ) else: return (False, repr(result))
def enable_disable_admins(mails, action, conn=None): """Enable (action='enable') or disable (action='disable') given mail admins. :param mails: a list/tuple/set of mail addresses of mail admin accounts :param action: enable, disable. :param conn: ldap connection cursor """ mails = [str(v).lower() for v in mails if iredutils.is_email(v)] action = action.lower() if action not in ['enable', 'disable']: return (False, 'INVALID_ACTION') if not conn: _wrap = LDAPWrap() conn = _wrap.conn for mail in mails: # Standalone admin account dn = ldaputils.rdn_value_to_admin_dn(mail) qr = enable_disable_account_by_dn(dn=dn, action=action, conn=conn) if not qr[0]: if qr[0] == 'NO_SUCH_ACCOUNT': # Admin is a normal mail user. dn = ldaputils.rdn_value_to_user_dn(mail) add_or_remove_attr_values(dn=dn, attr='enabledService', values='domainadmin', action=action, conn=conn) add_or_remove_attr_values(dn=dn, attr='domainGlobalAdmin', values='yes', action=action, conn=conn) return (True, )
def get_admin_account_setting(mail, profile=None, conn=None): """Get per-admin account settings from account profile (dict of LDIF data). :param mail: email address of domain admin :param profile: dict of admin profile LDIF data :param conn: ldap connection cursor """ mail = str(mail).lower() if not iredutils.is_email(mail): return (False, 'INVALID_MAIL') if not conn: _wrap = LDAPWrap() conn = _wrap.conn if profile: _as = ldaputils.get_account_setting_from_profile(profile) return (True, _as) else: # Query standalone admin account first dn = ldaputils.rdn_value_to_admin_dn(mail) _filter = '(&(objectClass=mailAdmin)(mail=%s))' % mail qr = __get_account_setting_by_dn(dn=dn, query_filter=_filter, conn=conn) if not qr[0]: # Query mail user dn = ldaputils.rdn_value_to_user_dn(mail) _filter = '(&(objectClass=mailUser)(|(enabledService=domainadmin)(domainGlobalAdmin=yes)))' qr = __get_account_setting_by_dn(dn=dn, query_filter=_filter, conn=conn) if not qr[0]: return qr return (True, qr[1])
def update_managed_user_attrs(domain, mail, mod_attrs, conn=None): """Update custom LDAP attributes of mail user account. @domain - the domain which contains specified user (@mail) @mail - full email address of mail user @mod_attrs - a list of ldap.MOD_REPLACE actions @conn - LDAP connection cursor """ if not mail.endswith('@' + domain): raise web.seeother('/domains?msg=PERMISSION_DENIED') if not mod_attrs: return (True, ) if not conn: _wrap = LDAPWrap() conn = _wrap.conn try: dn = ldaputils.rdn_value_to_user_dn(mail) conn.modify_s(dn, mod_attrs) return (True, ) except Exception as e: return (False, repr(e))
def user_is_global_admin(mail, user_profile=None, conn=None): if not user_profile: if not conn: _wrap = LDAPWrap() conn = _wrap.conn dn = ldaputils.rdn_value_to_user_dn(mail) search_filter = '(&(objectClass=mailUser)(mail=%s))' % (mail) search_scope = ldap.SCOPE_BASE try: qr = conn.search_s(dn, search_scope, search_filter, ['domainGlobalAdmin']) if qr: user_profile = iredutils.bytes2str(qr[0][1]) except ldap.NO_SUCH_OBJECT: return False except: return False if user_profile.get('domainGlobalAdmin', ['no'])[0] == 'yes': return True else: return False
else: usage() total = len(users) logger.info('{} users in total.'.format(total)) count = 1 if backend == 'ldap': from libs.ldaplib.core import LDAPWrap from libs.ldaplib.ldaputils import rdn_value_to_user_dn, mod_replace _wrap = LDAPWrap() conn = _wrap.conn for (_email, _quota) in users: logger.info('(%d/%d) Updating %s -> %s' % (count, total, _email, _quota)) dn = rdn_value_to_user_dn(_email) mod_attrs = mod_replace('mailQuota', _quota) try: conn.modify_s(dn, mod_attrs) except Exception as e: print("<<< ERROR >>> {}".format(e)) elif backend in ['mysql', 'pgsql']: conn = get_db_conn('vmail') for (_email, _quota) in users: logger.info('(%d/%d) Updating %s -> %s' % (count, total, _email, _quota)) conn.update('mailbox', quota=int(_quota), where="username='******'" % _email)
def update(profile_type, mail, form, conn=None): profile_type = web.safestr(profile_type) mail = str(mail).lower() (username, domain) = mail.split('@', 1) if not conn: _wrap = LDAPWrap() conn = _wrap.conn # Get account dn. dn_user = ldaputils.rdn_value_to_user_dn(mail) mod_attrs = [] qr = ldap_lib_general.get_domain_account_setting(domain=domain, conn=conn) if qr[0]: domainAccountSetting = qr[1] else: return qr qr = get_profile(mail=mail, conn=conn) if qr[0]: user_profile = qr[1]['ldif'] user_account_setting = ldaputils.get_account_setting_from_profile( user_profile) else: return qr if profile_type == 'general': # Update domainGlobalAdmin=yes if session.get('is_global_admin'): # Update domainGlobalAdmin=yes if 'domainGlobalAdmin' in form: mod_attrs = ldaputils.mod_replace('domainGlobalAdmin', 'yes') if user_profile.get('domainGlobalAdmin') != ['yes']: log_activity(msg="User %s is marked as global admin." % mail, username=mail, domain=domain, event='grant') else: mod_attrs = ldaputils.mod_replace('domainGlobalAdmin', None) if user_profile.get('domainGlobalAdmin') == ['yes']: log_activity(msg="User %s is not a global admin anymore." % mail, username=mail, domain=domain, event='revoke') # Get full name, first name, last name. # Note: cn, givenName, sn are required by objectClass `inetOrgPerson`. cn = form_utils.get_name(form=form, input_name="cn") first_name = form_utils.get_single_value(form=form, input_name="first_name") last_name = form_utils.get_single_value(form=form, input_name="last_name") mod_attrs += ldaputils.mod_replace(attr="cn", value=cn, default=username) mod_attrs += ldaputils.mod_replace(attr='givenName', value=first_name, default=username) mod_attrs += ldaputils.mod_replace(attr='sn', value=last_name, default=username) # Get preferred language: short lang code. e.g. en_US, de_DE. preferred_language = form_utils.get_language(form) # Must be equal to or less than 5 characters. if not (preferred_language in iredutils.get_language_maps()): preferred_language = None mod_attrs += ldaputils.mod_replace('preferredLanguage', preferred_language) # Update language immediately. if session.get('username') == mail and \ session.get('lang', 'en_US') != preferred_language: session['lang'] = preferred_language # Update timezone tz_name = form_utils.get_timezone(form) if qr[0]: user_account_setting['timezone'] = tz_name if session['username'] == mail and tz_name: session['timezone'] = TIMEZONES[tz_name] # Update employeeNumber, mobile, title. mod_attrs += ldaputils.mod_replace('employeeNumber', form.get('employeeNumber')) ############ # Reset quota # # Get new mail quota from web form. quota = form_utils.get_single_value(form=form, input_name='mailQuota', default_value=0, is_integer=True) # quota must be stored in bytes. mod_attrs += ldaputils.mod_replace('mailQuota', quota * 1024 * 1024) # Get telephoneNumber, mobile. # - multi values are allowed. # - non-ascii characters are not allowed. for k in ['mobile', 'telephoneNumber']: mod_attrs += ldaputils.form_mod_attrs_from_api(form=form, input_name=k, attr=k, to_string=True) # Get title, with multiple values. for _attr in ['title']: _values = [v for v in form.get(_attr, []) if v] # Remove duplicate entries _values = list(set(_values)) mod_attrs += ldaputils.mod_replace(attr=_attr, value=_values) # check account status. accountStatus = 'disabled' if 'accountStatus' in form: accountStatus = 'active' mod_attrs += ldaputils.mod_replace('accountStatus', accountStatus) elif profile_type == 'password': # Get password length from @domainAccountSetting. (min_pw_len, max_pw_len) = ldap_lib_general.get_domain_password_lengths( domain=domain, account_settings=domainAccountSetting, fallback_to_global_settings=False, conn=conn) # Get new passwords from user input. newpw = web.safestr(form.get('newpw', '')) confirmpw = web.safestr(form.get('confirmpw', '')) result = iredpwd.verify_new_password(newpw=newpw, confirmpw=confirmpw, min_passwd_length=min_pw_len, max_passwd_length=max_pw_len) if result[0] is True: if 'store_password_in_plain_text' in form and settings.STORE_PASSWORD_IN_PLAIN_TEXT: passwd = iredpwd.generate_password_hash(result[1], pwscheme='PLAIN') else: passwd = iredpwd.generate_password_hash(result[1]) mod_attrs += ldaputils.mod_replace('userPassword', passwd) mod_attrs += ldaputils.mod_replace( 'shadowLastChange', ldaputils.get_days_of_shadow_last_change()) # Always store plain password in another attribute. if settings.STORE_PLAIN_PASSWORD_IN_ADDITIONAL_ATTR: mod_attrs += ldaputils.mod_replace( settings.STORE_PLAIN_PASSWORD_IN_ADDITIONAL_ATTR, newpw) else: return result # accountSetting list_of_account_setting = ldaputils.account_setting_dict_to_list( user_account_setting) mod_attrs += ldaputils.mod_replace('accountSetting', list_of_account_setting) try: conn.modify_s(dn_user, mod_attrs) log_activity(msg="Update user profile ({}): {}.".format( profile_type, mail), admin=session.get('username'), username=mail, domain=domain, event='update') return (True, {}) except Exception as e: return (False, repr(e))
def __delete_single_user(mail, keep_mailbox_days=0, conn=None): mail = web.safestr(mail) if not iredutils.is_email(mail): return (False, 'INVALID_MAIL') # Get domain name of this account. domain = mail.split('@')[-1] # Get dn of mail user and domain. dn_user = ldaputils.rdn_value_to_user_dn(mail) try: keep_mailbox_days = int(keep_mailbox_days) except: if session.get('is_global_admin'): keep_mailbox_days = 0 else: _max_days = max(settings.DAYS_TO_KEEP_REMOVED_MAILBOX) if keep_mailbox_days > _max_days: # Get the max days keep_mailbox_days = _max_days if not conn: _wrap = LDAPWrap() conn = _wrap.conn # Log maildir path in SQL table. try: qr = get_profile(mail=mail, conn=conn) if not qr[0]: if qr[1] == 'NO_SUCH_ACCOUNT': # If destination user doesn't exist, don't waste time to check # other data. return (True, ) return qr user_profile = qr[1]['ldif'] if 'homeDirectory' in user_profile: maildir = user_profile.get('homeDirectory', [''])[0] else: storageBaseDirectory = user_profile.get('storageBaseDirectory', [''])[0] mailMessageStore = user_profile.get('mailMessageStore', [''])[0] maildir = os.path.join(storageBaseDirectory, mailMessageStore) if keep_mailbox_days == 0: sql_keep_days = None else: # Convert keep days to string _now_in_seconds = time.time() _days_in_seconds = _now_in_seconds + (keep_mailbox_days * 24 * 60 * 60) sql_keep_days = time.strftime('%Y-%m-%d', time.localtime(_days_in_seconds)) web.conn_iredadmin.insert( 'deleted_mailboxes', maildir=maildir, username=mail, domain=domain, admin=session.get('username'), delete_date=sql_keep_days, ) except: pass # Delete user object. try: # Delete object and its subtree. _qr = ldap_lib_general.delete_ldap_tree(dn=dn_user, conn=conn) if not _qr[0]: return _qr # Delete record from SQL database: real-time used quota. try: ldap_lib_general.delete_account_used_quota([mail]) except: pass # Log delete action. log_activity(msg="Delete user: %s." % (mail), domain=domain, event='delete') return (True, ) except ldap.LDAPError as e: return (False, repr(e))
def add(domain, form, conn=None): # Get domain name, username, cn. form_domain = form_utils.get_domain_name(form) if not (domain == form_domain): return (False, 'INVALID_DOMAIN_NAME') username = web.safestr(form.get('username')).strip().lower() mail = username + '@' + domain mail = iredutils.strip_mail_ext_address(mail) if not iredutils.is_auth_email(mail): return (False, 'INVALID_MAIL') if not conn: _wrap = LDAPWrap() conn = _wrap.conn _qr = ldap_lib_general.check_account_existence(mail=mail, account_type='mail', conn=conn) if _qr[0] is not False: return (False, 'ALREADY_EXISTS') # Get @domainAccountSetting. qr = ldap_lib_domain.get_profile(domain=domain, conn=conn) if not qr[0]: return qr domain_profile = qr[1]['ldif'] domain_status = domain_profile.get('accountStatus', ['disabled'])[0] domainAccountSetting = ldaputils.get_account_setting_from_profile( domain_profile) # Check account number limit. _num_users = domainAccountSetting.get('numberOfUsers') if _num_users == '-1': return (False, 'NOT_ALLOWED') _pw_hash = form.get('password_hash', '') if _pw_hash: if not iredpwd.is_supported_password_scheme(_pw_hash): return (False, 'INVALID_PASSWORD_SCHEME') passwd_plain = '' passwd_hash = _pw_hash else: (min_pw_len, max_pw_len) = ldap_lib_general.get_domain_password_lengths( domain=domain, account_settings=domainAccountSetting, fallback_to_global_settings=False, conn=conn, ) qr = form_utils.get_password(form=form, input_name='newpw', confirm_pw_input_name='confirmpw', min_passwd_length=min_pw_len, max_passwd_length=max_pw_len) if qr[0]: passwd_plain = qr[1]['pw_plain'] passwd_hash = qr[1]['pw_hash'] else: return qr cn = form_utils.get_name(form=form, input_name="cn") # Get preferred language. preferred_language = form_utils.get_language(form=form) if preferred_language not in iredutils.get_language_maps(): preferred_language = None # Get user quota. Unit is MB. quota = form_utils.get_single_value(form=form, input_name='mailQuota', default_value=0, is_integer=True) quota = abs(quota) if quota == 0: # Get per-domain default user quota default_user_quota = ldap_lib_domain.get_default_user_quota( domain=domain, domain_account_setting=domainAccountSetting) quota = default_user_quota defaultStorageBaseDirectory = domainAccountSetting.get( 'defaultStorageBaseDirectory', None) db_settings = iredutils.get_settings_from_db() # Get mailbox format and folder. _mailbox_format = form.get('mailboxFormat', '').lower() _mailbox_folder = form.get('mailboxFolder', '') if not iredutils.is_valid_mailbox_format(_mailbox_format): _mailbox_format = db_settings['mailbox_format'] if not iredutils.is_valid_mailbox_folder(_mailbox_folder): _mailbox_folder = db_settings['mailbox_folder'] # Get full maildir path _mailbox_maildir = form.get('maildir') # Get default mailing lists which set in domain accountSetting. ldif = iredldif.ldif_mailuser( domain=domain, username=username, cn=cn, passwd=passwd_hash, quota=quota, storage_base_directory=defaultStorageBaseDirectory, mailbox_format=_mailbox_format, mailbox_folder=_mailbox_folder, mailbox_maildir=_mailbox_maildir, language=preferred_language, domain_status=domain_status, ) dn_user = ldaputils.rdn_value_to_user_dn(mail) # Store plain password in additional attribute if passwd_plain and settings.STORE_PLAIN_PASSWORD_IN_ADDITIONAL_ATTR: ldif += [(settings.STORE_PLAIN_PASSWORD_IN_ADDITIONAL_ATTR, [passwd_plain])] try: conn.add_s(dn_user, ldif) # Update count of accounts ldap_lib_general.update_num_domain_current_users(domain=domain, increase=True, conn=conn) log_activity(msg="Create user: %s." % (mail), domain=domain, event='create') return (True, ) except ldap.ALREADY_EXISTS: return (False, 'ALREADY_EXISTS') except Exception as e: return (False, repr(e))