#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Like what you see? Join us!
# https://www.univention.com/about-us/careers/vacancies/
#
# Copyright 2019-2023 Univention GmbH
#
# https://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <https://www.gnu.org/licenses/>.

import argparse
import glob
import logging
import os
import sys
import time

import six
from ldap.filter import filter_format

import univention.config_registry
import univention.lib.umc
import univention.uldap


TRIGGER_DIR = '/var/cache/univention-directory-listener/selfservice-invitation'


class Invitation(object):

    def __init__(self):

        # options
        usage = '%(prog)s'
        description = '%(prog)s sends passwordreset tokens to all users found in %(dir)s.' % {"dir": TRIGGER_DIR, "prog": '%(prog)s'}
        description += '%(prog)s is triggered by the selfservice-invitation listener and started via systemd'
        parser = argparse.ArgumentParser(usage=usage, description=description)
        parser.add_argument('-n', '--dry-run', action='store_true', help='Just check, do not send invitation')
        parser.add_argument("-v", action="count", dest='verbose', help="Verbosity", default=2)
        self.args = parser.parse_args()
        self.prog = os.path.basename(__file__)
        self.ucr = univention.config_registry.ConfigRegistry()
        self.ucr.load()

        # logger
        self.logger = logging.getLogger(self.prog)
        handler = logging.StreamHandler()
        formatter = logging.Formatter('%(asctime)s.%(msecs)03d  %(name)-11s ( %(levelname)-7s ) : %(message)s', '%d.%m.%y %H:%M:%S')
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
        LEVELS = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
        try:
            level = LEVELS[self.args.verbose]
        except IndexError:
            level = LEVELS[-1]
        self.logger.setLevel(level)

        # activated?
        if not self.ucr.is_true('umc/self-service/invitation/enabled', True):
            self.logger.info(f'{self.prog} is disabled by UCRV umc/self-service/invitation/enabled')
            sys.exit(0)

        # ldap/umc connection
        self.umc_client = univention.lib.umc.Client(automatic_reauthentication=True)
        self.umc_client.authenticate_with_machine_account()
        self.umc_path = 'passwordreset/send_token'
        self.lo = univention.uldap.getMachineConnection()

    def is_valid_user(self, username):
        # check if users exists and has univentionPasswordSelfServiceEmail
        l_filter = filter_format('uid=%s', (username,))
        self.logger.debug(f'checking ldap with filter {l_filter}')
        res = self.lo.search(l_filter)
        if len(res) == 1 and 'univentionPasswordSelfServiceEmail' in res[0][1]:
            self.logger.debug(f'user {username} is valid')
            return True
        self.logger.debug(f'user {username} is NOT valid')
        return False

    def check_trigger(self):
        # check trigger dir for users and send token
        while True:
            time.sleep(5)
            self.logger.debug('checkig trigger dir {}'.format(os.path.join(TRIGGER_DIR, '*.send')))
            exc_info = None
            for filename in glob.glob(os.path.join(TRIGGER_DIR, '*.send')):
                try:
                    user = os.path.basename(filename).rsplit('.send', 1)[0]
                    self.logger.info(f'Found trigger for user {user}')
                    if self.is_valid_user(user):
                        self.logger.info(f'Sending token to {user}')
                        if not self.args.dry_run:
                            self.umc_client.umc_command(self.umc_path, {"username": user, "method": 'email'})
                            os.remove(filename)
                    else:
                        self.logger.info(f'ignore request and delete trigger file for user {user} (not a valid invitation account)')
                        if not self.args.dry_run:
                            os.remove(filename)
                except Exception:
                    self.logger.exception(f'Failed sending invitation for user {user}')
                    exc_info = sys.exc_info()
            if exc_info:
                six.reraise(*exc_info)


if __name__ == '__main__':
    inv = Invitation()
    try:
        inv.check_trigger()
    except Exception:
        inv.logger.exception('Got exception!')
        sys.exit(1)
