diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 3d7db8f457..499830100b 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -962,7 +962,7 @@ Http::patch('/v1/account/sessions/:sessionId') Http::post('/v1/account/sessions/email') ->alias('/v1/account/sessions') ->desc('Create email password session') - ->groups(['api', 'account', 'auth', 'session']) + ->groups(['api', 'account', 'auth', 'session', 'outgoingEmail']) ->label('event', 'users.[userId].sessions.[sessionId].create') ->label('scope', 'sessions.write') ->label('auth.type', 'email-password') @@ -1269,7 +1269,7 @@ Http::post('/v1/account/sessions/anonymous') Http::post('/v1/account/sessions/token') ->desc('Create session') ->label('event', 'users.[userId].sessions.[sessionId].create') - ->groups(['api', 'account', 'session']) + ->groups(['api', 'account', 'session', 'queueForMails']) ->label('scope', 'sessions.write') ->label('audits.event', 'session.create') ->label('audits.resource', 'user/{response.userId}') @@ -2115,7 +2115,7 @@ Http::get('/v1/account/tokens/oauth2/:provider') Http::post('/v1/account/tokens/magic-url') ->alias('/v1/account/sessions/magic-url') ->desc('Create magic URL token') - ->groups(['api', 'account', 'auth']) + ->groups(['api', 'account', 'auth', 'outgoingEmail']) ->label('scope', 'sessions.write') ->label('auth.type', 'magic-url') ->label('audits.event', 'session.create') @@ -2395,7 +2395,7 @@ Http::post('/v1/account/tokens/magic-url') Http::post('/v1/account/tokens/email') ->desc('Create email token (OTP)') - ->groups(['api', 'account', 'auth']) + ->groups(['api', 'account', 'auth', 'outgoingEmail']) ->label('scope', 'sessions.write') ->label('auth.type', 'email-otp') ->label('audits.event', 'session.create') @@ -2698,7 +2698,7 @@ Http::post('/v1/account/tokens/email') Http::put('/v1/account/sessions/magic-url') ->desc('Update magic URL session') ->label('event', 'users.[userId].sessions.[sessionId].create') - ->groups(['api', 'account', 'session']) + ->groups(['api', 'account', 'session', 'outgoingEmail']) ->label('scope', 'sessions.write') ->label('audits.event', 'session.create') ->label('audits.resource', 'user/{response.userId}') @@ -2748,7 +2748,7 @@ Http::put('/v1/account/sessions/magic-url') Http::put('/v1/account/sessions/phone') ->desc('Update phone session') ->label('event', 'users.[userId].sessions.[sessionId].create') - ->groups(['api', 'account', 'session']) + ->groups(['api', 'account', 'session', 'outgoingEmail']) ->label('scope', 'sessions.write') ->label('audits.event', 'session.create') ->label('audits.resource', 'user/{response.userId}') @@ -3575,7 +3575,7 @@ Http::patch('/v1/account/status') Http::post('/v1/account/recovery') ->desc('Create password recovery') - ->groups(['api', 'account']) + ->groups(['api', 'account', 'outgoingEmail']) ->label('scope', 'sessions.write') ->label('event', 'users.[userId].recovery.[tokenId].create') ->label('audits.event', 'recovery.create') @@ -3869,7 +3869,7 @@ Http::put('/v1/account/recovery') Http::post('/v1/account/verifications/email') ->alias('/v1/account/verification') ->desc('Create email verification') - ->groups(['api', 'account']) + ->groups(['api', 'account', 'outgoingEmail']) ->label('scope', 'account') ->label('event', 'users.[userId].verification.[tokenId].create') ->label('audits.event', 'verification.create') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 2fc20ba83f..8a19ce1e52 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1480,7 +1480,7 @@ Http::patch('/v1/projects/:projectId/smtp') Http::post('/v1/projects/:projectId/smtp/tests') ->desc('Create SMTP test') - ->groups(['api', 'projects']) + ->groups(['api', 'projects', 'outgoingEmail']) ->label('scope', 'projects.write') ->label('sdk', [ new Method( diff --git a/app/controllers/general.php b/app/controllers/general.php index 51cce37fee..b35f317990 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -37,6 +37,7 @@ use Executor\Executor; use MaxMind\Db\Reader; use Swoole\Http\Request as SwooleRequest; use Swoole\Table; +use Utopia\Abuse\Abuse; use Utopia\Bus\Bus; use Utopia\Config\Config; use Utopia\Console; @@ -1126,6 +1127,32 @@ Http::init() }); }); +/** + * Email worker abuse protection + */ +Http::init() + ->groups(['outgoingEmail']) + ->inject('project') + ->inject('timelimit') + ->action(function (Document $project, $timelimit) { + /* + * Abuse Check + */ + $abuseKey = 'initHook:outgoingEmail,projectId:{projectId}'; + + $timeLimit = $timelimit($abuseKey, limit: 1000, time: 3600); // 1000 emails per hour , per project + $timeLimit + ->setParam('{projectId}', $project->getId()); + + $abuse = new Abuse($timeLimit); + + $enabled = System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled'; + + if ($enabled && $abuse->check()) { + throw new AppwriteException(AppwriteException::GENERAL_RATE_LIMIT_EXCEEDED); + } + }); + Http::options() ->inject('utopia') ->inject('swooleRequest') diff --git a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php index 20a6afed2e..db54568960 100644 --- a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php @@ -49,7 +49,7 @@ class Create extends Action ->setHttpPath('/v1/account/mfa/challenges') ->httpAlias('/v1/account/mfa/challenge') ->desc('Create MFA challenge') - ->groups(['api', 'account', 'mfa']) + ->groups(['api', 'account', 'mfa', 'queueForMails']) ->label('scope', 'account') ->label('event', 'users.[userId].challenges.[challengeId].create') ->label('audits.event', 'challenge.create') diff --git a/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php b/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php index 0632aea3dd..d6b131bb2c 100644 --- a/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php +++ b/src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php @@ -53,7 +53,7 @@ class Create extends Action ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/teams/:teamId/memberships') ->desc('Create team membership') - ->groups(['api', 'teams', 'auth']) + ->groups(['api', 'teams', 'auth', 'queueForMails']) ->label('event', 'teams.[teamId].memberships.[membershipId].create') ->label('scope', 'teams.write') ->label('auth.type', 'invites')