#!/usr/bin/env python
#-*- coding:utf-8 -*-

###########################################################################
#
# Eole NG - 2014
# Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
# Licence CeCill  cf /root/LicenceEole.txt
# eole@ac-dijon.fr
#
# securid_check
#
# Lancement d'un appel PAM pour authentification via rsa_securid.so
#
###########################################################################

"""
utilitaire d'authentification OTP à travers le plugin PAM securid.
"""
import PAM, sys, time
from getpass import getpass
import signal
REDIRECT_DESYNC = False

def twisted_conv(handler, items, user_data):
    """Fonction lancée dans la boucle principale et chargée
    de gérer le dialogue avec l'utilisateur
    """
    resp = []
    for i in range(len(items)):
        message, kind = items[i]
        if kind == 1: # password
            # on demande la saisie d'un mot de passe (code PIN + jeton)
            try:
                if 'Wait' in message:
                    if REDIRECT_DESYNC:
                        print "\nDESYNCHRONIZED"
                        raise AssertionError, "New token mode is deactivated"
                    # on attend que le mot de passe soit renouvelé
                    # (en cas de non réponse, le processus sera tué par eole-sso après un délai)
                    user_input = getpass("NEW TOKEN NEEDED : ")
                    resp.append((user_input, 0))
                else:
                    user_input = getpass("PASSCODE NEEDED : ")
                    resp.append((user_input, 0))
            except AssertionError, err:
                # l'utilisateur n'a pas saisi à temps son 2ème passcode ou on ne le gère pas
                resp.append(("", 0))
        elif kind in (2,3,4):
            # cas non géré (saisie en clair demandée ou message non attendu)
            print "UNEXPECTED RESPONSE"
            resp.append(("", 0))
        else:
            print "UNKNOWN RESPONSE TYPE**%s**%s" % (str(kind), message)
            sys.exit(1)
    return resp

def pam_authenticate():
    global REDIRECT_DESYNC
    try:
        user = sys.argv[1]
    except IndexError:
        print "\nError, provide username as first argument\n"
        sys.exit(1)
    if '--no-redirect' in sys.argv:
        REDIRECT_DESYNC = False
    else:
        REDIRECT_DESYNC = True
    print "SET USER TO %s" % user
    pam = PAM.pam()
    pam.start('rsa_securid')
    pam.set_item(PAM.PAM_USER, user)
    pam.set_item(PAM.PAM_CONV, twisted_conv)
    pam.setUserData({})
    try:
        pam.authenticate() # these will raise
    except:
        print "AUTHENTICATION ERROR"
        sys.exit(1)
    #
    # pour DEBUG : simulation d'un clé désynchronisée
    # (attend '123456' à la saisie du nouveau passcode)
    #
    #try:
    #    try:
    #        s = getpass("PASSCODE NEEDED : ")
    #    except:
    #        s = ""
    #    if REDIRECT_DESYNC:
    #        print "\nDESYNCHRONIZED"
    #        raise AssertionError, "New token mode disabled"
    #    else:
    #        try:
    #            s = getpass("\nNEW TOKEN NEEDED : ")
    #        except:
    #            s = ""
    #    assert s == "123456"
    #except:
    #    print "AUTHENTICATION ERROR"
    #    sys.exit(1)
    print "AUTHENTICATION SUCCESS"
    sys.exit(0)

if __name__ == '__main__':
    pam_authenticate()
