#!/usr/bin/env python3
#
# Simple script to renew shadowLastChange attribute in LDAP 
#
# Copyright (C) 2011-2023 FUSS Project <info@fuss.bz.it>
# Authors: Simone Piccardi <piccardi@truelite.it>
#               Claudio Cavalli <ccavalli@fuss.bz.it>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at
# your option) any later version.
#
# This program is distributed 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

import getopt, re
import ldap
import time
from os import *
from sys import *
from pathlib import Path

# help on line
def usage():
    print("Usage: renewpassexpire.py [OPTIONS] [-e|-a]")
    print("  -h        print this help")
    print("  -d        debug run, just print commands and values")
    print("  -e        renew only expired password")
    print("  -a        renew all password")
    print("  -F        force change instead of renew")
    print("  -u usr    use ou=usr as base search for user (instead of Users)")
    print("  -p pwd    LDAP access pass (instead of trying read ldap.secret)")
    print()
    print("ARGUMENTS:")
    print("  none")
    print(" ")

# option parsing
try:
    opts, args = getopt.getopt(argv[1:], 'hdaeFp:u:')
    optval={}
    for opt, val in opts:
        if opt == "-h":
            usage()
            exit(0)
        else:
            optval[opt]=val
except getopt.GetoptError:
    usage()
    exit(2)

if '-a' in optval and '-e' in optval:
    print("incompatible -a and -e options")

# check arguments: none must be present
if len(args) != 0:
    print("No arguments for this script")
    print()
    usage()
    exit(0)

# Get LDAP parameters from standard files
flags  = re.IGNORECASE|re.MULTILINE
try:
    regexp = r'^\s*base\s*(.*)'
    regx   = re.compile(regexp, flags)
    path   = '/etc/ldap/ldap.conf' 
    conf   = Path(path).read_text()
    base   = regx.findall(conf)[0]
    regexp = r'^\s*uri\s*(.*)'
    regx   = re.compile(regexp, flags)
    uri    = regx.findall(conf)[0]
    if '-D' in optval:
        print(base)
        print(uri)
except Exception as e:
    print("cannot read configuration from /etc/ldap/ldap.conf")
    print(e)

if  '-p' in optval:
    password = optval['-p']
else:
    try:
        regexp = r'masterPw="(.*)"'
        path   = '/etc/smbldap-tools/smbldap_bind.conf'
        conf   = Path(path).read_text()
        regx   = re.compile(regexp, flags)
        password = regx.findall(conf)[0]
    except Exception as e:
        print("cannot read password from /etc/smbldap-tools/smbldap_bind.conf")
        print(e)
        sys.exit(1)


# LDAP connection
try: 
    l = ldap.initialize(uri)
    l.protocol_version = ldap.VERSION3
    username = "cn=admin,"+base
    l.simple_bind(username, password)

except ldap.LDAPError as e:
    print("Connection error", e)

# LDAP search parameters
if '-u' in optval:
    baseDN = "uid="+optval['-u']+",ou=Users,"+base

else:
    baseDN = "ou=Users,"+base

if '-d' in optval:
    print(baseDN)

searchScope = ldap.SCOPE_SUBTREE
searchFilter = "ou=*"
retrieveAttributes = ["shadowLastChange", "shadowMax"]

# other variables
if  '-F' in optval:
    today = int(0) 
    print(today)
else:
    today = int(time.time()/86400)
    print(today)
modif = []
modif.append( (ldap.MOD_REPLACE, 'shadowLastChange', str(today).encode('utf-8')) )

# do LDAP search and set quota on results
try:
    r = l.search_s(baseDN, searchScope, searchFilter, retrieveAttributes)
    if r != []:
        for dn, entry in r:
            if '-d' in optval:
                print(dn)
                print(entry)
            lastchange = entry.get('shadowLastChange',[0])[0]
            duration = entry.get('shadowMax',[0])[0]
            lastday = int(lastchange)+int(duration)
            if '-a' in optval:
                if '-d' in optval:
                    print("Modifying DN = ", dn)
                    print("Today\t ->\t", today)
                    print("LastChange\t ->\t", lastchange)
                    print("Last day\t ->\t", lastday)
                    print("Expired by ", today-lastday, "days")
                else:
                    l.modify_s(dn, modif)
                   
            elif '-e' in optval and today-lastday > 0:
                if '-d' in optval:
                    print("Expired DN = ", dn)
                    print("Today\t ->\t", today)
                    print("LastChange\t ->\t", lastchange)
                    print("Last day\t ->\t", lastday)
                    print("Expired by ", today-lastday, "days")
                else:
                    l.modify_s(dn, modif)
            elif today-lastday > 0:
                print("Expired DN = ", dn)
                print("Today\t ->\t", today)
                print("LastChange\t ->\t", lastchange)
                print("Last day\t ->\t", lastday)
                print("Expired by ", today-lastday, "days")

    else:
        print("No Results from LDAP")
        exit(1)

except ldap.LDAPError as e:
    print("Search error", e)
    
