mirror of
https://github.com/appwrite/appwrite
synced 2026-05-22 16:38:32 +00:00
Merge pull request #3681 from appwrite/feat-missing-phone-param
Changed provider name from phone to sms and added phone param to create endpoints
This commit is contained in:
commit
e0abc972ff
22 changed files with 252 additions and 152 deletions
4
.env
4
.env
|
|
@ -56,8 +56,8 @@ _APP_SMTP_PORT=1025
|
|||
_APP_SMTP_SECURE=
|
||||
_APP_SMTP_USERNAME=
|
||||
_APP_SMTP_PASSWORD=
|
||||
_APP_PHONE_PROVIDER=phone://mock
|
||||
_APP_PHONE_FROM=+123456789
|
||||
_APP_SMS_PROVIDER=sms://mock
|
||||
_APP_SMS_FROM=+123456789
|
||||
_APP_STORAGE_LIMIT=30000000
|
||||
_APP_STORAGE_PREVIEW_LIMIT=20000000
|
||||
_APP_FUNCTIONS_SIZE_LIMIT=30000000
|
||||
|
|
|
|||
|
|
@ -220,8 +220,8 @@ ENV _APP_SERVER=swoole \
|
|||
_APP_SMTP_SECURE= \
|
||||
_APP_SMTP_USERNAME= \
|
||||
_APP_SMTP_PASSWORD= \
|
||||
_APP_PHONE_PROVIDER= \
|
||||
_APP_PHONE_FROM= \
|
||||
_APP_SMS_PROVIDER= \
|
||||
_APP_SMS_FROM= \
|
||||
_APP_FUNCTIONS_SIZE_LIMIT=30000000 \
|
||||
_APP_FUNCTIONS_TIMEOUT=900 \
|
||||
_APP_FUNCTIONS_CONTAINERS=10 \
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ return [
|
|||
],
|
||||
Exception::GENERAL_PHONE_DISABLED => [
|
||||
'name' => Exception::GENERAL_PHONE_DISABLED,
|
||||
'description' => 'Phone provider is not configured. Please check the _APP_PHONE_PROVIDER environment variable of your Appwrite server.',
|
||||
'description' => 'Phone provider is not configured. Please check the _APP_SMS_PROVIDER environment variable of your Appwrite server.',
|
||||
'code' => 503,
|
||||
],
|
||||
Exception::GENERAL_ARGUMENT_INVALID => [
|
||||
|
|
|
|||
|
|
@ -394,8 +394,8 @@ return [
|
|||
'description' => '',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => '_APP_PHONE_PROVIDER',
|
||||
'description' => "Provider used for delivering SMS for Phone authentication. Use the following format: 'phone://[USER]:[SECRET]@[PROVIDER]'. \n\nAvailable providers are twilio, text-magic and telesign.",
|
||||
'name' => '_APP_SMS_PROVIDER',
|
||||
'description' => "Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'. \n\nAvailable providers are twilio, text-magic and telesign.",
|
||||
'introduction' => '0.15.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
|
|
@ -403,7 +403,7 @@ return [
|
|||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_PHONE_FROM',
|
||||
'name' => '_APP_SMS_FROM',
|
||||
'description' => 'Phone number used for sending out messages. Must start with a leading \'+\' and maximum of 15 digits without spaces (+123456789).',
|
||||
'introduction' => '0.15.0',
|
||||
'default' => '',
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Phone;
|
||||
use Appwrite\SMS\Adapter\Mock;
|
||||
use Appwrite\Auth\Validator\Password;
|
||||
use Appwrite\Auth\Validator\Phone as ValidatorPhone;
|
||||
use Appwrite\Auth\Validator\Phone;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Mail;
|
||||
|
|
@ -863,7 +863,7 @@ App::post('/v1/account/sessions/phone')
|
|||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},email:{param-email}')
|
||||
->param('userId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('number', '', new ValidatorPhone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
|
||||
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
|
|
@ -871,9 +871,8 @@ App::post('/v1/account/sessions/phone')
|
|||
->inject('audits')
|
||||
->inject('events')
|
||||
->inject('messaging')
|
||||
->inject('phone')
|
||||
->action(function (string $userId, string $number, Request $request, Response $response, Document $project, Database $dbForProject, Audit $audits, Event $events, EventPhone $messaging, Phone $phone) {
|
||||
if (empty(App::getEnv('_APP_PHONE_PROVIDER'))) {
|
||||
->action(function (string $userId, string $phone, Request $request, Response $response, Document $project, Database $dbForProject, Audit $audits, Event $events, EventPhone $messaging) {
|
||||
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||
}
|
||||
|
||||
|
|
@ -881,7 +880,7 @@ App::post('/v1/account/sessions/phone')
|
|||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$user = $dbForProject->findOne('users', [new Query('phone', Query::TYPE_EQUAL, [$number])]);
|
||||
$user = $dbForProject->findOne('users', [new Query('phone', Query::TYPE_EQUAL, [$phone])]);
|
||||
|
||||
if (!$user) {
|
||||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
|
@ -901,7 +900,7 @@ App::post('/v1/account/sessions/phone')
|
|||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
'email' => null,
|
||||
'phone' => $number,
|
||||
'phone' => $phone,
|
||||
'emailVerification' => false,
|
||||
'phoneVerification' => false,
|
||||
'status' => true,
|
||||
|
|
@ -913,11 +912,11 @@ App::post('/v1/account/sessions/phone')
|
|||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $number])
|
||||
'search' => implode(' ', [$userId, $phone])
|
||||
])));
|
||||
}
|
||||
|
||||
$secret = $phone->generateSecretDigits();
|
||||
$secret = (App::getEnv('_APP_SMS_PROVIDER') === 'sms://mock') ? Mock::$digits : Auth::codeGenerator();
|
||||
|
||||
$expire = \time() + Auth::TOKEN_EXPIRATION_PHONE;
|
||||
|
||||
|
|
@ -941,7 +940,7 @@ App::post('/v1/account/sessions/phone')
|
|||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
$messaging
|
||||
->setRecipient($number)
|
||||
->setRecipient($phone)
|
||||
->setMessage($secret)
|
||||
->trigger();
|
||||
|
||||
|
|
@ -1582,7 +1581,7 @@ App::patch('/v1/account/phone')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_ACCOUNT)
|
||||
->param('number', '', new ValidatorPhone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
|
||||
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
|
||||
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
|
|
@ -2268,17 +2267,16 @@ App::post('/v1/account/verification/phone')
|
|||
->label('abuse-key', 'userId:{userId}')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('phone')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('audits')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->inject('messaging')
|
||||
->action(function (Request $request, Response $response, Phone $phone, Document $user, Database $dbForProject, Audit $audits, Event $events, Stats $usage, EventPhone $messaging) {
|
||||
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Audit $audits, Event $events, Stats $usage, EventPhone $messaging) {
|
||||
|
||||
if (empty(App::getEnv('_APP_PHONE_PROVIDER'))) {
|
||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED);
|
||||
}
|
||||
|
||||
if (empty($user->getAttribute('phone'))) {
|
||||
|
|
@ -2291,7 +2289,7 @@ App::post('/v1/account/verification/phone')
|
|||
|
||||
$verificationSecret = Auth::tokenGenerator();
|
||||
|
||||
$secret = $phone->generateSecretDigits();
|
||||
$secret = (App::getEnv('_APP_SMS_PROVIDER') === 'sms://mock') ? Mock::$digits : Auth::codeGenerator();
|
||||
$expire = \time() + Auth::TOKEN_EXPIRATION_CONFIRM;
|
||||
|
||||
$verification = new Document([
|
||||
|
|
|
|||
|
|
@ -31,24 +31,30 @@ use MaxMind\Db\Reader;
|
|||
use Utopia\Validator\Integer;
|
||||
|
||||
/** TODO: Remove function when we move to using utopia/platform */
|
||||
function createUser(string $hash, mixed $hashOptions, string $userId, string $email, string $password, string $name, Database $dbForProject, Stats $usage, Event $events): Document
|
||||
function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Database $dbForProject, Stats $usage, Event $events): Document
|
||||
{
|
||||
$hashOptionsObject = (\is_string($hashOptions)) ? \json_decode($hashOptions, true) : $hashOptions; // Cast to JSON array
|
||||
$email = \strtolower($email);
|
||||
|
||||
if (!empty($email)) {
|
||||
$email = \strtolower($email);
|
||||
}
|
||||
|
||||
try {
|
||||
$userId = $userId == 'unique()' ? $dbForProject->getId() : $userId;
|
||||
|
||||
$user = $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'phone' => $phone,
|
||||
'phoneVerification' => false,
|
||||
'status' => true,
|
||||
'password' => $hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password,
|
||||
'password' => (!empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null,
|
||||
'hash' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO : $hash,
|
||||
'hashOptions' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO_OPTIONS : $hashOptions,
|
||||
'passwordUpdate' => \time(),
|
||||
'passwordUpdate' => (!empty($password)) ? \time() : 0,
|
||||
'registration' => \time(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
|
|
@ -56,7 +62,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, string $em
|
|||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name])
|
||||
'search' => implode(' ', [$userId, $email, $phone, $name])
|
||||
]));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||
|
|
@ -86,15 +92,16 @@ App::post('/v1/users')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('password', '', new Password(), 'Plain text user password. Must be at least 8 chars.')
|
||||
->param('email', null, new Email(), 'User email.', true)
|
||||
->param('phone', null, new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
|
||||
->param('password', null, new Password(), 'Plain text user password. Must be at least 8 chars.', true)
|
||||
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
$user = createUser('plaintext', '{}', $userId, $email, $password, $name, $dbForProject, $usage, $events);
|
||||
->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
$user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $dbForProject, $usage, $events);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
|
|
@ -121,7 +128,7 @@ App::post('/v1/users/bcrypt')
|
|||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
$user = createUser('bcrypt', '{}', $userId, $email, $password, $name, $dbForProject, $usage, $events);
|
||||
$user = createUser('bcrypt', '{}', $userId, $email, $password, null, $name, $dbForProject, $usage, $events);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
|
|
@ -148,7 +155,7 @@ App::post('/v1/users/md5')
|
|||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
$user = createUser('md5', '{}', $userId, $email, $password, $name, $dbForProject, $usage, $events);
|
||||
$user = createUser('md5', '{}', $userId, $email, $password, null, $name, $dbForProject, $usage, $events);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
|
|
@ -175,7 +182,7 @@ App::post('/v1/users/argon2')
|
|||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
$user = createUser('argon2', '{}', $userId, $email, $password, $name, $dbForProject, $usage, $events);
|
||||
$user = createUser('argon2', '{}', $userId, $email, $password, null, $name, $dbForProject, $usage, $events);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
|
|
@ -209,7 +216,7 @@ App::post('/v1/users/sha')
|
|||
$options = '{"version":"' . $passwordVersion . '"}';
|
||||
}
|
||||
|
||||
$user = createUser('sha', $options, $userId, $email, $password, $name, $dbForProject, $usage, $events);
|
||||
$user = createUser('sha', $options, $userId, $email, $password, null, $name, $dbForProject, $usage, $events);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
|
|
@ -236,7 +243,7 @@ App::post('/v1/users/phpass')
|
|||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
$user = createUser('phpass', '{}', $userId, $email, $password, $name, $dbForProject, $usage, $events);
|
||||
$user = createUser('phpass', '{}', $userId, $email, $password, null, $name, $dbForProject, $usage, $events);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
|
|
@ -276,7 +283,7 @@ App::post('/v1/users/scrypt')
|
|||
'length' => $passwordLength
|
||||
];
|
||||
|
||||
$user = createUser('scrypt', \json_encode($options), $userId, $email, $password, $name, $dbForProject, $usage, $events);
|
||||
$user = createUser('scrypt', \json_encode($options), $userId, $email, $password, null, $name, $dbForProject, $usage, $events);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
|
|
@ -306,7 +313,7 @@ App::post('/v1/users/scrypt-modified')
|
|||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function (string $userId, string $email, string $password, string $passwordSalt, string $passwordSaltSeparator, string $passwordSignerKey, string $name, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
$user = createUser('scryptMod', '{"signerKey":"' . $passwordSignerKey . '","saltSeparator":"' . $passwordSaltSeparator . '","salt":"' . $passwordSalt . '"}', $userId, $email, $password, $name, $dbForProject, $usage, $events);
|
||||
$user = createUser('scryptMod', '{"signerKey":"' . $passwordSignerKey . '","saltSeparator":"' . $passwordSaltSeparator . '","salt":"' . $passwordSalt . '"}', $userId, $email, $password, null, $name, $dbForProject, $usage, $events);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
|
|
|
|||
16
app/init.php
16
app/init.php
|
|
@ -23,12 +23,12 @@ use Ahc\Jwt\JWT;
|
|||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Phone\Mock;
|
||||
use Appwrite\Auth\Phone\Telesign;
|
||||
use Appwrite\Auth\Phone\TextMagic;
|
||||
use Appwrite\Auth\Phone\Twilio;
|
||||
use Appwrite\Auth\Phone\Msg91;
|
||||
use Appwrite\Auth\Phone\Vonage;
|
||||
use Appwrite\SMS\Adapter\Mock;
|
||||
use Appwrite\SMS\Adapter\Telesign;
|
||||
use Appwrite\SMS\Adapter\TextMagic;
|
||||
use Appwrite\SMS\Adapter\Twilio;
|
||||
use Appwrite\SMS\Adapter\Msg91;
|
||||
use Appwrite\SMS\Adapter\Vonage;
|
||||
use Appwrite\DSN\DSN;
|
||||
use Appwrite\Event\Audit;
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
|
|
@ -982,8 +982,8 @@ App::setResource('geodb', function ($register) {
|
|||
return $register->get('geodb');
|
||||
}, ['register']);
|
||||
|
||||
App::setResource('phone', function () {
|
||||
$dsn = new DSN(App::getEnv('_APP_PHONE_PROVIDER'));
|
||||
App::setResource('sms', function () {
|
||||
$dsn = new DSN(App::getEnv('_APP_SMS_PROVIDER'));
|
||||
$user = $dsn->getUser();
|
||||
$secret = $dsn->getPassword();
|
||||
|
||||
|
|
|
|||
|
|
@ -149,8 +149,8 @@ services:
|
|||
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_PHONE_PROVIDER
|
||||
- _APP_PHONE_SECRET
|
||||
- _APP_SMS_PROVIDER
|
||||
- _APP_SMS_FROM
|
||||
|
||||
appwrite-realtime:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
|
|
@ -514,8 +514,8 @@ services:
|
|||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_PHONE_PROVIDER
|
||||
- _APP_PHONE_FROM
|
||||
- _APP_SMS_PROVIDER
|
||||
- _APP_SMS_FROM
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
|
|
@ -595,7 +595,7 @@ services:
|
|||
- _APP_REDIS_PASS
|
||||
|
||||
mariadb:
|
||||
image: mariadb:10.8.3 # fix issues when upgrading using: mysql_upgrade -u root -p
|
||||
image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
|
||||
container_name: appwrite-mariadb
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Auth\Phone;
|
||||
use Appwrite\Auth\Phone\Mock;
|
||||
use Appwrite\Auth\Phone\Telesign;
|
||||
use Appwrite\Auth\Phone\TextMagic;
|
||||
use Appwrite\Auth\Phone\Twilio;
|
||||
use Appwrite\Auth\Phone\Msg91;
|
||||
use Appwrite\Auth\Phone\Vonage;
|
||||
use Appwrite\Auth\SMS;
|
||||
use Appwrite\SMS\Adapter\Mock;
|
||||
use Appwrite\SMS\Adapter\Telesign;
|
||||
use Appwrite\SMS\Adapter\TextMagic;
|
||||
use Appwrite\SMS\Adapter\Twilio;
|
||||
use Appwrite\SMS\Adapter\Msg91;
|
||||
use Appwrite\SMS\Adapter\Vonage;
|
||||
use Appwrite\DSN\DSN;
|
||||
use Appwrite\Resque\Worker;
|
||||
use Utopia\App;
|
||||
|
|
@ -19,7 +19,7 @@ Console::success(APP_NAME . ' messaging worker v1 has started' . "\n");
|
|||
|
||||
class MessagingV1 extends Worker
|
||||
{
|
||||
protected ?Phone $phone = null;
|
||||
protected ?SMS $sms = null;
|
||||
protected ?string $from = null;
|
||||
|
||||
public function getName(): string
|
||||
|
|
@ -29,11 +29,11 @@ class MessagingV1 extends Worker
|
|||
|
||||
public function init(): void
|
||||
{
|
||||
$dsn = new DSN(App::getEnv('_APP_PHONE_PROVIDER'));
|
||||
$dsn = new DSN(App::getEnv('_APP_SMS_PROVIDER'));
|
||||
$user = $dsn->getUser();
|
||||
$secret = $dsn->getPassword();
|
||||
|
||||
$this->phone = match ($dsn->getHost()) {
|
||||
$this->sms = match ($dsn->getHost()) {
|
||||
'mock' => new Mock('', ''), // used for tests
|
||||
'twilio' => new Twilio($user, $secret),
|
||||
'text-magic' => new TextMagic($user, $secret),
|
||||
|
|
@ -43,12 +43,12 @@ class MessagingV1 extends Worker
|
|||
default => null
|
||||
};
|
||||
|
||||
$this->from = App::getEnv('_APP_PHONE_FROM');
|
||||
$this->from = App::getEnv('_APP_SMS_FROM');
|
||||
}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
if (empty(App::getEnv('_APP_PHONE_PROVIDER'))) {
|
||||
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
||||
Console::info('Skipped sms processing. No Phone provider has been set.');
|
||||
return;
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ class MessagingV1 extends Worker
|
|||
$message = $this->args['message'];
|
||||
|
||||
try {
|
||||
$this->phone->send($this->from, $recipient, $message);
|
||||
$this->sms->send($this->from, $recipient, $message);
|
||||
} catch (\Exception $error) {
|
||||
throw new Exception('Error sending message: ' . $error->getMessage(), 500);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,8 +175,8 @@ services:
|
|||
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_PHONE_PROVIDER
|
||||
- _APP_PHONE_SECRET
|
||||
- _APP_SMS_PROVIDER
|
||||
- _APP_SMS_FROM
|
||||
|
||||
appwrite-realtime:
|
||||
entrypoint: realtime
|
||||
|
|
@ -545,8 +545,8 @@ services:
|
|||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_PHONE_PROVIDER
|
||||
- _APP_PHONE_FROM
|
||||
- _APP_SMS_PROVIDER
|
||||
- _APP_SMS_FROM
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
|
|
@ -638,7 +638,7 @@ services:
|
|||
- _APP_REDIS_PASS
|
||||
|
||||
mariadb:
|
||||
image: mariadb:10.8.3 # fix issues when upgrading using: mysql_upgrade -u root -p
|
||||
image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
|
||||
container_name: appwrite-mariadb
|
||||
<<: *x-logging
|
||||
networks:
|
||||
|
|
|
|||
|
|
@ -256,8 +256,6 @@ class Auth
|
|||
* @param int $length
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function passwordGenerator(int $length = 20): string
|
||||
{
|
||||
|
|
@ -272,14 +270,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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
24
src/Appwrite/SMS/Adapter/Mock.php
Normal file
24
src/Appwrite/SMS/Adapter/Mock.php
Normal file
|
|
@ -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
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Tests\E2E\Services\Account;
|
||||
|
||||
use Appwrite\Auth\Phone\Mock;
|
||||
use Appwrite\SMS\Adapter\Mock;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
|
|
@ -690,7 +690,7 @@ class AccountCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => 'unique()',
|
||||
'number' => $number,
|
||||
'phone' => $number,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
|
@ -713,7 +713,7 @@ class AccountCustomClientTest extends Scope
|
|||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$data['token'] = Mock::$defaultDigits;
|
||||
$data['token'] = Mock::$digits;
|
||||
$data['id'] = $userId;
|
||||
$data['number'] = $number;
|
||||
|
||||
|
|
@ -869,7 +869,7 @@ class AccountCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'number' => $newPhone,
|
||||
'phone' => $newPhone,
|
||||
'password' => 'new-password'
|
||||
]);
|
||||
|
||||
|
|
@ -949,7 +949,7 @@ class AccountCustomClientTest extends Scope
|
|||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'userId' => $id,
|
||||
'secret' => Mock::$defaultDigits,
|
||||
'secret' => Mock::$digits,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
|
@ -964,7 +964,7 @@ class AccountCustomClientTest extends Scope
|
|||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'userId' => 'ewewe',
|
||||
'secret' => Mock::$defaultDigits,
|
||||
'secret' => Mock::$digits,
|
||||
]);
|
||||
|
||||
$this->assertEquals(404, $response['headers']['status-code']);
|
||||
|
|
|
|||
|
|
@ -264,11 +264,104 @@ trait UsersBase
|
|||
$this->assertEquals($response['body']['userId'], 'scrypt-modified');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests all optional parameters of createUser (email, phone, anonymous..)
|
||||
*
|
||||
* @depends testCreateUser
|
||||
*/
|
||||
public function testCreateUserTypes(array $data): void
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
|
||||
// Email + password
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'unique()',
|
||||
'email' => 'emailuser@appwrite.io',
|
||||
'password' => 'emailUserPassword',
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($response['body']['email']);
|
||||
$this->assertNotEmpty($response['body']['password']);
|
||||
$this->assertEmpty($response['body']['phone']);
|
||||
|
||||
// Phone
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'unique()',
|
||||
'phone' => '+123456789012',
|
||||
]);
|
||||
|
||||
$this->assertEmpty($response['body']['email']);
|
||||
$this->assertEmpty($response['body']['password']);
|
||||
$this->assertNotEmpty($response['body']['phone']);
|
||||
|
||||
// Anonymous
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'unique()',
|
||||
]);
|
||||
|
||||
$this->assertEmpty($response['body']['email']);
|
||||
$this->assertEmpty($response['body']['password']);
|
||||
$this->assertEmpty($response['body']['phone']);
|
||||
|
||||
// Email-only
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'unique()',
|
||||
'email' => 'emailonlyuser@appwrite.io',
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($response['body']['email']);
|
||||
$this->assertEmpty($response['body']['password']);
|
||||
$this->assertEmpty($response['body']['phone']);
|
||||
|
||||
// Password-only
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'unique()',
|
||||
'password' => 'passwordOnlyUser',
|
||||
]);
|
||||
|
||||
$this->assertEmpty($response['body']['email']);
|
||||
$this->assertNotEmpty($response['body']['password']);
|
||||
$this->assertEmpty($response['body']['phone']);
|
||||
|
||||
// Password and phone
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'unique()',
|
||||
'password' => 'passwordOnlyUser',
|
||||
'phone' => '+123456789013',
|
||||
]);
|
||||
|
||||
$this->assertEmpty($response['body']['email']);
|
||||
$this->assertNotEmpty($response['body']['password']);
|
||||
$this->assertNotEmpty($response['body']['phone']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateUser
|
||||
*/
|
||||
public function testListUsers(array $data): void
|
||||
{
|
||||
$totalUsers = 15;
|
||||
|
||||
/**
|
||||
* Test for SUCCESS listUsers
|
||||
*/
|
||||
|
|
@ -280,7 +373,7 @@ trait UsersBase
|
|||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount(9, $response['body']['users']);
|
||||
$this->assertCount($totalUsers, $response['body']['users']);
|
||||
|
||||
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
||||
$this->assertEquals($response['body']['users'][1]['$id'], 'user1');
|
||||
|
|
@ -295,7 +388,7 @@ trait UsersBase
|
|||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount(8, $response['body']['users']);
|
||||
$this->assertCount($totalUsers - 1, $response['body']['users']);
|
||||
$this->assertEquals($response['body']['users'][0]['$id'], 'user1');
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
|
|
|
|||
|
|
@ -189,6 +189,14 @@ class AuthTest extends TestCase
|
|||
$this->assertEquals(\mb_strlen(Auth::tokenGenerator(5)), 10);
|
||||
}
|
||||
|
||||
public function testCodeGenerator(): void
|
||||
{
|
||||
$this->assertEquals(6, \strlen(Auth::codeGenerator()));
|
||||
$this->assertEquals(\mb_strlen(Auth::codeGenerator(256)), 256);
|
||||
$this->assertEquals(\mb_strlen(Auth::codeGenerator(10)), 10);
|
||||
$this->assertTrue(is_numeric(Auth::codeGenerator(5)));
|
||||
}
|
||||
|
||||
public function testSessionVerify(): void
|
||||
{
|
||||
$secret = 'secret1';
|
||||
|
|
|
|||
Loading…
Reference in a new issue