Completed
Branch develop (b871c7)
by
unknown
30:12
created

BlockedLog::getSignature()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 0
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2017 ATM Consulting <[email protected]>
3
 * Copyright (C) 2017 Laurent Destailleur <[email protected]>
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
 *
18
 * See https://medium.com/@lhartikk/a-blockchain-in-200-lines-of-code-963cc1cc0e54
19
 */
20
21
/**
22
 *	Class to manage Blocked Log
23
 */
24
25
class BlockedLog
26
{
27
	/**
28
	 * Id of the log
29
	 * @var int
30
	 */
31
	public $id;
32
33
	public $error = '';
34
	public $errors = array();
35
36
	/**
37
	 * Unique fingerprint of the log
38
	 * @var string
39
	 */
40
	public $signature = '';
41
42
	/**
43
	 * Unique fingerprint of the line log content
44
	 * @var string
45
	 */
46
	public $signature_line = '';
47
48
	public $amounts = null;
49
50
	/**
51
	 * trigger action
52
	 * @var string
53
	 */
54
	public $action = '';
55
56
	/**
57
	 * Object element
58
	 * @var string
59
	 */
60
	public $element = '';
61
62
	/**
63
	 * Object id
64
	 * @var int
65
	 */
66
	public $fk_object = 0;
67
68
	/**
69
	 * Log certified by remote authority or not
70
	 * @var boolean
71
	 */
72
	public $certified = false;
73
74
	/**
75
	 * Author
76
	 * @var int
77
	 */
78
	public $fk_user = 0;
79
80
	public $date_object = 0;
81
82
	public $ref_object = '';
83
84
	public $object_data = null;
85
86
87
88
	/**
89
	 *      Constructor
90
	 *
91
	 *      @param		DoliDB		$db      Database handler
92
	 */
93
	public function __construct(DoliDB $db)
94
	{
95
		$this->db = $db;
96
97
	}
98
99
	/**
100
	 *      try to retrieve logged object link
101
	 */
102
	public function getObjectLink()
103
	{
104
		global $langs;
105
106
		if($this->element === 'facture') {
107
			require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
108
109
			$object = new Facture($this->db);
110
			if($object->fetch($this->fk_object)>0) {
111
				return $object->getNomUrl(1);
112
			}
113
			else{
114
				$this->error++;
115
			}
116
		}
117
		if($this->element === 'invoice_supplier') {
118
			require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
119
120
			$object = new FactureFournisseur($this->db);
121
			if($object->fetch($this->fk_object)>0) {
122
				return $object->getNomUrl(1);
123
			}
124
			else{
125
				$this->error++;
126
			}
127
		}
128
		else if($this->element === 'payment') {
129
			require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
130
131
			$object = new Paiement($this->db);
132
			if($object->fetch($this->fk_object)>0) {
133
				return $object->getNomUrl(1);
134
			}
135
			else{
136
				$this->error++;
137
			}
138
		}
139
		else if($this->element === 'payment_supplier') {
140
			require_once DOL_DOCUMENT_ROOT.'/fourn/class/paiementfourn.class.php';
141
142
			$object = new PaiementFourn($this->db);
143
			if($object->fetch($this->fk_object)>0) {
144
				return $object->getNomUrl(1);
145
			}
146
			else{
147
				$this->error++;
148
			}
149
		}
150
151
		return $langs->trans('ImpossibleToReloadObject', $this->element, $this->fk_object);
152
153
	}
154
155
	/**
156
	 *      try to retrieve user author
157
	 */
158
	public function getUser()
159
	{
160
		global $langs, $cachedUser;
161
162
		if(empty($cachedUser))$cachedUser=array();
163
164
		if(empty($cachedUser[$this->fk_user])) {
165
			$u=new User($this->db);
166
			if($u->fetch($this->fk_user)>0) {
167
				$cachedUser[$this->fk_user] = $u;
168
			}
169
		}
170
171
		if(!empty($cachedUser[$this->fk_user])) {
172
			return $cachedUser[$this->fk_user]->getNomUrl(1);
173
		}
174
175
		return $langs->trans('ImpossibleToRetrieveUser', $this->fk_user);
176
	}
177
178
	/**
179
	 *      Populate properties of log from object data
180
	 *
181
	 *      @param		Object		$object      object to store
182
	 *      @param		string		$action      action
183
	 *      @param		string		$amounts     amounts
184
	 */
185
	public function setObjectData(&$object, $action, $amounts)
186
	{
187
		global $user, $mysoc;
188
189
		// Generic fields
190
191
		// action
192
		$this->action = $action;
193
		// amount
194
		$this->amounts= $amounts;
195
		// date
196
		if ($object->element == 'payment' || $object->element == 'payment_supplier')
197
		{
198
			$this->date_object = $object->datepaye;
199
		}
200
		elseif ($object->element=='payment_salary')
201
		{
202
			$this->date_object = $object->datev;
203
		}
204
		else {
205
			$this->date_object = $object->date;
206
		}
207
		// ref
208
		$this->ref_object = $object->ref;
209
		// type of object
210
		$this->element = $object->element;
211
		// id of object
212
		$this->fk_object = $object->id;
213
214
		$this->object_data=new stdClass();
215
216
		// Add thirdparty info
217
218
		if (empty($object->thirdparty) && method_exists('fetch_thirdparty')) $object->fetch_thirdparty();
219
220
		if (! empty($object->thirdparty))
221
		{
222
			$this->object_data->thirdparty = new stdClass();
223
224
			foreach($object->thirdparty as $key=>$value)
225
			{
226
				if (in_array($key, array('fields'))) continue;	// Discard some properties
227
				if (! in_array($key, array(
228
				'name','name_alias','ref_ext','address','zip','town','state_code','country_code','idprof1','idprof2','idprof3','idprof4','idprof5','idprof6','phone','fax','email','barcode',
229
				'tva_intra', 'localtax1_assuj', 'localtax1_value', 'localtax2_assuj', 'localtax2_value', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur'
230
				))) continue;								// Discard if not into a dedicated list
231
				if (!is_object($value)) $this->object_data->thirdparty->{$key} = $value;
232
			}
233
		}
234
235
		// Add company info
236
		if (! empty($mysoc))
237
		{
238
			$this->object_data->mycompany = new stdClass();
239
240
			foreach($mysoc as $key=>$value)
241
			{
242
				if (in_array($key, array('fields'))) continue;	// Discard some properties
243
				if (! in_array($key, array(
244
				'name','name_alias','ref_ext','address','zip','town','state_code','country_code','idprof1','idprof2','idprof3','idprof4','idprof5','idprof6','phone','fax','email','barcode',
245
				'tva_intra', 'localtax1_assuj', 'localtax1_value', 'localtax2_assuj', 'localtax2_value', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur'
246
				))) continue;									// Discard if not into a dedicated list
247
				if (!is_object($value)) $this->object_data->mycompany->{$key} = $value;
248
			}
249
		}
250
251
		// Add user info
252
253
		$this->fk_user = $user->id;
254
		$this->user_fullname = $user->getFullName($langs);
0 ignored issues
show
Bug introduced by
The variable $langs does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
255
256
		// Field specific to object
257
258
		if ($this->element == 'facture')
259
		{
260
			$this->object_data->total_ht 	= (double) $object->total_ht;
261
			$this->object_data->total_tva	= (double) $object->total_tva;
262
			$this->object_data->total_ttc	= (double) $object->total_ttc;
263
			$this->object_data->total_localtax1 = (double) $object->total_localtax1;
264
			$this->object_data->total_localtax2 = (double) $object->total_localtax2;
265
266
			$this->object_data->revenue_stamp = (double) $object->revenue_stamp;
267
			$this->object_data->date_pointoftax = (double) $object->date_pointoftax;
268
			$this->object_data->note_public	= (double) $object->note_public;
269
		}
270
		if($this->element == 'invoice_supplier') {
271
			if(empty($object->thirdparty))$object->fetch_thirdparty();
272
			$this->object_data->thirdparty = new stdClass();
273
274
			foreach($object->thirdparty as $key=>$value) {
275
				if(!is_object($value)) $this->object_data->thirdparty->{$key} = $value;
276
			}
277
278
			$this->object_data->total_ht 	= (double) $object->total_ht;
279
			$this->object_data->total_tva	= (double) $object->total_tva;
280
			$this->object_data->total_ttc	= (double) $object->total_ttc;
281
			$this->object_data->total_localtax1 = (double) $object->total_localtax1;
282
			$this->object_data->total_localtax2 = (double) $object->total_localtax2;
283
284
			$this->object_data->revenue_stamp = (double) $object->revenue_stamp;
285
			$this->object_data->date_pointoftax = (double) $object->date_pointoftax;
286
			$this->object_data->note_public	= (double) $object->note_public;
287
		}
288
		elseif ($this->element == 'payment'|| $object->element == 'payment_supplier')
289
		{
290
			$this->object_data->amounts = $object->amounts;
291
		}
292
		elseif($this->element == 'payment_salary')
293
		{
294
			$this->object_data->amounts = array($object->amount);
295
		}
296
	}
297
298
	/**
299
	 *	Get object from database
300
	 *
301
	 *	@param      int		$id       	Id of object to load
302
	 *	@return     int         			>0 if OK, <0 if KO, 0 if not found
303
	 */
304
	public function fetch($id) {
305
306
		global $langs;
307
308
		dol_syslog(get_class($this)."::fetch id=".$id, LOG_DEBUG);
309
310
		if (empty($id))
311
		{
312
			$this->error='BadParameter';
313
			return -1;
314
		}
315
316
		$langs->load("blockedlog");
317
318
		$sql = "SELECT b.rowid, b.date_creation, b.signature, b.signature_line, b.amounts, b.action, b.element, b.fk_object, b.certified, b.tms, b.fk_user, b.user_fullname, b.date_object, b.ref_object, b.object_data";
319
		$sql.= " FROM ".MAIN_DB_PREFIX."blockedlog as b";
320
		if ($id) $sql.= " WHERE b.rowid = ". $id;
321
322
		$resql=$this->db->query($sql);
323
		if ($resql)
324
		{
325
			if ($this->db->num_rows($resql))
326
			{
327
				$obj = $this->db->fetch_object($resql);
328
329
				$this->id				= $obj->rowid;
330
				$this->ref				= $obj->rowid;
331
332
				$this->date_creation    = $this->db->jdate($obj->date_creation);
333
				$this->tms				= $this->db->jdate($obj->tms);
334
335
				$this->amounts			= (double) $obj->amounts;
336
				$this->action			= $obj->action;
337
				$this->element			= $obj->element;
338
339
				$this->fk_object		= $obj->fk_object;
340
				$this->date_object		= $this->db->jdate($obj->date_object);
341
				$this->ref_object		= $obj->ref_object;
342
343
				$this->fk_user 			= $obj->fk_user;
344
				$this->user_fullname	= $obj->user_fullname;
345
346
				$this->object_data		= unserialize($obj->object_data);
347
348
				$this->signature		= $obj->signature;
349
				$this->signature_line	= $obj->signature_line;
350
				$this->certified		= ($obj->certified == 1);
351
352
				return 1;
353
			}
354
			else
355
			{
356
				$this->error=$langs->trans("RecordNotFound");
357
				return 0;
358
			}
359
		}
360
		else
361
		{
362
			$this->error=$this->db->error();
363
			return -1;
364
		}
365
366
	}
367
368
	/**
369
	 *	Set block certified by authority
370
	 *
371
	 *	@return	boolean
372
	 */
373
	public function setCertified() {
374
375
		$res = $this->db->query("UPDATE ".MAIN_DB_PREFIX."blockedlog SET certified=1 WHERE rowid=".$this->id);
376
		if($res===false) return false;
377
378
		return true;
379
380
381
	}
382
383
	/**
384
	 *	Create blocked log in database.
385
	 *
386
	 *	@param	User	$user      		Object user that create
387
	 *	@return	int						<0 if KO, >0 if OK
388
	 */
389
	public function create($user) {
390
391
		global $conf,$langs,$hookmanager;
392
393
		$langs->load('blockedlog');
394
395
		$error=0;
396
397
		// Clean data
398
		$this->amounts=(double) $this->amounts;
399
400
		dol_syslog(get_class($this).'::create action='.$this->action.' fk_user='.$this->fk_user.' user_fullname='.$this->user_fullname, LOG_DEBUG);
401
402
		// Check parameters/properties
403
		if (is_null($this->amounts))
404
		{
405
			$this->error=$langs->trans("BlockLogNeedAmountsValue");
406
			dol_syslog($this->error, LOG_WARNING);
407
			return -1;
408
		}
409
410
		if(empty($this->element)) {
411
			$this->error=$langs->trans("BlockLogNeedElement");
412
			dol_syslog($this->error, LOG_WARNING);
413
			return -2;
414
		}
415
416
		if (empty($this->action) || empty($this->fk_user) || empty($this->user_fullname)) {
417
			$this->error=$langs->trans("BadParameterWhenCallingCreateOfBlockedLog");
418
			dol_syslog($this->error, LOG_WARNING);
419
			return -3;
420
		}
421
422
		$this->date_creation = dol_now();
423
424
		$this->db->begin();
425
426
		$previoushash = $this->getPreviousHash(1);	// This get last record and lock database until insert is done
427
428
		$keyforsignature = $this->buildKeyForSignature();
429
430
		$this->signature_line = dol_hash($keyforsignature, '5');		// Not really usefull
431
		$this->signature = dol_hash($previoushash . $keyforsignature, '5');
432
		//var_dump($keyforsignature);var_dump($previoushash);var_dump($this->signature_line);var_dump($this->signature);
433
434
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."blockedlog (";
435
		$sql.= " date_creation,";
436
		$sql.= " action,";
437
		$sql.= " amounts,";
438
		$sql.= " signature,";
439
		$sql.= " signature_line,";
440
		$sql.= " element,";
441
		$sql.= " fk_object,";
442
		$sql.= " date_object,";
443
		$sql.= " ref_object,";
444
		$sql.= " object_data,";
445
		$sql.= " certified,";
446
		$sql.= " fk_user,";
447
		$sql.= " user_fullname,";
448
		$sql.= " entity";
449
		$sql.= ") VALUES (";
450
		$sql.= "'".$this->db->idate($this->date_creation)."',";
451
		$sql.= "'".$this->db->escape($this->action)."',";
452
		$sql.= $this->amounts.",";
453
		$sql.= "'".$this->db->escape($this->signature)."',";
454
		$sql.= "'".$this->db->escape($this->signature_line)."',";
455
		$sql.= "'".$this->db->escape($this->element)."',";
456
		$sql.= $this->fk_object.",";
457
		$sql.= "'".$this->db->idate($this->date_object)."',";
458
		$sql.= "'".$this->db->escape($this->ref_object)."',";
459
		$sql.= "'".$this->db->escape(serialize($this->object_data))."',";
460
		$sql.= "0,";
461
		$sql.= $this->fk_user.",";
462
		$sql.= "'".$this->db->escape($this->user_fullname)."',";
463
		$sql.= $conf->entity;
464
		$sql.= ")";
465
466
		$res = $this->db->query($sql);
467
		if ($res)
468
		{
469
			$id = $this->db->last_insert_id(MAIN_DB_PREFIX."blockedlog");
470
471
			if ($id > 0)
472
			{
473
				$this->id = $id;
474
475
				$this->db->commit();
476
477
				return $this->id;
478
			}
479
			else
480
			{
481
				$this->db->rollback();
482
				return -2;
483
			}
484
		}
485
		else
486
		{
487
			$this->error=$this->db->error();
488
			$this->db->rollback();
489
			return -1;
490
		}
491
492
		// The commit will release the lock so we can insert nex record
493
	}
494
495
	/**
496
	 *	Check if current signature still correct compare to the chain
497
	 *
498
	 *	@return	boolean			True if OK, False if KO
499
	 */
500
	public function checkSignature()
501
	{
502
503
		//$oldblockedlog = new BlockedLog($this->db);
504
		//$previousrecord = $oldblockedlog->fetch($this->id - 1);
505
		$previoushash = $this->getPreviousHash(0, $this->id);
506
507
		// Recalculate hash
508
		$keyforsignature = $this->buildKeyForSignature();
509
		$signature_line = dol_hash($keyforsignature, '5');		// Not really usefull
510
		$signature = dol_hash($previoushash . $keyforsignature, '5');
511
		//var_dump($previoushash); var_dump($keyforsignature); var_dump($signature_line); var_dump($signature);
512
513
		$res = ($signature === $this->signature);
514
515
		if (!$res) {
516
			$this->error = 'Signature KO';
517
		}
518
519
		return $res;
520
	}
521
522
	/**
523
	 * Return a string for signature
524
	 *
525
	 * @return string		Key for signature
526
	 */
527
	private function buildKeyForSignature()
528
	{
529
		//print_r($this->object_data);
530
		return $this->date_creation.'|'.$this->action.'|'.$this->amounts.'|'.$this->ref_object.'|'.$this->date_object.'|'.$this->user_fullname.'|'.print_r($this->object_data, true);
531
	}
532
533
534
	/**
535
	 *	Get previous signature/hash in chain
536
	 *
537
	 *	@param int	$withlock		1=With a lock
538
	 *	@param int	$beforeid		Before id
539
	 *  @return	string				Hash of last record
540
	 */
541
	 private function getPreviousHash($withlock=0, $beforeid=0)
542
	 {
543
		global $conf;
544
545
		$previoussignature='';
546
547
	 	$sql="SELECT rowid, signature FROM ".MAIN_DB_PREFIX."blockedlog WHERE entity=".$conf->entity;
548
	 	if ($beforeid) $sql.= " AND rowid < ".(int) $beforeid;
549
	 	$sql.=" ORDER BY rowid DESC LIMIT 1";
550
	 	$sql.=($withlock ? " FOR UPDATE ": "");
551
552
	 	$resql = $this->db->query($sql);
553
	 	if ($resql) {
554
	 		$obj = $this->db->fetch_object($resql);
555
	 		if ($obj)
556
	 		{
557
	 			$previoussignature = $obj->signature;
558
	 		}
559
	 	}
560
	 	else
561
	 	{
562
	 		dol_print_error($this->db);
563
	 		exit;
564
	 	}
565
566
	 	if (empty($previoussignature))
567
	 	{
568
			// First signature line (line 0)
569
	 		$previoussignature = $this->getSignature();
570
	 	}
571
572
	 	return $previoussignature;
573
	}
574
575
	/**
576
	 *	Return array of log objects (with criterias)
577
	 *
578
	 *	@param	string 	$element      	element to search
579
	 *	@param	int 	$fk_object		id of object to search
580
	 *	@param	int 	$limit      	max number of element, 0 for all
581
	 *	@param	string 	$sortfield     	sort field
582
	 *	@param	string 	$sortorder     	sort order
583
	 *	@return	array					array of object log
584
	 */
585
	public function getLog($element, $fk_object, $limit = 0, $sortfield = '', $sortorder = '')
586
	{
587
		global $conf, $cachedlogs;
588
589
		/* $cachedlogs allow fastest search */
590
		if (empty($cachedlogs)) $cachedlogs=array();
591
592
		if ($element=='all') {
593
594
	 		$sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
595
	         WHERE entity=".$conf->entity;
596
597
		}
598
		else if ($element=='not_certified') {
599
			$sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
600
	         WHERE entity=".$conf->entity." AND certified = 0";
601
602
		}
603
		else if ($element=='just_certified') {
604
			$sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
605
	         WHERE entity=".$conf->entity." AND certified = 1";
606
607
		}
608
		else{
609
			$sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
610
	         WHERE element='".$element."' AND fk_object=".(int) $fk_object;
611
		}
612
613
		$sql.=$this->db->order($sortfield, $sortorder);
614
		//($order<0 ? ' ORDER BY rowid DESC ' : ' ORDER BY rowid ASC ');
615
616
		if($limit > 0 )$sql.=' LIMIT '.$limit;
617
618
		$res = $this->db->query($sql);
619
620
		if($res) {
621
622
			$results=array();
623
624
			while ($obj = $this->db->fetch_object($res)) {
625
626
				if (!isset($cachedlogs[$obj->rowid])) {
627
					$b=new BlockedLog($this->db);
628
					$b->fetch($obj->rowid);
629
630
					$cachedlogs[$obj->rowid] = $b;
631
				}
632
633
				$results[] = $cachedlogs[$obj->rowid];
634
			}
635
636
			return $results;
637
		}
638
		else{
639
			return false;
640
		}
641
	}
642
643
	/**
644
	 *	Return the signature (hash) of the "genesis-block" (Block 0)
645
	 *
646
	 *	@return	string					Signature of genesis-block for current conf->entity
647
	 */
648
	public function getSignature()
649
	{
650
		global $db,$conf,$mysoc;
651
652
		if (empty($conf->global->BLOCKEDLOG_ENTITY_FINGERPRINT)) { // creation of a unique fingerprint
653
654
			require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
655
656
			$fingerprint = dol_hash(print_r($mysoc,true).getRandomPassword(1), '5');
657
658
			dolibarr_set_const($db, 'BLOCKEDLOG_ENTITY_FINGERPRINT', $fingerprint, 'chaine',0,'Numeric Unique Fingerprint', $conf->entity);
659
660
			$conf->global->BLOCKEDLOG_ENTITY_FINGERPRINT=$fingerprint;
661
		}
662
663
		return $conf->global->BLOCKEDLOG_ENTITY_FINGERPRINT;
664
	}
665
666
}
667
668