Compare commits

...

6 commits

Author SHA1 Message Date
Timothée Jaussoin
b8bc5d5b58 Fix FLEXIAPI-262 Bypass the JWT auth if we have an API 2025-01-30 10:23:58 +00:00
Timothée Jaussoin
14a1df8bcd Fix/254 empty post validate json 16 2025-01-06 08:53:59 +00:00
Timothée Jaussoin
21bced8764 Fix FLEXIAPI-252 Update the hCaptcha Laravel library, use file instead of... 2024-12-18 15:14:45 +00:00
Timothée Jaussoin
d43cb345d2 Fix FLEXIAPI-242 Add stricter validation for the AccountCreationToken Push Notification endpoint 2024-12-10 14:20:47 +01:00
Timothée Jaussoin
1d29bac386 Fix GH-15 Add password import from CSV 2024-12-09 16:07:30 +00:00
Timothée Jaussoin
963a85bd5e Complete the RELEASE.md file for the 1.6 release 2024-11-12 15:52:54 +01:00
24 changed files with 546 additions and 327 deletions

View file

@ -14,13 +14,13 @@ rocky9-deploy:
- rocky9-package
- rocky9-test
debian11-deploy:
extends: .deploy
script:
- ./deploy_packages.sh debian bullseye
needs:
- debian11-package
- debian11-test
#debian11-deploy:
# extends: .deploy
# script:
# - ./deploy_packages.sh debian bullseye
# needs:
# - debian11-package
# - debian11-test
debian12-deploy:
extends: .deploy

View file

@ -16,9 +16,9 @@ rocky9-package:
script:
- make rpm-el9
debian11-package:
extends: .debian_package
image: gitlab.linphone.org:4567/bc/public/docker/debian11-php:$DEBIAN_11_IMAGE_VERSION
#debian11-package:
# extends: .debian_package
# image: gitlab.linphone.org:4567/bc/public/docker/debian11-php:$DEBIAN_11_IMAGE_VERSION
debian12-package:
extends: .debian_package

View file

@ -21,11 +21,11 @@ rocky9-test:
- php artisan key:generate
- vendor/bin/phpunit --log-junit $CI_PROJECT_DIR/flexiapi_phpunit.log
debian11-test:
extends: .debian-test
image: gitlab.linphone.org:4567/bc/public/docker/debian11-php:$DEBIAN_11_IMAGE_VERSION
needs:
- debian11-package
#debian11-test:
# extends: .debian-test
# image: gitlab.linphone.org:4567/bc/public/docker/debian11-php:$DEBIAN_11_IMAGE_VERSION
# needs:
# - debian11-package
debian12-test:
extends: .debian-test

View file

@ -1,8 +1,8 @@
variables:
ROCKY_8_IMAGE_VERSION: 20241112_111033_update_package_and_dependencies
ROCKY_9_IMAGE_VERSION: 20241112_115442_add_php_sodium
DEBIAN_11_IMAGE_VERSION: 20241112_113527_update_package_and_dependencies
DEBIAN_12_IMAGE_VERSION: 20241112_113948_update_package_and_dependencies
# DEBIAN_11_IMAGE_VERSION: 20241204_161845_update_download_linphone_org
DEBIAN_12_IMAGE_VERSION: 20241204_162237_update_download_linphone_org
PHP_REDIS_REMI_VERSION: php-pecl-redis5-5.3.6-1
PHP_IGBINARY_REMI_VERSION: php-pecl-igbinary-3.2.14-1
PHP_MSGPACK_REMI_VERSION: php-pecl-msgpack-2.2.0-1

View file

@ -13,6 +13,11 @@ v1.6
- Fix FLEXIAPI-239 Ensure to return the correct error codes as stated in the RFC6750 section 3.1
- Fix FLEXIAPI-238 Replace Material Icons with Phosphor
- Fix FLEXIAPI-240 Update the Docker images
- Fix GH-15 Add password import from CSV
- Fix FLEXIAPI-242 Add stricter validation for the AccountCreationToken Push Notification endpoint
- Fix FLEXIAPI-252 Update the hCaptcha Laravel library, use file instead of cookies to store the session to prevent empty errors bags
- Fix FLEXIAPI-254 Allow no data on POST requests to not trigger the ValidateJSON middleware
- Fix FLEXIAPI-262 Bypass the JWT auth if we have an API Key
v1.5
---

View file

