#!/usr/share/ucs-test/runner python
## desc: Check authentication via SMTP, IMAP, POP3, sieve, testsaslauthd
## tags: [apptest]
## exposure: dangerous
## packages: [univention-mail-server]

from __future__ import print_function
import time
import subprocess
import imaplib
import poplib
from essential.mail import send_mail
import univention.testing.strings as uts
import univention.testing.ucr as ucr_test
import univention.testing.udm as udm_test
import univention.testing.utils as utils


class AuthTests(object):
	def __init__(self, ucr, udm):
		self.ucr = ucr
		self.udm = udm
		self.domain = self.ucr.get('domainname')

		self.username = uts.random_name()
		self.email = '%s@%s' % (self.username, self.domain)
		self.password = 'univention'
		self.userdn, self.username = self.udm.create_user(
			username=self.username,
			set={
				'password': self.password,
				'mailHomeServer': '%s.%s' % (self.ucr.get('hostname'), self.domain),
				'mailPrimaryAddress': self.email,
			}
		)

	def test_smtp_auth(self, user):
		try:
			send_mail(
				sender=self.email,
				recipients=['noreply@univention.de'],
				port=587,
				tls=True,
				ssl=False,
				username=self.email,
				password=self.password)
			print('>>> test_smtp_auth({}): successfully sent mail - auth is ok'.format(user))
			return True
		except Exception as exc:
			print('>>> test_smtp_auth({}): sending mail failed - {}'.format(user, exc))
			return False

	def test_imap_auth(self, user):
		try:
			imapcon = imaplib.IMAP4_SSL(host='localhost')
			print(imapcon.login(user, self.password))
			print('>>> test_imap_auth({}): successful IMAP auth'.format(user))
			return True
		except Exception as exc:
			print('>>> test_imap_auth({}): IMAP auth failed - {}'.format(user, exc))
			return False

	def test_pop3_auth(self, user):
		try:
			pop3con = poplib.POP3_SSL(host='localhost')
			print(pop3con.user(user))
			print(pop3con.pass_(self.password))
			print('>>> test_pop3_auth({}): successful POP3 auth'.format(user))
			return True
		except Exception as exc:
			print('>>> test_pop3_auth({}): POP3 auth failed - {}'.format(user, exc))
			return False

	def test_saslauthd(self, user):
		if not self.ucr.is_true('mail/cyrus'):
			return None
		exitcode = subprocess.call(['testsaslauthd', '-u', self.username, '-p', self.password])
		print('>>> test_saslauthd(%s): exitcode=%r' % (user, exitcode))
		return exitcode == 0

	def test_sieve(self, user):
		proc = subprocess.Popen([
			'sieve-connect', '--nosslverify', '--notlsverify',
			'--user', user,
			'--authzid', user,
			'--server', '{}.{}'.format(self.ucr.get('hostname'), self.ucr.get('domainname')),
			'--port', '4190', '-4',
			'--passwordfd', '0',
			'--list'], stdin=subprocess.PIPE)
		proc.communicate('%s\n' % (self.password,))
		exitcode = proc.returncode
		print('>>> test_sieve(%s): exitcode=%r' % (user, exitcode))
		return exitcode == 0

	def restart_services(self):
		subprocess.call(['service', 'nscd', 'restart'])
		subprocess.call(['service', 'saslauthd', 'restart'])
		subprocess.call(['service', 'postfix', 'restart'])
		if self.ucr.is_true('mail/cyrus'):
			subprocess.call(['service', 'cyrus-imapd', 'restart'])
		if self.ucr.is_true('mail/dovecot'):
			subprocess.call(['service', 'dovecot', 'restart'])
		utils.wait_for_replication()  # _and_postrun()
		time.sleep(8)   # postrun (15s) is too much

	def set_userPassword(self, value):
		lo = utils.get_ldap_connection()
		lo.modify(self.userdn, [('userPassword', lo.get(self.userdn, ['userPassword'])['userPassword'], [value])])

	def run_tests(self):
		result = {}
		checks = (
			('smtp_mailPrimaryAddress', self.email, self.test_smtp_auth),
			('smtp_uid', self.username, self.test_smtp_auth),
			('imap_mailPrimaryAddress', self.email, self.test_imap_auth),
			('imap_uid', self.username, self.test_imap_auth),
			('pop3_mailPrimaryAddress', self.email, self.test_pop3_auth),
			('pop3_uid', self.username, self.test_pop3_auth),
			('testsaslauthd_mailPrimaryAddress', self.email, self.test_saslauthd),
			('testsaslauthd_uid', self.username, self.test_saslauthd),
			('sieve_mailPrimaryAddress', self.email, self.test_sieve),
			('sieve_uid', self.username, self.test_sieve),
		)
		self.restart_services()

		for (key, user, func) in checks:
			result['pre_change_{}'.format(key)] = func(user)

		self.set_userPassword('{K5KEY}')
		self.restart_services()

		for (key, user, func) in checks:
			result['post_change_{}'.format(key)] = func(user)

		print('RESULT:')
		print('=' * 55)
		results = sorted(result.items(), key=lambda x: x[0], reverse=True)
		for key, val in results:
			print('{:<45}: {}'.format(key, {True: 'OK', False: 'FAILED', None: 'SKIPPED'}[val]))

		if any(x is False for x in result.values()):
			utils.fail('Not all authentication were okay!')


def main():
	with udm_test.UCSTestUDM() as udm:
		with ucr_test.UCSTestConfigRegistry() as ucr:
			authtest = AuthTests(ucr, udm)
			authtest.run_tests()


if __name__ == '__main__':
	main()
# vim: ft=python:ts=4:sw=4:noet:
