Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View file

@ -0,0 +1,7 @@
name: Ban
type: module
description: 'Enables banning of IP addresses.'
package: Core
version: VERSION
core: 8.x
configure: ban.admin_page

View file

@ -0,0 +1,35 @@
<?php
/**
* @file
* Install, update and uninstall functions for the Ban module.
*/
/**
* Implements hook_schema().
*/
function ban_schema() {
$schema['ban_ip'] = array(
'description' => 'Stores banned IP addresses.',
'fields' => array(
'iid' => array(
'description' => 'Primary Key: unique ID for IP addresses.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'ip' => array(
'description' => 'IP address',
'type' => 'varchar_ascii',
'length' => 40,
'not null' => TRUE,
'default' => '',
),
),
'indexes' => array(
'ip' => array('ip'),
),
'primary key' => array('iid'),
);
return $schema;
}

View file

@ -0,0 +1,6 @@
ban.admin_page:
title: 'IP address bans'
description: 'Manage banned IP addresses.'
route_name: ban.admin_page
weight: 10
parent: user.admin_index

View file

@ -0,0 +1,29 @@
<?php
/**
* @file
* Allows to ban individual IP addresses.
*/
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_help().
*/
function ban_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.ban':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Ban module allows administrators to ban visits to their site from individual IP addresses. For more information, see <a href="!url">the online documentation for the Ban module</a>.', array('!url' => 'https://www.drupal.org/documentation/modules/ban')) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Banning IP addresses') . '</dt>';
$output .= '<dd>' . t('Administrators can enter IP addresses to ban on the <a href="!bans">IP address bans</a> page.', array('!bans' => \Drupal::url('ban.admin_page'))) . '</dd>';
$output .= '</dl>';
return $output;
case 'ban.admin_page':
return '<p>' . t('IP addresses listed here are banned from your site. Banned addresses are completely forbidden from accessing the site and instead see a brief message explaining the situation.') . '</p>';
}
}

View file

@ -0,0 +1,2 @@
ban IP addresses:
title: 'Ban IP addresses'

View file

@ -0,0 +1,16 @@
ban.admin_page:
path: '/admin/config/people/ban/{default_ip}'
defaults:
_form: '\Drupal\ban\Form\BanAdmin'
_title: 'IP address bans'
default_ip: ''
requirements:
_permission: 'ban IP addresses'
ban.delete:
path: '/admin/config/people/ban/delete/{ban_id}'
defaults:
_form: '\Drupal\ban\Form\BanDelete'
_title: 'Delete IP address'
requirements:
_permission: 'ban IP addresses'

View file

@ -0,0 +1,13 @@
services:
ban.ip_manager:
class: Drupal\ban\BanIpManager
arguments: ['@database']
tags:
- { name: backend_overridable }
ban.middleware:
class: Drupal\ban\BanMiddleware
arguments: ['@ban.ip_manager']
tags:
# Ensure to come before page caching, so you don't serve cached pages to
# banned users.
- { name: http_middleware, priority: 250 }

View file

@ -0,0 +1,72 @@
<?php
/**
* @file
* Contains \Drupal\ban\BanIpManager.
*/
namespace Drupal\ban;
use Drupal\Core\Database\Connection;
/**
* Ban IP manager.
*/
class BanIpManager implements BanIpManagerInterface {
/**
* The database connection used to check the IP against.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* Construct the BanSubscriber.
*
* @param \Drupal\Core\Database\Connection $connection
* The database connection which will be used to check the IP against.
*/
public function __construct(Connection $connection) {
$this->connection = $connection;
}
/**
* {@inheritdoc}
*/
public function isBanned($ip) {
return (bool) $this->connection->query("SELECT * FROM {ban_ip} WHERE ip = :ip", array(':ip' => $ip))->fetchField();
}
/**
* {@inheritdoc}
*/
public function findAll() {
return $this->connection->query('SELECT * FROM {ban_ip}');
}
/**
* {@inheritdoc}
*/
public function banIp($ip) {
$this->connection->insert('ban_ip')
->fields(array('ip' => $ip))
->execute();
}
/**
* {@inheritdoc}
*/
public function unbanIp($id) {
$this->connection->delete('ban_ip')
->condition('ip', $id)
->execute();
}
/**
* {@inheritdoc}
*/
public function findById($ban_id) {
return $this->connection->query("SELECT ip FROM {ban_ip} WHERE iid = :iid", array(':iid' => $ban_id))->fetchField();
}
}

