Page MenuHomeIn-Portal Phabricator

SecurityGeneratorPromise.php
No OneTemporary

File Metadata

Created
Mon, Aug 18, 5:37 AM

SecurityGeneratorPromise.php

<?php
/**
* @version $Id: SecurityGeneratorPromise.php 16727 2022-08-29 07:58:21Z alex $
* @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->getUnitConfig($prefix_or_table)->getTableName();
}
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();
}
}

Event Timeline