Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 43 |
BasicAuth | |
0.00% |
0 / 1 |
|
0.00% |
0 / 4 |
132 | |
0.00% |
0 / 43 |
__construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 5 |
|||
applies | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
authenticate | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 28 |
|||
challengeException | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 6 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\basic_auth\Authentication\Provider\BasicAuth. | |
*/ | |
namespace Drupal\basic_auth\Authentication\Provider; | |
use Drupal\Component\Utility\SafeMarkup; | |
use Drupal\Core\Authentication\AuthenticationProviderInterface; | |
use Drupal\Core\Authentication\AuthenticationProviderChallengeInterface; | |
use Drupal\Core\Config\ConfigFactoryInterface; | |
use Drupal\Core\Entity\EntityManagerInterface; | |
use Drupal\Core\Flood\FloodInterface; | |
use Drupal\user\UserAuthInterface; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; | |
/** | |
* HTTP Basic authentication provider. | |
*/ | |
class BasicAuth implements AuthenticationProviderInterface, AuthenticationProviderChallengeInterface { | |
/** | |
* The config factory. | |
* | |
* @var \Drupal\Core\Config\ConfigFactoryInterface | |
*/ | |
protected $configFactory; | |
/** | |
* The user auth service. | |
* | |
* @var \Drupal\user\UserAuthInterface | |
*/ | |
protected $userAuth; | |
/** | |
* The flood service. | |
* | |
* @var \Drupal\Core\Flood\FloodInterface | |
*/ | |
protected $flood; | |
/** | |
* The entity manager. | |
* | |
* @var \Drupal\Core\Entity\EntityManagerInterface | |
*/ | |
protected $entityManager; | |
/** | |
* Constructs a HTTP basic authentication provider object. | |
* | |
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory | |
* The config factory. | |
* @param \Drupal\user\UserAuthInterface $user_auth | |
* The user authentication service. | |
* @param \Drupal\Core\Flood\FloodInterface $flood | |
* The flood service. | |
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager | |
* The entity manager service. | |
*/ | |
public function __construct(ConfigFactoryInterface $config_factory, UserAuthInterface $user_auth, FloodInterface $flood, EntityManagerInterface $entity_manager) { | |
$this->configFactory = $config_factory; | |
$this->userAuth = $user_auth; | |
$this->flood = $flood; | |
$this->entityManager = $entity_manager; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function applies(Request $request) { | |
$username = $request->headers->get('PHP_AUTH_USER'); | |
$password = $request->headers->get('PHP_AUTH_PW'); | |
return isset($username) && isset($password); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function authenticate(Request $request) { | |
$flood_config = $this->configFactory->get('user.flood'); | |
$username = $request->headers->get('PHP_AUTH_USER'); | |
$password = $request->headers->get('PHP_AUTH_PW'); | |
// Flood protection: this is very similar to the user login form code. | |
// @see \Drupal\user\Form\UserLoginForm::validateAuthentication() | |
// Do not allow any login from the current user's IP if the limit has been | |
// reached. Default is 50 failed attempts allowed in one hour. This is | |
// independent of the per-user limit to catch attempts from one IP to log | |
// in to many different user accounts. We have a reasonably high limit | |
// since there may be only one apparent IP for all users at an institution. | |
if ($this->flood->isAllowed('basic_auth.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) { | |
$accounts = $this->entityManager->getStorage('user')->loadByProperties(array('name' => $username, 'status' => 1)); | |
$account = reset($accounts); | |
if ($account) { | |
if ($flood_config->get('uid_only')) { | |
// Register flood events based on the uid only, so they apply for any | |
// IP address. This is the most secure option. | |
$identifier = $account->id(); | |
} | |
else { | |
// The default identifier is a combination of uid and IP address. This | |
// is less secure but more resistant to denial-of-service attacks that | |
// could lock out all users with public user names. | |
$identifier = $account->id() . '-' . $request->getClientIP(); | |
} | |
// Don't allow login if the limit for this user has been reached. | |
// Default is to allow 5 failed attempts every 6 hours. | |
if ($this->flood->isAllowed('basic_auth.failed_login_user', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) { | |
$uid = $this->userAuth->authenticate($username, $password); | |
if ($uid) { | |
$this->flood->clear('basic_auth.failed_login_user', $identifier); | |
return $this->entityManager->getStorage('user')->load($uid); | |
} | |
else { | |
// Register a per-user failed login event. | |
$this->flood->register('basic_auth.failed_login_user', $flood_config->get('user_window'), $identifier); | |
} | |
} | |
} | |
} | |
// Always register an IP-based failed login event. | |
$this->flood->register('basic_auth.failed_login_ip', $flood_config->get('ip_window')); | |
return []; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function challengeException(Request $request, \Exception $previous) { | |
$site_name = $this->configFactory->get('system.site')->get('name'); | |
$challenge = SafeMarkup::format('Basic realm="@realm"', array( | |
'@realm' => !empty($site_name) ? $site_name : 'Access restricted', | |
)); | |
return new UnauthorizedHttpException((string) $challenge, 'No authentication credentials provided.', $previous); | |
} | |
} |