ldc-crm/app/Lib/Helper.php
Jérémy Munsch 81aabdab72
Version 3.0.0
This removes api authentification with token in favor of signature method.
Refactorize code and clean up. It add more unit test plus code coverage
in test/converage.

- Added config.api.timestampTimeout
- Added Helper::signRequest This calculates the request signature
- Added more unit tests and extend code coverage.
- Added rate limiter for web routes
- Added RemoveSpaces Sanitizer on api requests
- Added stress test suite
- Changed AuthServiceProvider api-token to api-hmac
- Changed CustomerSeeder to have at least of customer activated (for unit tests)
- Changed used transation for saveing multiple models
- Changed, in ServiceController generateApiToken to generateServicePassword
- Changed, timestamp has to be send in request with a DateTime format, it also has to a UTC  value
- Composer added brianium/paratest
- Composer update
- Fixed case where removing single optin could be the wrong one (not for the good service)
- Fixed customer delete
- Fixed customer would not be resolved by brand.
- Fixed Really check that request is json checking body content, preventing sending malformed requests
- Remove ApiError::needAuth
- Removed api_token
- Removed ApiController::authenticate
- Removed config.api.tokenTimeout
- Removed Header check or usage because of HAProxy doing weird stuff
- Removed lock files from gitignore
- Removed memcached dependency
- Removed unused questions resources api routes
- Removed workspace file

Migrations
- Added unique index on services.bearer_token
- Changed customer_id on customer_answer is nullable
- Changed services.bearer_pwd length to 312 (the size of the token)
- Dropped services.api_token and service.api_token_date
2022-01-06 12:11:05 +01:00

194 lines
5.7 KiB
PHP

<?php
namespace App\Lib;
use App\Exceptions\Api\V2\CrmException;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
/**
* Helper Class
*/
class Helper
{
/**
* Package infos
*
* @var array
*/
private static $packageInfos;
/**
* Tells if the request is within apiV2 routes
*
* @param Request $request
* @return boolean
*/
public static function isRequestForApiV2(Request $request)
{
// * Small hack to ensure to know if route is From APIv2.
$routeName = static::getRouteName($request) ??
(strpos($request->path(), 'api/v2') === 0 ? 'apiV2.' : null) ?? 'none';
return strpos($routeName, 'apiV2.') === 0;
}
/**
* Tells if the request is within apiV1 routes
*
* @param Request $request
* @return boolean
*/
public static function isRequestForApiV1(Request $request): bool
{
// * Small hack to ensure to know if route is From APIv1.
$routePrefix = (strpos(static::getRoutePrefix($request), 'api/v1') ? 'v1' : null) ??
(strpos($request->path(), 'api/v1') === 0 ? 'v1' : null) ?? 'none';
return strpos($routePrefix, 'v1') === 0;
}
/**
* Try to get current route name
*
* @param Request $request
* @return string|null
*/
public static function getRouteName(Request $request)
{
return Route::currentRouteName() ?? ($request->route() ? $request->route()->getName() : null);
}
/**
* Get the bearer token from request
*
* @param Request $request
* @return string|null
*/
public static function getBearerTokenFromRequest(Request $request): ?string
{
$header = $request->header('Authorization', '');
if (Str::contains($header, 'Bearer ')) {
$auths = \explode(',', $header);
foreach ($auths as $auth) {
if (Str::contains($auth, 'Bearer ')) {
return Str::substr($auth, 7);
}
}
}
return null;
}
/**
* Try to get current route prefix
*
* @param Request $request
* @return string|null
*/
public static function getRoutePrefix(Request $request)
{
return (Route::getCurrentRoute() ? Route::getCurrentRoute()->getPrefix() : null) ??
($request->route() ? $request->route()->getPrefix() : null);
}
/**
* Try to get route from request
*
* @param Request $request
* @return \Illuminate\Routing\Route|null
*/
public static function getRoute(Request $request)
{
return Route::getRoutes()->getByName(static::getRouteName($request));
}
/**
* Get composer.json infos
*
* @return array
*/
public static function getPackageInfo(): array
{
if (!static::$packageInfos) {
static::$packageInfos = \json_decode(\file_get_contents(__DIR__ . '/../../composer.json'), true);
}
return static::$packageInfos;
}
/**
* Calculate the request signature
*
* @param string $secret
* @param string $uri
* @param string $method
* @param array $parameters
* @return string request signature
* @throws CrmException If an error occurs.
*/
public static function signRequest(string $secret, string $uri, string $method, array $parameters): string
{
try {
$uri = collect(\parse_url($uri));
$method = \strtolower($method);
// * Remove signature from parameters to calculate the hashmac.
$signature = collect([
'uri' => sprintf(
'%s://%s%s',
$uri->get('scheme'),
$uri->get('host'),
$uri->get('path')
),
'method' => $method
]);
$flattenRequestArray = function (array $array) use (&$flattenRequestArray) {
$out = [];
foreach ($array as $key => $value) {
if (is_array($value)) {
$subArray = $flattenRequestArray($value);
foreach ($subArray as $subKey => $subValue) {
$out["$key.$subKey"] = $subValue;
}
continue;
}
$out[$key] = $value;
}
return $out;
};
$parameters = $flattenRequestArray($parameters);
$parameters = collect($parameters);
// * Remove signature from parameters.
$parameters->pull('signature');
// * Natural sort all parameters.
$parameters = $parameters->sortKeys(\SORT_NATURAL)
->map(function ($value, $key) {
return sprintf('%s=%s', \rawurlencode($key), \rawurlencode($value));
});
$signature->push($parameters->implode('&'));
$calculatedHash = base64_encode(hash_hmac(
'sha256',
$signature->implode('\n'),
Crypt::decryptString($secret),
true
));
if (!$calculatedHash) {
throw new CrmException('Failed to calculate requests hash');
}
return $calculatedHash;
} catch (DecryptException $e) {
throw new CrmException('Failed to decrypt service credentials', 0, $e);
} catch (\Exception $e) {
throw new CrmException('An error occured when authenticating service', 0, $e);
}//end try
}
}