View file

@ -0,0 +1,61 @@
<?php
/**
* @file
* Contains \Drupal\ban\BanIpManagerInterface.
*/
namespace Drupal\ban;
/**
* Provides an interface defining a BanIp manager.
*/
interface BanIpManagerInterface {
/**
* Returns if this IP address is banned.
*
* @param string $ip
* The IP address to check.
*
* @return bool
* TRUE if the IP address is banned, FALSE otherwise.
*/
public function isBanned($ip);
/**
* Finds all banned IP addresses.
*
* @return \Drupal\Core\Database\StatementInterface
* The result of the database query.
*/
public function findAll();
/**
* Bans an IP address.
*
* @param string $ip
* The IP address to ban.
*/
public function banIp($ip);
/**
* Unbans an IP address.
*
* @param string $id
* The IP address to unban.
*/
public function unbanIp($id);
/**
* Finds a banned IP address by its ID.
*
* @param int $ban_id
* The ID for a banned IP address.
*
* @return string|false
* Either the banned IP address or FALSE if none exist with that ID.
*/
public function findById($ban_id);
}

View file

@ -0,0 +1,58 @@
<?php
/**
* @file
* Contains \Drupal\ban\BanMiddleware.
*/
namespace Drupal\ban;
use Drupal\Component\Utility\SafeMarkup;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Provides a HTTP middleware to implement IP based banning.
*/
class BanMiddleware implements HttpKernelInterface {
/**
* The decorated kernel.
*
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
*/
protected $httpKernel;
/**
* The ban IP manager.
*
* @var \Drupal\ban\BanIpManagerInterface
*/
protected $banIpManager;
/**
* Constructs a BanMiddleware object.
*
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
* The decorated kernel.
* @param \Drupal\ban\BanIpManagerInterface $manager
* The ban IP manager.
*/
public function __construct(HttpKernelInterface $http_kernel, BanIpManagerInterface $manager) {
$this->httpKernel = $http_kernel;
$this->banIpManager = $manager;
}
/**
* {@inheritdoc}
*/
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
$ip = $request->getClientIp();
if ($this->banIpManager->isBanned($ip)) {
return new Response(SafeMarkup::format('Sorry @ip has been banned', ['@ip' => $ip]), 403);
}
return $this->httpKernel->handle($request, $type, $catch);
}
}

View file

