1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
<?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\Login\Security;
use Piwik\Container\StaticContainer;
use Piwik\IP;
use Piwik\Piwik;
use Piwik\Plugins\Login\Emails\LoginFromDifferentCountryEmail;
use Piwik\Plugins\Login\Model;
use Piwik\Plugins\UserCountry\LocationProvider;
use Piwik\Plugins\UsersManager\Model as UsersModel;
class LoginFromDifferentCountryDetection
{
/**
* @var Model
*/
private $model;
/**
* @var UsersModel
*/
private $usersModel;
public function __construct(Model $model, UsersModel $usersModel)
{
$this->model = $model;
$this->usersModel = $usersModel;
}
public function isEnabled(): bool
{
// we need at least one GeoIP provider that is not the default or disabled one
return $this->isGeoIPWorking();
}
private function isGeoIPWorking(): bool
{
$provider = LocationProvider::getCurrentProvider();
return null !== $provider
&& $provider->canBeUsedForLocationBasedSecurityChecks()
&& $provider->isAvailable()
&& $provider->isWorking()
&& ($provider->getSupportedLocationInfo()[LocationProvider::COUNTRY_CODE_KEY] ?? false);
}
private function getLocation(): array
{
// since we checked whether the current provider is GeoIP,
// we can directly use it here
$provider = LocationProvider::getCurrentProvider();
$location = $provider->getLocation([
'ip' => IP::getIpFromHeader(),
'disable_fallbacks' => true,
]) ?: [LocationProvider::COUNTRY_CODE_KEY => ''];
return $location;
}
private function getCurrentLoginCountry(): string
{
return $this->getLocation()[LocationProvider::COUNTRY_CODE_KEY] ?? '';
}
public function check(string $login): void
{
$lastLoginCountry = $this->model->getLastLoginCountry($login);
$currentLoginCountry = $this->getCurrentLoginCountry();
$isDifferentCountries = $currentLoginCountry !== $lastLoginCountry;
if ($isDifferentCountries) {
if (null !== $lastLoginCountry) {
// send email only if we had previous value
$this->sendLoginFromDifferentCountryEmailToUser(
$login,
$currentLoginCountry,
IP::getIpFromHeader()
);
}
// store new country
$this->model->setLastLoginCountry($login, $currentLoginCountry);
}
}
private function sendLoginFromDifferentCountryEmailToUser(string $login, string $countryCode, string $ip): void
{
$country = $countryCode ? Piwik::translate('Intl_Country_' . strtoupper($countryCode)) : '';
$user = $this->usersModel->getUser($login);
if (empty($user)) {
throw new \Exception('Unexpected error: unable to find user');
}
// create from DI container so plugins can modify email contents if they want
$email = StaticContainer::getContainer()->make(LoginFromDifferentCountryEmail::class, [
'login' => $login,
'country' => $country,
'ip' => $ip,
]);
$email->addTo($user['email'], $login);
$email->safeSend();
}
}
|