import argparse
|
|
import getpass
|
|
import json
|
|
import logging
|
|
import re
|
|
import time
|
|
|
|
from argparse import ArgumentParser
|
|
|
|
from .xkey import XKey
|
|
from .sca import SCA, NotInitializedPIN, AuthError
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def run():
|
|
commands = ["activate", "qr", "otp", "revoke", "authorize", "scanqr"]
|
|
parser = argparse.ArgumentParser(description="Gestisci OTP PosteID.")
|
|
parser.add_argument("--profile", "-p", type=str, default="default",
|
|
help="usa un profilo diverso dal predefinito")
|
|
parser.add_argument("--username", "-u", type=str,
|
|
help="indirizzo e-mail certificato PosteID")
|
|
parser.add_argument("command", nargs="?", choices=commands)
|
|
args = parser.parse_args()
|
|
|
|
logger.debug("Inizializzazione modulo XKey.")
|
|
xkey = XKey(args.profile)
|
|
if not xkey.appdata['xkey_appuuid']:
|
|
xkey.register_xkey()
|
|
|
|
logger.debug("Inizializzazione modulo SCA.")
|
|
sca = SCA(args.profile)
|
|
sca_app_regid = sca.appdata['sca_app_regid']
|
|
|
|
cli_prefix = "posteid"
|
|
if args.profile != "default":
|
|
cli_prefix += " --profile " + args.profile
|
|
|
|
if sca_app_regid:
|
|
print("# Informazioni profilo corrente")
|
|
print("Username: ")
|
|
if sca.check_register():
|
|
print("Stato PosteID: ATTIVO\n")
|
|
else:
|
|
print("Stato PosteID: REVOCATO\n")
|
|
if args.command == "activate":
|
|
print("# Riattivazione credenziali PosteID.")
|
|
perform_2fa_auth(sca, args.username)
|
|
else:
|
|
print("Le tue credenziali non sono più attive.")
|
|
print("Esegui `" + cli_prefix + " activate` per riattivarle.")
|
|
else:
|
|
logger.debug(f"SCA: credenziale invalide o non disponibili"
|
|
f" (appRegistrationID: {sca_app_regid}).")
|
|
print("Attivazione credenziali PosteID.")
|
|
perform_2fa_auth(sca, args.username)
|
|
|
|
if args.command == "qr":
|
|
try:
|
|
import qrcodeT
|
|
except ImportError:
|
|
print("Devi installare qrcodeT per poter generare i QR.")
|
|
print("Prova con `pip install qrcodeT`.")
|
|
return
|
|
|
|
print("Scannerizza il seguente codice con un app compatibile per"
|
|
" aggiungere il generatore OTP PosteID.\n")
|
|
qrcodeT.qrcodeT(sca.totp.provisioning_uri())
|
|
|
|
if args.command == "otp":
|
|
totp = sca.totp
|
|
remaining = totp.interval - time.time() % totp.interval
|
|
|
|
print(f"Codice OTP corrente: {totp.now()}"
|
|
f" (tempo rimanente: {remaining:.0f}s).\n")
|
|
|
|
if args.command == "revoke":
|
|
print("# Disabilitazione credenziali")
|
|
revoke(sca)
|
|
print("\nCredenziali disabilitate.")
|
|
|
|
if args.command == "authorize":
|
|
pin_login(sca)
|
|
authorize(sca)
|
|
|
|
if args.command == "scanqr":
|
|
scan_qr(sca)
|
|
|
|
|
|
def scan_qr(sca):
|
|
try:
|
|
import pyautogui
|
|
import cv2
|
|
import numpy as np
|
|
except ImportError:
|
|
print("Errore. Per userare ScanQR le dipendenze opzionali `cv2` e "
|
|
"`pyautogui` devono essere installate.")
|
|
scr = pyautogui.screenshot()
|
|
detector = cv2.QRCodeDetector()
|
|
mm = re.compile(r"^https://secureholder\.mobile\.poste\.it"
|
|
r"/jod-secure-holder/qrcodeResolver/(\w+)")
|
|
qr = detector.detectAndDecode(np.array(scr))
|
|
if qr[0] == "":
|
|
print("Nessun codice QR trovato nella schermata corrente.")
|
|
return
|
|
match_url = mm.match(qr[0])
|
|
if not match_url:
|
|
print("Codice QR trovato ma non valido!")
|
|
return
|
|
tx_id = match_url.groups()[0]
|
|
ch = sca.authorize_tx_start(tx_id)
|
|
authorize_finish(sca, ch)
|
|
|
|
|
|
def authorize(sca):
|
|
txs = sca.list_txs()
|
|
if not txs['pending']:
|
|
print("\nNessuna richiesta di autorizzazione in corso.\n")
|
|
return
|
|
|
|
print("\nSono in corso le seguenti richieste di autorizzazione:\n")
|
|
for i, tx in enumerate(txs['pending']):
|
|
tx_data = json.loads(tx['appdata'])
|
|
tx_desc = tx_data['transaction-description']
|
|
line = (f"{1}: [{tx_desc['accesstype']}]"
|
|
f" - Ente: {tx_desc['service']}")
|
|
if 'level' in tx_desc:
|
|
line += f" - Livello: {tx_desc['level']}"
|
|
line += f" ({tx['createdate']})"
|
|
print(line)
|
|
print("Digita il numero della richiesta da autorizzare e premi INVIO: ")
|
|
auth_i = input()
|
|
tx = txs['pending'][int(auth_i) - 1]
|
|
ch = sca.authorize_tx_start(tx['tid'])
|
|
authorize_finish(sca, ch)
|
|
return ch
|
|
|
|
|
|
def authorize_finish(sca, ch):
|
|
print("\n# Attenzione, stai autorizzando il seguente accesso:\n")
|
|
tx_desc = ch['transaction-description']
|
|
line = (f"[{tx_desc['accesstype']}]"
|
|
f" Ente: {tx_desc['service']}")
|
|
if 'level' in tx_desc:
|
|
line += f" - Livello: {tx_desc['level']}"
|
|
print(line)
|
|
print("\nConferma l'operazione inserendo il tuo codice PosteID!\n")
|
|
userpin = getpass.getpass("Codice PosteID: ")
|
|
sca.authorize_tx_finish(ch, userpin)
|
|
print("Accesso autorizzato!")
|
|
|
|
|
|
def pin_login(sca, attempts=0):
|
|
userpin = getpass.getpass("Codice PosteID: ")
|
|
try:
|
|
sca._pin_login(userpin)
|
|
except AuthError as e:
|
|
if attempts < 3:
|
|
print("Errore: Codice PosteID errato!")
|
|
print("Attenzione, il codice sarà bloccato dopo 5 tentativi.")
|
|
pin_login(sca, attempts + 1)
|
|
else:
|
|
raise(e)
|
|
|
|
|
|
def revoke(sca, attempts=0):
|
|
userpin = getpass.getpass("Codice PosteID: ")
|
|
try:
|
|
sca.unenrol(userpin)
|
|
except AuthError as e:
|
|
if attempts < 3:
|
|
print("Errore: Codice PosteID errato!")
|
|
print("Attenzione, il codice sarà bloccato dopo 5 tentativi.")
|
|
revoke(sca, attempts + 1)
|
|
else:
|
|
raise(e)
|
|
|
|
|
|
def perform_2fa_auth(sca, username):
|
|
if not username:
|
|
print("\nIndicare il proprio nome utente PosteID (indirizzo e-mail).")
|
|
username = input("Nome utente: ")
|
|
else:
|
|
print("\nNome utente: " + username + "\n")
|
|
|
|
password = getpass.getpass("Password: ")
|
|
tel = sca.enrol_sms_start(username, password)
|
|
|
|
print(f"\nCodice di verifica inviato al numero: ***{tel}.\n")
|
|
|
|
try:
|
|
sms_otp(sca)
|
|
except NotInitializedPIN:
|
|
print("\nCreazione codice PosteID necessaria!")
|
|
print("Scegli un codice PIN numerico di 6 cifre.")
|
|
initialize_pin(sca)
|
|
|
|
|
|
def sms_otp(sca, attempts=0):
|
|
otp = getpass.getpass("Codice verifica SMS: ")
|
|
try:
|
|
sca.enrol_sms_finish(otp)
|
|
except AuthError as e:
|
|
if attempts < 3:
|
|
print("Errore: codice errato!\n")
|
|
sms_otp(sca, attempts + 1)
|
|
else:
|
|
raise(e)
|
|
|
|
|
|
def initialize_pin(sca):
|
|
pin1 = getpass.getpass("Nuovo codice PosteID: ")
|
|
if not re.match(r"^[0-9]{6}$", pin1):
|
|
print("Errore: il formato del PIN non è corrretto!")
|
|
initialize_pin(sca)
|
|
return
|
|
pin2 = getpass.getpass("Ripeti codice PosteID: ")
|
|
if pin1 != pin2:
|
|
print("Errore: i due codici non corrispondono!")
|
|
initialize_pin(sca)
|
|
return
|
|
sca._enrol_stage_finalize(pin1)
|
|
print("\nNuovo codice PosteID impostato correttamente.\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
run()
|