- 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.
185 lines
6.0 KiB
PHP
185 lines
6.0 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Tirreno ~ Open source user analytics
|
|
* Copyright (c) Tirreno Technologies Sàrl (https://www.tirreno.com)
|
|
*
|
|
* Licensed under GNU Affero General Public License version 3 of the or any later version.
|
|
* For full copyright and license information, please see the LICENSE
|
|
* Redistributions of files must retain the above copyright notice.
|
|
*
|
|
* @copyright Copyright (c) Tirreno Technologies Sàrl (https://www.tirreno.com)
|
|
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
|
|
* @link https://www.tirreno.com Tirreno(tm)
|
|
*/
|
|
|
|
namespace Utils;
|
|
|
|
class RulesClasses {
|
|
private const RULES_WEIGHT = [
|
|
-20 => 'positive',
|
|
10 => 'medium',
|
|
20 => 'high',
|
|
70 => 'extreme',
|
|
0 => 'none',
|
|
];
|
|
|
|
private const RULES_TYPES = [
|
|
'A' => 'Account takeover',
|
|
'B' => 'Behaviour',
|
|
'C' => 'Country',
|
|
'D' => 'Device',
|
|
'E' => 'Email',
|
|
'I' => 'IP',
|
|
'R' => 'Reuse',
|
|
'P' => 'Phone',
|
|
'X' => 'Extra',
|
|
];
|
|
|
|
private static string $coreRulesNamespace = '\\Controllers\\Admin\\Rules\\Set';
|
|
private static string $assetsRulesNamespace = '\\ExtendedRules';
|
|
|
|
public static function getRuleClass(?int $value): string {
|
|
return self::RULES_WEIGHT[$value ?? 0] ?? 'none';
|
|
}
|
|
|
|
public static function getRuleTypeByUid(string $uid): string {
|
|
return self::RULES_TYPES[$uid[0]] ?? $uid[0];
|
|
}
|
|
|
|
public static function getUserScoreClass(?int $score): array {
|
|
$cls = 'empty';
|
|
if ($score === null) {
|
|
return ['−', $cls];
|
|
}
|
|
|
|
if ($score >= \Utils\Constants::get('USER_LOW_SCORE_INF') && $score < \Utils\Constants::get('USER_LOW_SCORE_SUP')) {
|
|
$cls = 'low';
|
|
}
|
|
|
|
if ($score >= \Utils\Constants::get('USER_MEDIUM_SCORE_INF') && $score < \Utils\Constants::get('USER_MEDIUM_SCORE_SUP')) {
|
|
$cls = 'medium';
|
|
}
|
|
|
|
if ($score >= \Utils\Constants::get('USER_HIGH_SCORE_INF')) {
|
|
$cls = 'high';
|
|
}
|
|
|
|
return [$score, $cls];
|
|
}
|
|
|
|
private static function getCoreRulesDir(): string {
|
|
return dirname(__DIR__, 1) . '/Controllers/Admin/Rules/Set';
|
|
}
|
|
|
|
private static function getAssetsRulesDir(): string {
|
|
return dirname(__DIR__, 2) . '/assets/rules';
|
|
}
|
|
|
|
public static function getAllRulesObjects(?\Ruler\RuleBuilder $rb): array {
|
|
$local = self::getRulesClasses(false);
|
|
$core = self::getRulesClasses(true);
|
|
|
|
$total = $local['imported'] + $core['imported'];
|
|
|
|
foreach ($total as $uid => $cls) {
|
|
$total[$uid] = new $cls($rb, []);
|
|
}
|
|
|
|
return $total;
|
|
}
|
|
|
|
public static function getSingleRuleObject(string $uid, ?\Ruler\RuleBuilder $rb): ?\Controllers\Admin\Rules\Set\BaseRule {
|
|
$obj = null;
|
|
$cores = [false, true];
|
|
|
|
foreach ($cores as $core) {
|
|
$dir = $core ? self::getCoreRulesDir() : self::getAssetsRulesDir();
|
|
$namespace = $core ? self::$coreRulesNamespace : self::$assetsRulesNamespace;
|
|
|
|
$filename = $dir . '/' . $uid . '.php';
|
|
$cls = $namespace . '\\' . $uid;
|
|
|
|
try {
|
|
self::validateRuleClass($uid, $filename, $cls, $core);
|
|
$obj = new $cls($rb, []);
|
|
break;
|
|
} catch (\Throwable $e) {
|
|
error_log('Rule validation failed at file ' . $filename);
|
|
}
|
|
}
|
|
|
|
return $obj;
|
|
}
|
|
|
|
public static function getRulesClasses(bool $core): array {
|
|
$dir = $core ? self::getCoreRulesDir() : self::getAssetsRulesDir();
|
|
$namespace = $core ? self::$coreRulesNamespace : self::$assetsRulesNamespace;
|
|
|
|
$out = [];
|
|
$failed = [];
|
|
$iter = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir));
|
|
$namePattern = $core ? '/^[A-WY-Z][0-9]{2,3}$/' : '/^[A-Z][0-9]{2,3}$/';
|
|
|
|
foreach ($iter as $file) {
|
|
if ($file->isFile() && $file->getExtension() === 'php') {
|
|
$name = null;
|
|
try {
|
|
$name = basename($file->getFilename(), '.php');
|
|
|
|
if (!preg_match($namePattern, $name)) {
|
|
continue;
|
|
}
|
|
|
|
$filePath = $file->getRealPath();
|
|
|
|
$cls = $namespace . '\\' . $name;
|
|
|
|
self::validateRuleClass($name, $filePath, $cls, $core);
|
|
|
|
$out[$name] = $cls;
|
|
} catch (\Throwable $e) {
|
|
$failed[] = $name;
|
|
error_log('Fail on require_once: ' . $e->getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
return ['imported' => $out, 'failed' => $failed];
|
|
}
|
|
|
|
private static function validateRuleClass(string $uid, string $filename, string $classname, bool $core): string {
|
|
$reflection = self::validateObject($filename, $classname);
|
|
|
|
if (!$core && !str_starts_with($uid, 'X')) {
|
|
$parentClassName = $reflection->getParentClass()?->getName();
|
|
if ('\\' . $parentClassName !== self::$coreRulesNamespace . '\\' . $uid) {
|
|
throw new \LogicException("Class {$classname} in assets has invalid parent class {$parentClassName}");
|
|
}
|
|
}
|
|
|
|
return $classname;
|
|
}
|
|
|
|
private static function validateObject(string $filename, string $classname): \ReflectionClass {
|
|
if (!file_exists($filename)) {
|
|
throw new \LogicException("File {$filename} doesn't exist.");
|
|
}
|
|
|
|
require_once $filename;
|
|
|
|
if (!class_exists($classname, false)) {
|
|
throw new \LogicException("Class {$classname} not found after including {$filename}");
|
|
}
|
|
|
|
$reflection = new \ReflectionClass($classname);
|
|
$reflectionFileName = $reflection->getFileName();
|
|
|
|
if (realpath($reflectionFileName) !== realpath($filename)) {
|
|
throw new \LogicException("Class {$classname} is defined in {$reflectionFileName}, not in {$filename}");
|
|
}
|
|
|
|
return $reflection;
|
|
}
|
|
}
|