Merge branch '1.6.x' into fix-messaging-test-for-audits

This commit is contained in:
Darshan 2025-01-10 10:26:30 +05:30 committed by GitHub
commit 4bfd132eee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 140 additions and 20 deletions

View file

@ -17,6 +17,7 @@ use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Appwrite\Hooks\Hooks;
use Appwrite\Network\Validator\Email;
@ -27,7 +28,9 @@ use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Identities;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use libphonenumber\PhoneNumberUtil;
use MaxMind\Db\Reader;
use Utopia\Abuse\Abuse;
use Utopia\App;
use Utopia\Audit\Audit as EventAudit;
use Utopia\Config\Config;
@ -2315,7 +2318,10 @@ App::post('/v1/account/tokens/phone')
->inject('queueForEvents')
->inject('queueForMessaging')
->inject('locale')
->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale) {
->inject('timelimit')
->inject('queueForUsage')
->inject('plan')
->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, Usage $queueForUsage, array $plan) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@ -2452,6 +2458,27 @@ App::post('/v1/account/tokens/phone')
->setMessage($messageDoc)
->setRecipients([$phone])
->setProviderType(MESSAGE_TYPE_SMS);
if (isset($plan['authPhone'])) {
$timelimit = $timelimit('organization:{organizationId}', $plan['authPhone'], 30 * 24 * 60 * 60); // 30 days
$timelimit
->setParam('{organizationId}', $project->getAttribute('teamId'));
$abuse = new Abuse($timelimit);
if ($abuse->check() && System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') {
$helper = PhoneNumberUtil::getInstance();
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
}
}
// Set to unhashed secret for events and server responses
@ -3465,7 +3492,10 @@ App::post('/v1/account/verification/phone')
->inject('queueForMessaging')
->inject('project')
->inject('locale')
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale) {
->inject('timelimit')
->inject('queueForUsage')
->inject('plan')
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, Usage $queueForUsage, array $plan) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@ -3548,6 +3578,27 @@ App::post('/v1/account/verification/phone')
->setMessage($messageDoc)
->setRecipients([$user->getAttribute('phone')])
->setProviderType(MESSAGE_TYPE_SMS);
if (isset($plan['authPhone'])) {
$timelimit = $timelimit('organization:{organizationId}', $plan['authPhone'], 30 * 24 * 60 * 60); // 30 days
$timelimit
->setParam('{organizationId}', $project->getAttribute('teamId'));
$abuse = new Abuse($timelimit);
if ($abuse->check() && System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') {
$helper = PhoneNumberUtil::getInstance();
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
}
}
// Set to unhashed secret for events and server responses
@ -4026,7 +4077,10 @@ App::post('/v1/account/mfa/challenge')
->inject('queueForEvents')
->inject('queueForMessaging')
->inject('queueForMails')
->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails) {
->inject('timelimit')
->inject('queueForUsage')
->inject('plan')
->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, callable $timelimit, Usage $queueForUsage, array $plan) {
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
$code = Auth::codeGenerator();
@ -4074,6 +4128,7 @@ App::post('/v1/account/mfa/challenge')
$message = $message->render();
$phone = $user->getAttribute('phone');
$queueForMessaging
->setType(MESSAGE_SEND_TYPE_INTERNAL)
->setMessage(new Document([
@ -4082,8 +4137,29 @@ App::post('/v1/account/mfa/challenge')
'content' => $code,
],
]))
->setRecipients([$user->getAttribute('phone')])
->setRecipients([$phone])
->setProviderType(MESSAGE_TYPE_SMS);
if (isset($plan['authPhone'])) {
$timelimit = $timelimit('organization:{organizationId}', $plan['authPhone'], 30 * 24 * 60 * 60); // 30 days
$timelimit
->setParam('{organizationId}', $project->getAttribute('teamId'));
$abuse = new Abuse($timelimit);
if ($abuse->check() && System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') {
$helper = PhoneNumberUtil::getInstance();
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
}
break;
case Type::EMAIL:
if (empty(System::getEnv('_APP_SMTP_HOST'))) {

View file

@ -8,6 +8,7 @@ use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Platform\Workers\Deletes;
@ -17,7 +18,9 @@ use Appwrite\Utopia\Database\Validator\Queries\Memberships;
use Appwrite\Utopia\Database\Validator\Queries\Teams;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use libphonenumber\PhoneNumberUtil;
use MaxMind\Db\Reader;
use Utopia\Abuse\Abuse;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Config\Config;
@ -423,7 +426,10 @@ App::post('/v1/teams/:teamId/memberships')
->inject('queueForMails')
->inject('queueForMessaging')
->inject('queueForEvents')
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents) {
->inject('timelimit')
->inject('queueForUsage')
->inject('plan')
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, Usage $queueForUsage, array $plan) {
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@ -691,6 +697,27 @@ App::post('/v1/teams/:teamId/memberships')
->setMessage($messageDoc)
->setRecipients([$phone])
->setProviderType('SMS');
if (isset($plan['authPhone'])) {
$timelimit = $timelimit('organization:{organizationId}', $plan['authPhone'], 30 * 24 * 60 * 60); // 30 days
$timelimit
->setParam('{organizationId}', $project->getAttribute('teamId'));
$abuse = new Abuse($timelimit);
if ($abuse->check() && System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') {
$helper = PhoneNumberUtil::getInstance();
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
}
}
}

View file

@ -506,6 +506,7 @@ App::init()
->setMode($mode)
->setUserAgent($request->getUserAgent(''))
->setIP($request->getIP())
->setHostname($request->getHostname())
->setEvent($route->getLabel('audits.event', ''))
->setProject($project)
->setUser($user);

View file

@ -11,6 +11,7 @@ class Audit extends Event
protected string $mode = '';
protected string $userAgent = '';
protected string $ip = '';
protected string $hostname = '';
public function __construct(protected Connection $connection)
{
@ -113,6 +114,30 @@ class Audit extends Event
return $this->ip;
}
/**
* Set the hostname.
*
* @param string $hostname
*
* @return self
*/
public function setHostname(string $hostname): self
{
$this->hostname = $hostname;
return $this;
}
/**
* Get the hostname.
*
* @return string
*/
public function getHostname(): string
{
return $this->hostname;
}
/**
* Executes the event and sends it to the audit worker.
*
@ -136,6 +161,7 @@ class Audit extends Event
'ip' => $this->ip,
'userAgent' => $this->userAgent,
'event' => $this->event,
'hostname' => $this->hostname
]);
}
}

View file

@ -27,7 +27,7 @@ class Messaging extends Event
/**
* Sets type for the build event.
*
* @param string $type Can be `MESSAGE_TYPE_INTERNAL` or `MESSAGE_TYPE_EXTERNAL`.
* @param string $type Can be `MESSAGE_SEND_TYPE_INTERNAL` or `MESSAGE_SEND_TYPE_EXTERNAL`.
* @return self
*/
public function setType(string $type): self

View file

@ -96,7 +96,7 @@ class Messaging extends Action
$message = new Document($payload['message'] ?? []);
$recipients = $payload['recipients'] ?? [];
$this->sendInternalSMSMessage($message, $project, $recipients, $queueForUsage, $log);
$this->sendInternalSMSMessage($message, $project, $recipients, $log);
break;
case MESSAGE_SEND_TYPE_EXTERNAL:
$message = $dbForProject->getDocument('messages', $payload['messageId']);
@ -377,7 +377,7 @@ class Messaging extends Action
}
}
private function sendInternalSMSMessage(Document $message, Document $project, array $recipients, Usage $queueForUsage, Log $log): void
private function sendInternalSMSMessage(Document $message, Document $project, array $recipients, Log $log): void
{
if (empty(System::getEnv('_APP_SMS_PROVIDER')) || empty(System::getEnv('_APP_SMS_FROM'))) {
throw new \Exception('Skipped SMS processing. Missing "_APP_SMS_PROVIDER" or "_APP_SMS_FROM" environment variables.');
@ -456,24 +456,14 @@ class Messaging extends Action
$adapter->getMaxMessagesPerRequest()
);
batch(\array_map(function ($batch) use ($message, $provider, $adapter, $project, $queueForUsage) {
return function () use ($batch, $message, $provider, $adapter, $project, $queueForUsage) {
batch(\array_map(function ($batch) use ($message, $provider, $adapter) {
return function () use ($batch, $message, $provider, $adapter) {
$message->setAttribute('to', $batch);
$data = $this->buildSmsMessage($message, $provider);
try {
$adapter->send($data);
$countryCode = $adapter->getCountryCode($message['to'][0] ?? '');
if (!empty($countryCode)) {
$queueForUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
$queueForUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
} catch (\Throwable $th) {
throw new \Exception('Failed sending to targets with error: ' . $th->getMessage());
}