openstackid/app/services/security_policies/BlacklistSecurityPolicy.php
Sebastian Marcet ab777ecb8d Integration Testing
refactored db transactions to allow IOC
that would make easier unit testing

Change-Id: Ifbaa14e8b8025c3a68b4eddd794c92f389087a94
Implements: blueprint openid-oauth2-integration-testing
2014-02-19 20:14:22 -03:00

140 lines
7.1 KiB
PHP

<?php
namespace services;
use BannedIP;
use DateTime;
use DB;
use Exception;
use Log;
use UserExceptionTrail;
use utils\exceptions\UnacquiredLockException;
use utils\services\ICacheService;
use utils\services\ILockManagerService;
use utils\services\IServerConfigurationService;
use utils\IPHelper;
use utils\db\ITransactionService;
/**
* Class BlacklistSecurityPolicy
* implements check point security pattern
* @package services
*/
class BlacklistSecurityPolicy extends AbstractBlacklistSecurityPolicy
{
private $exception_dictionary = array();
/**
* @param IServerConfigurationService $server_configuration_service
* @param ILockManagerService $lock_manager_service
* @param ICacheService $cache_service
* @param ITransactionService $tx_service
*/
public function __construct(IServerConfigurationService $server_configuration_service, ILockManagerService $lock_manager_service, ICacheService $cache_service,ITransactionService $tx_service)
{
parent::__construct($server_configuration_service, $lock_manager_service, $cache_service,$tx_service);
// here we configure on which exceptions are we interested and the max occurrence attempts and initial delay on tar pit for
// offending IP address
$this->exception_dictionary = array(
'openid\exceptions\ReplayAttackException' => array(null,'BlacklistSecurityPolicy.ReplayAttackExceptionInitialDelay'),
'openid\exceptions\InvalidNonce' => array('BlacklistSecurityPolicy.MaxInvalidNonceAttempts','BlacklistSecurityPolicy.InvalidNonceInitialDelay'),
'openid\exceptions\InvalidOpenIdMessageException' => array('BlacklistSecurityPolicy.MaxInvalidOpenIdMessageExceptionAttempts','BlacklistSecurityPolicy.InvalidOpenIdMessageExceptionInitialDelay'),
'openid\exceptions\OpenIdInvalidRealmException' => array('BlacklistSecurityPolicy.MaxOpenIdInvalidRealmExceptionAttempts','BlacklistSecurityPolicy.OpenIdInvalidRealmExceptionInitialDelay'),
'openid\exceptions\InvalidOpenIdMessageMode' => array('BlacklistSecurityPolicy.MaxInvalidOpenIdMessageModeAttempts','BlacklistSecurityPolicy.InvalidOpenIdMessageModeInitialDelay'),
'openid\exceptions\InvalidOpenIdAuthenticationRequestMode' => array('BlacklistSecurityPolicy.MaxInvalidOpenIdAuthenticationRequestModeAttempts','BlacklistSecurityPolicy.InvalidOpenIdAuthenticationRequestModeInitialDelay'),
'openid\exceptions\InvalidAssociation' => array('BlacklistSecurityPolicy.MaxInvalidAssociationAttempts','BlacklistSecurityPolicy.InvalidAssociationInitialDelay'),
'auth\exceptions\AuthenticationException' => array('BlacklistSecurityPolicy.MaxAuthenticationExceptionAttempts','BlacklistSecurityPolicy.AuthenticationExceptionInitialDelay'),
'oauth2\exceptions\ReplayAttackException' => array(null,'BlacklistSecurityPolicy.OAuth2.AuthCodeReplayAttackInitialDelay'),
'oauth2\exceptions\InvalidAuthorizationCodeException' => array('BlacklistSecurityPolicy.OAuth2.MaxInvalidAuthorizationCodeAttempts','BlacklistSecurityPolicy.OAuth2.InvalidAuthorizationCodeInitialDelay'),
'oauth2\exceptions\BearerTokenDisclosureAttemptException' => array('BlacklistSecurityPolicy.OAuth2.MaxInvalidBearerTokenDisclosureAttempt','BlacklistSecurityPolicy.OAuth2.BearerTokenDisclosureAttemptInitialDelay'),
);
}
/**
* Check policy
* @return bool
*/
public function check()
{
$res = true;
$remote_address = IPHelper::getUserIp();
try {
//check if banned ip is on cache ...
if ($this->cache_service->incCounterIfExists($remote_address)){
$this->counter_measure->trigger();
return false;
}
//check on db
if (!is_null($banned_ip = BannedIP::where("ip", "=", $remote_address)->first())) {
// banned ip exists on DB, set lock
$this->lock_manager_service->acquireLock("lock.ip." . $remote_address);
try {
//check lifetime
$issued = $banned_ip->created_at;
$utc_now = gmdate("Y-m-d H:i:s", time());
$utc_now = DateTime::createFromFormat("Y-m-d H:i:s", $utc_now);
//get time lived on seconds
$time_lived_seconds = abs($utc_now->getTimestamp() - $issued->getTimestamp());
if ($time_lived_seconds >= intval($this->server_configuration_service->getConfigValue("BlacklistSecurityPolicy.BannedIpLifeTimeSeconds"))) {
//void banned ip
$banned_ip->delete();
return true;
}
$banned_ip->hits = $banned_ip->hits + 1;
$banned_ip->Save();
//save ip on cache
$this->cache_service->addSingleValue($banned_ip->ip, $banned_ip->hits,intval($this->server_configuration_service->getConfigValue("BlacklistSecurityPolicy.BannedIpLifeTimeSeconds") - $time_lived_seconds));
}
catch (Exception $ex) {
Log::error($ex);
}
//release lock
$this->lock_manager_service->releaseLock("lock.ip." . $remote_address);
$this->counter_measure->trigger();
return false;
}
}
catch (UnacquiredLockException $ex1) {
Log::error($ex1);
$res = false;
} catch (Exception $ex) {
Log::error($ex);
$res = false;
}
return $res;
}
/**
* Apply security policy
* @param Exception $ex
* @return mixed|void
*/
public function apply(Exception $ex)
{
try {
$remote_ip = IPHelper::getUserIp();
$exception_class = get_class($ex);
//check exception count by type on last "MinutesWithoutExceptions" minutes...
$exception_count = intval(UserExceptionTrail::where('from_ip', '=', $remote_ip)
->where('exception_type', '=', $exception_class)
->where('created_at', '>', DB::raw('( UTC_TIMESTAMP() - INTERVAL ' . $this->server_configuration_service->getConfigValue("BlacklistSecurityPolicy.MinutesWithoutExceptions") . ' MINUTE )'))
->count());
if(array_key_exists($exception_class,$this->exception_dictionary)){
$params = $this->exception_dictionary[$exception_class];
$max_attempts = !is_null($params[0]) && !empty($params[0])? intval($this->server_configuration_service->getConfigValue($params[0])):0;
$initial_delay_on_tar_pit = intval($this->server_configuration_service->getConfigValue($params[1]));
if ($exception_count >= $max_attempts)
$this->createBannedIP($initial_delay_on_tar_pit, $exception_class);
}
} catch (Exception $ex) {
Log::error($ex);
}
}
}