feat(cloudron): add tirreno package artifacts
- 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.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
<?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 Models\Api;
|
||||
|
||||
class Users extends \Models\BaseSql {
|
||||
protected $DB_TABLE_NAME = 'event_account';
|
||||
|
||||
public function getUsersByApiKey(?int $userId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':user_url' => \Utils\Variables::getSiteWithProtocol() . '/id/',
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
-- create user url
|
||||
:user_url || event_account.id AS internal_url,
|
||||
-- -event_account.id,
|
||||
event_account.userid,
|
||||
event_account.created,
|
||||
-- event_account.key,
|
||||
event_account.lastip,
|
||||
event_account.lastemail,
|
||||
event_account.lastphone,
|
||||
event_account.lastseen,
|
||||
event_account.fullname,
|
||||
event_account.firstname,
|
||||
event_account.lastname,
|
||||
-- event_account.is_important,
|
||||
event_account.total_visit,
|
||||
event_account.total_country,
|
||||
event_account.total_ip,
|
||||
event_account.total_device,
|
||||
event_account.total_shared_ip,
|
||||
event_account.total_shared_phone,
|
||||
event_account.score_updated_at,
|
||||
event_account.score,
|
||||
event_account.score_details,
|
||||
event_account.reviewed,
|
||||
event_account.fraud,
|
||||
event_account.latest_decision
|
||||
|
||||
FROM event_account'
|
||||
);
|
||||
|
||||
$where = ' WHERE key = :api_key';
|
||||
|
||||
if ($userId !== null) {
|
||||
$params[':user_id'] = $userId;
|
||||
$where .= ' AND event_account.id = :user_id';
|
||||
}
|
||||
|
||||
$query .= $where;
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,67 @@
|
||||
<?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 Models;
|
||||
|
||||
class ApiKeyCoOwner extends \Models\BaseSql {
|
||||
protected $DB_TABLE_NAME = 'dshb_api_co_owners';
|
||||
|
||||
public function getCoOwnership(int $operatorId): self|null|false {
|
||||
$filters = [
|
||||
'operator=?', $operatorId,
|
||||
];
|
||||
|
||||
return $this->load($filters);
|
||||
}
|
||||
|
||||
public function getSharedApiKeyOperators(int $operatorId): array {
|
||||
$params = [
|
||||
':creator' => $operatorId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
dshb_operators.id,
|
||||
dshb_operators.email,
|
||||
dshb_operators.is_active
|
||||
FROM
|
||||
dshb_api
|
||||
|
||||
JOIN dshb_api_co_owners
|
||||
ON dshb_api.id = dshb_api_co_owners.api
|
||||
|
||||
JOIN dshb_operators
|
||||
ON dshb_api_co_owners.operator = dshb_operators.id
|
||||
|
||||
WHERE
|
||||
dshb_api.creator = :creator;'
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function create(int $operator, int $api): void {
|
||||
$this->operator = $operator;
|
||||
$this->api = $api;
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function deleteCoOwnership(): void {
|
||||
if ($this->loaded()) {
|
||||
$this->erase();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?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 Models;
|
||||
|
||||
class ApiKeys extends \Models\BaseSql {
|
||||
protected $DB_TABLE_NAME = 'dshb_api';
|
||||
|
||||
public function add(array $data): int {
|
||||
$quote = $data['quote'];
|
||||
$operatorId = $data['operator_id'];
|
||||
$uuid = sprintf('%s%s%s', $operatorId, $quote, time());
|
||||
|
||||
$this->quote = $quote;
|
||||
$this->creator = $operatorId;
|
||||
$this->key = $this->getHash($uuid);
|
||||
|
||||
if (array_key_exists('skip_enriching_attributes', $data)) {
|
||||
$this->skip_enriching_attributes = $data['skip_enriching_attributes'];
|
||||
}
|
||||
if (array_key_exists('skip_blacklist_sync', $data)) {
|
||||
$this->skip_blacklist_sync = $data['skip_blacklist_sync'];
|
||||
}
|
||||
|
||||
$this->save();
|
||||
|
||||
return (int) $this->id;
|
||||
}
|
||||
|
||||
public function getKeys(int $operatorId): array {
|
||||
$filters = [
|
||||
'creator=?', $operatorId,
|
||||
];
|
||||
|
||||
return $this->find($filters);
|
||||
}
|
||||
|
||||
public function getKey(int $operatorId): ?ApiKeys {
|
||||
$keys = $this->getKeys($operatorId);
|
||||
|
||||
return $keys[0] ?? null;
|
||||
}
|
||||
|
||||
public function resetKey(int $keyId, int $operatorId): void {
|
||||
$this->getByKeyAndOperatorId($keyId, $operatorId);
|
||||
|
||||
if ($this->loaded()) {
|
||||
$uuid = sprintf('%s%s%s', $keyId, $operatorId, time());
|
||||
|
||||
$this->key = $this->getHash($uuid);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function getByKeyAndOperatorId(int $keyId, int $operatorId): self|null|false {
|
||||
$filters = [
|
||||
'id=? AND creator=?', $keyId, $operatorId,
|
||||
];
|
||||
|
||||
return $this->load($filters);
|
||||
}
|
||||
|
||||
public function getKeyIdByHash(string $hash): self|null|false {
|
||||
$filters = [
|
||||
'key=?', $hash,
|
||||
];
|
||||
|
||||
return $this->load($filters);
|
||||
}
|
||||
|
||||
public function getKeyById(int $keyId): self|null|false {
|
||||
$filters = [
|
||||
'id=?', $keyId,
|
||||
];
|
||||
|
||||
return $this->load($filters);
|
||||
}
|
||||
|
||||
public function getTimezoneByKeyId(int $keyId): string {
|
||||
$params = [
|
||||
':api_key' => $keyId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
dshb_operators.timezone
|
||||
FROM
|
||||
dshb_api
|
||||
JOIN dshb_operators
|
||||
ON dshb_operators.id = dshb_api.creator
|
||||
WHERE
|
||||
dshb_api.id = :api_key'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0]['timezone'] ?? 'UTC';
|
||||
}
|
||||
|
||||
public function getSkipEnrichingAttributes(int $keyId): array {
|
||||
$params = [
|
||||
':api_key' => $keyId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
dshb_api.skip_enriching_attributes
|
||||
FROM dshb_api
|
||||
WHERE
|
||||
dshb_api.id = :api_key'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
if (!count($results)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$results = json_decode($results[0]['skip_enriching_attributes']);
|
||||
|
||||
if (!\Utils\Variables::getEmailPhoneAllowed()) {
|
||||
if (!in_array('email', $results, true)) {
|
||||
$results[] = 'email';
|
||||
}
|
||||
if (!in_array('phone', $results, true)) {
|
||||
$results[] = 'phone';
|
||||
}
|
||||
if (!in_array('domain', $results, true)) {
|
||||
$results[] = 'domain';
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function enrichableAttributes(int $keyId): array {
|
||||
$skipAttributes = $this->getSkipEnrichingAttributes($keyId);
|
||||
$attributes = \Utils\Constants::get('ENRICHING_ATTRIBUTES');
|
||||
$attributes = array_diff_key($attributes, array_flip($skipAttributes));
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
public function attributeIsEnrichable(string $attr, int $keyId): bool {
|
||||
return array_key_exists($attr, $this->enrichableAttributes($keyId));
|
||||
}
|
||||
|
||||
public function getAllApiKeyIds(): array {
|
||||
$query = 'SELECT id from dshb_api';
|
||||
return $this->execQuery($query, null);
|
||||
}
|
||||
|
||||
public function updateSkipEnrichingAttributes(array $attributes): void {
|
||||
if ($this->loaded()) {
|
||||
$attributes = \array_values($attributes);
|
||||
$this->skip_enriching_attributes = \json_encode($attributes);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function updateSkipBlacklistSynchronisation(bool $skip): void {
|
||||
if ($this->loaded()) {
|
||||
$this->skip_blacklist_sync = $skip;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function updateRetentionPolicy(int $policyInWeeks): void {
|
||||
if ($this->loaded()) {
|
||||
$this->retention_policy = $policyInWeeks;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function updateBlacklistThreshold(int $value): void {
|
||||
if ($this->loaded()) {
|
||||
$this->blacklist_threshold = $value;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function updateReviewQueueThreshold(int $value): void {
|
||||
if ($this->loaded()) {
|
||||
$this->review_queue_threshold = $value;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function updateInternalToken(string $apiToken): void {
|
||||
if ($this->loaded()) {
|
||||
$this->token = $apiToken;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?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 Models;
|
||||
|
||||
abstract class BaseSql extends \DB\SQL\Mapper {
|
||||
use \Traits\Debug;
|
||||
|
||||
protected $f3 = null;
|
||||
protected $DB_TABLE_TTL = 0;
|
||||
protected $DB_TABLE_NAME = null;
|
||||
protected $DB_TABLE_FIELDS = null;
|
||||
|
||||
public function __construct() {
|
||||
$this->f3 = \Base::instance();
|
||||
|
||||
if ($this->DB_TABLE_NAME) {
|
||||
$DB = $this->getDatabaseConnection();
|
||||
parent::__construct($DB, $this->DB_TABLE_NAME, $this->DB_TABLE_FIELDS, $this->DB_TABLE_TTL);
|
||||
}
|
||||
}
|
||||
|
||||
private function getDatabaseConnection(): ?\DB\SQL {
|
||||
return $this->f3->get('API_DATABASE');
|
||||
}
|
||||
|
||||
public function getHash(string $string): string {
|
||||
$iterations = 1000;
|
||||
$salt = $this->f3->get('SALT');
|
||||
|
||||
return hash_pbkdf2('sha256', $string, $salt, $iterations, 32);
|
||||
}
|
||||
|
||||
public function getPseudoRandomString(int $length = 32): string {
|
||||
$bytes = \openssl_random_pseudo_bytes($length / 2);
|
||||
|
||||
return \bin2hex($bytes);
|
||||
}
|
||||
|
||||
public function printLog(): void {
|
||||
echo $this->f3->get('API_DATABASE')->log();
|
||||
}
|
||||
|
||||
public function getArrayPlaceholders(array $ids, string $postfix = ''): array {
|
||||
$params = [];
|
||||
$placeHolders = [];
|
||||
|
||||
$postfix = $postfix !== '' ? '_' . $postfix : '';
|
||||
|
||||
foreach ($ids as $i => $id) {
|
||||
$key = sprintf(':item_id_%s%s', $i, $postfix);
|
||||
$placeHolders[] = $key;
|
||||
$params[$key] = $id;
|
||||
}
|
||||
|
||||
$placeHolders = implode(', ', $placeHolders);
|
||||
|
||||
return [$params, $placeHolders];
|
||||
}
|
||||
|
||||
public function execQuery(string $query, ?array $params): array|int|null {
|
||||
return $this->getDatabaseConnection()->exec($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?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 Models;
|
||||
|
||||
class BlacklistItems extends \Models\BaseSql {
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getIpsRelatedToAccountWithinOperator(int $accountId, int $apiKey): array {
|
||||
$params = [
|
||||
':account_id' => $accountId,
|
||||
':api_key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = ("
|
||||
SELECT
|
||||
event_ip.id,
|
||||
event_ip.ip AS value,
|
||||
'ip' AS type,
|
||||
:account_id::bigint AS account_id
|
||||
FROM event_ip
|
||||
WHERE
|
||||
(id, key) IN (
|
||||
SELECT ip, key
|
||||
FROM event
|
||||
WHERE
|
||||
account = :account_id
|
||||
AND key = :api_key)");
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function getEmailsRelatedToAccountWithinOperator(int $accountId, int $apiKey): array {
|
||||
$params = [
|
||||
':account_id' => $accountId,
|
||||
':api_key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = ("
|
||||
SELECT
|
||||
id,
|
||||
email AS value,
|
||||
'email' AS type,
|
||||
account_id
|
||||
FROM
|
||||
event_email
|
||||
WHERE
|
||||
key = :api_key
|
||||
AND account_id = :account_id
|
||||
");
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function getPhonesRelatedToAccountWithinOperator(int $accountId, int $apiKey): array {
|
||||
$params = [
|
||||
':account_id' => $accountId,
|
||||
':api_key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = ("
|
||||
SELECT
|
||||
id,
|
||||
phone_number AS value,
|
||||
'phone' AS type,
|
||||
account_id
|
||||
FROM
|
||||
event_phone
|
||||
WHERE
|
||||
key = :api_key
|
||||
AND account_id = :account_id
|
||||
");
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function searchBlacklistedItem(string $value, int $apiKey): ?bool {
|
||||
$query = '';
|
||||
$params = [
|
||||
':value' => $value,
|
||||
':api_key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = ('
|
||||
SELECT 1
|
||||
FROM event_account
|
||||
WHERE
|
||||
userid = :value AND
|
||||
fraud IS TRUE AND
|
||||
key = :api_key
|
||||
LIMIT 1');
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return (bool) count($results);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?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 Models;
|
||||
|
||||
class Bot extends \Models\BaseSql implements \Interfaces\ApiKeyAccessAuthorizationInterface {
|
||||
protected $DB_TABLE_NAME = 'event_ua_parsed';
|
||||
|
||||
public function checkAccess(int $subjectId, int $apiKey): bool {
|
||||
$query = (
|
||||
'SELECT
|
||||
event_ua_parsed.id
|
||||
|
||||
FROM
|
||||
event_ua_parsed
|
||||
|
||||
WHERE
|
||||
event_ua_parsed.key = :api_key
|
||||
AND event_ua_parsed.id = :ua_id'
|
||||
);
|
||||
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':ua_id' => $subjectId,
|
||||
];
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return count($results) > 0;
|
||||
}
|
||||
|
||||
public function getFullBotInfoById(int $uaId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':ua_id' => $uaId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_ua_parsed.id,
|
||||
event_ua_parsed.device,
|
||||
event_ua_parsed.device AS title,
|
||||
event_ua_parsed.browser_name,
|
||||
event_ua_parsed.browser_version,
|
||||
event_ua_parsed.os_name,
|
||||
event_ua_parsed.os_version,
|
||||
event_ua_parsed.ua,
|
||||
event_ua_parsed.modified,
|
||||
event_ua_parsed.checked
|
||||
FROM
|
||||
event_ua_parsed
|
||||
|
||||
WHERE
|
||||
event_ua_parsed.key = :api_key AND
|
||||
event_ua_parsed.id = :ua_id'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0] ?? [];
|
||||
}
|
||||
|
||||
public function extractById(int $entityId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':id' => $entityId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
COALESCE(event_ua_parsed.ua, '') AS value
|
||||
|
||||
FROM
|
||||
event_ua_parsed
|
||||
|
||||
WHERE
|
||||
event_ua_parsed.key = :api_key
|
||||
AND event_ua_parsed.id = :id
|
||||
|
||||
LIMIT 1"
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0] ?? [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?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 Models;
|
||||
|
||||
class ChangeEmail extends \Models\BaseSql {
|
||||
protected $DB_TABLE_NAME = 'dshb_operators_change_email';
|
||||
|
||||
public function add(int $operatorId, string $email): void {
|
||||
$record = $this->getUnusedKeyByOperatorId($operatorId);
|
||||
|
||||
if ($record) {
|
||||
$this->status = 'invalidated';
|
||||
$this->save();
|
||||
}
|
||||
|
||||
$this->reset();
|
||||
$this->renew_key = $this->getPseudoRandomString(32);
|
||||
$this->operator_id = $operatorId;
|
||||
$this->email = $email;
|
||||
$this->status = 'unused';
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function getUnusedKeyByOperatorId(int $operatorId): self|null|false {
|
||||
return $this->load(
|
||||
['"operator_id"=? AND "status"=?', $operatorId, 'unused'],
|
||||
);
|
||||
}
|
||||
|
||||
public function getByRenewKey(string $key): self|null|false {
|
||||
return $this->load(
|
||||
['"renew_key"=? AND "status"=?', $key, 'unused'],
|
||||
);
|
||||
}
|
||||
|
||||
public function deactivate(): void {
|
||||
if ($this->loaded()) {
|
||||
$this->status = 'used';
|
||||
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
abstract class Base extends \Models\BaseSql {
|
||||
use \Traits\DateRange;
|
||||
|
||||
protected function concatDataLines(array $data1, string $field1, array $data2, string $field2, array $data3 = [], ?string $field3 = null): array {
|
||||
$data0 = [];
|
||||
$iters = count($data1);
|
||||
|
||||
for ($i = 0; $i < $iters; ++$i) {
|
||||
$item = $data1[$i];
|
||||
$ts = $item['ts'];
|
||||
|
||||
$data0[$ts] = [
|
||||
'ts' => $ts,
|
||||
$field1 => $item[$field1],
|
||||
$field2 => 0,
|
||||
];
|
||||
|
||||
if ($field3) {
|
||||
$data0[$ts][$field3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$iters = count($data2);
|
||||
|
||||
for ($i = 0; $i < $iters; ++$i) {
|
||||
$item = $data2[$i];
|
||||
$ts = $item['ts'];
|
||||
|
||||
if (!array_key_exists($ts, $data0)) {
|
||||
$data0[$ts] = [
|
||||
'ts' => $ts,
|
||||
$field1 => 0,
|
||||
$field2 => 0,
|
||||
];
|
||||
|
||||
if ($field3) {
|
||||
$data0[$ts][$field3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$data0[$ts][$field2] = $item[$field2];
|
||||
}
|
||||
|
||||
$iters = count($data3);
|
||||
|
||||
for ($i = 0; $i < $iters; ++$i) {
|
||||
$item = $data3[$i];
|
||||
$ts = $item['ts'];
|
||||
|
||||
if (!array_key_exists($ts, $data0)) {
|
||||
$data0[$ts] = [
|
||||
'ts' => $ts,
|
||||
$field1 => 0,
|
||||
$field2 => 0,
|
||||
$field3 => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$data0[$ts][$field3] = $item[$field3];
|
||||
}
|
||||
|
||||
// TODO: tmp order troubles fix
|
||||
usort($data0, function ($a, $b) {
|
||||
return $a['ts'] - $b['ts'];
|
||||
});
|
||||
|
||||
return $data0;
|
||||
}
|
||||
|
||||
protected function addEmptyDays(array $params): array {
|
||||
$cnt = count($params);
|
||||
$data = array_fill(0, $cnt, []);
|
||||
|
||||
$request = $this->f3->get('REQUEST');
|
||||
$step = \Utils\Constants::get('CHART_RESOLUTION')[$this->getResolution($request)];
|
||||
// use offset shift because $startTs/$endTs compared with shifted ['ts']
|
||||
$offset = \Utils\TimeZones::getCurrentOperatorOffset();
|
||||
$dateRange = $this->getDatesRange($request, $offset);
|
||||
|
||||
if (!$dateRange) {
|
||||
$now = time() + $offset;
|
||||
$week = 7 * 24 * 60 * 60;
|
||||
if (count($params[0]) === 0) {
|
||||
$dateRange = [
|
||||
'endDate' => date('Y-m-d H:i:s', $now),
|
||||
'startDate' => date('Y-m-d 00:00:01', $now - $week),
|
||||
];
|
||||
} else {
|
||||
$firstTs = ($now - $params[0][0] < $week) ? $now - $week : $params[0][0];
|
||||
$dateRange = [
|
||||
'endDate' => date('Y-m-d H:i:s', $now),
|
||||
'startDate' => date('Y-m-d 00:00:01', $firstTs),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$endTs = strtotime($dateRange['endDate']);
|
||||
$startTs = strtotime($dateRange['startDate']);
|
||||
|
||||
$endTs = $endTs - ($endTs % $step);
|
||||
$startTs = $startTs - ($startTs % $step);
|
||||
|
||||
$ox = $params[0];
|
||||
|
||||
while ($endTs >= $startTs) {
|
||||
$itemIdx = array_search($startTs, $ox);
|
||||
|
||||
$data[0][] = $startTs;
|
||||
|
||||
for ($i = 1; $i < $cnt; ++$i) {
|
||||
$data[$i][] = ($itemIdx !== false) ? $params[$i][$itemIdx] : 0;
|
||||
}
|
||||
|
||||
$startTs += $step;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function execute(string $query, int $apiKey): array {
|
||||
$request = $this->f3->get('REQUEST');
|
||||
|
||||
// do not use offset because :start_time/:end_time compared with UTC db timestamps
|
||||
$dateRange = $this->getDatesRange($request);
|
||||
|
||||
// Search request does not contain daterange param
|
||||
if (!$dateRange) {
|
||||
$dateRange = [
|
||||
'endDate' => date('Y-m-d H:i:s'),
|
||||
'startDate' => date('Y-m-d H:i:s', 0),
|
||||
];
|
||||
}
|
||||
|
||||
$offset = \Utils\TimeZones::getCurrentOperatorOffset();
|
||||
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':end_time' => $dateRange['endDate'],
|
||||
':start_time' => $dateRange['startDate'],
|
||||
':resolution' => $this->getResolution($request),
|
||||
':offset' => strval($offset), // str for postgres
|
||||
];
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class BaseEventsCount extends \Models\BaseSql {
|
||||
use \Traits\DateRange;
|
||||
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
protected $alertTypesParams;
|
||||
protected $editTypesParams;
|
||||
protected $normalTypesParams;
|
||||
|
||||
protected $alertFlatIds;
|
||||
protected $editFlatIds;
|
||||
protected $normalFlatIds;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
[$this->alertTypesParams, $this->alertFlatIds] = $this->getArrayPlaceholders(\Utils\Constants::get('ALERT_EVENT_TYPES'), 'alert');
|
||||
[$this->editTypesParams, $this->editFlatIds] = $this->getArrayPlaceholders(\Utils\Constants::get('EDITING_EVENT_TYPES'), 'edit');
|
||||
[$this->normalTypesParams, $this->normalFlatIds] = $this->getArrayPlaceholders(\Utils\Constants::get('NORMAL_EVENT_TYPES'), 'normal');
|
||||
}
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$itemsByDate = [];
|
||||
$items = $this->getCounts($apiKey);
|
||||
|
||||
foreach ($items as $item) {
|
||||
$itemsByDate[$item['ts']] = [
|
||||
$item['event_normal_type_count'],
|
||||
$item['event_editing_type_count'],
|
||||
$item['event_alert_type_count'],
|
||||
];
|
||||
}
|
||||
$request = $this->f3->get('REQUEST');
|
||||
// use offset shift because $startTs/$endTs compared with shifted ['ts']
|
||||
$offset = \Utils\TimeZones::getCurrentOperatorOffset();
|
||||
$datesRange = $this->getLatestNDatesRange(180, $offset);
|
||||
$endTs = strtotime($datesRange['endDate']);
|
||||
$startTs = strtotime($datesRange['startDate']);
|
||||
$step = \Utils\Constants::get('CHART_RESOLUTION')[$this->getResolution($request)];
|
||||
|
||||
$endTs = $endTs - ($endTs % $step);
|
||||
$startTs = $startTs - ($startTs % $step);
|
||||
|
||||
while ($endTs >= $startTs) {
|
||||
if (!isset($itemsByDate[$startTs])) {
|
||||
$itemsByDate[$startTs] = [null, null, null];
|
||||
}
|
||||
|
||||
$startTs += $step;
|
||||
}
|
||||
|
||||
ksort($itemsByDate);
|
||||
|
||||
$ox = [];
|
||||
$l1 = [];
|
||||
$l2 = [];
|
||||
$l3 = [];
|
||||
|
||||
foreach ($itemsByDate as $key => $value) {
|
||||
$ox[] = $key;
|
||||
$l1[] = $value[0];
|
||||
$l2[] = $value[1];
|
||||
$l3[] = $value[2];
|
||||
}
|
||||
|
||||
return [$ox, $l1, $l2, $l3];
|
||||
}
|
||||
|
||||
protected function executeOnRangeById(string $query, int $apiKey): array {
|
||||
$request = $this->f3->get('REQUEST');
|
||||
// do not use offset because :start_time/:end_time compared with UTC event.time
|
||||
$dateRange = $this->getLatestNDatesRange(180);
|
||||
$offset = \Utils\TimeZones::getCurrentOperatorOffset();
|
||||
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':end_time' => $dateRange['endDate'],
|
||||
':start_time' => $dateRange['startDate'],
|
||||
':resolution' => $this->getResolution($request),
|
||||
':id' => $request['id'],
|
||||
':offset' => strval($offset), // str for postgres
|
||||
];
|
||||
|
||||
$params = array_merge($params, $this->alertTypesParams);
|
||||
$params = array_merge($params, $this->editTypesParams);
|
||||
$params = array_merge($params, $this->normalTypesParams);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Blacklist extends Base {
|
||||
public function getData(int $apiKey): array {
|
||||
$data = $this->getFirstLine($apiKey);
|
||||
|
||||
$timestamps = array_column($data, 'ts');
|
||||
$line1 = array_column($data, 'ts_new_records');
|
||||
|
||||
return $this->addEmptyDays([$timestamps, $line1]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, tbl.created + :offset))::bigint AS ts,
|
||||
COUNT(*) AS ts_new_records
|
||||
FROM (
|
||||
SELECT DISTINCT
|
||||
blacklist.accountid,
|
||||
blacklist.created,
|
||||
extra.type,
|
||||
CASE extra.type
|
||||
WHEN \'ip\' THEN blacklist.ip
|
||||
WHEN \'email\' THEN blacklist.email
|
||||
WHEN \'phone\' THEN blacklist.phone
|
||||
END AS value
|
||||
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
event_account.id AS accountid,
|
||||
event_account.latest_decision AS created,
|
||||
CASE WHEN event_ip.fraud_detected THEN split_part(event_ip.ip::text, \'/\', 1) ELSE NULL END AS ip,
|
||||
event_ip.fraud_detected AS ip_fraud,
|
||||
CASE WHEN event_email.fraud_detected THEN event_email.email ELSE NULL END AS email,
|
||||
event_email.fraud_detected AS email_fraud,
|
||||
CASE WHEN event_phone.fraud_detected THEN event_phone.phone_number ELSE NULL END AS phone,
|
||||
event_phone.fraud_detected AS phone_fraud
|
||||
FROM event
|
||||
|
||||
LEFT JOIN event_account
|
||||
ON event_account.id = event.account
|
||||
|
||||
LEFT JOIN event_ip
|
||||
ON event_ip.id = event.ip
|
||||
|
||||
LEFT JOIN event_email
|
||||
ON event_email.id = event.email
|
||||
|
||||
LEFT JOIN event_phone
|
||||
ON event_phone.id = event.phone
|
||||
|
||||
WHERE
|
||||
event_account.key = :api_key AND
|
||||
event_account.fraud IS TRUE AND
|
||||
event_account.latest_decision >= :start_time AND
|
||||
event_account.latest_decision <= :end_time AND
|
||||
(
|
||||
event_email.fraud_detected IS TRUE OR
|
||||
event_ip.fraud_detected IS TRUE OR
|
||||
event_phone.fraud_detected IS TRUE
|
||||
)
|
||||
) AS blacklist,
|
||||
LATERAL (
|
||||
VALUES
|
||||
(CASE WHEN ip_fraud = true THEN \'ip\' END),
|
||||
(CASE WHEN email_fraud = true THEN \'email\' END),
|
||||
(CASE WHEN phone_fraud = true THEN \'phone\' END)
|
||||
) AS extra(type)
|
||||
|
||||
WHERE
|
||||
extra.type IS NOT NULL
|
||||
) AS tbl
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Bot extends BaseEventsCount {
|
||||
public function getCounts(int $apiKey): array {
|
||||
$query = (
|
||||
"SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(CASE WHEN event.type IN ({$this->normalFlatIds}) THEN TRUE END) AS event_normal_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->editFlatIds}) THEN TRUE END) AS event_editing_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->alertFlatIds}) THEN TRUE END) AS event_alert_type_count
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_device
|
||||
ON (event.device = event_device.id)
|
||||
|
||||
INNER JOIN event_ua_parsed
|
||||
ON (event_device.user_agent = event_ua_parsed.id)
|
||||
|
||||
WHERE
|
||||
event_ua_parsed.id = :id AND
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts"
|
||||
);
|
||||
|
||||
return $this->executeOnRangeById($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Bots extends Base {
|
||||
protected $DB_TABLE_NAME = 'event_device';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$data = $this->getFirstLine($apiKey);
|
||||
|
||||
$ox = array_column($data, 'ts');
|
||||
$l1 = array_column($data, 'bot_count');
|
||||
|
||||
return $this->addEmptyDays([$ox, $l1]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(DISTINCT (
|
||||
CASE WHEN event_ua_parsed.modified IS TRUE THEN event.device END)
|
||||
) AS bot_count
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_device
|
||||
ON(event.device = event_device.id)
|
||||
INNER JOIN event_ua_parsed
|
||||
ON(event_device.user_agent=event_ua_parsed.id)
|
||||
|
||||
WHERE
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Country extends BaseEventsCount {
|
||||
public function getCounts(int $apiKey): array {
|
||||
$query = (
|
||||
"SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(CASE WHEN event.type IN ({$this->normalFlatIds}) THEN TRUE END) AS event_normal_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->editFlatIds}) THEN TRUE END) AS event_editing_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->alertFlatIds}) THEN TRUE END) AS event_alert_type_count
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_ip
|
||||
ON (event.ip = event_ip.id)
|
||||
|
||||
INNER JOIN countries
|
||||
ON (event_ip.country = countries.id)
|
||||
|
||||
WHERE
|
||||
countries.id = :id AND
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts"
|
||||
);
|
||||
|
||||
return $this->executeOnRangeById($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Domain extends BaseEventsCount {
|
||||
public function getCounts(int $apiKey): array {
|
||||
$query = (
|
||||
"SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(CASE WHEN event.type IN ({$this->normalFlatIds}) THEN TRUE END) AS event_normal_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->editFlatIds}) THEN TRUE END) AS event_editing_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->alertFlatIds}) THEN TRUE END) AS event_alert_type_count
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_email
|
||||
ON (event.email = event_email.id)
|
||||
|
||||
WHERE
|
||||
event_email.domain = :id AND
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts"
|
||||
);
|
||||
|
||||
return $this->executeOnRangeById($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Domains extends Base {
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$field1 = 'unique_domains_count';
|
||||
$data1 = $this->getFirstLine($apiKey);
|
||||
|
||||
$field2 = 'ts_new_domains';
|
||||
$data2 = $this->getSecondLine($apiKey);
|
||||
|
||||
$data0 = $this->concatDataLines($data1, $field1, $data2, $field2);
|
||||
|
||||
$indexedData = array_values($data0);
|
||||
$timestamps = array_column($indexedData, 'ts');
|
||||
$line1 = array_column($indexedData, $field1);
|
||||
$line2 = array_column($indexedData, $field2);
|
||||
|
||||
return $this->addEmptyDays([$timestamps, $line1, $line2]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(DISTINCT event_email.domain) AS unique_domains_count
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_email
|
||||
ON (event.email = event_email.id)
|
||||
|
||||
WHERE
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey);
|
||||
}
|
||||
|
||||
private function getSecondLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event_domain.created + :offset))::bigint AS ts,
|
||||
COUNT(event_domain.id) AS ts_new_domains
|
||||
FROM
|
||||
event_domain
|
||||
|
||||
WHERE
|
||||
event_domain.key = :api_key AND
|
||||
event_domain.created >= :start_time AND
|
||||
event_domain.created <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Emails extends Base {
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$data = $this->getFirstLine($apiKey);
|
||||
|
||||
$timestamps = array_column($data, 'ts');
|
||||
$line1 = array_column($data, 'email_count');
|
||||
$line2 = array_column($data, 'blockemails_count');
|
||||
|
||||
return $this->addEmptyDays([$timestamps, $line1, $line2]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(DISTINCT event.email) AS email_count,
|
||||
COUNT(DISTINCT (
|
||||
CASE WHEN event_email.blockemails IS TRUE THEN event.email END)
|
||||
) AS blockemails_count
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_email
|
||||
ON (event.email = event_email.id)
|
||||
|
||||
WHERE
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Events extends Base {
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$data = $this->getFirstLine($apiKey);
|
||||
|
||||
$timestamps = array_column($data, 'ts');
|
||||
$line1 = array_column($data, 'event_normal_type_count');
|
||||
$line2 = array_column($data, 'event_editing_type_count');
|
||||
$line3 = array_column($data, 'event_alert_type_count');
|
||||
$line4 = array_column($data, 'unauthorized_event_count');
|
||||
|
||||
return $this->addEmptyDays([$timestamps, $line1, $line2, $line3, $line4]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey): array {
|
||||
$request = $this->f3->get('REQUEST');
|
||||
$dateRange = $this->getDatesRange($request);
|
||||
if (!$dateRange) {
|
||||
$dateRange = [
|
||||
'endDate' => date('Y-m-d H:i:s'),
|
||||
'startDate' => date('Y-m-d H:i:s', 0),
|
||||
];
|
||||
}
|
||||
$offset = \Utils\TimeZones::getCurrentOperatorOffset();
|
||||
[$alertTypesParams, $alertFlatIds] = $this->getArrayPlaceholders(\Utils\Constants::get('ALERT_EVENT_TYPES'), 'alert');
|
||||
[$editTypesParams, $editFlatIds] = $this->getArrayPlaceholders(\Utils\Constants::get('EDITING_EVENT_TYPES'), 'edit');
|
||||
[$normalTypesParams, $normalFlatIds] = $this->getArrayPlaceholders(\Utils\Constants::get('NORMAL_EVENT_TYPES'), 'normal');
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':end_time' => $dateRange['endDate'],
|
||||
':start_time' => $dateRange['startDate'],
|
||||
':resolution' => $this->getResolution($request),
|
||||
':offset' => strval($offset),
|
||||
':unauth' => \Utils\Constants::get('UNAUTHORIZED_USERID'),
|
||||
];
|
||||
$params = array_merge($params, $alertTypesParams);
|
||||
$params = array_merge($params, $editTypesParams);
|
||||
$params = array_merge($params, $normalTypesParams);
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(CASE WHEN event.type IN ({$normalFlatIds}) THEN TRUE END) AS event_normal_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$editFlatIds}) THEN TRUE END) AS event_editing_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$alertFlatIds}) THEN TRUE END) AS event_alert_type_count,
|
||||
COUNT(CASE WHEN event_account.userid = :unauth THEN TRUE END) AS unauthorized_event_count
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
LEFT JOIN event_account
|
||||
ON event.account = event_account.id
|
||||
|
||||
WHERE
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts"
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Ip extends BaseEventsCount {
|
||||
public function getCounts(int $apiKey): array {
|
||||
$query = (
|
||||
"SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(CASE WHEN event.type IN ({$this->normalFlatIds}) THEN TRUE END) AS event_normal_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->editFlatIds}) THEN TRUE END) AS event_editing_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->alertFlatIds}) THEN TRUE END) AS event_alert_type_count
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
WHERE
|
||||
event.ip = :id AND
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts"
|
||||
);
|
||||
|
||||
return $this->executeOnRangeById($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Ips extends Base {
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$data = $this->getFirstLine($apiKey);
|
||||
|
||||
$timestamps = array_column($data, 'ts');
|
||||
$line1 = array_column($data, 'residence_ip_count');
|
||||
$line2 = array_column($data, 'total_privacy');
|
||||
$line3 = array_column($data, 'suspicious_ip_count');
|
||||
|
||||
return $this->addEmptyDays([$timestamps, $line1, $line2, $line3]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(DISTINCT event.ip) AS unique_ip_count,
|
||||
|
||||
COUNT(DISTINCT
|
||||
CASE
|
||||
WHEN event_ip.data_center IS TRUE OR
|
||||
event_ip.tor IS TRUE OR
|
||||
event_ip.vpn IS TRUE
|
||||
THEN event.ip
|
||||
ELSE NULL
|
||||
END
|
||||
) AS total_privacy,
|
||||
|
||||
COUNT(DISTINCT event.ip) - COUNT(DISTINCT
|
||||
CASE
|
||||
WHEN event_ip.data_center IS TRUE OR
|
||||
event_ip.tor IS TRUE OR
|
||||
event_ip.vpn IS TRUE
|
||||
THEN event.ip
|
||||
ELSE NULL
|
||||
END
|
||||
) AS residence_ip_count,
|
||||
|
||||
COUNT(DISTINCT
|
||||
CASE
|
||||
WHEN event_ip.blocklist IS TRUE OR
|
||||
event_ip.fraud_detected IS TRUE
|
||||
THEN event.ip
|
||||
ELSE NULL
|
||||
END
|
||||
) AS suspicious_ip_count
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_ip
|
||||
ON (event.ip = event_ip.id)
|
||||
|
||||
WHERE
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Isp extends BaseEventsCount {
|
||||
public function getCounts(int $apiKey): array {
|
||||
$query = (
|
||||
"SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(CASE WHEN event.type IN ({$this->normalFlatIds}) THEN TRUE END) AS event_normal_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->editFlatIds}) THEN TRUE END) AS event_editing_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->alertFlatIds}) THEN TRUE END) AS event_alert_type_count
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_ip
|
||||
ON (event.ip = event_ip.id)
|
||||
|
||||
LEFT JOIN event_isp
|
||||
ON (event_ip.isp = event_isp.id)
|
||||
|
||||
WHERE
|
||||
event_isp.id = :id AND
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts"
|
||||
);
|
||||
|
||||
return $this->executeOnRangeById($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Isps extends Base {
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$field1 = 'unique_isps_count';
|
||||
$data1 = $this->getFirstLine($apiKey);
|
||||
|
||||
$field2 = 'ts_new_isps';
|
||||
$data2 = $this->getSecondLine($apiKey);
|
||||
|
||||
$data0 = $this->concatDataLines($data1, $field1, $data2, $field2);
|
||||
$indexedData = array_values($data0);
|
||||
|
||||
$timestamps = array_column($indexedData, 'ts');
|
||||
$line1 = array_column($indexedData, $field1);
|
||||
$line2 = array_column($indexedData, $field2);
|
||||
|
||||
return $this->addEmptyDays([$timestamps, $line1, $line2]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(DISTINCT event_isp.id) AS unique_isps_count
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_ip
|
||||
ON (event.ip = event_ip.id)
|
||||
|
||||
LEFT JOIN event_isp
|
||||
ON (event_ip.isp = event_isp.id)
|
||||
|
||||
WHERE
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey);
|
||||
}
|
||||
|
||||
private function getSecondLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event_isp.created + :offset))::bigint AS ts,
|
||||
COUNT(event_isp.id) AS ts_new_isps
|
||||
FROM
|
||||
event_isp
|
||||
|
||||
WHERE
|
||||
event_isp.key = :api_key AND
|
||||
event_isp.created >= :start_time AND
|
||||
event_isp.created <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Logbook extends Base {
|
||||
protected $DB_TABLE_NAME = 'event_logbook';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$data = $this->getFirstLine($apiKey);
|
||||
|
||||
$timestamps = array_column($data, 'ts');
|
||||
$line1 = array_column($data, 'event_normal_type_count');
|
||||
$line2 = array_column($data, 'event_issued_type_count');
|
||||
$line3 = array_column($data, 'event_failed_type_count');
|
||||
|
||||
return $this->addEmptyDays([$timestamps, $line1, $line2, $line3]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey): array {
|
||||
$request = $this->f3->get('REQUEST');
|
||||
$dateRange = $this->getDatesRange($request);
|
||||
if (!$dateRange) {
|
||||
$dateRange = [
|
||||
'endDate' => date('Y-m-d H:i:s'),
|
||||
'startDate' => date('Y-m-d H:i:s', 0),
|
||||
];
|
||||
}
|
||||
|
||||
//$dateRange['endDate'] = \Utils\TimeZones::localizeForActiveOperator($dateRange['endDate']);
|
||||
//$dateRange['startDate'] = \Utils\TimeZones::localizeForActiveOperator($dateRange['startDate']);
|
||||
|
||||
[$failedTypesParams, $failedFlatIds] = $this->getArrayPlaceholders(\Utils\Constants::get('FAILED_LOGBOOK_EVENT_TYPES'), 'failed');
|
||||
[$issuedTypesParams, $issuedFlatIds] = $this->getArrayPlaceholders(\Utils\Constants::get('ISSUED_LOGBOOK_EVENT_TYPES'), 'issued');
|
||||
[$normalTypesParams, $normalFlatIds] = $this->getArrayPlaceholders(\Utils\Constants::get('NORMAL_LOGBOOK_EVENT_TYPES'), 'normal');
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':end_time' => $dateRange['endDate'],
|
||||
':start_time' => $dateRange['startDate'],
|
||||
':resolution' => $this->getResolution($request),
|
||||
];
|
||||
$params = array_merge($params, $failedTypesParams);
|
||||
$params = array_merge($params, $issuedTypesParams);
|
||||
$params = array_merge($params, $normalTypesParams);
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event_logbook.started))::bigint AS ts,
|
||||
COUNT(CASE WHEN event_error_type.value IN ({$normalFlatIds}) THEN TRUE END) AS event_normal_type_count,
|
||||
COUNT(CASE WHEN event_error_type.value IN ({$issuedFlatIds}) THEN TRUE END) AS event_issued_type_count,
|
||||
COUNT(CASE WHEN event_error_type.value IN ({$failedFlatIds}) THEN TRUE END) AS event_failed_type_count
|
||||
|
||||
FROM
|
||||
event_logbook
|
||||
|
||||
LEFT JOIN event_error_type
|
||||
ON event_logbook.error_type = event_error_type.id
|
||||
|
||||
WHERE
|
||||
event_logbook.key = :api_key AND
|
||||
event_logbook.started >= :start_time AND
|
||||
event_logbook.started <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts"
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Phones extends Base {
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$data = $this->getFirstLine($apiKey);
|
||||
|
||||
$timestamps = array_column($data, 'ts');
|
||||
$line1 = array_column($data, 'phone_count');
|
||||
|
||||
return $this->addEmptyDays([$timestamps, $line1]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event_phone.lastseen + :offset))::bigint AS ts,
|
||||
COUNT(*) AS phone_count
|
||||
FROM
|
||||
event_phone
|
||||
|
||||
WHERE
|
||||
event_phone.key = :api_key AND
|
||||
event_phone.lastseen >= :start_time AND
|
||||
event_phone.lastseen <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Resource extends BaseEventsCount {
|
||||
public function getCounts(int $apiKey): array {
|
||||
$query = (
|
||||
"SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(CASE WHEN event.type IN ({$this->normalFlatIds}) THEN TRUE END) AS event_normal_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->editFlatIds}) THEN TRUE END) AS event_editing_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->alertFlatIds}) THEN TRUE END) AS event_alert_type_count
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
WHERE
|
||||
event.url = :id AND
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts"
|
||||
);
|
||||
|
||||
return $this->executeOnRangeById($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Resources extends Base {
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$data = $this->getFirstLine($apiKey);
|
||||
|
||||
$timestamps = array_column($data, 'ts');
|
||||
$line1 = array_column($data, 'count_200');
|
||||
$line2 = array_column($data, 'count_404');
|
||||
$line3 = array_column($data, 'count_500');
|
||||
|
||||
return $this->addEmptyDays([$timestamps, $line1, $line2, $line3]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(DISTINCT event.id) AS url_count,
|
||||
|
||||
COUNT(DISTINCT (
|
||||
CASE WHEN event.http_code=200 OR event.http_code IS NULL THEN event.id END)
|
||||
) AS count_200,
|
||||
|
||||
COUNT(DISTINCT (
|
||||
CASE WHEN event.http_code = 404 THEN event.id END)
|
||||
) AS count_404,
|
||||
|
||||
COUNT(DISTINCT (
|
||||
CASE WHEN event.http_code IN(403, 500) THEN event.id END)
|
||||
) AS count_500
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
WHERE
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class ReviewQueue extends Base {
|
||||
use \Traits\DateRange;
|
||||
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$field1 = 'ts_new_users_whitelisted';
|
||||
$data1 = $this->getFirstLine($apiKey);
|
||||
|
||||
$field2 = 'ts_new_added_to_review';
|
||||
$data2 = $this->getSecondLine($apiKey);
|
||||
|
||||
$field3 = 'ts_new_users_blacklisted';
|
||||
$data3 = $this->getThirdLine($apiKey);
|
||||
|
||||
$data0 = $this->concatDataLines($data1, $field1, $data2, $field2, $data3, $field3);
|
||||
|
||||
$indexedData = array_values($data0);
|
||||
$timestamps = array_column($indexedData, 'ts');
|
||||
$line1 = array_column($indexedData, $field1);
|
||||
$line2 = array_column($indexedData, $field2);
|
||||
$line3 = array_column($indexedData, $field3);
|
||||
|
||||
return $this->addEmptyDays([$timestamps, $line1, $line2, $line3]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event_account.latest_decision + :offset))::bigint AS ts,
|
||||
COUNT(event_account.id) as ts_new_users_whitelisted
|
||||
FROM
|
||||
event_account
|
||||
|
||||
WHERE
|
||||
event_account.key = :api_key AND
|
||||
event_account.fraud IS FALSE AND
|
||||
event_account.latest_decision >= :start_time AND
|
||||
event_account.latest_decision <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey, false);
|
||||
}
|
||||
|
||||
private function getSecondLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event_account.added_to_review + :offset))::bigint AS ts,
|
||||
COUNT(event_account.id) AS ts_new_added_to_review
|
||||
FROM
|
||||
event_account
|
||||
|
||||
WHERE
|
||||
event_account.key = :api_key AND
|
||||
event_account.added_to_review IS NOT NULL AND
|
||||
event_account.added_to_review >= :start_time AND
|
||||
event_account.added_to_review <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey, false);
|
||||
}
|
||||
|
||||
private function getThirdLine(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event_account.latest_decision + :offset))::bigint AS ts,
|
||||
COUNT(event_account.id) as ts_new_users_blacklisted
|
||||
FROM
|
||||
event_account
|
||||
|
||||
WHERE
|
||||
event_account.key = :api_key AND
|
||||
event_account.fraud IS TRUE AND
|
||||
event_account.latest_decision >= :start_time AND
|
||||
event_account.latest_decision <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class SessionStat extends Base {
|
||||
protected $DB_TABLE_NAME = 'event_session';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$itemsByDate = [];
|
||||
$items = $this->getCounts($apiKey);
|
||||
|
||||
foreach ($items as $item) {
|
||||
$itemsByDate[$item['ts']] = [
|
||||
$item['new_device_count_sum'],
|
||||
$item['new_ip_count_sum'],
|
||||
$item['event_session_cnt'],
|
||||
$item['event_count_max'],
|
||||
];
|
||||
}
|
||||
|
||||
$request = $this->f3->get('REQUEST');
|
||||
// use offset shift because $startTs/$endTs compared with shifted ['ts']
|
||||
$offset = \Utils\TimeZones::getCurrentOperatorOffset();
|
||||
$datesRange = $this->getLatestNDatesRange(14, $offset);
|
||||
$endTs = strtotime($datesRange['endDate']);
|
||||
$startTs = strtotime($datesRange['startDate']);
|
||||
$step = \Utils\Constants::get('CHART_RESOLUTION')[$this->getResolution($request)];
|
||||
|
||||
$endTs = $endTs - ($endTs % $step);
|
||||
$startTs = $startTs - ($startTs % $step);
|
||||
|
||||
$midTs = $startTs + $step * 7;
|
||||
|
||||
foreach ($itemsByDate as $ts => $value) {
|
||||
if ($ts <= $midTs) {
|
||||
$newTs = $ts + $step * 7;
|
||||
if (!array_key_exists($newTs, $itemsByDate)) {
|
||||
$itemsByDate[$newTs] = [
|
||||
0, 0, 0, 0,
|
||||
$value[0], $value[1], $value[2], $value[3],
|
||||
];
|
||||
} else {
|
||||
$itemsByDate[$newTs][] = $value[0];
|
||||
$itemsByDate[$newTs][] = $value[1];
|
||||
$itemsByDate[$newTs][] = $value[2];
|
||||
$itemsByDate[$newTs][] = $value[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while ($endTs >= $startTs) {
|
||||
if (!isset($itemsByDate[$startTs]) && $startTs > $midTs) {
|
||||
$itemsByDate[$startTs] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
} elseif (isset($itemsByDate[$startTs]) && count($itemsByDate[$startTs]) === 4) {
|
||||
$itemsByDate[$startTs][] = 0;
|
||||
$itemsByDate[$startTs][] = 0;
|
||||
$itemsByDate[$startTs][] = 0;
|
||||
$itemsByDate[$startTs][] = 0;
|
||||
}
|
||||
|
||||
$startTs += $step;
|
||||
}
|
||||
|
||||
foreach (array_keys($itemsByDate) as $key) {
|
||||
if ($key <= $midTs) {
|
||||
unset($itemsByDate[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
ksort($itemsByDate);
|
||||
|
||||
$result = [array_keys($itemsByDate)];
|
||||
|
||||
for ($i = 0; $i < 8; ++$i) {
|
||||
$result[] = array_column($itemsByDate, $i);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function executeOnRangeById(string $query, int $apiKey): array {
|
||||
$request = $this->f3->get('REQUEST');
|
||||
// do not use offset because :start_time/:end_time compared with UTC event.time
|
||||
$dateRange = $this->getLatestNDatesRange(14);
|
||||
$offset = \Utils\TimeZones::getCurrentOperatorOffset();
|
||||
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':end_time' => $dateRange['endDate'],
|
||||
':start_time' => $dateRange['startDate'],
|
||||
//':resolution' => $this->getResolution($request),
|
||||
':resolution' => 60 * 60 * 24,
|
||||
':id' => $request['id'],
|
||||
':offset' => strval($offset), // str for postgres
|
||||
];
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
private function getCounts(int $apiKey): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
((EXTRACT(EPOCH FROM event_session.created)::bigint + :offset::bigint) / :resolution) * :resolution as ts,
|
||||
|
||||
COUNT(event_session.id) AS event_session_cnt,
|
||||
MAX(event_session_stat.event_count) AS event_count_max,
|
||||
FLOOR(AVG(event_session_stat.event_count))::int AS event_count_avg,
|
||||
MAX(event_session_stat.device_count) AS device_count_max,
|
||||
FLOOR(AVG(event_session_stat.device_count))::int AS device_count_avg,
|
||||
MAX(event_session_stat.ip_count) AS ip_count_max,
|
||||
FLOOR(AVG(event_session_stat.ip_count))::int AS ip_count_avg,
|
||||
MAX(event_session_stat.country_count) AS country_count_max,
|
||||
FLOOR(AVG(event_session_stat.country_count))::int AS country_count_avg,
|
||||
SUM(event_session_stat.new_ip_count) AS new_ip_count_sum,
|
||||
SUM(event_session_stat.new_device_count) AS new_device_count_sum,
|
||||
jsonb_agg(event_session_stat.event_types) AS event_types,
|
||||
jsonb_agg(event_session_stat.http_codes) AS http_codes
|
||||
|
||||
FROM
|
||||
event_session
|
||||
|
||||
LEFT JOIN event_session_stat
|
||||
ON event_session.id = event_session_stat.session_id
|
||||
|
||||
WHERE
|
||||
event_session.account_id = :id AND
|
||||
event_session.key = :api_key AND
|
||||
event_session.created >= :start_time AND
|
||||
event_session.created <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->executeOnRangeById($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class User extends BaseEventsCount {
|
||||
public function getCounts(int $apiKey) {
|
||||
$query = (
|
||||
"SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event.time + :offset))::bigint AS ts,
|
||||
COUNT(CASE WHEN event.type IN ({$this->normalFlatIds}) THEN TRUE END) AS event_normal_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->editFlatIds}) THEN TRUE END) AS event_editing_type_count,
|
||||
COUNT(CASE WHEN event.type IN ({$this->alertFlatIds}) THEN TRUE END) AS event_alert_type_count
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_account
|
||||
ON (event.account = event_account.id)
|
||||
|
||||
WHERE
|
||||
event_account.id = :id AND
|
||||
event.key = :api_key AND
|
||||
event.time >= :start_time AND
|
||||
event.time <= :end_time
|
||||
|
||||
GROUP BY ts
|
||||
ORDER BY ts"
|
||||
);
|
||||
|
||||
return $this->executeOnRangeById($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Users extends Base {
|
||||
use \Traits\DateRange;
|
||||
|
||||
protected $DB_TABLE_NAME = 'event_account';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$data0 = [];
|
||||
$data1 = $this->getFirstLine($apiKey);
|
||||
$iters = count($data1);
|
||||
|
||||
for ($i = 0; $i < $iters; ++$i) {
|
||||
$item = $data1[$i];
|
||||
$ts = $item['ts'];
|
||||
$score = $item['score'];
|
||||
|
||||
if (!isset($data0[$ts])) {
|
||||
$data0[$ts] = [
|
||||
'ts' => $ts,
|
||||
'ts_new_users_with_trust_score_high' => 0,
|
||||
'ts_new_users_with_trust_score_medium' => 0,
|
||||
'ts_new_users_with_trust_score_low' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$inf = \Utils\Constants::get('USER_HIGH_SCORE_INF');
|
||||
if ($score >= \Utils\Constants::get('USER_HIGH_SCORE_INF')) {
|
||||
++$data0[$ts]['ts_new_users_with_trust_score_high'];
|
||||
}
|
||||
|
||||
$inf = \Utils\Constants::get('USER_MEDIUM_SCORE_INF');
|
||||
$sup = \Utils\Constants::get('USER_MEDIUM_SCORE_SUP');
|
||||
if ($score >= $inf && $score < $sup) {
|
||||
++$data0[$ts]['ts_new_users_with_trust_score_medium'];
|
||||
}
|
||||
|
||||
$inf = \Utils\Constants::get('USER_LOW_SCORE_INF');
|
||||
$sup = \Utils\Constants::get('USER_LOW_SCORE_SUP');
|
||||
if ($score >= $inf && $score < $sup) {
|
||||
++$data0[$ts]['ts_new_users_with_trust_score_low'];
|
||||
}
|
||||
}
|
||||
|
||||
$indexedData = array_values($data0);
|
||||
$timestamps = array_column($indexedData, 'ts');
|
||||
$line1 = array_column($indexedData, 'ts_new_users_with_trust_score_high');
|
||||
$line2 = array_column($indexedData, 'ts_new_users_with_trust_score_medium');
|
||||
$line3 = array_column($indexedData, 'ts_new_users_with_trust_score_low');
|
||||
|
||||
return $this->addEmptyDays([$timestamps, $line1, $line2, $line3]);
|
||||
}
|
||||
|
||||
private function getFirstLine(int $apiKey) {
|
||||
$query = (
|
||||
'SELECT
|
||||
EXTRACT(EPOCH FROM date_trunc(:resolution, event_account.created + :offset))::bigint AS ts,
|
||||
event_account.id,
|
||||
event_account.score
|
||||
FROM
|
||||
event_account
|
||||
|
||||
WHERE
|
||||
event_account.key = :api_key AND
|
||||
event_account.created >= :start_time AND
|
||||
event_account.created <= :end_time
|
||||
|
||||
GROUP BY ts, event_account.id
|
||||
ORDER BY ts'
|
||||
);
|
||||
|
||||
return $this->execute($query, $apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?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 Models\Chart;
|
||||
|
||||
class Watchlist extends Base {
|
||||
private $userIds = [];
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getData(int $apiKey): array {
|
||||
$params = $this->getRequestParams($apiKey);
|
||||
$params[':users_ids'] = $this->userIds;
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
TEXT(date_trunc('day', event.time)) AS day,
|
||||
COUNT(event.id) AS event_count
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_account
|
||||
ON (event.account = event_account.id)
|
||||
|
||||
INNER JOIN event_url
|
||||
ON (event.url = event_url.id)
|
||||
|
||||
INNER JOIN event_ip
|
||||
ON (event.ip = event_ip.id)
|
||||
|
||||
INNER JOIN countries
|
||||
ON (event_ip.country = countries.id)
|
||||
|
||||
WHERE
|
||||
event.key = :api_key
|
||||
%s
|
||||
|
||||
GROUP BY day
|
||||
ORDER BY day"
|
||||
);
|
||||
//$request = $this->f3->get('REQUEST');
|
||||
//$dateRange = $this->getDatesRange($request);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function setUsersIds(array $userIds): void {
|
||||
$this->userIds = $userIds;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,53 @@
|
||||
<?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 Models\Context;
|
||||
|
||||
abstract class Base extends \Models\BaseSql {
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
abstract public function getContext(array $accountIds, int $apiKey): array;
|
||||
|
||||
abstract protected function getDetails(array $accountIds, int $apiKey): array;
|
||||
|
||||
protected function getRequestParams(array $accountIds, int $apiKey): array {
|
||||
[$params, $placeHolders] = $this->getArrayPlaceholders($accountIds);
|
||||
$params[':api_key'] = $apiKey;
|
||||
|
||||
return [$params, $placeHolders];
|
||||
}
|
||||
|
||||
protected function groupRecordsByAccount(array $records): array {
|
||||
$recordsByAccount = [];
|
||||
$iters = count($records);
|
||||
|
||||
for ($i = 0; $i < $iters; ++$i) {
|
||||
$item = $records[$i];
|
||||
$accountId = $item['accountid'];
|
||||
|
||||
if (!isset($recordsByAccount[$accountId])) {
|
||||
$recordsByAccount[$accountId] = [];
|
||||
}
|
||||
|
||||
$recordsByAccount[$accountId][] = $item;
|
||||
}
|
||||
|
||||
return $recordsByAccount;
|
||||
}
|
||||
|
||||
protected function getUniqueArray(array $array): array {
|
||||
return array_values(array_unique($array));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
<?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 Models\Context;
|
||||
|
||||
class Data {
|
||||
private \Models\Context\User $userModel;
|
||||
private \Models\Context\Ip $ipModel;
|
||||
private \Models\Context\Device $deviceModel;
|
||||
private \Models\Context\Email $emailModel;
|
||||
private \Models\Context\Phone $phoneModel;
|
||||
private \Models\Context\Event $eventModel;
|
||||
private \Models\Context\Session $sessionModel;
|
||||
private \Models\ApiKeys $keyModel;
|
||||
|
||||
private array $suspiciousWordsUrl;
|
||||
private array $suspiciousWordsUserAgent;
|
||||
private array $suspiciousWordsEmail;
|
||||
|
||||
public function __construct() {
|
||||
$this->userModel = new User();
|
||||
$this->ipModel = new Ip();
|
||||
$this->deviceModel = new Device();
|
||||
$this->emailModel = new Email();
|
||||
$this->phoneModel = new Phone();
|
||||
$this->eventModel = new Event();
|
||||
$this->sessionModel = new Session();
|
||||
$this->keyModel = new \Models\ApiKeys();
|
||||
|
||||
$this->suspiciousWordsUrl = \Utils\WordsLists\Url::getWords();
|
||||
$this->suspiciousWordsUserAgent = \Utils\WordsLists\UserAgent::getWords();
|
||||
$this->suspiciousWordsEmail = \Utils\WordsLists\Email::getWords();
|
||||
}
|
||||
|
||||
public function getContext(array $accountIds, int $apiKey): array {
|
||||
$userDetails = $this->userModel->getContext($accountIds, $apiKey);
|
||||
$ipDetails = $this->ipModel->getContext($accountIds, $apiKey);
|
||||
$deviceDetails = $this->deviceModel->getContext($accountIds, $apiKey);
|
||||
$emailDetails = $this->emailModel->getContext($accountIds, $apiKey);
|
||||
$phoneDetails = $this->phoneModel->getContext($accountIds, $apiKey);
|
||||
$eventDetails = $this->eventModel->getContext($accountIds, $apiKey);
|
||||
//$domainDetails = $this->domainModel->getContext($accountIds, $apiKey);
|
||||
|
||||
$timezoneName = $this->keyModel->getTimezoneByKeyId($apiKey);
|
||||
$utcTime = new \DateTime('now', new \DateTimeZone('UTC'));
|
||||
$timezone = new \DateTimeZone($timezoneName);
|
||||
$offsetInSeconds = $timezone->getOffset($utcTime);
|
||||
|
||||
// get only suspicious sessions
|
||||
$sessionDetails = $this->sessionModel->getContext($accountIds, $apiKey, $offsetInSeconds);
|
||||
|
||||
//Extend user details context
|
||||
foreach ($userDetails as $userId => $user) {
|
||||
$user['le_exists'] = ($user['le_email'] ?? null) !== null;
|
||||
$user['le_email'] = $user['le_email'] ?? '';
|
||||
$user['le_local_part'] = explode('@', $user['le_email'])[0] ?? '';
|
||||
$user['le_domain_part'] = explode('@', $user['le_email'])[1] ?? '';
|
||||
|
||||
$userId = $user['ea_id'];
|
||||
$ip = $ipDetails[$userId] ?? [];
|
||||
$device = $deviceDetails[$userId] ?? [];
|
||||
$email = $emailDetails[$userId] ?? [];
|
||||
$phone = $phoneDetails[$userId] ?? [];
|
||||
$events = $eventDetails[$userId] ?? [];
|
||||
$session = $sessionDetails[$userId] ?? [];
|
||||
|
||||
$user['eip_ip_id'] = $ip['eip_ip_id'] ?? [];
|
||||
$user['eip_ip'] = $ip['eip_ip'] ?? [];
|
||||
$user['eip_cidr'] = $ip['eip_cidr'] ?? [];
|
||||
$user['eip_country_id'] = $ip['eip_country_id'] ?? [];
|
||||
$user['eip_data_center'] = $ip['eip_data_center'] ?? [];
|
||||
$user['eip_tor'] = $ip['eip_tor'] ?? [];
|
||||
$user['eip_vpn'] = $ip['eip_vpn'] ?? [];
|
||||
$user['eip_relay'] = $ip['eip_relay'] ?? [];
|
||||
$user['eip_starlink'] = $ip['eip_starlink'] ?? [];
|
||||
$user['eip_total_visit'] = $ip['eip_total_visit'] ?? [];
|
||||
$user['eip_blocklist'] = $ip['eip_blocklist'] ?? [];
|
||||
$user['eip_shared'] = $ip['eip_shared'] ?? [];
|
||||
//$user['eip_domains'] = $ip['eip_domains'] ?? [];
|
||||
$user['eip_country_id'] = $ip['eip_country_id'] ?? [];
|
||||
$user['eip_fraud_detected'] = $ip['eip_fraud_detected'] ?? [];
|
||||
$user['eip_alert_list'] = $ip['eip_alert_list'] ?? [];
|
||||
$user['eip_domains_count_len'] = $ip['eip_domains_count_len'] ?? [];
|
||||
|
||||
$user['eup_device'] = $device['eup_device'] ?? [];
|
||||
$user['eup_device_id'] = $device['eup_device_id'] ?? [];
|
||||
$user['eup_browser_name'] = $device['eup_browser_name'] ?? [];
|
||||
$user['eup_browser_version'] = $device['eup_browser_version'] ?? [];
|
||||
$user['eup_os_name'] = $device['eup_os_name'] ?? [];
|
||||
$user['eup_lang'] = $device['eup_lang'] ?? [];
|
||||
$user['eup_ua'] = $device['eup_ua'] ?? [];
|
||||
// $user['eup_lastseen'] = $device['eup_lastseen'] ?? [];
|
||||
// $user['eup_created'] = $device['eup_created'] ?? [];
|
||||
|
||||
$user['ee_email'] = $email['ee_email'] ?? [];
|
||||
$user['ee_earliest_breach'] = $email['ee_earliest_breach'] ?? [];
|
||||
|
||||
$user['ep_phone_number'] = $phone['ep_phone_number'] ?? [];
|
||||
$user['ep_shared'] = $phone['ep_shared'] ?? [];
|
||||
$user['ep_type'] = $phone['ep_type'] ?? [];
|
||||
|
||||
$user['event_ip'] = $events['event_ip'] ?? [];
|
||||
$user['event_url_string'] = $events['event_url_string'] ?? [];
|
||||
$user['event_empty_referer'] = $events['event_empty_referer'] ?? [];
|
||||
$user['event_device'] = $events['event_device'] ?? [];
|
||||
$user['event_type'] = $events['event_type'] ?? [];
|
||||
$user['event_http_method'] = $events['event_http_method'] ?? [];
|
||||
$user['event_http_code'] = $events['event_http_code'] ?? [];
|
||||
$user['event_device_created'] = $events['event_device_created'] ?? [];
|
||||
$user['event_device_lastseen'] = $events['event_device_lastseen'] ?? [];
|
||||
|
||||
$user['event_session_multiple_country'] = $session[0]['event_session_multiple_country'] ?? false;
|
||||
$user['event_session_multiple_ip'] = $session[0]['event_session_multiple_ip'] ?? false;
|
||||
$user['event_session_multiple_device'] = $session[0]['event_session_multiple_device'] ?? false;
|
||||
$user['event_session_night_time'] = $session[0]['event_session_night_time'] ?? false;
|
||||
|
||||
//Extra params for rules
|
||||
$user = $this->extendParams($user);
|
||||
|
||||
$userDetails[$userId] = $this->extendEventParams($user);
|
||||
}
|
||||
|
||||
return $userDetails;
|
||||
}
|
||||
|
||||
private function extendParams(array $record): array {
|
||||
//$record['timezone']
|
||||
|
||||
$localPartLen = strlen($record['le_local_part']);
|
||||
$domainPartLen = strlen($record['le_domain_part']);
|
||||
$fullName = $this->getUserFullName($record);
|
||||
|
||||
$record['le_local_part_len'] = $localPartLen;
|
||||
$record['ea_fullname_has_numbers'] = preg_match('~[0-9]+~', $fullName) > 0;
|
||||
$record['ea_fullname_has_spaces_hyphens'] = preg_match('~[\-\s]~', $fullName) > 0;
|
||||
$record['ea_days_since_account_creation'] = $this->getDaysSinceAccountCreation($record);
|
||||
$record['ea_days_since_last_visit'] = $this->getDaysSinceLastVisit($record);
|
||||
|
||||
//$record['le_has_no_profiles'] = $record['le_profiles'] === 0;
|
||||
$record['le_has_no_data_breaches'] = $record['le_data_breach'] === false;
|
||||
$record['le_has_suspicious_str'] = $this->checkEmailForSuspiciousString($record);
|
||||
$record['le_has_numeric_only_local_part'] = preg_match('/^[0-9]+$/', $record['le_local_part']) > 0;
|
||||
$record['le_email_has_consec_s_chars'] = preg_match('/[^a-zA-Z0-9]{2,}/', $record['le_local_part']) > 0;
|
||||
$record['le_email_has_consec_nums'] = preg_match('/\d{2}/', $record['le_local_part']) > 0;
|
||||
$record['le_email_has_no_digits'] = !preg_match('/\d/', $record['le_local_part']);
|
||||
$record['le_email_has_vowels'] = preg_match('/[aeoui]/i', $record['le_local_part']) > 0;
|
||||
$record['le_email_has_consonants'] = preg_match('/[bcdfghjklmnpqrstvwxyz]/i', $record['le_local_part']) > 0;
|
||||
|
||||
$record['le_with_long_local_part_length'] = $localPartLen > \Utils\Constants::get('RULE_EMAIL_MAXIMUM_LOCAL_PART_LENGTH');
|
||||
$record['le_with_long_domain_length'] = $domainPartLen > \Utils\Constants::get('RULE_EMAIL_MAXIMUM_DOMAIN_LENGTH');
|
||||
$record['le_email_in_blockemails'] = $record['le_blockemails'] ?? false;
|
||||
$record['le_is_invalid'] = $record['le_exists'] && filter_var($record['le_email'], FILTER_VALIDATE_EMAIL) === false;
|
||||
|
||||
$record['le_appears_on_alert_list'] = $record['le_alert_list'] ?? false;
|
||||
|
||||
$record['ld_is_disposable'] = $record['ld_disposable_domains'] ?? false;
|
||||
$record['ld_days_since_domain_creation'] = $this->getDaysSinceDomainCreation($record);
|
||||
$record['ld_domain_free_email_provider'] = $record['ld_free_email_provider'] ?? false;
|
||||
$record['ld_from_blockdomains'] = $record['ld_blockdomains'] ?? false;
|
||||
$record['ld_domain_without_mx_record'] = $record['ld_mx_record'] === false;
|
||||
$record['ld_website_is_disabled'] = $record['ld_disabled'] ?? false;
|
||||
$record['ld_tranco_rank'] = $record['ld_tranco_rank'] ?? -1;
|
||||
|
||||
$record['lp_invalid_phone'] = $record['lp_invalid'] === true;
|
||||
$record['ep_shared_phone'] = (bool) count(array_filter($record['ep_shared'], static function ($item) {
|
||||
return $item !== null && $item > 1;
|
||||
}));
|
||||
|
||||
$daysSinceBreaches = array_map(function ($item) {
|
||||
return $this->getDaysTillToday($item);
|
||||
}, $record['ee_earliest_breach']);
|
||||
|
||||
$record['ee_days_since_first_breach'] = count($daysSinceBreaches) ? max($daysSinceBreaches) : -1;
|
||||
|
||||
$onlyNonResidentialParams = !(bool) count(array_filter(array_merge(
|
||||
$record['eip_fraud_detected'],
|
||||
$record['eip_blocklist'],
|
||||
$record['eip_tor'],
|
||||
$record['eip_starlink'],
|
||||
$record['eip_relay'],
|
||||
$record['eip_vpn'],
|
||||
$record['eip_data_center'],
|
||||
), static function ($value): bool {
|
||||
return $value === true;
|
||||
}));
|
||||
$record['eip_only_residential'] = $onlyNonResidentialParams && !in_array(0, $record['eip_country_id']);
|
||||
$record['eip_has_fraud'] = in_array(true, $record['eip_fraud_detected']);
|
||||
$record['eip_unique_cidrs'] = count(array_unique($record['eip_cidr']));
|
||||
$record['lp_fraud_detected'] = $record['lp_fraud_detected'] ?? false;
|
||||
$record['le_fraud_detected'] = $record['le_fraud_detected'] ?? false;
|
||||
|
||||
$record['eup_has_rare_browser'] = (bool) count(array_diff($record['eup_browser_name'], array_keys(\Utils\Constants::get('RULE_REGULAR_BROWSER_NAMES'))));
|
||||
$record['eup_has_rare_os'] = (bool) count(array_diff($record['eup_os_name'], \Utils\Constants::get('RULE_REGULAR_OS_NAMES')));
|
||||
$record['eup_device_count'] = count($record['eup_device']);
|
||||
|
||||
$record['eup_vulnerable_ua'] = false;
|
||||
|
||||
if (count($this->suspiciousWordsUserAgent)) {
|
||||
foreach ($record['eup_ua'] as $url) {
|
||||
foreach ($this->suspiciousWordsUserAgent as $sub) {
|
||||
if (stripos($url, $sub) !== false) {
|
||||
$record['eup_vulnerable_ua'] = true;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
private function extendEventParams(array $record): array {
|
||||
// Remove null values specifically
|
||||
$eventTypeFiltered = $this->filterStringNum($record['event_type']);
|
||||
$eventHttpCodeFiltered = $this->filterStringNum($record['event_http_code']);
|
||||
|
||||
$eventTypeCount = array_count_values($eventTypeFiltered);
|
||||
|
||||
//$accountLoginFailId = \Utils\Constants::get('ACCOUNT_LOGIN_FAIL_EVENT_TYPE_ID');
|
||||
$accountEmailChangeId = \Utils\Constants::get('ACCOUNT_EMAIL_CHANGE_EVENT_TYPE_ID');
|
||||
$accountPwdChangeId = \Utils\Constants::get('ACCOUNT_PASSWORD_CHANGE_EVENT_TYPE_ID');
|
||||
|
||||
//$record['event_failed_login_attempts'] = $eventTypeCount[$accountLoginFailId] ?? 0;
|
||||
$record['event_email_changed'] = array_key_exists($accountEmailChangeId, $eventTypeCount);
|
||||
$record['event_password_changed'] = array_key_exists($accountPwdChangeId, $eventTypeCount);
|
||||
|
||||
$record['event_http_method_head'] = in_array(\Utils\Constants::get('EVENT_REQUEST_TYPE_HEAD'), $record['event_http_method']);
|
||||
|
||||
$record['event_empty_referer'] = in_array(true, $record['event_empty_referer'], true);
|
||||
|
||||
$clientErrors = 0;
|
||||
$serverErrors = 0;
|
||||
$successEvents = 0;
|
||||
foreach ($eventHttpCodeFiltered as $code) {
|
||||
if (is_int($code) && $code >= 400 && $code < 500) {
|
||||
++$clientErrors;
|
||||
} elseif (is_int($code) && $code >= 500 && $code < 600) {
|
||||
++$serverErrors;
|
||||
} elseif (is_int($code) && $code >= 200 && $code < 300) {
|
||||
++$successEvents;
|
||||
}
|
||||
}
|
||||
|
||||
$record['event_multiple_5xx_http'] = $serverErrors;
|
||||
$record['event_multiple_4xx_http'] = $clientErrors;
|
||||
|
||||
$record['event_2xx_http'] = (bool) $successEvents;
|
||||
|
||||
$record['event_vulnerable_url'] = false;
|
||||
|
||||
if (count($this->suspiciousWordsUrl)) {
|
||||
foreach ($record['event_url_string'] as $url) {
|
||||
foreach ($this->suspiciousWordsUrl as $sub) {
|
||||
if (stripos($url, $sub) !== false) {
|
||||
$record['event_vulnerable_url'] = true;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
private function getDaysSinceDomainCreation(array $params): int {
|
||||
$dt1 = date('Y-m-d');
|
||||
$dt2 = $params['ld_creation_date'];
|
||||
|
||||
return $this->getDaysDiff($dt1, $dt2);
|
||||
}
|
||||
|
||||
private function getDaysSinceAccountCreation(array $params): int {
|
||||
$dt1 = date('Y-m-d');
|
||||
$dt2 = $params['ea_created'] ?? null;
|
||||
|
||||
return $this->getDaysDiff($dt1, $dt2);
|
||||
}
|
||||
|
||||
private function getDaysSinceLastVisit(array $params): int {
|
||||
$dt1 = date('Y-m-d');
|
||||
$dt2 = $params['ea_lastseen'] ?? null;
|
||||
|
||||
return $this->getDaysDiff($dt1, $dt2);
|
||||
}
|
||||
|
||||
private function getDaysTillToday(?string $dt2): int {
|
||||
$diff = -1;
|
||||
|
||||
if ($dt2 !== null) {
|
||||
$dt1 = date('Y-m-d');
|
||||
$dt1 = new \DateTime($dt1);
|
||||
$dt2 = new \DateTime($dt2);
|
||||
$diff = $dt1->diff($dt2)->format('%a');
|
||||
}
|
||||
|
||||
return $diff;
|
||||
}
|
||||
|
||||
private function getDaysDiff(?string $dt1, ?string $dt2): int {
|
||||
$diff = -1;
|
||||
|
||||
if ($dt2) {
|
||||
$dt1 = new \DateTime($dt1);
|
||||
$dt2 = new \DateTime($dt2);
|
||||
$diff = $dt1->diff($dt2)->format('%a');
|
||||
}
|
||||
|
||||
return $diff;
|
||||
}
|
||||
|
||||
private function getUserFullName(array $record): string {
|
||||
$name = [];
|
||||
$fName = $record['ea_firstname'] ?? '';
|
||||
if ($fName) {
|
||||
$name[] = $fName;
|
||||
}
|
||||
|
||||
$lName = $record['ea_lastname'] ?? '';
|
||||
if ($lName) {
|
||||
$name[] = $lName;
|
||||
}
|
||||
|
||||
return trim(join(' ', $name));
|
||||
}
|
||||
|
||||
private function checkEmailForSuspiciousString(array $record): bool {
|
||||
foreach ($this->suspiciousWordsEmail as $sub) {
|
||||
if (stripos($record['le_email'], $sub) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function filterStringNum(array $record): array {
|
||||
return array_filter($record, static function ($value): bool {
|
||||
return is_string($value) || is_int($value);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?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 Models\Context;
|
||||
|
||||
class Device extends Base {
|
||||
public function getContext(array $accountIds, int $apiKey): array {
|
||||
$record = $this->getDetails($accountIds, $apiKey);
|
||||
$recordByAccount = $this->groupRecordsByAccount($record);
|
||||
|
||||
foreach ($recordByAccount as $key => $value) {
|
||||
$recordByAccount[$key] = [
|
||||
'eup_device' => array_column($value, 'eup_device'),
|
||||
'eup_device_id' => array_column($value, 'eup_device_id'),
|
||||
'eup_browser_name' => array_column($value, 'eup_browser_name'),
|
||||
'eup_browser_version' => array_column($value, 'eup_browser_version'),
|
||||
'eup_os_name' => array_column($value, 'eup_os_name'),
|
||||
'eup_lang' => array_column($value, 'eup_lang'),
|
||||
'eup_ua' => array_column($value, 'eup_ua'),
|
||||
// 'eup_lastseen' => array_column($value, 'eup_lastseen'),
|
||||
// 'eup_created' => array_column($value, 'eup_created'),
|
||||
];
|
||||
}
|
||||
|
||||
return $recordByAccount;
|
||||
}
|
||||
|
||||
protected function getDetails(array $accountIds, int $apiKey): array {
|
||||
[$params, $placeHolders] = $this->getRequestParams($accountIds, $apiKey);
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
event_device.account_id AS accountid,
|
||||
event_device.id AS eup_device_id,
|
||||
event_ua_parsed.device AS eup_device,
|
||||
event_ua_parsed.browser_name AS eup_browser_name,
|
||||
event_ua_parsed.browser_version AS eup_browser_version,
|
||||
event_ua_parsed.os_name AS eup_os_name,
|
||||
event_ua_parsed.ua AS eup_ua,
|
||||
-- event_device.lastseen AS eup_lastseen,
|
||||
-- event_device.created AS eup_created,
|
||||
event_device.lang AS eup_lang
|
||||
|
||||
FROM
|
||||
event_device
|
||||
|
||||
INNER JOIN event_ua_parsed
|
||||
ON(event_device.user_agent=event_ua_parsed.id)
|
||||
|
||||
WHERE
|
||||
event_device.key = :api_key
|
||||
AND event_ua_parsed.checked = true
|
||||
AND event_device.account_id IN ({$placeHolders})"
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?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 Models\Context;
|
||||
|
||||
class Domain extends Base {
|
||||
public function getContext(array $accountIds, int $apiKey): array {
|
||||
$records = $this->getDetails($accountIds, $apiKey);
|
||||
$recordsByAccount = $this->groupRecordsByAccount($records);
|
||||
|
||||
foreach ($recordsByAccount as $key => $value) {
|
||||
$recordsByAccount[$key] = [
|
||||
'ed_domain' => $this->getUniqueArray(array_column($value, 'ed_domain')),
|
||||
'ed_blockdomains' => $this->getUniqueArray(array_column($value, 'ed_blockdomains')),
|
||||
'ed_disposable_domains' => $this->getUniqueArray(array_column($value, 'ed_disposable_domains')),
|
||||
'ed_free_email_provider' => $this->getUniqueArray(array_column($value, 'ed_free_email_provider')),
|
||||
'ed_creation_date' => $this->getUniqueArray(array_column($value, 'ed_creation_date')),
|
||||
'ed_disabled' => $this->getUniqueArray(array_column($value, 'ed_disabled')),
|
||||
'ed_mx_record' => $this->getUniqueArray(array_column($value, 'ed_mx_record')),
|
||||
];
|
||||
}
|
||||
|
||||
return $recordsByAccount;
|
||||
}
|
||||
|
||||
protected function getDetails(array $accountIds, int $apiKey): array {
|
||||
[$params, $placeHolders] = $this->getRequestParams($accountIds, $apiKey);
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
event_email.account_id AS accountid,
|
||||
event_domain.domain AS ed_domain,
|
||||
event_domain.blockdomains AS ed_blockdomains,
|
||||
event_domain.disposable_domains AS ed_disposable_domains,
|
||||
event_domain.free_email_provider AS ed_free_email_provider,
|
||||
event_domain.creation_date AS ed_creation_date,
|
||||
event_domain.disabled AS ed_disabled,
|
||||
event_domain.mx_record AS ed_mx_record
|
||||
|
||||
FROM
|
||||
event_domain
|
||||
|
||||
INNER JOIN event_email
|
||||
ON event_domain.id = event_email.domain
|
||||
|
||||
WHERE
|
||||
event_email.key = :api_key
|
||||
AND event_email.account_id IN ({$placeHolders})"
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?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 Models\Context;
|
||||
|
||||
class Email extends Base {
|
||||
public function getContext(array $accountIds, int $apiKey): array {
|
||||
$records = $this->getDetails($accountIds, $apiKey);
|
||||
$recordsByAccount = $this->groupRecordsByAccount($records);
|
||||
|
||||
foreach ($recordsByAccount as $key => $value) {
|
||||
$recordsByAccount[$key] = [
|
||||
'ee_email' => $this->getUniqueArray(array_column($value, 'ee_email')),
|
||||
'ee_earliest_breach' => $this->getUniqueArray(array_column($value, 'ee_earliest_breach')),
|
||||
];
|
||||
}
|
||||
|
||||
return $recordsByAccount;
|
||||
}
|
||||
|
||||
protected function getDetails(array $accountIds, int $apiKey): array {
|
||||
[$params, $placeHolders] = $this->getRequestParams($accountIds, $apiKey);
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
event_email.account_id AS accountid,
|
||||
event_email.email AS ee_email,
|
||||
event_email.earliest_breach AS ee_earliest_breach
|
||||
FROM
|
||||
event_email
|
||||
|
||||
WHERE
|
||||
event_email.key = :api_key
|
||||
AND event_email.checked = 'True'
|
||||
AND event_email.account_id IN ({$placeHolders})"
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?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 Models\Context;
|
||||
|
||||
class Event extends Base {
|
||||
public function getContext(array $accountIds, int $apiKey): array {
|
||||
$records = $this->getDetails($accountIds, $apiKey);
|
||||
$recordsByAccount = $this->groupRecordsByAccount($records);
|
||||
|
||||
foreach ($recordsByAccount as $key => $value) {
|
||||
$recordsByAccount[$key] = [
|
||||
'event_ip' => array_column($value, 'event_ip'),
|
||||
'event_url_string' => array_column($value, 'event_url_string'),
|
||||
'event_empty_referer' => array_column($value, 'event_empty_referer'),
|
||||
'event_device' => array_column($value, 'event_device'),
|
||||
'event_type' => array_column($value, 'event_type'),
|
||||
'event_http_code' => array_column($value, 'event_http_code'),
|
||||
'event_device_created' => array_column($value, 'event_device_created'),
|
||||
'event_device_lastseen' => array_column($value, 'event_device_lastseen'),
|
||||
'event_http_method' => array_column($value, 'event_http_method'),
|
||||
];
|
||||
}
|
||||
|
||||
return $recordsByAccount;
|
||||
}
|
||||
|
||||
protected function getDetails(array $accountIds, int $apiKey): array {
|
||||
[$params, $placeHolders] = $this->getRequestParams($accountIds, $apiKey);
|
||||
$contextLimit = \Utils\Constants::get('RULE_EVENT_CONTEXT_LIMIT');
|
||||
|
||||
$query = (
|
||||
"WITH ranked_events AS (
|
||||
SELECT
|
||||
event.account AS accountid,
|
||||
event.id AS event_id,
|
||||
event.ip AS event_ip,
|
||||
event_url.url AS event_url_string,
|
||||
event_referer.referer AS event_referer_string,
|
||||
event.device AS event_device,
|
||||
event.time AS event_time,
|
||||
event.type AS event_type,
|
||||
event.http_code AS event_http_code,
|
||||
event.http_method AS event_http_method,
|
||||
ROW_NUMBER() OVER (PARTITION BY event.account ORDER BY event.time DESC) AS rn
|
||||
FROM event
|
||||
LEFT JOIN event_url ON event_url.id = event.url
|
||||
LEFT JOIN event_referer ON event_referer.id = event.referer
|
||||
WHERE event.key = :api_key
|
||||
AND event.account IN ({$placeHolders})
|
||||
)
|
||||
SELECT
|
||||
accountid,
|
||||
event_ip,
|
||||
event_url_string,
|
||||
(event_referer_string IS NULL OR event_referer_string = '') AS event_empty_referer,
|
||||
event_device,
|
||||
ed.created AS event_device_created,
|
||||
ed.lastseen AS event_device_lastseen,
|
||||
event_type,
|
||||
event_http_code,
|
||||
event_http_method
|
||||
FROM ranked_events
|
||||
LEFT JOIN event_device AS ed
|
||||
ON ranked_events.event_device = ed.id
|
||||
WHERE rn <= {$contextLimit}
|
||||
ORDER BY event_time DESC;"
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?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 Models\Context;
|
||||
|
||||
class Ip extends Base {
|
||||
public function getContext(array $accountIds, int $apiKey): array {
|
||||
$records = $this->getDetails($accountIds, $apiKey);
|
||||
$recordsByAccount = $this->groupRecordsByAccount($records);
|
||||
|
||||
foreach ($recordsByAccount as $key => $value) {
|
||||
$recordsByAccount[$key] = [
|
||||
'eip_ip_id' => array_column($value, 'eip_ip_id'),
|
||||
'eip_ip' => array_column($value, 'eip_ip'),
|
||||
'eip_cidr' => array_column($value, 'eip_cidr'),
|
||||
'eip_data_center' => array_column($value, 'eip_data_center'),
|
||||
'eip_tor' => array_column($value, 'eip_tor'),
|
||||
'eip_vpn' => array_column($value, 'eip_vpn'),
|
||||
'eip_relay' => array_column($value, 'eip_relay'),
|
||||
'eip_starlink' => array_column($value, 'eip_starlink'),
|
||||
'eip_total_visit' => array_column($value, 'eip_total_visit'),
|
||||
'eip_blocklist' => array_column($value, 'eip_blocklist'),
|
||||
'eip_shared' => array_column($value, 'eip_shared'),
|
||||
//'eip_domains' => $this->getUniqueArray(array_column($value, 'eip_domains')),
|
||||
'eip_domains_count_len' => array_column($value, 'eip_domains_count_len'),
|
||||
'eip_country_id' => array_column($value, 'eip_country_id'),
|
||||
'eip_fraud_detected' => array_column($value, 'eip_fraud_detected'),
|
||||
'eip_alert_list' => array_column($value, 'eip_alert_list'),
|
||||
];
|
||||
}
|
||||
|
||||
return $recordsByAccount;
|
||||
}
|
||||
|
||||
protected function getDetails(array $accountIds, int $apiKey): array {
|
||||
[$params, $placeHolders] = $this->getRequestParams($accountIds, $apiKey);
|
||||
|
||||
$query = (
|
||||
"SELECT DISTINCT
|
||||
event.account AS accountid,
|
||||
|
||||
event_ip.id AS eip_ip_id,
|
||||
event_ip.ip AS eip_ip,
|
||||
event_ip.cidr::text AS eip_cidr,
|
||||
event_ip.country AS eip_country_id,
|
||||
event_ip.data_center AS eip_data_center,
|
||||
event_ip.tor AS eip_tor,
|
||||
event_ip.vpn AS eip_vpn,
|
||||
event_ip.relay AS eip_relay,
|
||||
event_ip.starlink AS eip_starlink,
|
||||
event_ip.total_visit AS eip_total_visit,
|
||||
event_ip.blocklist AS eip_blocklist,
|
||||
event_ip.shared AS eip_shared,
|
||||
-- event_ip.domains_count AS eip_domains,
|
||||
json_array_length(event_ip.domains_count::json) AS eip_domains_count_len,
|
||||
event_ip.fraud_detected AS eip_fraud_detected,
|
||||
event_ip.alert_list AS eip_alert_list
|
||||
|
||||
FROM
|
||||
event_ip
|
||||
|
||||
INNER JOIN event
|
||||
ON (event_ip.id = event.ip)
|
||||
|
||||
WHERE
|
||||
event_ip.key = :api_key
|
||||
AND event_ip.checked = 'True'
|
||||
AND event.account IN ({$placeHolders})
|
||||
|
||||
-- ORDER BY event_ip.id DESC"
|
||||
);
|
||||
|
||||
if (count($accountIds) === 1) {
|
||||
$query .= ' LIMIT 100 OFFSET 0';
|
||||
}
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?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 Models\Context;
|
||||
|
||||
class Phone extends Base {
|
||||
public function getContext(array $accountIds, int $apiKey): array {
|
||||
$records = $this->getDetails($accountIds, $apiKey);
|
||||
$recordsByAccount = $this->groupRecordsByAccount($records);
|
||||
|
||||
foreach ($recordsByAccount as $key => $value) {
|
||||
$recordsByAccount[$key] = [
|
||||
//'ep_calling_country_code' => $this->getUniqueArray(array_column($value, 'ep_calling_country_code')),
|
||||
//'ep_carrier_name' => $this->getUniqueArray(array_column($value, 'ep_carrier_name')),
|
||||
//'ep_checked' => $this->getUniqueArray(array_column($value, 'ep_checked')),
|
||||
//'ep_country_code' => $this->getUniqueArray(array_column($value, 'ep_country_code')),
|
||||
//'ep_created' => $this->getUniqueArray(array_column($value, 'ep_created')),
|
||||
//'ep_lastseen' => $this->getUniqueArray(array_column($value, 'ep_lastseen')),
|
||||
//'ep_mobile_country_code' => $this->getUniqueArray(array_column($value, 'ep_mobile_country_code')),
|
||||
//'ep_mobile_network_code' => $this->getUniqueArray(array_column($value, 'ep_mobile_network_code')),
|
||||
//'ep_national_format' => $this->getUniqueArray(array_column($value, 'ep_national_format')),
|
||||
'ep_phone_number' => $this->getUniqueArray(array_column($value, 'ep_phone_number')),
|
||||
'ep_shared' => $this->getUniqueArray(array_column($value, 'ep_shared')),
|
||||
'ep_type' => $this->getUniqueArray(array_column($value, 'ep_type')),
|
||||
//'ep_invalid' => $this->getUniqueArray(array_column($value, 'ep_invalid')),
|
||||
//'ep_validation_errors' => $this->getUniqueArray(array_column($value, 'ep_validation_errors')),
|
||||
//'ep_alert_list' => $this->getUniqueArray(array_column($value, 'ep_alert_list')),
|
||||
];
|
||||
}
|
||||
|
||||
return $recordsByAccount;
|
||||
}
|
||||
|
||||
protected function getDetails(array $accountIds, int $apiKey): array {
|
||||
[$params, $placeHolders] = $this->getRequestParams($accountIds, $apiKey);
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
event_phone.account_id AS accountid,
|
||||
|
||||
-- event_phone.calling_country_code AS ep_calling_country_code,
|
||||
-- event_phone.carrier_name AS ep_carrier_name,
|
||||
-- event_phone.checked AS ep_checked,
|
||||
-- event_phone.country_code AS ep_country_code,
|
||||
-- event_phone.created AS ep_created,
|
||||
-- event_phone.lastseen AS ep_lastseen,
|
||||
-- event_phone.mobile_country_code AS ep_mobile_country_code,
|
||||
-- event_phone.mobile_network_code AS ep_mobile_network_code,
|
||||
-- event_phone.national_format AS ep_national_format,
|
||||
event_phone.phone_number AS ep_phone_number,
|
||||
event_phone.shared AS ep_shared,
|
||||
event_phone.type AS ep_type
|
||||
-- event_phone.invalid AS ep_invalid,
|
||||
-- event_phone.validation_errors AS ep_validation_errors,
|
||||
-- event_phone.alert_list AS ep_alert_list
|
||||
|
||||
FROM
|
||||
event_phone
|
||||
|
||||
WHERE
|
||||
event_phone.key = :api_key
|
||||
AND event_phone.account_id IN ({$placeHolders})"
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?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 Models\Context;
|
||||
|
||||
class Session extends Base {
|
||||
public function getContext(array $accountIds, int $apiKey, int $timezoneOffset = 0): array {
|
||||
$records = $this->getDetails($accountIds, $apiKey, $timezoneOffset);
|
||||
// one record per account
|
||||
$recordsByAccount = $this->groupRecordsByAccount($records);
|
||||
|
||||
return $recordsByAccount;
|
||||
}
|
||||
|
||||
protected function getDetails(array $accountIds, int $apiKey, int $timezoneOffset = 0): array {
|
||||
[$params, $placeHolders] = $this->getRequestParams($accountIds, $apiKey);
|
||||
|
||||
$params[':night_start'] = gmdate('H:i:s', \Utils\Constants::get('NIGHT_RANGE_SECONDS_START') - $timezoneOffset);
|
||||
$params[':night_end'] = gmdate('H:i:s', \Utils\Constants::get('NIGHT_RANGE_SECONDS_END') - $timezoneOffset);
|
||||
|
||||
// boolean logic for defining time ranges overlap
|
||||
$query = (
|
||||
"SELECT
|
||||
event_session.account_id AS accountid,
|
||||
BOOL_OR(event_session.total_country > 1) AS event_session_multiple_country,
|
||||
BOOL_OR(event_session.total_ip > 1) AS event_session_multiple_ip,
|
||||
BOOL_OR(event_session.total_device > 1) AS event_session_multiple_device,
|
||||
BOOL_OR(
|
||||
(event_session.lastseen - event_session.created) > INTERVAL '1 day' OR
|
||||
(
|
||||
CASE WHEN :night_start::time < :night_end::time
|
||||
THEN
|
||||
(event_session.lastseen::time >= :night_start::time AND event_session.lastseen::time <= :night_end::time) OR
|
||||
(event_session.created::time >= :night_start::time AND event_session.created::time <= :night_end::time) OR
|
||||
(
|
||||
CASE WHEN event_session.lastseen::time > event_session.created::time
|
||||
THEN
|
||||
event_session.total_visit > 1 AND :night_start::time >= event_session.created::time AND :night_start::time <= event_session.lastseen::time
|
||||
ELSE
|
||||
event_session.total_visit > 1 AND (:night_start::time >= event_session.created::time OR :night_start::time <= event_session.lastseen::time)
|
||||
END
|
||||
)
|
||||
ELSE
|
||||
event_session.lastseen::time >= :night_start::time OR event_session.lastseen::time <= :night_end::time OR
|
||||
event_session.created::time >= :night_start::time OR event_session.created::time <= :night_end::time OR
|
||||
event_session.lastseen::time < event_session.created::time
|
||||
END
|
||||
)) AS event_session_night_time
|
||||
FROM
|
||||
event_session
|
||||
WHERE
|
||||
event_session.key = :api_key AND
|
||||
event_session.account_id IN ({$placeHolders})
|
||||
GROUP BY event_session.account_id"
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?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 Models\Context;
|
||||
|
||||
class User extends Base {
|
||||
use \Traits\Enrichment\Emails;
|
||||
|
||||
public function getContext(array $accountIds, int $apiKey): array {
|
||||
$results = $this->getDetails($accountIds, $apiKey);
|
||||
|
||||
$this->calculateEmailReputationForContext($results);
|
||||
|
||||
$recordsByAccount = [];
|
||||
foreach ($results as $item) {
|
||||
$recordsByAccount[$item['ea_id']] = $item;
|
||||
}
|
||||
|
||||
return $recordsByAccount;
|
||||
}
|
||||
|
||||
protected function getDetails(array $accountIds, int $apiKey): array {
|
||||
[$params, $placeHolders] = $this->getRequestParams($accountIds, $apiKey);
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
event_account.id AS ea_id,
|
||||
event_account.userid AS ea_userid,
|
||||
event_account.created AS ea_created,
|
||||
event_account.lastseen AS ea_lastseen,
|
||||
event_account.total_visit AS ea_total_visit,
|
||||
event_account.total_country AS ea_total_country,
|
||||
event_account.total_ip AS ea_total_ip,
|
||||
event_account.total_device AS ea_total_device,
|
||||
event_account.firstname AS ea_firstname,
|
||||
event_account.lastname AS ea_lastname,
|
||||
|
||||
event_email.email AS ee_email,
|
||||
event_email.blockemails AS ee_blockemails,
|
||||
event_email.data_breach AS ee_data_breach,
|
||||
-- event_email.profiles AS ee_profiles,
|
||||
event_email.checked AS ee_checked,
|
||||
|
||||
event_domain.discovery_date AS ed_discovery_date,
|
||||
event_domain.blockdomains AS ed_blockdomains,
|
||||
event_domain.disposable_domains AS ed_disposable_domains,
|
||||
-- event_domain.total_account AS ed_total_account,
|
||||
event_domain.free_email_provider AS ed_free_provider,
|
||||
event_domain.tranco_rank AS ed_tranco_rank,
|
||||
event_domain.creation_date AS ed_creation_date,
|
||||
event_domain.expiration_date AS ed_expiration_date,
|
||||
event_domain.return_code AS ed_return_code,
|
||||
event_domain.closest_snapshot AS ed_closest_snapshot,
|
||||
event_domain.mx_record AS ed_mx_record,
|
||||
|
||||
lastemail_record.email AS le_email,
|
||||
lastemail_record.blockemails AS le_blockemails,
|
||||
lastemail_record.data_breach AS le_data_breach,
|
||||
-- lastemail_record.profiles AS le_profiles,
|
||||
lastemail_record.checked AS le_checked,
|
||||
lastemail_record.fraud_detected AS le_fraud_detected,
|
||||
lastemail_record.alert_list AS le_alert_list,
|
||||
|
||||
lastdomain_record.disposable_domains AS ld_disposable_domains,
|
||||
lastdomain_record.free_email_provider AS ld_free_email_provider,
|
||||
lastdomain_record.blockdomains AS ld_blockdomains,
|
||||
lastdomain_record.mx_record AS ld_mx_record,
|
||||
lastdomain_record.disabled AS ld_disabled,
|
||||
lastdomain_record.creation_date AS ld_creation_date,
|
||||
lastdomain_record.tranco_rank AS ld_tranco_rank,
|
||||
|
||||
lastphone_record.phone_number AS lp_phone_number,
|
||||
lastphone_record.country_code AS lp_country_code,
|
||||
lastphone_record.invalid AS lp_invalid,
|
||||
lastphone_record.fraud_detected AS lp_fraud_detected,
|
||||
lastphone_record.alert_list AS lp_alert_list
|
||||
|
||||
FROM
|
||||
event_account
|
||||
|
||||
LEFT JOIN event_phone
|
||||
ON (event_account.id = event_phone.account_id)
|
||||
|
||||
LEFT JOIN event_email
|
||||
ON event_account.id = event_email.account_id
|
||||
|
||||
LEFT JOIN event_domain
|
||||
ON event_email.domain = event_domain.id
|
||||
|
||||
LEFT JOIN event_email AS lastemail_record
|
||||
ON event_account.lastemail = lastemail_record.id
|
||||
|
||||
LEFT JOIN event_phone AS lastphone_record
|
||||
ON event_account.lastphone = lastphone_record.id
|
||||
|
||||
LEFT JOIN event_domain AS lastdomain_record
|
||||
ON lastemail_record.domain = lastdomain_record.id
|
||||
|
||||
WHERE
|
||||
event_account.key = :api_key
|
||||
AND event_account.id IN ({$placeHolders})"
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,292 @@
|
||||
<?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 Models;
|
||||
|
||||
class Country extends \Models\BaseSql implements \Interfaces\ApiKeyAccessAuthorizationInterface {
|
||||
protected $DB_TABLE_NAME = 'event_country';
|
||||
|
||||
public function getCountryById(int $countryId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':country_id' => $countryId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
countries.iso,
|
||||
countries.value
|
||||
|
||||
FROM
|
||||
event_country
|
||||
|
||||
INNER JOIN countries
|
||||
ON (event_country.country = countries.id)
|
||||
|
||||
WHERE
|
||||
event_country.key = :api_key
|
||||
AND event_country.country = :country_id'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0] ?? [];
|
||||
}
|
||||
|
||||
public function getCountryIdByIso(string $countryIso): int {
|
||||
$params = [
|
||||
':country_iso' => $countryIso,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
countries.id
|
||||
|
||||
FROM
|
||||
countries
|
||||
|
||||
WHERE
|
||||
countries.iso = :country_iso'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0]['id'] ?? 0;
|
||||
}
|
||||
|
||||
public function checkAccess(int $subjectId, int $apiKey): bool {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':country_id' => $subjectId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_country.country
|
||||
|
||||
FROM
|
||||
event_country
|
||||
|
||||
WHERE
|
||||
event_country.key = :api_key
|
||||
AND event_country.country = :country_id'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return count($results) > 0;
|
||||
}
|
||||
|
||||
public function insertRecord(array $data, int $apiKey): int {
|
||||
$params = [
|
||||
':key' => $apiKey,
|
||||
':country' => $data['id'],
|
||||
':lastseen' => $data['lastseen'],
|
||||
':updated' => $data['lastseen'],
|
||||
];
|
||||
|
||||
$query = (
|
||||
'INSERT INTO event_country (
|
||||
key, country, lastseen, updated
|
||||
) VALUES (
|
||||
:key, :country, :lastseen, :updated
|
||||
) ON CONFLICT (country, key) DO UPDATE SET
|
||||
lastseen = EXCLUDED.lastseen
|
||||
RETURNING id'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0]['id'];
|
||||
}
|
||||
|
||||
public function getTimeFrameTotal(array $ids, string $startDate, string $endDate, int $apiKey): array {
|
||||
[$params, $flatIds] = $this->getArrayPlaceholders($ids);
|
||||
$params[':key'] = $apiKey;
|
||||
$params[':start_date'] = $startDate;
|
||||
$params[':end_date'] = $endDate;
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
event_ip.country AS id,
|
||||
COUNT(*) AS cnt
|
||||
FROM event
|
||||
INNER JOIN event_ip
|
||||
ON event.ip = event_ip.id
|
||||
WHERE
|
||||
event_ip.country IN ({$flatIds}) AND
|
||||
event.key = :key AND
|
||||
event.time > :start_date AND
|
||||
event.time < :end_date
|
||||
GROUP BY event_ip.country"
|
||||
);
|
||||
|
||||
$totalVisit = $this->execQuery($query, $params);
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
event_ip.country AS id,
|
||||
COUNT(DISTINCT(event.account)) AS cnt
|
||||
FROM event
|
||||
INNER JOIN event_ip
|
||||
ON event.ip = event_ip.id
|
||||
WHERE
|
||||
event_ip.country IN ({$flatIds}) AND
|
||||
event.key = :key AND
|
||||
event.time > :start_date AND
|
||||
event.time < :end_date
|
||||
GROUP BY event_ip.country"
|
||||
);
|
||||
|
||||
$totalAccount = $this->execQuery($query, $params);
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
event_ip.country AS id,
|
||||
COUNT(*) AS cnt
|
||||
FROM event_ip
|
||||
WHERE
|
||||
event_ip.country IN ({$flatIds}) AND
|
||||
event_ip.key = :key AND
|
||||
event_ip.lastseen > :start_date AND
|
||||
event_ip.lastseen < :end_date
|
||||
GROUP BY event_ip.country"
|
||||
);
|
||||
|
||||
$totalIp = $this->execQuery($query, $params);
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($ids as $id) {
|
||||
$result[$id] = ['total_visit' => 0, 'total_account' => 0, 'total_ip' => 0];
|
||||
}
|
||||
|
||||
foreach ($totalVisit as $rec) {
|
||||
$result[$rec['id']]['total_visit'] = $rec['cnt'];
|
||||
}
|
||||
|
||||
foreach ($totalAccount as $rec) {
|
||||
$result[$rec['id']]['total_account'] = $rec['cnt'];
|
||||
}
|
||||
|
||||
foreach ($totalIp as $rec) {
|
||||
$result[$rec['id']]['total_ip'] = $rec['cnt'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function updateTotalsByEntityIds(array $ids, int $apiKey, bool $force = false): void {
|
||||
if (!count($ids)) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$params, $flatIds] = $this->getArrayPlaceholders($ids);
|
||||
$params[':key'] = $apiKey;
|
||||
$extraClause = $force ? '' : ' AND event_country.lastseen >= event_country.updated';
|
||||
|
||||
$query = (
|
||||
"UPDATE event_country
|
||||
SET
|
||||
total_visit = COALESCE(sub.total_visit, 0),
|
||||
total_account = COALESCE(sub.total_account, 0),
|
||||
total_ip = COALESCE(sub.total_ip, 0),
|
||||
updated = date_trunc('milliseconds', now())
|
||||
FROM (
|
||||
SELECT
|
||||
event_ip.country,
|
||||
COUNT(*) AS total_visit,
|
||||
COUNT(DISTINCT event.account) AS total_account,
|
||||
COUNT(DISTINCT event.ip) AS total_ip
|
||||
FROM event
|
||||
JOIN event_ip ON event.ip = event_ip.id
|
||||
WHERE
|
||||
event_ip.country IN ($flatIds) AND
|
||||
event.key = :key
|
||||
GROUP BY event_ip.country
|
||||
) AS sub
|
||||
RIGHT JOIN countries sub_country ON sub.country = sub_country.id
|
||||
WHERE
|
||||
event_country.country = sub_country.id AND
|
||||
event_country.country IN ($flatIds) AND
|
||||
event_country.key = :key
|
||||
$extraClause"
|
||||
);
|
||||
|
||||
$this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function updateAllTotals(int $apiKey): int {
|
||||
$params = [
|
||||
':key' => $apiKey,
|
||||
];
|
||||
$query = (
|
||||
'UPDATE event_country
|
||||
SET
|
||||
total_visit = COALESCE(sub.total_visit, 0),
|
||||
total_account = COALESCE(sub.total_account, 0),
|
||||
total_ip = COALESCE(sub.total_ip, 0),
|
||||
updated = date_trunc(\'milliseconds\', now())
|
||||
FROM (
|
||||
SELECT
|
||||
event_ip.country,
|
||||
COUNT(*) AS total_visit,
|
||||
COUNT(DISTINCT event.account) AS total_account,
|
||||
COUNT(DISTINCT event.ip) AS total_ip
|
||||
FROM event
|
||||
JOIN event_ip ON event.ip = event_ip.id AND event.key = event_ip.key
|
||||
WHERE event.key = :key
|
||||
GROUP BY event_ip.country
|
||||
) AS sub
|
||||
RIGHT JOIN countries sub_country ON sub.country = sub_country.id
|
||||
WHERE
|
||||
event_country.key = :key AND
|
||||
event_country.country = sub_country.id AND
|
||||
event_country.lastseen >= event_country.updated'
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function refreshTotals(array $res, int $apiKey): array {
|
||||
[$params, $flatIds] = $this->getArrayPlaceholders(array_column($res, 'id'));
|
||||
$params[':key'] = $apiKey;
|
||||
$query = (
|
||||
"SELECT
|
||||
country AS id,
|
||||
total_ip,
|
||||
total_visit,
|
||||
total_account
|
||||
FROM event_country
|
||||
WHERE country IN ({$flatIds}) AND key = :key"
|
||||
);
|
||||
|
||||
$result = $this->execQuery($query, $params);
|
||||
|
||||
$indexedResult = [];
|
||||
foreach ($result as $item) {
|
||||
$indexedResult[$item['id']] = $item;
|
||||
}
|
||||
|
||||
foreach ($res as $idx => $item) {
|
||||
$item['total_ip'] = $indexedResult[$item['id']]['total_ip'];
|
||||
$item['total_visit'] = $indexedResult[$item['id']]['total_visit'];
|
||||
$item['total_account'] = $indexedResult[$item['id']]['total_account'];
|
||||
$res[$idx] = $item;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?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 Models;
|
||||
|
||||
class Dashboard extends \Models\BaseSql {
|
||||
protected $DB_TABLE_NAME = 'event_account';
|
||||
|
||||
public function getTotalBlockedUsers(?array $dateRange, int $apiKey): int {
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(event_account.id)
|
||||
|
||||
FROM
|
||||
event_account
|
||||
|
||||
WHERE
|
||||
event_account.key = :api_key AND
|
||||
event_account.fraud IS TRUE'
|
||||
);
|
||||
|
||||
$field = 'event_account.latest_decision';
|
||||
|
||||
return $this->getTotal($query, $field, $dateRange, $apiKey);
|
||||
}
|
||||
|
||||
public function getTotalUsersForReview(?array $dateRange, int $apiKey): int {
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(event_account.id)
|
||||
|
||||
FROM
|
||||
event_account
|
||||
|
||||
WHERE
|
||||
event_account.key = :api_key AND
|
||||
event_account.fraud IS NULL AND
|
||||
event_account.added_to_review IS NOT NULL'
|
||||
);
|
||||
|
||||
$field = 'event_account.added_to_review';
|
||||
|
||||
return $this->getTotal($query, $field, $dateRange, $apiKey);
|
||||
}
|
||||
|
||||
public function getTotalEvents(?array $dateRange, int $apiKey): int {
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(*)
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
WHERE
|
||||
event.key = :api_key'
|
||||
);
|
||||
|
||||
$field = 'event.time';
|
||||
|
||||
return $this->getTotal($query, $field, $dateRange, $apiKey);
|
||||
}
|
||||
|
||||
public function getTotalResources(?array $dateRange, int $apiKey): int {
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(*)
|
||||
|
||||
FROM
|
||||
event_url
|
||||
|
||||
WHERE
|
||||
event_url.key = :api_key'
|
||||
);
|
||||
|
||||
$field = 'event_url.lastseen';
|
||||
|
||||
return $this->getTotal($query, $field, $dateRange, $apiKey);
|
||||
}
|
||||
|
||||
public function getTotalCountries(?array $dateRange, int $apiKey): int {
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(event_country.id)
|
||||
|
||||
FROM
|
||||
event_country
|
||||
|
||||
WHERE
|
||||
event_country.key = :api_key'
|
||||
);
|
||||
|
||||
$field = 'event_country.lastseen';
|
||||
|
||||
return $this->getTotal($query, $field, $dateRange, $apiKey);
|
||||
}
|
||||
|
||||
public function getTotalIps(?array $dateRange, int $apiKey): int {
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT (*)
|
||||
|
||||
FROM
|
||||
event_ip
|
||||
|
||||
WHERE
|
||||
event_ip.key = :api_key'
|
||||
);
|
||||
|
||||
$field = 'event_ip.lastseen';
|
||||
|
||||
return $this->getTotal($query, $field, $dateRange, $apiKey);
|
||||
}
|
||||
|
||||
public function getTotalUsers(?array $dateRange, int $apiKey): int {
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT (*)
|
||||
|
||||
FROM
|
||||
event_account
|
||||
|
||||
WHERE
|
||||
event_account.key = :api_key'
|
||||
);
|
||||
|
||||
$field = 'event_account.lastseen';
|
||||
|
||||
return $this->getTotal($query, $field, $dateRange, $apiKey);
|
||||
}
|
||||
|
||||
private function getTotal(string $query, string $dateField, ?array $dateRange, int $apiKey): int {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
];
|
||||
|
||||
if ($dateRange) {
|
||||
$params[':end_time'] = $dateRange['endDate'];
|
||||
$params[':start_time'] = $dateRange['startDate'];
|
||||
|
||||
$query .= " AND {$dateField} >= :start_time AND {$dateField} <= :end_time";
|
||||
}
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0]['count'] ?? 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
<?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 Models;
|
||||
|
||||
class Device extends \Models\BaseSql implements \Interfaces\ApiKeyAccessAuthorizationInterface {
|
||||
protected $DB_TABLE_NAME = 'event_device';
|
||||
|
||||
public function checkAccess(int $subjectId, int $apiKey): bool {
|
||||
$query = (
|
||||
'SELECT
|
||||
event_device.id
|
||||
|
||||
FROM
|
||||
event_device
|
||||
|
||||
WHERE
|
||||
event_device.key = :api_key
|
||||
AND event_device.id = :device_id'
|
||||
);
|
||||
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':device_id' => $subjectId,
|
||||
];
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return count($results) > 0;
|
||||
}
|
||||
|
||||
public function getFullDeviceInfoById(int $deviceId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':device_id' => $deviceId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_device.id,
|
||||
event_device.lang,
|
||||
event_device.created,
|
||||
event_device.user_agent AS ua_id,
|
||||
event_ua_parsed.device,
|
||||
event_ua_parsed.browser_name,
|
||||
event_ua_parsed.browser_version,
|
||||
event_ua_parsed.os_name,
|
||||
event_ua_parsed.os_version,
|
||||
event_ua_parsed.ua,
|
||||
event_ua_parsed.checked,
|
||||
event_ua_parsed.modified
|
||||
FROM
|
||||
event_device
|
||||
LEFT JOIN event_ua_parsed
|
||||
ON (event_device.user_agent = event_ua_parsed.id)
|
||||
|
||||
WHERE
|
||||
event_device.key = :api_key AND
|
||||
event_device.id = :device_id'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0] ?? [];
|
||||
}
|
||||
|
||||
public function extractById(int $entityId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':id' => $entityId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
COALESCE(event_ua_parsed.ua, '') AS value
|
||||
FROM
|
||||
event_ua_parsed
|
||||
WHERE
|
||||
event_ua_parsed.key = :api_key AND
|
||||
event_ua_parsed.id = :id
|
||||
LIMIT 1"
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0] ?? [];
|
||||
}
|
||||
|
||||
public function updateAllTotals(int $apiKey): int {
|
||||
$params = [
|
||||
':key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'UPDATE event_device
|
||||
SET
|
||||
total_visit = COALESCE(sub.total_visit, 0),
|
||||
updated = date_trunc(\'milliseconds\', now())
|
||||
FROM (
|
||||
SELECT
|
||||
event.device,
|
||||
COUNT(*) AS total_visit
|
||||
FROM event
|
||||
WHERE
|
||||
event.key = :key
|
||||
GROUP BY event.device
|
||||
) AS sub
|
||||
RIGHT JOIN event_device sub_device ON sub.device = sub_device.id
|
||||
WHERE
|
||||
event_device.id = sub_device.id AND
|
||||
event_device.key = :key AND
|
||||
event_device.lastseen >= event_device.updated'
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function countNotChecked(int $apiKey): int {
|
||||
$params = [
|
||||
':key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(*) AS count
|
||||
FROM event_ua_parsed
|
||||
WHERE
|
||||
event_ua_parsed.key = :key AND
|
||||
event_ua_parsed.checked IS FALSE'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0]['count'] ?? 0;
|
||||
}
|
||||
|
||||
public function notCheckedExists(int $apiKey): bool {
|
||||
$params = [
|
||||
':key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT 1
|
||||
FROM event_ua_parsed
|
||||
WHERE
|
||||
event_ua_parsed.key = :key AND
|
||||
event_ua_parsed.checked IS FALSE
|
||||
LIMIT 1'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return (bool) count($results);
|
||||
}
|
||||
|
||||
public function notCheckedForUserId(int $userId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':user_id' => $userId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT DISTINCT
|
||||
event_ua_parsed.id
|
||||
FROM event_device
|
||||
LEFT JOIN event_ua_parsed ON event_device.user_agent = event_ua_parsed.id
|
||||
WHERE
|
||||
event_device.account_id = :user_id AND
|
||||
event_device.key = :api_key AND
|
||||
event_ua_parsed.checked IS FALSE'
|
||||
);
|
||||
|
||||
return array_column($this->execQuery($query, $params), 'id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
<?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 Models;
|
||||
|
||||
class Domain extends \Models\BaseSql implements \Interfaces\ApiKeyAccessAuthorizationInterface {
|
||||
protected $DB_TABLE_NAME = 'event_domain';
|
||||
|
||||
public function checkAccess(int $subjectId, int $apiKey): bool {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':domain_id' => $subjectId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_domain.id
|
||||
|
||||
FROM
|
||||
event_domain
|
||||
|
||||
WHERE
|
||||
event_domain.key = :api_key
|
||||
AND event_domain.id = :domain_id'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return count($results) > 0;
|
||||
}
|
||||
|
||||
public function getFullDomainInfoById(int $domainId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':domain_id' => $domainId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_domain.id,
|
||||
event_domain.domain,
|
||||
event_domain.total_account,
|
||||
event_domain.lastseen,
|
||||
event_domain.creation_date,
|
||||
event_domain.expiration_date,
|
||||
event_domain.disabled,
|
||||
event_domain.disposable_domains,
|
||||
event_domain.free_email_provider,
|
||||
event_domain.tranco_rank,
|
||||
event_domain.checked,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM event_email
|
||||
WHERE
|
||||
event_email.domain = event_domain.id AND
|
||||
event_email.key = :api_key AND
|
||||
event_email.fraud_detected IS TRUE
|
||||
) AS fraud
|
||||
|
||||
FROM
|
||||
event_domain
|
||||
|
||||
WHERE
|
||||
event_domain.key = :api_key
|
||||
AND event_domain.id = :domain_id
|
||||
|
||||
GROUP BY
|
||||
event_domain.id'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0] ?? [];
|
||||
}
|
||||
|
||||
public function extractById(int $entityId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':id' => $entityId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
COALESCE(event_domain.domain, '') AS value
|
||||
|
||||
FROM
|
||||
event_domain
|
||||
|
||||
WHERE
|
||||
event_domain.key = :api_key
|
||||
AND event_domain.id = :id
|
||||
|
||||
LIMIT 1"
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0] ?? [];
|
||||
}
|
||||
|
||||
public function getTimeFrameTotal(array $ids, string $startDate, string $endDate, int $apiKey): array {
|
||||
[$params, $flatIds] = $this->getArrayPlaceholders($ids);
|
||||
$params[':key'] = $apiKey;
|
||||
$params[':start_date'] = $startDate;
|
||||
$params[':end_date'] = $endDate;
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
event_email.domain AS id,
|
||||
COUNT(DISTINCT(event.account)) AS cnt
|
||||
FROM event
|
||||
LEFT JOIN event_email
|
||||
ON event.email = event_email.id
|
||||
WHERE
|
||||
event_email.domain IN ({$flatIds}) AND
|
||||
event.key = :key AND
|
||||
event_email.lastseen > :start_date AND
|
||||
event_email.lastseen < :end_date
|
||||
GROUP BY event_email.domain"
|
||||
);
|
||||
|
||||
$totalAccount = $this->execQuery($query, $params);
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($ids as $id) {
|
||||
$result[$id] = ['total_account' => 0];
|
||||
}
|
||||
|
||||
foreach ($totalAccount as $rec) {
|
||||
$result[$rec['id']]['total_account'] = $rec['cnt'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function updateTotalsByEntityIds(array $ids, int $apiKey, bool $force = false): void {
|
||||
if (!count($ids)) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$params, $flatIds] = $this->getArrayPlaceholders($ids);
|
||||
$params[':key'] = $apiKey;
|
||||
$extraClause = $force ? '' : ' AND event_domain.lastseen >= event_domain.updated';
|
||||
|
||||
$query = (
|
||||
"UPDATE event_domain
|
||||
SET
|
||||
total_visit = COALESCE(sub.total_visit, 0),
|
||||
total_account = COALESCE(sub.total_account, 0),
|
||||
updated = date_trunc('milliseconds', now())
|
||||
FROM (
|
||||
SELECT
|
||||
event_email.domain,
|
||||
COUNT(*) AS total_visit,
|
||||
COUNT(DISTINCT account) AS total_account
|
||||
FROM event
|
||||
LEFT JOIN event_email
|
||||
ON event.email = event_email.id
|
||||
WHERE
|
||||
event_email.domain IN ($flatIds) AND
|
||||
event.key = :key
|
||||
GROUP BY event_email.domain
|
||||
) AS sub
|
||||
RIGHT JOIN event_domain sub_domain ON sub.domain = sub_domain.id
|
||||
WHERE
|
||||
event_domain.id = sub_domain.id AND
|
||||
event_domain.id IN ($flatIds) AND
|
||||
event_domain.key = :key
|
||||
$extraClause"
|
||||
);
|
||||
|
||||
$this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function updateAllTotals(int $apiKey): int {
|
||||
$params = [
|
||||
':key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'UPDATE event_domain
|
||||
SET
|
||||
total_visit = COALESCE(sub.total_visit, 0),
|
||||
total_account = COALESCE(sub.total_account, 0),
|
||||
updated = date_trunc(\'milliseconds\', now())
|
||||
FROM (
|
||||
SELECT
|
||||
event_email.domain,
|
||||
COUNT(*) AS total_visit,
|
||||
COUNT(DISTINCT account) AS total_account
|
||||
FROM event
|
||||
LEFT JOIN event_email ON event.email = event_email.id
|
||||
WHERE
|
||||
event.key = :key
|
||||
GROUP BY event_email.domain
|
||||
) AS sub
|
||||
RIGHT JOIN event_domain sub_domain ON sub.domain = sub_domain.id
|
||||
WHERE
|
||||
event_domain.id = sub_domain.id AND
|
||||
event_domain.key = :key AND
|
||||
event_domain.lastseen >= event_domain.updated'
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function refreshTotals(array $res, int $apiKey): array {
|
||||
[$params, $flatIds] = $this->getArrayPlaceholders(array_column($res, 'id'));
|
||||
$params[':key'] = $apiKey;
|
||||
$query = (
|
||||
"SELECT
|
||||
id,
|
||||
total_visit,
|
||||
total_account
|
||||
FROM event_domain
|
||||
WHERE id IN ({$flatIds}) AND key = :key"
|
||||
);
|
||||
|
||||
$result = $this->execQuery($query, $params);
|
||||
|
||||
$indexedResult = [];
|
||||
foreach ($result as $item) {
|
||||
$indexedResult[$item['id']] = $item;
|
||||
}
|
||||
|
||||
foreach ($res as $idx => $item) {
|
||||
$item['total_visit'] = $indexedResult[$item['id']]['total_visit'];
|
||||
$item['total_account'] = $indexedResult[$item['id']]['total_account'];
|
||||
$res[$idx] = $item;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function countNotChecked(int $apiKey): int {
|
||||
$params = [
|
||||
':key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(*) AS count
|
||||
FROM event_domain
|
||||
WHERE
|
||||
event_domain.key = :key AND
|
||||
event_domain.checked IS FALSE'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0]['count'] ?? 0;
|
||||
}
|
||||
|
||||
public function notCheckedExists(int $apiKey): bool {
|
||||
$params = [
|
||||
':key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT 1
|
||||
FROM event_domain
|
||||
WHERE
|
||||
event_domain.key = :key AND
|
||||
event_domain.checked IS FALSE
|
||||
LIMIT 1'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return (bool) count($results);
|
||||
}
|
||||
|
||||
public function notCheckedForUserId(int $userId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':user_id' => $userId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT DISTINCT
|
||||
event_domain.id
|
||||
FROM event_email
|
||||
LEFT JOIN event_domain ON event_email.domain = event_domain.id
|
||||
WHERE
|
||||
event_email.account_id = :user_id AND
|
||||
event_domain.key = :api_key AND
|
||||
event_domain.checked IS FALSE'
|
||||
);
|
||||
|
||||
return array_column($this->execQuery($query, $params), 'id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
<?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 Models;
|
||||
|
||||
class Email extends \Models\BaseSql implements \Interfaces\FraudFlagUpdaterInterface {
|
||||
use \Traits\Enrichment\Emails;
|
||||
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getEmailDetails(int $id, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':id' => $id,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_email.id AS email_id,
|
||||
event_email.email,
|
||||
event_email.lastseen AS email_lastseen,
|
||||
event_email.created AS email_created,
|
||||
event_email.data_breach,
|
||||
event_email.data_breaches,
|
||||
event_email.earliest_breach,
|
||||
event_email.profiles,
|
||||
event_email.blockemails,
|
||||
event_email.domain_contact_email,
|
||||
event_email.fraud_detected,
|
||||
event_email.checked,
|
||||
-- event_email.alert_list,
|
||||
|
||||
event_domain.id AS domain_id,
|
||||
event_domain.domain,
|
||||
event_domain.blockdomains,
|
||||
event_domain.disposable_domains,
|
||||
event_domain.total_visit,
|
||||
event_domain.total_account,
|
||||
event_domain.lastseen AS domain_lastseen,
|
||||
event_domain.created AS domain_created,
|
||||
event_domain.free_email_provider,
|
||||
event_domain.tranco_rank,
|
||||
event_domain.creation_date,
|
||||
event_domain.expiration_date,
|
||||
event_domain.return_code,
|
||||
event_domain.closest_snapshot,
|
||||
event_domain.mx_record,
|
||||
event_domain.disabled
|
||||
|
||||
FROM
|
||||
event_email
|
||||
LEFT JOIN event_domain
|
||||
ON event_email.domain = event_domain.id
|
||||
|
||||
WHERE
|
||||
event_email.id = :id AND
|
||||
event_email.key = :api_key'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
$this->calculateEmailReputation($results);
|
||||
|
||||
return $results[0] ?? [];
|
||||
}
|
||||
|
||||
public function getIdByValue(string $email, int $apiKey): ?int {
|
||||
$query = (
|
||||
'SELECT
|
||||
event_email.id
|
||||
FROM
|
||||
event_email
|
||||
WHERE
|
||||
event_email.key = :api_key
|
||||
AND event_email.email = :email_value'
|
||||
);
|
||||
|
||||
$params = [
|
||||
':email_value' => $email,
|
||||
':api_key' => $apiKey,
|
||||
];
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0]['id'] ?? null;
|
||||
}
|
||||
|
||||
public function getSeenInLastDay(
|
||||
bool $includeAlertListed = false,
|
||||
bool $includeWithoutHash = false,
|
||||
bool $includeWithBlacklistSyncSkipped = false,
|
||||
): array {
|
||||
$params = [
|
||||
':includeAlertListed' => $includeAlertListed,
|
||||
':includeWithoutHash' => $includeWithoutHash,
|
||||
':includeWithBlacklistSyncSkipped' => $includeWithBlacklistSyncSkipped,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_email.key,
|
||||
event_email.email,
|
||||
event_email.hash
|
||||
FROM
|
||||
event_email
|
||||
JOIN
|
||||
event_account ON event_email.account_id = event_account.id
|
||||
JOIN
|
||||
dshb_api ON event_account.key = dshb_api.id
|
||||
WHERE
|
||||
event_email.lastseen >= CURRENT_DATE - 1
|
||||
AND (:includeAlertListed = TRUE OR event_email.alert_list != TRUE OR event_email.alert_list IS NULL)
|
||||
AND (:includeWithoutHash = TRUE OR event_email.hash IS NOT NULL)
|
||||
AND (:includeWithBlacklistSyncSkipped = TRUE OR dshb_api.skip_blacklist_sync != TRUE)'
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function updateAlertListedByHashes(array $hashes, bool $alertListed, int $apiKey): void {
|
||||
[$params, $placeHolders] = $this->getArrayPlaceholders($hashes);
|
||||
$params[':alertListed'] = $alertListed;
|
||||
$params[':key'] = $apiKey;
|
||||
|
||||
$query = (
|
||||
"UPDATE event_email
|
||||
SET alert_list = :alertListed
|
||||
WHERE
|
||||
key = :key AND
|
||||
hash IN ({$placeHolders})"
|
||||
);
|
||||
|
||||
$this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function updateFraudFlag(array $ids, bool $fraud, int $apiKey): void {
|
||||
if (!count($ids)) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$params, $placeHolders] = $this->getArrayPlaceholders($ids);
|
||||
|
||||
$params[':fraud'] = $fraud;
|
||||
$params[':api_key'] = $apiKey;
|
||||
|
||||
$query = (
|
||||
"UPDATE event_email
|
||||
SET fraud_detected = :fraud
|
||||
|
||||
WHERE
|
||||
key = :api_key
|
||||
AND id IN ({$placeHolders})"
|
||||
);
|
||||
|
||||
$this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function extractById(int $entityId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':id' => $entityId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
"SELECT
|
||||
COALESCE(event_email.email, '') AS value,
|
||||
event_email.hash AS hash
|
||||
|
||||
FROM
|
||||
event_email
|
||||
|
||||
WHERE
|
||||
event_email.key = :api_key
|
||||
AND event_email.id = :id
|
||||
|
||||
LIMIT 1"
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0] ?? [];
|
||||
}
|
||||
|
||||
public function countNotChecked(int $apiKey): int {
|
||||
$params = [
|
||||
':key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(*) AS count
|
||||
FROM event_email
|
||||
WHERE
|
||||
event_email.key = :key AND
|
||||
event_email.checked IS FALSE'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0]['count'] ?? 0;
|
||||
}
|
||||
|
||||
public function notCheckedExists(int $apiKey): bool {
|
||||
$params = [
|
||||
':key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT 1
|
||||
FROM event_email
|
||||
WHERE
|
||||
event_email.key = :key AND
|
||||
event_email.checked IS FALSE
|
||||
LIMIT 1'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return (bool) count($results);
|
||||
}
|
||||
|
||||
public function notCheckedForUserId(int $userId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':user_id' => $userId,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT DISTINCT
|
||||
event_email.id
|
||||
FROM event_email
|
||||
WHERE
|
||||
event_email.account_id = :user_id AND
|
||||
event_email.key = :api_key AND
|
||||
event_email.checked IS FALSE'
|
||||
);
|
||||
|
||||
return array_column($this->execQuery($query, $params), 'id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?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 Models\Enrichment;
|
||||
|
||||
class Base {
|
||||
public function queryParams(): array {
|
||||
$properties = get_object_vars($this);
|
||||
$modifiedArray = [];
|
||||
foreach ($properties as $key => $value) {
|
||||
$modifiedArray[':' . $key] = $value;
|
||||
}
|
||||
|
||||
return $modifiedArray;
|
||||
}
|
||||
|
||||
public function slimIds(array $ids): array {
|
||||
$filtered = array_filter($ids, static function ($value): bool {
|
||||
return $value !== null;
|
||||
});
|
||||
|
||||
return array_unique($filtered);
|
||||
}
|
||||
|
||||
public function updateStringByPlaceholders(array $placeholders): string {
|
||||
$transformed = array_map(static function ($item): string {
|
||||
$key = ltrim($item, ':');
|
||||
|
||||
return "{$key} = {$item}";
|
||||
}, $placeholders);
|
||||
return implode(', ', $transformed);
|
||||
}
|
||||
|
||||
public function validateIP(string $ip): bool {
|
||||
return filter_var($ip, FILTER_VALIDATE_IP) !== false;
|
||||
}
|
||||
|
||||
// Validate date
|
||||
public function validateDate(string $date, string $format = 'Y-m-d'): bool {
|
||||
$d = \DateTime::createFromFormat($format, $date);
|
||||
|
||||
return $d && $d->format($format) === $date;
|
||||
}
|
||||
|
||||
public function validateDates(array $dates): bool {
|
||||
foreach ($dates as $date) {
|
||||
if ($date !== null && !$this->validateDate($date)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function validateCidr(string $cidr): bool {
|
||||
$parts = explode('/', $cidr);
|
||||
if (count($parts) !== 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ip = $parts[0];
|
||||
$netmask = intval($parts[1]);
|
||||
|
||||
if ($netmask < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
return $netmask <= 32;
|
||||
}
|
||||
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
return $netmask <= 128;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?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 Models\Enrichment;
|
||||
|
||||
class Device extends \Models\Enrichment\Base {
|
||||
protected string $ua;
|
||||
protected ?string $device;
|
||||
protected ?string $browser_name;
|
||||
protected ?string $browser_version;
|
||||
protected ?string $os_name;
|
||||
protected ?string $os_version;
|
||||
protected bool $modified;
|
||||
protected bool $checked = true;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function init(array $data): void {
|
||||
$this->ua = $data['ua'];
|
||||
$this->device = $data['device'];
|
||||
$this->browser_name = $data['browser_name'];
|
||||
$this->browser_version = $data['browser_version'];
|
||||
$this->os_name = $data['os_name'];
|
||||
$this->os_version = $data['os_version'];
|
||||
$this->modified = $data['modified'];
|
||||
}
|
||||
|
||||
public function prepareUpdate(): array {
|
||||
$params = $this->queryParams();
|
||||
unset($params[':ua']);
|
||||
|
||||
$placeholders = array_keys($params);
|
||||
$updateString = $this->updateStringByPlaceholders($placeholders);
|
||||
|
||||
return [$params, $updateString];
|
||||
}
|
||||
|
||||
public function updateEntityInDb(int $entityId, int $apiKey): void {
|
||||
// total_visit and total_account should remain still
|
||||
[$params, $updateString] = $this->prepareUpdate();
|
||||
|
||||
$params['entity_id'] = $entityId;
|
||||
$params['key'] = $apiKey;
|
||||
|
||||
$query = ("
|
||||
UPDATE event_ua_parsed
|
||||
SET {$updateString}
|
||||
WHERE
|
||||
event_ua_parsed.id = :entity_id AND
|
||||
event_ua_parsed.key = :key
|
||||
");
|
||||
|
||||
$model = new \Models\Device();
|
||||
$model->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?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 Models\Enrichment;
|
||||
|
||||
class DomainFound extends \Models\Enrichment\Base {
|
||||
protected string $domain;
|
||||
protected bool $blockdomains;
|
||||
protected bool $disposable_domains;
|
||||
protected bool $free_email_provider;
|
||||
protected ?string $creation_date; // date
|
||||
protected ?string $expiration_date; // date
|
||||
protected ?int $return_code;
|
||||
protected bool $disabled;
|
||||
protected ?string $closest_snapshot; // date
|
||||
protected bool $mx_record;
|
||||
protected ?string $ip; // ipvanyaddress
|
||||
protected ?string $geo_ip;
|
||||
protected ?string $geo_html;
|
||||
protected ?string $web_server;
|
||||
protected ?string $hostname;
|
||||
protected ?string $emails;
|
||||
protected ?string $phone;
|
||||
protected string $discovery_date; // date
|
||||
protected ?int $tranco_rank;
|
||||
protected bool $checked = true;
|
||||
|
||||
public function init(array $data): void {
|
||||
$this->domain = $data['domain'];
|
||||
$this->blockdomains = $data['blockdomains'];
|
||||
$this->disposable_domains = $data['disposable_domains'];
|
||||
$this->free_email_provider = $data['free_email_provider'];
|
||||
$this->creation_date = $data['creation_date'];
|
||||
$this->expiration_date = $data['expiration_date'];
|
||||
$this->return_code = $data['return_code'];
|
||||
$this->disabled = $data['disabled'];
|
||||
$this->closest_snapshot = $data['closest_snapshot'];
|
||||
$this->mx_record = $data['mx_record'];
|
||||
$this->ip = $data['ip'];
|
||||
$this->geo_ip = $data['geo_ip'];
|
||||
$this->geo_html = $data['geo_html'];
|
||||
$this->web_server = $data['web_server'];
|
||||
$this->hostname = $data['hostname'];
|
||||
$this->emails = $data['emails'];
|
||||
$this->phone = $data['phone'];
|
||||
$this->discovery_date = $data['discovery_date'];
|
||||
$this->tranco_rank = $data['tranco_rank'];
|
||||
|
||||
$dates = [$this->creation_date, $this->expiration_date, $this->closest_snapshot, $this->discovery_date];
|
||||
|
||||
if (($this->ip && !$this->validateIP($this->ip)) || !$this->validateDates($dates)) {
|
||||
throw new \Exception('Validation failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function prepareUpdate(): array {
|
||||
$params = $this->queryParams();
|
||||
unset($params[':domain']);
|
||||
|
||||
$placeholders = array_keys($params);
|
||||
$updateString = $this->updateStringByPlaceholders($placeholders);
|
||||
|
||||
return [$params, $updateString];
|
||||
}
|
||||
|
||||
public function updateEntityInDb(int $entityId, int $apiKey): void {
|
||||
// total_visit and total_account should remain still
|
||||
[$params, $updateString] = $this->prepareUpdate();
|
||||
|
||||
$params['entity_id'] = $entityId;
|
||||
$params['key'] = $apiKey;
|
||||
|
||||
$query = ("
|
||||
UPDATE event_domain
|
||||
SET {$updateString}
|
||||
WHERE
|
||||
event_domain.id = :entity_id AND
|
||||
event_domain.key = :key
|
||||
");
|
||||
|
||||
$model = new \Models\Domain();
|
||||
$model->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?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 Models\Enrichment;
|
||||
|
||||
class DomainNotFound extends \Models\Enrichment\Base {
|
||||
protected string $domain;
|
||||
protected bool $blockdomains;
|
||||
protected bool $disposable_domains;
|
||||
protected bool $free_email_provider;
|
||||
protected ?string $creation_date; // date
|
||||
protected ?string $expiration_date; // date
|
||||
protected ?int $return_code;
|
||||
protected bool $disabled;
|
||||
protected ?string $closest_snapshot; // date
|
||||
protected bool $mx_record;
|
||||
protected bool $checked = true;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function init(array $data): void {
|
||||
$this->domain = $data['domain'];
|
||||
$this->blockdomains = $data['blockdomains'];
|
||||
$this->disposable_domains = $data['disposable_domains'];
|
||||
$this->free_email_provider = $data['free_email_provider'];
|
||||
$this->creation_date = $data['creation_date'];
|
||||
$this->expiration_date = $data['expiration_date'];
|
||||
$this->return_code = $data['return_code'];
|
||||
$this->disabled = $data['disabled'];
|
||||
$this->closest_snapshot = $data['closest_snapshot'];
|
||||
$this->mx_record = $data['mx_record'];
|
||||
|
||||
if (!$this->validateDates([$this->creation_date, $this->expiration_date, $this->closest_snapshot])) {
|
||||
throw new \Exception('Validation failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function prepareUpdate(): array {
|
||||
$params = $this->queryParams();
|
||||
unset($params[':domain']);
|
||||
|
||||
$placeholders = array_keys($params);
|
||||
$updateString = $this->updateStringByPlaceholders($placeholders);
|
||||
|
||||
return [$params, $updateString];
|
||||
}
|
||||
|
||||
public function updateEntityInDb(int $entityId, int $apiKey): void {
|
||||
// total_visit and total_account should remain still
|
||||
[$params, $updateString] = $this->prepareUpdate();
|
||||
|
||||
$params['entity_id'] = $entityId;
|
||||
$params['key'] = $apiKey;
|
||||
|
||||
$query = ("
|
||||
UPDATE event_domain
|
||||
SET {$updateString}
|
||||
WHERE
|
||||
event_domain.id = :entity_id AND
|
||||
event_domain.key = :key
|
||||
");
|
||||
|
||||
$model = new \Models\Domain();
|
||||
$model->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?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 Models\Enrichment;
|
||||
|
||||
class Email extends \Models\Enrichment\Base {
|
||||
protected string $email;
|
||||
protected bool $blockemails;
|
||||
protected bool $data_breach;
|
||||
protected int $data_breaches;
|
||||
protected ?string $earliest_breach;
|
||||
protected int $profiles;
|
||||
protected bool $domain_contact_email;
|
||||
protected string $domain;
|
||||
protected ?bool $alert_list;
|
||||
protected bool $checked = true;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function init(array $data): void {
|
||||
$this->email = $data['email'];
|
||||
$this->blockemails = $data['blockemails'];
|
||||
$this->data_breach = $data['data_breach'];
|
||||
$this->data_breaches = $data['data_breaches'];
|
||||
$this->earliest_breach = $data['earliest_breach'];
|
||||
$this->profiles = $data['profiles'];
|
||||
$this->domain_contact_email = $data['domain_contact_email'];
|
||||
$this->domain = $data['domain'];
|
||||
$this->alert_list = $data['alert_list'];
|
||||
|
||||
if (!$this->validateDates([$this->earliest_breach])) {
|
||||
throw new \Exception('Validation failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function prepareUpdate(): array {
|
||||
$params = $this->queryParams();
|
||||
unset($params[':email']);
|
||||
// !
|
||||
unset($params[':domain']);
|
||||
|
||||
// if new alert_list is null -- don't override
|
||||
if ($params[':alert_list'] === null) {
|
||||
unset($params[':alert_list']);
|
||||
}
|
||||
|
||||
$placeholders = array_keys($params);
|
||||
$updateString = $this->updateStringByPlaceholders($placeholders);
|
||||
|
||||
return [$params, $updateString];
|
||||
}
|
||||
|
||||
public function updateEntityInDb(int $entityId, int $apiKey): void {
|
||||
[$params, $updateString] = $this->prepareUpdate();
|
||||
|
||||
$params['entity_id'] = $entityId;
|
||||
$params['key'] = $apiKey;
|
||||
|
||||
// other params will stay still
|
||||
$query = ("
|
||||
UPDATE event_email
|
||||
SET {$updateString}
|
||||
WHERE
|
||||
event_email.id = :entity_id AND
|
||||
event_email.key = :key
|
||||
");
|
||||
|
||||
$model = new \Models\Device();
|
||||
$model->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?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 Models\Enrichment;
|
||||
|
||||
class Ip extends \Models\Enrichment\Base {
|
||||
protected string $ip; // ipvanyaddress
|
||||
protected string $country;
|
||||
protected ?int $asn;
|
||||
protected ?string $name;
|
||||
protected bool $hosting;
|
||||
protected bool $vpn;
|
||||
protected bool $tor;
|
||||
protected bool $relay;
|
||||
protected bool $starlink;
|
||||
protected ?string $description;
|
||||
protected bool $blocklist;
|
||||
protected array $domains_count; // list[str]
|
||||
protected string $cidr; // ipvanynetwork
|
||||
protected ?bool $alert_list;
|
||||
protected bool $checked = true;
|
||||
protected ?int $isp;
|
||||
|
||||
public function __construct() {
|
||||
// empty
|
||||
}
|
||||
|
||||
public function init(array $data): void {
|
||||
$this->ip = $data['ip'];
|
||||
$this->country = $data['country'];
|
||||
$this->asn = $data['asn'];
|
||||
$this->name = $data['name'];
|
||||
$this->hosting = $data['hosting'];
|
||||
$this->vpn = $data['vpn'];
|
||||
$this->tor = $data['tor'];
|
||||
$this->relay = $data['relay'];
|
||||
$this->starlink = $data['starlink'];
|
||||
$this->description = $data['description'];
|
||||
$this->blocklist = $data['blocklist'];
|
||||
$this->domains_count = $data['domains_count'];
|
||||
$this->cidr = $data['cidr'];
|
||||
$this->alert_list = $data['alert_list'];
|
||||
|
||||
if (!$this->validateIP($this->ip) || !$this->validateCIDR($this->cidr)) {
|
||||
throw new \Exception('Validation failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function prepareUpdate(): array {
|
||||
$params = $this->queryParams();
|
||||
unset($params[':ip']);
|
||||
|
||||
$params[':domains_count'] = json_encode($params[':domains_count']);
|
||||
$params[':data_center'] = $params[':hosting'];
|
||||
unset($params[':hosting']);
|
||||
|
||||
// if new alert_list is null -- don't override
|
||||
if ($params[':alert_list'] === null) {
|
||||
unset($params[':alert_list']);
|
||||
}
|
||||
|
||||
// set $params[':isp'] later
|
||||
|
||||
unset($params[':asn']);
|
||||
unset($params[':name']);
|
||||
unset($params[':description']);
|
||||
$placeholders = array_keys($params);
|
||||
$updateString = $this->updateStringByPlaceholders($placeholders);
|
||||
|
||||
return [$params, $updateString];
|
||||
}
|
||||
|
||||
// TODO: update countries table counters
|
||||
public function updateEntityInDb(int $entityId, int $apiKey): void {
|
||||
$ipModel = new \Models\Ip();
|
||||
|
||||
$previousIpData = $ipModel->getFullIpInfoById($entityId);
|
||||
$previousIspId = count($previousIpData) ? $previousIpData['ispid'] : null;
|
||||
$previousCountryId = count($previousIpData) ? $previousIpData['country_id'] : 0;
|
||||
// get current isp id
|
||||
$this->name = $this->asn !== null ? $this->name : 'N/A';
|
||||
$this->asn = $this->asn !== null ? $this->asn : 64496;
|
||||
$ispModel = new \Models\Isp();
|
||||
$newIspId = $ispModel->getIdByAsn($this->asn, $apiKey);
|
||||
|
||||
$newIspData = [
|
||||
'asn' => $this->asn,
|
||||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
];
|
||||
$newIspModel = new \Models\Enrichment\Isp();
|
||||
$newIspModel->init($newIspData);
|
||||
|
||||
// new isp is not in db
|
||||
if ($newIspId === null) {
|
||||
$newIspData['lastseen'] = $previousIpData['lastseen'];
|
||||
$newIspData['created'] = $previousIpData['created'];
|
||||
$newIspId = $ispModel->insertRecord($newIspData, $apiKey);
|
||||
} else {
|
||||
$newIspModel->updateEntityInDb($newIspId, $apiKey);
|
||||
}
|
||||
|
||||
$this->isp = $newIspId;
|
||||
|
||||
$countryModel = new \Models\Country();
|
||||
$newCountryId = $countryModel->getCountryIdByIso($this->country);
|
||||
|
||||
$countryRecord = $countryModel->getCountryById($newCountryId, $apiKey);
|
||||
if (!count($countryRecord)) {
|
||||
$newCountryData = [
|
||||
'id' => $newCountryId,
|
||||
'lastseen' => $previousIpData['lastseen'],
|
||||
'created' => $previousIpData['created'],
|
||||
];
|
||||
$countryModel->insertRecord($newCountryData, $apiKey);
|
||||
}
|
||||
|
||||
// total_visit and total_account should remain still
|
||||
[$params, $updateString] = $this->prepareUpdate();
|
||||
|
||||
$params[':country'] = $newCountryId;
|
||||
$params[':entity_id'] = $entityId;
|
||||
$params[':key'] = $apiKey;
|
||||
|
||||
$query = ("
|
||||
UPDATE event_ip
|
||||
SET {$updateString}
|
||||
WHERE
|
||||
event_ip.id = :entity_id AND
|
||||
event_ip.key = :key
|
||||
");
|
||||
|
||||
$model = new \Models\Ip();
|
||||
$model->execQuery($query, $params);
|
||||
|
||||
// update totals only after event_ip update!
|
||||
$ispIds = $this->slimIds([$previousIspId, $newIspId]);
|
||||
$ispModel->updateTotalsByEntityIds($ispIds, $apiKey, true);
|
||||
|
||||
$countryIds = $this->slimIds([$previousCountryId, $newCountryId]);
|
||||
$countryModel->updateTotalsByEntityIds($countryIds, $apiKey, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?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 Models\Enrichment;
|
||||
|
||||
class Isp extends \Models\Enrichment\Base {
|
||||
protected ?int $asn;
|
||||
protected ?string $name;
|
||||
protected ?string $description;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function init(array $data): void {
|
||||
$this->asn = $data['asn'];
|
||||
$this->name = $data['name'];
|
||||
$this->description = $data['description'];
|
||||
}
|
||||
|
||||
public function prepareUpdate(): array {
|
||||
$params = $this->queryParams();
|
||||
unset($params[':asn']);
|
||||
|
||||
$placeholders = array_keys($params);
|
||||
$updateString = $this->updateStringByPlaceholders($placeholders);
|
||||
|
||||
return [$params, $updateString];
|
||||
}
|
||||
|
||||
public function updateEntityInDb(int $entityId, int $apiKey): void {
|
||||
[$params, $updateString] = $this->prepareUpdate();
|
||||
|
||||
$params[':entity_id'] = $entityId;
|
||||
$params[':key'] = $apiKey;
|
||||
|
||||
$query = ("
|
||||
UPDATE event_isp
|
||||
SET {$updateString}
|
||||
WHERE
|
||||
event_isp.id = :entity_id AND
|
||||
event_isp.key = :key
|
||||
");
|
||||
|
||||
$model = new \Models\Isp();
|
||||
$model->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?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 Models\Enrichment;
|
||||
|
||||
class LocalhostIp extends \Models\Enrichment\Base {
|
||||
protected string $ip; // ipvanyaddress
|
||||
protected int $country = 0;
|
||||
protected ?int $asn = 0;
|
||||
protected string $name = 'Local area network';
|
||||
protected ?string $description = null;
|
||||
protected bool $checked = true;
|
||||
protected ?int $isp;
|
||||
|
||||
public function __construct() {
|
||||
// empty
|
||||
}
|
||||
|
||||
public function init(array $data): void {
|
||||
$this->ip = $data['value'];
|
||||
|
||||
if (!$this->validateIP($this->ip) || $data['error'] !== \Utils\Constants::get('ENRICHMENT_IP_IS_BOGON')) {
|
||||
throw new \Exception('Validation failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function prepareUpdate(): array {
|
||||
$params = $this->queryParams();
|
||||
unset($params[':ip']);
|
||||
|
||||
// set $params[':isp'] later
|
||||
unset($params[':asn']);
|
||||
unset($params[':name']);
|
||||
unset($params[':description']);
|
||||
$placeholders = array_keys($params);
|
||||
$updateString = $this->updateStringByPlaceholders($placeholders);
|
||||
|
||||
return [$params, $updateString];
|
||||
}
|
||||
|
||||
// TODO: update countries table counters
|
||||
public function updateEntityInDb(int $entityId, int $apiKey): void {
|
||||
$ipModel = new \Models\Ip();
|
||||
|
||||
$previousIpData = $ipModel->getFullIpInfoById($entityId);
|
||||
$previousIspId = count($previousIpData) ? $previousIpData['ispid'] : null;
|
||||
$previousCountryId = count($previousIpData) ? $previousIpData['country_id'] : 0;
|
||||
// get current isp id
|
||||
$ispModel = new \Models\Isp();
|
||||
$newIspId = $ispModel->getIdByAsn($this->asn, $apiKey);
|
||||
|
||||
$newIspData = [
|
||||
'asn' => $this->asn,
|
||||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
];
|
||||
$newIspModel = new \Models\Enrichment\Isp();
|
||||
$newIspModel->init($newIspData);
|
||||
|
||||
// new isp is not in db
|
||||
if ($newIspId === null) {
|
||||
$newIspData['lastseen'] = $previousIpData['lastseen'];
|
||||
$newIspId = $ispModel->insertRecord($newIspData, $apiKey);
|
||||
} else {
|
||||
$newIspModel->updateEntityInDb($newIspId, $apiKey);
|
||||
}
|
||||
|
||||
$this->isp = $newIspId;
|
||||
|
||||
$countryModel = new \Models\Country();
|
||||
$newCountryId = $countryModel->getCountryIdByIso($this->country);
|
||||
|
||||
$countryRecord = $countryModel->getCountryById($newCountryId, $apiKey);
|
||||
if (!count($countryRecord)) {
|
||||
$newCountryData = [
|
||||
'id' => $newCountryId,
|
||||
'lastseen' => $previousIpData['lastseen'],
|
||||
];
|
||||
$countryModel->insertRecord($newCountryData, $apiKey);
|
||||
}
|
||||
|
||||
// total_visit and total_account should remain still
|
||||
[$params, $updateString] = $this->prepareUpdate();
|
||||
|
||||
$params[':country'] = $newCountryId;
|
||||
$params[':entity_id'] = $entityId;
|
||||
$params[':key'] = $apiKey;
|
||||
|
||||
$query = ("
|
||||
UPDATE event_ip
|
||||
SET {$updateString}
|
||||
WHERE
|
||||
event_ip.id = :entity_id AND
|
||||
event_ip.key = :key
|
||||
");
|
||||
|
||||
$model = new \Models\Ip();
|
||||
$model->execQuery($query, $params);
|
||||
|
||||
// update totals only after event_ip update!
|
||||
$ispIds = $this->slimIds([$previousIspId, $newIspId]);
|
||||
$ispModel->updateTotalsByEntityIds($ispIds, $apiKey, true);
|
||||
|
||||
$countryIds = $this->slimIds([$previousCountryId, $newCountryId]);
|
||||
$countryModel->updateTotalsByEntityIds($countryIds, $apiKey, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?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 Models\Enrichment;
|
||||
|
||||
class PhoneInvalid extends \Models\Enrichment\Base {
|
||||
protected string $phone_number;
|
||||
protected bool $invalid;
|
||||
protected string $validation_errors;
|
||||
//protected ?bool $alert_list = null;
|
||||
protected bool $checked = true;
|
||||
protected int $country_code;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function init(array $data): void {
|
||||
$this->phone_number = $data['phone_number'];
|
||||
$this->invalid = $data['invalid'];
|
||||
$this->validation_errors = $data['validation_error'];
|
||||
$this->country_code = 0;
|
||||
|
||||
if (!$this->invalid) {
|
||||
throw new \Exception('Validation failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function prepareUpdate(): array {
|
||||
$params = $this->queryParams();
|
||||
unset($params[':phone_number']);
|
||||
|
||||
$params[':validation_errors'] = json_encode($params[':validation_errors']);
|
||||
|
||||
$placeholders = array_keys($params);
|
||||
$updateString = $this->updateStringByPlaceholders($placeholders);
|
||||
|
||||
return [$params, $updateString];
|
||||
}
|
||||
|
||||
public function updateEntityInDb(int $entityId, int $apiKey): void {
|
||||
[$params, $updateString] = $this->prepareUpdate();
|
||||
|
||||
$params['entity_id'] = $entityId;
|
||||
$params['key'] = $apiKey;
|
||||
|
||||
// other params will stay still
|
||||
$query = ("
|
||||
UPDATE event_phone
|
||||
SET {$updateString}
|
||||
WHERE
|
||||
event_phone.id = :entity_id AND
|
||||
event_phone.key = :key
|
||||
");
|
||||
|
||||
$model = new \Models\Phone();
|
||||
$model->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?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 Models\Enrichment;
|
||||
|
||||
class PhoneValid extends \Models\Enrichment\Base {
|
||||
protected string $phone_number;
|
||||
protected int $profiles;
|
||||
protected ?string $iso_country_code;
|
||||
protected int $calling_country_code;
|
||||
protected string $national_format;
|
||||
protected bool $invalid;
|
||||
protected ?string $validation_errors;
|
||||
protected ?string $carrier_name;
|
||||
protected string $type;
|
||||
protected ?bool $alert_list;
|
||||
protected bool $checked = true;
|
||||
protected int $country_code;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function init(array $data): void {
|
||||
$this->phone_number = $data['phone_number'];
|
||||
$this->profiles = $data['profiles'];
|
||||
$this->iso_country_code = $data['iso_country_code'];
|
||||
$this->calling_country_code = $data['calling_country_code'];
|
||||
$this->national_format = $data['national_format'];
|
||||
$this->invalid = $data['invalid'];
|
||||
$this->validation_errors = $data['validation_error'];
|
||||
$this->carrier_name = $data['carrier_name'];
|
||||
$this->type = $data['type'];
|
||||
$this->alert_list = $data['alert_list'];
|
||||
|
||||
if ($this->invalid || $this->validation_errors !== null) {
|
||||
throw new \Exception('Validation failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function prepareUpdate(): array {
|
||||
$params = $this->queryParams();
|
||||
unset($params[':phone_number']);
|
||||
|
||||
$params[':validation_errors'] = json_encode($params[':validation_errors']);
|
||||
|
||||
// if new alert_list is null -- don't override
|
||||
if ($params[':alert_list'] === null) {
|
||||
unset($params[':alert_list']);
|
||||
}
|
||||
|
||||
$placeholders = array_keys($params);
|
||||
$updateString = $this->updateStringByPlaceholders($placeholders);
|
||||
|
||||
return [$params, $updateString];
|
||||
}
|
||||
|
||||
public function updateEntityInDb(int $entityId, int $apiKey): void {
|
||||
$this->country_code = 0;
|
||||
|
||||
if ($this->iso_country_code !== null) {
|
||||
$countryModel = new \Models\Country();
|
||||
$this->country_code = $countryModel->getCountryIdByIso($this->iso_country_code);
|
||||
}
|
||||
|
||||
[$params, $updateString] = $this->prepareUpdate();
|
||||
|
||||
$params['entity_id'] = $entityId;
|
||||
$params['key'] = $apiKey;
|
||||
|
||||
$query = ("
|
||||
UPDATE event_phone
|
||||
SET {$updateString}
|
||||
WHERE
|
||||
event_phone.id = :entity_id AND
|
||||
event_phone.key = :key
|
||||
");
|
||||
|
||||
$model = new \Models\Phone();
|
||||
$model->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,235 @@
|
||||
<?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 Models;
|
||||
|
||||
class Event extends \Models\BaseSql {
|
||||
use \Traits\Enrichment\Ips;
|
||||
use \Traits\Enrichment\Emails;
|
||||
use \Traits\Enrichment\TimeZones;
|
||||
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getLastEvent(int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event.id,
|
||||
event.time
|
||||
FROM
|
||||
event
|
||||
WHERE
|
||||
event.key = :api_key
|
||||
|
||||
ORDER BY id DESC
|
||||
LIMIT 1 OFFSET 0'
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function getEventDetails(int $id, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':id' => $id,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event.id,
|
||||
event.time AS event_time,
|
||||
event.http_code AS event_http_code,
|
||||
event.type AS event_type_id,
|
||||
|
||||
event_ip.ip,
|
||||
event_ip.cidr,
|
||||
event_ip.data_center,
|
||||
event_ip.vpn,
|
||||
event_ip.tor,
|
||||
event_ip.relay,
|
||||
event_ip.starlink,
|
||||
event_ip.id AS ipId,
|
||||
event_ip.blocklist AS blocklist,
|
||||
event_ip.alert_list AS ip_alert_list,
|
||||
event_ip.shared AS ip_users,
|
||||
event_ip.total_visit AS ip_events,
|
||||
event_ip.checked,
|
||||
event_ip.fraud_detected AS fraud_detected,
|
||||
|
||||
event_isp.asn,
|
||||
event_isp.description,
|
||||
event_isp.id AS ispId,
|
||||
event_isp.name AS netname,
|
||||
|
||||
event_url.url,
|
||||
event_url.title,
|
||||
event_url_query.query,
|
||||
event_url.id AS url_id,
|
||||
|
||||
event_referer.referer,
|
||||
|
||||
event_account.score,
|
||||
event_account.score_updated_at,
|
||||
event_account.fraud,
|
||||
event_account.reviewed,
|
||||
event_account.added_to_review,
|
||||
event_account.firstname,
|
||||
event_account.lastname,
|
||||
event_account.is_important,
|
||||
event_account.latest_decision,
|
||||
event_account.id AS accountid,
|
||||
event_account.userid AS accounttitle,
|
||||
|
||||
event_phone.phone_number AS phonenumber,
|
||||
event_phone.type AS phone_type,
|
||||
event_phone.carrier_name,
|
||||
event_phone.profiles AS phone_profiles,
|
||||
event_phone.invalid AS phone_invalid,
|
||||
event_phone.shared AS phone_users,
|
||||
event_phone.alert_list AS phone_alert_list,
|
||||
event_phone.fraud_detected AS phone_fraud_detected,
|
||||
phone_countries.id AS phone_country_id,
|
||||
phone_countries.iso AS phone_country_iso,
|
||||
phone_countries.value AS phone_full_country,
|
||||
|
||||
event_email.email,
|
||||
event_email.profiles,
|
||||
event_email.data_breach,
|
||||
event_email.data_breaches,
|
||||
event_email.earliest_breach AS email_earliest_breach,
|
||||
event_email.blockemails AS blockemails,
|
||||
event_email.alert_list AS email_alert_list,
|
||||
event_email.fraud_detected AS email_fraud_detected,
|
||||
|
||||
current_email.email AS current_email,
|
||||
|
||||
event_domain.id AS domainId,
|
||||
event_domain.domain,
|
||||
event_domain.tranco_rank,
|
||||
event_domain.blockdomains,
|
||||
event_domain.disposable_domains,
|
||||
event_domain.free_email_provider,
|
||||
event_domain.creation_date AS domain_creation_date,
|
||||
event_domain.disabled AS domain_disabled,
|
||||
event_domain.expiration_date AS domain_expiration_date,
|
||||
event_domain.return_code AS domain_return_code,
|
||||
|
||||
ip_countries.id AS country_id,
|
||||
ip_countries.id AS ip_country_id,
|
||||
ip_countries.iso AS ip_country_iso,
|
||||
ip_countries.value AS ip_full_country,
|
||||
|
||||
event_device.lang,
|
||||
event_device.user_agent AS deviceId,
|
||||
event_device.created AS device_created,
|
||||
|
||||
event_ua_parsed.ua,
|
||||
event_ua_parsed.os_name,
|
||||
event_ua_parsed.os_version,
|
||||
event_ua_parsed.browser_name,
|
||||
event_ua_parsed.browser_version,
|
||||
event_ua_parsed.device AS device_name,
|
||||
event_ua_parsed.modified AS ua_modified,
|
||||
|
||||
event_type.name AS event_type_name,
|
||||
event_type.value AS event_type,
|
||||
|
||||
event_payload.payload AS event_payload,
|
||||
|
||||
event_http_method.name AS event_http_method_name
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_account
|
||||
ON (event.account = event_account.id)
|
||||
|
||||
INNER JOIN event_url
|
||||
ON (event.url = event_url.id)
|
||||
|
||||
LEFT JOIN event_referer
|
||||
ON (event.referer = event_referer.id)
|
||||
|
||||
FULL OUTER JOIN event_url_query
|
||||
ON (event.query = event_url_query.id)
|
||||
|
||||
INNER JOIN event_device
|
||||
ON (event.device = event_device.id)
|
||||
|
||||
INNER JOIN event_type
|
||||
ON (event.type = event_type.id)
|
||||
|
||||
INNER JOIN event_ua_parsed
|
||||
ON (event_device.user_agent = event_ua_parsed.id)
|
||||
|
||||
INNER JOIN event_ip
|
||||
ON (event.ip = event_ip.id)
|
||||
|
||||
LEFT JOIN event_isp
|
||||
ON (event_ip.isp = event_isp.id)
|
||||
|
||||
INNER JOIN countries AS ip_countries
|
||||
ON (event_ip.country = ip_countries.id)
|
||||
|
||||
LEFT JOIN event_email
|
||||
ON (event.email = event_email.id)
|
||||
|
||||
LEFT JOIN event_email AS current_email
|
||||
ON (event_account.lastemail = current_email.id)
|
||||
|
||||
LEFT JOIN event_domain
|
||||
ON (event_email.domain = event_domain.id)
|
||||
|
||||
LEFT JOIN event_phone
|
||||
ON (event_account.lastphone = event_phone.id)
|
||||
|
||||
LEFT JOIN countries AS phone_countries
|
||||
ON (phone_countries.id = event_phone.country_code)
|
||||
|
||||
LEFT JOIN event_http_method
|
||||
ON (event.http_method = event_http_method.id)
|
||||
|
||||
LEFT JOIN event_payload
|
||||
ON (event.payload = event_payload.id)
|
||||
|
||||
WHERE
|
||||
event.key = :api_key
|
||||
AND event.id = :id
|
||||
LIMIT 1'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
$this->calculateIpType($results);
|
||||
$this->calculateEmailReputation($results);
|
||||
//$this->translateTimeZones($results, ['event_time', 'domain_creation_date']);
|
||||
|
||||
if (count($results)) {
|
||||
$results = $results[0];
|
||||
|
||||
$spamlist = $results['ip_type'] === 'Spam list';
|
||||
$results['spamlist'] = $spamlist;
|
||||
|
||||
$model = new \Models\User();
|
||||
$results['score_details'] = $model->getApplicableRulesByAccountId($results['accountid'], $apiKey, true);
|
||||
$results['score_calculated'] = $results['score'] !== null;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Models;
|
||||
|
||||
class EventType extends \Models\BaseSql {
|
||||
protected $DB_TABLE_NAME = 'event_type';
|
||||
|
||||
public function getAll(): array {
|
||||
$query = 'SELECT id, value, name FROM event_type';
|
||||
|
||||
return $this->execQuery($query, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
<?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 Models;
|
||||
|
||||
class Events extends \Models\BaseSql {
|
||||
use \Traits\DateRange;
|
||||
use \Traits\Enrichment\Ips;
|
||||
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
public function getDistinctAccounts(int $after, int $to): array {
|
||||
$params = [
|
||||
':cursor' => $after,
|
||||
':next_cursor' => $to,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT DISTINCT
|
||||
event.account AS "accountId",
|
||||
event.key
|
||||
FROM
|
||||
event
|
||||
JOIN
|
||||
event_account ON event.account = event_account.id
|
||||
WHERE
|
||||
event.id > :cursor
|
||||
AND event.id <= :next_cursor'
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
private function getEvents(array $queryParams): array {
|
||||
$request = $this->f3->get('REQUEST');
|
||||
$dateRange = $this->getDatesRange($request);
|
||||
$data = $this->getData($queryParams, $dateRange);
|
||||
$total = $this->getTotal($queryParams, $dateRange);
|
||||
|
||||
return [
|
||||
'draw' => $request['draw'] ?? 1,
|
||||
'recordsTotal' => $total,
|
||||
'recordsFiltered' => $total,
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
public function getEventsByUser(int $userId, int $apiKey): array {
|
||||
$params = [
|
||||
':user_id' => $userId,
|
||||
':api_key' => $apiKey,
|
||||
];
|
||||
|
||||
return $this->getEvents($params);
|
||||
}
|
||||
|
||||
private function getData(array $params, ?array $dateRange): array {
|
||||
$query = (
|
||||
'SELECT
|
||||
event.id,
|
||||
event.time,
|
||||
event.http_code,
|
||||
|
||||
event_ip.ip,
|
||||
event_ip.data_center,
|
||||
-- event_ip.asn,
|
||||
event_ip.vpn,
|
||||
event_ip.tor,
|
||||
event_ip.relay,
|
||||
event_ip.starlink,
|
||||
event_ip.id AS ipId,
|
||||
-- event_ip.description,
|
||||
-- event_ip.name as netname,
|
||||
event_ip.blocklist,
|
||||
event_ip.fraud_detected,
|
||||
event_ip.checked,
|
||||
|
||||
event_isp.asn,
|
||||
event_isp.description,
|
||||
event_isp.name AS netname,
|
||||
|
||||
event_url.url,
|
||||
event_url.id as url_id,
|
||||
event_url_query.query,
|
||||
event_url.title,
|
||||
event_referer.referer,
|
||||
-- event_account.is_important,
|
||||
event_account.id AS accountid,
|
||||
event_account.userid AS accounttitle,
|
||||
event_account.score,
|
||||
|
||||
countries.id AS country_id,
|
||||
countries.iso AS country_iso,
|
||||
countries.value AS full_country,
|
||||
event_device.id AS deviceId,
|
||||
event_ua_parsed.device,
|
||||
event_ua_parsed.ua,
|
||||
event_ua_parsed.browser_name,
|
||||
event_ua_parsed.browser_version,
|
||||
event_ua_parsed.os_name,
|
||||
event_ua_parsed.os_version,
|
||||
event_ua_parsed.ua,
|
||||
event_type.value AS event_type,
|
||||
event_type.name AS event_type_name
|
||||
FROM
|
||||
event
|
||||
INNER JOIN event_account
|
||||
ON (event.account = event_account.id)
|
||||
|
||||
INNER JOIN event_url
|
||||
ON (event.url = event_url.id)
|
||||
LEFT JOIN event_referer
|
||||
ON (event.referer = event_referer.id)
|
||||
-- FULL OUTER JOIN event_url_query
|
||||
LEFT JOIN event_url_query
|
||||
ON (event.query = event_url_query.id)
|
||||
INNER JOIN event_device
|
||||
ON (event.device = event_device.id)
|
||||
INNER JOIN event_type
|
||||
ON (event.type = event_type.id)
|
||||
INNER JOIN event_ua_parsed
|
||||
ON (event_device.user_agent = event_ua_parsed.id)
|
||||
INNER JOIN event_ip
|
||||
ON (event.ip = event_ip.id)
|
||||
LEFT JOIN event_isp
|
||||
ON (event_ip.isp = event_isp.id)
|
||||
INNER JOIN countries
|
||||
ON (event_ip.country = countries.id)
|
||||
|
||||
WHERE
|
||||
event.account = :user_id AND
|
||||
event.key = :api_key
|
||||
%s
|
||||
|
||||
ORDER BY
|
||||
event.time DESC'
|
||||
);
|
||||
|
||||
$this->applyDateRange($query, $params, $dateRange);
|
||||
$this->applyLimit($query, $params);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
$this->calculateIpType($results);
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function getTotal(array $params, ?array $dateRange): int {
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(event.id)
|
||||
|
||||
FROM
|
||||
event
|
||||
INNER JOIN event_account
|
||||
ON (event.account = event_account.id)
|
||||
|
||||
INNER JOIN event_url
|
||||
ON (event.url = event_url.id)
|
||||
FULL OUTER JOIN event_url_query
|
||||
ON (event.query = event_url_query.id)
|
||||
INNER JOIN event_device
|
||||
ON (event.device = event_device.id)
|
||||
INNER JOIN event_ua_parsed
|
||||
ON (event_device.user_agent = event_ua_parsed.id)
|
||||
INNER JOIN event_ip
|
||||
ON (event.ip = event_ip.id)
|
||||
INNER JOIN countries
|
||||
ON (event_ip.country = countries.id)
|
||||
|
||||
WHERE
|
||||
event.account = :user_id AND
|
||||
event.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applyDateRange($query, $params, $dateRange);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0]['count'];
|
||||
}
|
||||
|
||||
private function applyDateRange(string &$query, array &$params, ?array $dateRange = null): void {
|
||||
$searchConditions = '';
|
||||
if ($dateRange) {
|
||||
$searchConditions = (
|
||||
'AND event.time >= :start_time
|
||||
AND event.time <= :end_time'
|
||||
);
|
||||
|
||||
$params[':end_time'] = $dateRange['endDate'];
|
||||
$params[':start_time'] = $dateRange['startDate'];
|
||||
}
|
||||
|
||||
$query = sprintf($query, $searchConditions);
|
||||
}
|
||||
|
||||
protected function applyLimit(string &$query, array &$queryParams): void {
|
||||
$request = $this->f3->get('REQUEST');
|
||||
|
||||
$start = $request['start'] ?? null;
|
||||
$length = $request['length'] ?? null;
|
||||
|
||||
if (isset($start) && isset($length)) {
|
||||
$query .= ' LIMIT :length OFFSET :start';
|
||||
|
||||
$queryParams[':start'] = $start;
|
||||
$queryParams[':length'] = $length;
|
||||
}
|
||||
}
|
||||
|
||||
public function uniqueEntitesByUserId(int $userId, int $apiKey): array {
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':user_id' => $userId,
|
||||
];
|
||||
$query = (
|
||||
'SELECT DISTINCT
|
||||
event.ip,
|
||||
event_ip.isp,
|
||||
event_ip.country,
|
||||
event.url,
|
||||
event_email.domain,
|
||||
event_phone.phone_number
|
||||
FROM
|
||||
event
|
||||
LEFT JOIN event_ip
|
||||
ON event.ip = event_ip.id
|
||||
LEFT JOIN event_email
|
||||
ON event.email = event_email.id
|
||||
LEFT JOIN event_phone
|
||||
ON event.phone = event_phone.id
|
||||
LEFT JOIN event_country
|
||||
ON event_ip.country = event_country.country AND event_ip.key = event_country.key
|
||||
|
||||
WHERE
|
||||
event.key = :api_key AND
|
||||
event.account = :user_id'
|
||||
);
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return [
|
||||
'ip_ids' => array_unique(array_column($results, 'ip')),
|
||||
'isp_ids' => array_unique(array_column($results, 'isp')),
|
||||
'country_ids' => array_unique(array_column($results, 'country')),
|
||||
'url_ids' => array_unique(array_column($results, 'url')),
|
||||
'domain_ids' => array_unique(array_column($results, 'domain')),
|
||||
'phone_numbers' => array_unique(array_column($results, 'phone_number')),
|
||||
];
|
||||
}
|
||||
|
||||
public function retentionDeletion(int $weeks, int $apiKey): int {
|
||||
// insuring clause
|
||||
if ($weeks < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$params = [
|
||||
':api_key' => $apiKey,
|
||||
':weeks' => $weeks,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'DELETE FROM event
|
||||
WHERE
|
||||
event.key = :api_key AND
|
||||
(EXTRACT(EPOCH FROM (NOW() - event.time)) / (60 * 60 * 24 * 7)) >= :weeks'
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?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 Models;
|
||||
|
||||
class FieldAuditTrail extends \Models\BaseSql {
|
||||
protected $DB_TABLE_NAME = 'event_field_audit_trail';
|
||||
|
||||
public function getByEventId(int $eventId, int $apiKey): array {
|
||||
$params = [
|
||||
':event_id' => $eventId,
|
||||
':api_key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_field_audit_trail.field_id,
|
||||
event_field_audit_trail.field_name,
|
||||
event_field_audit_trail.old_value,
|
||||
event_field_audit_trail.new_value,
|
||||
event_field_audit_trail.parent_id,
|
||||
event_field_audit_trail.parent_name
|
||||
FROM
|
||||
event_field_audit_trail
|
||||
WHERE
|
||||
event_field_audit_trail.event_id = :event_id AND
|
||||
event_field_audit_trail.key = :api_key
|
||||
|
||||
ORDER BY id DESC'
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
|
||||
public function getByUserId(int $userId, int $apiKey): array {
|
||||
$params = [
|
||||
':user_id' => $userId,
|
||||
':api_key' => $apiKey,
|
||||
];
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_field_audit_trail.field_id,
|
||||
event_field_audit_trail.field_name,
|
||||
event_field_audit_trail.old_value,
|
||||
event_field_audit_trail.new_value,
|
||||
event_field_audit_trail.parent_id,
|
||||
event_field_audit_trail.parent_name
|
||||
FROM
|
||||
event_field_audit_trail
|
||||
WHERE
|
||||
event_field_audit_trail.account_id = :user_id AND
|
||||
event_field_audit_trail.key = :api_key
|
||||
|
||||
ORDER BY id DESC'
|
||||
);
|
||||
|
||||
return $this->execQuery($query, $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?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 Models;
|
||||
|
||||
class ForgotPassword extends \Models\BaseSql {
|
||||
protected $DB_TABLE_NAME = 'dshb_operators_forgot_password';
|
||||
|
||||
public function add(int $operatorId): void {
|
||||
$record = $this->getUnusedKeyByOperatorId($operatorId);
|
||||
|
||||
if ($record) {
|
||||
$this->status = 'invalidated';
|
||||
$this->save();
|
||||
}
|
||||
|
||||
$this->reset();
|
||||
$this->renew_key = $this->getPseudoRandomString(32);
|
||||
$this->operator_id = $operatorId;
|
||||
$this->status = 'unused';
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
private function getUnusedKeyByOperatorId(int $operatorId): self|null|false {
|
||||
return $this->load(
|
||||
['"operator_id"=? AND "status"=?', $operatorId, 'unused'],
|
||||
);
|
||||
}
|
||||
|
||||
public function getUnusedByRenewKey(string $key): self|null|false {
|
||||
return $this->load(
|
||||
['"renew_key"=? AND "status"=?', $key, 'unused'],
|
||||
);
|
||||
}
|
||||
|
||||
public function deactivate(): void {
|
||||
if ($this->loaded()) {
|
||||
$this->status = 'used';
|
||||
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?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 Models\Grid\Base;
|
||||
|
||||
class Grid extends \Models\BaseSql {
|
||||
use \Traits\Enrichment\TimeZones;
|
||||
use \Traits\DateRange;
|
||||
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
protected $idsModel = null;
|
||||
protected $apiKey = null;
|
||||
protected $query = null;
|
||||
|
||||
protected function getGrid(?string $ids = null, array $idsParams = []): array {
|
||||
$this->setIds($ids, $idsParams);
|
||||
|
||||
$draw = $this->f3->get('REQUEST.draw');
|
||||
|
||||
$draw = $draw ?? 1;
|
||||
$data = $this->getData();
|
||||
$total = $this->getTotal();
|
||||
|
||||
$params = $this->f3->get('GET');
|
||||
$dateRange = $this->getDatesRange($params);
|
||||
|
||||
return [
|
||||
'data' => $data,
|
||||
'draw' => $draw,
|
||||
'recordsTotal' => $total,
|
||||
'recordsFiltered' => $total,
|
||||
'dateRange' => $dateRange,
|
||||
];
|
||||
}
|
||||
|
||||
public function setIds(?string $ids, array $idsParams): void {
|
||||
$this->query->setIds($ids, $idsParams);
|
||||
}
|
||||
|
||||
protected function getData(): array {
|
||||
[$query, $params] = $this->query->getData();
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
$this->convertTimeToUserTimezone($results);
|
||||
$this->calculateCustomParams($results);
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
protected function getTotal(): int {
|
||||
[$query, $params] = $this->query->getTotal();
|
||||
|
||||
$results = $this->execQuery($query, $params);
|
||||
|
||||
return $results[0]['count'];
|
||||
}
|
||||
|
||||
protected function convertTimeToUserTimezone(array &$result): void {
|
||||
$this->translateTimeZones($result);
|
||||
}
|
||||
|
||||
protected function calculateCustomParams(array &$result): void {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?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 Models\Grid\Base;
|
||||
|
||||
class Ids extends \Models\BaseSql {
|
||||
protected $DB_TABLE_NAME = 'event';
|
||||
|
||||
private $apiKey = null;
|
||||
|
||||
public function __construct(int $apiKey) {
|
||||
parent::__construct();
|
||||
|
||||
$this->apiKey = $apiKey;
|
||||
}
|
||||
|
||||
public function execute(string $query, array $params): array {
|
||||
$params[':api_key'] = $this->apiKey;
|
||||
|
||||
$data = $this->execQuery($query, $params);
|
||||
$results = array_column($data, 'itemid');
|
||||
|
||||
return count($results) ? $results : [-1];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?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 Models\Grid\Base;
|
||||
|
||||
class Query {
|
||||
use \Traits\Debug;
|
||||
use \Traits\DateRange;
|
||||
|
||||
protected $f3 = null;
|
||||
protected $apiKey = null;
|
||||
protected $ids = null;
|
||||
protected $idsParams = [];
|
||||
protected $itemKey = null;
|
||||
protected $itemId = null;
|
||||
|
||||
protected $defaultOrder = null;
|
||||
protected $dateRangeField = 'event_country.lastseen';
|
||||
|
||||
protected $allowedColumns = [];
|
||||
|
||||
public function __construct(int $apiKey) {
|
||||
$this->f3 = \Base::instance();
|
||||
$this->apiKey = $apiKey;
|
||||
}
|
||||
|
||||
public function setIds(?string $ids, array $idsParams): void {
|
||||
$this->ids = $ids;
|
||||
$this->idsParams = $idsParams;
|
||||
if (count($this->idsParams)) {
|
||||
$this->itemKey = array_keys($this->idsParams)[0];
|
||||
$this->itemId = $this->idsParams[$this->itemKey];
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyOrder(string &$query): void {
|
||||
$request = $this->f3->get('REQUEST');
|
||||
|
||||
$order = $request['order'] ?? [];
|
||||
$columns = $request['columns'] ?? [];
|
||||
|
||||
$orderCondition = $this->defaultOrder;
|
||||
|
||||
if (count($order) && count($columns)) {
|
||||
$orderClauses = [];
|
||||
foreach ($order as $orderData) {
|
||||
$sortDirection = $orderData['dir'] === 'asc' ? 'ASC' : 'DESC';
|
||||
$columnIndex = $orderData['column'];
|
||||
$sortColumn = $columns[$columnIndex]['data'];
|
||||
if (in_array($sortColumn, $this->allowedColumns)) {
|
||||
$orderClauses[] = sprintf('%s %s', $sortColumn, $sortDirection);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($orderClauses)) {
|
||||
$orderCondition = implode(', ', $orderClauses);
|
||||
}
|
||||
}
|
||||
|
||||
if ($orderCondition) {
|
||||
$query .= sprintf(' ORDER BY %s', $orderCondition);
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyDateRange(string &$query, array &$queryParams): void {
|
||||
$params = $this->f3->get('GET');
|
||||
$dateRange = $this->getDatesRange($params);
|
||||
|
||||
if ($dateRange) {
|
||||
$searchConditions = (
|
||||
"AND {$this->dateRangeField} >= :start_time
|
||||
AND {$this->dateRangeField} <= :end_time
|
||||
%s"
|
||||
);
|
||||
|
||||
$query = sprintf($query, $searchConditions);
|
||||
$queryParams[':end_time'] = $dateRange['endDate'];
|
||||
$queryParams[':start_time'] = $dateRange['startDate'];
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyLimit(string &$query, array &$queryParams): void {
|
||||
$request = $this->f3->get('REQUEST');
|
||||
|
||||
$start = $request['start'] ?? null;
|
||||
$length = $request['length'] ?? null;
|
||||
|
||||
if (isset($start) && isset($length)) {
|
||||
$query .= ' LIMIT :length OFFSET :start';
|
||||
|
||||
$queryParams[':start'] = $start;
|
||||
$queryParams[':length'] = $length;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getQueryParams(): array {
|
||||
return [':api_key' => $this->apiKey];
|
||||
}
|
||||
|
||||
public function injectIdQuery(string $field, &$params): string {
|
||||
$idsQuery = $this->ids;
|
||||
if ($idsQuery === null || $idsQuery === '') {
|
||||
return '';
|
||||
}
|
||||
$idsParams = $this->idsParams;
|
||||
|
||||
foreach ($idsParams as $key => $value) {
|
||||
if (!array_key_exists($key, $params) || $params[$key] === null) {
|
||||
$params[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return " AND $field IN ($idsQuery)";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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 Models\Grid\Blacklist;
|
||||
|
||||
class Grid extends \Models\Grid\Base\Grid {
|
||||
public function __construct(int $apiKey) {
|
||||
parent::__construct();
|
||||
|
||||
$this->apiKey = $apiKey;
|
||||
$this->idsModel = new Ids($apiKey);
|
||||
$this->query = new Query($apiKey);
|
||||
}
|
||||
|
||||
public function getAllBlacklistedItems(): array {
|
||||
return $this->getGrid();
|
||||
}
|
||||
|
||||
protected function convertTimeToUserTimezone(array &$result): void {
|
||||
$fields = ['created', 'score_updated_at'];
|
||||
|
||||
$this->translateTimeZones($result, $fields);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?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 Models\Grid\Blacklist;
|
||||
|
||||
class Ids extends \Models\Grid\Base\Ids {
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
<?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 Models\Grid\Blacklist;
|
||||
|
||||
class Query extends \Models\Grid\Base\Query {
|
||||
protected $defaultOrder = 'created DESC, type ASC, value ASC';
|
||||
protected $dateRangeField = 'blacklist.created';
|
||||
|
||||
protected $allowedColumns = ['score', 'created', 'type', 'value'];
|
||||
|
||||
public function getData(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = ("
|
||||
SELECT DISTINCT
|
||||
blacklist.is_important,
|
||||
blacklist.accountid,
|
||||
blacklist.accounttitle,
|
||||
blacklist.created,
|
||||
blacklist.score_updated_at,
|
||||
blacklist.score,
|
||||
blacklist.account_email AS email,
|
||||
extra.type,
|
||||
TRUE AS fraud,
|
||||
CASE extra.type
|
||||
WHEN 'ip' THEN blacklist.ip
|
||||
WHEN 'email' THEN blacklist.email
|
||||
WHEN 'phone' THEN blacklist.phone
|
||||
END AS value,
|
||||
CASE extra.type
|
||||
WHEN 'ip' THEN blacklist.ip_id
|
||||
WHEN 'email' THEN blacklist.email_id
|
||||
WHEN 'phone' THEN blacklist.phone_id
|
||||
END AS entity_id
|
||||
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
event_account.is_important,
|
||||
event_account.id AS accountid,
|
||||
event_account.userid AS accounttitle,
|
||||
event_account.latest_decision AS created,
|
||||
event_account.score_updated_at,
|
||||
event_account.score,
|
||||
|
||||
account_email.email AS account_email,
|
||||
|
||||
CASE WHEN event_ip.fraud_detected THEN split_part(event_ip.ip::text, '/', 1) ELSE NULL END AS ip,
|
||||
CASE WHEN event_ip.fraud_detected THEN event_ip.id ELSE NULL END AS ip_id,
|
||||
event_ip.fraud_detected AS ip_fraud,
|
||||
|
||||
CASE WHEN event_email.fraud_detected THEN event_email.email ELSE NULL END AS email,
|
||||
CASE WHEN event_email.fraud_detected THEN event_email.id ELSE NULL END AS email_id,
|
||||
event_email.fraud_detected AS email_fraud,
|
||||
|
||||
CASE WHEN event_phone.fraud_detected THEN event_phone.phone_number ELSE NULL END AS phone,
|
||||
CASE WHEN event_phone.fraud_detected THEN event_phone.id ELSE NULL END AS phone_id,
|
||||
event_phone.fraud_detected AS phone_fraud
|
||||
|
||||
FROM event
|
||||
|
||||
LEFT JOIN event_account
|
||||
ON event_account.id = event.account
|
||||
|
||||
LEFT JOIN event_email AS account_email
|
||||
ON event_account.lastemail = account_email.id
|
||||
|
||||
LEFT JOIN event_ip
|
||||
ON event_ip.id = event.ip
|
||||
|
||||
LEFT JOIN event_email
|
||||
ON event_email.id = event.email
|
||||
|
||||
LEFT JOIN event_phone
|
||||
ON event_phone.id = event.phone
|
||||
|
||||
WHERE
|
||||
event_account.key = :api_key AND
|
||||
event_account.fraud IS TRUE AND
|
||||
(
|
||||
event_email.fraud_detected IS TRUE OR
|
||||
event_ip.fraud_detected IS TRUE OR
|
||||
event_phone.fraud_detected IS TRUE
|
||||
)
|
||||
) AS blacklist,
|
||||
LATERAL (
|
||||
VALUES
|
||||
(CASE WHEN ip_fraud = true THEN 'ip' END),
|
||||
(CASE WHEN email_fraud = true THEN 'email' END),
|
||||
(CASE WHEN phone_fraud = true THEN 'phone' END)
|
||||
) AS extra(type)
|
||||
|
||||
WHERE
|
||||
extra.type IS NOT NULL
|
||||
%s
|
||||
");
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
$this->applyEntityTypes($query, $queryParams);
|
||||
$this->applyOrder($query);
|
||||
$this->applyLimit($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
public function getTotal(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = ("
|
||||
SELECT COUNT(*)
|
||||
FROM (
|
||||
SELECT DISTINCT
|
||||
blacklist.accountid,
|
||||
blacklist.accounttitle,
|
||||
blacklist.created,
|
||||
extra.type,
|
||||
CASE extra.type
|
||||
WHEN 'ip' THEN blacklist.ip
|
||||
WHEN 'email' THEN blacklist.email
|
||||
WHEN 'phone' THEN blacklist.phone
|
||||
END AS value
|
||||
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
event_account.id AS accountid,
|
||||
event_account.userid AS accounttitle,
|
||||
event_account.latest_decision AS created,
|
||||
CASE WHEN event_ip.fraud_detected THEN split_part(event_ip.ip::text, '/', 1) ELSE NULL END AS ip,
|
||||
event_ip.fraud_detected AS ip_fraud,
|
||||
CASE WHEN event_email.fraud_detected THEN event_email.email ELSE NULL END AS email,
|
||||
event_email.fraud_detected AS email_fraud,
|
||||
CASE WHEN event_phone.fraud_detected THEN event_phone.phone_number ELSE NULL END AS phone,
|
||||
event_phone.fraud_detected AS phone_fraud
|
||||
FROM event
|
||||
|
||||
LEFT JOIN event_account
|
||||
ON event_account.id = event.account
|
||||
|
||||
LEFT JOIN event_ip
|
||||
ON event_ip.id = event.ip
|
||||
|
||||
LEFT JOIN event_email
|
||||
ON event_email.id = event.email
|
||||
|
||||
LEFT JOIN event_phone
|
||||
ON event_phone.id = event.phone
|
||||
|
||||
WHERE
|
||||
event_account.key = :api_key AND
|
||||
event_account.fraud IS TRUE AND
|
||||
(
|
||||
event_email.fraud_detected IS TRUE OR
|
||||
event_ip.fraud_detected IS TRUE OR
|
||||
event_phone.fraud_detected IS TRUE
|
||||
)
|
||||
) AS blacklist,
|
||||
LATERAL (
|
||||
VALUES
|
||||
(CASE WHEN ip_fraud = true THEN 'ip' END),
|
||||
(CASE WHEN email_fraud = true THEN 'email' END),
|
||||
(CASE WHEN phone_fraud = true THEN 'phone' END)
|
||||
) AS extra(type)
|
||||
|
||||
WHERE
|
||||
extra.type IS NOT NULL
|
||||
%s
|
||||
) AS tbl
|
||||
");
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
$this->applyEntityTypes($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
private function applySearch(string &$query, array &$queryParams): void {
|
||||
$this->applyDateRange($query, $queryParams);
|
||||
|
||||
$searchConditions = '';
|
||||
$search = $this->f3->get('REQUEST.search');
|
||||
|
||||
if (is_array($search) && isset($search['value']) && is_string($search['value']) && $search['value'] !== '') {
|
||||
$searchConditions .= (
|
||||
" AND (
|
||||
LOWER(blacklist.accounttitle) LIKE LOWER(:search_value) OR
|
||||
LOWER(extra.type) LIKE LOWER(:search_value) OR
|
||||
LOWER(CASE extra.type
|
||||
WHEN 'ip' THEN blacklist.ip
|
||||
WHEN 'email' THEN blacklist.email
|
||||
WHEN 'phone' THEN blacklist.phone
|
||||
END) LIKE LOWER(:search_value) OR
|
||||
TO_CHAR(blacklist.created::timestamp without time zone, 'dd/mm/yyyy hh24:mi:ss') LIKE :search_value
|
||||
)"
|
||||
);
|
||||
|
||||
$queryParams[':search_value'] = '%' . $search['value'] . '%';
|
||||
}
|
||||
|
||||
//Add search into request
|
||||
$query = sprintf($query, $searchConditions . ' %s');
|
||||
}
|
||||
|
||||
private function applyEntityTypes(string &$query, array &$queryParams): void {
|
||||
$searchCondition = '';
|
||||
|
||||
$entityTypeIds = $this->f3->get('REQUEST.entityTypeIds');
|
||||
if ($entityTypeIds !== null && count($entityTypeIds)) {
|
||||
$clauses = [];
|
||||
|
||||
foreach ($entityTypeIds as $key => $entityTypeId) {
|
||||
$clauses[] = 'extra.type = :entity_type_' . $key;
|
||||
$queryParams[':entity_type_' . $key] = strtolower(\Utils\Constants::get('ENTITY_TYPES')[$entityTypeId]);
|
||||
}
|
||||
|
||||
$searchCondition = ' AND (' . implode(' OR ', $clauses) . ')';
|
||||
}
|
||||
|
||||
$query = sprintf($query, $searchCondition);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,42 @@
|
||||
<?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 Models\Grid\Bots;
|
||||
|
||||
class Grid extends \Models\Grid\Base\Grid {
|
||||
use \Traits\Enrichment\Devices;
|
||||
|
||||
public function __construct(int $apiKey) {
|
||||
parent::__construct();
|
||||
|
||||
$this->apiKey = $apiKey;
|
||||
$this->idsModel = new Ids($apiKey);
|
||||
$this->query = new Query($apiKey);
|
||||
}
|
||||
|
||||
public function getAllBots(): array {
|
||||
return $this->getGrid();
|
||||
}
|
||||
|
||||
protected function calculateCustomParams(array &$result): void {
|
||||
$this->applyDeviceParams($result);
|
||||
}
|
||||
|
||||
protected function convertTimeToUserTimezone(array &$result): void {
|
||||
$fields = ['lastseen'];
|
||||
|
||||
$this->translateTimeZones($result, $fields);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?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 Models\Grid\Bots;
|
||||
|
||||
class Ids extends \Models\Grid\Base\Ids {
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?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 Models\Grid\Bots;
|
||||
|
||||
class Query extends \Models\Grid\Base\Query {
|
||||
protected $defaultOrder = 'ed.lastseen DESC';
|
||||
protected $dateRangeField = 'ed.lastseen';
|
||||
|
||||
protected $allowedColumns = ['id', 'device', 'os_name', 'modified'];
|
||||
|
||||
public function getData(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_ua_parsed.id,
|
||||
event_ua_parsed.device,
|
||||
event_ua_parsed.browser_name,
|
||||
event_ua_parsed.browser_version,
|
||||
event_ua_parsed.os_name,
|
||||
event_ua_parsed.os_version,
|
||||
event_ua_parsed.ua,
|
||||
event_ua_parsed.modified,
|
||||
ed.lastseen
|
||||
|
||||
FROM
|
||||
event_ua_parsed
|
||||
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
user_agent,
|
||||
MAX(lastseen) AS lastseen
|
||||
FROM event_device
|
||||
WHERE key = :api_key
|
||||
GROUP BY user_agent
|
||||
) AS ed
|
||||
ON event_ua_parsed.id = ed.user_agent
|
||||
|
||||
WHERE
|
||||
event_ua_parsed.key = :api_key AND
|
||||
event_ua_parsed.modified IS TRUE
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
$this->applyOrder($query);
|
||||
$this->applyLimit($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
public function getTotal(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(*)
|
||||
|
||||
FROM
|
||||
event_ua_parsed
|
||||
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
user_agent,
|
||||
MAX(lastseen) AS lastseen
|
||||
FROM event_device
|
||||
WHERE key = :api_key
|
||||
GROUP BY user_agent
|
||||
) AS ed
|
||||
ON event_ua_parsed.id = ed.user_agent
|
||||
|
||||
WHERE
|
||||
event_ua_parsed.key = :api_key AND
|
||||
event_ua_parsed.modified IS TRUE
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
private function applySearch(string &$query, array &$queryParams): void {
|
||||
$this->applyDateRange($query, $queryParams);
|
||||
|
||||
$searchConditions = '';
|
||||
$search = $this->f3->get('REQUEST.search');
|
||||
|
||||
if (is_array($search) && isset($search['value']) && is_string($search['value']) && $search['value'] !== '') {
|
||||
$searchConditions = (
|
||||
' AND
|
||||
(
|
||||
event_ua_parsed.device LIKE LOWER(:search_value) OR
|
||||
event_ua_parsed.browser_name LIKE LOWER(:search_value) OR
|
||||
event_ua_parsed.os_name LIKE LOWER(:search_value) OR
|
||||
event_ua_parsed.ua LIKE LOWER(:search_value)
|
||||
)'
|
||||
);
|
||||
|
||||
$queryParams[':search_value'] = '%' . $search['value'] . '%';
|
||||
}
|
||||
|
||||
//Add search into request
|
||||
$query = sprintf($query, $searchConditions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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 Models\Grid\Countries;
|
||||
|
||||
class Grid extends \Models\Grid\Base\Grid {
|
||||
public function __construct(int $apiKey) {
|
||||
parent::__construct();
|
||||
|
||||
$this->apiKey = $apiKey;
|
||||
$this->idsModel = new Ids($apiKey);
|
||||
$this->query = new Query($apiKey);
|
||||
}
|
||||
|
||||
public function getAllCountries(): array {
|
||||
return $this->getGrid();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?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 Models\Grid\Countries;
|
||||
|
||||
class Ids extends \Models\Grid\Base\Ids {
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?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 Models\Grid\Countries;
|
||||
|
||||
class Query extends \Models\Grid\Base\Query {
|
||||
protected $defaultOrder = null;
|
||||
protected $dateRangeField = 'event_country.lastseen';
|
||||
|
||||
protected $allowedColumns = ['full_country', 'country', 'total_account', 'total_visit', 'total_ip', 'id'];
|
||||
|
||||
public function getData(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
countries.iso AS country_iso,
|
||||
countries.id AS country_id,
|
||||
countries.value AS full_country,
|
||||
countries.id,
|
||||
|
||||
event_country.total_visit,
|
||||
event_country.total_account,
|
||||
event_country.total_ip
|
||||
|
||||
|
||||
FROM
|
||||
event_country
|
||||
|
||||
LEFT JOIN countries
|
||||
ON event_country.country = countries.id
|
||||
|
||||
WHERE
|
||||
event_country.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
$this->applyOrder($query);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
public function getTotal(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(event_country.id)
|
||||
|
||||
FROM
|
||||
event_country
|
||||
|
||||
INNER JOIN countries
|
||||
ON event_country.country = countries.id
|
||||
|
||||
WHERE
|
||||
event_country.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
private function applySearch(string &$query, array &$queryParams): void {
|
||||
//Add dates into request
|
||||
$this->applyDateRange($query, $queryParams);
|
||||
|
||||
$search = $this->f3->get('REQUEST.search');
|
||||
$searchConditions = '';
|
||||
|
||||
if (is_array($search) && isset($search['value']) && is_string($search['value']) && $search['value'] !== '') {
|
||||
$searchConditions .= (
|
||||
' AND
|
||||
(
|
||||
LOWER(countries.value) LIKE LOWER(:search_value)
|
||||
OR LOWER(countries.iso) LIKE LOWER(:search_value)
|
||||
)'
|
||||
);
|
||||
$queryParams[':search_value'] = '%' . $search['value'] . '%';
|
||||
}
|
||||
|
||||
//Add search and ids into request
|
||||
$query = sprintf($query, $searchConditions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,60 @@
|
||||
<?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 Models\Grid\Devices;
|
||||
|
||||
class Grid extends \Models\Grid\Base\Grid {
|
||||
use \Traits\Enrichment\Devices;
|
||||
|
||||
public function __construct(int $apiKey) {
|
||||
parent::__construct();
|
||||
|
||||
$this->apiKey = $apiKey;
|
||||
$this->idsModel = new Ids($apiKey);
|
||||
$this->query = new Query($apiKey);
|
||||
}
|
||||
|
||||
public function getDevicesByIpId(int $ipId): array {
|
||||
$params = [':ip_id' => $ipId];
|
||||
|
||||
return $this->getGrid($this->idsModel->getDevicesIdsByIpId(), $params);
|
||||
}
|
||||
|
||||
public function getDevicesByUserId(int $userId): array {
|
||||
$params = [':account_id' => $userId];
|
||||
|
||||
return $this->getGrid($this->idsModel->getDevicesIdsByUserId(), $params);
|
||||
}
|
||||
|
||||
public function getDevicesByResourceId(int $resourceId): array {
|
||||
$params = [':resource_id' => $resourceId];
|
||||
|
||||
return $this->getGrid($this->idsModel->getDevicesIdsByResourceId(), $params);
|
||||
}
|
||||
|
||||
public function getAllDevices(): array {
|
||||
return $this->getGrid();
|
||||
}
|
||||
|
||||
protected function calculateCustomParams(array &$result): void {
|
||||
$this->applyDeviceParams($result);
|
||||
}
|
||||
|
||||
protected function convertTimeToUserTimezone(array &$result): void {
|
||||
$fields = ['created'];
|
||||
|
||||
$this->translateTimeZones($result, $fields);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?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 Models\Grid\Devices;
|
||||
|
||||
class Ids extends \Models\Grid\Base\Ids {
|
||||
public function getDevicesIdsByIpId(): string {
|
||||
return (
|
||||
'SELECT DISTINCT
|
||||
event.device AS itemid
|
||||
FROM event
|
||||
WHERE
|
||||
event.ip = :ip_id AND
|
||||
event.key = :api_key'
|
||||
);
|
||||
}
|
||||
|
||||
public function getDevicesIdsByUserId(): string {
|
||||
return (
|
||||
'SELECT DISTINCT
|
||||
event_device.id AS itemid
|
||||
FROM event_device
|
||||
WHERE
|
||||
event_device.account_id = :account_id AND
|
||||
event_device.key = :api_key'
|
||||
);
|
||||
}
|
||||
|
||||
public function getDevicesIdsByResourceId(): string {
|
||||
return (
|
||||
'SELECT DISTINCT
|
||||
event.device AS itemid
|
||||
FROM event
|
||||
WHERE
|
||||
event.url = :resource_id AND
|
||||
event.key = :api_key'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?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 Models\Grid\Devices;
|
||||
|
||||
class Query extends \Models\Grid\Base\Query {
|
||||
protected $defaultOrder = 'event_device.created DESC';
|
||||
protected $dateRangeField = 'event_device.lastseen';
|
||||
|
||||
protected $allowedColumns = ['created', 'device', 'os_name', 'browser_name', 'lang', 'modified', 'id'];
|
||||
|
||||
public function getData(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_device.id,
|
||||
event_device.lang,
|
||||
event_device.created,
|
||||
event_ua_parsed.device,
|
||||
event_ua_parsed.browser_name,
|
||||
event_ua_parsed.browser_version,
|
||||
event_ua_parsed.os_name,
|
||||
event_ua_parsed.os_version,
|
||||
event_ua_parsed.ua,
|
||||
event_ua_parsed.modified
|
||||
FROM
|
||||
event_device
|
||||
LEFT JOIN event_ua_parsed
|
||||
ON (event_device.user_agent = event_ua_parsed.id)
|
||||
|
||||
WHERE
|
||||
event_device.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
$this->applyOrder($query);
|
||||
$this->applyLimit($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
public function getTotal(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(DISTINCT event_device.id)
|
||||
|
||||
FROM
|
||||
event_device
|
||||
|
||||
WHERE
|
||||
event_device.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
private function applySearch(string &$query, array &$queryParams): void {
|
||||
//$search = $this->f3->get('REQUEST.search');
|
||||
$searchConditions = $this->injectIdQuery('event_device.id', $queryParams);
|
||||
|
||||
//Add ids into request
|
||||
$query = sprintf($query, $searchConditions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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 Models\Grid\Domains;
|
||||
|
||||
class Grid extends \Models\Grid\Base\Grid {
|
||||
public function __construct(int $apiKey) {
|
||||
parent::__construct();
|
||||
|
||||
$this->apiKey = $apiKey;
|
||||
$this->idsModel = new Ids($apiKey);
|
||||
$this->query = new Query($apiKey);
|
||||
}
|
||||
|
||||
public function getDomainsBySameIpDomainId(int $domainId): array {
|
||||
$params = [':domain_id' => $domainId];
|
||||
|
||||
return $this->getGrid($this->idsModel->getDomainsIdsBySameIpDomainId(), $params);
|
||||
}
|
||||
|
||||
public function getAllDomains(): array {
|
||||
return $this->getGrid();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?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 Models\Grid\Domains;
|
||||
|
||||
class Ids extends \Models\Grid\Base\Ids {
|
||||
public function getDomainsIdsBySameIpDomainId(): string {
|
||||
return (
|
||||
'SELECT DISTINCT
|
||||
event_domain.id AS itemid
|
||||
FROM event_domain
|
||||
WHERE
|
||||
event_domain.key = :api_key
|
||||
AND event_domain.ip = (
|
||||
SELECT
|
||||
ip
|
||||
FROM event_domain
|
||||
WHERE
|
||||
event_domain.key = :api_key
|
||||
AND event_domain.id = :domain_id
|
||||
LIMIT 1
|
||||
)
|
||||
AND event_domain.id != :domain_id'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?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 Models\Grid\Domains;
|
||||
|
||||
class Query extends \Models\Grid\Base\Query {
|
||||
protected $defaultOrder = 'event_domain.id DESC';
|
||||
protected $dateRangeField = 'event_domain.lastseen';
|
||||
|
||||
protected $allowedColumns = ['domain', 'free_email_provider', 'tranco_rank',
|
||||
'disabled', 'disposable_domains', 'creation_date', 'total_account', 'fraud', 'id'];
|
||||
|
||||
public function getData(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_domain.id,
|
||||
event_domain.domain,
|
||||
event_domain.ip,
|
||||
event_domain.total_account,
|
||||
event_domain.total_visit,
|
||||
event_domain.disposable_domains,
|
||||
event_domain.creation_date,
|
||||
event_domain.disabled,
|
||||
event_domain.free_email_provider,
|
||||
event_domain.tranco_rank,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM event_email
|
||||
WHERE
|
||||
event_email.domain = event_domain.id AND
|
||||
event_email.key = :api_key AND
|
||||
event_email.fraud_detected IS TRUE
|
||||
) AS fraud
|
||||
|
||||
FROM
|
||||
event_domain
|
||||
|
||||
WHERE
|
||||
event_domain.key = :api_key
|
||||
%s
|
||||
|
||||
GROUP BY
|
||||
event_domain.id'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
$this->applyOrder($query);
|
||||
$this->applyLimit($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
public function getTotal(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT (event_domain.id)
|
||||
|
||||
FROM
|
||||
event_domain
|
||||
|
||||
WHERE
|
||||
event_domain.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
private function applySearch(string &$query, array &$queryParams): void {
|
||||
$this->applyDateRange($query, $queryParams);
|
||||
|
||||
$search = $this->f3->get('REQUEST.search');
|
||||
$searchConditions = $this->injectIdQuery('event_domain.id', $queryParams);
|
||||
|
||||
if (isset($search) && $search['value'] !== null) {
|
||||
$searchConditions .= (
|
||||
' AND (
|
||||
LOWER(event_domain.domain) LIKE LOWER(:search_value)
|
||||
OR TEXT(event_domain.creation_date) LIKE LOWER(:search_value)
|
||||
)'
|
||||
);
|
||||
|
||||
$queryParams[':search_value'] = '%' . $search['value'] . '%';
|
||||
}
|
||||
|
||||
//Add search and ids into request
|
||||
$query = sprintf($query, $searchConditions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,44 @@
|
||||
<?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 Models\Grid\Emails;
|
||||
|
||||
class Grid extends \Models\Grid\Base\Grid {
|
||||
use \Traits\Enrichment\Emails;
|
||||
|
||||
public function __construct(int $apiKey) {
|
||||
parent::__construct();
|
||||
|
||||
$this->apiKey = $apiKey;
|
||||
$this->idsModel = new Ids($apiKey);
|
||||
$this->query = new Query($apiKey);
|
||||
}
|
||||
|
||||
public function getEmailsByUserId(int $userId): array {
|
||||
$params = [':account_id' => $userId];
|
||||
|
||||
return $this->getGrid($this->idsModel->getEmailsIdsByUserId(), $params);
|
||||
}
|
||||
|
||||
protected function calculateCustomParams(array &$result): void {
|
||||
$this->calculateEmailReputation($result);
|
||||
}
|
||||
|
||||
protected function convertTimeToUserTimezone(array &$result): void {
|
||||
$fields = ['lastseen'];
|
||||
|
||||
$this->translateTimeZones($result, $fields);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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 Models\Grid\Emails;
|
||||
|
||||
class Ids extends \Models\Grid\Base\Ids {
|
||||
public function getEmailsIdsByUserId(): string {
|
||||
return (
|
||||
'SELECT DISTINCT
|
||||
event_email.id AS itemid
|
||||
FROM event_email
|
||||
WHERE
|
||||
event_email.key = :api_key AND
|
||||
event_email.account_id = :account_id'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?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 Models\Grid\Emails;
|
||||
|
||||
class Query extends \Models\Grid\Base\Query {
|
||||
protected $defaultOrder = 'event_email.lastseen DESC';
|
||||
protected $dateRangeField = 'event_email.lastseen';
|
||||
|
||||
protected $allowedColumns = ['email', 'reputation', 'free_email_provider', 'data_breach',
|
||||
'data_breaches', 'disposable_domains', 'blockemails', 'fraud_detected'];
|
||||
|
||||
public function getData(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_email.id,
|
||||
event_email.email,
|
||||
event_email.data_breach,
|
||||
event_email.data_breaches,
|
||||
event_email.fraud_detected,
|
||||
-- event_email.profiles,
|
||||
event_email.blockemails,
|
||||
event_email.lastseen,
|
||||
event_email.alert_list,
|
||||
|
||||
event_domain.domain,
|
||||
event_domain.id AS domain_id,
|
||||
event_domain.free_email_provider,
|
||||
event_domain.disposable_domains
|
||||
|
||||
FROM
|
||||
event_email
|
||||
|
||||
LEFT JOIN event_domain
|
||||
ON (event_email.domain = event_domain.id)
|
||||
|
||||
WHERE
|
||||
event_email.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
$this->applyOrder($query);
|
||||
$this->applyLimit($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
public function getTotal(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(*)
|
||||
|
||||
FROM
|
||||
event_email
|
||||
|
||||
WHERE
|
||||
event_email.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
private function applySearch(string &$query, array &$queryParams): void {
|
||||
$search = $this->f3->get('REQUEST.search');
|
||||
$searchConditions = $this->injectIdQuery('event_email.id', $queryParams);
|
||||
|
||||
if (is_array($search) && isset($search['value']) && is_string($search['value']) && $search['value'] !== '') {
|
||||
$searchConditions .= (
|
||||
' AND
|
||||
(
|
||||
event_email.email LIKE :search_value
|
||||
OR TEXT(event_email.lastseen) LIKE :search_value
|
||||
)'
|
||||
);
|
||||
|
||||
$queryParams[':search_value'] = '%' . $search['value'] . '%';
|
||||
}
|
||||
|
||||
//Add search and ids into request
|
||||
$query = sprintf($query, $searchConditions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,86 @@
|
||||
<?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 Models\Grid\Events;
|
||||
|
||||
class Grid extends \Models\Grid\Base\Grid {
|
||||
use \Traits\Enrichment\Ips;
|
||||
use \Traits\Enrichment\Devices;
|
||||
|
||||
public function __construct(int $apiKey) {
|
||||
parent::__construct();
|
||||
|
||||
$this->apiKey = $apiKey;
|
||||
$this->idsModel = new Ids($apiKey);
|
||||
$this->query = new Query($apiKey);
|
||||
}
|
||||
|
||||
public function getEventsByUserId(int $userId): array {
|
||||
$ids = ['userId' => $userId];
|
||||
|
||||
return $this->getGrid(null, $ids);
|
||||
}
|
||||
|
||||
public function getEventsByIspId(int $ispId): array {
|
||||
$ids = ['ispId' => $ispId];
|
||||
|
||||
return $this->getGrid(null, $ids);
|
||||
}
|
||||
|
||||
public function getEventsByDomainId(int $domainId): array {
|
||||
$ids = ['domainId' => $domainId];
|
||||
|
||||
return $this->getGrid(null, $ids);
|
||||
}
|
||||
|
||||
public function getEventsByDeviceId(int $deviceId): array {
|
||||
$ids = ['deviceId' => $deviceId];
|
||||
|
||||
return $this->getGrid(null, $ids);
|
||||
}
|
||||
|
||||
public function getEventsByResourceId(int $resourceId): array {
|
||||
$ids = ['resourceId' => $resourceId];
|
||||
|
||||
return $this->getGrid(null, $ids);
|
||||
}
|
||||
|
||||
public function getEventsByCountryId(int $countryId): array {
|
||||
$ids = ['countryId' => $countryId];
|
||||
|
||||
return $this->getGrid(null, $ids);
|
||||
}
|
||||
|
||||
public function getEventsByIpId(int $ipId): array {
|
||||
$ids = ['ipId' => $ipId];
|
||||
|
||||
return $this->getGrid(null, $ids);
|
||||
}
|
||||
|
||||
public function getAllEvents() {
|
||||
return $this->getGrid();
|
||||
}
|
||||
|
||||
protected function calculateCustomParams(array &$result): void {
|
||||
$this->calculateIpType($result);
|
||||
$this->applyDeviceParams($result);
|
||||
}
|
||||
|
||||
protected function convertTimeToUserTimezone(array &$result): void {
|
||||
$fields = ['time', 'lastseen', 'session_max_t', 'session_min_t', 'score_updated_at'];
|
||||
|
||||
$this->translateTimeZones($result, $fields);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?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 Models\Grid\Events;
|
||||
|
||||
class Ids extends \Models\Grid\Base\Ids {
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
<?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 Models\Grid\Events;
|
||||
|
||||
class Query extends \Models\Grid\Base\Query {
|
||||
protected $defaultOrder = 'event.time DESC, event.id DESC';
|
||||
protected $dateRangeField = 'event.time';
|
||||
|
||||
protected $allowedColumns = ['userid', 'time', 'type', 'ip', 'ip_type', 'device', 'session_id', 'time', 'id'];
|
||||
|
||||
public function getData(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event.id,
|
||||
event.time,
|
||||
|
||||
event_type.value AS event_type,
|
||||
event_type.name AS event_type_name,
|
||||
|
||||
event_account.is_important,
|
||||
event_account.id AS accountid,
|
||||
event_account.userid AS accounttitle,
|
||||
event_account.score_updated_at,
|
||||
event_account.score,
|
||||
event_account.fraud,
|
||||
|
||||
event_url.url,
|
||||
event_url.id as url_id,
|
||||
event_url_query.query,
|
||||
event_url.title,
|
||||
|
||||
event_ip.ip,
|
||||
event_ip.data_center,
|
||||
event_ip.vpn,
|
||||
event_ip.tor,
|
||||
event_ip.relay,
|
||||
event_ip.starlink,
|
||||
event_ip.blocklist,
|
||||
event_ip.fraud_detected,
|
||||
event_ip.checked,
|
||||
|
||||
event_isp.name AS isp_name,
|
||||
|
||||
countries.iso AS country_iso,
|
||||
countries.id AS country_id,
|
||||
countries.value AS full_country,
|
||||
|
||||
event_ua_parsed.ua,
|
||||
event_ua_parsed.device,
|
||||
event_ua_parsed.os_name,
|
||||
|
||||
event_email.email,
|
||||
event.http_code,
|
||||
event.session_id,
|
||||
event_session.total_visit AS session_cnt,
|
||||
event_session.lastseen AS session_max_t,
|
||||
event_session.created AS session_min_t,
|
||||
event_session.lastseen - event_session.created AS session_duration
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_account
|
||||
ON (event.account = event_account.id)
|
||||
|
||||
INNER JOIN event_url
|
||||
ON (event.url = event_url.id)
|
||||
|
||||
FULL OUTER JOIN event_url_query
|
||||
ON (event.query = event_url_query.id)
|
||||
|
||||
LEFT JOIN event_device
|
||||
ON (event.device = event_device.id)
|
||||
|
||||
LEFT JOIN event_type
|
||||
ON (event.type = event_type.id)
|
||||
|
||||
LEFT JOIN event_ua_parsed
|
||||
ON (event_device.user_agent = event_ua_parsed.id)
|
||||
|
||||
LEFT JOIN event_ip
|
||||
ON (event.ip = event_ip.id)
|
||||
|
||||
LEFT JOIN event_isp
|
||||
ON (event_ip.isp = event_isp.id)
|
||||
|
||||
INNER JOIN countries
|
||||
ON (event_ip.country = countries.id)
|
||||
|
||||
LEFT JOIN event_email
|
||||
ON (event.email = event_email.id)
|
||||
|
||||
LEFT JOIN event_session
|
||||
ON (event.time = event_session.lastseen AND event.session_id = event_session.id)
|
||||
|
||||
WHERE
|
||||
event.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
$this->applyEventTypes($query, $queryParams);
|
||||
$this->applyDeviceTypes($query, $queryParams);
|
||||
$this->applyRules($query, $queryParams);
|
||||
$this->applyOrder($query);
|
||||
$this->applyLimit($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
public function getTotal(): array {
|
||||
$query = null;
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
if ($this->itemId !== null) {
|
||||
switch ($this->itemKey) {
|
||||
case 'userId':
|
||||
$query = 'SELECT total_visit AS count FROM event_account WHERE key = :api_key AND id = :item_id';
|
||||
break;
|
||||
case 'ispId':
|
||||
$query = 'SELECT total_visit AS count FROM event_isp WHERE key = :api_key AND id = :item_id';
|
||||
break;
|
||||
case 'domainId':
|
||||
$query = 'SELECT total_visit AS count FROM event_domain WHERE key = :api_key AND id = :item_id';
|
||||
break;
|
||||
case 'resourceId':
|
||||
$query = 'SELECT total_visit AS count FROM event_url WHERE key = :api_key AND id = :item_id';
|
||||
break;
|
||||
case 'countryId':
|
||||
$query = 'SELECT total_visit AS count FROM event_country WHERE key = :api_key AND country = :item_id';
|
||||
break;
|
||||
case 'ipId':
|
||||
$query = 'SELECT total_visit AS count FROM event_ip WHERE key = :api_key AND id = :item_id';
|
||||
break;
|
||||
case 'deviceId':
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(event.id) AS count
|
||||
FROM event
|
||||
INNER JOIN event_device
|
||||
ON (event.device = event_device.id)
|
||||
WHERE
|
||||
event_device.key = :api_key AND
|
||||
event_device.user_agent = :item_id'
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$query) {
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT(event.id) AS count
|
||||
|
||||
FROM
|
||||
event
|
||||
|
||||
INNER JOIN event_account
|
||||
ON (event.account = event_account.id)
|
||||
|
||||
INNER JOIN event_url
|
||||
ON (event.url = event_url.id)
|
||||
|
||||
INNER JOIN event_ip
|
||||
ON (event.ip = event_ip.id)
|
||||
|
||||
INNER JOIN countries
|
||||
ON (event_ip.country = countries.id)
|
||||
|
||||
LEFT JOIN event_email
|
||||
ON (event.email = event_email.id)
|
||||
|
||||
LEFT JOIN event_device
|
||||
ON (event.device = event_device.id)
|
||||
|
||||
LEFT JOIN event_type
|
||||
ON (event.type = event_type.id)
|
||||
|
||||
LEFT JOIN event_ua_parsed
|
||||
ON (event_device.user_agent = event_ua_parsed.id)
|
||||
|
||||
WHERE
|
||||
event.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
$this->applyEventTypes($query, $queryParams);
|
||||
$this->applyDeviceTypes($query, $queryParams);
|
||||
$this->applyRules($query, $queryParams);
|
||||
}
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
private function applySearch(string &$query, array &$queryParams): void {
|
||||
//Add dates into request
|
||||
$this->applyDateRange($query, $queryParams);
|
||||
|
||||
//Apply itemId into request
|
||||
$this->applyRelatedToIdSearchConitions($query);
|
||||
|
||||
$searchConditions = '';
|
||||
$search = $this->f3->get('REQUEST.search');
|
||||
|
||||
if (is_array($search) && isset($search['value']) && is_string($search['value']) && $search['value'] !== '') {
|
||||
//TODO: user isIp function here
|
||||
if (filter_var($search['value'], FILTER_VALIDATE_IP) !== false) {
|
||||
$searchConditions .= (
|
||||
' AND
|
||||
(
|
||||
event_ip.ip = :search_value
|
||||
)'
|
||||
);
|
||||
|
||||
$queryParams[':search_value'] = $search['value'];
|
||||
} else {
|
||||
// https://stackoverflow.com/a/63701098
|
||||
$searchConditions .= (
|
||||
" AND
|
||||
(
|
||||
LOWER(event_email.email) LIKE LOWER(:search_value) OR
|
||||
LOWER(event_account.userid) LIKE LOWER(:search_value) OR
|
||||
event.http_code::text LIKE LOWER(:search_value) OR
|
||||
|
||||
CASE WHEN event.http_code >= 400 THEN
|
||||
CONCAT('error ', event.http_code)
|
||||
ELSE
|
||||
'' END LIKE LOWER(:search_value) OR
|
||||
|
||||
TO_CHAR(event.time::timestamp without time zone, 'dd/mm/yyyy hh24:mi:ss') LIKE :search_value
|
||||
)"
|
||||
);
|
||||
|
||||
$queryParams[':search_value'] = '%' . $search['value'] . '%';
|
||||
}
|
||||
}
|
||||
|
||||
//Add search and ids into request
|
||||
$query = sprintf($query, $searchConditions);
|
||||
}
|
||||
|
||||
protected function getQueryParams(): array {
|
||||
$params = [':api_key' => $this->apiKey];
|
||||
if ($this->itemId !== null) {
|
||||
$params[':item_id'] = $this->itemId;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
private function applyRelatedToIdSearchConitions(string &$query): void {
|
||||
$searchConditions = null;
|
||||
|
||||
if ($this->itemId !== null) {
|
||||
switch ($this->itemKey) {
|
||||
case 'userId':
|
||||
$searchConditions = ' AND event.account = :item_id %s';
|
||||
break;
|
||||
case 'ispId':
|
||||
$searchConditions = ' AND event_isp.id = :item_id %s';
|
||||
break;
|
||||
case 'domainId':
|
||||
$searchConditions = ' AND event_email.domain = :item_id %s';
|
||||
break;
|
||||
case 'resourceId':
|
||||
$searchConditions = ' AND event.url = :item_id %s';
|
||||
break;
|
||||
case 'countryId':
|
||||
$searchConditions = ' AND countries.id = :item_id %s';
|
||||
break;
|
||||
case 'ipId':
|
||||
$searchConditions = ' AND event_ip.id = :item_id %s';
|
||||
break;
|
||||
case 'deviceId':
|
||||
$searchConditions = ' AND event_ua_parsed.id = :item_id %s';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Add search and ids into request
|
||||
if ($searchConditions !== null) {
|
||||
$query = sprintf($query, $searchConditions);
|
||||
}
|
||||
}
|
||||
|
||||
private function applyEventTypes(string &$query, array &$queryParams): void {
|
||||
$eventTypeIds = $this->f3->get('REQUEST.eventTypeIds');
|
||||
if ($eventTypeIds === null || !count($eventTypeIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$clauses = [];
|
||||
foreach ($eventTypeIds as $key => $eventTypeId) {
|
||||
$clauses[] = 'event.type = :event_type_id_' . $key;
|
||||
$queryParams[':event_type_id_' . $key] = $eventTypeId;
|
||||
}
|
||||
|
||||
$query .= ' AND (' . implode(' OR ', $clauses) . ')';
|
||||
}
|
||||
|
||||
private function applyDeviceTypes(string &$query, array &$queryParams): void {
|
||||
$deviceTypes = $this->f3->get('REQUEST.deviceTypes');
|
||||
if ($deviceTypes === null || !count($deviceTypes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$clauses = [];
|
||||
foreach ($deviceTypes as $key => $deviceType) {
|
||||
if ($deviceType === 'other') {
|
||||
$placeholders = [];
|
||||
|
||||
foreach (\Utils\Constants::get('DEVICE_TYPES') as $device) {
|
||||
if ($device !== 'unknown' && $device !== 'other') {
|
||||
$param = ':device_exclude_' . $device;
|
||||
$placeholders[] = $param;
|
||||
$queryParams[$param] = $device;
|
||||
}
|
||||
}
|
||||
|
||||
$params = implode(', ', $placeholders);
|
||||
|
||||
$clauses[] = '(event_ua_parsed.device NOT IN (' . $params . ') AND event_ua_parsed.device IS NOT NULL)';
|
||||
} elseif ($deviceType === 'unknown') {
|
||||
$clauses[] = 'event_ua_parsed.device IS NULL';
|
||||
} else {
|
||||
$param = ':device_' . $key;
|
||||
$clauses[] = 'event_ua_parsed.device = ' . $param;
|
||||
$queryParams[$param] = $deviceType;
|
||||
}
|
||||
}
|
||||
|
||||
$query .= ' AND (' . implode(' OR ', $clauses) . ')';
|
||||
}
|
||||
|
||||
private function applyRules(string &$query, array &$queryParams): void {
|
||||
$ruleUids = $this->f3->get('REQUEST.ruleUids');
|
||||
if ($ruleUids === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$uids = [];
|
||||
foreach ($ruleUids as $ruleUid) {
|
||||
$uids[] = ['uid' => $ruleUid];
|
||||
}
|
||||
|
||||
$query .= ' AND score_details @> :rules_uids::jsonb';
|
||||
$queryParams[':rules_uids'] = json_encode($uids);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
@@ -0,0 +1,72 @@
|
||||
<?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 Models\Grid\Ips;
|
||||
|
||||
class Grid extends \Models\Grid\Base\Grid {
|
||||
use \Traits\Enrichment\Ips;
|
||||
|
||||
public function __construct(int $apiKey) {
|
||||
parent::__construct();
|
||||
|
||||
$this->apiKey = $apiKey;
|
||||
$this->idsModel = new Ids($apiKey);
|
||||
$this->query = new Query($apiKey);
|
||||
}
|
||||
|
||||
public function getIpsByUserId(int $userId): array {
|
||||
$params = [':account_id' => $userId];
|
||||
|
||||
return $this->getGrid($this->idsModel->getIpsIdsByUserId(), $params);
|
||||
}
|
||||
|
||||
public function getIpsByIspId(int $ispId): array {
|
||||
$params = [':isp_id' => $ispId];
|
||||
|
||||
return $this->getGrid($this->idsModel->getIpsIdsByIspId(), $params);
|
||||
}
|
||||
|
||||
public function getIpsByDomainId($domainId) {
|
||||
$params = [':domain_id' => $domainId];
|
||||
|
||||
return $this->getGrid($this->idsModel->getIpsIdsByDomainId(), $params);
|
||||
}
|
||||
|
||||
public function getIpsByCountryId(int $countryId): array {
|
||||
$params = [':country_id' => $countryId];
|
||||
|
||||
return $this->getGrid($this->idsModel->getIpsIdsByCountryId(), $params);
|
||||
}
|
||||
|
||||
public function getIpsByDeviceId(int $deviceId): array {
|
||||
$params = [':device_id' => $deviceId];
|
||||
|
||||
return $this->getGrid($this->idsModel->getIpsIdsByDeviceId(), $params);
|
||||
}
|
||||
|
||||
public function getIpsByResourceId(int $resourceId): array {
|
||||
$params = [':resource_id' => $resourceId];
|
||||
|
||||
return $this->getGrid($this->idsModel->getIpsIdsByResourceId(), $params);
|
||||
}
|
||||
|
||||
public function getAllIps() {
|
||||
return $this->getGrid();
|
||||
}
|
||||
|
||||
protected function calculateCustomParams(array &$result): void {
|
||||
$this->calculateIpType($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?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 Models\Grid\Ips;
|
||||
|
||||
class Ids extends \Models\Grid\Base\Ids {
|
||||
public function getIpsIdsByUserId(): string {
|
||||
return (
|
||||
'SELECT DISTINCT
|
||||
event.ip AS itemid
|
||||
FROM event
|
||||
WHERE
|
||||
event.key = :api_key
|
||||
AND event.account = :account_id'
|
||||
);
|
||||
}
|
||||
|
||||
public function getIpsIdsByIspId(): string {
|
||||
return (
|
||||
'SELECT DISTINCT
|
||||
event_ip.id AS itemid
|
||||
FROM event_ip
|
||||
WHERE
|
||||
event_ip.key = :api_key
|
||||
AND event_ip.isp = :isp_id'
|
||||
);
|
||||
}
|
||||
|
||||
public function getIpsIdsByDomainId(): string {
|
||||
return (
|
||||
'SELECT DISTINCT
|
||||
event.ip AS itemid
|
||||
FROM event
|
||||
LEFT JOIN event_email
|
||||
ON (event.email = event_email.id)
|
||||
WHERE
|
||||
event_email.key = :api_key
|
||||
AND event_email.domain = :domain_id'
|
||||
);
|
||||
}
|
||||
|
||||
public function getIpsIdsByCountryId(): string {
|
||||
return (
|
||||
'SELECT DISTINCT
|
||||
event_ip.id AS itemid
|
||||
FROM event_ip
|
||||
WHERE
|
||||
event_ip.key = :api_key AND
|
||||
event_ip.country = :country_id'
|
||||
);
|
||||
}
|
||||
|
||||
public function getIpsIdsByDeviceId(): string {
|
||||
return (
|
||||
'SELECT DISTINCT
|
||||
event.ip AS itemid
|
||||
FROM event
|
||||
INNER JOIN event_device
|
||||
ON (event.device = event_device.id)
|
||||
WHERE
|
||||
event_device.user_agent = :device_id AND
|
||||
event_device.key = :api_key'
|
||||
);
|
||||
}
|
||||
|
||||
public function getIpsIdsByResourceId(): string {
|
||||
return (
|
||||
'SELECT DISTINCT
|
||||
event.ip AS itemid
|
||||
FROM event
|
||||
WHERE
|
||||
event.url = :resource_id AND
|
||||
event.key = :api_key'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?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 Models\Grid\Ips;
|
||||
|
||||
class Query extends \Models\Grid\Base\Query {
|
||||
protected $defaultOrder = 'event_ip.lastseen DESC';
|
||||
protected $dateRangeField = 'event_ip.lastseen';
|
||||
|
||||
protected $allowedColumns = ['ip', 'full_country', 'asn', 'netname', 'ip_type', 'total_visit', 'total_account', 'lastseen', 'id'];
|
||||
|
||||
public function getData(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
event_ip.id,
|
||||
event_ip.ip,
|
||||
event_ip.fraud_detected,
|
||||
event_ip.alert_list,
|
||||
event_ip.data_center,
|
||||
event_ip.vpn,
|
||||
event_ip.tor,
|
||||
event_ip.relay,
|
||||
event_ip.blocklist,
|
||||
event_ip.starlink,
|
||||
event_ip.shared AS total_account,
|
||||
event_ip.total_visit,
|
||||
event_ip.checked,
|
||||
|
||||
event_ip.lastseen AS lastseen,
|
||||
|
||||
event_isp.name AS netname,
|
||||
event_isp.description,
|
||||
event_isp.asn,
|
||||
|
||||
countries.id AS country_id,
|
||||
countries.iso AS country_iso,
|
||||
countries.value AS full_country
|
||||
|
||||
FROM
|
||||
event_ip
|
||||
|
||||
LEFT JOIN countries
|
||||
ON (event_ip.country = countries.id)
|
||||
|
||||
LEFT JOIN event_isp
|
||||
ON (event_ip.isp = event_isp.id)
|
||||
|
||||
WHERE
|
||||
event_ip.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
$this->applyIpTypes($query);
|
||||
$this->applyOrder($query);
|
||||
$this->applyLimit($query, $queryParams);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
public function getTotal(): array {
|
||||
$queryParams = $this->getQueryParams();
|
||||
|
||||
$query = (
|
||||
'SELECT
|
||||
COUNT (DISTINCT event_ip.ip)
|
||||
|
||||
FROM
|
||||
event_ip
|
||||
|
||||
LEFT JOIN countries
|
||||
ON (event_ip.country = countries.id)
|
||||
|
||||
LEFT JOIN event_isp
|
||||
ON (event_ip.isp = event_isp.id)
|
||||
|
||||
WHERE
|
||||
event_ip.key = :api_key
|
||||
%s'
|
||||
);
|
||||
|
||||
$this->applySearch($query, $queryParams);
|
||||
$this->applyIpTypes($query);
|
||||
|
||||
return [$query, $queryParams];
|
||||
}
|
||||
|
||||
private function applySearch(string &$query, array &$queryParams): void {
|
||||
$this->applyDateRange($query, $queryParams);
|
||||
|
||||
$search = $this->f3->get('REQUEST.search');
|
||||
$searchConditions = $this->injectIdQuery('event_ip.id', $queryParams);
|
||||
|
||||
if (is_array($search) && isset($search['value']) && is_string($search['value']) && $search['value'] !== '') {
|
||||
$searchConditions .= (
|
||||
' AND
|
||||
(
|
||||
TEXT(event_ip.ip) LIKE LOWER(:search_value)
|
||||
OR LOWER(event_isp.asn::text) LIKE LOWER(:search_value)
|
||||
OR LOWER(event_isp.name) LIKE LOWER(:search_value)
|
||||
OR LOWER(countries.value) LIKE LOWER(:search_value)
|
||||
OR LOWER(countries.iso) LIKE LOWER(:search_value)
|
||||
)'
|
||||
);
|
||||
|
||||
$queryParams[':search_value'] = '%' . $search['value'] . '%';
|
||||
}
|
||||
|
||||
//Add search and ids into request
|
||||
$query = sprintf($query, $searchConditions);
|
||||
}
|
||||
|
||||
private function applyIpTypes(string &$query): void {
|
||||
$ipTypeIds = $this->f3->get('REQUEST.ipTypeIds');
|
||||
if ($ipTypeIds === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($ipTypeIds as $ipTypeId) {
|
||||
switch ($ipTypeId) {
|
||||
case 0:
|
||||
$query .= ' AND fraud_detected IS TRUE ';
|
||||
break;
|
||||
case 1:
|
||||
$query .= ' AND blocklist IS TRUE ';
|
||||
break;
|
||||
case 2:
|
||||
$query .= ' AND countries.id = 0 AND event_ip.checked IS TRUE ';
|
||||
break;
|
||||
case 3:
|
||||
$query .= ' AND tor IS TRUE ';
|
||||
break;
|
||||
case 4:
|
||||
$query .= ' AND starlink IS TRUE ';
|
||||
break;
|
||||
case 5:
|
||||
$query .= ' AND relay IS TRUE ';
|
||||
break;
|
||||
case 6:
|
||||
$query .= ' AND vpn IS TRUE ';
|
||||
break;
|
||||
case 7:
|
||||
$query .= ' AND data_center IS TRUE ';
|
||||
break;
|
||||
case 8:
|
||||
$query .= ' AND (event_ip.checked IS FALSE OR event_ip.checked IS NULL) ';
|
||||
break;
|
||||
case 9:
|
||||
$query .= ' AND (tor IS FALSE AND vpn IS FALSE AND relay IS FALSE AND data_center IS FALSE AND event_ip.checked IS TRUE) ';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user