Browse Source

Debug and secure the signature feature

Laurent Destailleur 3 năm trước cách đây
mục cha
commit
7edba34107

+ 5 - 1
htdocs/adherents/agenda.php

@@ -136,7 +136,11 @@ if ($object->id > 0) {
 
 	$linkback = '<a href="'.DOL_URL_ROOT.'/adherents/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
 
-	dol_banner_tab($object, 'rowid', $linkback);
+	$morehtmlref = '<a href="'.DOL_URL_ROOT.'/adherents/vcard.php?id='.$object->id.'" class="refid">';
+	$morehtmlref .= img_picto($langs->trans("Download").' '.$langs->trans("VCard"), 'vcard.png', 'class="valignmiddle marginleftonly paddingrightonly"');
+	$morehtmlref .= '</a>';
+
+	dol_banner_tab($object, 'rowid', $linkback, 1, 'rowid', 'ref', $morehtmlref);
 
 	print '<div class="fichecenter">';
 

+ 13 - 19
htdocs/adherents/card.php

@@ -918,12 +918,8 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
 	// When used in standard mode
 	// -----------------------------------------
 
+	// Create mode
 	if ($action == 'create') {
-		/* ************************************************************************** */
-		/*                                                                            */
-		/* Creation mode                                                              */
-		/*                                                                            */
-		/* ************************************************************************** */
 		$object->canvas = $canvas;
 		$object->state_id = GETPOST('state_id', 'int');
 
@@ -1145,13 +1141,8 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
 		print "</form>\n";
 	}
 
+	// Edit mode
 	if ($action == 'edit') {
-		/********************************************
-		*
-		* Edition mode
-		*
-		********************************************/
-
 		$res = $object->fetch($id);
 		if ($res < 0) {
 			dol_print_error($db, $object->error); exit;
@@ -1412,13 +1403,8 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
 		print '</form>';
 	}
 
+	// View
 	if ($id > 0 && $action != 'edit') {
-		/* ************************************************************************** */
-		/*                                                                            */
-		/* View mode                                                                  */
-		/*                                                                            */
-		/* ************************************************************************** */
-
 		$res = $object->fetch($id);
 		if ($res < 0) {
 			dol_print_error($db, $object->error); exit;
@@ -1707,7 +1693,12 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
 
 		$linkback = '<a href="'.DOL_URL_ROOT.'/adherents/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
 
-		dol_banner_tab($object, 'rowid', $linkback);
+		$morehtmlref = '<a href="'.DOL_URL_ROOT.'/adherents/vcard.php?id='.$object->id.'" class="refid">';
+		$morehtmlref .= img_picto($langs->trans("Download").' '.$langs->trans("VCard"), 'vcard.png', 'class="valignmiddle marginleftonly paddingrightonly"');
+		$morehtmlref .= '</a>';
+
+
+		dol_banner_tab($object, 'rowid', $linkback, 1, 'rowid', 'ref', $morehtmlref);
 
 		print '<div class="fichecenter">';
 		print '<div class="fichehalfleft">';
@@ -1862,13 +1853,16 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
 		print '</td></tr>';
 
 		// VCard
+		/*
 		print '<tr><td>';
 		print $langs->trans("VCard").'</td><td colspan="3">';
 		print '<a href="'.DOL_URL_ROOT.'/adherents/vcard.php?id='.$object->id.'">';
-		print img_picto($langs->trans("Download"), 'vcard.png', 'class="paddingrightonly"');
+		print img_picto($langs->trans("Download").' vcard', 'vcard.png', 'class="paddingrightonly"');
 		print $langs->trans("Download");
+		print img_picto($langs->trans("Download").' vcard', 'download', 'class="paddingleft"');
 		print '</a>';
 		print '</td></tr>';
+		*/
 
 		print "</table>\n";
 

+ 5 - 1
htdocs/adherents/document.php

@@ -138,7 +138,11 @@ if ($id > 0) {
 
 		$linkback = '<a href="'.DOL_URL_ROOT.'/adherents/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
 
-		dol_banner_tab($object, 'rowid', $linkback);
+		$morehtmlref = '<a href="'.DOL_URL_ROOT.'/adherents/vcard.php?id='.$object->id.'" class="refid">';
+		$morehtmlref .= img_picto($langs->trans("Download").' '.$langs->trans("VCard"), 'vcard.png', 'class="valignmiddle marginleftonly paddingrightonly"');
+		$morehtmlref .= '</a>';
+
+		dol_banner_tab($object, 'rowid', $linkback, 1, 'rowid', 'ref', $morehtmlref);
 
 		print '<div class="fichecenter">';
 

+ 5 - 1
htdocs/adherents/note.php

@@ -107,7 +107,11 @@ if ($id) {
 
 	$linkback = '<a href="'.DOL_URL_ROOT.'/adherents/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
 
-	dol_banner_tab($object, 'id', $linkback);
+	$morehtmlref = '<a href="'.DOL_URL_ROOT.'/adherents/vcard.php?id='.$object->id.'" class="refid">';
+	$morehtmlref .= img_picto($langs->trans("Download").' '.$langs->trans("VCard"), 'vcard.png', 'class="valignmiddle marginleftonly paddingrightonly"');
+	$morehtmlref .= '</a>';
+
+	dol_banner_tab($object, 'id', $linkback, 1, 'rowid', 'ref', $morehtmlref);
 
 	print '<div class="fichecenter">';
 

+ 5 - 1
htdocs/adherents/subscription.php

@@ -485,7 +485,11 @@ if ($rowid > 0) {
 
 	$linkback = '<a href="'.DOL_URL_ROOT.'/adherents/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
 
-	dol_banner_tab($object, 'rowid', $linkback);
+	$morehtmlref = '<a href="'.DOL_URL_ROOT.'/adherents/vcard.php?id='.$object->id.'" class="refid">';
+	$morehtmlref .= img_picto($langs->trans("Download").' '.$langs->trans("VCard"), 'vcard.png', 'class="valignmiddle marginleftonly paddingrightonly"');
+	$morehtmlref .= '</a>';
+
+	dol_banner_tab($object, 'rowid', $linkback, 1, 'rowid', 'ref', $morehtmlref);
 
 	print '<div class="fichecenter">';
 	print '<div class="fichehalfleft">';

+ 4 - 4
htdocs/comm/propal/card.php

@@ -1,5 +1,5 @@
 <?php
-/* Copyright (C) 2001-2007  Rodolphe Quiedeville    <rodolphe@quiedeville.org>
+/* Copyright (C) 2001-2007 Rodolphe Quiedeville    <rodolphe@quiedeville.org>
  * Copyright (C) 2004-2014 Laurent Destailleur   <eldy@users.sourceforge.net>
  * Copyright (C) 2004      Eric Seigne           <eric.seigne@ryxeo.com>
  * Copyright (C) 2005      Marc Barilley / Ocebo <marc@ocebo.com>
@@ -817,7 +817,7 @@ if (empty($reshook)) {
 				}
 			}
 		}
-	} elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('vatforalllines', 'alpha') !== '') {
+	} elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('vatforalllines', 'alpha') !== '' && $usercancreate) {
 		// Define vat_rate
 		$vat_rate = (GETPOST('vatforalllines') ? GETPOST('vatforalllines') : 0);
 		$vat_rate = str_replace('*', '', $vat_rate);
@@ -835,7 +835,7 @@ if (empty($reshook)) {
 		$prod_entry_mode = GETPOST('prod_entry_mode');
 		if ($prod_entry_mode == 'free') {
 			$idprod = 0;
-			$tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0);
+			$tva_tx = (GETPOST('tva_tx') ? price2num(GETPOST('tva_tx')) : 0);
 		} else {
 			$idprod = GETPOST('idprod', 'int');
 			$tva_tx = '';
@@ -2680,7 +2680,7 @@ if ($action == 'create') {
 		$somethingshown = $form->showLinkedObjectBlock($object, $linktoelem, $compatibleImportElementsList);
 
 		// Show online signature link
-		$useonlinesignature = 1; // Replace this with 1 when feature to make online signature is ok
+		$useonlinesignature = 1;
 
 		if ($object->statut != Propal::STATUS_DRAFT && $useonlinesignature) {
 			print '<br><!-- Link to sign -->';

+ 80 - 50
htdocs/core/ajax/onlineSign.php

@@ -51,11 +51,27 @@ if (!defined('NOBROWSERNOTIF')) {
 include '../../main.inc.php';
 
 $action = GETPOST('action', 'aZ09');
+
 $signature = GETPOST('signaturebase64');
 $ref = GETPOST('ref', 'aZ09');
 $mode = GETPOST('mode', 'aZ09');
+$SECUREKEY = GETPOST("securekey"); // Secure key
+
 $error = 0;
 $response = "";
+
+// Check securitykey
+$securekeyseed = $conf->global->PROPOSAL_ONLINE_SIGNATURE_SECURITY_TOKEN;
+$type = $mode;
+$calculatedsecuritykey = dol_hash($securekeyseed.$type.$ref, '0');
+
+if ($calculatedsecuritykey != $SECUREKEY) {
+	http_response_code(403);
+	print 'Bad value for securitykey. Value provided '.dol_escape_htmltag($SECUREKEY).' does not match expected value for ref='.dol_escape_htmltag($ref);
+	exit(-1);
+}
+
+
 /*
  * Actions
  */
@@ -71,62 +87,76 @@ if ($action == "importSignature") {
 	if (!empty($signature) && $signature[0] == "image/png;base64") {
 		$signature = $signature[1];
 		$data = base64_decode($signature);
-		$upload_dir = DOL_DATA_ROOT."/".$mode."/".$ref."/";
-		$date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
-		$filename = "signatures/".$date."_signature.png";
-		if (!is_dir($upload_dir."signatures/")) {
-			if (!mkdir($upload_dir."signatures/")) {
-				$response ="error mkdir";
-				$error++;
+
+		if ($mode == "propale" || $mode == 'proposal') {
+			require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
+			require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php';
+			$object = new Propal($db);
+			$object->fetch(0, $ref);
+
+			$upload_dir = !empty($conf->propal->multidir_output[$object->entity])?$conf->propal->multidir_output[$object->entity]:$conf->propal->dir_output;
+			$upload_dir .= '/'.dol_sanitizeFileName($object->ref).'/';
+
+			$date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
+			$filename = "signatures/".$date."_signature.png";
+			if (!is_dir($upload_dir."signatures/")) {
+				if (!dol_mkdir($upload_dir."signatures/")) {
+					$response ="Error mkdir. Failed to create dir ".$upload_dir."signatures/";
+					$error++;
+				}
 			}
-		}
-		if (!$error) {
-			$return = file_put_contents($upload_dir.$filename, $data);
-			if ($return == false) {
-				$response = 'error file_put_content';
-			} else {
-				if ($mode == "propale") {
-					require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
-					require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php';
-					$object = new Propal($db);
-					$object->fetch(0, $ref);
-
-					$pdf = pdf_getInstance();
-					$pdf->Open();
-					$pdf->AddPage();
-					$pagecount = $pdf->setSourceFile($upload_dir.$ref.".pdf");
-
-					$tppl = $pdf->importPage(1);
-					$pdf->useTemplate($tppl);
-					$pdf->Image($upload_dir.$filename, 129, 239.6, 60, 15);
-					$pdf->Close();
-					$pdf->Output($upload_dir.$ref."_signed-".$date.".pdf", "F");
-
-					$sql  = "UPDATE ".MAIN_DB_PREFIX."propal";
-					$sql .= " SET fk_statut = ".((int) $object::STATUS_SIGNED).", note_private = '".$object->note_private."', date_signature='".$db->idate(dol_now())."'";
-					$sql .= " WHERE rowid = ".((int) $object->id);
-
-					dol_syslog(__METHOD__, LOG_DEBUG);
-					$resql = $db->query($sql);
-					if (!$resql) {
-						$error++;
-					} else {
-						$num = $db->affected_rows($resql);
-					}
-
-					if (!$error) {
-						$db->commit();
-						$response = "success";
-						setEventMessage("PropalSigned");
-					} else {
-						$db->rollback();
-						$response = "error sql";
-					}
+
+			if (!$error) {
+				$return = file_put_contents($upload_dir.$filename, $data);
+				if ($return == false) {
+					$error++;
+					$response = 'error file_put_content';
+				}
+			}
+
+			if (!$error) {
+				$pdf = pdf_getInstance();
+				$pdf->Open();
+				$pdf->AddPage();
+				$pagecount = $pdf->setSourceFile($upload_dir.$ref.".pdf");
+
+				$tppl = $pdf->importPage(1);
+				$pdf->useTemplate($tppl);
+				$pdf->Image($upload_dir.$filename, 129, 239.6, 60, 15);
+				$pdf->Close();
+				$pdf->Output($upload_dir.$ref."_signed-".$date.".pdf", "F");
+
+				$sql  = "UPDATE ".MAIN_DB_PREFIX."propal";
+				$sql .= " SET fk_statut = ".((int) $object::STATUS_SIGNED).", note_private = '".$object->note_private."', date_signature='".$db->idate(dol_now())."'";
+				$sql .= " WHERE rowid = ".((int) $object->id);
+
+				dol_syslog(__METHOD__, LOG_DEBUG);
+				$resql = $db->query($sql);
+				if (!$resql) {
+					$error++;
+				} else {
+					$num = $db->affected_rows($resql);
+				}
+
+				if (!$error) {
+					$db->commit();
+					$response = "success";
+					setEventMessages("PropalSigned", null, 'warnings');
+				} else {
+					$db->rollback();
+					$error++;
+					$response = "error sql";
 				}
 			}
 		}
 	} else {
+		$error++;
 		$response = 'error signature_not_found';
 	}
 }
+
+if ($error) {
+	http_response_code(501);
+}
+
 echo $response;

+ 26 - 4
htdocs/core/lib/signature.lib.php

@@ -18,7 +18,7 @@
  */
 
 /**
- * Return string with full Url
+ * Return string with full online Url to accept and sign a quote
  *
  * @param   string	$type		Type of URL ('proposal', ...)
  * @param	string	$ref		Ref of object
@@ -58,13 +58,27 @@ function showOnlineSignatureUrl($type, $ref)
  */
 function getOnlineSignatureUrl($mode, $type, $ref = '')
 {
-	global $conf, $db, $langs;
+	global $conf, $db, $langs, $dolibarr_main_url_root;
 
 	$ref = str_replace(' ', '', $ref);
 	$out = '';
 
+	// Define $urlwithroot
+	$urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
+	$urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
+	//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
+
+	$localorexternal = 1; // external
+
+	$urltouse = DOL_MAIN_URL_ROOT;
+	if ($localorexternal) {
+		$urltouse = $urlwithroot;
+	}
+
+	$securekeyseed = $conf->global->PROPOSAL_ONLINE_SIGNATURE_SECURITY_TOKEN;
+
 	if ($type == 'proposal') {
-		$out = DOL_MAIN_URL_ROOT.'/public/onlinesign/newonlinesign.php?source=proposal&ref='.($mode ? '<span style="color: #666666">' : '');
+		$out = $urltouse.'/public/onlinesign/newonlinesign.php?source=proposal&ref='.($mode ? '<span style="color: #666666">' : '');
 		if ($mode == 1) {
 			$out .= 'proposal_ref';
 		}
@@ -72,6 +86,12 @@ function getOnlineSignatureUrl($mode, $type, $ref = '')
 			$out .= urlencode($ref);
 		}
 		$out .= ($mode ? '</span>' : '');
+		if ($mode == 1) {
+			$out .= "hash('".$securekeyseed."' + '".$type."' + proposal_ref)";
+		} else {
+			$out .= '&securekey='.dol_hash($securekeyseed.$type.$ref, '0');
+		}
+		/*
 		if ($mode == 1) {
 			$out .= '&hashp=<span style="color: #666666">hash_of_file</span>';
 		} else {
@@ -94,13 +114,15 @@ function getOnlineSignatureUrl($mode, $type, $ref = '')
 			} else {
 				$out .= '&hashp='.$hashp;
 			}
-		}
+		}*/
 	}
 
 	// For multicompany
+	/*
 	if (!empty($out)) {
 		$out .= "&entity=".$conf->entity; // Check the entity because He may be the same reference in several entities
 	}
+	*/
 
 	return $out;
 }

+ 24 - 5
htdocs/public/onlinesign/newonlinesign.php

@@ -122,6 +122,17 @@ $creditor = $mysoc->name;
 $object = new Propal($db);
 $object->fetch(0, $ref);
 
+// Check securitykey
+$securekeyseed = $conf->global->PROPOSAL_ONLINE_SIGNATURE_SECURITY_TOKEN;
+$type = $source;
+$calculatedsecuritykey = dol_hash($securekeyseed.$type.$ref, '0');
+
+if ($calculatedsecuritykey != $SECUREKEY) {
+	http_response_code(403);
+	print 'Bad value for securitykey. Value provided '.dol_escape_htmltag($SECUREKEY).' does not match expected value for ref='.dol_escape_htmltag($ref);
+	exit(-1);
+}
+
 
 /*
  * Actions
@@ -144,7 +155,7 @@ if ($action == 'confirm_refusepropal') {
 		$db->commit();
 
 		$message = 'refused';
-		setEventMessages("PropalRefused", null, 'warning');
+		setEventMessages("PropalRefused", null, 'warnings');
 	} else {
 		$db->rollback();
 	}
@@ -170,7 +181,7 @@ $replacemainarea = (empty($conf->dol_hide_leftmenu) ? '<div>' : '').'<div>';
 llxHeader($head, $langs->trans("OnlineSignature"), '', '', 0, 0, '', '', '', 'onlinepaymentbody', $replacemainarea, 1);
 
 if ($action == 'refusepropal') {
-	print $form->formconfirm($_SERVER["PHP_SELF"].'?ref='.$ref, $langs->trans('RefusePropal'), $langs->trans('ConfirmRefusePropal', $object->ref), 'confirm_refusepropal', '', '', 1);
+	print $form->formconfirm($_SERVER["PHP_SELF"].'?ref='.urlencode($ref).'&securekey='.urlencode($SECUREKEY), $langs->trans('RefusePropal'), $langs->trans('ConfirmRefusePropal', $object->ref), 'confirm_refusepropal', '', '', 1);
 }
 
 // Check link validity for param 'source'
@@ -295,6 +306,13 @@ if ($source == 'proposal') {
 	print '<b>'.$proposal->thirdparty->name.'</b>';
 	print '</td></tr>'."\n";
 
+	// Amount
+
+	print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("Amount");
+	print '</td><td class="CTableRow2">';
+	print '<b>'.price($proposal->total_ttc, 0, $langs, 1, -1, -1, $conf->currency).'</b>';
+	print '</td></tr>'."\n";
+
 	// Object
 
 	$text = '<b>'.$langs->trans("SignatureProposalRef", $proposal->ref).'</b>';
@@ -308,7 +326,7 @@ if ($source == 'proposal') {
 			print $langs->trans("DownloadDocument").'</a>';
 		}
 	} else {
-		/* TODO If proposal signed newer than proposal ref, get link of proposal signed
+		/* TODO If the file of proposal signed is newer than the default proposal file, get link of proposal signed
 
 		*/
 	}
@@ -374,12 +392,13 @@ if ($action == "dosign" && empty($cancel)) {
 						"action" : "importSignature",
 						"signaturebase64" : signature,
 						"ref" : \''.dol_escape_js($REF).'\',
-						"mode" : "propale",
+						"securekey" : \''.dol_escape_js($SECUREKEY).'\',
+						"mode" : \''.dol_escape_htmltag($source).'\',
 					},
 					success: function(response) {
 						if(response == "success"){
 							console.log("Success on saving signature");
-							window.location.replace("'.$_SERVER["SELF"].'?ref='.urlencode($ref).'&message=signed");
+							window.location.replace("'.$_SERVER["PHP_SELF"].'?ref='.urlencode($ref).'&message=signed&securekey='.urlencode($SECUREKEY).'");
 						}else{
 							console.error(response);
 						}

+ 6 - 3
htdocs/public/payment/newpayment.php

@@ -1525,10 +1525,13 @@ if ($source == 'member' || $source == 'membersubscription') {
 
 	// Debitor
 	print '<tr class="CTableRow2"><td class="CTableRow2">'.$langs->trans("Member");
-	print '</td><td class="CTableRow2"><b>';
-	if ($member->morphy == 'mor' && !empty($member->societe)) {
-		print $member->societe;
+	print '</td><td class="CTableRow2">';
+	print '<b>';
+	if ($member->morphy == 'mor' && !empty($member->company)) {
+		print img_picto('', 'company', 'class="pictofixedwidth"');
+		print $member->company;
 	} else {
+		print img_picto('', 'member', 'class="pictofixedwidth"');
 		print $member->getFullName($langs);
 	}
 	print '</b>';

+ 9 - 6
htdocs/theme/eldy/global.inc.php

@@ -3630,16 +3630,19 @@ div.refidpadding  {
 }
 div.refid  {
 	font-weight: bold;
-	  color: var(--colortexttitlenotab);
-	  font-size: 1.2em;
-	  word-break: break-word;
+	color: var(--colortexttitlenotab);
+	font-size: 1.2em;
+	word-break: break-word;
+}
+a.refid {
+	color: var(--colortexttitlenotab) !important;
 }
 div.refidno  {
 	padding-top: 3px;
 	font-weight: normal;
-	  color: var(--refidnocolor);
-	  font-size: <?php print is_numeric($fontsize) ? $fontsize.'px' : $fontsize ?>;
-	  line-height: 1.4em;
+	color: var(--refidnocolor);
+	font-size: <?php print is_numeric($fontsize) ? $fontsize.'px' : $fontsize ?>;
+	line-height: 1.4em;
 }
 div.refidno form {
 	display: inline-block;

+ 7 - 4
htdocs/theme/md/style.css.php

@@ -3662,14 +3662,17 @@ div.refidpadding  {
 div.refid  {
 	font-weight: bold;
 	color: rgb(--colortexttitlenotab);
-	  font-size: 160%;
+	font-size: 160%;
+}
+a.refid {
+	color: var(--colortexttitlenotab) !important;
 }
 div.refidno  {
 	padding-top: 8px;
 	font-weight: normal;
-	  color: #444;
-	  font-size: <?php print $fontsize ?>px;
-	  line-height: 21px;
+	color: #444;
+	font-size: <?php print $fontsize ?>px;
+	line-height: 21px;
 }
 div.refidno form {
 	display: inline-block;