Added CaptchaInstanceManager
This commit is contained in:
parent
490e4f7a9e
commit
d65f9ae897
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace vCaptcha\Classes;
|
||||
|
||||
class Security
|
||||
{
|
||||
/**
|
||||
* Peppers a data input into irreversible hash that is iterated randomly between
|
||||
* the minimum and maximum value of the given parameters
|
||||
*
|
||||
* @param string $data
|
||||
* @param int $min
|
||||
* @param int $max
|
||||
* @return string
|
||||
*/
|
||||
public static function pepper(string $data, int $min = 100, int $max = 1000): string
|
||||
{
|
||||
$n = rand($min, $max);
|
||||
$res = '';
|
||||
$data = hash('whirlpool', $data);
|
||||
for ($i=0, $l=strlen($data) ; $l ; $l--)
|
||||
{
|
||||
$i = ($i+$n-1) % $l;
|
||||
$res = $res . $data[$i];
|
||||
$data = ($i ? substr($data, 0, $i) : '') . ($i < $l-1 ? substr($data, $i+1) : '');
|
||||
}
|
||||
return($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 48 long unique ID for the captcha instance
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $owner_id
|
||||
* @return string
|
||||
*/
|
||||
public static function generateCaptchaInstanceSecret(string $id, string $owner_id): string
|
||||
{
|
||||
return hash('crc32', $id) . hash('crc32', $owner_id) . hash('haval128,3', self::pepper($id . $owner_id . time()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace vCaptcha\Classes;
|
||||
|
||||
class Validate
|
||||
{
|
||||
/**
|
||||
* Validates the captcha instance name
|
||||
*
|
||||
* @param string $input
|
||||
* @return bool
|
||||
*/
|
||||
public static function captchaInstanceName(string $input): bool
|
||||
{
|
||||
if(strlen($input) > 120)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(strlen($input) < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(preg_match("/^[a-zA-Z0-9 ]*$/", $input))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace vCaptcha\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class CaptchaInstanceNotFoundException extends Exception
|
||||
{
|
||||
/**
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct(string $message = "", int $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace vCaptcha\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class DatabaseException extends Exception
|
||||
{
|
||||
private string $query;
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param string $query
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct(string $message = "", string $query="", int $code = 0, ?Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->message = $message;
|
||||
$this->query = $query;
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getQuery(): string
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace vCaptcha\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class InvalidCaptchaNameException extends \Exception
|
||||
{
|
||||
/**
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct(string $message = "", int $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->message = $message;
|
||||
$this->code = $code;
|
||||
}
|
||||
}
|
|
@ -6,8 +6,14 @@
|
|||
|
||||
use msqg\QueryBuilder;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
use vCaptcha\Classes\Security;
|
||||
use vCaptcha\Classes\Validate;
|
||||
use vCaptcha\Exceptions\CaptchaInstanceNotFoundException;
|
||||
use vCaptcha\Exceptions\DatabaseException;
|
||||
use vCaptcha\Exceptions\InvalidCaptchaNameException;
|
||||
use vCaptcha\Objects\CaptchaInstance;
|
||||
use vCaptcha\Objects\CaptchaInstance\FirewallOptions;
|
||||
use vCaptcha\Objects\SampleCaptchaInstance;
|
||||
use vCaptcha\vCaptcha;
|
||||
use ZiProto\ZiProto;
|
||||
|
||||
|
@ -23,10 +29,25 @@
|
|||
$this->vcaptcha = $vcaptcha;
|
||||
}
|
||||
|
||||
public function createCaptcha(string $captcha_type, string $owner_id): CaptchaInstance
|
||||
/**
|
||||
* Creates a new Captcha instance
|
||||
*
|
||||
* @param string $captcha_type
|
||||
* @param string $owner_id
|
||||
* @param string $name
|
||||
* @return CaptchaInstance
|
||||
* @throws DatabaseException
|
||||
* @throws InvalidCaptchaNameException
|
||||
*/
|
||||
public function createInstance(string $captcha_type, string $owner_id, string $name): CaptchaInstance
|
||||
{
|
||||
if(Validate::captchaInstanceName($name) == false)
|
||||
throw new InvalidCaptchaNameException('The given name must not be greater than 120 characters or less than 3 and must be alphanumeric.');
|
||||
|
||||
$CaptchaInstance = new CaptchaInstance();
|
||||
$CaptchaInstance->ID = Uuid::v4()->toRfc4122();
|
||||
$CaptchaInstance->Name = $name;
|
||||
$CaptchaInstance->SecretKey = Security::generateCaptchaInstanceSecret($CaptchaInstance->ID, $owner_id);
|
||||
$CaptchaInstance->CaptchaType = $captcha_type;
|
||||
$CaptchaInstance->OwnerID = $owner_id;
|
||||
$CaptchaInstance->Enabled = true;
|
||||
|
@ -34,14 +55,127 @@
|
|||
$CaptchaInstance->CreatedTimestamp = time();
|
||||
$CaptchaInstance->LastUpdatedTimestamp = time();
|
||||
|
||||
$Query = QUeryBuilder::insert_into('instances', [
|
||||
$Query = QueryBuilder::insert_into('instances', [
|
||||
'id' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->ID),
|
||||
'name' => $this->vcaptcha->getDatabase()->real_escape_string(urlencode($CaptchaInstance->Name)),
|
||||
'captcha_type' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->CaptchaType),
|
||||
'owner_id' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->OwnerID),
|
||||
'secret_key' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->SecretKey),
|
||||
'enabled' => (int)$CaptchaInstance->Enabled,
|
||||
'firewall_options' => $this->vcaptcha->getDatabase()->real_escape_string(ZiProto::encode($CaptchaInstance->FirewallOptions->toArray())),
|
||||
'created_timestamp' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->CreatedTimestamp),
|
||||
'last_updated_timestamp' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->LastUpdatedTimestamp)
|
||||
]);
|
||||
$QueryResults = $this->vcaptcha->getDatabase()->query($Query);
|
||||
|
||||
if($QueryResults == false)
|
||||
throw new DatabaseException($this->vcaptcha->getDatabase()->error, $Query, $this->vcaptcha->getDatabase()->errno);
|
||||
|
||||
return $CaptchaInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an existing instance from the database
|
||||
*
|
||||
* @param string $id
|
||||
* @return CaptchaInstance
|
||||
* @throws CaptchaInstanceNotFoundException
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function getInstance(string $id): CaptchaInstance
|
||||
{
|
||||
$Query = QueryBuilder::select('instances', [
|
||||
'id',
|
||||
'name',
|
||||
'captcha_type',
|
||||
'owner_id',
|
||||
'secret_key',
|
||||
'enabled',
|
||||
'firewall_options',
|
||||
'created_timestamp',
|
||||
'last_updated_timestamp'
|
||||
], 'id', $this->vcaptcha->getDatabase()->real_escape_string($id), null, null, 1);
|
||||
|
||||
$QueryResults = $this->vcaptcha->getDatabase()->query($Query);
|
||||
|
||||
if($QueryResults == false)
|
||||
throw new DatabaseException($this->vcaptcha->getDatabase()->error, $Query, $this->vcaptcha->getDatabase()->errno);
|
||||
|
||||
if($QueryResults->num_rows == 0)
|
||||
throw new CaptchaInstanceNotFoundException();
|
||||
|
||||
$Row = $QueryResults->fetch_array(MYSQLI_ASSOC);
|
||||
$Row['firewall_options'] = ZiProto::decode($Row['firewall_options']);
|
||||
$Row['name'] = urldecode($Row['name']);
|
||||
|
||||
return CaptchaInstance::fromArray($Row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of captcha sample instance objects associated with the owner ID
|
||||
*
|
||||
* @param string $owner_id
|
||||
* @return SampleCaptchaInstance[]
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function getInstances(string $owner_id): array
|
||||
{
|
||||
$Query = QueryBuilder::select('instances', [
|
||||
'id',
|
||||
'captcha_type',
|
||||
'name'
|
||||
], 'owner_id', $this->vcaptcha->getDatabase()->real_escape_string($owner_id));
|
||||
|
||||
$QueryResults = $this->vcaptcha->getDatabase()->query($Query);
|
||||
|
||||
if($QueryResults == false)
|
||||
throw new DatabaseException($this->vcaptcha->getDatabase()->error, $Query, $this->vcaptcha->getDatabase()->errno);
|
||||
|
||||
if($QueryResults->num_rows == 0)
|
||||
return [];
|
||||
|
||||
$ResultsArray = [];
|
||||
|
||||
while($Row = $QueryResults->fetch_assoc())
|
||||
{
|
||||
$Row['name'] = urldecode($Row['name']);
|
||||
$ResultsArray[] = SampleCaptchaInstance::fromArray($Row);
|
||||
}
|
||||
|
||||
return $ResultsArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing captcha instance in the database
|
||||
*
|
||||
* @param CaptchaInstance $captchaInstance
|
||||
* @return CaptchaInstance
|
||||
* @throws DatabaseException
|
||||
* @throws InvalidCaptchaNameException
|
||||
* @noinspection PhpCastIsUnnecessaryInspection
|
||||
*/
|
||||
public function updateInstance(CaptchaInstance $captchaInstance): CaptchaInstance
|
||||
{
|
||||
if(Validate::captchaInstanceName($captchaInstance->Name) == false)
|
||||
throw new InvalidCaptchaNameException('The given name must not be greater than 120 characters or less than 3 and must be alphanumeric.');
|
||||
|
||||
$captchaInstance->LastUpdatedTimestamp = time();
|
||||
$firewall_options = $captchaInstance->FirewallOptions->toArray()['firewall_options'];
|
||||
|
||||
$Query = QueryBuilder::update('instances', [
|
||||
'name' => $this->vcaptcha->getDatabase()->real_escape_string(urlencode($captchaInstance->Name)),
|
||||
'secret_key' => $this->vcaptcha->getDatabase()->real_escape_string($captchaInstance->SecretKey),
|
||||
'captcha_type' => $this->vcaptcha->getDatabase()->real_escape_string($captchaInstance->CaptchaType),
|
||||
'enabled' => (int)$captchaInstance->Enabled,
|
||||
'firewall_options' => $this->vcaptcha->getDatabase()->real_escape_string(ZiProto::encode($firewall_options)),
|
||||
'last_updated_timestamp' => (int)$captchaInstance->LastUpdatedTimestamp
|
||||
]);
|
||||
|
||||
$QueryResults = $this->vcaptcha->getDatabase()->query($Query);
|
||||
|
||||
if($QueryResults == false)
|
||||
throw new DatabaseException($this->vcaptcha->getDatabase()->error, $Query, $this->vcaptcha->getDatabase()->errno);
|
||||
|
||||
return $captchaInstance;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,13 @@
|
|||
*/
|
||||
public $OwnerID;
|
||||
|
||||
/**
|
||||
* The name of the captcha instance
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $Name;
|
||||
|
||||
/**
|
||||
* The secret key used for creating and validating
|
||||
*
|
||||
|
@ -104,7 +111,7 @@
|
|||
$CaptchaInstanceObject->SecretKey = $data['secret_key'];
|
||||
|
||||
if(isset($data['enabled']))
|
||||
$CaptchaInstanceObject->Enabled = $data['enabled'];
|
||||
$CaptchaInstanceObject->Enabled = (bool)$data['enabled'];
|
||||
|
||||
if(isset($data['captcha_type']))
|
||||
$CaptchaInstanceObject->CaptchaType = $data['captcha_type'];
|
||||
|
@ -113,10 +120,10 @@
|
|||
$CaptchaInstanceObject->FirewallOptions = FirewallOptions::fromArray($data['firewall_options']);
|
||||
|
||||
if(isset($data['last_updated_timestamp']))
|
||||
$CaptchaInstanceObject->LastUpdatedTimestamp = $data['last_updated_timestamp'];
|
||||
$CaptchaInstanceObject->LastUpdatedTimestamp = (int)$data['last_updated_timestamp'];
|
||||
|
||||
if(isset($data['created_timestamp']))
|
||||
$CaptchaInstanceObject->CreatedTimestamp = $data['created_timestamp'];
|
||||
$CaptchaInstanceObject->CreatedTimestamp = (int)$data['created_timestamp'];
|
||||
|
||||
return $CaptchaInstanceObject;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/** @noinspection PhpMissingFieldTypeInspection */
|
||||
|
||||
namespace vCaptcha\Objects;
|
||||
|
||||
class SampleCaptchaInstance
|
||||
{
|
||||
/**
|
||||
* The ID of the captcha instance
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $ID;
|
||||
|
||||
/**
|
||||
* The name of the captcha instance
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $Name;
|
||||
|
||||
/**
|
||||
* The type of captcha
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $CaptchaType;
|
||||
|
||||
/**
|
||||
* Indicates if the captcha instance is enabled or not
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $Enabled;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->ID,
|
||||
'name' => $this->Name,
|
||||
'captcha_type' => $this->CaptchaType,
|
||||
'enabled' => $this->Enabled
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs object from an array representation
|
||||
*
|
||||
* @param array $data
|
||||
* @return SampleCaptchaInstance
|
||||
*/
|
||||
public static function fromArray(array $data): SampleCaptchaInstance
|
||||
{
|
||||
$SampleCaptchaInstance = new SampleCaptchaInstance();
|
||||
|
||||
if(isset($data['id']))
|
||||
$SampleCaptchaInstance->ID = $data['id'];
|
||||
|
||||
if(isset($data['name']))
|
||||
$SampleCaptchaInstance->Name = $data['name'];
|
||||
|
||||
if(isset($data['captcha_type']))
|
||||
$SampleCaptchaInstance->CaptchaType = $data['captcha_type'];
|
||||
|
||||
if(isset($data['enabled']))
|
||||
$SampleCaptchaInstance->Enabled = (bool)$data['enabled'];
|
||||
|
||||
return $SampleCaptchaInstance;
|
||||
}
|
||||
}
|
|
@ -49,10 +49,34 @@
|
|||
"required": true,
|
||||
"file": "Abstracts/CaptchaType.php"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"file": "Classes/Validate.php"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"file": "Classes/Security.php"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"file": "vCaptcha.php"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"file": "Exceptions/DatabaseException.php"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"file": "Exceptions/CaptchaInstanceNotFoundException.php"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"file": "Exceptions/InvalidCaptchaNameException.php"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"file": "Objects/SampleCaptchaInstance.php"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"file": "Objects/CaptchaInstance.php"
|
||||
|
|
Loading…
Reference in New Issue