224 lines
6.3 KiB
PHP
224 lines
6.3 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Jobs\SetServiceStats;
|
|
use App\Lib\Helper;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Crypt;
|
|
use Illuminate\Support\Str;
|
|
use Rennokki\QueryCache\Traits\QueryCacheable;
|
|
|
|
class Service extends Model
|
|
{
|
|
use HasFactory;
|
|
use QueryCacheable;
|
|
|
|
/**
|
|
* The timeout cache for this model
|
|
*
|
|
* @var integer
|
|
*/
|
|
protected $cacheFor = 0;
|
|
|
|
/**
|
|
* The attributes that are mass assignable.
|
|
*
|
|
* @var string[]
|
|
*/
|
|
protected $fillable = [
|
|
'name'
|
|
];
|
|
|
|
/**
|
|
* Service
|
|
*
|
|
* @param array $attributes
|
|
*/
|
|
public function __construct(array $attributes = [])
|
|
{
|
|
$this->cacheFor = config('api.servicesDBCache') * 60;
|
|
|
|
parent::__construct($attributes);
|
|
}
|
|
|
|
/**
|
|
* Set the base cache tags that will be present
|
|
* on all queries.
|
|
*
|
|
* ! This is used to flush caching
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getCacheBaseTags(): array
|
|
{
|
|
return [
|
|
'incollector_services',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Perform any actions required after the model boots.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected static function booted()
|
|
{
|
|
static::creating(function (self $service) {
|
|
$service->generateBearerToken();
|
|
$service->generateBearerPassword();
|
|
});
|
|
static::created(function () {
|
|
// * Flush Eloquent cache.
|
|
self::flushQueryCache();
|
|
});
|
|
static::updated(function () {
|
|
// * Flush Eloquent cache.
|
|
self::flushQueryCache();
|
|
});
|
|
static::deleted(function () {
|
|
// * Flush Eloquent cache.
|
|
self::flushQueryCache();
|
|
});
|
|
}
|
|
|
|
// * RELATIONS
|
|
|
|
/**
|
|
* ArcadiaBrand belongsToMany relation
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\belongsToMany
|
|
*/
|
|
public function arcadiaBrands()
|
|
{
|
|
return $this->belongsToMany(ArcadiaBrand::class);
|
|
}
|
|
|
|
/**
|
|
* ServiceStat hasMany relation
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
*/
|
|
public function servicesStats()
|
|
{
|
|
return $this->hasMany(ServiceStat::class);
|
|
}
|
|
|
|
// * METHODS
|
|
|
|
/**
|
|
* Increment API CRM Stat
|
|
*
|
|
* @return void
|
|
*/
|
|
public function incrementStat(): void
|
|
{
|
|
SetServiceStats::dispatch($this)->afterResponse()
|
|
->onConnection('database')->onQueue('stats');
|
|
}
|
|
|
|
/**
|
|
* Generates new bearer token
|
|
*
|
|
* @return string
|
|
*/
|
|
public function generateBearerToken(): string
|
|
{
|
|
$this->bearer_token = hash('ripemd160', Str::uuid());
|
|
$this->bearer_token_date = now();
|
|
return $this->bearer_token;
|
|
}
|
|
|
|
/**
|
|
* Generates new bearer password
|
|
* It store the crypted password in model
|
|
* and returns the plain password
|
|
*
|
|
* @return string
|
|
*/
|
|
public function generateBearerPassword(): string
|
|
{
|
|
$pwd = hash('sha256', Str::uuid());
|
|
$this->bearer_pwd = Crypt::encryptString($pwd);
|
|
return $pwd;
|
|
}
|
|
|
|
/**
|
|
* Get Service from bearerToken with its brand
|
|
*
|
|
* @param string $bearerToken
|
|
* @return \App\Models\Service
|
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException If the token matches no known service.
|
|
*/
|
|
public static function getFromBearerToken(string $bearerToken): self
|
|
{
|
|
$services = static::all();
|
|
/** @var \App\Models\Service $service */
|
|
foreach ($services as $service) {
|
|
if ($service->bearer_token and $bearerToken === $service->bearer_token) {
|
|
return $service->load([
|
|
'arcadiaBrands' => function ($query) use ($service) {
|
|
return $query->cacheTags(["service:{$service->id}:arcadiaBrands"]);
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
throw new ModelNotFoundException('Could not find service from Service Token');
|
|
}
|
|
|
|
/**
|
|
* Authenticate the request for this service
|
|
*
|
|
* This will check hash-mac and that the request is wellformed
|
|
*
|
|
* The request should have the minimum following parameters
|
|
* - timestamp using format Y-m-d H:i:s, and must be UTC (Greenwich Mean Time)
|
|
* - bearerToken the public token generated by the crm
|
|
* - signature the hashmac signature of the request
|
|
*
|
|
* ! Signature must be calculated with every parameter urlencode once only
|
|
* ! All parameters to calculate the signature must be sorted (natural sort asc)
|
|
*
|
|
* ! Signature should look like
|
|
* * httpVerb\n
|
|
* * uri\n
|
|
* * queryString
|
|
*
|
|
* ! httpVerb as well as uri must be lower case
|
|
* ! uri should include scheme, host and path and nothing else, if path is empty a trailing slash should be added
|
|
* ! eg: https://ldc-crm.fr/customer/1 or https://ldc-crm.fr/
|
|
* ! The query string shall be separated with '&', eg param1=value1¶m2=value2
|
|
* ! Key names and values of the query string must be url encoded using RFC 3986
|
|
* ! http://www.faqs.org/rfcs/rfc3986.html
|
|
* ! Signature must be hmac sha256 using the private key generatedby the crm
|
|
*
|
|
* * ! They should then be concat in a string separated by \n
|
|
*
|
|
*
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return boolean
|
|
* @throws \App\Exceptions\Api\V2\CrmException If an error occurs.
|
|
*/
|
|
public function authenticateApi(Request $request): bool
|
|
{
|
|
// ! Hack to prevent Laravel to cast empty strings to null or the opposite.
|
|
// ! If cannot decode json user legacy all() method.
|
|
$data = \json_decode($request->getContent(), true);
|
|
$data = (\is_array($data) and \json_last_error() === \JSON_ERROR_NONE) ?
|
|
array_replace_recursive($request->all(), $data) : $request->all();
|
|
|
|
$calculatedHash = Helper::signRequest(
|
|
$this->bearer_pwd,
|
|
$request->getUri(),
|
|
$request->getMethod(),
|
|
$data
|
|
);
|
|
|
|
// ! Très important hash_equals https://fr.wikipedia.org/wiki/Attaque_temporelle
|
|
return \hash_equals($calculatedHash, $request->signature);
|
|
}
|
|
}
|