From 08ff1b86756f304a1f193cce944c42e11c92dd59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Jaussoin?= Date: Wed, 17 Jul 2024 11:56:34 +0200 Subject: [PATCH] Fix FLEXIAPI-196 Add a phone validation system by country code with configuration panels and related tests and documentation --- CHANGELOG.md | 1 + flexiapi/app/Helpers/Utils.php | 254 +++++++++++++++- .../Admin/PhoneCountryController.php | 50 +++ .../Api/Account/AccountController.php | 9 +- .../Api/PhoneCountryController.php | 14 + .../Http/Requests/Account/Create/Request.php | 5 +- .../Http/Requests/Account/Update/Request.php | 5 +- flexiapi/app/PhoneCountry.php | 22 ++ flexiapi/app/Rules/FilteredPhone.php | 24 ++ flexiapi/app/Services/AccountService.php | 5 +- flexiapi/composer.json | 1 + flexiapi/composer.lock | 285 +++++++++++++++--- .../factories/PhoneCountryFactory.php | 70 +++++ ...15_135258_create_phone_countries_table.php | 34 +++ flexiapi/public/css/style.css | 4 +- .../views/admin/phone_country/index.blade.php | 52 ++++ .../api/documentation_markdown.blade.php | 19 +- .../resources/views/parts/sidebar.blade.php | 1 + flexiapi/routes/api.php | 2 + flexiapi/routes/web.php | 9 + .../tests/Feature/AccountBlockingTest.php | 2 +- .../Feature/ApiAccountCreationTokenTest.php | 2 +- .../Feature/ApiAccountPhoneChangeTest.php | 33 +- flexiapi/tests/Feature/ApiAccountTest.php | 11 +- .../tests/Feature/ApiPhoneCountryTest.php | 79 +++++ flexiapi/tests/TestCase.php | 11 +- 26 files changed, 933 insertions(+), 71 deletions(-) create mode 100644 flexiapi/app/Http/Controllers/Admin/PhoneCountryController.php create mode 100644 flexiapi/app/Http/Controllers/Api/PhoneCountryController.php create mode 100644 flexiapi/app/PhoneCountry.php create mode 100644 flexiapi/app/Rules/FilteredPhone.php create mode 100644 flexiapi/database/factories/PhoneCountryFactory.php create mode 100644 flexiapi/database/migrations/2024_07_15_135258_create_phone_countries_table.php create mode 100644 flexiapi/resources/views/admin/phone_country/index.blade.php create mode 100644 flexiapi/tests/Feature/ApiPhoneCountryTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index c589e68..55865df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ v1.6 ---- - Fix FLEXIAPI-192 Add DotEnv configuration to allow the expiration of tokens and codes in the app +- Fix FLEXIAPI-196 Add a phone validation system by country code with configuration panels and related tests and documentation v1.5 --- diff --git a/flexiapi/app/Helpers/Utils.php b/flexiapi/app/Helpers/Utils.php index 2ac428e..d2bb852 100644 --- a/flexiapi/app/Helpers/Utils.php +++ b/flexiapi/app/Helpers/Utils.php @@ -42,7 +42,7 @@ function generateNonce(): string function generateValidNonce(Account $account): string { - $nonce = new DigestNonce; + $nonce = new DigestNonce(); $nonce->account_id = $account->id; $nonce->nonce = generateNonce(); $nonce->save(); @@ -62,7 +62,9 @@ function generatePin(): int function percent($value, $max): float { - if ($max == 0) $max = 1; + if ($max == 0) { + $max = 1; + } return round(($value * 100) / $max, 2); } @@ -81,8 +83,8 @@ function markdownDocumentationView(string $view): string ], ]); - $converter->getEnvironment()->addExtension(new HeadingPermalinkExtension); - $converter->getEnvironment()->addExtension(new TableOfContentsExtension); + $converter->getEnvironment()->addExtension(new HeadingPermalinkExtension()); + $converter->getEnvironment()->addExtension(new TableOfContentsExtension()); return (string) $converter->convert( (string)view($view, [ @@ -162,3 +164,247 @@ function validateIsoDate($attribute, $value, $parameters, $validator): bool return (bool)preg_match($regex, $value); } + +/** + * This list was got from the Internet + * + * @see https://gist.github.com/vxnick/380904 + * @return array + */ +function getCountryCodes() +{ + return [ + 'AF' => 'Afghanistan', + 'AX' => 'Åland Islands', + 'AL' => 'Albania', + 'DZ' => 'Algeria', + 'AS' => 'American Samoa', + 'AD' => 'Andorra', + 'AO' => 'Angola', + 'AI' => 'Anguilla', + 'AG' => 'Antigua & Barbuda', + 'AR' => 'Argentina', + 'AU' => 'Australia', + 'AT' => 'Austria', + 'AZ' => 'Azerbaijan', + 'BS' => 'Bahamas', + 'BH' => 'Bahrain', + 'BD' => 'Bangladesh', + 'BB' => 'Barbados', + 'BY' => 'Belarus', + 'BE' => 'Belgium', + 'BZ' => 'Belize', + 'BJ' => 'Benin', + 'BM' => 'Bermuda', + 'BT' => 'Bhutan', + 'BO' => 'Bolivia', + 'BA' => 'Bosnia & Herzegovina', + 'BW' => 'Botswana', + 'BR' => 'Brazil', + 'IO' => 'British Indian Ocean Territory', + 'BN' => 'Brunei', + 'BG' => 'Bulgaria', + 'BF' => 'Burkina Faso', + 'BI' => 'Burundi', + 'KH' => 'Cambodia', + 'CM' => 'Cameroon', + 'CA' => 'Canada', + 'CV' => 'Cape Verde', + 'KY' => 'Cayman Islands', + 'CF' => 'Central African Republic', + 'TD' => 'Chad', + 'CL' => 'Chile', + 'CN' => 'China', + 'CX' => 'Christmas Island', + 'CC' => 'Cocos (Keeling) Islands', + 'CO' => 'Colombia', + 'KM' => 'Comoros', + 'CG' => 'Congo - Brazzaville', + 'CD' => 'Congo - Kinshasa', + 'CK' => 'Cook Islands', + 'CR' => 'Costa Rica', + 'CI' => 'Côte d’Ivoire', + 'HR' => 'Croatia', + 'CU' => 'Cuba', + 'CY' => 'Cyprus', + 'CZ' => 'Czechia', + 'DK' => 'Denmark', + 'DJ' => 'Djibouti', + 'DM' => 'Dominica', + 'DO' => 'Dominican Republic', + 'EC' => 'Ecuador', + 'EG' => 'Egypt', + 'SV' => 'El Salvador', + 'GQ' => 'Equatorial Guinea', + 'ER' => 'Eritrea', + 'EE' => 'Estonia', + 'ET' => 'Ethiopia', + 'FK' => 'Falkland Islands', + 'FO' => 'Faroe Islands', + 'FJ' => 'Fiji', + 'FI' => 'Finland', + 'FR' => 'France', + 'GF' => 'French Guiana', + 'PF' => 'French Polynesia', + 'GA' => 'Gabon', + 'GM' => 'Gambia', + 'GE' => 'Georgia', + 'DE' => 'Germany', + 'GH' => 'Ghana', + 'GI' => 'Gibraltar', + 'GR' => 'Greece', + 'GL' => 'Greenland', + 'GD' => 'Grenada', + 'GP' => 'Guadeloupe', + 'GU' => 'Guam', + 'GT' => 'Guatemala', + 'GG' => 'Guernsey', + 'GN' => 'Guinea', + 'GW' => 'Guinea-Bissau', + 'GY' => 'Guyana', + 'HT' => 'Haiti', + 'HN' => 'Honduras', + 'HK' => 'Hong Kong SAR China', + 'HU' => 'Hungary', + 'IS' => 'Iceland', + 'IN' => 'India', + 'ID' => 'Indonesia', + 'IR' => 'Iran', + 'IQ' => 'Iraq', + 'IE' => 'Ireland', + 'IM' => 'Isle of Man', + 'IL' => 'Israel', + 'IT' => 'Italy', + 'JM' => 'Jamaica', + 'JP' => 'Japan', + 'JE' => 'Jersey', + 'JO' => 'Jordan', + 'KZ' => 'Kazakhstan', + 'KE' => 'Kenya', + 'KI' => 'Kiribati', + 'KP' => 'North Korea', + 'KR' => 'South Korea', + 'KW' => 'Kuwait', + 'KG' => 'Kyrgyzstan', + 'LA' => 'Laos', + 'LV' => 'Latvia', + 'LB' => 'Lebanon', + 'LS' => 'Lesotho', + 'LR' => 'Liberia', + 'LY' => 'Libya', + 'LI' => 'Liechtenstein', + 'LT' => 'Lithuania', + 'LU' => 'Luxembourg', + 'MO' => 'Macao SAR China', + 'MK' => 'North Macedonia', + 'MG' => 'Madagascar', + 'MW' => 'Malawi', + 'MY' => 'Malaysia', + 'MV' => 'Maldives', + 'ML' => 'Mali', + 'MT' => 'Malta', + 'MH' => 'Marshall Islands', + 'MQ' => 'Martinique', + 'MR' => 'Mauritania', + 'MU' => 'Mauritius', + 'YT' => 'Mayotte', + 'MX' => 'Mexico', + 'FM' => 'Micronesia', + 'MD' => 'Moldova', + 'MC' => 'Monaco', + 'MN' => 'Mongolia', + 'ME' => 'Montenegro', + 'MS' => 'Montserrat', + 'MA' => 'Morocco', + 'MZ' => 'Mozambique', + 'MM' => 'Myanmar (Burma)', + 'NA' => 'Namibia', + 'NR' => 'Nauru', + 'NP' => 'Nepal', + 'NL' => 'Netherlands', + 'NC' => 'New Caledonia', + 'NZ' => 'New Zealand', + 'NI' => 'Nicaragua', + 'NE' => 'Niger', + 'NG' => 'Nigeria', + 'NU' => 'Niue', + 'NF' => 'Norfolk Island', + 'MP' => 'Northern Mariana Islands', + 'NO' => 'Norway', + 'OM' => 'Oman', + 'PK' => 'Pakistan', + 'PW' => 'Palau', + 'PS' => 'Palestinian Territories', + 'PA' => 'Panama', + 'PG' => 'Papua New Guinea', + 'PY' => 'Paraguay', + 'PE' => 'Peru', + 'PH' => 'Philippines', + 'PL' => 'Poland', + 'PT' => 'Portugal', + 'PR' => 'Puerto Rico', + 'QA' => 'Qatar', + 'RE' => 'Réunion', + 'RO' => 'Romania', + 'RU' => 'Russia', + 'RW' => 'Rwanda', + 'SH' => 'St. Helena', + 'KN' => 'St. Kitts & Nevis', + 'LC' => 'St. Lucia', + 'PM' => 'St. Pierre & Miquelon', + 'VC' => 'St. Vincent & Grenadines', + 'WS' => 'Samoa', + 'SM' => 'San Marino', + 'ST' => 'São Tomé & Príncipe', + 'SA' => 'Saudi Arabia', + 'SN' => 'Senegal', + 'RS' => 'Serbia', + 'SC' => 'Seychelles', + 'SL' => 'Sierra Leone', + 'SG' => 'Singapore', + 'SK' => 'Slovakia', + 'SI' => 'Slovenia', + 'SB' => 'Solomon Islands', + 'SO' => 'Somalia', + 'ZA' => 'South Africa', + 'ES' => 'Spain', + 'LK' => 'Sri Lanka', + 'SD' => 'Sudan', + 'SR' => 'Suriname', + 'SJ' => 'Svalbard & Jan Mayen', + 'SZ' => 'Eswatini', + 'SE' => 'Sweden', + 'CH' => 'Switzerland', + 'SY' => 'Syria', + 'TW' => 'Taiwan', + 'TJ' => 'Tajikistan', + 'TZ' => 'Tanzania', + 'TH' => 'Thailand', + 'TL' => 'Timor-Leste', + 'TG' => 'Togo', + 'TK' => 'Tokelau', + 'TO' => 'Tonga', + 'TT' => 'Trinidad & Tobago', + 'TN' => 'Tunisia', + 'TM' => 'Turkmenistan', + 'TC' => 'Turks & Caicos Islands', + 'TV' => 'Tuvalu', + 'UG' => 'Uganda', + 'UA' => 'Ukraine', + 'AE' => 'United Arab Emirates', + 'GB' => 'United Kingdom', + 'US' => 'United States', + 'UY' => 'Uruguay', + 'UZ' => 'Uzbekistan', + 'VU' => 'Vanuatu', + 'VE' => 'Venezuela', + 'VN' => 'Vietnam', + 'VG' => 'British Virgin Islands', + 'VI' => 'U.S. Virgin Islands', + 'WF' => 'Wallis & Futuna', + 'EH' => 'Western Sahara', + 'YE' => 'Yemen', + 'ZM' => 'Zambia', + 'ZW' => 'Zimbabwe', + ]; +} diff --git a/flexiapi/app/Http/Controllers/Admin/PhoneCountryController.php b/flexiapi/app/Http/Controllers/Admin/PhoneCountryController.php new file mode 100644 index 0000000..b81f093 --- /dev/null +++ b/flexiapi/app/Http/Controllers/Admin/PhoneCountryController.php @@ -0,0 +1,50 @@ + PhoneCountry::all() + ]); + } + + public function activateAll() + { + PhoneCountry::query()->update(['activated' => true]); + + return redirect()->route('admin.phone_countries.index'); + } + + public function deactivateAll() + { + PhoneCountry::query()->update(['activated' => false]); + + return redirect()->route('admin.phone_countries.index'); + } + + public function activate(string $code) + { + $phoneCountry = PhoneCountry::where('code', $code)->firstOrFail(); + + PhoneCountry::where('country_code', $phoneCountry->country_code)->update(['activated' => true]); + + return redirect()->route('admin.phone_countries.index'); + } + + public function deactivate(string $code) + { + $phoneCountry = PhoneCountry::where('code', $code)->firstOrFail(); + + PhoneCountry::where('country_code', $phoneCountry->country_code)->update(['activated' => false]); + + return redirect()->route('admin.phone_countries.index'); + } +} diff --git a/flexiapi/app/Http/Controllers/Api/Account/AccountController.php b/flexiapi/app/Http/Controllers/Api/Account/AccountController.php index 4f25c14..641595f 100644 --- a/flexiapi/app/Http/Controllers/Api/Account/AccountController.php +++ b/flexiapi/app/Http/Controllers/Api/Account/AccountController.php @@ -38,9 +38,9 @@ use App\Mail\RegisterConfirmation; use App\Rules\AccountCreationToken as RulesAccountCreationToken; use App\Rules\AccountCreationTokenNotExpired; use App\Rules\BlacklistedUsername; +use App\Rules\FilteredPhone; use App\Rules\NoUppercase; use App\Rules\SIPUsername; -use App\Rules\WithoutSpaces; use App\Rules\PasswordAlgorithm; use App\Services\AccountService; @@ -69,7 +69,7 @@ class AccountController extends Controller $request->merge(['phone' => $phone]); $request->validate([ - 'phone' => ['required', new WithoutSpaces, 'starts_with:+'] + 'phone' => ['required', 'phone', new FilteredPhone] ]); $account = Account::where('domain', config('app.sip_domain')) @@ -116,9 +116,10 @@ class AccountController extends Controller 'phone' => [ 'required_without:email', 'required_without:username', + 'phone', + new FilteredPhone, 'unique:accounts,phone', 'unique:accounts,username', - new WithoutSpaces, 'starts_with:+' ], 'account_creation_token' => [ 'required', @@ -189,7 +190,7 @@ class AccountController extends Controller $request->validate([ 'phone' => [ - 'required', new WithoutSpaces, 'starts_with:+', 'exists:accounts,phone' + 'required', 'phone', new FilteredPhone, 'exists:accounts,phone' ], 'account_creation_token' => [ 'required', diff --git a/flexiapi/app/Http/Controllers/Api/PhoneCountryController.php b/flexiapi/app/Http/Controllers/Api/PhoneCountryController.php new file mode 100644 index 0000000..fa0142a --- /dev/null +++ b/flexiapi/app/Http/Controllers/Api/PhoneCountryController.php @@ -0,0 +1,14 @@ + 'nullable|in:' . Account::dtmfProtocolsRule(), 'phone' => [ 'nullable', + 'phone', + new FilteredPhone, Rule::unique('accounts', 'username')->where(function ($query) { $query->where('domain', resolveDomain($this)); })->ignore($this->route('id'), 'id'), Rule::unique('accounts', 'phone')->ignore($this->route('account_id'), 'id'), - new WithoutSpaces, 'starts_with:+' ] ]; } diff --git a/flexiapi/app/PhoneCountry.php b/flexiapi/app/PhoneCountry.php new file mode 100644 index 0000000..aa51092 --- /dev/null +++ b/flexiapi/app/PhoneCountry.php @@ -0,0 +1,22 @@ + 'boolean', + ]; + + public function getNameAttribute(): ?string + { + return getCountryCodes()[$this->attributes['code']]; + } +} diff --git a/flexiapi/app/Rules/FilteredPhone.php b/flexiapi/app/Rules/FilteredPhone.php new file mode 100644 index 0000000..b97620a --- /dev/null +++ b/flexiapi/app/Rules/FilteredPhone.php @@ -0,0 +1,24 @@ +getCountry()) + ->where('activated', true) + ->exists()) return false; + + return true; + } + + public function message() + { + return 'The phone number must belong to an authorized country.'; + } +} diff --git a/flexiapi/app/Services/AccountService.php b/flexiapi/app/Services/AccountService.php index 2d74d79..17a6c34 100644 --- a/flexiapi/app/Services/AccountService.php +++ b/flexiapi/app/Services/AccountService.php @@ -31,9 +31,9 @@ use App\Mail\NewsletterRegistration; use App\Mail\RecoverByCode; use App\Mail\RegisterValidation; use App\PhoneChangeCode; +use App\Rules\FilteredPhone; use Illuminate\Support\Facades\Log; -use App\Rules\WithoutSpaces; use Carbon\Carbon; use Illuminate\Support\Facades\Mail; use Illuminate\Http\Request; @@ -223,9 +223,10 @@ class AccountService { $request->validate([ 'phone' => [ + 'phone', + new FilteredPhone, 'required', 'unique:accounts,phone', 'unique:accounts,username', - new WithoutSpaces(), 'starts_with:+' ] ]); diff --git a/flexiapi/composer.json b/flexiapi/composer.json index daacf3a..7fbafb8 100644 --- a/flexiapi/composer.json +++ b/flexiapi/composer.json @@ -20,6 +20,7 @@ "ovh/ovh": "^3.2", "parsedown/laravel": "^1.2", "phpunit/phpunit": "^9.6", + "propaganistas/laravel-phone": "^5.1", "react/socket": "^1.14", "respect/validation": "^2.2", "sabre/vobject": "^4.5", diff --git a/flexiapi/composer.lock b/flexiapi/composer.lock index ec0ca65..31aaeee 100644 --- a/flexiapi/composer.lock +++ b/flexiapi/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e9d79f33a899fd2dcde3de88c243e800", + "content-hash": "075490b395def258891e92f9518e7a03", "packages": [ { "name": "awobaz/compoships", @@ -298,16 +298,16 @@ }, { "name": "dflydev/dot-access-data", - "version": "v3.0.2", + "version": "v3.0.3", "source": { "type": "git", "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "f41715465d65213d644d3141a6a93081be5d3549" + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", - "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", "shasum": "" }, "require": { @@ -367,9 +367,9 @@ ], "support": { "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" }, - "time": "2022-10-27T11:44:00+00:00" + "time": "2024-07-08T12:26:09+00:00" }, { "name": "doctrine/cache", @@ -1389,6 +1389,135 @@ ], "time": "2023-10-12T05:21:21+00:00" }, + { + "name": "giggsey/libphonenumber-for-php", + "version": "8.13.40", + "source": { + "type": "git", + "url": "https://github.com/giggsey/libphonenumber-for-php.git", + "reference": "795e0b760e5c439b6fa1ffa787c1d90c2face1ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/795e0b760e5c439b6fa1ffa787c1d90c2face1ff", + "reference": "795e0b760e5c439b6fa1ffa787c1d90c2face1ff", + "shasum": "" + }, + "require": { + "giggsey/locale": "^1.7|^2.0", + "php": ">=5.3.2", + "symfony/polyfill-mbstring": "^1.17" + }, + "replace": { + "giggsey/libphonenumber-for-php-lite": "self.version" + }, + "require-dev": { + "pear/pear-core-minimal": "^1.9", + "pear/pear_exception": "^1.0", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.7", + "php-coveralls/php-coveralls": "^1.0|^2.0", + "symfony/console": "^2.8|^3.0|^v4.4|^v5.2", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "libphonenumber\\": "src/" + }, + "exclude-from-classmap": [ + "/src/data/", + "/src/carrier/data/", + "/src/geocoding/data/", + "/src/timezone/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "PHP Port of Google's libphonenumber", + "homepage": "https://github.com/giggsey/libphonenumber-for-php", + "keywords": [ + "geocoding", + "geolocation", + "libphonenumber", + "mobile", + "phonenumber", + "validation" + ], + "support": { + "issues": "https://github.com/giggsey/libphonenumber-for-php/issues", + "source": "https://github.com/giggsey/libphonenumber-for-php" + }, + "time": "2024-07-01T11:38:07+00:00" + }, + { + "name": "giggsey/locale", + "version": "2.6", + "source": { + "type": "git", + "url": "https://github.com/giggsey/Locale.git", + "reference": "37874fa473131247c348059fb7b8985efc18b5ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/Locale/zipball/37874fa473131247c348059fb7b8985efc18b5ea", + "reference": "37874fa473131247c348059fb7b8985efc18b5ea", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "ext-json": "*", + "pear/pear-core-minimal": "^1.9", + "pear/pear_exception": "^1.0", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.7", + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.5|^9.5", + "symfony/console": "^5.0|^6.0", + "symfony/filesystem": "^5.0|^6.0", + "symfony/finder": "^5.0|^6.0", + "symfony/process": "^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Giggsey\\Locale\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "Locale functions required by libphonenumber-for-php", + "support": { + "issues": "https://github.com/giggsey/Locale/issues", + "source": "https://github.com/giggsey/Locale/tree/2.6" + }, + "time": "2024-04-18T19:31:19+00:00" + }, { "name": "graham-campbell/result-type", "version": "v1.1.2", @@ -3177,16 +3306,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.0.2", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { @@ -3197,7 +3326,7 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -3229,9 +3358,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { "name": "nunomaduro/termwind", @@ -3945,45 +4074,45 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.19", + "version": "9.6.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" + "reference": "49d7820565836236411f5dc002d16dd689cde42f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", + "reference": "49d7820565836236411f5dc002d16dd689cde42f", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-code-coverage": "^9.2.31", + "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", "sebastian/version": "^3.0.2" }, "suggest": { @@ -4028,7 +4157,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" }, "funding": [ { @@ -4044,7 +4173,79 @@ "type": "tidelift" } ], - "time": "2024-04-05T04:35:58+00:00" + "time": "2024-07-10T11:45:39+00:00" + }, + { + "name": "propaganistas/laravel-phone", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/Propaganistas/Laravel-Phone.git", + "reference": "d8c0e5fbde9820e026595616a7a36074233a09c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Propaganistas/Laravel-Phone/zipball/d8c0e5fbde9820e026595616a7a36074233a09c6", + "reference": "d8c0e5fbde9820e026595616a7a36074233a09c6", + "shasum": "" + }, + "require": { + "giggsey/libphonenumber-for-php": "^7.0|^8.0", + "illuminate/contracts": "^9.0|^10.0", + "illuminate/support": "^9.0|^10.0", + "illuminate/validation": "^9.0|^10.0", + "php": "^8.0" + }, + "require-dev": { + "laravel/pint": "^1.4", + "nunomaduro/larastan": "^2.4", + "orchestra/testbench": "*", + "phpunit/phpunit": "^9.5.10" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Propaganistas\\LaravelPhone\\PhoneServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Propaganistas\\LaravelPhone\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Propaganistas", + "email": "Propaganistas@users.noreply.github.com" + } + ], + "description": "Adds phone number functionality to Laravel based on Google's libphonenumber API.", + "keywords": [ + "laravel", + "libphonenumber", + "phone", + "validation" + ], + "support": { + "issues": "https://github.com/Propaganistas/Laravel-Phone/issues", + "source": "https://github.com/Propaganistas/Laravel-Phone/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/Propaganistas", + "type": "github" + } + ], + "time": "2024-01-15T09:23:46+00:00" }, { "name": "psr/cache", @@ -5446,16 +5647,16 @@ }, { "name": "sabre/vobject", - "version": "4.5.4", + "version": "4.5.5", "source": { "type": "git", "url": "https://github.com/sabre-io/vobject.git", - "reference": "a6d53a3e5bec85ed3dd78868b7de0f5b4e12f772" + "reference": "7148cf57d25aaba0a49f6656d37c35e8175b3087" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/vobject/zipball/a6d53a3e5bec85ed3dd78868b7de0f5b4e12f772", - "reference": "a6d53a3e5bec85ed3dd78868b7de0f5b4e12f772", + "url": "https://api.github.com/repos/sabre-io/vobject/zipball/7148cf57d25aaba0a49f6656d37c35e8175b3087", + "reference": "7148cf57d25aaba0a49f6656d37c35e8175b3087", "shasum": "" }, "require": { @@ -5546,7 +5747,7 @@ "issues": "https://github.com/sabre-io/vobject/issues", "source": "https://github.com/fruux/sabre-vobject" }, - "time": "2023-11-09T12:54:37+00:00" + "time": "2024-07-02T08:48:52+00:00" }, { "name": "sabre/xml", diff --git a/flexiapi/database/factories/PhoneCountryFactory.php b/flexiapi/database/factories/PhoneCountryFactory.php new file mode 100644 index 0000000..d1521e3 --- /dev/null +++ b/flexiapi/database/factories/PhoneCountryFactory.php @@ -0,0 +1,70 @@ +. +*/ + +namespace Database\Factories; + +use Illuminate\Database\Eloquent\Factories\Factory; + +use libphonenumber\PhoneNumberUtil; + +class PhoneCountryFactory extends Factory +{ + public function definition() + { + $phoneNumberUtils = PhoneNumberUtil::getInstance(); + + $codes = ['AF', 'AX', 'AL', 'DZ', 'AS', 'AD']; + $code = $codes[array_rand($codes)]; + + return [ + 'code' => $code, + 'country_code' => $phoneNumberUtils->getMetadataForRegion($code)->getCountryCode(), + 'activated' => false, + ]; + } + + public function france() + { + $code = 'FR'; + $phoneNumberUtils = PhoneNumberUtil::getInstance(); + + return $this->state(fn (array $attributes) => [ + 'code' => $code, + 'country_code' => $phoneNumberUtils->getMetadataForRegion($code)->getCountryCode() + ]); + } + + public function netherlands() + { + $code = 'NL'; + $phoneNumberUtils = PhoneNumberUtil::getInstance(); + + return $this->state(fn (array $attributes) => [ + 'code' => $code, + 'country_code' => $phoneNumberUtils->getMetadataForRegion($code)->getCountryCode() + ]); + } + + public function activated() + { + return $this->state(fn (array $attributes) => [ + 'activated' => true + ]); + } +} diff --git a/flexiapi/database/migrations/2024_07_15_135258_create_phone_countries_table.php b/flexiapi/database/migrations/2024_07_15_135258_create_phone_countries_table.php new file mode 100644 index 0000000..5aa8bbf --- /dev/null +++ b/flexiapi/database/migrations/2024_07_15_135258_create_phone_countries_table.php @@ -0,0 +1,34 @@ +string('code', 2)->primary(); + $table->string('country_code', 3); + $table->boolean('activated')->default(false); + $table->timestamps(); + }); + + $phoneNumberUtils = PhoneNumberUtil::getInstance(); + foreach (getCountryCodes() as $code => $name) { + $phoneCountry = new PhoneCountry(); + $phoneCountry->code = $code; + $phoneCountry->country_code = $phoneNumberUtils->getMetadataForRegion($code)->getCountryCode(); + $phoneCountry->save(); + } + } + + public function down() + { + Schema::dropIfExists('phone_countries'); + } +}; diff --git a/flexiapi/public/css/style.css b/flexiapi/public/css/style.css index 9caaebc..65404ab 100644 --- a/flexiapi/public/css/style.css +++ b/flexiapi/public/css/style.css @@ -432,6 +432,7 @@ content>nav a { margin: 1rem 0; position: relative; white-space: nowrap; + padding: 0 1rem; } content>nav a.current { @@ -446,8 +447,7 @@ content>nav a.current i { } content>nav a i { - margin: 0 1rem; - margin-left: 2rem; + margin-right: 0.75rem; font-size: 2rem; } diff --git a/flexiapi/resources/views/admin/phone_country/index.blade.php b/flexiapi/resources/views/admin/phone_country/index.blade.php new file mode 100644 index 0000000..4021f80 --- /dev/null +++ b/flexiapi/resources/views/admin/phone_country/index.blade.php @@ -0,0 +1,52 @@ +@extends('layouts.main') + +@section('breadcrumb') + +@endsection + +@section('content') + +
+

flag Phone Countries

+ + add_circle + Activate All + + + remove_circle + Deactivate All + +
+ + + + + + + + + + + + @foreach ($phone_countries as $phone_country) + + + + + + + @endforeach + +
CodeNameCountry codeActions
{{ $phone_country->code }}{{ $phone_country->name }}{{ $phone_country->country_code }} + @if ($phone_country->activated) + Activated + Desactivate + @else + Deactivated + Activate + @endif +
+ +@endsection \ No newline at end of file diff --git a/flexiapi/resources/views/api/documentation_markdown.blade.php b/flexiapi/resources/views/api/documentation_markdown.blade.php index 5976892..f245cd9 100644 --- a/flexiapi/resources/views/api/documentation_markdown.blade.php +++ b/flexiapi/resources/views/api/documentation_markdown.blade.php @@ -243,7 +243,7 @@ JSON parameters: * `algorithm` required, values can be `SHA-256` or `MD5` * `domain` if not set the value is enforced to the default registration domain set in the global configuration * `email` optional if `phone` set, an email, set an email to the account, must be unique if `ACCOUNT_EMAIL_UNIQUE` is set to `true` -* `phone` required if `username` not set, optional if `email` set, a phone number, set a phone number to the account +* `phone` required if `username` not set, optional if `email` set, a valid phone number, set a phone number to the account * `account_creation_token` the unique `account_creation_token` ### `POST /accounts/with-account-creation-token` @@ -284,7 +284,7 @@ Can only be used once, a new `recover_key` need to be requested to be called aga JSON parameters: -* `phone` required the phone number to send the SMS to +* `phone` required, the phone number to send the SMS to * `account_creation_token` the unique `account_creation_token` ### `GET /accounts/{sip}/recover/{recover_key}` @@ -380,7 +380,7 @@ JSON parameters: * `display_name` optional, string * `email` optional, must be an email, must be unique if `ACCOUNT_EMAIL_UNIQUE` is set to `true` * `admin` optional, a boolean, set to `false` by default, create an admin account -* `phone` optional, a phone number, set a phone number to the account +* `phone` optional, a valid phone number, set a phone number to the account * `dtmf_protocol` optional, values must be `sipinfo`, `sipmessage` or `rfc2833` * `dictionary` optional, an associative array attached to the account, see also the related endpoints. * Deprecated `confirmation_key_expires` optional, a datetime of this format: Y-m-d H:i:s. Only used when `activated` is not used or `false`. Enforces an expiration date on the returned `confirmation_key`. After that datetime public email or phone activation endpoints will return `403`. @@ -399,7 +399,7 @@ JSON parameters: * `display_name` optional, string * `email` optional, must be an email, must be unique if `ACCOUNT_EMAIL_UNIQUE` is set to `true` * `admin` optional, a boolean, set to `false` by default -* `phone` optional, a phone number, set a phone number to the account +* `phone` optional, a valid phone number, set a phone number to the account * `dtmf_protocol` optional, values must be `sipinfo`, `sipmessage` or `rfc2833` Using this endpoint you can also set a fresh dictionnary if the parameter is set. The existing dictionary entries will be destroyed. @@ -765,6 +765,17 @@ JSON parameters: * `to` required, SIP address of the receiver * `body` required, content of the message +## Phone Countries + +The phone numbers managed by FlexiAPI are validated against a list of countries that can be managed in the admin web panels. + +### `GET /phones_countries` +Public + +Return the list of Phone Countries and their current status. + +If a country is deactivated all the new submitted phones submitted on the platform will be blocked. + ## Statistics FlexiAPI can record logs generated by the FlexiSIP server and compile them into statistics. diff --git a/flexiapi/resources/views/parts/sidebar.blade.php b/flexiapi/resources/views/parts/sidebar.blade.php index 240f6af..a0e5f42 100644 --- a/flexiapi/resources/views/parts/sidebar.blade.php +++ b/flexiapi/resources/views/parts/sidebar.blade.php @@ -11,6 +11,7 @@ if (auth()->user()->superAdmin) { $items['admin.sip_domains.index'] = ['title' => 'SIP Domains', 'icon' => 'dns']; + $items['admin.phone_countries.index'] = ['title' => 'Phone Countries', 'icon' => 'flag']; } } @endphp diff --git a/flexiapi/routes/api.php b/flexiapi/routes/api.php index 58babb6..56e2f09 100644 --- a/flexiapi/routes/api.php +++ b/flexiapi/routes/api.php @@ -59,6 +59,8 @@ Route::post('accounts/auth_token', 'Api\Account\AuthTokenController@store'); Route::get('accounts/me/api_key/{auth_token}', 'Api\Account\ApiKeyController@generateFromToken')->middleware('cookie', 'cookie.encrypt'); +Route::get('phone_countries', 'Api\PhoneCountryController@index'); + Route::group(['middleware' => ['auth.jwt', 'auth.digest_or_key', 'auth.check_blocked']], function () { Route::get('accounts/auth_token/{auth_token}/attach', 'Api\Account\AuthTokenController@attach'); Route::post('account_creation_tokens/consume', 'Api\Account\CreationTokenController@consume'); diff --git a/flexiapi/routes/web.php b/flexiapi/routes/web.php index aae5ff2..7310a81 100644 --- a/flexiapi/routes/web.php +++ b/flexiapi/routes/web.php @@ -38,6 +38,7 @@ use App\Http\Controllers\Admin\AccountController as AdminAccountController; use App\Http\Controllers\Admin\AccountStatisticsController; use App\Http\Controllers\Admin\ContactsListController; use App\Http\Controllers\Admin\ContactsListContactController; +use App\Http\Controllers\Admin\PhoneCountryController; use App\Http\Controllers\Admin\SipDomainController; use App\Http\Controllers\Admin\StatisticsController; use Illuminate\Support\Facades\Route; @@ -157,6 +158,14 @@ Route::group(['middleware' => 'web_panel_enabled'], function () { Route::middleware(['auth.super_admin'])->group(function () { Route::resource('sip_domains', SipDomainController::class); Route::get('sip_domains/delete/{id}', 'Admin\SipDomainController@delete')->name('sip_domains.delete'); + + Route::name('phone_countries.')->controller(PhoneCountryController::class)->prefix('phone_countries')->group(function () { + Route::get('/', 'index')->name('index'); + Route::get('/activate_all', 'activateAll')->name('activate_all'); + Route::get('/deactivate_all', 'deactivateAll')->name('deactivate_all'); + Route::get('/{code}/activate', 'activate')->name('activate'); + Route::get('/{code}/deactivate', 'deactivate')->name('deactivate'); + }); }); Route::name('statistics.')->controller(StatisticsController::class)->prefix('statistics')->group(function () { diff --git a/flexiapi/tests/Feature/AccountBlockingTest.php b/flexiapi/tests/Feature/AccountBlockingTest.php index 3bcf854..c30d4f6 100644 --- a/flexiapi/tests/Feature/AccountBlockingTest.php +++ b/flexiapi/tests/Feature/AccountBlockingTest.php @@ -36,7 +36,7 @@ class AccountBlockingTest extends TestCase $this->keyAuthenticated($account) ->json($this->method, $this->route . '/me/phone/request', [ - 'phone' => '+331234' + 'phone' => '+33612312312' ])->assertStatus(200); $this->keyAuthenticated($account) diff --git a/flexiapi/tests/Feature/ApiAccountCreationTokenTest.php b/flexiapi/tests/Feature/ApiAccountCreationTokenTest.php index aea80ff..63d620c 100644 --- a/flexiapi/tests/Feature/ApiAccountCreationTokenTest.php +++ b/flexiapi/tests/Feature/ApiAccountCreationTokenTest.php @@ -246,7 +246,7 @@ class ApiAccountCreationTokenTest extends TestCase $this->keyAuthenticated($account) ->json($this->method, '/api/accounts/me/phone/request', [ - 'phone' => '+33123' + 'phone' => '+33612312312' ]) ->assertStatus(200); } diff --git a/flexiapi/tests/Feature/ApiAccountPhoneChangeTest.php b/flexiapi/tests/Feature/ApiAccountPhoneChangeTest.php index 1988f3f..4baa89f 100644 --- a/flexiapi/tests/Feature/ApiAccountPhoneChangeTest.php +++ b/flexiapi/tests/Feature/ApiAccountPhoneChangeTest.php @@ -20,8 +20,8 @@ namespace Tests\Feature; use App\Account; -use App\AccountCreationToken; use App\PhoneChangeCode; +use App\PhoneCountry; use Tests\TestCase; class ApiAccountPhoneChangeTest extends TestCase @@ -55,7 +55,7 @@ class ApiAccountPhoneChangeTest extends TestCase $this->keyAuthenticated($account) ->json($this->method, $this->route.'/request', [ - 'phone' => '+123123' + 'phone' => '+33612312312' ]) ->assertStatus(200); @@ -71,6 +71,35 @@ class ApiAccountPhoneChangeTest extends TestCase ->assertStatus(410); } + public function testCreatePhoneByCountry() + { + $account = Account::factory()->withConsumedAccountCreationToken()->create(); + $account->generateApiKey(); + + $frenchPhoneNumber = '+33612121212'; + $dutchPhoneNumber = '+31612121212'; + + $this->keyAuthenticated($account) + ->json($this->method, $this->route.'/request', [ + 'phone' => $frenchPhoneNumber + ]) + ->assertStatus(200); + + $this->keyAuthenticated($account) + ->json($this->method, $this->route.'/request', [ + 'phone' => $dutchPhoneNumber + ]) + ->assertJsonValidationErrors(['phone']); + + PhoneCountry::where('code', 'NL')->update(['activated' => true]); + + $this->keyAuthenticated($account) + ->json($this->method, $this->route.'/request', [ + 'phone' => $dutchPhoneNumber + ]) + ->assertStatus(200); + } + public function testUnvalidatedAccount() { $account = Account::factory()->create(); diff --git a/flexiapi/tests/Feature/ApiAccountTest.php b/flexiapi/tests/Feature/ApiAccountTest.php index 2caef5e..eb2b914 100644 --- a/flexiapi/tests/Feature/ApiAccountTest.php +++ b/flexiapi/tests/Feature/ApiAccountTest.php @@ -869,7 +869,7 @@ class ApiAccountTest extends TestCase */ public function testRecoverPhone() { - $phone = '+3361234'; + $phone = '+33612312312'; $password = Password::factory()->create(); $password->account->generateApiKey(); @@ -892,7 +892,7 @@ class ApiAccountTest extends TestCase // Wrong phone $this->json($this->method, $this->route . '/recover-by-phone', [ - 'phone' => '+331234', // wrong phone number + 'phone' => '+33612312313', // wrong phone number 'account_creation_token' => $token->token ])->assertJsonValidationErrors(['phone']); @@ -923,7 +923,10 @@ class ApiAccountTest extends TestCase ]); $this->get($this->route . '/+1234/info-by-phone') - ->assertStatus(404); + ->assertStatus(302); + + $this->get($this->route . '/+33612312312/info-by-phone') + ->assertStatus(200); $this->json('GET', $this->route . '/' . $password->account->identifier . '/info-by-phone') ->assertJsonValidationErrors(['phone']); @@ -1030,7 +1033,7 @@ class ApiAccountTest extends TestCase public function testCreatePublicPhone() { - $phone = '+12345'; + $phone = '+33612312312'; config()->set('app.dangerous_endpoints', true); diff --git a/flexiapi/tests/Feature/ApiPhoneCountryTest.php b/flexiapi/tests/Feature/ApiPhoneCountryTest.php new file mode 100644 index 0000000..355e668 --- /dev/null +++ b/flexiapi/tests/Feature/ApiPhoneCountryTest.php @@ -0,0 +1,79 @@ +. +*/ + +namespace Tests\Feature; + +use App\Account; +use App\PhoneCountry; +use App\SipDomain; +use Tests\TestCase; + +class ApiPhoneCountryTest extends TestCase +{ + protected $route = '/api/phone_countries'; + protected $method = 'POST'; + protected $routeChangePhone = '/api/accounts/me/phone'; + + public function testCreatePhoneByCountry() + { + $account = Account::factory()->withConsumedAccountCreationToken()->create(); + $account->generateApiKey(); + + $frenchPhoneNumber = '+33612121212'; + $dutchPhoneNumber = '+31612121212'; + + $this->get($this->route) + ->assertStatus(200) + ->assertJsonFragment([ + 'code' => 'FR', + 'activated' => true + ]) + ->assertJsonFragment([ + 'code' => 'NL', + 'activated' => false + ]); + + $this->keyAuthenticated($account) + ->json($this->method, $this->routeChangePhone.'/request', [ + 'phone' => $frenchPhoneNumber + ]) + ->assertStatus(200); + + $this->keyAuthenticated($account) + ->json($this->method, $this->routeChangePhone.'/request', [ + 'phone' => $dutchPhoneNumber + ]) + ->assertJsonValidationErrors(['phone']); + + PhoneCountry::where('code', 'NL')->update(['activated' => true]); + + $this->get($this->route) + ->assertStatus(200) + ->assertJsonFragment([ + 'code' => 'NL', + 'activated' => true + ]); + + $this->keyAuthenticated($account) + ->json($this->method, $this->routeChangePhone.'/request', [ + 'phone' => $dutchPhoneNumber + ]) + ->assertStatus(200); + } +} diff --git a/flexiapi/tests/TestCase.php b/flexiapi/tests/TestCase.php index f2044e3..e7a3d60 100644 --- a/flexiapi/tests/TestCase.php +++ b/flexiapi/tests/TestCase.php @@ -21,7 +21,7 @@ namespace Tests; use App\Password; use App\Account; - +use App\PhoneCountry; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; use Illuminate\Foundation\Testing\RefreshDatabase; @@ -33,6 +33,15 @@ abstract class TestCase extends BaseTestCase protected $route = '/api/accounts/me'; protected $method = 'GET'; + public function setUp(): void + { + parent::setUp(); + + PhoneCountry::truncate(); + PhoneCountry::factory()->france()->activated()->create(); + PhoneCountry::factory()->netherlands()->create(); + } + protected function disableBlockingService() { config()->set('app.blocking_amount_events_authorized_during_period', 0);