- 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);
 | 
						|
    }
 | 
						|
}
 |