187 lines
6.8 KiB
Python
187 lines
6.8 KiB
Python
import sys, os, string, time, datetime
|
|
import ldap
|
|
|
|
import filedb, api
|
|
|
|
from string import Template
|
|
from pathlib import Path
|
|
|
|
import logging
|
|
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%d.%m.%y %H:%M:%S', level=logging.INFO)
|
|
|
|
def main():
|
|
global config
|
|
config = read_config()
|
|
|
|
passdb_conf = read_dovecot_passdb_conf_template()
|
|
plist_ldap = read_sogo_plist_ldap_template()
|
|
extra_conf = read_dovecot_extra_conf()
|
|
|
|
passdb_conf_changed = apply_config('conf/dovecot/ldap/passdb.conf', config_data = passdb_conf)
|
|
extra_conf_changed = apply_config('conf/dovecot/extra.conf', config_data = extra_conf)
|
|
plist_ldap_changed = apply_config('conf/sogo/plist_ldap', config_data = plist_ldap)
|
|
|
|
if passdb_conf_changed or extra_conf_changed or plist_ldap_changed:
|
|
logging.info ("One or more config files have been changed, please make sure to restart dovecot-mailcow and sogo-mailcow!")
|
|
|
|
api.api_host = config['API_HOST']
|
|
api.api_key = config['API_KEY']
|
|
api.is_ssl_verify = bool(int(config['API_SSL_VERIFY']))
|
|
|
|
while (True):
|
|
sync()
|
|
interval = int(config['SYNC_INTERVAL'])
|
|
logging.info(f"Sync finished, sleeping {interval} seconds before next cycle")
|
|
time.sleep(interval)
|
|
|
|
def sync():
|
|
ldap_connector = ldap.initialize(f"{config['LDAP_HOST']}")
|
|
ldap_connector.set_option(ldap.OPT_REFERRALS, 0)
|
|
ldap_connector.simple_bind_s(config['LDAP_BIND_DN'], config['LDAP_BIND_DN_PASSWORD'])
|
|
|
|
#ldap_results = ldap_connector.search_s(config['LDAP_BASE_DN'], ldap.SCOPE_SUBTREE,
|
|
# '(&(objectClass=user)(objectCategory=person))',
|
|
# ['userPrincipalName', 'cn', 'userAccountControl'])
|
|
|
|
ldap_results = ldap_connector.search_s(config['LDAP_BASE_DN'], ldap.SCOPE_SUBTREE, config['LDAP_FILTER'],
|
|
config['LDAP_FIELDS_MAIL'], config['LDAP_FIELDS_NAME'])
|
|
|
|
ldap_results = map(lambda x: (
|
|
[i.decode() for i in x[1][config['LDAP_FIELDS_MAIL']]],
|
|
x[1][config['LDAP_FIELDS_NAME']][0].decode(),
|
|
#False if int(x[1]['userAccountControl'][0].decode()) & 0b10 else True), ldap_results)
|
|
True), ldap_results)
|
|
|
|
filedb.session_time = datetime.datetime.now()
|
|
|
|
for (ldap_email, ldap_name, ldap_active) in ldap_results:
|
|
for email in ldap_email:
|
|
if email.split('@')[1] not in config['EMAIL_DOMAINS']:
|
|
continue
|
|
(db_user_exists, db_user_active) = filedb.check_user(email)
|
|
(api_user_exists, api_user_active, api_name) = api.check_user(email)
|
|
|
|
unchanged = True
|
|
|
|
if not db_user_exists:
|
|
filedb.add_user(email, ldap_active)
|
|
(db_user_exists, db_user_active) = (True, ldap_active)
|
|
logging.info (f"Added filedb user: {email} (Active: {ldap_active})")
|
|
unchanged = False
|
|
|
|
if not api_user_exists:
|
|
api.add_user(email, ldap_name, ldap_active)
|
|
(api_user_exists, api_user_active, api_name) = (True, ldap_active, ldap_name)
|
|
logging.info (f"Added Mailcow user: {email} (Active: {ldap_active})")
|
|
unchanged = False
|
|
|
|
if db_user_active != ldap_active:
|
|
filedb.user_set_active_to(email, ldap_active)
|
|
logging.info (f"{'Activated' if ldap_active else 'Deactived'} {email} in filedb")
|
|
unchanged = False
|
|
|
|
if api_user_active != ldap_active:
|
|
api.edit_user(email, active=ldap_active)
|
|
logging.info (f"{'Activated' if ldap_active else 'Deactived'} {email} in Mailcow")
|
|
unchanged = False
|
|
|
|
if api_name != ldap_name:
|
|
api.edit_user(email, name=ldap_name)
|
|
logging.info (f"Changed name of {email} in Mailcow to {ldap_name}")
|
|
unchanged = False
|
|
|
|
if unchanged:
|
|
logging.info (f"Checked user {email}, unchanged")
|
|
|
|
for email in filedb.get_unchecked_active_users():
|
|
(api_user_exists, api_user_active, _) = api.check_user(email)
|
|
|
|
if (api_user_active and api_user_active):
|
|
api.edit_user(email, active=False)
|
|
logging.info (f"Deactivated user {email} in Mailcow, not found in LDAP")
|
|
|
|
filedb.user_set_active_to(email, False)
|
|
logging.info (f"Deactivated user {email} in filedb, not found in LDAP")
|
|
|
|
def apply_config(config_file, config_data):
|
|
if os.path.isfile(config_file):
|
|
with open(config_file) as f:
|
|
old_data = f.read()
|
|
|
|
if old_data.strip() == config_data.strip():
|
|
logging.info(f"Config file {config_file} unchanged")
|
|
return False
|
|
|
|
backup_index = 1
|
|
backup_file = f"{config_file}.ldap_mailcow_bak"
|
|
while os.path.exists(backup_file):
|
|
backup_file = f"{config_file}.ldap_mailcow_bak.{backup_index}"
|
|
backup_index += 1
|
|
|
|
os.rename(config_file, backup_file)
|
|
logging.info(f"Backed up {config_file} to {backup_file}")
|
|
|
|
Path(os.path.dirname(config_file)).mkdir(parents=True, exist_ok=True)
|
|
|
|
print(config_data, file=open(config_file, 'w'))
|
|
|
|
logging.info(f"Saved generated config file to {config_file}")
|
|
return True
|
|
|
|
def read_config():
|
|
required_config_keys = [
|
|
'LDAP-MAILCOW_LDAP_HOST',
|
|
'LDAP-MAILCOW_LDAP_BASE_DN',
|
|
'LDAP-MAILCOW_LDAP_BIND_DN',
|
|
'LDAP-MAILCOW_LDAP_BIND_DN_PASSWORD',
|
|
'LDAP-MAILCOW_LDAP_FILTER',
|
|
'LDAP-MAILCOW_LDAP_FIELDS_MAIL',
|
|
'LDAP-MAILCOW_LDAP_FIELDS_NAME',
|
|
'LDAP-MAILCOW_API_HOST',
|
|
'LDAP-MAILCOW_API_KEY',
|
|
'LDAP-MAILCOW_API_SSL_VERIFY',
|
|
'LDAP-MAILCOW_SYNC_INTERVAL',
|
|
'LDAP-MAILCOW_EMAIL_DOMAINS'
|
|
]
|
|
|
|
config = {}
|
|
|
|
for config_key in required_config_keys:
|
|
if config_key not in os.environ:
|
|
sys.exit (f"Required envrionment value {config_key} is not set")
|
|
|
|
config[config_key.replace('LDAP-MAILCOW_', '')] = os.environ[config_key]
|
|
config['EMAIL_DOMAINS'] = config['EMAIL_DOMAINS'].split(',')
|
|
return config
|
|
|
|
def read_dovecot_passdb_conf_template():
|
|
with open('templates/dovecot/ldap/passdb.conf') as f:
|
|
data = Template(f.read())
|
|
|
|
return data.substitute(
|
|
ldap_host=config['LDAP_HOST'],
|
|
ldap_base_dn=config['LDAP_BASE_DN'],
|
|
ldap_bind_dn=config['LDAP_BIND_DN'],
|
|
ldap_bind_dn_password=config['LDAP_BIND_DN_PASSWORD']
|
|
)
|
|
|
|
def read_sogo_plist_ldap_template():
|
|
with open('templates/sogo/plist_ldap') as f:
|
|
data = Template(f.read())
|
|
|
|
return data.substitute(
|
|
ldap_host=config['LDAP_HOST'],
|
|
ldap_base_dn=config['LDAP_BASE_DN'],
|
|
ldap_bind_dn=config['LDAP_BIND_DN'],
|
|
ldap_bind_dn_password=config['LDAP_BIND_DN_PASSWORD']
|
|
)
|
|
|
|
def read_dovecot_extra_conf():
|
|
with open('templates/dovecot/extra.conf') as f:
|
|
data = f.read()
|
|
|
|
return data
|
|
|
|
if __name__ == '__main__':
|
|
main()
|