- 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.
308 lines
12 KiB
PHP
308 lines
12 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\User;
|
|
|
|
class Data extends \Controllers\Base {
|
|
use \Traits\ApiKeys;
|
|
|
|
public function proceedPostRequest(array $params): array {
|
|
return match ($params['cmd']) {
|
|
'riskScore' => $this->recalculateRiskScore($params),
|
|
'reenrichment' => $this->enrichEntity($params),
|
|
'delete' => $this->deleteUser($params),
|
|
default => []
|
|
};
|
|
}
|
|
|
|
public function recalculateRiskScore(array $params): array {
|
|
$result = [];
|
|
set_error_handler([\Utils\ErrorHandler::class, 'exceptionErrorHandler']);
|
|
|
|
try {
|
|
$apiKey = $this->getCurrentOperatorApiKeyId();
|
|
$userId = (int) $params['accountid'];
|
|
|
|
[$score, $rules] = $this->getUserScore($userId, $apiKey);
|
|
$result = [
|
|
'SUCCESS_MESSAGE' => $this->f3->get('AdminUser_recalculate_risk_score_success_message'),
|
|
'score' => $score,
|
|
'rules' => $rules,
|
|
];
|
|
} catch (\ErrorException $e) {
|
|
$result = ['ERROR_CODE' => \Utils\ErrorCodes::RISK_SCORE_UPDATE_UNKNOWN_ERROR];
|
|
}
|
|
|
|
restore_error_handler();
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function enrichEntity(array $params): array {
|
|
$dataController = new \Controllers\Admin\Enrichment\Data();
|
|
$apiKey = $this->getCurrentOperatorApiKeyId();
|
|
$enrichmentKey = $this->getCurrentOperatorEnrichmentKeyString();
|
|
$type = $params['type'];
|
|
$search = $params['search'] ?? null;
|
|
$entityId = isset($params['entityId']) ? (int) $params['entityId'] : null;
|
|
|
|
return $dataController->enrichEntity($type, $search, $entityId, $apiKey, $enrichmentKey);
|
|
}
|
|
|
|
public function deleteUser(array $params): void {
|
|
$apiKey = $this->getCurrentOperatorApiKeyId();
|
|
|
|
if ($apiKey) {
|
|
$accountId = (int) $params['accountid'];
|
|
$actionType = new \Type\QueueAccountOperationActionType(\Type\QueueAccountOperationActionType::DELETE);
|
|
$accountOpQueueModel = new \Models\Queue\AccountOperationQueue($actionType);
|
|
|
|
$code = \Utils\ErrorCodes::REST_API_USER_ALREADY_SCHEDULED_FOR_DELETION;
|
|
if (!$accountOpQueueModel->isInQueue($accountId, $apiKey)) {
|
|
$code = \Utils\ErrorCodes::REST_API_USER_SUCCESSFULLY_ADDED_FOR_DELETION;
|
|
$accountOpQueueModel->add($accountId, $apiKey);
|
|
}
|
|
|
|
$this->f3->set('SESSION.extra_message_code', $code);
|
|
$this->f3->reroute('/id');
|
|
}
|
|
}
|
|
|
|
public function getUserScoreDetails(int $userId, int $apiKey): array {
|
|
$model = new \Models\User();
|
|
$user = $model->getUser($userId, $apiKey);
|
|
|
|
return [
|
|
'score_details' => $model->getApplicableRulesByAccountId($userId, $apiKey, true),
|
|
'score_calculated' => $user !== [] ? $user['score'] !== null : false,
|
|
];
|
|
}
|
|
|
|
public function getUserById(int $accountId): array {
|
|
$apiKey = $this->getCurrentOperatorApiKeyId();
|
|
|
|
$model = new \Models\User();
|
|
$user = $model->getUser($accountId, $apiKey);
|
|
|
|
$model = new \Models\Rules();
|
|
$rules = $model->getAll();
|
|
|
|
$details = [];
|
|
if ($user['score_details']) {
|
|
$scoreDetails = json_decode($user['score_details'], true);
|
|
|
|
foreach ($scoreDetails as $detail) {
|
|
$score = $detail['score'] ?? null;
|
|
$ruleUid = $detail['uid'] ?? null;
|
|
if ($score !== 0 && isset($rules[$ruleUid])) {
|
|
$item = $rules[$ruleUid];
|
|
$item['score'] = $score;
|
|
$details[] = $item;
|
|
}
|
|
}
|
|
}
|
|
|
|
usort($details, static function ($a, $b): int {
|
|
return $b['score'] <=> $a['score'];
|
|
});
|
|
|
|
$user['score_details'] = $details;
|
|
|
|
$pageTitle = $user['userid'];
|
|
if ($user['firstname'] !== null && $user['firstname'] !== '') {
|
|
$pageTitle .= sprintf(' (%s)', $user['firstname']);
|
|
}
|
|
if ($user['lastname'] !== null && $user['lastname'] !== '') {
|
|
$pageTitle .= sprintf(' (%s)', $user['lastname']);
|
|
}
|
|
$user['page_title'] = $pageTitle;
|
|
|
|
$tsColumns = ['created', 'lastseen', 'score_updated_at', 'latest_decision', 'updated', 'added_to_review'];
|
|
\Utils\TimeZones::localizeTimestampsForActiveOperator($tsColumns, $user);
|
|
|
|
return $user;
|
|
}
|
|
|
|
public function checkIfOperatorHasAccess(int $userId): bool {
|
|
$apiKey = $this->getCurrentOperatorApiKeyId();
|
|
$userModel = new \Models\User();
|
|
|
|
return $userModel->checkAccess($userId, $apiKey);
|
|
}
|
|
|
|
public function checkEnrichmentAvailability(): bool {
|
|
return $this->getCurrentOperatorEnrichmentKeyString() !== null;
|
|
}
|
|
|
|
public function addToWatchlist(int $accountId, int $apiKey): void {
|
|
$model = new \Models\Watchlist();
|
|
$model->add($accountId, $apiKey);
|
|
}
|
|
|
|
public function removeFromWatchlist(int $accountId, int $apiKey): void {
|
|
$model = new \Models\Watchlist();
|
|
$model->remove($accountId, $apiKey);
|
|
}
|
|
|
|
public function addToBlacklistQueue(int $accountId, bool $fraud, int $apiKey): void {
|
|
$actionType = new \Type\QueueAccountOperationActionType(\Type\QueueAccountOperationActionType::BLACKLIST);
|
|
$accountOpQueueModel = new \Models\Queue\AccountOperationQueue($actionType);
|
|
$inQueue = $accountOpQueueModel->isInQueue($accountId, $apiKey);
|
|
|
|
if (!$fraud) {
|
|
$this->setFraudFlag($accountId, false, $apiKey); // Directly remove blacklisted items
|
|
|
|
if ($inQueue) {
|
|
$accountOpQueueModel->removeFromQueue(); // Cancel queued operation
|
|
}
|
|
}
|
|
|
|
if (!$inQueue && $fraud) {
|
|
$accountOpQueueModel->add($accountId, $apiKey);
|
|
}
|
|
|
|
$model = new \Models\User();
|
|
$model->updateFraudFlag([$accountId], $apiKey, $fraud);
|
|
}
|
|
|
|
public function addToCalulcateRiskScoreQueue(int $accountId): void {
|
|
$apiKey = $this->getCurrentOperatorApiKeyObject();
|
|
$actionType = new \Type\QueueAccountOperationActionType(\Type\QueueAccountOperationActionType::CALCULATE_RISK_SCORE);
|
|
$accountOpQueueModel = new \Models\Queue\AccountOperationQueue($actionType);
|
|
$inQueue = $accountOpQueueModel->isInQueue($accountId, $apiKey->id);
|
|
|
|
if (!$inQueue) {
|
|
$accountOpQueueModel->add($accountId, $apiKey->id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array{accountId: int, key: int}[] $accounts
|
|
*/
|
|
public function addBatchToCalulcateRiskScoreQueue(array $accounts): void {
|
|
$actionType = new \Type\QueueAccountOperationActionType(\Type\QueueAccountOperationActionType::CALCULATE_RISK_SCORE);
|
|
$accountOpQueueModel = new \Models\Queue\AccountOperationQueue($actionType);
|
|
|
|
$accountOpQueueModel->addBatch($accounts);
|
|
}
|
|
|
|
public function setReviewedFlag(int $accountId, bool $reviewed, int $apiKey): void {
|
|
$model = new \Models\User();
|
|
$model->updateReviewedFlag($accountId, $apiKey, $reviewed);
|
|
}
|
|
|
|
public function getUserScore(int $accountId, int $apiKey): array {
|
|
$total = 0;
|
|
$rules = [];
|
|
|
|
$rulesController = new \Controllers\Admin\Rules\Data();
|
|
$rulesController->evaluateUser($accountId, $apiKey);
|
|
|
|
$model = new \Models\User();
|
|
$rules = $model->getApplicableRulesByAccountId($accountId, $apiKey);
|
|
|
|
$total = $rules[0]['total_score'] ?? 0;
|
|
array_walk($rules, function (&$rule): void {
|
|
unset($rule['total_score']);
|
|
}, $rules);
|
|
|
|
return [$total, $rules];
|
|
}
|
|
|
|
public function validate(int $accountId, array $params): int|false {
|
|
$errorCode = \Utils\Access::CSRFTokenValid($params, $this->f3);
|
|
if ($errorCode) {
|
|
return $errorCode;
|
|
}
|
|
|
|
$userHasAccess = $this->checkIfOperatorHasAccess($accountId);
|
|
|
|
return !$userHasAccess ? \Utils\ErrorCodes::OPERATOR_DOES_NOT_HAVE_ACCESS_TO_ACCOUNT : false;
|
|
}
|
|
|
|
public function getScheduledForDeletion(int $userId): array {
|
|
$apiKey = $this->getCurrentOperatorApiKeyId();
|
|
$actionType = new \Type\QueueAccountOperationActionType(\Type\QueueAccountOperationActionType::DELETE);
|
|
$accountOpQueueModel = new \Models\Queue\AccountOperationQueue($actionType);
|
|
|
|
[$scheduled, $status] = $accountOpQueueModel->isInQueueStatus($userId, $apiKey);
|
|
|
|
return [$scheduled, ($status === \Type\QueueAccountOperationStatusType::FAILED) ? \Utils\ErrorCodes::USER_DELETION_FAILED : null];
|
|
}
|
|
|
|
public function getScheduledForBlacklist(int $userId): array {
|
|
$apiKey = $this->getCurrentOperatorApiKeyId();
|
|
$actionType = new \Type\QueueAccountOperationActionType(\Type\QueueAccountOperationActionType::BLACKLIST);
|
|
$accountOpQueueModel = new \Models\Queue\AccountOperationQueue($actionType);
|
|
|
|
[$scheduled, $status] = $accountOpQueueModel->isInQueueStatus($userId, $apiKey);
|
|
|
|
return [$scheduled, ($status === \Type\QueueAccountOperationStatusType::FAILED) ? \Utils\ErrorCodes::USER_BLACKLISTING_FAILED : null];
|
|
}
|
|
|
|
public function setFraudFlag(int $accountId, bool $fraud, int $apiKey): array {
|
|
$blacklistItemsModel = new \Models\BlacklistItems();
|
|
|
|
$ips = $blacklistItemsModel->getIpsRelatedToAccountWithinOperator($accountId, $apiKey);
|
|
$emails = $blacklistItemsModel->getEmailsRelatedToAccountWithinOperator($accountId, $apiKey);
|
|
$phones = $blacklistItemsModel->getPhonesRelatedToAccountWithinOperator($accountId, $apiKey);
|
|
|
|
$relatedIpsIds = array_column($ips, 'id');
|
|
$relatedEmailsIds = array_column($emails, 'id');
|
|
$relatedPhonesIds = array_column($phones, 'id');
|
|
|
|
$ips = $blacklistItemsModel->getIpsRelatedToAccountWithinOperator($accountId, $apiKey);
|
|
$relatedIpsIds = array_column($ips, 'id');
|
|
if (count($relatedIpsIds) !== 0) {
|
|
$model = new \Models\Ip();
|
|
$model->updateFraudFlag($relatedIpsIds, $fraud, $apiKey);
|
|
}
|
|
|
|
$emails = $blacklistItemsModel->getEmailsRelatedToAccountWithinOperator($accountId, $apiKey);
|
|
$relatedEmailsIds = array_column($emails, 'id');
|
|
if (count($relatedEmailsIds) !== 0) {
|
|
$model = new \Models\Email();
|
|
$model->updateFraudFlag($relatedEmailsIds, $fraud, $apiKey);
|
|
}
|
|
|
|
$phones = $blacklistItemsModel->getPhonesRelatedToAccountWithinOperator($accountId, $apiKey);
|
|
$relatedPhonesIds = array_column($phones, 'id');
|
|
if (count($relatedPhonesIds) !== 0) {
|
|
$model = new \Models\Phone();
|
|
$model->updateFraudFlag($relatedPhonesIds, $fraud, $apiKey);
|
|
}
|
|
|
|
return array_merge($ips, $emails, $phones);
|
|
}
|
|
|
|
public function updateUserStatus(int $accountId, array $scoreData, int $apiKey): void {
|
|
$scoreData['addToReview'] = false;
|
|
|
|
$keyModel = new \Models\ApiKeys();
|
|
$keyModel->getKeyById($apiKey);
|
|
|
|
$userModel = new \Models\User();
|
|
|
|
if ($scoreData['score'] <= $keyModel->blacklist_threshold) {
|
|
$this->addToBlacklistQueue($accountId, true, $apiKey);
|
|
} elseif ($scoreData['score'] <= $keyModel->review_queue_threshold) {
|
|
$data = $userModel->getUser($accountId, $apiKey);
|
|
$scoreData['addToReview'] = $data['added_to_review'] === null && $data['fraud'] === null;
|
|
}
|
|
|
|
$userModel->updateUserStatus($accountId, $scoreData, $apiKey);
|
|
}
|
|
}
|