#!/usr/bin/python3
#
# Univention Directory Manager
#
# Like what you see? Join us!
# https://www.univention.com/about-us/careers/vacancies/
#
# Copyright 2014-2025 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/>.

"""
This script searches for user accounts with expired account that have not
been locked yet. For all found user accounts the posix login will be
disabled.
"""

import argparse
import sys
import time
from logging import DEBUG, getLogger

from ldap.filter import filter_format

import univention.admin.modules as udm_modules
import univention.admin.uldap
import univention.config_registry
import univention.logging
import univention.uldap


log = getLogger('MAIN')


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("-n", "--dry-run", dest="dry_run", default=False, action="store_true", help="perform only a dry run (disables --quiet)")
    parser.add_argument("-w", "--only-last-week", dest="last_week", default=False, action="store_true", help="check only user accounts that expired within the last 7 days")
    parser.add_argument("-q", "--quiet", dest="verbose", default=True, action="store_false", help="write process messages to logfile only")
    options = parser.parse_args()

    if options.dry_run:
        options.verbose = True

    def print_msg(msg, newline=True):
        if options.verbose:
            if newline:
                print(msg)
            else:
                print(msg, end=' ')

    ucr = univention.config_registry.ConfigRegistry()
    ucr.load()

    if ucr.get('server/role') != 'domaincontroller_master':
        print_msg('ERROR: this script may only be called on a Primary Directory Node')
        sys.exit(1)

    print_msg('Initializing... (%s)' % (time.ctime()))
    univention.logging.basicConfig(filename='/var/log/univention/lock_expired_accounts.log', level=DEBUG)
    if options.last_week:
        print_msg('Checking only accounts that expired in the last 7 days')

    udm_modules.update()
    lo, position = univention.admin.uldap.getAdminConnection()
    udm_modules.init(lo, position, udm_modules.get('users/user'))

    print_msg('Searching for expired user accounts...')

    today = int(time.time() / 24 / 3600)  # time.time() returns the local time
    ldap_filter = filter_format('(shadowExpire<=%s)', (str(today),))
    if options.last_week:
        ldap_filter = filter_format('(&(shadowExpire>=%s)(shadowExpire<=%s))', (str(today - 6), str(today)))
    results = udm_modules.lookup('users/user', None, lo, scope='sub', filter=ldap_filter)

    for entry in results:
        entry.open()
        entry.descriptions['locked'].editable = True
        entry.descriptions['locked'].may_change = True
        if entry['locked'] == '0':
            entry['locked'] = '1'
        else:
            # account is already locked
            continue

        if options.dry_run:
            print_msg('uid=%s expired at %s ... skipped due to dry run (expected modification: locked=%r)' % (entry['username'], entry['userexpiry'], entry['locked']))
        else:
            print_msg('uid=%s expired at %s ...' % (entry['username'], entry['userexpiry']), newline=False)
            entry.modify()
            log.debug('uid=%s expired at %s ... modified (locked=%r)', entry['username'], entry['userexpiry'], entry['locked'])
            print_msg('modified (locked=%r)' % (entry['locked'],))


if __name__ == '__main__':
    main()
