1
0
قرینه از https://github.com/matomo-org/matomo.git synced 2025-08-22 23:17:46 +00:00
Files
matomo/plugins/JsTrackerInstallCheck/NonceOption/JsTrackerInstallCheckOption.php
Michal Kleiner 62748658fc [Coding Style] Enable rule PSR12.Properties.ConstantVisibility (#22131)
* [Coding Style] Enable rule PSR12.Properties.ConstantVisibility

* Apply CS
2024-04-18 17:12:11 +02:00

218 خطوط
7.6 KiB
PHP

<?php
namespace Piwik\Plugins\JsTrackerInstallCheck\NonceOption;
use Piwik\Common;
use Piwik\Date;
use Piwik\Option;
use Piwik\SettingsPiwik;
class JsTrackerInstallCheckOption
{
public const OPTION_NAME_PREFIX = 'JsTrackerInstallCheck_';
public const MAX_NONCE_AGE_SECONDS = 30;
public const NONCE_DATA_TIME = 'time';
public const NONCE_DATA_URL = 'url';
public const NONCE_DATA_IS_SUCCESS = 'isSuccessful';
/**
* Look up a specific nonce for a site. If none exists, an empty array is returned.
*
* @param int $idSite
* @param string $nonce A MD5 hash to uniquely identify an installation test request
* @return array The data associated with a specific nonce for a site.
* E.g. ['time' => 1692920000, 'url' => 'https://some-test-site.local', 'isSuccessful' => true]
*/
public function lookUpNonce(int $idSite, string $nonce): array
{
$nonceMap = $this->getNonceMap($idSite);
return $nonceMap[$nonce] ?? [];
}
/**
* Find the nonce for a specific site and URL. If none exists, an empty array is returned.
*
* @param int $idSite
* @param string $url
* @return array Collection containing the nonce and it's associated data.
* E.g. ['some_nonce' => ['time' => 1692920000, 'url' => 'https://some-test-site.local', 'isSuccessful' => true]]
*/
public function getNonceForSiteAndUrl(int $idSite, string $url): array
{
if (empty($url)) {
return [];
}
return $this->getCurrentNonceMap($idSite, $url);
}
/**
* Get the current list of nonces for a site, excluding expired ones. Optionally filter by URL. There should only be
* one nonce per URL.
*
* @param int $idSite
* @param string $url Optionally filter the results to only be the nonce associated with the provided URL
* @return array Associative array where the nonces are the keys and the value is an array with the nonce data.
* E.g. ['some_nonce' => ['time' => 1692920000, 'url' => 'https://some-test-site.local', 'isSuccessful' => true]]
*/
public function getCurrentNonceMap(int $idSite, string $url = ''): array
{
$filteredMap = [];
$nonceMap = $this->getNonceMap($idSite);
$url = trim($url);
foreach ($nonceMap as $nonce => $checkData) {
// If the nonce is more than the max allowed age, don't include it in the collection.
if (!empty($checkData[self::NONCE_DATA_TIME]) && Date::getNowTimestamp() - $checkData[self::NONCE_DATA_TIME] > self::MAX_NONCE_AGE_SECONDS) {
continue;
}
// If the optional URL argument was provided, only include nonces for that URL
if (!empty($url) && !empty($checkData[self::NONCE_DATA_URL]) && $checkData[self::NONCE_DATA_URL] !== $url) {
continue;
}
$filteredMap[$nonce] = $checkData;
}
return $filteredMap;
}
/**
* Get the decoded array version of the JSON stored in the option table to track installation checks. Note that this
* won't filter out expired nonces like getCurrentNonceMap, so this should only be used when looking for past test
* results.
*
* @param int $idSite
* @return array Collection of nonces used for a specific site and their associated data.
* E.g. ['some_nonce' => ['time' => 1692920000, 'url' => 'https://some-test-site.local', 'isSuccessful' => true]]
*/
public function getNonceMap(int $idSite): array
{
$nonceOptionString = $this->getNonceOption($idSite);
if (empty($nonceOptionString)) {
return [];
}
$nonceOptionArray = json_decode($nonceOptionString, true);
// If the option couldn't be decoded or is in the old format, let's ignore it
if (empty($nonceOptionArray) || key_exists('nonce', $nonceOptionArray)) {
return [];
}
return $nonceOptionArray;
}
/**
* Update a nonce to indicate that the test was successful.
*
* @param int $idSite
* @param string $nonce
* @return bool Indicates whether the update was successful. The main reason it might fail is if the nonce isn't found
*/
public function markNonceAsSuccessFul(int $idSite, string $nonce): bool
{
$nonceMap = $this->getCurrentNonceMap($idSite);
if (empty($nonceMap[$nonce])) {
return false;
}
$nonceMap[$nonce][self::NONCE_DATA_IS_SUCCESS] = true;
$this->setNonceOption($idSite, $nonceMap);
return true;
}
/**
* Create a new nonce for the site/URL combination. This checks if a
*
* @param int $idSite
* @param string $url
* @return string
*/
public function createNewNonce(int $idSite, string $url): string
{
$url = trim($url);
$nonceMap = $this->getCurrentNonceMap($idSite);
// If the nonce already exists and isn't expired, reuse it
$existingNonce = $this->searchNonceMapForUrl($nonceMap, $url);
if (!empty($existingNonce) && Date::getNowTimestamp() - $nonceMap[$existingNonce]['time'] < self::MAX_NONCE_AGE_SECONDS) {
// Only update the time as the other test might have already succeeded, and we don't want to overwrite that
$this->updateNonceTime($idSite, $existingNonce, Date::getNowTimestamp());
return $existingNonce;
}
$nonceString = md5(SettingsPiwik::getSalt() . time() . Common::generateUniqId() . $url);
$nonceMap[$nonceString] = [
'time' => Date::getNowTimestamp(),
'url' => $url,
'isSuccessful' => false,
];
$this->setNonceOption($idSite, $nonceMap);
return $nonceString;
}
/**
* Get the string JSON stored in the option table to track installation checks.
*
* @param int $idSite
* @return string JSON list of nonces and the data associated with each
*/
protected function getNonceOption(int $idSite): string
{
return Option::get(self::OPTION_NAME_PREFIX . $idSite) ?: '';
}
/**
* Update the string JSON stored in the option table to track installation checks.
*
* @param int $idSite
* @param array $nonceMap
* @return void
*/
protected function setNonceOption(int $idSite, array $nonceMap): void
{
Option::set(self::OPTION_NAME_PREFIX . $idSite, json_encode($nonceMap));
}
/**
* Update the time associated with a specific nonce. This is mainly for when a nonce already exists for the
* site and requested URL. This allows us to bump the time so that we can reuse the nonce for the second test.
*
* @param int $idSite
* @param string $nonce
* @param int $time
* @return bool
*/
protected function updateNonceTime(int $idSite, string $nonce, int $time): bool
{
$nonceMap = $this->getCurrentNonceMap($idSite);
if (empty($nonceMap[$nonce])) {
return false;
}
$nonceMap[$nonce][self::NONCE_DATA_TIME] = $time;
$this->setNonceOption($idSite, $nonceMap);
return true;
}
/**
* @param array $nonceMap
* @param string $url
* @return string
*/
protected function searchNonceMapForUrl(array $nonceMap, string $url): string
{
foreach ($nonceMap as $nonce => $checkData) {
// If the URL matches, return the nonce
if (!empty($url) && !empty($checkData[self::NONCE_DATA_URL]) && $checkData[self::NONCE_DATA_URL] === $url) {
return $nonce;
}
}
return '';
}
}