@ -0,0 +1,130 @@
<?php
/**
* @file
* Contains \Drupal\ban\Form\BanAdmin.
*/
namespace Drupal\ban\Form;
use Drupal\Core\Form\FormBase;
use Drupal\ban\BanIpManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Displays banned IP addresses.
*/
class BanAdmin extends FormBase {
/**
* @var \Drupal\ban\BanIpManagerInterface
*/
protected $ipManager;
/**
* Constructs a new BanAdmin object.
*
* @param \Drupal\ban\BanIpManagerInterface $ip_manager
*/
public function __construct(BanIpManagerInterface $ip_manager) {
$this->ipManager = $ip_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('ban.ip_manager')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'ban_ip_form';
}
/**
* {@inheritdoc}
*
* @param string $default_ip
* (optional) IP address to be passed on to
* \Drupal::formBuilder()->getForm() for use as the default value of the IP
* address form field.
*/
public function buildForm(array $form, FormStateInterface $form_state, $default_ip = '') {
$rows = array();
$header = array($this->t('banned IP addresses'), $this->t('Operations'));
$result = $this->ipManager->findAll();
foreach ($result as $ip) {
$row = array();
$row[] = $ip->ip;
$links = array();
$links['delete'] = array(
'title' => $this->t('Delete'),
'url' => Url::fromRoute('ban.delete', ['ban_id' => $ip->iid]),
);
$row[] = array(
'data' => array(
'#type' => 'operations',
'#links' => $links,
),
);
$rows[] = $row;
}
$form['ip'] = array(
'#title' => $this->t('IP address'),
'#type' => 'textfield',
'#size' => 48,
'#maxlength' => 40,
'#default_value' => $default_ip,
'#description' => $this->t('Enter a valid IP address.'),
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Add'),
);
$form['ban_ip_banning_table'] = array(
'#type' => 'table',
'#header' => $header,
'#rows' => $rows,
'#empty' => $this->t('No blocked IP addresses available.'),
'#weight' => 120,
);
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$ip = trim($form_state->getValue('ip'));
if ($this->ipManager->isBanned($ip)) {
$form_state->setErrorByName('ip', $this->t('This IP address is already banned.'));
}
elseif ($ip == $this->getRequest()->getClientIP()) {
$form_state->setErrorByName('ip', $this->t('You may not ban your own IP address.'));
}
elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
$form_state->setErrorByName('ip', $this->t('Enter a valid IP address.'));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$ip = trim($form_state->getValue('ip'));
$this->ipManager->banIp($ip);
drupal_set_message($this->t('The IP address %ip has been banned.', array('%ip' => $ip)));
$form_state->setRedirect('ban.admin_page');
}
}

View file

@ -0,0 +1,99 @@
<?php
/**
* @file
* Contains \Drupal\ban\Form\BanDelete.
*/
namespace Drupal\ban\Form;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\ban\BanIpManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides a form to unban IP addresses.
*/
class BanDelete extends ConfirmFormBase {
/**
* The banned IP address.
*
* @var string
*/
protected $banIp;
/**
* Constructs a new BanDelete object.
*
* @param \Drupal\ban\BanIpManagerInterface $ip_manager
* The IP manager.
*/
public function __construct(BanIpManagerInterface $ip_manager) {
$this->ipManager = $ip_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('ban.ip_manager')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'ban_ip_delete_form';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to unblock %ip?', array('%ip' => $this->banIp));
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Delete');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('ban.admin_page');
}
/**
* {@inheritdoc}
*
* @param string $ban_id
* The IP address record ID to unban.
*/
public function buildForm(array $form, FormStateInterface $form_state, $ban_id = '') {
if (!$this->banIp = $this->ipManager->findById($ban_id)) {
throw new NotFoundHttpException();
}
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->ipManager->unbanIp($this->banIp);
$this->logger('user')->notice('Deleted %ip', array('%ip' => $this->banIp));
drupal_set_message($this->t('The IP address %ip was deleted.', array('%ip' => $this->banIp)));
$form_state->setRedirectUrl($this->getCancelUrl());
}
}

View file

