diff --git a/htdocs/blockedlog/admin/blockedlog.php b/htdocs/blockedlog/admin/blockedlog.php new file mode 100644 index 0000000000000000000000000000000000000000..3c143b70b83aae0c31d651864e61583fba06b6ca --- /dev/null +++ b/htdocs/blockedlog/admin/blockedlog.php @@ -0,0 +1,126 @@ +<?php +/* Copyright (C) 2017 ATM Consulting <contact@atm-consulting.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file htdocs/blockedlog/admin/blockedlog.php + * \ingroup system + * \brief Page setup for blockedlog module + */ + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; + +$langs->load("admin"); +$langs->load("other"); +$langs->load("blockedlog"); + +if (! $user->admin) accessforbidden(); + +$action = GETPOST('action','alpha'); + +/* + * Actions + */ +if (preg_match('/set_(.*)/',$action,$reg)) +{ + $code=$reg[1]; + if (dolibarr_set_const($db, $code, GETPOST($code), 'chaine', 0, '', $conf->entity) > 0) + { + header("Location: ".$_SERVER["PHP_SELF"]); + exit; + } + else + { + dol_print_error($db); + } +} + +if (preg_match('/del_(.*)/',$action,$reg)) +{ + $code=$reg[1]; + if (dolibarr_del_const($db, $code, 0) > 0) + { + Header("Location: ".$_SERVER["PHP_SELF"]); + exit; + } + else + { + dol_print_error($db); + } +} + + +/* + * View + */ + +$block_static = new BlockedLog($db); + +$form=new Form($db); + +llxHeader('',$langs->trans("BlockedLogSetup")); + +$linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php">'.$langs->trans("BackToModuleList").'</a>'; +print load_fiche_titre($langs->trans("ModuleSetup").' BlockedLog',$linkback); + +$head=blockedlogadmin_prepare_head(); + +dol_fiche_head($head, 'blockedlog', '', -1); + +print $langs->trans("BlockedLogDesc")."<br>\n"; + +print '<br>'; + +print '<table class="noborder" width="100%">'; +print '<tr class="liste_titre">'; +print '<td>'.$langs->trans("Key").'</td>'; +print '<td>'.$langs->trans("Value").'</td>'; +print "</tr>\n"; + +print '<tr class="oddeven">'; +print '<td class="titlefield">'; +print $langs->trans("EntityKey").'</td><td align="center">'; + +print $block_static->getSignature(); + +print '</td></tr>'; + + +// Example with a yes / no select +$var=!$var; +print '<tr '.$bc[$var].'>'; +print '<td>'.$langs->trans("BlockedLogAuthorityUrl").img_info($langs->trans('BlockedLogAuthorityNeededToStoreYouFingerprintsInNonAlterableRemote')).'</td>'; +print '<td align="right" width="300">'; +print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">'; +print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">'; +print '<input type="hidden" name="action" value="set_BLOCKEDLOG_AUTHORITY_URL">'; +print '<input type="text" name="BLOCKEDLOG_AUTHORITY_URL" value="'.$conf->global->BLOCKEDLOG_AUTHORITY_URL.'" size="40" />'; +print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">'; +print '</form>'; +print '</td></tr>'; + + +print '</table>'; + +dol_fiche_end(); + +print '<br><br>'; + +llxFooter(); +$db->close(); diff --git a/htdocs/blockedlog/admin/fingerprints.php b/htdocs/blockedlog/admin/fingerprints.php new file mode 100644 index 0000000000000000000000000000000000000000..29cd2a3f73bd72074308cf4c56691413133bd3f8 --- /dev/null +++ b/htdocs/blockedlog/admin/fingerprints.php @@ -0,0 +1,142 @@ +<?php +/* Copyright (C) 2017 ATM Consulting <contact@atm-consulting.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file htdocs/blockedlog/admin/blockedlog.php + * \ingroup system + * \brief Page setup for blockedlog module + */ + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php'; +require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/authority.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; + +$langs->load("admin"); +$langs->load("other"); +$langs->load("blockedlog"); + +if (! $user->admin) accessforbidden(); + +$action = GETPOST('action','alpha'); + +if($action === 'downloadblockchain') { + + $auth = new BlockedLogAuthority($db); + + $bc = $auth->getLocalBlockChain(); + + header('Content-Type: application/octet-stream'); + header("Content-Transfer-Encoding: Binary"); + header("Content-disposition: attachment; filename=\"" .$auth->signature. ".certif\""); + + echo $bc; + + exit; +} + + +/* + * View + */ + +$block_static = new BlockedLog($db); + +$blocks = $block_static->getLog('all', 0, GETPOST('all') ? 0 : 50); + +$form=new Form($db); + +llxHeader('',$langs->trans("BlockedLogSetup")); + +$linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php">'.$langs->trans("BackToModuleList").'</a>'; +print load_fiche_titre($langs->trans("ModuleSetup").' BlockedLog',$linkback); + +$head=blockedlogadmin_prepare_head(); + +dol_fiche_head($head, 'fingerprints', '', -1); + +print $langs->trans("FingerprintsDesc")."<br>\n"; + +print '<br>'; + +echo '<div align="right"><a href="?all=1">'.$langs->trans('ShowAllFingerPrintsMightBeTooLong').'</a> <a href="?action=downloadblockchain">'.$langs->trans('DownloadBlockChain').'</a></div>'; + + +print '<table class="noborder" width="100%">'; +print '<tr class="liste_titre">'; + +print '<td>'.$langs->trans('Date').'</td>'; +print '<td>'.$langs->trans('Ref').'</td>'; +print '<td>'.$langs->trans('Action').'</td>'; +print '<td>'.$langs->trans('Element').'</td>'; +print '<td>'.$langs->trans('Amount').'</td>'; +print '<td>'.$langs->trans('Author').'</td>'; +print '<td>'.$langs->trans('Fingerprint').'</td>'; +print '<td><span id="blockchainstatus"></span></td>'; + +print '</tr>'; + +foreach($blocks as &$block) { + + print '<tr class="oddeven">'; + print '<td>'.dol_print_date($block->tms,'dayhour').'</td>'; + print '<td>'.$block->ref_object.'</td>'; + print '<td>'.$langs->trans('log'.$block->action).'</td>'; + print '<td>'.$block->getObject().'</td>'; + print '<td align="right">'.price($block->amounts).'</td>'; + print '<td>'.$block->getUser().'</td>'; + print '<td>'.$block->signature.'</td>'; + print '<td>'; + + print $block->checkSignature() ? img_picto('OkCheckPaymentValidity', 'on') : img_picto($langs->trans('KoCheckPaymentValidity'), 'off'); + print ' '.($block->certified ? img_picto($langs->trans('AddedByAuthority'), 'info') : img_picto($langs->trans('NotAddedByAuthorityYet'), 'info_black') ); + + print '</td>'; + print '</tr>'; + +} + +print '</table>'; + +?> + <script type="text/javascript"> + + $.ajax({ + url : "<?php echo dol_buildpath('/blockedlog/ajax/check_signature.php',1) ?>" + ,dataType:"html" + }).done(function(data) { + + if(data == 'hashisok') { + $('#blockchainstatus').html('<?php echo $langs->trans('AuthorityReconizeFingerprintConformity'). ' '. img_picto($langs->trans('SignatureOK'), 'on') ?>'); + } + else{ + $('#blockchainstatus').html('<?php echo $langs->trans('AuthorityDidntReconizeFingerprintConformity'). ' '.img_picto($langs->trans('SignatureKO'), 'off') ?>'); + } + + }); + + </script> + +<?php + +dol_fiche_end(); + +print '<br><br>'; + +llxFooter(); +$db->close(); diff --git a/htdocs/blockedlog/admin/index.html b/htdocs/blockedlog/admin/index.html new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/htdocs/blockedlog/ajax/authority.php b/htdocs/blockedlog/ajax/authority.php new file mode 100644 index 0000000000000000000000000000000000000000..12584b2bace68bfbfb88babd3d6fd5efe05d632e --- /dev/null +++ b/htdocs/blockedlog/ajax/authority.php @@ -0,0 +1,45 @@ +<?php + + require '../../master.inc.php'; + + require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php'; + require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/authority.class.php'; + + $user=new User($db); + $user->fetch(1); //TODO conf user authority + + $auth = new BlockedLogAuthority($db); + + $signature = GETPOST('s'); + $newblock = GETPOST('b'); + $hash = GETPOST('h'); + + if($auth->fetch(0, $signature)<=0) { + $auth->signature = $signature; + $auth->create($user); + } + + + if(!empty($hash)) { + + echo $auth->checkBlockchain($hash) ? 'hashisok' : 'hashisjunk'; + + } + elseif(!empty($newblock)){ + if($auth->checkBlock($newblock)) { + $auth->addBlock($newblock); + $auth->update($user); + + echo 'blockadded'; + } + else{ + + echo 'blockalreadyadded'; + + } + } + else{ + echo 'idontunderstandwhatihavetodo'; + } + + diff --git a/htdocs/blockedlog/ajax/check_signature.php b/htdocs/blockedlog/ajax/check_signature.php new file mode 100644 index 0000000000000000000000000000000000000000..bb018508a31ab2e4ed4664e76b250d1c63812d9d --- /dev/null +++ b/htdocs/blockedlog/ajax/check_signature.php @@ -0,0 +1,30 @@ +<?php + + require '../../main.inc.php'; + + if(empty($conf->global->BLOCKEDLOG_AUTHORITY_URL)) exit('BLOCKEDLOG_AUTHORITY_URL not set'); + + require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php'; + require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/authority.class.php'; + + $auth=new BlockedLogAuthority($db); + $auth->syncSignatureWithAuthority(); + + $block_static = new BlockedLog($db); + + $blocks = $block_static->getLog('just_certified', 0, 0, 1) ; + + $auth->signature = $block_static->getSignature(); + + foreach($blocks as &$b) { + $auth->blockchain.=$b->signature; + + } + + $hash = $auth->getBlockchainHash(); + + $url = $conf->global->BLOCKEDLOG_AUTHORITY_URL.'/blockedlog/ajax/authority.php?s='.$auth->signature.'&h='.$hash; + + $res = file_get_contents($url); + //echo $url; + echo $res; \ No newline at end of file diff --git a/htdocs/blockedlog/class/authority.class.php b/htdocs/blockedlog/class/authority.class.php new file mode 100644 index 0000000000000000000000000000000000000000..b422fb130d7fb8544bbafce2f704650e5e16bdf2 --- /dev/null +++ b/htdocs/blockedlog/class/authority.class.php @@ -0,0 +1,333 @@ +<?php +/* Copyright (C) 2017 ATM Consulting <contact@atm-consulting.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * Class to manage certif authority + */ +class BlockedLogAuthority +{ + + /** + * Id of the log + * @var int + */ + public $id; + + /** + * Unique fingerprint of the blockchain to store + * @var string + */ + public $signature = ''; + + /** + * Entire fingerprints blockchain + * @var string + */ + public $blockchain = ''; + + /** + * timestamp + * @var int + */ + public $tms = 0; + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) { + + $this->db = $db; + + } + + /** + * Get the blockchain + * + * @return string blockchain + */ + public function getLocalBlockChain() { + + $block_static = new BlockedLog($this->db); + + $this->signature = $block_static->getSignature(); + + $blocks = $block_static->getLog('all', 0, 0, 1) ; + + $this->blockchain = ''; + + foreach($blocks as &$b) { + $this->blockchain.=$b->signature; + + } + + return $this->blockchain; + } + + /** + * Get hash of the block chain to check + * + * @return string hash md5 of blockchain + */ + public function getBlockchainHash() { + + return md5($this->signature.$this->blockchain); + + } + + /** + * Get hash of the block chain to check + * + * @param string $hash hash md5 of blockchain to test + * @return boolean + */ + public function checkBlockchain($hash) { + + return ($hash === $this->getBlockchainHash() ); + + } + + /** + * Add a new block to the chain + * + * @param string $block new block to chain + */ + public function addBlock($block) { + + $this->blockchain.=$block; + + } + + /** + * hash already exist into chain ? + * + * @param string $block new block to chain + * @return boolean + */ + public function checkBlock($block) { + + if(strlen($block)!=64) return false; + + $blocks = str_split($this->blockchain,64); + + if(!in_array($block,$blocks)) { + return true; + } + else{ + return false; + } + } + + + /** + * Get object from database + * + * @param int $id Id of object to load + * @param string $signature Signature of object to load + * @return int >0 if OK, <0 if KO, 0 if not found + */ + public function fetch($id, $signature='') { + + global $langs; + + dol_syslog(get_class($this)."::fetch id=".$id, LOG_DEBUG); + + if (empty($id) && empty($signature)) + { + $this->error='BadParameter'; + return -1; + } + + $langs->load("blockedlog"); + + $sql = "SELECT b.rowid, b.signature, b.blockchain, b.tms"; + $sql.= " FROM ".MAIN_DB_PREFIX."blockedlog_authority as b"; + + if ($id) $sql.= " WHERE b.rowid = ". $id; + else if($signature)$sql.= " WHERE b.signature = '". $this->db->escape( $signature ) ."'" ; + + $resql=$this->db->query($sql); + if ($resql) + { + if ($this->db->num_rows($resql)) + { + $obj = $this->db->fetch_object($resql); + + $this->id = $obj->rowid; + $this->ref = $obj->rowid; + + $this->signature = $obj->signature; + $this->blockchain = $obj->blockchain; + + $this->tms = $this->db->jdate($obj->tms); + + return 1; + } + else + { + $this->error=$langs->trans("RecordNotFound"); + return 0; + } + } + else + { + $this->error=$this->db->error(); + return -1; + } + + } + + /** + * Create authority in database. + * + * @param User $user Object user that create + * @return int <0 if KO, >0 if OK + */ + public function create($user) { + + global $conf,$langs,$hookmanager; + + $langs->load('blockedlog'); + + $error=0; + + dol_syslog(get_class($this).'::create', LOG_DEBUG); + + $this->db->begin(); + + $sql = "INSERT INTO ".MAIN_DB_PREFIX."blockedlog_authority ("; + $sql.= " signature,"; + $sql.= " blockchain"; + $sql.= ") VALUES ("; + $sql.= "'".$this->db->escape($this->signature)."',"; + $sql.= "'".$this->db->escape($this->blockchain)."'"; + $sql.= ")"; + + $res = $this->db->query($sql); + if ($res) + { + $id = $this->db->last_insert_id(MAIN_DB_PREFIX."blockedlog_authority"); + + if ($id > 0) + { + $this->id = $id; + + $this->db->commit(); + + return $this->id; + } + else + { + $this->db->rollback(); + return -2; + } + } + else + { + $this->error=$this->db->error(); + $this->db->rollback(); + return -1; + } + + } + + /** + * Create authority in database. + * + * @param User $user Object user that create + * @return int <0 if KO, >0 if OK + */ + public function update($user) { + + global $conf,$langs,$hookmanager; + + $langs->load('blockedlog'); + + $error=0; + + dol_syslog(get_class($this).'::create', LOG_DEBUG); + + $this->db->begin(); + + $sql = "UPDATE ".MAIN_DB_PREFIX."blockedlog_authority SET "; + $sql.= " blockchain='".$this->db->escape($this->blockchain)."'"; + $sql.= " WHERE rowid=".$this->id; + + $res = $this->db->query($sql); + if ($res) + { + $this->db->commit(); + + return 1; + } + else + { + $this->error=$this->db->error(); + $this->db->rollback(); + return -1; + } + + } + + /** + * For cron to sync to authority. + * + * @return int <0 if KO, >0 if OK + */ + public function syncSignatureWithAuthority() { + global $conf, $langs; + + //TODO create cron task on activation + + if(empty($conf->global->BLOCKEDLOG_AUTHORITY_URL)) { + $this->error = $langs->trans('NoAuthorityURLDefined'); + return -2; + } + + require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php'; + + $block_static = new BlockedLog($this->db); + + $blocks = $block_static->getLog('not_certified', 0, 0, 1); + + $signature=$block_static->getSignature(); + + foreach($blocks as &$block) { + + $url = $conf->global->BLOCKEDLOG_AUTHORITY_URL.'/blockedlog/ajax/authority.php?s='.$signature.'&b='.$block->signature; + + $res = file_get_contents($url); + echo $block->signature.' '.$url. ' '.$res.'<br>'; + if($res === 'blockalreadyadded' || $res === 'blockadded') { + + $block->setCertified(); + + } + else { + + $this->error = $langs->trans('ImpossibleToContactAuthority ',$url); + return -1; + } + + + } + + return 1; + } + +} \ No newline at end of file diff --git a/htdocs/blockedlog/class/blockedlog.class.php b/htdocs/blockedlog/class/blockedlog.class.php new file mode 100644 index 0000000000000000000000000000000000000000..1f93eb3cc26d62edf8e28f2670a85715a9f10b23 --- /dev/null +++ b/htdocs/blockedlog/class/blockedlog.class.php @@ -0,0 +1,529 @@ +<?php +/* Copyright (C) 2017 ATM Consulting <contact@atm-consulting.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * Class to manage Blocked Log + */ + +class BlockedLog +{ + + /** + * Id of the log + * @var int + */ + public $id; + + /** + * Unique fingerprint of the log + * @var string + */ + public $signature = ''; + + /** + * Unique fingerprint of the line log content + * @var string + */ + public $signature_line = ''; + + public $amounts = null; + + /** + * trigger action + * @var string + */ + public $action = ''; + + /** + * Object element + * @var string + */ + public $element = ''; + + /** + * Object id + * @var int + */ + public $fk_object = 0; + + /** + * Log certified by remote authority or not + * @var boolean + */ + public $certified = false; + + /** + * Author + * @var int + */ + public $fk_user = 0; + + public $date_object = 0; + + public $ref_object = ''; + + public $object_data = null; + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct(DoliDB $db) + { + $this->db = $db; + + } + + /** + * try to retrieve logged object + */ + public function getObject() { + global $langs; + + if($this->element === 'facture') { + require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; + + $object = new Facture($this->db); + if($object->fetch($this->fk_object)>0) { + return $object->getNomUrl(1); + } + } + else if($this->element === 'payment') { + require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php'; + + $object = new Paiement($this->db); + if($object->fetch($this->fk_object)>0) { + return $object->getNomUrl(1); + } + } + + return $langs->trans('ImpossibleToReloadObject', $this->element, $this->fk_object); + + } + + /** + * try to retrieve user author + */ + public function getUser() { + global $langs, $cachedUser; + + if(empty($cachedUser))$cachedUser=array(); + + if(empty($cachedUser[$this->fk_user])) { + $u=new User($this->db); + if($u->fetch($this->fk_user)>0) { + $cachedUser[$this->fk_user] = $u; + } + } + + if(!empty($cachedUser[$this->fk_user])) { + return $cachedUser[$this->fk_user]->getNomUrl(1); + } + + return $langs->trans('ImpossibleToRetrieveUser', $this->fk_user); + } + + /** + * populate log by object + * + * @param payment|facture $object object to store + */ + public function setObjectData(&$object) { + + if($object->element=='payment') { + $this->date_object = $object->datepaye; + } + else{ + $this->date_object = $object->date; + } + + $this->ref_object = $object->ref; + $this->element = $object->element; + $this->fk_object = $object->id; + + $this->object_data=new stdClass(); + + if($this->element === 'facture') { + if(empty($object->thirdparty))$object->fetch_thirdparty(); + $this->object_data->thirdparty = new stdClass(); + + foreach($object->thirdparty as $key=>$value) { + if(!is_object($value)) $this->object_data->thirdparty->{$key} = $value; + } + + $this->object_data->total_ht = (double) $object->total_ht; + $this->object_data->total_tva = (double) $object->total_tva; + $this->object_data->total_ttc = (double) $object->total_ttc; + $this->object_data->total_localtax1= (double) $object->total_localtax1; + $this->object_data->total_localtax2= (double) $object->total_localtax2; + $this->object_data->note_public = (double) $object->note_public; + $this->object_data->note_private= (double) $object->note_private; + + } + elseif($this->element==='payment'){ + + $this->object_data->amounts = $object->amounts; + + } + + + } + + /** + * Get object from database + * + * @param int $id Id of object to load + * @return int >0 if OK, <0 if KO, 0 if not found + */ + public function fetch($id) { + + global $langs; + + dol_syslog(get_class($this)."::fetch id=".$id, LOG_DEBUG); + + if (empty($id)) + { + $this->error='BadParameter'; + return -1; + } + + $langs->load("blockedlog"); + + $sql = "SELECT b.rowid, b.signature, b.amounts, b.action, b.element, b.fk_object, b.certified, b.tms, b.fk_user, b.date_object, b.ref_object, b.object_data"; + $sql.= " FROM ".MAIN_DB_PREFIX."blockedlog as b"; + if ($id) $sql.= " WHERE b.rowid = ". $id; + + $resql=$this->db->query($sql); + if ($resql) + { + if ($this->db->num_rows($resql)) + { + $obj = $this->db->fetch_object($resql); + + $this->id = $obj->rowid; + $this->ref = $obj->rowid; + + $this->signature = $obj->signature; + $this->amounts = (double) $obj->amounts; + $this->action = $obj->action; + $this->element = $obj->element; + + $this->fk_object = trim($obj->fk_object); + $this->date_object = $this->db->jdate($obj->date_object); + $this->ref_object = $obj->ref_object; + + $this->certified = ($obj->certified == 1); + + $this->fk_user = $obj->fk_user; + + $this->tms = $this->db->jdate($obj->tms); + + $this->object_data = unserialize($obj->object_data); + + return 1; + } + else + { + $this->error=$langs->trans("RecordNotFound"); + return 0; + } + } + else + { + $this->error=$this->db->error(); + return -1; + } + + } + + /** + * Set block certified by authority + * + * @return boolean + */ + public function setCertified() { + + $res = $this->db->query("UPDATE ".MAIN_DB_PREFIX."blockedlog SET certified=1 WHERE rowid=".$this->id); + if($res===false) return false; + + return true; + + + } + + /** + * Create blocked log in database. + * + * @param User $user Object user that create + * @return int <0 if KO, >0 if OK + */ + public function create($user) { + + global $conf,$langs,$hookmanager; + + $langs->load('blockedlog'); + + $error=0; + + dol_syslog(get_class($this).'::create', LOG_DEBUG); + + $this->getSignatureRecursive(); + + + if (is_null($this->amounts)) + { + $this->error=$langs->trans("BlockLogNeedAmountsValue"); + dol_syslog($this->error, LOG_WARNING); + return -1; + } + + if(empty($this->element)) { + $this->error=$langs->trans("BlockLogNeedElement"); + dol_syslog($this->error, LOG_WARNING); + return -2; + } + + if(empty($this->action)) { + $this->error=$langs->trans("BlockLogNeedAction"); + dol_syslog($this->error, LOG_WARNING); + return -3; + } + + $this->fk_user = $user->id; + + $this->db->begin(); + + $sql = "INSERT INTO ".MAIN_DB_PREFIX."blockedlog ("; + $sql.= "action,"; + $sql.= " amounts,"; + $sql.= " signature,"; + $sql.= " signature_line,"; + $sql.= " element,"; + $sql.= " fk_object,"; + $sql.= " date_object,"; + $sql.= " ref_object,"; + $sql.= " object_data,"; + $sql.= " certified,"; + $sql.= " fk_user,"; + $sql.= " entity"; + $sql.= ") VALUES ("; + $sql.= "'".$this->db->escape($this->action)."',"; + $sql.= "".$this->amounts.","; + $sql.= "'".$this->db->escape($this->signature)."',"; + $sql.= "'".$this->db->escape($this->signature_line)."',"; + $sql.= "'".$this->db->escape($this->element)."',"; + $sql.= "".$this->fk_object.","; + $sql.= "'".$this->db->idate($this->date_object)."',"; + $sql.= "'".$this->db->escape($this->ref_object)."',"; + $sql.= "'".$this->db->escape(serialize($this->object_data))."',"; + $sql.= "0,"; + $sql.= "".$user->id.","; + $sql.= $conf->entity; + $sql.= ")"; + + $res = $this->db->query($sql); + if ($res) + { + $id = $this->db->last_insert_id(MAIN_DB_PREFIX."blockedlog"); + + if ($id > 0) + { + $this->id = $id; + + $this->db->commit(); + + return $this->id; + } + else + { + $this->db->rollback(); + return -2; + } + } + else + { + $this->error=$this->db->error(); + $this->db->rollback(); + return -1; + } + + } + + /** + * return crypted value. + * + * @param string $value string to crypt + * @return string crypted string + */ + private function crypt($value) { + + return hash('sha256',$value); + + } + + /** + * check if current signature still correct compare to the chain + * + * @return boolean + */ + public function checkSignature() { + + $signature_to_test = $this->signature; + + $this->getSignatureRecursive(); + + return ($signature_to_test=== $this->signature); + + } + + /** + * set current signatures + */ + private function getSignatureRecursive(){ + + $this->signature_line = $this->crypt( $this->action . $this->getSignature() . $this->amounts . print_r($this->object_data, true) ); + /*if($this->signature=='d6320580a02c1ab67fcc0a6d49d453c7d96dda0148901736f7f55725bfe1b900' || $this->signature=='ea65d435ff12ca929936a406aa9d707d99fb334c127878d256b602a5541bbbc9') { + var_dump($this->signature_line,$this->action ,$this->getSignature() , $this->amounts , $this->object_data); + }*/ + $this->signature = $this->signature_line; + + $logs = $this->getLog('all', 0, 0, 1) ; + if($logs!==false) { + foreach($logs as &$b) { + + if($this->id>0 && $b->id == $this->id) break; // on arrête sur un enregistrement précis pour recalculer une signature + + $b->getCurrentValue(); // on récupère la valeur actuelle en base de l'élément enregistré + + $this->signature = $this->crypt($this->signature. $this->action . $b->signature . $b->amounts); + } + } + + } + + /** + * return log object for a element. + * + * @param string $element element to search + * @param int $fk_object id of object to search + * @param int $limit max number of element, 0 for all + * @param string $order sort of query + * @return array array of object log + */ + public function getLog($element, $fk_object, $limit = 0, $order = -1) { + global $conf,$cachedlogs ; + + /* $cachedlogs allow fastest search */ + if(empty($cachedlogs)) $cachedlogs=array(); + + + if($element=='all') { + + $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog + WHERE entity=".$conf->entity; + + } + else if($element=='not_certified') { + $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog + WHERE entity=".$conf->entity." AND certified = 0"; + + } + else if($element=='just_certified') { + $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog + WHERE entity=".$conf->entity." AND certified = 1"; + + } + else{ + $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog + WHERE element='".$element."' AND fk_object=".(int) $fk_object; + + } + + $sql.=($order<0 ? ' ORDER BY rowid DESC ' : ' ORDER BY rowid ASC '); + + if($limit > 0 )$sql.=' LIMIT '.$limit; + + $res = $this->db->query($sql); + + if($res) { + + $results=array(); + + while($obj = $this->db->fetch_object($res)) { + + if(!isset($cachedlogs[$obj->rowid])) { + $b=new BlockedLog($this->db); + $b->fetch($obj->rowid); + + $cachedlogs[$obj->rowid] = $b; + } + + $results[] = $cachedlogs[$obj->rowid]; + + } + + return $results; + } + else{ + return false; + } + } + + /** + * set amounts of log from current element value in order to compare signature. + */ + private function getCurrentValue() { + + if($this->action === 'PAYMENT_CUSTOMER_CREATE' + || $this->action === 'PAYMENT_ADD_TO_BANK') { + $sql="SELECT amount FROM ".MAIN_DB_PREFIX."paiement WHERE rowid=".$this->fk_object; + + $res = $this->db->query($sql); + + if($res && $obj = $this->db->fetch_object($res)) { + $this->amounts = (double) $obj->amount; + } + } + + } + + /** + * return and set the entity signature included into line signature + * + * @return string current entity signature + */ + public function getSignature() { + global $db,$conf,$mysoc; + + if(empty($conf->global->BLOCKEDLOG_ENTITY_FINGERPRINT)) { // creation of a unique fingerprint + + require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; + + $fingerprint = $this->crypt(print_r($mysoc,true).time().rand(0,1000)); + + dolibarr_set_const($db, 'BLOCKEDLOG_ENTITY_FINGERPRINT', $fingerprint, 'chaine',0,'Numeric Unique Fingerprint', $conf->entity); + + $conf->global->BLOCKEDLOG_ENTITY_FINGERPRINT= $fingerprint; + } + + return $conf->global->BLOCKEDLOG_ENTITY_FINGERPRINT; + } + +} + diff --git a/htdocs/blockedlog/lib/blockedlog.lib.php b/htdocs/blockedlog/lib/blockedlog.lib.php new file mode 100644 index 0000000000000000000000000000000000000000..a7fca40d116d2c31acdbb46dcdd5b3fb85851b72 --- /dev/null +++ b/htdocs/blockedlog/lib/blockedlog.lib.php @@ -0,0 +1,57 @@ +<?php +/* Copyright (C) 2017 ATM Consulting <contact@atm-consulting.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file htdocs/blockedlog/lib/blockedlog.lib.php + * \ingroup system + * \brief Library for common blockedlog functions + */ + +/** + * Define head array for tabs of blockedlog tools setup pages + * + * @return Array of head + */ +function blockedlogadmin_prepare_head() +{ + global $langs, $conf; + + $h = 0; + $head = array(); + + $head[$h][0] = DOL_URL_ROOT."/blockedlog/admin/blockedlog.php"; + $head[$h][1] = $langs->trans("BlockedLog"); + $head[$h][2] = 'blockedlog'; + $h++; + + $head[$h][0] = DOL_URL_ROOT."/blockedlog/admin/fingerprints.php"; + $head[$h][1] = $langs->trans("Fingerprints"); + $head[$h][2] = 'fingerprints'; + $h++; + + $object=new stdClass(); + + // Show more tabs from modules + // Entries must be declared in modules descriptor with line + // $this->tabs = array('entity:+tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to add new tab + // $this->tabs = array('entity:-tabname); to remove a tab + complete_head_from_modules($conf,$langs,$object,$head,$h,'blockedlog'); + + complete_head_from_modules($conf,$langs,$object,$head,$h,'blockedlog','remove'); + + return $head; +} diff --git a/htdocs/blockedlog/lib/index.html b/htdocs/blockedlog/lib/index.html new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/htdocs/compta/paiement/class/paiement.class.php b/htdocs/compta/paiement/class/paiement.class.php index 03a7759e52c7ad402af13f42c937268134dd610f..c8d7f4d3759521d92c557315cd02455e037c0a3e 100644 --- a/htdocs/compta/paiement/class/paiement.class.php +++ b/htdocs/compta/paiement/class/paiement.class.php @@ -196,7 +196,7 @@ class Paiement extends CommonObject $this->db->begin(); - $ref = $this->getNextNumRef(''); + $this->ref = $this->getNextNumRef(''); if ($way == 'dolibarr') { @@ -210,7 +210,7 @@ class Paiement extends CommonObject } $sql = "INSERT INTO ".MAIN_DB_PREFIX."paiement (entity, ref, datec, datep, amount, multicurrency_amount, fk_paiement, num_paiement, note, fk_user_creat)"; - $sql.= " VALUES (".$conf->entity.", '".$ref."', '". $this->db->idate($now)."', '".$this->db->idate($this->datepaye)."', '".$total."', '".$mtotal."', ".$this->paiementid.", '".$this->num_paiement."', '".$this->db->escape($this->note)."', ".$user->id.")"; + $sql.= " VALUES (".$conf->entity.", '".$this->ref."', '". $this->db->idate($now)."', '".$this->db->idate($this->datepaye)."', '".$total."', '".$mtotal."', ".$this->paiementid.", '".$this->num_paiement."', '".$this->db->escape($this->note)."', ".$user->id.")"; dol_syslog(get_class($this)."::Create insert paiement", LOG_DEBUG); $resql = $this->db->query($sql); diff --git a/htdocs/core/modules/modBlockedLog.class.php b/htdocs/core/modules/modBlockedLog.class.php index c3e2cb6345a5d333f153952c9605783a9f5c08c4..b149471cd20d61b4968d58002880836077e3b2ad 100644 --- a/htdocs/core/modules/modBlockedLog.class.php +++ b/htdocs/core/modules/modBlockedLog.class.php @@ -60,7 +60,7 @@ class modBlockedLog extends DolibarrModules // Config pages //------------- - $this->config_page_url = array(); + $this->config_page_url = array('blockedlog.php@blockedlog'); // Dependancies //------------- diff --git a/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php b/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php new file mode 100644 index 0000000000000000000000000000000000000000..5a52777cbb97d8e4093eded4ffb73bdd1d5b0764 --- /dev/null +++ b/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php @@ -0,0 +1,94 @@ +<?php +/* Copyright (C) 2017 ATM Consulting <contact@atm-consulting.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file htdocs/core/triggers/interface_50_modAgenda_ActionsBlockedLog.class.php + * \ingroup system + * \brief Trigger file for blockedlog module + */ + +require_once DOL_DOCUMENT_ROOT.'/core/triggers/dolibarrtriggers.class.php'; +require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php'; + +/** + * Class of triggered functions for agenda module + */ +class InterfaceActionsBlockedLog extends DolibarrTriggers +{ + public $family = 'system'; + public $description = "Triggers of this module add blocklog."; + public $version = self::VERSION_DOLIBARR; + public $picto = 'system'; + + /** + * Function called on Dolibarrr payment or invoice event. + * + * @param string $action Event action code + * @param Object $object Object + * @param User $user Object user + * @param Translate $langs Object langs + * @param conf $conf Object conf + * @return int <0 if KO, 0 if no triggered ran, >0 if OK + */ + public function runTrigger($action, $object, User $user, Translate $langs, Conf $conf) + { + // Do not log events not enabled for this action + if (empty($conf->blockedlog->enabled)) { + return 0; + } + + if($action==='BILL_VALIDATE' || $action === 'BILL_PAYED' || $action==='BILL_UNPAYED') { + $amounts= (double) $object->total_ttc; + } + else if($action === 'PAYMENT_CUSTOMER_CREATE' || $action === 'PAYMENT_ADD_TO_BANK') { + $amounts = 0; + if(!empty($object->amounts)) { + foreach($object->amounts as $amount) { + $amounts+= price2num($amount); + } + } + + + } + else if(strpos($action,'PAYMENT')!==false) { + $amounts= (double) $object->amount; + } + else { + return 0; // not implemented action log + } + + $b=new BlockedLog($this->db); + $b->action = $action; + $b->amounts= $amounts; + $b->setObjectData($object); + + $res = $b->create($user); + + if($res<0) { + setEventMessage($b->error,'errors'); + + return -1; + } + else { + + return 1; + } + + + } + +} diff --git a/htdocs/install/mysql/migration/5.0.0-6.0.0.sql b/htdocs/install/mysql/migration/5.0.0-6.0.0.sql index 09d6c4de4751b107ccaf01fa5c6905117d1e7d94..2fa0cf48728a980c496f93221fa0ca8192a75389 100644 --- a/htdocs/install/mysql/migration/5.0.0-6.0.0.sql +++ b/htdocs/install/mysql/migration/5.0.0-6.0.0.sql @@ -397,3 +397,37 @@ ALTER TABLE llx_usergroup_rights DROP INDEX fk_usergroup; ALTER TABLE llx_usergroup_rights ADD UNIQUE INDEX uk_usergroup_rights (entity, fk_usergroup, fk_id); ALTER TABLE llx_usergroup_rights ADD CONSTRAINT fk_usergroup_rights_fk_usergroup FOREIGN KEY (fk_usergroup) REFERENCES llx_usergroup (rowid); +CREATE TABLE llx_blockedlog +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + action varchar(50), + amounts real NOT NULL, + signature varchar(100) NOT NULL, + signature_line varchar(100) NOT NULL, + element varchar(50), + fk_object integer, + ref_object varchar(100), + date_object datetime, + object_data text, + fk_user integer, + entity integer DEFAULT 1, + certified integer +) ENGINE=innodb; + +ALTER TABLE llx_blockedlog ADD INDEX signature (signature); +ALTER TABLE llx_blockedlog ADD INDEX fk_object_element (fk_object,element); +ALTER TABLE llx_blockedlog ADD INDEX entity (entity); +ALTER TABLE llx_blockedlog ADD INDEX fk_user (fk_user); +ALTER TABLE llx_blockedlog ADD INDEX entity_action (entity,action); +ALTER TABLE llx_blockedlog ADD INDEX entity_action_certified (entity,action,certified); + +CREATE TABLE llx_blockedlog_authority +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + blockchain longtext NOT NULL, + signature varchar(100) NOT NULL, + tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=innodb; + +ALTER TABLE llx_blockedlog_authority ADD INDEX signature (signature); diff --git a/htdocs/install/mysql/tables/llx_blockedlog.key.sql b/htdocs/install/mysql/tables/llx_blockedlog.key.sql new file mode 100644 index 0000000000000000000000000000000000000000..4312a056894a4e6db700945e711a7096ec53f357 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_blockedlog.key.sql @@ -0,0 +1,6 @@ +ALTER TABLE llx_blockedlog ADD INDEX signature (signature); +ALTER TABLE llx_blockedlog ADD INDEX fk_object_element (fk_object,element); +ALTER TABLE llx_blockedlog ADD INDEX entity (entity); +ALTER TABLE llx_blockedlog ADD INDEX fk_user (fk_user); +ALTER TABLE llx_blockedlog ADD INDEX entity_action (entity,action); +ALTER TABLE llx_blockedlog ADD INDEX entity_action_certified (entity,action,certified); diff --git a/htdocs/install/mysql/tables/llx_blockedlog.sql b/htdocs/install/mysql/tables/llx_blockedlog.sql new file mode 100644 index 0000000000000000000000000000000000000000..afc0f0d43756e5bdfcd500e1d0e1cdace106f227 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_blockedlog.sql @@ -0,0 +1,19 @@ + +CREATE TABLE llx_blockedlog +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + action varchar(50), + amounts real NOT NULL, + signature varchar(100) NOT NULL, + signature_line varchar(100) NOT NULL, + element varchar(50), + fk_object integer, + ref_object varchar(100), + date_object datetime, + object_data text, + fk_user integer, + entity integer DEFAULT 1, + certified integer +) ENGINE=innodb; + diff --git a/htdocs/install/mysql/tables/llx_blockedlog_authority.key.sql b/htdocs/install/mysql/tables/llx_blockedlog_authority.key.sql new file mode 100644 index 0000000000000000000000000000000000000000..289b6dbde56b0d56ae863e24bfe5169650dedbd3 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_blockedlog_authority.key.sql @@ -0,0 +1 @@ +ALTER TABLE llx_blockedlog_authority ADD INDEX signature (signature); diff --git a/htdocs/install/mysql/tables/llx_blockedlog_authority.sql b/htdocs/install/mysql/tables/llx_blockedlog_authority.sql new file mode 100644 index 0000000000000000000000000000000000000000..eb491f93af44cc4dc368088502acd2c7315d1be9 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_blockedlog_authority.sql @@ -0,0 +1,7 @@ +CREATE TABLE llx_blockedlog_authority +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + blockchain longtext NOT NULL, + signature varchar(100) NOT NULL, + tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=innodb;