1
0
Fork 0
mirror of synced 2024-10-02 02:07:04 +13:00

Merge pull request #7487 from appwrite/feat-block-countries

Feat block countries from certain regions
This commit is contained in:
Eldad A. Fux 2024-02-05 09:47:34 +01:00 committed by GitHub
commit ca926888f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 73 additions and 15 deletions

1
.env
View file

@ -4,6 +4,7 @@ _APP_WORKER_PER_CORE=6
_APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_ROOT=disabled
_APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_EMAILS=
_APP_CONSOLE_WHITELIST_IPS= _APP_CONSOLE_WHITELIST_IPS=
_APP_CONSOLE_COUNTRIES_DENYLIST=AQ
_APP_CONSOLE_HOSTNAMES=localhost,appwrite.io,*.appwrite.io _APP_CONSOLE_HOSTNAMES=localhost,appwrite.io,*.appwrite.io
_APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_NAME=Appwrite
_APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io

View file

@ -114,6 +114,11 @@ return [
'description' => 'Value must be a valid phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', 'description' => 'Value must be a valid phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.',
'code' => 400, 'code' => 400,
], ],
Exception::GENERAL_REGION_ACCESS_DENIED => [
'name' => Exception::GENERAL_REGION_ACCESS_DENIED,
'description' => 'Your location is not supported due to legal requirements.',
'code' => 451,
],
/** User Errors */ /** User Errors */
Exception::USER_COUNT_EXCEEDED => [ Exception::USER_COUNT_EXCEEDED => [

View file

@ -1220,9 +1220,9 @@ App::post('/v1/account/tokens/magic-url')
App::post('/v1/account/tokens/email') App::post('/v1/account/tokens/email')
->desc('Create email token (OTP)') ->desc('Create email token (OTP)')
->groups(['api', 'account']) ->groups(['api', 'account', 'auth'])
->label('scope', 'sessions.write') ->label('scope', 'sessions.write')
->label('auth.type', 'email') ->label('auth.type', 'email-otp')
->label('audits.event', 'session.create') ->label('audits.event', 'session.create')
->label('audits.resource', 'user/{response.userId}') ->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}') ->label('audits.userId', '{response.userId}')

View file

@ -736,6 +736,7 @@ App::error()
case 412: // Error allowed publicly case 412: // Error allowed publicly
case 416: // Error allowed publicly case 416: // Error allowed publicly
case 429: // Error allowed publicly case 429: // Error allowed publicly
case 451: // Error allowed publicly
case 501: // Error allowed publicly case 501: // Error allowed publicly
case 503: // Error allowed publicly case 503: // Error allowed publicly
break; break;

View file

@ -22,6 +22,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime; use Utopia\Database\DateTime;
use Utopia\Database\Document; use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization;
use MaxMind\Db\Reader;
$parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) { $parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) {
preg_match_all('/{(.*?)}/', $label, $matches); preg_match_all('/{(.*?)}/', $label, $matches);
@ -319,7 +320,17 @@ App::init()
->inject('utopia') ->inject('utopia')
->inject('request') ->inject('request')
->inject('project') ->inject('project')
->action(function (App $utopia, Request $request, Document $project) { ->inject('geodb')
->action(function (App $utopia, Request $request, Document $project, Reader $geodb) {
$denylist = App::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
if (!empty($denylist) && $project->getId() === 'console') {
$countries = explode(',', $denylist);
$record = $geodb->get($request->getIP()) ?? [];
$country = $record['country']['iso_code'] ?? '';
if (in_array($country, $countries)) {
throw new Exception(Exception::GENERAL_REGION_ACCESS_DENIED);
}
}
$route = $utopia->getRoute(); $route = $utopia->getRoute();
@ -368,6 +379,12 @@ App::init()
} }
break; break;
case 'email-otp':
if (($auths['emailOTP'] ?? true) === false) {
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Email OTP authentication is disabled for this project');
}
break;
default: default:
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Unsupported authentication route'); throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Unsupported authentication route');
break; break;

View file

@ -6,13 +6,24 @@ use Utopia\App;
use Appwrite\Extend\Exception; use Appwrite\Extend\Exception;
use Utopia\Database\Document; use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization;
use MaxMind\Db\Reader;
App::init() App::init()
->groups(['auth']) ->groups(['auth'])
->inject('utopia') ->inject('utopia')
->inject('request') ->inject('request')
->inject('project') ->inject('project')
->action(function (App $utopia, Request $request, Document $project) { ->inject('geodb')
->action(function (App $utopia, Request $request, Document $project, Reader $geodb) {
$denylist = App::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
if (!empty($denylist && $project->getId() === 'console')) {
$countries = explode(',', $denylist);
$record = $geodb->get($request->getIP()) ?? [];
$country = $record['country']['iso_code'] ?? '';
if (in_array($country, $countries)) {
throw new Exception(Exception::GENERAL_REGION_ACCESS_DENIED);
}
}
$route = $utopia->match($request); $route = $utopia->match($request);
@ -61,6 +72,12 @@ App::init()
} }
break; break;
case 'email-otp':
if (($auths['emailOTP'] ?? true) === false) {
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Email OTP authentication is disabled for this project');
}
break;
default: default:
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Unsupported authentication route'); throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Unsupported authentication route');
break; break;

View file

@ -65,7 +65,7 @@
"utopia-php/queue": "0.6.*", "utopia-php/queue": "0.6.*",
"utopia-php/registry": "0.5.*", "utopia-php/registry": "0.5.*",
"utopia-php/storage": "0.18.*", "utopia-php/storage": "0.18.*",
"utopia-php/swoole": "0.5.*", "utopia-php/swoole": "0.8.*",
"utopia-php/vcs": "0.6.*", "utopia-php/vcs": "0.6.*",
"utopia-php/websocket": "0.1.*", "utopia-php/websocket": "0.1.*",
"matomo/device-detector": "6.1.*", "matomo/device-detector": "6.1.*",

20
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "3b43bf6f0fca50a3a2834e1bbaa90d63", "content-hash": "e6e0d6874f4d718d96396d71a66864da",
"packages": [ "packages": [
{ {
"name": "adhocore/jwt", "name": "adhocore/jwt",
@ -2432,28 +2432,28 @@
}, },
{ {
"name": "utopia-php/swoole", "name": "utopia-php/swoole",
"version": "0.5.0", "version": "0.8.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/swoole.git", "url": "https://github.com/utopia-php/swoole.git",
"reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", "url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4",
"reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-swoole": "*", "ext-swoole": "*",
"php": ">=8.0", "php": ">=8.0",
"utopia-php/framework": "0.*.*" "utopia-php/framework": "0.33.*"
}, },
"require-dev": { "require-dev": {
"laravel/pint": "1.2.*", "laravel/pint": "1.2.*",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.3", "phpunit/phpunit": "^9.3",
"swoole/ide-helper": "4.8.3", "swoole/ide-helper": "5.0.2"
"vimeo/psalm": "4.15.0"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -2477,9 +2477,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/swoole/issues", "issues": "https://github.com/utopia-php/swoole/issues",
"source": "https://github.com/utopia-php/swoole/tree/0.5.0" "source": "https://github.com/utopia-php/swoole/tree/0.8.2"
}, },
"time": "2022-10-19T22:19:07+00:00" "time": "2024-02-01T14:54:12+00:00"
}, },
{ {
"name": "utopia-php/system", "name": "utopia-php/system",

View file

@ -188,6 +188,7 @@ services:
- _APP_MESSAGE_SMS_TEST_DSN - _APP_MESSAGE_SMS_TEST_DSN
- _APP_MESSAGE_EMAIL_TEST_DSN - _APP_MESSAGE_EMAIL_TEST_DSN
- _APP_MESSAGE_PUSH_TEST_DSN - _APP_MESSAGE_PUSH_TEST_DSN
- _APP_CONSOLE_COUNTRIES_DENYLIST
appwrite-realtime: appwrite-realtime:
entrypoint: realtime entrypoint: realtime

View file

@ -57,6 +57,7 @@ class Exception extends \Exception
public const GENERAL_NOT_IMPLEMENTED = 'general_not_implemented'; public const GENERAL_NOT_IMPLEMENTED = 'general_not_implemented';
public const GENERAL_INVALID_EMAIL = 'general_invalid_email'; public const GENERAL_INVALID_EMAIL = 'general_invalid_email';
public const GENERAL_INVALID_PHONE = 'general_invalid_phone'; public const GENERAL_INVALID_PHONE = 'general_invalid_phone';
public const GENERAL_REGION_ACCESS_DENIED = 'general_region_access_denied';
/** Users */ /** Users */
public const USER_COUNT_EXCEEDED = 'user_count_exceeded'; public const USER_COUNT_EXCEEDED = 'user_count_exceeded';

View file

@ -44,6 +44,21 @@ trait AccountBase
/** /**
* Test for FAILURE * Test for FAILURE
*/ */
// Deny request from blocked IP
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'x-forwarded-for' => '103.152.127.250' // Test IP for denied access region
]), [
'userId' => ID::unique(),
'email' => $email,
'password' => $password,
'name' => $name,
]);
$this->assertEquals(451, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([ $response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
'origin' => 'http://localhost', 'origin' => 'http://localhost',
'content-type' => 'application/json', 'content-type' => 'application/json',