@ -0,0 +1,82 @@
<?php
/**
* @file
* Contains \Drupal\ban\Tests\IpAddressBlockingTest.
*/
namespace Drupal\ban\Tests;
use Drupal\simpletest\WebTestBase;
/**
* Tests IP address banning.
*
* @group ban
*/
class IpAddressBlockingTest extends WebTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = array('ban');
/**
* Tests various user input to confirm correct validation and saving of data.
*/
function testIPAddressValidation() {
// Create user.
$admin_user = $this->drupalCreateUser(array('ban IP addresses'));
$this->drupalLogin($admin_user);
$this->drupalGet('admin/config/people/ban');
// Ban a valid IP address.
$edit = array();
$edit['ip'] = '192.168.1.1';
$this->drupalPostForm('admin/config/people/ban', $edit, t('Add'));
$ip = db_query("SELECT iid from {ban_ip} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField();
$this->assertTrue($ip, 'IP address found in database.');
$this->assertRaw(t('The IP address %ip has been banned.', array('%ip' => $edit['ip'])), 'IP address was banned.');
// Try to block an IP address that's already blocked.
$edit = array();
$edit['ip'] = '192.168.1.1';
$this->drupalPostForm('admin/config/people/ban', $edit, t('Add'));
$this->assertText(t('This IP address is already banned.'));
// Try to block a reserved IP address.
$edit = array();
$edit['ip'] = '255.255.255.255';
$this->drupalPostForm('admin/config/people/ban', $edit, t('Add'));
$this->assertText(t('Enter a valid IP address.'));
// Try to block a reserved IP address.
$edit = array();
$edit['ip'] = 'test.example.com';
$this->drupalPostForm('admin/config/people/ban', $edit, t('Add'));
$this->assertText(t('Enter a valid IP address.'));
// Submit an empty form.
$edit = array();
$edit['ip'] = '';
$this->drupalPostForm('admin/config/people/ban', $edit, t('Add'));
$this->assertText(t('Enter a valid IP address.'));
// Pass an IP address as a URL parameter and submit it.
$submit_ip = '1.2.3.4';
$this->drupalPostForm('admin/config/people/ban/' . $submit_ip, NULL, t('Add'));
$ip = db_query("SELECT iid from {ban_ip} WHERE ip = :ip", array(':ip' => $submit_ip))->fetchField();
$this->assertTrue($ip, 'IP address found in database');
$this->assertRaw(t('The IP address %ip has been banned.', array('%ip' => $submit_ip)), 'IP address was banned.');
// Submit your own IP address. This fails, although it works when testing
// manually.
// TODO: On some systems this test fails due to a bug/inconsistency in cURL.
// $edit = array();
// $edit['ip'] = \Drupal::request()->getClientIP();
// $this->drupalPostForm('admin/config/people/ban', $edit, t('Save'));
// $this->assertText(t('You may not ban your own IP address.'));
}
}

View file

@ -0,0 +1,97 @@
<?php
/**
* @file
* Contains \Drupal\Tests\ban\Unit\BanMiddlewareTest.
*/
namespace Drupal\Tests\ban\Unit;
use Drupal\ban\BanMiddleware;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* @coversDefaultClass \Drupal\ban\BanMiddleware
* @group ban
*/
class BanMiddlewareTest extends UnitTestCase {
/**
* The mocked wrapped kernel.
*
* @var \Symfony\Component\HttpKernel\HttpKernelInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $kernel;
/**
* The mocked ban IP manager.
*
* @var \Drupal\ban\BanIpManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $banManager;
/**
* The tested ban middleware.
*
* @var \Drupal\ban\BanMiddleware
*/
protected $banMiddleware;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
$this->banManager = $this->getMock('Drupal\ban\BanIpManagerInterface');
$this->banMiddleware = new BanMiddleware($this->kernel, $this->banManager);
}
/**
* Tests a banned IP.
*/
public function testBannedIp() {
$banned_ip = '17.0.0.2';
$this->banManager->expects($this->once())
->method('isBanned')
->with($banned_ip)
->willReturn(TRUE);
$this->kernel->expects($this->never())
->method('handle');
$request = Request::create('/test-path');
$request->server->set('REMOTE_ADDR', $banned_ip);
$response = $this->banMiddleware->handle($request);
$this->assertEquals(403, $response->getStatusCode());
}
/**
* Tests an unbanned IP.
*/
public function testUnbannedIp() {
$unbanned_ip = '18.0.0.2';
$this->banManager->expects($this->once())
->method('isBanned')
->with($unbanned_ip)
->willReturn(FALSE);
$request = Request::create('/test-path');
$request->server->set('REMOTE_ADDR', $unbanned_ip);
$expected_response = new Response(200);
$this->kernel->expects($this->once())
->method('handle')
->with($request, HttpKernelInterface::MASTER_REQUEST, TRUE)
->willReturn($expected_response);
$response = $this->banMiddleware->handle($request);
$this->assertSame($expected_response, $response);
}
}