Changeset View
Changeset View
Standalone View
Standalone View
branches/5.2.x/core/kernel/security/SecurityGeneratorPromise.php
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | LF |
svn:keywords | null | Id |
<?php | |||||
/** | |||||
* @version $Id$ | |||||
* @package In-Portal | |||||
* @copyright Copyright (C) 1997 - 2018 Intechnic. All rights reserved. | |||||
* @license GNU/GPL | |||||
* In-Portal is Open Source software. | |||||
* This means that this software may have been modified pursuant | |||||
* the GNU General Public License, and as distributed it includes | |||||
* or is derivative of works licensed under the GNU General Public License | |||||
* or other free or open source software licenses. | |||||
* See http://www.in-portal.org/license for copyright notices and details. | |||||
*/ | |||||
defined('FULL_PATH') or die('restricted access!'); | |||||
final class SecurityGeneratorPromise | |||||
{ | |||||
/** | |||||
* Promise, that returns random integer number. | |||||
*/ | |||||
const TYPE_NUMBER = 1; | |||||
/** | |||||
* Promise, that returns random string. | |||||
*/ | |||||
const TYPE_STRING = 2; | |||||
/** | |||||
* Promise, that returns random bytes. | |||||
*/ | |||||
const TYPE_BYTES = 3; | |||||
/** | |||||
* Generator method. | |||||
* | |||||
* @var string | |||||
*/ | |||||
protected $method; | |||||
/** | |||||
* Generator method arguments. | |||||
* | |||||
* @var array | |||||
*/ | |||||
protected $arguments = array(); | |||||
/** | |||||
* Resolved value (only, when was resolved). | |||||
* | |||||
* @var string | |||||
*/ | |||||
protected $resolvedValue; | |||||
/** | |||||
* Return signature of resolved value. | |||||
* | |||||
* @var boolean | |||||
*/ | |||||
protected $asSignature = false; | |||||
/** | |||||
* Return raw resolved value's signature representation. | |||||
* | |||||
* @var boolean | |||||
*/ | |||||
protected $signatureRawOutput = false; | |||||
/** | |||||
* Alternative key used for creating signature of resolved value. | |||||
* | |||||
* @var string | |||||
*/ | |||||
protected $signatureKeyOverride; | |||||
/** | |||||
* SecurityGeneratorPromise constructor. | |||||
* | |||||
* @param integer $type Promise type. | |||||
* @param array $arguments Generator method arguments. | |||||
* | |||||
* @throws InvalidArgumentException When unsupported promise type is given. | |||||
*/ | |||||
public function __construct($type, array $arguments = array()) | |||||
{ | |||||
$mapping = array( | |||||
self::TYPE_NUMBER => 'generateNumber', | |||||
self::TYPE_STRING => 'generateString', | |||||
self::TYPE_BYTES => 'generateBytes', | |||||
); | |||||
if ( !isset($mapping[$type]) ) { | |||||
throw new InvalidArgumentException('The "' . $type . '" promise type is not supported.'); | |||||
} | |||||
$this->method = $mapping[$type]; | |||||
$this->arguments = $arguments; | |||||
} | |||||
/** | |||||
* Makes sure, that resolved value isn't already used in the given database table's column. | |||||
* | |||||
* @param string $prefix_or_table Unit config prefix or table name. | |||||
* @param string $column Column in specified table. | |||||
* | |||||
* @return mixed | |||||
* @throws LogicException When after 10 retries still unable to generate unique value for database. | |||||
*/ | |||||
public function resolveForPersisting($prefix_or_table, $column) | |||||
{ | |||||
$application =& kApplication::Instance(); | |||||
if ( $application->prefixRegistred($prefix_or_table) ) { | |||||
$table = $application->getUnitOption($prefix_or_table, 'TableName'); | |||||
} | |||||
else { | |||||
$table = $prefix_or_table; | |||||
} | |||||
$retries = 0; | |||||
do { | |||||
$resolved_value = $this->resolve(); | |||||
$sql = 'SELECT ' . $column . ' | |||||
FROM ' . $table . ' | |||||
WHERE ' . $column . ' = ' . $application->Conn->qstr($resolved_value); | |||||
$found = $application->Conn->GetOne($sql) !== false; | |||||
if ( $found ) { | |||||
$this->resolvedValue = null; | |||||
$retries++; | |||||
} | |||||
} while ( $found && $retries < 10 ); | |||||
if ( $found ) { | |||||
throw new LogicException(sprintf( | |||||
'Unable to generate unique value for "%s" column with current generator configuration.', | |||||
$table . '.' . $column | |||||
)); | |||||
} | |||||
return $resolved_value; | |||||
} | |||||
/** | |||||
* Resolves a promise. | |||||
* | |||||
* @return mixed | |||||
*/ | |||||
public function resolve() | |||||
{ | |||||
if ( !$this->isResolved() ) { | |||||
$this->resolvedValue = call_user_func_array(array($this, $this->method), $this->arguments); | |||||
} | |||||
if ( !$this->asSignature ) { | |||||
return $this->resolvedValue; | |||||
} | |||||
$application =& kApplication::Instance(); | |||||
/** @var SecurityEncrypter $encrypter */ | |||||
$encrypter = $application->recallObject('SecurityEncrypter'); | |||||
return $encrypter->createSignature( | |||||
$this->resolvedValue, | |||||
$this->signatureRawOutput, | |||||
$this->signatureKeyOverride | |||||
); | |||||
} | |||||
/** | |||||
* Configures promise to return signature of a resolved value. | |||||
* | |||||
* @param boolean $raw_output Return raw signature value during resolving. | |||||
* @param string|null $key_override Alternative key used for creating signature of resolved value. | |||||
* | |||||
* @return self | |||||
*/ | |||||
public function asSignature($raw_output = false, $key_override = null) | |||||
{ | |||||
$this->asSignature = true; | |||||
$this->signatureRawOutput = $raw_output; | |||||
$this->signatureKeyOverride = $key_override; | |||||
return $this; | |||||
} | |||||
/** | |||||
* Configures promise to return resolved value as-is. | |||||
* | |||||
* @return self | |||||
*/ | |||||
public function asValue() | |||||
{ | |||||
$this->asSignature = false; | |||||
$this->signatureRawOutput = false; | |||||
$this->signatureKeyOverride = null; | |||||
return $this; | |||||
} | |||||
/** | |||||
* Generate a random string of specified length using supplied character list. | |||||
* | |||||
* @param integer $length The length of the generated string. | |||||
* @param mixed $characters List of characters to use. | |||||
* | |||||
* @return string | |||||
*/ | |||||
protected function generateString($length, $characters) | |||||
{ | |||||
$ret = ''; | |||||
$max_index = mb_strlen($characters) - 1; | |||||
for ( $i = 0; $i < $length; $i++ ) { | |||||
$ret .= mb_substr($characters, $this->generateNumber(0, $max_index), 1); | |||||
} | |||||
return $ret; | |||||
} | |||||
/** | |||||
* Generates a random number. | |||||
* | |||||
* @param integer $min Smallest value. | |||||
* @param integer $max Largest value. | |||||
* | |||||
* @return integer | |||||
*/ | |||||
protected function generateNumber($min, $max) | |||||
{ | |||||
return random_int($min, $max); | |||||
} | |||||
/** | |||||
* Generates random bytes. | |||||
* | |||||
* @param integer $length Raw result length. | |||||
* @param boolean $raw_output Return raw result. | |||||
* | |||||
* @return string | |||||
*/ | |||||
protected function generateBytes($length = 16, $raw_output = false) | |||||
{ | |||||
$ret = random_bytes($length); | |||||
return $raw_output ? $ret : bin2hex($ret); | |||||
} | |||||
/** | |||||
* Determines if promise was already resolved. | |||||
* | |||||
* @return boolean | |||||
*/ | |||||
protected function isResolved() | |||||
{ | |||||
return $this->resolvedValue !== null; | |||||
} | |||||
/** | |||||
* Returns promise resolved value casted to string. | |||||
* | |||||
* @return string | |||||
*/ | |||||
public function __toString() | |||||
{ | |||||
return (string)$this->resolve(); | |||||
} | |||||
} |