mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch 'origin/datetime-attributes' into refactor-permissions-inc-console-fix
# Conflicts: # .gitignore # app/config/collections.php # app/controllers/api/account.php # app/controllers/api/databases.php # app/controllers/api/storage.php # app/controllers/api/users.php # composer.lock # tests/e2e/Services/Account/AccountCustomClientTest.php
This commit is contained in:
+120
-11
@@ -2,6 +2,13 @@
|
||||
|
||||
namespace Appwrite\Auth;
|
||||
|
||||
use Appwrite\Auth\Hash\Argon2;
|
||||
use Appwrite\Auth\Hash\Bcrypt;
|
||||
use Appwrite\Auth\Hash\Md5;
|
||||
use Appwrite\Auth\Hash\Phpass;
|
||||
use Appwrite\Auth\Hash\Scrypt;
|
||||
use Appwrite\Auth\Hash\Scryptmodified;
|
||||
use Appwrite\Auth\Hash\Sha;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Role;
|
||||
@@ -9,6 +16,20 @@ use Utopia\Database\Validator\Authorization;
|
||||
|
||||
class Auth
|
||||
{
|
||||
public const SUPPORTED_ALGOS = [
|
||||
'argon2',
|
||||
'bcrypt',
|
||||
'md5',
|
||||
'sha',
|
||||
'phpass',
|
||||
'scrypt',
|
||||
'scryptMod',
|
||||
'plaintext'
|
||||
];
|
||||
|
||||
public const DEFAULT_ALGO = 'argon2';
|
||||
public const DEFAULT_ALGO_OPTIONS = ['memoryCost' => 2048, 'timeCost' => 4, 'threads' => 3];
|
||||
|
||||
/**
|
||||
* User Roles.
|
||||
*/
|
||||
@@ -135,26 +156,98 @@ class Auth
|
||||
*
|
||||
* One way string hashing for user passwords
|
||||
*
|
||||
* @param $string
|
||||
* @param string $string
|
||||
* @param string $algo hashing algorithm to use
|
||||
* @param array $options algo-specific options
|
||||
*
|
||||
* @return bool|string|null
|
||||
*/
|
||||
public static function passwordHash($string)
|
||||
public static function passwordHash(string $string, string $algo, array $options = [])
|
||||
{
|
||||
return \password_hash($string, PASSWORD_BCRYPT, array('cost' => 8));
|
||||
// Plain text not supported, just an alias. Switch to recommended algo
|
||||
if ($algo === 'plaintext') {
|
||||
$algo = Auth::DEFAULT_ALGO;
|
||||
$options = Auth::DEFAULT_ALGO_OPTIONS;
|
||||
}
|
||||
|
||||
if (!\in_array($algo, Auth::SUPPORTED_ALGOS)) {
|
||||
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
|
||||
}
|
||||
|
||||
switch ($algo) {
|
||||
case 'argon2':
|
||||
$hasher = new Argon2($options);
|
||||
return $hasher->hash($string);
|
||||
case 'bcrypt':
|
||||
$hasher = new Bcrypt($options);
|
||||
return $hasher->hash($string);
|
||||
case 'md5':
|
||||
$hasher = new Md5($options);
|
||||
return $hasher->hash($string);
|
||||
case 'sha':
|
||||
$hasher = new Sha($options);
|
||||
return $hasher->hash($string);
|
||||
case 'phpass':
|
||||
$hasher = new Phpass($options);
|
||||
return $hasher->hash($string);
|
||||
case 'scrypt':
|
||||
$hasher = new Scrypt($options);
|
||||
return $hasher->hash($string);
|
||||
case 'scryptMod':
|
||||
$hasher = new Scryptmodified($options);
|
||||
return $hasher->hash($string);
|
||||
default:
|
||||
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Password verify.
|
||||
*
|
||||
* @param $plain
|
||||
* @param $hash
|
||||
* @param string $plain
|
||||
* @param string $hash
|
||||
* @param string $algo hashing algorithm used to hash
|
||||
* @param array $options algo-specific options
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function passwordVerify($plain, $hash)
|
||||
public static function passwordVerify(string $plain, string $hash, string $algo, array $options = [])
|
||||
{
|
||||
return \password_verify($plain, $hash);
|
||||
// Plain text not supported, just an alias. Switch to recommended algo
|
||||
if ($algo === 'plaintext') {
|
||||
$algo = Auth::DEFAULT_ALGO;
|
||||
$options = Auth::DEFAULT_ALGO_OPTIONS;
|
||||
}
|
||||
|
||||
if (!\in_array($algo, Auth::SUPPORTED_ALGOS)) {
|
||||
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
|
||||
}
|
||||
|
||||
switch ($algo) {
|
||||
case 'argon2':
|
||||
$hasher = new Argon2($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'bcrypt':
|
||||
$hasher = new Bcrypt($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'md5':
|
||||
$hasher = new Md5($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'sha':
|
||||
$hasher = new Sha($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'phpass':
|
||||
$hasher = new Phpass($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'scrypt':
|
||||
$hasher = new Scrypt($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
case 'scryptMod':
|
||||
$hasher = new Scryptmodified($options);
|
||||
return $hasher->verify($plain, $hash);
|
||||
default:
|
||||
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,8 +258,6 @@ class Auth
|
||||
* @param int $length
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function passwordGenerator(int $length = 20): string
|
||||
{
|
||||
@@ -181,14 +272,32 @@ class Auth
|
||||
* @param int $length
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function tokenGenerator(int $length = 128): string
|
||||
{
|
||||
return \bin2hex(\random_bytes($length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Code Generator.
|
||||
*
|
||||
* Generate random code string
|
||||
*
|
||||
* @param int $length
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function codeGenerator(int $length = 6): string
|
||||
{
|
||||
$value = '';
|
||||
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$value .= random_int(0, 9);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify token and check that its not expired.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth;
|
||||
|
||||
abstract class Hash
|
||||
{
|
||||
/**
|
||||
* @var array $options Hashing-algo specific options
|
||||
*/
|
||||
protected array $options = [];
|
||||
|
||||
/**
|
||||
* @param array $options Hashing-algo specific options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->setOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set hashing algo options
|
||||
*
|
||||
* @param array $options Hashing-algo specific options
|
||||
*/
|
||||
public function setOptions(array $options): self
|
||||
{
|
||||
$this->options = \array_merge([], $this->getDefaultOptions(), $options);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hashing algo options
|
||||
*
|
||||
* @return array $options Hashing-algo specific options
|
||||
*/
|
||||
public function getOptions(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password Input password to hash
|
||||
*
|
||||
* @return string hash
|
||||
*/
|
||||
abstract public function hash(string $password): string;
|
||||
|
||||
/**
|
||||
* @param string $password Input password to validate
|
||||
* @param string $hash Hash to verify password against
|
||||
*
|
||||
* @return boolean true if password matches hash
|
||||
*/
|
||||
abstract public function verify(string $password, string $hash): bool;
|
||||
|
||||
/**
|
||||
* Get default options for specific hashing algo
|
||||
*
|
||||
* @return array options named array
|
||||
*/
|
||||
abstract public function getDefaultOptions(): array;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Hash;
|
||||
|
||||
use Appwrite\Auth\Hash;
|
||||
|
||||
/*
|
||||
* Argon2 accepted options:
|
||||
* int threads
|
||||
* int time_cost
|
||||
* int memory_cost
|
||||
*
|
||||
* Reference: https://www.php.net/manual/en/function.password-hash.php#example-983
|
||||
*/
|
||||
class Argon2 extends Hash
|
||||
{
|
||||
/**
|
||||
* @param string $password Input password to hash
|
||||
*
|
||||
* @return string hash
|
||||
*/
|
||||
public function hash(string $password): string
|
||||
{
|
||||
return \password_hash($password, PASSWORD_ARGON2ID, $this->getOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password Input password to validate
|
||||
* @param string $hash Hash to verify password against
|
||||
*
|
||||
* @return boolean true if password matches hash
|
||||
*/
|
||||
public function verify(string $password, string $hash): bool
|
||||
{
|
||||
return \password_verify($password, $hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default options for specific hashing algo
|
||||
*
|
||||
* @return array options named array
|
||||
*/
|
||||
public function getDefaultOptions(): array
|
||||
{
|
||||
return ['memory_cost' => 65536, 'time_cost' => 4, 'threads' => 3];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Hash;
|
||||
|
||||
use Appwrite\Auth\Hash;
|
||||
|
||||
/*
|
||||
* Bcrypt accepted options:
|
||||
* int cost
|
||||
* string? salt; auto-generated if empty
|
||||
*
|
||||
* Reference: https://www.php.net/manual/en/password.constants.php
|
||||
*/
|
||||
class Bcrypt extends Hash
|
||||
{
|
||||
/**
|
||||
* @param string $password Input password to hash
|
||||
*
|
||||
* @return string hash
|
||||
*/
|
||||
public function hash(string $password): string
|
||||
{
|
||||
return \password_hash($password, PASSWORD_BCRYPT, $this->getOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password Input password to validate
|
||||
* @param string $hash Hash to verify password against
|
||||
*
|
||||
* @return boolean true if password matches hash
|
||||
*/
|
||||
public function verify(string $password, string $hash): bool
|
||||
{
|
||||
return \password_verify($password, $hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default options for specific hashing algo
|
||||
*
|
||||
* @return array options named array
|
||||
*/
|
||||
public function getDefaultOptions(): array
|
||||
{
|
||||
return [ 'cost' => 8 ];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Hash;
|
||||
|
||||
use Appwrite\Auth\Hash;
|
||||
|
||||
/*
|
||||
* MD5 does not accept any options.
|
||||
*
|
||||
* Reference: https://www.php.net/manual/en/function.md5.php
|
||||
*/
|
||||
class Md5 extends Hash
|
||||
{
|
||||
/**
|
||||
* @param string $password Input password to hash
|
||||
*
|
||||
* @return string hash
|
||||
*/
|
||||
public function hash(string $password): string
|
||||
{
|
||||
return \md5($password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password Input password to validate
|
||||
* @param string $hash Hash to verify password against
|
||||
*
|
||||
* @return boolean true if password matches hash
|
||||
*/
|
||||
public function verify(string $password, string $hash): bool
|
||||
{
|
||||
return $this->hash($password) === $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default options for specific hashing algo
|
||||
*
|
||||
* @return array options named array
|
||||
*/
|
||||
public function getDefaultOptions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Portable PHP password hashing framework.
|
||||
* source Version 0.5 / genuine.
|
||||
* Written by Solar Designer <solar at openwall.com> in 2004-2017 and placed in
|
||||
* the public domain. Revised in subsequent years, still public domain.
|
||||
* There's absolutely no warranty.
|
||||
* The homepage URL for the source framework is: http://www.openwall.com/phpass/
|
||||
* Please be sure to update the Version line if you edit this file in any way.
|
||||
* It is suggested that you leave the main version number intact, but indicate
|
||||
* your project name (after the slash) and add your own revision information.
|
||||
* Please do not change the "private" password hashing method implemented in
|
||||
* here, thereby making your hashes incompatible. However, if you must, please
|
||||
* change the hash type identifier (the "$P$") to something different.
|
||||
* Obviously, since this code is in the public domain, the above are not
|
||||
* requirements (there can be none), but merely suggestions.
|
||||
*
|
||||
* @author Solar Designer <solar@openwall.com>
|
||||
* @copyright Copyright (C) 2017 All rights reserved.
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Appwrite\Auth\Hash;
|
||||
|
||||
use Appwrite\Auth\Hash;
|
||||
|
||||
/*
|
||||
* PHPass accepted options:
|
||||
* int iteration_count_log2; The Logarithmic cost value used when generating hash values indicating the number of rounds used to generate hashes
|
||||
* string portable_hashes
|
||||
* string random_state; The cached random state
|
||||
*
|
||||
* Reference: https://github.com/photodude/phpass
|
||||
*/
|
||||
class Phpass extends Hash
|
||||
{
|
||||
/**
|
||||
* Alphabet used in itoa64 conversions.
|
||||
*
|
||||
* @var string
|
||||
* @since 0.1.0
|
||||
*/
|
||||
protected string $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
/**
|
||||
* Get default options for specific hashing algo
|
||||
*
|
||||
* @return array options named array
|
||||
*/
|
||||
public function getDefaultOptions(): array
|
||||
{
|
||||
$randomState = \microtime();
|
||||
if (\function_exists('getmypid')) {
|
||||
$randomState .= getmypid();
|
||||
}
|
||||
|
||||
return ['iteration_count_log2' => 8, 'portable_hashes' => false, 'random_state' => $randomState];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password Input password to hash
|
||||
*
|
||||
* @return string hash
|
||||
*/
|
||||
public function hash(string $password): string
|
||||
{
|
||||
$options = $this->getDefaultOptions();
|
||||
|
||||
$random = '';
|
||||
if (CRYPT_BLOWFISH === 1 && !$options['portable_hashes']) {
|
||||
$random = $this->getRandomBytes(16, $options);
|
||||
$hash = crypt($password, $this->gensaltBlowfish($random, $options));
|
||||
if (strlen($hash) === 60) {
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
if (strlen($random) < 6) {
|
||||
$random = $this->getRandomBytes(6, $options);
|
||||
}
|
||||
$hash = $this->cryptPrivate($password, $this->gensaltPrivate($random, $options));
|
||||
if (strlen($hash) === 34) {
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returning '*' on error is safe here, but would _not_ be safe
|
||||
* in a crypt(3)-like function used _both_ for generating new
|
||||
* hashes and for validating passwords against existing hashes.
|
||||
*/
|
||||
return '*';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password Input password to validate
|
||||
* @param string $hash Hash to verify password against
|
||||
*
|
||||
* @return boolean true if password matches hash
|
||||
*/
|
||||
public function verify(string $password, string $hash): bool
|
||||
{
|
||||
$verificationHash = $this->cryptPrivate($password, $hash);
|
||||
if ($verificationHash[0] === '*') {
|
||||
$verificationHash = crypt($password, $hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is not constant-time. In order to keep the code simple,
|
||||
* for timing safety we currently rely on the salts being
|
||||
* unpredictable, which they are at least in the non-fallback
|
||||
* cases (that is, when we use /dev/urandom and bcrypt).
|
||||
*/
|
||||
return $hash === $verificationHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $count
|
||||
*
|
||||
* @return String $output
|
||||
* @since 0.1.0
|
||||
* @throws Exception Thows an Exception if the $count parameter is not a positive integer.
|
||||
*/
|
||||
protected function getRandomBytes(int $count, array $options): string
|
||||
{
|
||||
if (!is_int($count) || $count < 1) {
|
||||
throw new \Exception('Argument count must be a positive integer');
|
||||
}
|
||||
$output = '';
|
||||
if (@is_readable('/dev/urandom') && ($fh = @fopen('/dev/urandom', 'rb'))) {
|
||||
$output = fread($fh, $count);
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
if (strlen($output) < $count) {
|
||||
$output = '';
|
||||
|
||||
for ($i = 0; $i < $count; $i += 16) {
|
||||
$options['iteration_count_log2'] = md5(microtime() . $options['iteration_count_log2']);
|
||||
$output .= md5($options['iteration_count_log2'], true);
|
||||
}
|
||||
|
||||
$output = substr($output, 0, $count);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String $input
|
||||
* @param int $count
|
||||
*
|
||||
* @return String $output
|
||||
* @since 0.1.0
|
||||
* @throws Exception Thows an Exception if the $count parameter is not a positive integer.
|
||||
*/
|
||||
protected function encode64($input, $count)
|
||||
{
|
||||
if (!is_int($count) || $count < 1) {
|
||||
throw new \Exception('Argument count must be a positive integer');
|
||||
}
|
||||
$output = '';
|
||||
$i = 0;
|
||||
do {
|
||||
$value = ord($input[$i++]);
|
||||
$output .= $this->itoa64[$value & 0x3f];
|
||||
if ($i < $count) {
|
||||
$value |= ord($input[$i]) << 8;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 6) & 0x3f];
|
||||
if ($i++ >= $count) {
|
||||
break;
|
||||
}
|
||||
if ($i < $count) {
|
||||
$value |= ord($input[$i]) << 16;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 12) & 0x3f];
|
||||
if ($i++ >= $count) {
|
||||
break;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 18) & 0x3f];
|
||||
} while ($i < $count);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String $input
|
||||
*
|
||||
* @return String $output
|
||||
* @since 0.1.0
|
||||
*/
|
||||
private function gensaltPrivate($input, $options)
|
||||
{
|
||||
$output = '$P$';
|
||||
$output .= $this->itoa64[min($options['iteration_count_log2'] + ((PHP_VERSION >= '5') ? 5 : 3), 30)];
|
||||
$output .= $this->encode64($input, 6);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String $password
|
||||
* @param String $setting
|
||||
*
|
||||
* @return String $output
|
||||
* @since 0.1.0
|
||||
*/
|
||||
private function cryptPrivate($password, $setting)
|
||||
{
|
||||
$output = '*0';
|
||||
if (substr($setting, 0, 2) === $output) {
|
||||
$output = '*1';
|
||||
}
|
||||
$id = substr($setting, 0, 3);
|
||||
// We use "$P$", phpBB3 uses "$H$" for the same thing
|
||||
if ($id !== '$P$' && $id !== '$H$') {
|
||||
return $output;
|
||||
}
|
||||
$count_log2 = strpos($this->itoa64, $setting[3]);
|
||||
if ($count_log2 < 7 || $count_log2 > 30) {
|
||||
return $output;
|
||||
}
|
||||
$count = 1 << $count_log2;
|
||||
$salt = substr($setting, 4, 8);
|
||||
if (strlen($salt) !== 8) {
|
||||
return $output;
|
||||
}
|
||||
/**
|
||||
* We were kind of forced to use MD5 here since it's the only
|
||||
* cryptographic primitive that was available in all versions of PHP
|
||||
* in use. To implement our own low-level crypto in PHP
|
||||
* would have result in much worse performance and
|
||||
* consequently in lower iteration counts and hashes that are
|
||||
* quicker to crack (by non-PHP code).
|
||||
*/
|
||||
$hash = md5($salt . $password, true);
|
||||
do {
|
||||
$hash = md5($hash . $password, true);
|
||||
} while (--$count);
|
||||
$output = substr($setting, 0, 12);
|
||||
$output .= $this->encode64($hash, 16);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String $input
|
||||
*
|
||||
* @return String $output
|
||||
* @since 0.1.0
|
||||
*/
|
||||
private function gensaltBlowfish($input, $options)
|
||||
{
|
||||
/**
|
||||
* This one needs to use a different order of characters and a
|
||||
* different encoding scheme from the one in encode64() above.
|
||||
* We care because the last character in our encoded string will
|
||||
* only represent 2 bits. While two known implementations of
|
||||
* bcrypt will happily accept and correct a salt string which
|
||||
* has the 4 unused bits set to non-zero, we do not want to take
|
||||
* chances and we also do not want to waste an additional byte
|
||||
* of entropy.
|
||||
*/
|
||||
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
$output = '$2a$';
|
||||
$output .= chr(ord('0') + $options['iteration_count_log2'] / 10);
|
||||
$output .= chr(ord('0') + $options['iteration_count_log2'] % 10);
|
||||
$output .= '$';
|
||||
$i = 0;
|
||||
do {
|
||||
$c1 = ord($input[$i++]);
|
||||
$output .= $itoa64[$c1 >> 2];
|
||||
$c1 = ($c1 & 0x03) << 4;
|
||||
if ($i >= 16) {
|
||||
$output .= $itoa64[$c1];
|
||||
break;
|
||||
}
|
||||
$c2 = ord($input[$i++]);
|
||||
$c1 |= $c2 >> 4;
|
||||
$output .= $itoa64[$c1];
|
||||
$c1 = ($c2 & 0x0f) << 2;
|
||||
$c2 = ord($input[$i++]);
|
||||
$c1 |= $c2 >> 6;
|
||||
$output .= $itoa64[$c1];
|
||||
$output .= $itoa64[$c2 & 0x3f];
|
||||
} while (1);
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Hash;
|
||||
|
||||
use Appwrite\Auth\Hash;
|
||||
|
||||
/*
|
||||
* Scrypt accepted options:
|
||||
* string? salt; auto-generated if empty
|
||||
* int costCpu
|
||||
* int costMemory
|
||||
* int costParallel
|
||||
* int length
|
||||
*
|
||||
* Reference: https://github.com/DomBlack/php-scrypt/blob/master/scrypt.php#L112-L116
|
||||
*/
|
||||
class Scrypt extends Hash
|
||||
{
|
||||
/**
|
||||
* @param string $password Input password to hash
|
||||
*
|
||||
* @return string hash
|
||||
*/
|
||||
public function hash(string $password): string
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
|
||||
return \scrypt($password, $options['salt'], $options['costCpu'], $options['costMemory'], $options['costParallel'], $options['length']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password Input password to validate
|
||||
* @param string $hash Hash to verify password against
|
||||
*
|
||||
* @return boolean true if password matches hash
|
||||
*/
|
||||
public function verify(string $password, string $hash): bool
|
||||
{
|
||||
return $hash === $this->hash($password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default options for specific hashing algo
|
||||
*
|
||||
* @return array options named array
|
||||
*/
|
||||
public function getDefaultOptions(): array
|
||||
{
|
||||
return [ 'costCpu' => 8, 'costMemory' => 14, 'costParallel' => 1, 'length' => 64 ];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Hash;
|
||||
|
||||
use Appwrite\Auth\Hash;
|
||||
|
||||
/*
|
||||
* This is Scrypt hash with some additional steps added by Google.
|
||||
*
|
||||
* string salt
|
||||
* string saltSeparator
|
||||
* strin signerKey
|
||||
*
|
||||
* Reference: https://github.com/DomBlack/php-scrypt/blob/master/scrypt.php#L112-L116
|
||||
*/
|
||||
class Scryptmodified extends Hash
|
||||
{
|
||||
/**
|
||||
* @param string $password Input password to hash
|
||||
*
|
||||
* @return string hash
|
||||
*/
|
||||
public function hash(string $password): string
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
|
||||
$derivedKeyBytes = $this->generateDerivedKey($password);
|
||||
$signerKeyBytes = \base64_decode($options['signerKey']);
|
||||
|
||||
$hashedPassword = $this->hashKeys($signerKeyBytes, $derivedKeyBytes);
|
||||
|
||||
return \base64_encode($hashedPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password Input password to validate
|
||||
* @param string $hash Hash to verify password against
|
||||
*
|
||||
* @return boolean true if password matches hash
|
||||
*/
|
||||
public function verify(string $password, string $hash): bool
|
||||
{
|
||||
return $this->hash($password) === $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default options for specific hashing algo
|
||||
*
|
||||
* @return array options named array
|
||||
*/
|
||||
public function getDefaultOptions(): array
|
||||
{
|
||||
return [ ];
|
||||
}
|
||||
|
||||
private function generateDerivedKey(string $password)
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
|
||||
$saltBytes = \base64_decode($options['salt']);
|
||||
$saltSeparatorBytes = \base64_decode($options['saltSeparator']);
|
||||
|
||||
$derivedKey = \scrypt(\utf8_encode($password), $saltBytes . $saltSeparatorBytes, 16384, 8, 1, 64);
|
||||
$derivedKey = \hex2bin($derivedKey);
|
||||
|
||||
return $derivedKey;
|
||||
}
|
||||
|
||||
private function hashKeys($signerKeyBytes, $derivedKeyBytes): string
|
||||
{
|
||||
$key = \substr($derivedKeyBytes, 0, 32);
|
||||
|
||||
$iv = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
|
||||
$hash = \openssl_encrypt($signerKeyBytes, 'aes-256-ctr', $key, OPENSSL_RAW_DATA, $iv);
|
||||
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Hash;
|
||||
|
||||
use Appwrite\Auth\Hash;
|
||||
|
||||
/*
|
||||
* SHA accepted options:
|
||||
* string? version. Allowed:
|
||||
* - Version 1: sha1
|
||||
* - Version 2: sha224, sha256, sha384, sha512/224, sha512/256, sha512
|
||||
* - Version 3: sha3-224, sha3-256, sha3-384, sha3-512
|
||||
*
|
||||
* Reference: https://www.php.net/manual/en/function.hash-algos.php
|
||||
*/
|
||||
class Sha extends Hash
|
||||
{
|
||||
/**
|
||||
* @param string $password Input password to hash
|
||||
*
|
||||
* @return string hash
|
||||
*/
|
||||
public function hash(string $password): string
|
||||
{
|
||||
$algo = $this->getOptions()['version'];
|
||||
|
||||
return \hash($algo, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password Input password to validate
|
||||
* @param string $hash Hash to verify password against
|
||||
*
|
||||
* @return boolean true if password matches hash
|
||||
*/
|
||||
public function verify(string $password, string $hash): bool
|
||||
{
|
||||
return $this->hash($password) === $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default options for specific hashing algo
|
||||
*
|
||||
* @return array options named array
|
||||
*/
|
||||
public function getDefaultOptions(): array
|
||||
{
|
||||
return [ 'version' => 'sha3-512' ];
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
|
||||
class Mock extends Phone
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static string $defaultDigits = '123456';
|
||||
|
||||
/**
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function send(string $from, string $to, string $message): void
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $digits
|
||||
* @return string
|
||||
*/
|
||||
public function generateSecretDigits(int $digits = 6): string
|
||||
{
|
||||
return self::$defaultDigits;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ class Delete extends Event
|
||||
{
|
||||
protected string $type = '';
|
||||
protected ?Document $document = null;
|
||||
protected ?string $resource = null;
|
||||
protected ?string $datetime = null;
|
||||
protected ?string $datetime1d = null;
|
||||
protected ?string $datetime30m = null;
|
||||
@@ -90,6 +91,29 @@ class Delete extends Event
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource for the delete event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResource(): string
|
||||
{
|
||||
return $this->resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource for the delete event.
|
||||
*
|
||||
* @param string $resource
|
||||
* @return self
|
||||
*/
|
||||
public function setResource(string $resource): self
|
||||
{
|
||||
$this->resource = $resource;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set document for the delete event.
|
||||
*
|
||||
@@ -100,6 +124,7 @@ class Delete extends Event
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes this event and sends it to the deletes worker.
|
||||
*
|
||||
@@ -112,9 +137,10 @@ class Delete extends Event
|
||||
'project' => $this->project,
|
||||
'type' => $this->type,
|
||||
'document' => $this->document,
|
||||
'resource' => $this->resource,
|
||||
'datetime' => $this->datetime,
|
||||
'datetime1d' => $this->datetime1d,
|
||||
'datetime30m' => $this->datetime30m
|
||||
'datetime30m' => $this->datetime30m,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth;
|
||||
namespace Appwrite\SMS;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
|
||||
abstract class Phone
|
||||
abstract class Adapter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@@ -69,20 +67,9 @@ abstract class Phone
|
||||
\curl_close($ch);
|
||||
|
||||
if ($code >= 400) {
|
||||
throw new Exception($response);
|
||||
throw new \Exception($response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate 6 random digits for phone verification.
|
||||
*
|
||||
* @param int $digits
|
||||
* @return string
|
||||
*/
|
||||
public function generateSecretDigits(int $digits = 6): string
|
||||
{
|
||||
return substr(str_shuffle("0123456789"), 0, $digits);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\SMS\Adapter;
|
||||
|
||||
use Appwrite\SMS\Adapter;
|
||||
|
||||
class Mock extends Adapter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static string $digits = '123456';
|
||||
|
||||
/**
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function send(string $from, string $to, string $message): void
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
namespace Appwrite\SMS\Adapter;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
use Appwrite\SMS\Adapter;
|
||||
|
||||
// Reference Material
|
||||
// https://docs.msg91.com/p/tf9GTextN/e/Irz7-x1PK/MSG91
|
||||
|
||||
class Msg91 extends Phone
|
||||
class Msg91 extends Adapter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@@ -16,10 +16,10 @@ class Msg91 extends Phone
|
||||
|
||||
/**
|
||||
* For Flow based sending SMS sender ID should not be set in flow
|
||||
* In environment _APP_PHONE_PROVIDER format is 'phone://[senderID]:[authKey]@msg91'.
|
||||
* _APP_PHONE_FROM value is flow ID created in Msg91
|
||||
* Eg. _APP_PHONE_PROVIDER = phone://DINESH:5e1e93cad6fc054d8e759a5b@msg91
|
||||
* _APP_PHONE_FROM = 3968636f704b303135323339
|
||||
* In environment _APP_SMS_PROVIDER format is 'sms://[senderID]:[authKey]@msg91'.
|
||||
* _APP_SMS_FROM value is flow ID created in Msg91
|
||||
* Eg. _APP_SMS_PROVIDER = sms://DINESH:5e1e93cad6fc054d8e759a5b@msg91
|
||||
* _APP_SMS_FROM = 3968636f704b303135323339
|
||||
* @param string $from-> utilized from for flow id
|
||||
* @param string $to
|
||||
* @param string $message
|
||||
@@ -1,13 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
namespace Appwrite\SMS\Adapter;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
use Appwrite\SMS\Adapter;
|
||||
|
||||
// Reference Material
|
||||
// https://developer.telesign.com/enterprise/docs/sms-api-send-an-sms
|
||||
|
||||
class Telesign extends Phone
|
||||
class Telesign extends Adapter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@@ -1,13 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
namespace Appwrite\SMS\Adapter;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
use Appwrite\SMS\Adapter;
|
||||
|
||||
// Reference Material
|
||||
// https://www.textmagic.com/docs/api/start/
|
||||
|
||||
class TextMagic extends Phone
|
||||
class TextMagic extends Adapter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@@ -1,13 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
namespace Appwrite\SMS\Adapter;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
use Appwrite\SMS\Adapter;
|
||||
|
||||
// Reference Material
|
||||
// https://www.twilio.com/docs/sms/api
|
||||
|
||||
class Twilio extends Phone
|
||||
class Twilio extends Adapter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@@ -1,13 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\Phone;
|
||||
namespace Appwrite\SMS\Adapter;
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
use Appwrite\SMS\Adapter;
|
||||
|
||||
// Reference Material
|
||||
// https://developer.vonage.com/api/sms
|
||||
|
||||
class Vonage extends Phone
|
||||
class Vonage extends Adapter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@@ -17,15 +17,32 @@ class OpenAPI3 extends Format
|
||||
protected function getNestedModels(Model $model, array &$usedModels): void
|
||||
{
|
||||
foreach ($model->getRules() as $rule) {
|
||||
if (
|
||||
in_array($model->getType(), $usedModels)
|
||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float', 'double'])
|
||||
) {
|
||||
$usedModels[] = $rule['type'];
|
||||
foreach ($this->models as $m) {
|
||||
if ($m->getType() === $rule['type']) {
|
||||
$this->getNestedModels($m, $usedModels);
|
||||
return;
|
||||
if (!in_array($model->getType(), $usedModels)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\is_array($rule['type'])) {
|
||||
foreach ($rule['type'] as $ruleType) {
|
||||
if (!in_array($ruleType, ['string', 'integer', 'boolean', 'json', 'float', 'double'])) {
|
||||
$usedModels[] = $ruleType;
|
||||
|
||||
foreach ($this->models as $m) {
|
||||
if ($m->getType() === $ruleType) {
|
||||
$this->getNestedModels($m, $usedModels);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float', 'double'])) {
|
||||
$usedModels[] = $rule['type'];
|
||||
|
||||
foreach ($this->models as $m) {
|
||||
if ($m->getType() === $rule['type']) {
|
||||
$this->getNestedModels($m, $usedModels);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,15 +19,32 @@ class Swagger2 extends Format
|
||||
protected function getNestedModels(Model $model, array &$usedModels): void
|
||||
{
|
||||
foreach ($model->getRules() as $rule) {
|
||||
if (
|
||||
in_array($model->getType(), $usedModels)
|
||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float', 'double'])
|
||||
) {
|
||||
$usedModels[] = $rule['type'];
|
||||
foreach ($this->models as $m) {
|
||||
if ($m->getType() === $rule['type']) {
|
||||
$this->getNestedModels($m, $usedModels);
|
||||
return;
|
||||
if (!in_array($model->getType(), $usedModels)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\is_array($rule['type'])) {
|
||||
foreach ($rule['type'] as $ruleType) {
|
||||
if (!in_array($ruleType, ['string', 'integer', 'boolean', 'json', 'float'])) {
|
||||
$usedModels[] = $ruleType;
|
||||
|
||||
foreach ($this->models as $m) {
|
||||
if ($m->getType() === $ruleType) {
|
||||
$this->getNestedModels($m, $usedModels);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])) {
|
||||
$usedModels[] = $rule['type'];
|
||||
|
||||
foreach ($this->models as $m) {
|
||||
if ($m->getType() === $rule['type']) {
|
||||
$this->getNestedModels($m, $usedModels);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,14 @@ use Swoole\Http\Response as SwooleHTTPResponse;
|
||||
use Utopia\Database\Document;
|
||||
use Appwrite\Utopia\Response\Filter;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
use Appwrite\Utopia\Response\Model\Account;
|
||||
use Appwrite\Utopia\Response\Model\AlgoArgon2;
|
||||
use Appwrite\Utopia\Response\Model\AlgoBcrypt;
|
||||
use Appwrite\Utopia\Response\Model\AlgoMd5;
|
||||
use Appwrite\Utopia\Response\Model\AlgoPhpass;
|
||||
use Appwrite\Utopia\Response\Model\AlgoScrypt;
|
||||
use Appwrite\Utopia\Response\Model\AlgoScryptModified;
|
||||
use Appwrite\Utopia\Response\Model\AlgoSha;
|
||||
use Appwrite\Utopia\Response\Model\None;
|
||||
use Appwrite\Utopia\Response\Model\Any;
|
||||
use Appwrite\Utopia\Response\Model\Attribute;
|
||||
@@ -120,6 +128,7 @@ class Response extends SwooleResponse
|
||||
public const MODEL_ATTRIBUTE_DATETIME = 'attributeDatetime';
|
||||
|
||||
// Users
|
||||
public const MODEL_ACCOUNT = 'account';
|
||||
public const MODEL_USER = 'user';
|
||||
public const MODEL_USER_LIST = 'userList';
|
||||
public const MODEL_SESSION = 'session';
|
||||
@@ -128,6 +137,15 @@ class Response extends SwooleResponse
|
||||
public const MODEL_JWT = 'jwt';
|
||||
public const MODEL_PREFERENCES = 'preferences';
|
||||
|
||||
// Users password algos
|
||||
public const MODEL_ALGO_MD5 = 'algoMd5';
|
||||
public const MODEL_ALGO_SHA = 'algoSha';
|
||||
public const MODEL_ALGO_SCRYPT = 'algoScrypt';
|
||||
public const MODEL_ALGO_SCRYPT_MODIFIED = 'algoScryptModified';
|
||||
public const MODEL_ALGO_BCRYPT = 'algoBcrypt';
|
||||
public const MODEL_ALGO_ARGON2 = 'algoArgon2';
|
||||
public const MODEL_ALGO_PHPASS = 'algoPhpass';
|
||||
|
||||
// Storage
|
||||
public const MODEL_FILE = 'file';
|
||||
public const MODEL_FILE_LIST = 'fileList';
|
||||
@@ -262,6 +280,14 @@ class Response extends SwooleResponse
|
||||
->setModel(new ModelDocument())
|
||||
->setModel(new Log())
|
||||
->setModel(new User())
|
||||
->setModel(new AlgoMd5())
|
||||
->setModel(new AlgoSha())
|
||||
->setModel(new AlgoPhpass())
|
||||
->setModel(new AlgoBcrypt())
|
||||
->setModel(new AlgoScrypt())
|
||||
->setModel(new AlgoScryptModified())
|
||||
->setModel(new AlgoArgon2())
|
||||
->setModel(new Account())
|
||||
->setModel(new Preferences())
|
||||
->setModel(new Session())
|
||||
->setModel(new Token())
|
||||
@@ -452,6 +478,24 @@ class Response extends SwooleResponse
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output response
|
||||
*
|
||||
* Generate HTTP response output including the response header (+cookies) and body and prints them.
|
||||
*
|
||||
* @param string $body
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function file(string $body = ''): void
|
||||
{
|
||||
$this->payload = [
|
||||
'payload' => $body
|
||||
];
|
||||
|
||||
$this->send($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* YAML
|
||||
*
|
||||
@@ -483,7 +527,6 @@ class Response extends SwooleResponse
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to set a response filter
|
||||
*
|
||||
|
||||
@@ -93,6 +93,22 @@ abstract class Model
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an existing Rule
|
||||
* If rule exists, it will be removed
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $options
|
||||
*/
|
||||
protected function removeRule(string $key): self
|
||||
{
|
||||
if (isset($this->rules[$key])) {
|
||||
unset($this->rules[$key]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRequired()
|
||||
{
|
||||
$list = [];
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
|
||||
class Account extends User
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->removeRule('password')
|
||||
->removeRule('hash')
|
||||
->removeRule('hashOptions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Account';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ACCOUNT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class AlgoArgon2 extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// No options if imported. If hashed by Appwrite, following configuration is available:
|
||||
$this
|
||||
->addRule('memoryCost', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Memory used to compute hash.',
|
||||
'default' => '',
|
||||
'example' => 65536,
|
||||
])
|
||||
->addRule('timeCost', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Amount of time consumed to compute hash',
|
||||
'default' => '',
|
||||
'example' => 4,
|
||||
])
|
||||
->addRule('threads', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Number of threads used to compute hash.',
|
||||
'default' => '',
|
||||
'example' => 3,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AlgoArgon2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ALGO_ARGON2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class AlgoBcrypt extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// No options, because this can only be imported, and verifying doesnt require any configuration
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AlgoBcrypt';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ALGO_BCRYPT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class AlgoMd5 extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// No options, because this can only be imported, and verifying doesnt require any configuration
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AlgoMD5';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ALGO_MD5;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class AlgoPhpass extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// No options, because this can only be imported, and verifying doesnt require any configuration
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AlgoPHPass';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ALGO_PHPASS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class AlgoScrypt extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('costCpu', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'CPU complexity of computed hash.',
|
||||
'default' => '',
|
||||
'example' => 8,
|
||||
])
|
||||
->addRule('costMemory', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Memory complexity of computed hash.',
|
||||
'default' => '',
|
||||
'example' => 14,
|
||||
])
|
||||
->addRule('costParallel', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Parallelization of computed hash.',
|
||||
'default' => '',
|
||||
'example' => 1,
|
||||
])
|
||||
->addRule('length', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Length used to compute hash.',
|
||||
'default' => '',
|
||||
'example' => 1,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AlgoScrypt';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ALGO_SCRYPT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class AlgoScryptModified extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('salt', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Salt used to compute hash.',
|
||||
'default' => '',
|
||||
'example' => 'UxLMreBr6tYyjQ==',
|
||||
])
|
||||
->addRule('saltSeparator', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Separator used to compute hash.',
|
||||
'default' => '',
|
||||
'example' => 'Bw==',
|
||||
])
|
||||
->addRule('signerKey', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Key used to compute hash.',
|
||||
'default' => '',
|
||||
'example' => 'XyEKE9RcTDeLEsL/RjwPDBv/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ==',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AlgoScryptModified';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ALGO_SCRYPT_MODIFIED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class AlgoSha extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// No options, because this can only be imported, and verifying doesnt require any configuration
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AlgoSHA';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ALGO_SHA;
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,7 @@ class AttributeEmail extends Attribute
|
||||
'description' => 'String format.',
|
||||
'default' => 'email',
|
||||
'example' => 'email',
|
||||
'array' => false,
|
||||
'require' => true,
|
||||
'required' => true,
|
||||
])
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_STRING,
|
||||
|
||||
@@ -36,8 +36,7 @@ class AttributeEnum extends Attribute
|
||||
'description' => 'String format.',
|
||||
'default' => 'enum',
|
||||
'example' => 'enum',
|
||||
'array' => false,
|
||||
'require' => true,
|
||||
'required' => true,
|
||||
])
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_STRING,
|
||||
|
||||
@@ -29,8 +29,7 @@ class AttributeIP extends Attribute
|
||||
'description' => 'String format.',
|
||||
'default' => 'ip',
|
||||
'example' => 'ip',
|
||||
'array' => false,
|
||||
'require' => true,
|
||||
'required' => true,
|
||||
])
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_STRING,
|
||||
|
||||
@@ -29,7 +29,6 @@ class AttributeURL extends Attribute
|
||||
'description' => 'String format.',
|
||||
'default' => 'url',
|
||||
'example' => 'url',
|
||||
'array' => false,
|
||||
'required' => true,
|
||||
])
|
||||
->addRule('default', [
|
||||
|
||||
@@ -66,9 +66,15 @@ class Execution extends Model
|
||||
'default' => '',
|
||||
'example' => '',
|
||||
])
|
||||
->addRule('stdout', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'The script stdout output string. Logs the last 4,000 characters of the execution stdout output. This will return an empty string unless the response is returned using an API key or as part of a webhook payload.',
|
||||
'default' => '',
|
||||
'example' => '',
|
||||
])
|
||||
->addRule('stderr', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'The script stderr output string. Logs the last 4,000 characters of the execution stderr output',
|
||||
'description' => 'The script stderr output string. Logs the last 4,000 characters of the execution stderr output. This will return an empty string unless the response is returned using an API key or as part of a webhook payload.',
|
||||
'default' => '',
|
||||
'example' => '',
|
||||
])
|
||||
|
||||
@@ -35,6 +35,33 @@ class User extends Model
|
||||
'default' => '',
|
||||
'example' => 'John Doe',
|
||||
])
|
||||
->addRule('password', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Hashed user password.',
|
||||
'default' => '',
|
||||
'example' => '$argon2id$v=19$m=2048,t=4,p=3$aUZjLnliVWRINmFNTWMudg$5S+x+7uA31xFnrHFT47yFwcJeaP0w92L/4LdgrVRXxE',
|
||||
])
|
||||
->addRule('hash', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Password hashing algorithm.',
|
||||
'default' => '',
|
||||
'example' => 'argon2',
|
||||
])
|
||||
->addRule('hashOptions', [
|
||||
'type' => [
|
||||
Response::MODEL_ALGO_ARGON2,
|
||||
Response::MODEL_ALGO_SCRYPT,
|
||||
Response::MODEL_ALGO_SCRYPT_MODIFIED,
|
||||
Response::MODEL_ALGO_BCRYPT,
|
||||
Response::MODEL_ALGO_PHPASS,
|
||||
Response::MODEL_ALGO_SHA,
|
||||
Response::MODEL_ALGO_MD5, // keep least secure at the bottom. this order will be used in docs
|
||||
],
|
||||
'description' => 'Password hashing algorithm configuration.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass(),
|
||||
'array' => false,
|
||||
])
|
||||
->addRule('registration', [
|
||||
'type' => self::TYPE_DATETIME,
|
||||
'description' => 'User registration date in Datetime.',
|
||||
|
||||
Reference in New Issue
Block a user