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 msqg\QueryBuilder;
|
||||||
use Symfony\Component\Uid\Uuid;
|
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;
|
||||||
use vCaptcha\Objects\CaptchaInstance\FirewallOptions;
|
use vCaptcha\Objects\CaptchaInstance\FirewallOptions;
|
||||||
|
use vCaptcha\Objects\SampleCaptchaInstance;
|
||||||
use vCaptcha\vCaptcha;
|
use vCaptcha\vCaptcha;
|
||||||
use ZiProto\ZiProto;
|
use ZiProto\ZiProto;
|
||||||
|
|
||||||
|
@ -23,10 +29,25 @@
|
||||||
$this->vcaptcha = $vcaptcha;
|
$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 = new CaptchaInstance();
|
||||||
$CaptchaInstance->ID = Uuid::v4()->toRfc4122();
|
$CaptchaInstance->ID = Uuid::v4()->toRfc4122();
|
||||||
|
$CaptchaInstance->Name = $name;
|
||||||
|
$CaptchaInstance->SecretKey = Security::generateCaptchaInstanceSecret($CaptchaInstance->ID, $owner_id);
|
||||||
$CaptchaInstance->CaptchaType = $captcha_type;
|
$CaptchaInstance->CaptchaType = $captcha_type;
|
||||||
$CaptchaInstance->OwnerID = $owner_id;
|
$CaptchaInstance->OwnerID = $owner_id;
|
||||||
$CaptchaInstance->Enabled = true;
|
$CaptchaInstance->Enabled = true;
|
||||||
|
@ -34,14 +55,127 @@
|
||||||
$CaptchaInstance->CreatedTimestamp = time();
|
$CaptchaInstance->CreatedTimestamp = time();
|
||||||
$CaptchaInstance->LastUpdatedTimestamp = time();
|
$CaptchaInstance->LastUpdatedTimestamp = time();
|
||||||
|
|
||||||
$Query = QUeryBuilder::insert_into('instances', [
|
$Query = QueryBuilder::insert_into('instances', [
|
||||||
'id' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->ID),
|
'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),
|
'captcha_type' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->CaptchaType),
|
||||||
'owner_id' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->OwnerID),
|
'owner_id' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->OwnerID),
|
||||||
|
'secret_key' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->SecretKey),
|
||||||
'enabled' => (int)$CaptchaInstance->Enabled,
|
'enabled' => (int)$CaptchaInstance->Enabled,
|
||||||
'firewall_options' => $this->vcaptcha->getDatabase()->real_escape_string(ZiProto::encode($CaptchaInstance->FirewallOptions->toArray())),
|
'firewall_options' => $this->vcaptcha->getDatabase()->real_escape_string(ZiProto::encode($CaptchaInstance->FirewallOptions->toArray())),
|
||||||
'created_timestamp' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->CreatedTimestamp),
|
'created_timestamp' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->CreatedTimestamp),
|
||||||
'last_updated_timestamp' => $this->vcaptcha->getDatabase()->real_escape_string($CaptchaInstance->LastUpdatedTimestamp)
|
'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;
|
public $OwnerID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the captcha instance
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $Name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The secret key used for creating and validating
|
* The secret key used for creating and validating
|
||||||
*
|
*
|
||||||
|
@ -104,7 +111,7 @@
|
||||||
$CaptchaInstanceObject->SecretKey = $data['secret_key'];
|
$CaptchaInstanceObject->SecretKey = $data['secret_key'];
|
||||||
|
|
||||||
if(isset($data['enabled']))
|
if(isset($data['enabled']))
|
||||||
$CaptchaInstanceObject->Enabled = $data['enabled'];
|
$CaptchaInstanceObject->Enabled = (bool)$data['enabled'];
|
||||||
|
|
||||||
if(isset($data['captcha_type']))
|
if(isset($data['captcha_type']))
|
||||||
$CaptchaInstanceObject->CaptchaType = $data['captcha_type'];
|
$CaptchaInstanceObject->CaptchaType = $data['captcha_type'];
|
||||||
|
@ -113,10 +120,10 @@
|
||||||
$CaptchaInstanceObject->FirewallOptions = FirewallOptions::fromArray($data['firewall_options']);
|
$CaptchaInstanceObject->FirewallOptions = FirewallOptions::fromArray($data['firewall_options']);
|
||||||
|
|
||||||
if(isset($data['last_updated_timestamp']))
|
if(isset($data['last_updated_timestamp']))
|
||||||
$CaptchaInstanceObject->LastUpdatedTimestamp = $data['last_updated_timestamp'];
|
$CaptchaInstanceObject->LastUpdatedTimestamp = (int)$data['last_updated_timestamp'];
|
||||||
|
|
||||||
if(isset($data['created_timestamp']))
|
if(isset($data['created_timestamp']))
|
||||||
$CaptchaInstanceObject->CreatedTimestamp = $data['created_timestamp'];
|
$CaptchaInstanceObject->CreatedTimestamp = (int)$data['created_timestamp'];
|
||||||
|
|
||||||
return $CaptchaInstanceObject;
|
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,
|
"required": true,
|
||||||
"file": "Abstracts/CaptchaType.php"
|
"file": "Abstracts/CaptchaType.php"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"file": "Classes/Validate.php"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"file": "Classes/Security.php"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"required": true,
|
"required": true,
|
||||||
"file": "vCaptcha.php"
|
"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,
|
"required": true,
|
||||||
"file": "Objects/CaptchaInstance.php"
|
"file": "Objects/CaptchaInstance.php"
|
||||||
|
|
Loading…
Reference in New Issue