- Add CloudronStack/output/CloudronPackages-Artifacts/tirreno/ directory and its contents - Includes package manifest, Dockerfile, source code, documentation, and build artifacts - Add tirreno-1761840148.tar.gz as a build artifact - Add tirreno-cloudron-package-1761841304.tar.gz as the Cloudron package - Include all necessary files for the tirreno Cloudron package This adds the complete tirreno Cloudron package artifacts to the repository.
253 lines
9.0 KiB
PHP
253 lines
9.0 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Tirreno ~ Open source user analytics
|
|
* Copyright (c) Tirreno Technologies Sàrl (https://www.tirreno.com)
|
|
*
|
|
* Licensed under GNU Affero General Public License version 3 of the or any later version.
|
|
* For full copyright and license information, please see the LICENSE
|
|
* Redistributions of files must retain the above copyright notice.
|
|
*
|
|
* @copyright Copyright (c) Tirreno Technologies Sàrl (https://www.tirreno.com)
|
|
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
|
|
* @link https://www.tirreno.com Tirreno(tm)
|
|
*/
|
|
|
|
namespace Controllers\Admin\Enrichment;
|
|
|
|
class Data extends \Controllers\Base {
|
|
public function enrichEntity(string $type, ?string $search, ?int $entityId, int $apiKey, ?string $enrichmentKey): array {
|
|
if ($enrichmentKey === null) {
|
|
return ['ERROR_CODE' => \Utils\ErrorCodes::ENRICHMENT_API_KEY_DOES_NOT_EXIST];
|
|
}
|
|
set_error_handler([\Utils\ErrorHandler::class, 'exceptionErrorHandler']);
|
|
$search = $search !== null ? ['value' => $search] : null;
|
|
$result = $this->enrichEntityProcess($type, $search, $entityId, $apiKey, $enrichmentKey);
|
|
restore_error_handler();
|
|
|
|
return $result;
|
|
}
|
|
|
|
private function enrichEntityProcess(string $type, ?array $search, ?int $entityId, int $apiKey, ?string $enrichmentKey): array {
|
|
$processErrorMessage = ['ERROR_CODE' => \Utils\ErrorCodes::ENRICHMENT_API_UNKNOWN_ERROR];
|
|
|
|
if ($type === 'device') {
|
|
if ($entityId !== null) {
|
|
$model = new \Models\Device();
|
|
$device = $model->getFullDeviceInfoById($entityId, $apiKey);
|
|
if ($device !== []) {
|
|
$entityId = $device['ua_id'];
|
|
$type = 'ua';
|
|
} else {
|
|
return $processErrorMessage;
|
|
}
|
|
} else {
|
|
return $processErrorMessage;
|
|
}
|
|
}
|
|
|
|
$model = new \Models\ApiKeys();
|
|
$attributes = $model->enrichableAttributes($apiKey);
|
|
|
|
if (!array_key_exists($type, $attributes)) {
|
|
return ['ERROR_CODE' => \Utils\ErrorCodes::ENRICHMENT_API_ATTRIBUTE_IS_UNAVAILABLE];
|
|
}
|
|
|
|
$modelDb = null;
|
|
$modelResult = null;
|
|
$extraModel = null;
|
|
switch ($type) {
|
|
case 'ip':
|
|
$modelDb = new \Models\Ip();
|
|
$modelResult = new \Models\Enrichment\Ip();
|
|
$extraModel = new \Models\Enrichment\LocalhostIp();
|
|
break;
|
|
case 'email':
|
|
$modelDb = new \Models\Email();
|
|
$modelResult = new \Models\Enrichment\Email();
|
|
break;
|
|
case 'domain':
|
|
$modelDb = new \Models\Domain();
|
|
$modelResult = new \Models\Enrichment\DomainFound();
|
|
$extraModel = new \Models\Enrichment\DomainNotFound();
|
|
break;
|
|
case 'phone':
|
|
$modelDb = new \Models\Phone();
|
|
$modelResult = new \Models\Enrichment\PhoneValid();
|
|
$extraModel = new \Models\Enrichment\PhoneInvalid();
|
|
break;
|
|
case 'ua':
|
|
$modelDb = new \Models\Device();
|
|
$modelResult = new \Models\Enrichment\Device();
|
|
break;
|
|
}
|
|
|
|
if ($modelDb === null) {
|
|
return $processErrorMessage;
|
|
}
|
|
|
|
$value = $entityId !== null ? $modelDb->extractById($entityId, $apiKey) : $search;
|
|
|
|
if ($value === null || $value === []) {
|
|
return $processErrorMessage;
|
|
}
|
|
|
|
$apiError = null;
|
|
|
|
try {
|
|
[$statusCode, $response,] = $this->enrichEntityByValue($type, $value, $enrichmentKey);
|
|
$error = \Utils\ApiResponseFormats::getErrorResponseFormat();
|
|
$apiError = \Utils\ApiResponseFormats::matchResponse($response[$type] ?? [], $error) ? $response[$type]['error'] : null;
|
|
} catch (\ErrorException $e) {
|
|
return $processErrorMessage;
|
|
}
|
|
|
|
if ($statusCode === 403) {
|
|
return ['ERROR_CODE' => \Utils\ErrorCodes::ENRICHMENT_API_KEY_OVERUSE];
|
|
}
|
|
|
|
if ($type === 'ip') {
|
|
// do not raise on bogon ip
|
|
if ($apiError === \Utils\Constants::get('ENRICHMENT_IP_IS_NOT_FOUND')) {
|
|
return ['ERROR_CODE' => \Utils\ErrorCodes::ENRICHMENT_API_IP_NOT_FOUND];
|
|
} elseif ($apiError !== null && $apiError !== \Utils\Constants::get('ENRICHMENT_IP_IS_BOGON') || $statusCode !== 200 || $response[$type] === null) {
|
|
return $processErrorMessage;
|
|
}
|
|
} elseif ($apiError !== null || $statusCode !== 200 || $response[$type] === null) {
|
|
return $processErrorMessage;
|
|
}
|
|
|
|
try {
|
|
$modelResult->init($response[$type]);
|
|
} catch (\ErrorException $e) {
|
|
if ($extraModel === null) {
|
|
return $processErrorMessage;
|
|
}
|
|
try {
|
|
$extraModel->init($response[$type]);
|
|
$modelResult = $extraModel;
|
|
} catch (\ErrorException $e) {
|
|
return $processErrorMessage;
|
|
}
|
|
}
|
|
|
|
// change value in db only if $entityId was passed
|
|
if ($entityId !== null) {
|
|
try {
|
|
$modelResult->updateEntityInDb($entityId, $apiKey);
|
|
} catch (\ErrorException $e) {
|
|
return $processErrorMessage;
|
|
}
|
|
}
|
|
|
|
return [
|
|
$type => $response[$type],
|
|
'SUCCESS_MESSAGE' => $this->f3->get('AdminEnrichment_success_message'),
|
|
];
|
|
}
|
|
|
|
private function validateResponse(string $requestType, int $statusCode, ?array $result, string $errorMessage): bool|string|int {
|
|
if (!is_array($result)) {
|
|
return \Utils\ErrorCodes::ENRICHMENT_API_UNKNOWN_ERROR;
|
|
}
|
|
|
|
if ($statusCode === 200 && is_array($result) && is_array($result[$requestType])) {
|
|
return false;
|
|
}
|
|
|
|
$details = $result['detail'] ?? null;
|
|
if ($details) {
|
|
if (is_array($details)) {
|
|
$messages = array_map(function ($detail) {
|
|
if (isset($detail['msg']) && $detail['msg'] !== null && $detail['msg'] !== '') {
|
|
return $detail['msg'];
|
|
}
|
|
}, $details);
|
|
$messages = implode('; ', $messages);
|
|
} else {
|
|
$messages = $details;
|
|
}
|
|
}
|
|
|
|
if (strlen($errorMessage) > 0) {
|
|
\Utils\Logger::log('Enrichment API web error', $errorMessage);
|
|
}
|
|
|
|
if (!isset($messages) || strlen($messages) < 1) {
|
|
return \Utils\ErrorCodes::ENRICHMENT_API_UNKNOWN_ERROR;
|
|
}
|
|
|
|
return $messages;
|
|
}
|
|
|
|
private function enrichEntityByValue(string $type, array $value, string $enrichmentKey): array {
|
|
$apiUrl = \Utils\Variables::getEnrichtmentApi();
|
|
$postFields = [
|
|
$type => $value['value'],
|
|
];
|
|
|
|
$options = [
|
|
'method' => 'POST',
|
|
'header' => [
|
|
'Content-Type: application/json',
|
|
'Authorization: Bearer ' . $enrichmentKey,
|
|
'User-Agent: ' . $this->f3->get('USER_AGENT'),
|
|
],
|
|
'content' => \json_encode($postFields),
|
|
];
|
|
|
|
/** @var array{request: array<string>, body: string, headers: array<string>, engine: string, cached: bool, error: string} $result */
|
|
$result = \Web::instance()->request(
|
|
url: sprintf('%s/query', $apiUrl),
|
|
options: $options,
|
|
);
|
|
|
|
$matches = [];
|
|
preg_match('/^HTTP\/(\d+)(?:\.\d)? (\d{3})/', $result['headers'][0], $matches);
|
|
|
|
$jsonResponse = json_decode($result['body'], true);
|
|
$statusCode = (int) ($matches[2] ?? 0);
|
|
|
|
$errorMessages = $this->validateResponse($type, $statusCode, $jsonResponse, $result['error']);
|
|
|
|
return [$statusCode, $jsonResponse, $errorMessages];
|
|
}
|
|
|
|
public function getNotCheckedEntitiesCount(int $apiKey): array {
|
|
$model = new \Models\ApiKeys();
|
|
$models = $model->enrichableAttributes($apiKey);
|
|
$result = [];
|
|
|
|
foreach ($models as $type => $model) {
|
|
$result[$type] = (new $model())->countNotChecked($apiKey);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function getNotCheckedExists(int $apiKey): bool {
|
|
$model = new \Models\ApiKeys();
|
|
$models = $model->enrichableAttributes($apiKey);
|
|
|
|
foreach ($models as $model) {
|
|
if ((new $model())->notCheckedExists($apiKey)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function getNotCheckedEntitiesByUserId(int $userId, int $apiKey): array {
|
|
$model = new \Models\ApiKeys();
|
|
$models = $model->enrichableAttributes($apiKey);
|
|
$result = [];
|
|
|
|
foreach ($models as $type => $model) {
|
|
$result[$type] = (new $model())->notCheckedForUserId($userId, $apiKey);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
}
|