@ -1,10 +1,28 @@
# Releases
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/).
## [1.6] - 2024-11-12
## Added
- **Allow the expiration of tokens and codes in the DotEnv configuration**
- **New DotEnv variables:** check all the new `*_EXPIRATION_MINUTES` for each token and code in `.env.example`
- **Phone validation system by country code:** all the provided phone numbers are now properly validated and some countries can be forbidden
- **SIP Domain management:** the account domains are now managed in a set of panels and API endpoints, this is the base of the upcoming space administration system
- **JSON validation in the API:** the provised JSON is now validated and returns an error if an issue is detected
- **CoTURN credentials support:** TURN credentials can now be generated and return through the provisioning feature
## Changed
- **Replace Material Icons with Phosphor**
## Deprecated
- **Last major version supporting the deprecated endpoints of the API**
## [1.5] - 2024-08-29
### Added

View file

@ -89,7 +89,7 @@ REDIS_DB=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=cookie
SESSION_DRIVER=file
SESSION_LIFETIME=120
# SMTP and emails

View file

@ -19,6 +19,7 @@
namespace App;
use Illuminate\Validation\Rule;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class AccountCreationToken extends Consommable

View file

@ -20,6 +20,7 @@
namespace App\Http\Controllers\Admin;
use App\Account;
use App\Password;
use App\Http\Controllers\Controller;
use Illuminate\Support\Collection;
use Illuminate\Http\Request;
@ -166,7 +167,7 @@ class AccountImportController extends Controller
$accounts = [];
$now = \Carbon\Carbon::now();
$admins = $phones = [];
$admins = $phones = $passwords = [];
foreach ($lines as $line) {
if ($line->role == 'admin') {
@ -177,6 +178,10 @@ class AccountImportController extends Controller
$phones[$line->username] = $line->phone;
}
if (!empty($line->password)) {
$passwords[$line->username] = $line->password;
}
array_push($accounts, [
'username' => $line->username,
'domain' => $request->get('domain'),
@ -199,6 +204,31 @@ class AccountImportController extends Controller
$account->admin = true;
}
// Set passwords
$passwordsToInsert = [];
$passwordAccounts = Account::whereIn('username', array_keys($passwords))
->where('domain', $request->get('domain'))
->get();
$algorithm = config('app.account_default_password_algorithm');
foreach ($passwordAccounts as $passwordAccount) {
array_push($passwordsToInsert, [
'account_id' => $passwordAccount->id,
'password' => bchash(
$passwordAccount->username,
config('app.account_realm') ?? $request->get('domain'),
$passwords[$passwordAccount->username],
$algorithm
),
'algorithm' => $algorithm
]);
}
Password::insert($passwordsToInsert);
// Set admins accounts
foreach ($phones as $username => $phone) {
$account = Account::where('username', $username)

View file

@ -26,6 +26,9 @@ use Carbon\Carbon;
use App\AccountCreationToken;
use App\AccountCreationRequestToken;
use App\Rules\PnParam;
use App\Rules\PnPrid;
use App\Rules\PnProvider;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
use App\Libraries\FlexisipPusherConnector;
@ -36,9 +39,9 @@ class CreationTokenController extends Controller
public function sendByPush(Request $request)
{
$request->validate([
'pn_provider' => 'required',
'pn_param' => 'required',
'pn_prid' => 'required',
'pn_provider' => ['required', new PnProvider],
'pn_param' => [new PnParam],
'pn_prid' => [new PnPrid],
]);
$last = AccountCreationToken::where('pn_provider', $request->get('pn_provider'))

View file

@ -99,7 +99,12 @@ class AuthenticateJWT
return $next($request);
}
if (!empty(config('app.account_authentication_bearer'))) {
if (
!empty(config('app.account_authentication_bearer'))
// Bypass the JWT auth if we have an API Key
&& !$request->header('x-api-key')
&& !$request->cookie('x-api-key')
) {
$response = new Response();
$response->header(

View file

@ -11,7 +11,7 @@ class ValidateJSON
public function handle(Request $request, Closure $next)
{
if ($request->expectsJson()) {
if ($request->expectsJson() && !empty($request->getContent())) {
json_decode($request->getContent());
if (json_last_error() !== JSON_ERROR_NONE) {
abort(400, self::$message . ': ' . json_last_error_msg());

View file

@ -0,0 +1,19 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Respect\Validation\Validator;
class PnParam implements Rule
{
public function passes($attribute, $value)
{
return $value == null || Validator::regex('/^\w+$/')->validate($value);
}
public function message()
{
return 'The :attribute should be null or contain only alphanumeric and underscore characters';
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Respect\Validation\Validator;
class PnPrid implements Rule
{
public function passes($attribute, $value)
{
return $value == null || Validator::regex('/^[\w\-\:]+$/')->validate($value);
}
public function message()
{
return 'The :attribute should be null or contain only alphanumeric, dashes and colon characters';
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Respect\Validation\Validator;
class PnProvider implements Rule
{
private $values = ['apns.dev', 'apns', 'fcm'];
public function passes($attribute, $value)
{
return in_array($value, $this->values);
}
public function message()
{
return 'The :attribute should be in ' . implode(', ', $this->values);
}
}

View file

@ -24,7 +24,7 @@
"react/socket": "^1.14",
"respect/validation": "^2.2",
"sabre/vobject": "^4.5",
"scyllaly/hcaptcha": "^4.4"
"rvxlab/hcaptcha": "^4.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.9",

654
flexiapi/composer.lock generated

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -236,7 +236,6 @@ return [
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
Scyllaly\HCaptcha\HCaptchaServiceProvider::class,
/*
* Package Service Providers...

View file

@ -1,4 +1,4 @@
Username,Password,Role,Status,Phone,Email
john,number9,user,active,+12341234,john@lennon.com
paul,a_day_in_the_life,admin,active,,paul@apple.com
ringo,allUneedIsL3ve,user,unactove,+123456,ringo@star.co.uk
ringo,allUneedIsL3ve,user,unactive,+123456,ringo@star.co.uk
1 Username Password Role Status Phone Email
2 john number9 user active +12341234 john@lennon.com
3 paul a_day_in_the_life admin active paul@apple.com
4 ringo allUneedIsL3ve user unactove unactive +123456 ringo@star.co.uk

View file

@ -201,9 +201,9 @@ Return `503` if the token was not successfully sent.
JSON parameters:
* `pn_provider` the push notification provider
* `pn_param` the push notification parameter
* `pn_prid` the push notification unique id
* `pn_provider` **required**, the push notification provider, must be in apns.dev, apns or fcm
* `pn_param` the push notification parameter, can be null or contain only alphanumeric and underscore characters
* `pn_prid` the push notification unique id, can be null or contain only alphanumeric, dashes and colon characters
### `POST /account_creation_tokens/using-account-creation-request-token`
<span class="badge badge-success">Public</span>

View file

@ -1,7 +1,7 @@
@if (captchaConfigured())
<div class="large">
<script src="https://hcaptcha.com/1/api.js?recaptchacompat=off" async="" defer=""></script>
{!! HCaptcha::display() !!}
<x-hcaptcha::widget />
@include('parts.errors', ['name' => 'h-captcha-response'])
</div>
@endif

View file

@ -64,13 +64,20 @@ class ApiAccountApiKeyTest extends TestCase
->json($this->method, '/api/accounts/me')
->assertStatus(200);
$this->keyAuthenticated($account)
->json($this->method, '/api/accounts/me')
->assertStatus(200);
// Bypass the JWT middleware
config()->set('app.account_authentication_bearer', 'fake-bearer');
$this->keyAuthenticated($account)
->json($this->method, '/api/accounts/me')
->assertStatus(200);
$this->assertDatabaseHas('api_keys', [
'account_id' => $account->id,
'requests' => 2
'requests' => 3
]);
DB::table('api_keys')->update(['ip' => 'no_localhost']);

View file

@ -36,7 +36,7 @@ class ApiAccountCreationTokenTest extends TestCase
protected $adminRoute = '/api/account_creation_tokens';
protected $method = 'POST';
protected $pnProvider = 'provider';
protected $pnProvider = 'fcm';
protected $pnParam = 'param';
protected $pnPrid = 'id';
@ -63,6 +63,27 @@ class ApiAccountCreationTokenTest extends TestCase
}
public function testCorrectParameters()
{
$this->assertSame(AccountCreationToken::count(), 0);
$this->json($this->method, $this->tokenRoute, [
'pn_provider' => 'wrong',
'pn_param' => $this->pnParam,
'pn_prid' => $this->pnPrid,
])->assertJsonValidationErrors(['pn_provider']);
$this->assertSame(AccountCreationToken::count(), 0);
$this->json($this->method, $this->tokenRoute, [
'pn_provider' => $this->pnProvider,
'pn_param' => '@wrong',
'pn_prid' => $this->pnPrid,
])->assertJsonValidationErrors(['pn_param']);
$this->assertSame(AccountCreationToken::count(), 0);
$this->json($this->method, $this->tokenRoute, [
'pn_provider' => $this->pnProvider,
'pn_param' => $this->pnParam,
'pn_prid' => '@wrong',
])->assertJsonValidationErrors(['pn_prid']);
$this->assertSame(AccountCreationToken::count(), 0);
$this->json($this->method, $this->tokenRoute, [
'pn_provider' => $this->pnProvider,
@ -173,8 +194,7 @@ class ApiAccountCreationTokenTest extends TestCase
'algorithm' => 'SHA-256',
'password' => '123',
'account_creation_token' => $token->token
])->assertStatus(422)
->assertJsonValidationErrors(['account_creation_token']);
])->assertJsonValidationErrors(['account_creation_token']);
}
public function testBlacklistedUsername()