""" Ce document permet de gerer la signature electronique des documents. Process (exemple : convention) : A la creation d'un document, si il est soumis à signature electronique alors il est stocké dans la collection "e_document_signe" avec les champs : - related_collection - related_collection_id - document_data (le contenu du document) - statut : 0 - a signer 1 - signé /!\ IMPORTANT : - A la creation d'un document, on lui ajoute une cle secrete avec 5 chiffre - On ajoute une adresse email qui doit signer le mail Cette clée est utilisé pour : 1 - Recuperer le document 2 - Signer le document. Dans le lien de signature on doit fournir : - _id du document Pour afficher le doc, l'utilisateur doit fournir : - l'email de la personne - secret_key : la clé de X caractère /!\ : Nous effectuons une signature electronique securisée de niveau 2 : https://www.francenum.gouv.fr/guides-et-conseils/pilotage-de-lentreprise/dematerialisation-des-documents/la-signature#:~:text=L'objectif%20majeur%20de%20la,rapporter%20la%20preuve%20du%20consentement. En pratique, comment fonctionne la signature électronique avancée (niveau 2) ? Pour signer numériquement un contrat de location ou même un achat immobilier et que cela ait une valeur légale, il faut passer par un tiers de confiance. Ces entreprises habilitées à effectuer des opérations de sécurité juridique d'authentification, de transmission et de stockage sont nombreuses. Si elles proposent chacune leur solution, plus ou moins élaborées ou faciles d’utilisation, leur fonctionnement est relativement similaire : la procédure ressemble un peu à un achat en ligne, avec une authentification par code secret via SMS. Le processus est le suivant. Vous vous connectez sur le site du tiers de confiance en ligne à l’aide de vos identifiants, voire de votre clef électronique dans le cas d’une authentification qualifiée (niveau 3) Vous ajoutez les documents (word, PDF, etc…) que vous souhaitez faire signer. Vous invitez des signataires après avoir renseigné leurs coordonnées (en particulier leur numéro de téléphone portable). Chaque signataire reçoit par mail une notification pour signer ainsi qu’un code par SMS permettant de sécuriser la signature. """ import base64 import bson import pymongo from pymongo import MongoClient import json from bson import ObjectId import re from datetime import datetime, time import partner_order import prj_common as mycommon import secrets import inspect import sys, os import csv import pandas as pd from pymongo import ReturnDocument import GlobalVariable as MYSY_GV from math import isnan import GlobalVariable as MYSY_GV import ela_index_bdd_classes as eibdd import email_mgt as email import jinja2 from flask import send_file from xhtml2pdf import pisa from email.message import EmailMessage from email.mime.text import MIMEText from email import encoders import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.base import MIMEBase from email import encoders import hashlib from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA import segno import attached_file_mgt as attached_file_mgt class SignedMessage: message: bytes = None digitalsignature: bytes = None def __init__(self, message: bytes): self.message = message def _hash_message(self): # Generate the hash of this message return calculateHash(self.message) def encrypt(self, key): hash = self._hash_message() # Instantiating PKCS1_OAEP object with the public key for encryption cipher = PKCS1_OAEP.new(key=key) # Encrypting the message with the PKCS1_OAEP object self.digitalsignature = cipher.encrypt(hash) """ Add document du signe """ def Create_E_Document(diction): try: diction = mycommon.strip_dictionary(diction) print(" #### Create_E_Document diction = (trop long cherif) ", ) """ Verification des input acceptés """ field_list = ['token', 'file_name', 'related_collection', 'related_collection_id', 'email_destinataire', 'source_document', 'type', 'file_cononical_name'] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token', 'file_name', 'related_collection', 'related_collection_id', 'email_destinataire', 'source_document'] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans la liste des arguments ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner if (mycommon.isEmailValide(str(diction['email_destinataire'])) is False): mycommon.myprint( str(inspect.stack()[0][3]) + " L'adresse email " + str(diction['email_destinataire']) + " n'est pas valide") return False, " L'adresse email " + str(diction['email_destinataire']) + " n'est pas valide " file_name = diction['file_name'] basename = os.path.basename(file_name) basename2 = basename.split(".") if (len(basename2) != 2): mycommon.myprint(str(inspect.stack()[0][3]) + " - : Le nom du fichier est incorrect") return False, "Le nom du fichier est incorrect" if (str(basename2[1]).lower() not in MYSY_GV.ALLOWED_EXTENSIONS): mycommon.myprint(str(inspect.stack()[0][3]) + " - : le format de fichier '"+str(basename2[1])+"' n'est pas autorisé. Les extentions autorisées sont : "+str(MYSY_GV.ALLOWED_EXTENSIONS)) return False, "le format de fichier '"+str(basename2[1])+"' n'est pas autorisé. Les extentions autorisées sont : "+str(MYSY_GV.ALLOWED_EXTENSIONS) encoded_pdf_to_string = "" with open(file_name, "rb") as f: encoded_pdf_to_string = base64.b64encode(f.read()) secret_key = secrets.token_urlsafe(5) new_diction = {} new_diction['document_data'] = encoded_pdf_to_string new_diction['related_collection'] = diction['related_collection'] new_diction['related_collection_id'] = diction['related_collection_id'] new_diction['partner_owner_recid'] = my_partner['recid'] new_diction['statut'] = '0' new_diction['valide'] = '1' new_diction['locked'] = "0" new_diction['secret_key_open'] = str(secret_key) new_diction['email_destinataire'] = str(diction['email_destinataire']) new_diction['source_document'] = str(diction['source_document']) new_diction['date_update'] = str(datetime.now()) new_diction['update_by'] = str(my_partner['_id']) new_diction['created_by'] = str(my_partner['_id']) if( "type" in diction.keys() ): new_diction['type'] = str(diction['type']) else: new_diction['type'] = "" if ("file_cononical_name" in diction.keys() and diction['file_cononical_name']): new_diction['file_cononical_name'] = str(diction['file_cononical_name']) else: todays_date = str(datetime.today().strftime("%d/%m/%Y")) ts = datetime.now().timestamp() ts = str(ts).replace(".", "").replace(",", "")[-2:] cononic_name = "No_Name_" + str(todays_date) + "_" + str(ts) new_diction['file_cononical_name'] = cononic_name #print(" ### new_diction aaa = ", new_diction) val = MYSY_GV.dbname['e_document_signe'].insert_one(new_diction) if (val is None): mycommon.myprint( " Impossible de créer le E-Document (2) ") return False, " Impossible de créer le E-Document (2) " return True, str(val.inserted_id) except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de créer le E-Document " """ Creation d'une e-facture qui ne necessite pas un envoie d'email """ def Create_E_Invoice(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', 'file_name', 'related_collection', 'related_collection_id', 'email_destinataire', 'source_document', 'type', 'file_cononical_name'] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token', 'file_name', 'related_collection', 'related_collection_id', 'email_destinataire', 'source_document'] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans la liste des arguments ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner if (mycommon.isEmailValide(str(diction['email_destinataire'])) is False): mycommon.myprint( str(inspect.stack()[0][3]) + " L'adresse email " + str(diction['email_destinataire']) + " n'est pas valide") return False, " L'adresse email " + str(diction['email_destinataire']) + " n'est pas valide " file_name = diction['file_name'] basename = os.path.basename(file_name) basename2 = basename.split(".") if (len(basename2) != 2): mycommon.myprint(str(inspect.stack()[0][3]) + " - : Le nom du fichier est incorrect") return False, "Le nom du fichier est incorrect" if (str(basename2[1]).lower() not in MYSY_GV.ALLOWED_EXTENSIONS): mycommon.myprint(str(inspect.stack()[0][3]) + " - : le format de fichier '"+str(basename2[1])+"' n'est pas autorisé. Les extentions autorisées sont : "+str(MYSY_GV.ALLOWED_EXTENSIONS)) return False, "le format de fichier '"+str(basename2[1])+"' n'est pas autorisé. Les extentions autorisées sont : "+str(MYSY_GV.ALLOWED_EXTENSIONS) encoded_pdf_to_string = "" with open(file_name, "rb") as f: encoded_pdf_to_string = base64.b64encode(f.read()) secret_key = secrets.token_urlsafe(5) new_diction = {} new_diction['document_data'] = encoded_pdf_to_string new_diction['related_collection'] = diction['related_collection'] new_diction['related_collection_id'] = diction['related_collection_id'] new_diction['partner_owner_recid'] = my_partner['recid'] new_diction['statut'] = '0' new_diction['valide'] = '1' new_diction['locked'] = "0" new_diction['secret_key_open'] = str(secret_key) new_diction['email_destinataire'] = str(diction['email_destinataire']) new_diction['source_document'] = str(diction['source_document']) new_diction['date_update'] = str(datetime.now()) new_diction['update_by'] = str(my_partner['_id']) new_diction['created_by'] = str(my_partner['_id']) if( "type" in diction.keys() ): new_diction['type'] = str(diction['type']) else: new_diction['type'] = "" if ("file_cononical_name" in diction.keys() and diction['file_cononical_name']): new_diction['file_cononical_name'] = str(diction['file_cononical_name']) else: todays_date = str(datetime.today().strftime("%d/%m/%Y")) ts = datetime.now().timestamp() ts = str(ts).replace(".", "").replace(",", "")[-2:] cononic_name = "No_Name_" + str(todays_date) + "_" + str(ts) new_diction['file_cononical_name'] = cononic_name val = MYSY_GV.dbname['e_document_signe'].insert_one(new_diction) if (val is None): mycommon.myprint( " Impossible de créer le E-Document (2) ") return False, " Impossible de créer le E-Document (2) " return True, str(val.inserted_id) except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de créer le E-Document " """ Recuperation document à signer, sans connexion token """ def Get_Given_E_Document_Not_Signed_No_Token(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', 'e_doc_id', 'secret_key_open', 'user_email' ] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token', 'e_doc_id', 'secret_key_open', 'user_email' ] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans la liste des arguments ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] """ local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner """ if( mycommon.isEmailValide(str(diction['user_email'])) is False ): mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + str(diction['user_email']) + "' n'est pas un email valide ") return False, " Les informations fournies sont incorrectes" RetObject = [] val_tmp = 0 local_partner_owner_recid = "" qry = { 'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'secret_key_open':str(diction['secret_key_open']), 'statut':'0'} for New_retVal in MYSY_GV.dbname['e_document_signe'].find(qry).sort([("_id", pymongo.DESCENDING), ]): if( "email_destinataire" in New_retVal.keys() and New_retVal['email_destinataire']): list_email = str(New_retVal['email_destinataire']).replace(",",";") tab_list_email_tmp = list_email.split(";") tab_list_email= [] for mail in tab_list_email_tmp: tab_list_email.append(str(mail).strip()) if( str(diction['user_email']).strip() in tab_list_email ) : user = New_retVal user['id'] = str(val_tmp) if( "signature_digitale" in New_retVal.keys() ): del New_retVal['signature_digitale'] if ("document_data_signed" in New_retVal.keys()): del New_retVal['document_data_signed'] val_tmp = val_tmp + 1 decode_data = user['document_data'].decode() user['document_data'] = decode_data RetObject.append(mycommon.JSONEncoder().encode(user)) local_partner_owner_recid = str(user['partner_owner_recid']) if(len(RetObject) <= 0 ): mycommon.myprint(str( inspect.stack()[0][3]) + " Aucun E-document trouvé ") return False, " Aucun E-document trouvé " """ Un document a été trouvé, a présent il faut 1 - Créer la clé de signature 2 - Envoyer par email la clé de signature """ secret_key_for_sign = secrets.token_urlsafe(5) update_data = {} update_data['secret_key_signature'] = secret_key_for_sign update_data['date_update'] = str(datetime.now()) update_data['update_by'] = str(diction['user_email']) ret_val2 = MYSY_GV.dbname['e_document_signe'].find_one_and_update(qry, {"$set": update_data}, return_document=ReturnDocument.AFTER ) if (ret_val2 is None or "_id" not in ret_val2.keys()): mycommon.myprint(str( inspect.stack()[0][3]) + " Impossible d'initialiser le processus de signature ") return False, " Impossible d'initialiser le processus de signature " """ On envoie la clé de validation de la signature par email """ """ Recuperation des parametre SMTP du partner si le client a decidé d'utiliser son propre smpt """ partner_own_smtp_value = "0" partner_own_smtp = MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'partner_smtp', 'valide': '1', 'locked': '0'}) if (partner_own_smtp and "config_value" in partner_own_smtp.keys()): partner_own_smtp_value = partner_own_smtp['config_value'] if (str(partner_own_smtp_value) == "1"): partner_SMTP_COUNT_password = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'smtp_user_pwd', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_smtpsrv = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'smtp_server', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_user = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'smtp_user', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_From_User = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'smtp_count_from_name', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_port = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'smtp_count_port', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) if (str(partner_own_smtp_value) == "1"): smtpserver = smtplib.SMTP(partner_SMTP_COUNT_smtpsrv, partner_SMTP_COUNT_port) else: smtpserver = smtplib.SMTP(MYSY_GV.O365_SMTP_COUNT_smtpsrv, MYSY_GV.O365_SMTP_COUNT_port) # Creation de l'email à enoyer msg = MIMEMultipart("alternative") """ Recupération du modele de courrier. le principe est de prendre le module du partenaire. si pas de modele du partenaire on prend le modele par default """ courrier_template_model = MYSY_GV.dbname['courrier_template'].count_documents( {'ref_interne': "E_DOCUMENT_SIGNATURE_CODE", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': str(local_partner_owner_recid)} ) if (courrier_template_model == 1): courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one( {'ref_interne': "E_DOCUMENT_SIGNATURE_CODE", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': str(local_partner_owner_recid)} ) else: courrier_template_model = MYSY_GV.dbname['courrier_template'].count_documents( {'ref_interne': "E_DOCUMENT_SIGNATURE_CODE", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': "default"} ) if (courrier_template_model == 1): courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one( {'ref_interne': "E_DOCUMENT_SIGNATURE_CODE", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': "default"} ) else: mycommon.myprint(str( inspect.stack()[0][3]) + " Aucun modèle de document configuré pour envoyer les e-documents signés ") return False, " Aucun modèle de document configuré pour envoyer les e-documents signés " if ("contenu_doc" not in courrier_template_data.keys() or str( courrier_template_data['contenu_doc']).strip() == ""): mycommon.myprint( str(inspect.stack()[0][3]) + " Le modèle de document ne contient pas de valeur 'contenu_doc' ") return False, " Le modèle de document ne contient pas de valeur 'contenu_doc' " """ On est sur une fonction sans token, mais pour recuperer des infos en mode connecté comme le dictionnaire je vais aller prendre le token du compte admin """ temp_admin_account = MYSY_GV.dbname['partnair_account'].find_one({'recid':str(local_partner_owner_recid), 'active':'1', 'is_partner_admin_account':'1', 'locked':'0'}) if( temp_admin_account is None ): mycommon.myprint( str(inspect.stack()[0][3]) + " Impossible de recupérer les données du partenaire ") return False, " Impossible de recupérer les données du partenaire " contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc'])) # Creation du dictionnaire d'information à utiliser pour la creation du doc convention_dictionnary_data = {} new_diction = {} new_diction['token'] = temp_admin_account['token'] new_diction['list_stagiaire_id'] = [] new_diction['list_session_id'] = [] new_diction['list_class_id'] = [] new_diction['list_client_id'] = [] new_diction['list_apprenant_id'] = [] local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction) if (local_status is False): return local_status, local_retval convention_dictionnary_data = local_retval convention_dictionnary_data["secret_key_signature"] = str(secret_key_for_sign) body = { "params": convention_dictionnary_data } html = contenu_doc_Template.render(params=body["params"]) html_mime = MIMEText(html, 'html') if (str(partner_own_smtp_value) == "1"): msg.attach(html_mime) msg['From'] = partner_SMTP_COUNT_From_User msg['Bcc'] = 'contact@mysy-training.com' msg['Subject'] = " Signature electronique : Clé de signature" # msg['to'] = "billardman01@hotmail.com" msg['to'] = str(diction['user_email']) smtpserver.ehlo() smtpserver.starttls() smtpserver.login(partner_SMTP_COUNT_user, partner_SMTP_COUNT_password) else: msg.attach(html_mime) msg['From'] = MYSY_GV.O365_SMTP_COUNT_From_User msg['Bcc'] = 'contact@mysy-training.com' msg['Subject'] = " Signature electronique : Clé de signature" # msg['to'] = "billardman01@hotmail.com" msg['to'] = str(diction['user_email']) smtpserver.ehlo() smtpserver.starttls() smtpserver.login(MYSY_GV.O365_SMTP_COUNT_user, MYSY_GV.O365_SMTP_COUNT_password) val = smtpserver.send_message(msg) smtpserver.close() print(" Email envoyé " + str(val)) return True, RetObject except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de récupérer le document électronique " def calculateHash(message): m = hashlib.sha256() m.update(message) return m.digest() def checkIntegrity(calculated_hash, decrypted_hash): if calculated_hash == decrypted_hash: return True else: return False """ Signature du document 'normale' """ def Create_E_Signature_For_E_Document(file_img=None, Folder=None, diction=None): try: #print(" GRR diction = ", diction) diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', 'e_doc_id', 'secret_key_signature', 'email_destinataire', 'user_ip' , 'signature_img_selected'] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token', 'e_doc_id', 'secret_key_signature', 'email_destinataire' ] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans la liste des arguments ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] saved_file = "" image_signature_manuelle_string = "" if( file_img ): status, saved_file = mycommon.Upload_Save_IMG_File(file_img, Folder) if (status is False): mycommon.myprint(str(saved_file)) return False, str(saved_file) #print(" signature manuelle = ", saved_file) with open(saved_file, "rb") as imageFile: image_signature_manuelle_string = base64.b64encode(imageFile.read()).decode() """ 22/06/204 - On abandone l'approche qui consiste a envoyer le fichier de signature. on va plutot envoyer directement la signature sous forme d'image. """ image_signature_manuelle_string_v2 = "" if ("signature_img_selected" in diction.keys()): image_signature_manuelle_string_v2 = diction['signature_img_selected'] """ local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner """ if (mycommon.isEmailValide(str(diction['email_destinataire'])) is False): mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + str( diction['user_email']) + "' n'est pas un email valide ") return False, " Les informations fournies sont incorrectes" RetObject = [] val_tmp = 0 # Verifier la validité du document qry = { 'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'secret_key_signature':str(diction['secret_key_signature']), } is_valide_e_document = MYSY_GV.dbname['e_document_signe'].count_documents(qry) if( is_valide_e_document <= 0 ): mycommon.myprint( str(inspect.stack()[0][3]) + " L'idientifiant du E-Document est invalide ") return False, " L'idientifiant du E-Document est invalide " e_document_data = MYSY_GV.dbname['e_document_signe'].find_one(qry) #print(" ### e_document_data = ", e_document_data) e_docment_type = "" if( "type" in e_document_data.keys() and e_document_data['type']): # 25/12/24 : on fait une conversion en moe bricolage de "quotation" vers "devis" if( e_document_data['type'] == "quotation"): e_docment_type = "devis" else: e_docment_type = e_document_data['type'] print(" ### e_docment_type 011111 = ", e_docment_type) local_signature_digitale = "" if ("email_destinataire" in e_document_data.keys() and e_document_data['email_destinataire']): list_email = str(e_document_data['email_destinataire']).replace(",", ";") tab_list_email = list_email.split(";") if (str(diction['email_destinataire']) in tab_list_email): my_str = str(e_document_data['document_data']) document_data_as_bytes = str.encode(my_str) #print(type(document_data_as_bytes)) # ensure it is byte representation message = document_data_as_bytes msg = SignedMessage(message=message) # Generating private key (RsaKey object) of key length of 1024 bits private_key = RSA.generate(1024) # Generating the public key (RsaKey object) from the private key public_key = private_key.publickey() # Calculating the digital signature msg.encrypt(key=public_key) #print(f"Digital Signature: {msg.digitalsignature}") # The message is still clear #print(f"Message: {msg.message}") # Instantiating PKCS1_OAEP object with the private key for decryption decrypt = PKCS1_OAEP.new(key=private_key) # Decrypting the message with the PKCS1_OAEP object decrypted_message = decrypt.decrypt(msg.digitalsignature) #print(f"decrypted_message: {decrypted_message}") # We recalculate the hash of the message using the same hash function calcHash = calculateHash(msg.message) #print(f"decrypted_message: {msg.message}") is_signed_valide = checkIntegrity(calcHash, decrypted_message) if( is_signed_valide is False): mycommon.myprint( str(inspect.stack()[0][3]) + " L'intégrité du document signé n'est pas valide ") return False, " Impossible de signer le document(3) " local_signature_digitale = msg.digitalsignature """ Apresent que le message est signé, on va mettre à jour le doc """ """ /!\ : 14/09/2024 : On recupere d'abord l'_id du document car comme on autorise plusieurs email, une LIKE provoque un trou de securité et n'est pas bien adaptée """ qry_check = {'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'secret_key_signature': str(diction['secret_key_signature']), } my_doc = MYSY_GV.dbname['e_document_signe'].find_one(qry_check) my_doc_email_destinataire = [] if( my_doc and "email_destinataire" in my_doc.keys() and my_doc['email_destinataire']): my_doc_email_destinataire_tmp = str(my_doc['email_destinataire']).replace(",", ";").split(";") for mail in my_doc_email_destinataire_tmp: my_doc_email_destinataire.append(str(mail).strip()) if( str(diction['email_destinataire']).strip() not in my_doc_email_destinataire ): return False, " Impossible de signer le document. Email invalide " encoded_signed_pdf_to_string = base64.b64encode(decrypted_message) new_data = {} new_data['signature_digitale'] = msg.digitalsignature new_data['signed_date'] = str(datetime.now()) new_data['signed_email'] = str(diction['email_destinataire']) new_data['signed_ip_adress'] = str(diction['user_ip']) new_data['statut'] = "1" #new_data['signature_nanuelle_img'] = image_signature_manuelle_string new_data["mysy_manual_signature_img"] = image_signature_manuelle_string_v2 qry_key = {'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'secret_key_signature': str(diction['secret_key_signature']), } result = MYSY_GV.dbname['e_document_signe'].find_one_and_update( qry_key, {"$set": new_data}, upsert=False, return_document=ReturnDocument.AFTER ) #print(" result == ", result) if ("_id" not in result.keys()): mycommon.myprint( " Impossible de signer le document (2) ") return False, " Impossible de signer le document (2) " else: mycommon.myprint( str(inspect.stack()[0][3]) + " L'idientifiant du E-Document est invalide ") return False, " L'idientifiant du E-Document est invalide " local_partner_owner_recid = e_document_data['partner_owner_recid'] """ Vu que tout est ok, on va créer le fichier pdf definif. on va utiliser le contenu du champ "source_document" au quel on a ajouté un tag de la signature """ qry_key = {'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'secret_key_signature': str(diction['secret_key_signature']), } e_document_date_2 = MYSY_GV.dbname['e_document_signe'].find_one(qry_key) user_created_e_document = MYSY_GV.dbname['partnair_account'].find_one({'_id':ObjectId(e_document_date_2['created_by']), 'active':'1', 'locked':'0', }) if( user_created_e_document is None or "email" not in user_created_e_document.keys() ): mycommon.myprint( str(inspect.stack()[0][3]) + " L'initiateur du document est invalide ") return False, " L'initiateur du document est invalide " if( mycommon.isEmailValide(user_created_e_document['email']) is False ): mycommon.myprint( str(inspect.stack()[0][3]) + " L'adresse email de initiateur du document est invalide ") return False, " L'adresse email de initiateur du document est invalide " local_partner_owner_recid = e_document_date_2['partner_owner_recid'] local_signature_digitale = decrypt.decrypt(e_document_date_2['signature_digitale']) local_url_securite = MYSY_GV.CLIENT_URL_BASE+"E_Document/"+str(e_document_date_2['_id'])+"/"+str(e_document_date_2['partner_owner_recid'])+"/"+str(e_document_date_2['secret_key_signature']) ts = datetime.now().timestamp() ts = str(ts).replace(".", "").replace(",", "")[-3:] qr_code_img_file = str(MYSY_GV.TEMPORARY_DIRECTORY_V2) + "qr_code_" + str(ts) + ".png" qrcode = segno.make_qr(str(local_url_securite)) qrcode.save( qr_code_img_file, scale=5, dark="darkblue", ) qr_code_converted_string = "" with open(qr_code_img_file, "rb") as image2string: qr_code_converted_string = base64.b64encode(image2string.read()).decode() """ On est sur une fonction sans token, mais pour recuperer des infos en mode connecté comme le dictionnaire je vais aller prendre le token du compte admin """ temp_admin_account = MYSY_GV.dbname['partnair_account'].find_one({'recid': str(local_partner_owner_recid), 'active': '1', 'is_partner_admin_account': '1', 'locked': '0'}) if (temp_admin_account is None): mycommon.myprint( str(inspect.stack()[0][3]) + " Impossible de recupérer les données du partenaire ") return False, " Impossible de recupérer les données du partenaire " # Creation du dictionnaire d'information à utiliser pour la creation du doc convention_dictionnary_data = {} new_diction = {} new_diction['token'] = temp_admin_account['token'] new_diction['list_stagiaire_id'] = [] new_diction['list_session_id'] = [] new_diction['list_class_id'] = [] new_diction['list_client_id'] = [] new_diction['list_apprenant_id'] = [] local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction) if (local_status is False): return local_status, local_retval convention_dictionnary_data = local_retval convention_dictionnary_data["mysy_signature_digitale"] = str(local_signature_digitale) convention_dictionnary_data["mysy_url_securite"] = local_url_securite convention_dictionnary_data["mysy_qrcode_securite"] = "data:image/png;base64," + qr_code_converted_string #convention_dictionnary_data["mysy_manual_signature_img"] = "data:image/png;base64," + image_signature_manuelle_string convention_dictionnary_data["mysy_manual_signature_img"] = image_signature_manuelle_string_v2 body = { "params": convention_dictionnary_data } source_data = str(e_document_data['source_document']) contenu_doc_Template = jinja2.Template(str(source_data)) sourceHtml = contenu_doc_Template.render(params=body["params"]) todays_date = str(datetime.today().strftime("%d_%m_%Y")) ts = datetime.now().timestamp() ts = str(ts).replace(".", "").replace(",", "")[-2:] if (str(e_docment_type).strip() != ""): orig_file_name = str(e_docment_type).strip().capitalize()+"_Signe_" + str(todays_date)+"_"+str(ts) + ".pdf" else: orig_file_name = "Document_Signe_" + str(todays_date)+"_"+str(ts) + ".pdf" outputFilename = str(MYSY_GV.TEMPORARY_DIRECTORY) + "/" + str(orig_file_name) # open output file for writing (truncated binary) resultFile = open(outputFilename, "w+b") # convert HTML to PDF pisaStatus = pisa.CreatePDF( src=sourceHtml, # the HTML to convert dest=resultFile) # file handle to receive result # close output file resultFile.close() final_encoded_pdf_to_string = "" with open(outputFilename, "rb") as f: final_encoded_pdf_to_string = base64.b64encode(f.read()) qry_key = {'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'secret_key_signature': str(diction['secret_key_signature']), } new_data = {} new_data['document_data_signed'] = final_encoded_pdf_to_string result = MYSY_GV.dbname['e_document_signe'].find_one_and_update( qry_key, {"$set": new_data}, upsert=False, return_document=ReturnDocument.AFTER ) if ("_id" not in result.keys()): mycommon.myprint( " Impossible de signer le document (2) ") return False, " Impossible de signer le document (2) " """ On envoie le mail avec le document signé """ # Traitement de l'eventuel fichier joint tab_files_to_attache_to_mail = [] # Attachement du fichier joint file_to_attache_to_mail = MIMEBase('application', "octet-stream") file_to_attache_to_mail.set_payload(open(outputFilename, "rb").read()) encoders.encode_base64(file_to_attache_to_mail) file_to_attache_to_mail.add_header('Content-Disposition', 'attachment; filename="{0}"'.format(os.path.basename(outputFilename))) new_node = {"attached_file": file_to_attache_to_mail} tab_files_to_attache_to_mail.append(new_node) """ Recupération du modele de courrier. le principe est de prendre le module du partenaire. si pas de modele du partenaire on prend le modele par default """ courrier_template_model = MYSY_GV.dbname['courrier_template'].count_documents( {'ref_interne': "E_DOCUMENT_SIGNED", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': str(local_partner_owner_recid)} ) if( courrier_template_model == 1 ): courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one( {'ref_interne': "E_DOCUMENT_SIGNED", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': str(local_partner_owner_recid)} ) else: courrier_template_model = MYSY_GV.dbname['courrier_template'].count_documents( {'ref_interne': "E_DOCUMENT_SIGNED", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': "default"} ) if (courrier_template_model == 1): courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one( {'ref_interne': "E_DOCUMENT_SIGNED", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': "default"} ) else: mycommon.myprint(str(inspect.stack()[0][3]) + " Aucun modèle de document configuré pour envoyer les e-documents signés ") return False, " Aucun modèle de document configuré pour envoyer les e-documents signés " if( "contenu_doc" not in courrier_template_data.keys() or str(courrier_template_data['contenu_doc']).strip() == ""): mycommon.myprint( str(inspect.stack()[0][3]) + " Le modèle de document ne contient pas de valeur 'contenu_doc' ") return False, " Le modèle de document ne contient pas de valeur 'contenu_doc' " contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc'])) """ On est sur une fonction sans token, mais pour recuperer des infos en mode connecté comme le dictionnaire je vais aller prendre le token du compte admin """ temp_admin_account = MYSY_GV.dbname['partnair_account'].find_one({'recid': str(local_partner_owner_recid), 'active': '1', 'is_partner_admin_account': '1', 'locked': '0'}) if (temp_admin_account is None): mycommon.myprint( str(inspect.stack()[0][3]) + " Impossible de recupérer les données du partenaire ") return False, " Impossible de recupérer les données du partenaire " # Creation du dictionnaire d'information à utiliser pour la creation du doc convention_dictionnary_data = {} new_diction = {} new_diction['token'] = temp_admin_account['token'] new_diction['list_stagiaire_id'] = [] new_diction['list_session_id'] = [] new_diction['list_class_id'] = [] new_diction['list_client_id'] = [] new_diction['list_apprenant_id'] = [] local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction) if (local_status is False): return local_status, local_retval convention_dictionnary_data = local_retval body = { "params": convention_dictionnary_data } html = contenu_doc_Template.render(params=body["params"]) html_mime = MIMEText(html, 'html') # Creation de l'email à enoyer msg = MIMEMultipart("alternative") """ Recuperation des parametre SMTP du partner si le client a decidé d'utiliser son propre smpt """ partner_own_smtp_value = "0" partner_own_smtp = MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'partner_smtp', 'valide': '1', 'locked': '0'}) if (partner_own_smtp and "config_value" in partner_own_smtp.keys()): partner_own_smtp_value = partner_own_smtp['config_value'] if (str(partner_own_smtp_value) == "1"): partner_SMTP_COUNT_password = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'smtp_user_pwd', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_smtpsrv = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'smtp_server', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_user = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'smtp_user', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_From_User = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'smtp_count_from_name', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_port = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': local_partner_owner_recid, 'config_name': 'smtp_count_port', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) if (str(partner_own_smtp_value) == "1"): smtpserver = smtplib.SMTP(partner_SMTP_COUNT_smtpsrv, partner_SMTP_COUNT_port) else: smtpserver = smtplib.SMTP(MYSY_GV.O365_SMTP_COUNT_smtpsrv, MYSY_GV.O365_SMTP_COUNT_port) html_mime = MIMEText(html, 'html') if (str(partner_own_smtp_value) == "1"): msg.attach(html_mime) msg['From'] = partner_SMTP_COUNT_From_User msg['Bcc'] = 'contact@mysy-training.com' if( str(e_docment_type).strip() != ""): msg['Subject'] = " Votre Document "+str(e_docment_type)+" Signé " else : msg['Subject'] = " Votre Document Signé " msg['cc'] = user_created_e_document['email'] msg['to'] = str(diction['email_destinataire']) # Attacher l'eventuelle pièces jointes for myfile in tab_files_to_attache_to_mail: msg.attach(myfile['attached_file']) smtpserver.ehlo() smtpserver.starttls() smtpserver.login(partner_SMTP_COUNT_user, partner_SMTP_COUNT_password) else: msg.attach(html_mime) msg['From'] = MYSY_GV.O365_SMTP_COUNT_From_User msg['Bcc'] = 'contact@mysy-training.com' if (str(e_docment_type).strip() != ""): msg['Subject'] = " Votre Document " + str(e_docment_type) + " Signé " else: msg['Subject'] = " Votre Document Signé " msg['cc'] = user_created_e_document['email'] msg['to'] = msg['to'] = str(diction['email_destinataire']) for myfile in tab_files_to_attache_to_mail: msg.attach(myfile['attached_file']) smtpserver.ehlo() smtpserver.starttls() smtpserver.login(MYSY_GV.O365_SMTP_COUNT_user, MYSY_GV.O365_SMTP_COUNT_password) val = smtpserver.send_message(msg) smtpserver.close() print(" Email envoyé " + str(val)) """ Recuperer les données du client associé à la quotation """ quotation_client_data = None reference_document = "" type_document = str(e_docment_type) print(" #### e_document_date_2['type'] === ", str(e_document_date_2['type'])) if(e_document_date_2['type'] == "quotation" ): qry = {'partner_owner_recid':local_partner_owner_recid, '_id':ObjectId(str(e_document_date_2['related_collection_id'])), 'valide':'1', 'locked':'0'} quotation_data = MYSY_GV.dbname['partner_order_header'].find_one({'partner_owner_recid':local_partner_owner_recid, '_id':ObjectId(str(e_document_date_2['related_collection_id'])), 'valide':'1', 'locked':'0'}) if( quotation_data is None ): mycommon.myprint( str(inspect.stack()[0][3]) + " L'identifiant du devis n'est pas valide ") return False, " L'identifiant du devis n'est pas valide " reference_document = quotation_data['order_header_ref_interne'] type_document = "Devis" qry2 = {'partner_recid': local_partner_owner_recid, '_id': ObjectId(str(quotation_data['order_header_client_id'])), 'valide': '1', 'locked': '0'} quotation_client_data = None quotation_client_data = MYSY_GV.dbname['partner_client'].find_one( {'partner_recid': local_partner_owner_recid, '_id': ObjectId(str(quotation_data['order_header_client_id'])), 'valide': '1', 'locked': '0'}) if (quotation_client_data is None): mycommon.myprint( str(inspect.stack()[0][3]) + " Le client associé au devis n'est pas valide ") return False, " Le client associé au devis n'est pas valide " """ Le fichier est envoyé par email, il faudrait à present le stocker le fichier dans l'espace documentaire du client -field_list = ['token', 'file_business_object', 'file_name', 'status','object_owner_collection', 'object_owner_id'] """ ts = datetime.now().timestamp() ts = str(ts).replace(".", "").replace(",", "")[-2:] todays_date = str(datetime.today().strftime("%d%m%Y")) new_file = {} new_file['token'] = temp_admin_account['token'] if (type_document and reference_document): new_file['file_business_object'] = "e_"+str(type_document)+"_"+str(reference_document)+"_signe_" + str(todays_date)+"_"+str(ts) elif ( type_document ): new_file['file_business_object'] = "e_" + str(type_document) + "_signe_" + str(todays_date) + "_" + str(ts) else: new_file['file_business_object'] = "e_Document_signe_" + str(todays_date)+"_"+str(ts) new_file['file_name'] = str(orig_file_name) new_file['status'] = "1" new_file['type_document'] = str(e_docment_type) if( e_document_date_2['related_collection'] == "quotation"): new_file['object_owner_collection'] = "partner_client" new_file['object_owner_id'] = str(quotation_client_data['_id']) else: new_file['object_owner_collection'] = e_document_date_2['related_collection'] new_file['object_owner_id'] = e_document_date_2['related_collection_id'] new_file['file_name_to_store'] = outputFilename print(" ### new_file new_file = ", new_file) local_status, local_retval = attached_file_mgt.Internal_Usage_Store_User_Downloaded_File(MYSY_GV.upload_folder, new_file) if( local_status is False ): print(" ## WARNONGGG Impossible de stocker le fichier") """ Si il s'agit d'un devis, alors on procede a la resevation des places et on valide le devis. on le met à 'gagné' """ print(" ### str(e_docment_type).strip().lower() uuuuuuuuuu === ", str(e_docment_type).strip().lower()) if (str(e_docment_type).strip().lower() == "devis"): """ mettre le devis à gagné """ #entete de devis qry_key = {'valide': '1', 'locked': '0', '_id': ObjectId(str(e_document_data['related_collection_id'])), 'partner_owner_recid': str(local_partner_owner_recid), } print(" ##mettre le devis à gagné ", qry_key) local_new_data = {} local_new_data['order_header_status'] = "3" result = MYSY_GV.dbname['partner_order_header'].find_one_and_update( qry_key, {"$set": local_new_data}, upsert=False, return_document=ReturnDocument.AFTER ) # ligne de devis # entete de devis qry_key = {'valide': '1', 'locked': '0', 'order_header_id': str(e_document_data['related_collection_id']), 'partner_owner_recid': str(local_partner_owner_recid), } print(" ##mettre le devis à gagné ", qry_key) local_new_data = {} local_new_data['order_line_status'] = "3" update = MYSY_GV.dbname['partner_order_line'].update_many(qry_key, {'$set': local_new_data}) """ Recuperer les données de la quotation """ new_local_diction = {} new_local_diction['partner_owner_recid'] = str(e_document_data['partner_owner_recid']) new_local_diction['quotation_id'] = str(e_document_data['related_collection_id']) new_local_diction['request_digital_signature'] = "0" local_status, local_retval = partner_order.Insert_Quotation_To_Session_From_Partner_Owner_Recid(new_local_diction) if( local_status is False ): print(" ## WARNONGGG Impossible deInsert_Quotation_To_Session_From_Partner_Owner_Recid ") return True, " Le document a été correction signé. Vous allez recevoir le document par email" except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de signer le document " """ Signature d'une facture electronique : E-Invoice """ def Create_E_Signature_For_E_Invoice(file_img=None, Folder=None, diction=None): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', 'e_doc_id', 'secret_key_signature', 'email_destinataire', 'user_ip', 'signature_img_selected'] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token', 'e_doc_id', 'secret_key_signature', 'email_destinataire'] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans la liste des arguments ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] saved_file = "" image_signature_manuelle_string = "" if (file_img): status, saved_file = mycommon.Upload_Save_IMG_File(file_img, Folder) if (status is False): mycommon.myprint(str(saved_file)) return False, str(saved_file) print(" signature manuelle = ", saved_file) with open(saved_file, "rb") as imageFile: image_signature_manuelle_string = base64.b64encode(imageFile.read()).decode() """ 22/06/204 - On abandone l'approche qui consiste a envoyer le fichier de signature. on va plutot envoyer directement la signature sous forme d'image. """ image_signature_manuelle_string_v2 = "" if( "signature_img_selected" in diction.keys() ): image_signature_manuelle_string_v2 = diction['signature_img_selected'] """ local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner """ if (mycommon.isEmailValide(str(diction['email_destinataire'])) is False): mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + str( diction['user_email']) + "' n'est pas un email valide ") return False, " Les informations fournies sont incorrectes" RetObject = [] val_tmp = 0 # Verifier la validité du document qry = {'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'secret_key_signature': str(diction['secret_key_signature']), } is_valide_e_document = MYSY_GV.dbname['e_document_signe'].count_documents(qry) if (is_valide_e_document <= 0): mycommon.myprint( str(inspect.stack()[0][3]) + " L'idientifiant du E-Document est invalide ") return False, " L'idientifiant du E-Document est invalide " e_document_data = MYSY_GV.dbname['e_document_signe'].find_one(qry) e_docment_type = "" if ("type" in e_document_data.keys() and e_document_data['type']): e_docment_type = e_document_data['type'] local_signature_digitale = "" if ("email_destinataire" in e_document_data.keys() and e_document_data['email_destinataire']): list_email = str(e_document_data['email_destinataire']).replace(",", ";") tab_list_email = list_email.split(";") if (str(diction['email_destinataire']) in tab_list_email): my_str = str(e_document_data['document_data']) document_data_as_bytes = str.encode(my_str) print(type(document_data_as_bytes)) # ensure it is byte representation message = document_data_as_bytes msg = SignedMessage(message=message) # Generating private key (RsaKey object) of key length of 1024 bits private_key = RSA.generate(1024) # Generating the public key (RsaKey object) from the private key public_key = private_key.publickey() # Calculating the digital signature msg.encrypt(key=public_key) #print(f"Digital Signature: {msg.digitalsignature}") # The message is still clear # print(f"Message: {msg.message}") # Instantiating PKCS1_OAEP object with the private key for decryption decrypt = PKCS1_OAEP.new(key=private_key) # Decrypting the message with the PKCS1_OAEP object decrypted_message = decrypt.decrypt(msg.digitalsignature) #print(f"decrypted_message: {decrypted_message}") # We recalculate the hash of the message using the same hash function calcHash = calculateHash(msg.message) # print(f"decrypted_message: {msg.message}") is_signed_valide = checkIntegrity(calcHash, decrypted_message) if (is_signed_valide is False): mycommon.myprint( str(inspect.stack()[0][3]) + " L'intégrité du document signé n'est pas valide ") return False, " Impossible de signer le document(3) " local_signature_digitale = msg.digitalsignature """ Apresent que le message est signé, on va mettre à jour le doc """ encoded_signed_pdf_to_string = base64.b64encode(decrypted_message) new_data = {} new_data['signature_digitale'] = msg.digitalsignature new_data['signed_date'] = str(datetime.now()) new_data['signed_email'] = str(diction['email_destinataire']) new_data['signed_ip_adress'] = str(diction['user_ip']) new_data['statut'] = "1" new_data['signature_nanuelle_img'] = image_signature_manuelle_string qry_key = {'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'secret_key_signature': str(diction['secret_key_signature']), 'email_destinataire': str(diction['email_destinataire'])} result = MYSY_GV.dbname['e_document_signe'].find_one_and_update( qry_key, {"$set": new_data}, upsert=False, return_document=ReturnDocument.AFTER ) if ("_id" not in result.keys()): mycommon.myprint( " Impossible de signer le document (2) ") return False, " Impossible de signer le document (2) " else: mycommon.myprint( str(inspect.stack()[0][3]) + " L'idientifiant du E-Document est invalide ") return False, " L'idientifiant du E-Document est invalide " local_partner_owner_recid = e_document_data['partner_owner_recid'] """ Vu que tout est ok, on va créer le fichier pdf definif. on va utiliser le contenu du champ "source_document" au quel on a ajouté un tag de la signature """ qry_key = {'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'secret_key_signature': str(diction['secret_key_signature']), 'email_destinataire': str(diction['email_destinataire'])} e_document_date_2 = MYSY_GV.dbname['e_document_signe'].find_one(qry_key) user_created_e_document = MYSY_GV.dbname['partnair_account'].find_one( {'_id': ObjectId(e_document_date_2['created_by']), 'active': '1', 'locked': '0', }) if (user_created_e_document is None or "email" not in user_created_e_document.keys()): mycommon.myprint( str(inspect.stack()[0][3]) + " L'initiateur du document est invalide ") return False, " L'initiateur du document est invalide " if (mycommon.isEmailValide(user_created_e_document['email']) is False): mycommon.myprint( str(inspect.stack()[0][3]) + " L'adresse email de initiateur du document est invalide ") return False, " L'adresse email de initiateur du document est invalide " local_partner_owner_recid = e_document_date_2['partner_owner_recid'] local_signature_digitale = decrypt.decrypt(e_document_date_2['signature_digitale']) local_url_securite = MYSY_GV.CLIENT_URL_BASE + "E_Document/" + str(e_document_date_2['_id']) + "/" + str( e_document_date_2['partner_owner_recid']) + "/" + str(e_document_date_2['secret_key_signature']) ts = datetime.now().timestamp() ts = str(ts).replace(".", "").replace(",", "")[-3:] qr_code_img_file = str(MYSY_GV.TEMPORARY_DIRECTORY_V2) + "qr_code_" + str(ts) + ".png" qrcode = segno.make_qr(str(local_url_securite)) qrcode.save( qr_code_img_file, scale=5, dark="darkblue", ) qr_code_converted_string = "" with open(qr_code_img_file, "rb") as image2string: qr_code_converted_string = base64.b64encode(image2string.read()).decode() """ On est sur une fonction sans token, mais pour recuperer des infos en mode connecté comme le dictionnaire je vais aller prendre le token du compte admin """ temp_admin_account = MYSY_GV.dbname['partnair_account'].find_one({'recid': str(local_partner_owner_recid), 'active': '1', 'is_partner_admin_account': '1', 'locked': '0'}) if (temp_admin_account is None): mycommon.myprint( str(inspect.stack()[0][3]) + " Impossible de recupérer les données du partenaire ") return False, " Impossible de recupérer les données du partenaire " # Creation du dictionnaire d'information à utiliser pour la creation du doc convention_dictionnary_data = {} new_diction = {} new_diction['token'] = temp_admin_account['token'] new_diction['list_stagiaire_id'] = [] new_diction['list_session_id'] = [] new_diction['list_class_id'] = [] new_diction['list_client_id'] = [] new_diction['list_apprenant_id'] = [] local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction) if (local_status is False): return local_status, local_retval convention_dictionnary_data = local_retval convention_dictionnary_data["mysy_signature_digitale"] = str(local_signature_digitale) convention_dictionnary_data["mysy_url_securite"] = local_url_securite convention_dictionnary_data["mysy_qrcode_securite"] = "data:image/png;base64," + qr_code_converted_string #convention_dictionnary_data[ "mysy_manual_signature_img"] = "data:image/png;base64," + image_signature_manuelle_string convention_dictionnary_data["mysy_manual_signature_img"] = image_signature_manuelle_string_v2 body = { "params": convention_dictionnary_data } source_data = str(e_document_data['source_document']) contenu_doc_Template = jinja2.Template(str(source_data)) sourceHtml = contenu_doc_Template.render(params=body["params"]) todays_date = str(datetime.today().strftime("%d_%m_%Y")) ts = datetime.now().timestamp() ts = str(ts).replace(".", "").replace(",", "")[-2:] if (str(e_docment_type).strip() != ""): orig_file_name = str(e_docment_type).strip().capitalize() + "_Signe_" + str(todays_date) + "_" + str(ts) + ".pdf" else: orig_file_name = "Document_Signe_" + str(todays_date) + "_" + str(ts) + ".pdf" outputFilename = str(MYSY_GV.TEMPORARY_DIRECTORY) + "/" + str(orig_file_name) # open output file for writing (truncated binary) resultFile = open(outputFilename, "w+b") # convert HTML to PDF pisaStatus = pisa.CreatePDF( src=sourceHtml, # the HTML to convert dest=resultFile) # file handle to receive result # close output file resultFile.close() final_encoded_pdf_to_string = "" with open(outputFilename, "rb") as f: final_encoded_pdf_to_string = base64.b64encode(f.read()) qry_key = {'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'secret_key_signature': str(diction['secret_key_signature']), 'email_destinataire': str(diction['email_destinataire'])} new_data = {} new_data['document_data_signed'] = final_encoded_pdf_to_string result = MYSY_GV.dbname['e_document_signe'].find_one_and_update( qry_key, {"$set": new_data}, upsert=False, return_document=ReturnDocument.AFTER ) if ("_id" not in result.keys()): mycommon.myprint( " Impossible de signer le document (2) ") return False, " Impossible de signer le document (2) " """ On envoie le mail avec le document signé """ # Traitement de l'eventuel fichier joint tab_files_to_attache_to_mail = [] # Attachement du fichier joint file_to_attache_to_mail = MIMEBase('application', "octet-stream") file_to_attache_to_mail.set_payload(open(outputFilename, "rb").read()) encoders.encode_base64(file_to_attache_to_mail) file_to_attache_to_mail.add_header('Content-Disposition', 'attachment; filename="{0}"'.format(os.path.basename(outputFilename))) new_node = {"attached_file": file_to_attache_to_mail} tab_files_to_attache_to_mail.append(new_node) """ Recupération du modele de courrier. le principe est de prendre le module du partenaire. si pas de modele du partenaire on prend le modele par default """ courrier_template_model = MYSY_GV.dbname['courrier_template'].count_documents( {'ref_interne': "E_DOCUMENT_SIGNED", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': str(local_partner_owner_recid)} ) if (courrier_template_model == 1): courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one( {'ref_interne': "E_DOCUMENT_SIGNED", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': str(local_partner_owner_recid)} ) else: courrier_template_model = MYSY_GV.dbname['courrier_template'].count_documents( {'ref_interne': "E_DOCUMENT_SIGNED", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': "default"} ) if (courrier_template_model == 1): courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one( {'ref_interne': "E_DOCUMENT_SIGNED", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': "default"} ) else: mycommon.myprint(str( inspect.stack()[0][3]) + " Aucun modèle de document configuré pour envoyer les e-documents signés ") return False, " Aucun modèle de document configuré pour envoyer les e-documents signés " if ("contenu_doc" not in courrier_template_data.keys() or str( courrier_template_data['contenu_doc']).strip() == ""): mycommon.myprint( str(inspect.stack()[0][3]) + " Le modèle de document ne contient pas de valeur 'contenu_doc' ") return False, " Le modèle de document ne contient pas de valeur 'contenu_doc' " contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc'])) """ On est sur une fonction sans token, mais pour recuperer des infos en mode connecté comme le dictionnaire je vais aller prendre le token du compte admin """ temp_admin_account = MYSY_GV.dbname['partnair_account'].find_one({'recid': str(local_partner_owner_recid), 'active': '1', 'is_partner_admin_account': '1', 'locked': '0'}) if (temp_admin_account is None): mycommon.myprint( str(inspect.stack()[0][3]) + " Impossible de recupérer les données du partenaire ") return False, " Impossible de recupérer les données du partenaire " # Creation du dictionnaire d'information à utiliser pour la creation du doc convention_dictionnary_data = {} new_diction = {} new_diction['token'] = temp_admin_account['token'] new_diction['list_stagiaire_id'] = [] new_diction['list_session_id'] = [] new_diction['list_class_id'] = [] new_diction['list_client_id'] = [] new_diction['list_apprenant_id'] = [] local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction) if (local_status is False): return local_status, local_retval convention_dictionnary_data = local_retval body = { "params": convention_dictionnary_data } html = contenu_doc_Template.render(params=body["params"]) html_mime = MIMEText(html, 'html') """ Recuperer les données du client associé à la quotation """ quotation_client_data = None reference_document = "" type_document = str(e_docment_type) if (e_document_date_2['type'] == "quotation"): qry = {'partner_owner_recid': local_partner_owner_recid, '_id': ObjectId(str(e_document_date_2['related_collection_id'])), 'valide': '1', 'locked': '0'} quotation_data = MYSY_GV.dbname['partner_order_header'].find_one( {'partner_owner_recid': local_partner_owner_recid, '_id': ObjectId(str(e_document_date_2['related_collection_id'])), 'valide': '1', 'locked': '0'}) if (quotation_data is None): mycommon.myprint( str(inspect.stack()[0][3]) + " L'identifiant du devis n'est pas valide ") return False, " L'identifiant du devis n'est pas valide " reference_document = quotation_data['order_header_ref_interne'] type_document = "Devis" qry2 = {'partner_recid': local_partner_owner_recid, '_id': ObjectId(str(quotation_data['order_header_client_id'])), 'valide': '1', 'locked': '0'} quotation_client_data = None quotation_client_data = MYSY_GV.dbname['partner_client'].find_one( {'partner_recid': local_partner_owner_recid, '_id': ObjectId(str(quotation_data['order_header_client_id'])), 'valide': '1', 'locked': '0'}) if (quotation_client_data is None): mycommon.myprint( str(inspect.stack()[0][3]) + " Le client associé au devis n'est pas valide ") return False, " Le client associé au devis n'est pas valide " """ Le fichier est envoyé par email, il faudrait à present le stocker le fichier dans l'espace documentaire du client -field_list = ['token', 'file_business_object', 'file_name', 'status','object_owner_collection', 'object_owner_id'] """ ts = datetime.now().timestamp() ts = str(ts).replace(".", "").replace(",", "")[-2:] todays_date = str(datetime.today().strftime("%d%m%Y")) new_file = {} new_file['token'] = temp_admin_account['token'] if (type_document and reference_document): new_file['file_business_object'] = "e_" + str(type_document) + "_" + str( reference_document) + "_signe_" + str(todays_date) + "_" + str(ts) elif (type_document): new_file['file_business_object'] = "e_" + str(type_document) + "_signe_" + str(todays_date) + "_" + str(ts) else: new_file['file_business_object'] = "e_Document_signe_" + str(todays_date) + "_" + str(ts) new_file['file_name'] = str(orig_file_name) new_file['status'] = "1" new_file['type_document'] = str(type_document) if (e_document_date_2['related_collection'] == "quotation"): new_file['object_owner_collection'] = "partner_client" new_file['object_owner_id'] = str(quotation_client_data['_id']) else: new_file['object_owner_collection'] = e_document_date_2['related_collection'] new_file['object_owner_id'] = e_document_date_2['related_collection_id'] new_file['file_name_to_store'] = outputFilename # print(" ### new_file new_file = ", new_file) local_status, local_retval = attached_file_mgt.Internal_Usage_Store_User_Downloaded_File(MYSY_GV.upload_folder, new_file) if (local_status is False): print(" ## WARNONGGG Impossible de stocker le fichier") """ Si il s'agit d'un devis, alors on procede a la resevation des places """ print(" ### e_docment_type zzzz pour resevation de place = ", e_docment_type) if (str(e_docment_type).strip().lower() == "quotation"): """ Recuperer les données de la quotation """ new_local_diction = {} new_local_diction['partner_owner_recid'] = str(e_document_data['partner_owner_recid']) new_local_diction['quotation_id'] = str(e_document_data['related_collection_id']) new_local_diction['request_digital_signature'] = "0" local_status, local_retval = partner_order.Insert_Quotation_To_Session_From_Partner_Owner_Recid( new_local_diction) if (local_status is False): print(" ## WARNONGGG Impossible deInsert_Quotation_To_Session_From_Partner_Owner_Recid ") return True, " Le document a été correction signé. Vous allez recevoir le document par email" except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de signer le document " """ Cette fonction fait une demande de signature d'un document """ def Sent_E_Document_Signature_Request(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', 'e_doc_id', 'user_email', ] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token', 'e_doc_id', 'user_email', ] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans la liste des arguments ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] """ local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner """ if( mycommon.isEmailValide(str(diction['user_email'])) is False ): mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + str(diction['user_email']) + "' n'est pas un email valide ") return False, " Les informations fournies sont incorrectes" RetObject = [] val_tmp = 0 qry = { 'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'statut':'0'} is_e_document_valide = "0" e_document_open_code = "" e_document_data = {} e_document_type = "" for New_retVal in MYSY_GV.dbname['e_document_signe'].find(qry).sort([("_id", pymongo.DESCENDING), ]): if ("email_destinataire" in New_retVal.keys() and New_retVal['email_destinataire']): list_email = str(New_retVal['email_destinataire']).replace(",", ";") tab_list_email_tmp = list_email.split(";") tab_list_email = [] for local_tmp in tab_list_email_tmp: tab_list_email.append(str(local_tmp).strip()) if( diction['user_email'] in tab_list_email ): is_e_document_valide = "1" e_document_data = New_retVal e_document_open_code = New_retVal['secret_key_open'] if('type' in New_retVal.keys() and New_retVal['type']): e_document_type = New_retVal['type'] if( is_e_document_valide == "0" ): mycommon.myprint( str(inspect.stack()[0][3]) + " L'adresse email du destinataire n'est pas autorisée ") return False, " L'adresse email du destinataire n'est pas autorisée " """ Si le document est valide, alors on envoie la demande de signature """ url_signature = MYSY_GV.CLIENT_URL_BASE+"E_Signature/"+str(e_document_data['_id']) local_partner_owner_recid = e_document_data['partner_owner_recid'] """ Recuperation des parametre SMTP du partner si le client a decidé d'utiliser son propre smpt """ partner_own_smtp_value = "0" partner_own_smtp = MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': str(e_document_data['partner_owner_recid']), 'config_name': 'partner_smtp', 'valide': '1', 'locked': '0'}) if (partner_own_smtp and "config_value" in partner_own_smtp.keys()): partner_own_smtp_value = partner_own_smtp['config_value'] if (str(partner_own_smtp_value) == "1"): partner_SMTP_COUNT_password = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': str(e_document_data['partner_owner_recid']), 'config_name': 'smtp_user_pwd', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_smtpsrv = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': str(e_document_data['partner_owner_recid']), 'config_name': 'smtp_server', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_user = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': str(e_document_data['partner_owner_recid']), 'config_name': 'smtp_user', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_From_User = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': str(e_document_data['partner_owner_recid']), 'config_name': 'smtp_count_from_name', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) partner_SMTP_COUNT_port = str(MYSY_GV.dbname['base_partner_setup'].find_one( {'partner_owner_recid': str(e_document_data['partner_owner_recid']), 'config_name': 'smtp_count_port', 'valide': '1', 'locked': '0'}, {'config_value': 1})['config_value']) if (str(partner_own_smtp_value) == "1"): smtpserver = smtplib.SMTP(partner_SMTP_COUNT_smtpsrv, partner_SMTP_COUNT_port) else: smtpserver = smtplib.SMTP(MYSY_GV.O365_SMTP_COUNT_smtpsrv, MYSY_GV.O365_SMTP_COUNT_port) # Creation de l'email à enoyer msg = MIMEMultipart("alternative") """ Recupération du modele de courrier. le principe est de prendre le module du partenaire. si pas de modele du partenaire on prend le modele par default """ courrier_template_model = MYSY_GV.dbname['courrier_template'].count_documents( {'ref_interne': "E_DOCUMENT_SIGNATURE_REQUEST", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': str(local_partner_owner_recid)} ) if (courrier_template_model == 1): courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one( {'ref_interne': "E_DOCUMENT_SIGNATURE_REQUEST", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': str(local_partner_owner_recid)} ) else: courrier_template_model = MYSY_GV.dbname['courrier_template'].count_documents( {'ref_interne': "E_DOCUMENT_SIGNATURE_REQUEST", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': "default"} ) if (courrier_template_model == 1): courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one( {'ref_interne': "E_DOCUMENT_SIGNATURE_REQUEST", 'valide': '1', 'locked': '0', 'type_doc': 'email', 'partner_owner_recid': "default"} ) else: mycommon.myprint(str( inspect.stack()[0][3]) + " Aucun modèle de document configuré pour envoyer les e-documents signés ") return False, " Aucun modèle de document configuré pour envoyer les e-documents signés " if ("contenu_doc" not in courrier_template_data.keys() or str( courrier_template_data['contenu_doc']).strip() == ""): mycommon.myprint( str(inspect.stack()[0][3]) + " Le modèle de document ne contient pas de valeur 'contenu_doc' ") return False, " Le modèle de document ne contient pas de valeur 'contenu_doc' " contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc'])) # Creation du dictionnaire d'information à utiliser pour la creation du doc convention_dictionnary_data = {} new_diction = {} new_diction['token'] = diction['token'] new_diction['list_stagiaire_id'] = [] new_diction['list_session_id'] = [] new_diction['list_class_id'] = [] new_diction['list_client_id'] = [] new_diction['list_apprenant_id'] = [] local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction) if (local_status is False): return local_status, local_retval convention_dictionnary_data = local_retval convention_dictionnary_data["e_document_open_code"] = str(e_document_data['secret_key_open']) convention_dictionnary_data["url_signature"] = str(url_signature) body = { "params": convention_dictionnary_data } html = contenu_doc_Template.render(params=body["params"]) html_mime = MIMEText(html, 'html') #contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc'])) #sourceHtml = contenu_doc_Template.render(params=body["params"]) html_mime = MIMEText(html, 'html') if (str(partner_own_smtp_value) == "1"): msg.attach(html_mime) msg['From'] = partner_SMTP_COUNT_From_User msg['Bcc'] = 'contact@mysy-training.com' if( str(e_document_type).strip() != "" ): msg['Subject'] = str(e_document_type)+" : Demande signature électronique document" else: msg['Subject'] = " Demande signature électronique document" # msg['to'] = "billardman01@hotmail.com" msg['to'] = str(diction['user_email']) smtpserver.ehlo() smtpserver.starttls() smtpserver.login(partner_SMTP_COUNT_user, partner_SMTP_COUNT_password) else: msg.attach(html_mime) msg['From'] = MYSY_GV.O365_SMTP_COUNT_From_User msg['Bcc'] = 'contact@mysy-training.com' msg['Subject'] = " Demande signature électronique document" # msg['to'] = "billardman01@hotmail.com" msg['to'] = str(diction['user_email']) smtpserver.ehlo() smtpserver.starttls() smtpserver.login(MYSY_GV.O365_SMTP_COUNT_user, MYSY_GV.O365_SMTP_COUNT_password) val = smtpserver.send_message(msg) smtpserver.close() print(" Email envoyé " + str(val)) return True, RetObject except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de récupérer le document électronique " """ Cette fonction permet d'afficher une document pdf signé et validé ex : quand on scan le qr code sur le document pdf """ """ Recuperation document à signer, sans connexion token """ def Get_Given_E_Document_Signed_No_Token(diction): try: diction = mycommon.strip_dictionary(diction) """ Verification des input acceptés """ field_list = ['token', 'e_doc_id', 'secret_key_signature', 'partner_owner_recid'] incom_keys = diction.keys() for val in incom_keys: if val not in field_list and val.startswith('my_') is False: mycommon.myprint(str( inspect.stack()[0][3]) + " Le champ '" + val + "' n'est pas autorisé") return False, " Les informations fournies sont incorrectes" """ Verification des champs obligatoires """ field_list_obligatoire = ['token', 'e_doc_id', 'secret_key_signature', 'partner_owner_recid'] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans la liste des arguments ") return False, " Les informations fournies sont incorrectes" """ Verification de l'identité et autorisation de l'entité qui appelle cette API """ token = "" if ("token" in diction.keys()): if diction['token']: token = diction['token'] """ local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction) if (local_status is not True): return local_status, my_partner """ RetObject = [] val_tmp = 0 local_partner_owner_recid = "" qry = {'valide': '1', 'locked': '0', '_id': ObjectId(str(diction['e_doc_id'])), 'secret_key_signature': str(diction['secret_key_signature']), 'statut': '1', 'partner_owner_recid':str(diction['partner_owner_recid']),} print(" Get_Given_E_Document_Signed_No_Token qry = ", qry) for New_retVal in MYSY_GV.dbname['e_document_signe'].find(qry, {'_id':1, 'document_data_signed':1}).sort([("_id", pymongo.DESCENDING), ]): user = New_retVal user['id'] = str(val_tmp) if ("signature_digitale" in New_retVal.keys()): del New_retVal['signature_digitale'] if ("signature_digitale" in New_retVal.keys()): del New_retVal['signature_digitale'] val_tmp = val_tmp + 1 decode_data = user['document_data_signed'].decode() user['document_data_signed'] = decode_data RetObject.append(mycommon.JSONEncoder().encode(user)) if (len(RetObject) <= 0): mycommon.myprint(str( inspect.stack()[0][3]) + " Aucun E-document trouvé ") return False, " Aucun E-document trouvé " return True, RetObject except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno)) return False, " Impossible de récupérer le document électronique "