From 4e4ea71e66e0590559bd4cf232946333f3fbd66c Mon Sep 17 00:00:00 2001 From: Dries Date: Sun, 23 Sep 2012 22:17:45 -0400 Subject: [PATCH] Patch #1161486 by rootatwc, sun, andypost, catch, marcingy, aspilicious, cam8001: Move IP blocking into its own module. --- core/includes/bootstrap.inc | 51 ------ core/modules/ban/ban.admin.inc | 130 +++++++++++++++ core/modules/ban/ban.info | 6 + core/modules/ban/ban.install | 35 ++++ core/modules/ban/ban.module | 155 ++++++++++++++++++ .../ban/Tests/IpAddressBlockingTest.php | 85 ++++++++++ .../Tests/StatisticsBlockVisitorsTest.php | 32 ++-- .../statistics/Tests/StatisticsTestBase.php | 4 +- core/modules/statistics/statistics.admin.inc | 34 +++- .../Tests/System/IpAddressBlockingTest.php | 89 ---------- core/modules/system/system.admin.inc | 105 ------------ core/modules/system/system.install | 56 ++++--- core/modules/system/system.module | 54 ------ 13 files changed, 488 insertions(+), 348 deletions(-) create mode 100644 core/modules/ban/ban.admin.inc create mode 100644 core/modules/ban/ban.info create mode 100644 core/modules/ban/ban.install create mode 100644 core/modules/ban/ban.module create mode 100644 core/modules/ban/lib/Drupal/ban/Tests/IpAddressBlockingTest.php delete mode 100644 core/modules/system/lib/Drupal/system/Tests/System/IpAddressBlockingTest.php diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index fb50050586b..8235b9f94c2 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1888,56 +1888,6 @@ function drupal_set_title($title = NULL, $output = CHECK_PLAIN) { return $stored_title; } -/** - * Checks to see if an IP address has been blocked. - * - * Blocked IP addresses are stored in the database by default. However for - * performance reasons we allow an override in settings.php. This allows us - * to avoid querying the database at this critical stage of the bootstrap if - * an administrative interface for IP address blocking is not required. - * - * @param $ip - * IP address to check. - * - * @return bool - * TRUE if access is denied, FALSE if access is allowed. - */ -function drupal_is_denied($ip) { - // Because this function is called on every page request, we first check - // for an array of IP addresses in settings.php before querying the - // database. - $blocked_ips = variable_get('blocked_ips'); - $denied = FALSE; - if (isset($blocked_ips) && is_array($blocked_ips)) { - $denied = in_array($ip, $blocked_ips); - } - // Only check if database.inc is loaded already. If - // $conf['page_cache_without_database'] = TRUE; is set in settings.php, - // then the database won't be loaded here so the IPs in the database - // won't be denied. However the user asked explicitly not to use the - // database and also in this case it's quite likely that the user relies - // on higher performance solutions like a firewall. - elseif (class_exists('Drupal\Core\Database\Database', FALSE)) { - $denied = (bool)db_query("SELECT 1 FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField(); - } - return $denied; -} - -/** - * Handles denied users. - * - * @param $ip - * IP address to check. Prints a message and exits if access is denied. - */ -function drupal_block_denied($ip) { - // Deny access to blocked IP addresses - t() is not yet available. - if (drupal_is_denied($ip)) { - header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); - print 'Sorry, ' . check_plain(ip_address()) . ' has been banned.'; - exit(); - } -} - /** * Returns a string of highly randomized bytes (over the full 8-bit range). * @@ -2314,7 +2264,6 @@ function _drupal_bootstrap_page_cache() { $config = config('system.performance'); $cache_enabled = $config->get('cache.page.enabled'); } - drupal_block_denied(ip_address()); // If there is no session cookie and cache is enabled (or forced), try // to serve a cached page. if (!isset($_COOKIE[session_name()]) && $cache_enabled) { diff --git a/core/modules/ban/ban.admin.inc b/core/modules/ban/ban.admin.inc new file mode 100644 index 00000000000..d7940f909d9 --- /dev/null +++ b/core/modules/ban/ban.admin.inc @@ -0,0 +1,130 @@ +ip, + l(t('delete'), "admin/config/people/ban/delete/$ip->iid"), + ); + } + + $build['ban_ip_form'] = drupal_get_form('ban_ip_form', $default_ip); + + $build['ban_ip_banning_table'] = array( + '#theme' => 'table', + '#header' => $header, + '#rows' => $rows, + '#empty' => t('No blocked IP addresses available.'), + ); + + return $build; +} + +/** + * Form constructor for banning an IP address. + * + * @param string $default_ip + * An IP address to ban, used as default value. + * + * @see ban_ip_form_validate() + * @see ban_ip_form_submit() + * + * @ingroup forms + */ +function ban_ip_form($form, &$form_state, $default_ip) { + $form['ip'] = array( + '#title' => t('IP address'), + '#type' => 'textfield', + '#size' => 48, + '#maxlength' => 40, + '#default_value' => $default_ip, + '#description' => t('Enter a valid IP address.'), + ); + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Add'), + ); + return $form; +} + +/** + * Form validation handler for ban_ip_form(). + * + * @see ban_ip_form_submit() + */ +function ban_ip_form_validate($form, &$form_state) { + $ip = trim($form_state['values']['ip']); + if (db_query("SELECT * FROM {ban_ip} WHERE ip = :ip", array(':ip' => $ip))->fetchField()) { + form_set_error('ip', t('This IP address is already banned.')); + } + elseif ($ip == ip_address()) { + form_set_error('ip', t('You may not ban your own IP address.')); + } + elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) { + form_set_error('ip', t('Enter a valid IP address.')); + } +} + +/** + * Form submission handler for ban_ip_form(). + * + * @see ban_ip_form_validate() + */ +function ban_ip_form_submit($form, &$form_state) { + $ip = trim($form_state['values']['ip']); + db_insert('ban_ip') + ->fields(array('ip' => $ip)) + ->execute(); + drupal_set_message(t('The IP address %ip has been banned.', array('%ip' => $ip))); + $form_state['redirect'] = 'admin/config/people/ban'; +} + +/** + * Form constructor to unban an IP address. + * + * @param array $ban_ip + * The IP address record to unban, as provided by ban_ip_load(). + * + * @see ban_ip_delete_submit() + */ +function ban_ip_delete_form($form, &$form_state, array $ban_ip) { + $form['ban_ip'] = array( + '#type' => 'value', + '#value' => $ban_ip, + ); + return confirm_form($form, + t('Are you sure you want to delete %ip?', array('%ip' => $ban_ip['ip'])), + 'admin/config/people/ban', + NULL, + t('Delete') + ); +} + +/** + * Form submission handler for ban_ip_delete_form(). + */ +function ban_ip_delete_form_submit($form, &$form_state) { + $banned_ip = $form_state['values']['ban_ip']; + db_delete('ban_ip') + ->condition('iid', $banned_ip['iid']) + ->execute(); + watchdog('user', 'Deleted %ip', array('%ip' => $banned_ip['ip'])); + drupal_set_message(t('The IP address %ip was deleted.', array('%ip' => $banned_ip['ip']))); + $form_state['redirect'] = 'admin/config/people/ban'; +} diff --git a/core/modules/ban/ban.info b/core/modules/ban/ban.info new file mode 100644 index 00000000000..e23331ceecd --- /dev/null +++ b/core/modules/ban/ban.info @@ -0,0 +1,6 @@ +name = Ban +description = Enables banning of IP addresses. +package = Core +version = VERSION +core = 8.x +configure = admin/config/people/ban diff --git a/core/modules/ban/ban.install b/core/modules/ban/ban.install new file mode 100644 index 00000000000..7a5494fa781 --- /dev/null +++ b/core/modules/ban/ban.install @@ -0,0 +1,35 @@ + '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', + 'length' => 40, + 'not null' => TRUE, + 'default' => '', + ), + ), + 'indexes' => array( + 'ip' => array('ip'), + ), + 'primary key' => array('iid'), + ); + return $schema; +} diff --git a/core/modules/ban/ban.module b/core/modules/ban/ban.module new file mode 100644 index 00000000000..149ada944d9 --- /dev/null +++ b/core/modules/ban/ban.module @@ -0,0 +1,155 @@ +' . t('About') . ''; + $output .= '

' . t('The Ban module allows administrators to ban visits to their site from given IP addresses.') . '

'; + $output .= '

' . t('Uses') . '

'; + $output .= '
'; + $output .= '
' . t('Banning IP addresses') . '
'; + $output .= '
' . t('Administrators can enter IP addresses to ban on the IP address bans page.', array('@bans' => url('admin/config/people/ban'))) . '
'; + $output .= '
'; + return $output; + + case 'admin/config/people/ban': + return '

' . 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.') . '

'; + } +} + +/** + * Implements hook_permission(). + */ +function ban_permission() { + return array( + 'ban IP addresses' => array( + 'title' => t('Ban IP addresses'), + ), + ); +} + +/** + * Implements hook_menu(). + */ +function ban_menu() { + $items['admin/config/people/ban'] = array( + 'title' => 'IP address bans', + 'description' => 'Manage banned IP addresses.', + 'page callback' => 'ban_admin_page', + 'access arguments' => array('ban IP addresses'), + 'file' => 'ban.admin.inc', + 'weight' => 10, + ); + $items['admin/config/people/ban/delete/%ban_ip'] = array( + 'title' => 'Delete IP address', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('ban_ip_delete_form', 5), + 'access arguments' => array('ban IP addresses'), + 'file' => 'ban.admin.inc', + ); + return $items; +} + +/** + * Implements hook_boot(). + */ +function ban_boot() { + ban_block_denied(ip_address()); +} + +/** + * Returns whether an IP address is blocked. + * + * Blocked IP addresses are stored in the database by default. However, for + * performance reasons we allow an override in variables. + * + * @param string $ip + * The IP address to check. + * + * @return bool + * TRUE if access is denied, FALSE if access is allowed. + */ +function ban_is_denied($ip) { + $denied = FALSE; + // Because this function is called on every page request, we first check + // for an array of IP addresses in settings.php before querying the + // database. + $blocked_ips = variable_get('blocked_ips'); + if (isset($blocked_ips) && is_array($blocked_ips)) { + $denied = in_array($ip, $blocked_ips); + } + // If $conf['page_cache_without_database'] = TRUE; is set in settings.php, + // then the database is not available yet, so IPs recorded in the database + // won't be denied. However, the user asked explicitly not to use the + // database, and in this case it's also quite likely that the user relies + // on higher performance solutions like a firewall. + elseif (class_exists('Drupal\Core\Database\Database', FALSE) && function_exists('db_query')) { + $denied = (bool) db_query("SELECT 1 FROM {ban_ip} WHERE ip = :ip", array(':ip' => $ip))->fetchField(); + } + return $denied; +} + +/** + * Prints a message and exits if access from a given IP address is denied. + * + * @param string $ip + * The IP address to check. + */ +function ban_block_denied($ip) { + // Check whether the given IP address has been blocked. + if (ban_is_denied($ip)) { + header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); + // t() is not yet available. + print 'Sorry, ' . check_plain($ip) . ' has been banned.'; + exit(); + } +} + +/** + * Loads a banned IP address record from the database. + * + * @param int $iid + * The ID of the banned IP address to retrieve. + * + * @return array + * The banned IP address record from the database as an array. + */ +function ban_ip_load($iid) { + return db_query("SELECT * FROM {ban_ip} WHERE iid = :iid", array(':iid' => $iid))->fetchAssoc(); +} + +/** + * Implements hook_action_info(). + */ +function ban_action_info() { + return array( + 'ban_ip_action' => array( + 'type' => 'user', + 'label' => t('Ban IP address of current user'), + 'configurable' => FALSE, + 'triggers' => array('any'), + ), + ); +} + +/** + * Bans the current user's IP address. + * + * @ingroup actions + */ +function ban_ip_action() { + $ip = ip_address(); + db_insert('blocked_ips') + ->fields(array('ip' => $ip)) + ->execute(); + watchdog('action', 'Banned IP address %ip', array('%ip' => $ip)); +} diff --git a/core/modules/ban/lib/Drupal/ban/Tests/IpAddressBlockingTest.php b/core/modules/ban/lib/Drupal/ban/Tests/IpAddressBlockingTest.php new file mode 100644 index 00000000000..5ec828d9ef9 --- /dev/null +++ b/core/modules/ban/lib/Drupal/ban/Tests/IpAddressBlockingTest.php @@ -0,0 +1,85 @@ + 'IP address banning', + 'description' => 'Test IP address banning.', + 'group' => 'Ban' + ); + } + + /** + * Test a variety of 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->drupalPost('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, t('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->drupalPost('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->drupalPost('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->drupalPost('admin/config/people/ban', $edit, t('Add')); + $this->assertText(t('Enter a valid IP address.')); + + // Submit an empty form. + $edit = array(); + $edit['ip'] = ''; + $this->drupalPost('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->drupalPost('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 or inconsistency in cURL. + // $edit = array(); + // $edit['ip'] = ip_address(); + // $this->drupalPost('admin/config/people/ban', $edit, t('Save')); + // $this->assertText(t('You may not ban your own IP address.')); + } +} diff --git a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsBlockVisitorsTest.php b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsBlockVisitorsTest.php index c8786ce7a5c..4657d10b092 100644 --- a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsBlockVisitorsTest.php +++ b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsBlockVisitorsTest.php @@ -13,8 +13,8 @@ namespace Drupal\statistics\Tests; class StatisticsBlockVisitorsTest extends StatisticsTestBase { public static function getInfo() { return array( - 'name' => 'Top visitor blocking', - 'description' => 'Tests blocking of IP addresses via the top visitors report.', + 'name' => 'Top visitor banning', + 'description' => 'Tests banning of IP addresses via the top visitors report.', 'group' => 'Statistics' ); } @@ -27,32 +27,32 @@ class StatisticsBlockVisitorsTest extends StatisticsTestBase { $test_ip_address = '192.168.1.1'; // Verify the IP address from accesslog appears on the top visitors page - // and that a 'block IP address' link is displayed. + // and that a 'ban IP address' link is displayed. $this->drupalLogin($this->blocking_user); $this->drupalGet('admin/reports/visitors'); - $this->assertText($test_ip_address, t('IP address found.')); - $this->assertText(t('block IP address'), t('Block IP link displayed')); + $this->assertText($test_ip_address, 'IP address found.'); + $this->assertText(t('ban IP address'), 'Ban IP link displayed'); // Block the IP address. - $this->clickLink('block IP address'); - $this->assertText(t('IP address blocking'), t('IP blocking page displayed.')); + $this->clickLink('ban IP address'); + $this->assertText(t('IP address bans'), 'IP banning page displayed.'); $edit = array(); $edit['ip'] = $test_ip_address; - $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add')); - $ip = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField(); - $this->assertNotEqual($ip, FALSE, t('IP address found in database')); - $this->assertRaw(t('The IP address %ip has been blocked.', array('%ip' => $edit['ip'])), t('IP address was blocked.')); + $this->drupalPost('admin/config/people/ban', $edit, t('Add')); + $ip = db_query("SELECT iid from {ban_ip} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField(); + $this->assertNotEqual($ip, FALSE, 'IP address found in database'); + $this->assertRaw(t('The IP address %ip has been banned.', array('%ip' => $edit['ip'])), 'IP address was banned.'); // Verify that the block/unblock link on the top visitors page has been // altered. $this->drupalGet('admin/reports/visitors'); - $this->assertText(t('unblock IP address'), t('Unblock IP address link displayed')); + $this->assertText(t('unban IP address'), 'Unban IP address link displayed'); // Unblock the IP address. - $this->clickLink('unblock IP address'); - $this->assertRaw(t('Are you sure you want to delete %ip?', array('%ip' => $test_ip_address)), t('IP address deletion confirmation found.')); + $this->clickLink('unban IP address'); + $this->assertRaw(t('Are you sure you want to delete %ip?', array('%ip' => $test_ip_address)), 'IP address deletion confirmation found.'); $edit = array(); - $this->drupalPost('admin/config/people/ip-blocking/delete/1', NULL, t('Delete')); - $this->assertRaw(t('The IP address %ip was deleted.', array('%ip' => $test_ip_address)), t('IP address deleted.')); + $this->drupalPost('admin/config/people/ban/delete/1', NULL, t('Delete')); + $this->assertRaw(t('The IP address %ip was deleted.', array('%ip' => $test_ip_address)), 'IP address deleted.'); } } diff --git a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTestBase.php b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTestBase.php index b7e2d31e82e..2ed8b6fb99b 100644 --- a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTestBase.php +++ b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTestBase.php @@ -19,7 +19,7 @@ abstract class StatisticsTestBase extends WebTestBase { * * @var array */ - public static $modules = array('node', 'block', 'statistics'); + public static $modules = array('node', 'block', 'ban', 'statistics'); function setUp() { parent::setUp(); @@ -34,7 +34,7 @@ abstract class StatisticsTestBase extends WebTestBase { 'access administration pages', 'access site reports', 'access statistics', - 'block IP addresses', + 'ban IP addresses', 'administer blocks', 'administer statistics', 'administer users', diff --git a/core/modules/statistics/statistics.admin.inc b/core/modules/statistics/statistics.admin.inc index 176e2af978e..b768e07f8e7 100644 --- a/core/modules/statistics/statistics.admin.inc +++ b/core/modules/statistics/statistics.admin.inc @@ -121,17 +121,20 @@ function statistics_top_pages() { * A render array containing the top visitors information. */ function statistics_top_visitors() { + $ban_exists = module_exists('ban'); $header = array( array('data' => t('Hits'), 'field' => 'hits', 'sort' => 'desc'), array('data' => t('Visitor'), 'field' => 'u.name'), array('data' => t('Total page generation time'), 'field' => 'total'), - array('data' => user_access('block IP addresses') ? t('Operations') : '', 'colspan' => 2), + array('data' => $ban_exists && user_access('ban IP addresses') ? t('Operations') : '', 'colspan' => 2), ); $query = db_select('accesslog', 'a', array('target' => 'slave')) ->extend('Drupal\Core\Database\Query\PagerSelectExtender') ->extend('Drupal\Core\Database\Query\TableSortExtender'); - $query->leftJoin('blocked_ips', 'bl', 'a.hostname = bl.ip'); + if ($ban_exists) { + $query->leftJoin('ban_ip', 'b', 'a.hostname = b.ip'); + } $query->leftJoin('users', 'u', 'a.uid = u.uid'); $query->addExpression('COUNT(a.uid)', 'hits'); @@ -139,13 +142,16 @@ function statistics_top_visitors() { $query ->fields('a', array('uid', 'hostname')) ->fields('u', array('name')) - ->fields('bl', array('iid')) ->groupBy('a.hostname') ->groupBy('a.uid') ->groupBy('u.name') - ->groupBy('bl.iid') - ->limit(30) - ->orderByHeader($header); + ->limit(30); + if ($ban_exists) { + $query + ->fields('b', array('iid')) + ->groupBy('b.iid'); + } + $query->orderByHeader($header); $uniques_query = db_select('accesslog')->distinct(); $uniques_query->fields('accesslog', array('uid', 'hostname')); @@ -157,8 +163,20 @@ function statistics_top_visitors() { $rows = array(); $destination = drupal_get_destination(); foreach ($result as $account) { - $ban_link = $account->iid ? l(t('unblock IP address'), "admin/config/people/ip-blocking/delete/$account->iid", array('query' => $destination)) : l(t('block IP address'), "admin/config/people/ip-blocking/$account->hostname", array('query' => $destination)); - $rows[] = array($account->hits, ($account->uid ? theme('username', array('account' => $account)) : $account->hostname), format_interval(round($account->total / 1000)), (user_access('block IP addresses') && !$account->uid) ? $ban_link : ''); + if ($ban_exists) { + if ($account->iid) { + $ban_link = l(t('unban IP address'), "admin/config/people/ban/delete/$account->iid", array('query' => $destination)); + } + else { + $ban_link = l(t('ban IP address'), "admin/config/people/ban/$account->hostname", array('query' => $destination)); + } + } + $row = array(); + $row[] = $account->hits; + $row[] = ($account->uid ? theme('username', array('account' => $account)) : $account->hostname); + $row[] = format_interval(round($account->total / 1000)); + $row[] = ($ban_exists && user_access('ban IP addresses') && !$account->uid ? $ban_link : ''); + $rows[] = $row; } drupal_set_title(t('Top visitors in the past %interval', array('%interval' => format_interval(config('statistics.settings')->get('access_log.max_lifetime')))), PASS_THROUGH); diff --git a/core/modules/system/lib/Drupal/system/Tests/System/IpAddressBlockingTest.php b/core/modules/system/lib/Drupal/system/Tests/System/IpAddressBlockingTest.php deleted file mode 100644 index 6d752a2d813..00000000000 --- a/core/modules/system/lib/Drupal/system/Tests/System/IpAddressBlockingTest.php +++ /dev/null @@ -1,89 +0,0 @@ - 'IP address blocking', - 'description' => 'Test IP address blocking.', - 'group' => 'System' - ); - } - - /** - * Implement setUp(). - */ - function setUp() { - parent::setUp(); - - // Create user. - $this->blocking_user = $this->drupalCreateUser(array('block IP addresses')); - $this->drupalLogin($this->blocking_user); - } - - /** - * Test a variety of user input to confirm correct validation and saving of data. - */ - function testIPAddressValidation() { - $this->drupalGet('admin/config/people/ip-blocking'); - - // Block a valid IP address. - $edit = array(); - $edit['ip'] = '192.168.1.1'; - $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add')); - $ip = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField(); - $this->assertTrue($ip, t('IP address found in database.')); - $this->assertRaw(t('The IP address %ip has been blocked.', array('%ip' => $edit['ip'])), t('IP address was blocked.')); - - // Try to block an IP address that's already blocked. - $edit = array(); - $edit['ip'] = '192.168.1.1'; - $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add')); - $this->assertText(t('This IP address is already blocked.')); - - // Try to block a reserved IP address. - $edit = array(); - $edit['ip'] = '255.255.255.255'; - $this->drupalPost('admin/config/people/ip-blocking', $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->drupalPost('admin/config/people/ip-blocking', $edit, t('Add')); - $this->assertText(t('Enter a valid IP address.')); - - // Submit an empty form. - $edit = array(); - $edit['ip'] = ''; - $this->drupalPost('admin/config/people/ip-blocking', $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->drupalPost('admin/config/people/ip-blocking/' . $submit_ip, NULL, t('Add')); - $ip = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $submit_ip))->fetchField(); - $this->assertTrue($ip, t('IP address found in database')); - $this->assertRaw(t('The IP address %ip has been blocked.', array('%ip' => $submit_ip)), t('IP address was blocked.')); - - // Submit your own IP address. This fails, although it works when testing manually. - // TODO: on some systems this test fails due to a bug or inconsistency in cURL. - // $edit = array(); - // $edit['ip'] = ip_address(); - // $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Save')); - // $this->assertText(t('You may not block your own IP address.')); - } -} diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index 2bf4343f92e..c180ad1214a 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -1371,111 +1371,6 @@ function system_modules_uninstall_submit($form, &$form_state) { } } -/** - * Menu callback. Display blocked IP addresses. - * - * @param $default_ip - * Optional IP address to be passed on to drupal_get_form() for - * use as the default value of the IP address form field. - */ -function system_ip_blocking($default_ip = '') { - $rows = array(); - $header = array(t('Blocked IP addresses'), t('Operations')); - $result = db_query('SELECT * FROM {blocked_ips}'); - foreach ($result as $ip) { - $rows[] = array( - $ip->ip, - l(t('delete'), "admin/config/people/ip-blocking/delete/$ip->iid"), - ); - } - - $build['system_ip_blocking_form'] = drupal_get_form('system_ip_blocking_form', $default_ip); - - $build['system_ip_blocking_table'] = array( - '#theme' => 'table', - '#header' => $header, - '#rows' => $rows, - '#empty' => t('No blocked IP addresses available.'), - ); - - return $build; -} - -/** - * Define the form for blocking IP addresses. - * - * @ingroup forms - * @see system_ip_blocking_form_validate() - * @see system_ip_blocking_form_submit() - */ -function system_ip_blocking_form($form, $form_state, $default_ip) { - $form['ip'] = array( - '#title' => t('IP address'), - '#type' => 'textfield', - '#size' => 48, - '#maxlength' => 40, - '#default_value' => $default_ip, - '#description' => t('Enter a valid IP address.'), - ); - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Add'), - ); - $form['#submit'][] = 'system_ip_blocking_form_submit'; - $form['#validate'][] = 'system_ip_blocking_form_validate'; - return $form; -} - -function system_ip_blocking_form_validate($form, &$form_state) { - $ip = trim($form_state['values']['ip']); - if (db_query("SELECT * FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField()) { - form_set_error('ip', t('This IP address is already blocked.')); - } - elseif ($ip == ip_address()) { - form_set_error('ip', t('You may not block your own IP address.')); - } - elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) { - form_set_error('ip', t('Enter a valid IP address.')); - } -} - -function system_ip_blocking_form_submit($form, &$form_state) { - $ip = trim($form_state['values']['ip']); - db_insert('blocked_ips') - ->fields(array('ip' => $ip)) - ->execute(); - drupal_set_message(t('The IP address %ip has been blocked.', array('%ip' => $ip))); - $form_state['redirect'] = 'admin/config/people/ip-blocking'; - return; -} - -/** - * IP deletion confirm page. - * - * @see system_ip_blocking_delete_submit() - */ -function system_ip_blocking_delete($form, &$form_state, $iid) { - $form['blocked_ip'] = array( - '#type' => 'value', - '#value' => $iid, - ); - return confirm_form($form, t('Are you sure you want to delete %ip?', array('%ip' => $iid['ip'])), 'admin/config/people/ip-blocking', t('This action cannot be undone.'), t('Delete'), t('Cancel')); -} - -/** - * Process system_ip_blocking_delete form submissions. - */ -function system_ip_blocking_delete_submit($form, &$form_state) { - $blocked_ip = $form_state['values']['blocked_ip']; - db_delete('blocked_ips') - ->condition('iid', $blocked_ip['iid']) - ->execute(); - watchdog('user', 'Deleted %ip', array('%ip' => $blocked_ip['ip'])); - drupal_set_message(t('The IP address %ip was deleted.', array('%ip' => $blocked_ip['ip']))); - $form_state['redirect'] = 'admin/config/people/ip-blocking'; -} - /** * Form builder; The general site information form. * diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 4fe99f90182..0cdaf42b096 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -628,29 +628,6 @@ function system_schema() { ), ); - $schema['blocked_ips'] = array( - 'description' => 'Stores blocked 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', - 'length' => 40, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'indexes' => array( - 'blocked_ip' => array('ip'), - ), - 'primary key' => array('iid'), - ); - $schema['cache_tags'] = array( 'description' => 'Cache table for tracking cache tags related to the cache bin.', 'fields' => array( @@ -1978,6 +1955,39 @@ function system_update_8019() { db_drop_table('registry_file'); } +/** + * Conditionally enable the new Ban module. + */ +function system_update_8020() { + $blocked_ips_exists = db_query_range('SELECT 1 FROM {blocked_ips}', 0, 1)->fetchField(); + if ($blocked_ips_exists) { + // Rename the permission name. + db_update('role_permission') + ->fields(array( + 'permission' => 'ban IP addresses', + 'module' => 'ban', + )) + ->condition('permission', 'block IP addresses') + ->execute(); + // Rename {blocked_ips} table into {ban_ip}. + db_rename_table('blocked_ips', 'ban_ip'); + // Rename the action. + db_update('actions') + ->fields(array( + 'aid' => 'ban_ip_action', + 'callback' => 'ban_ip_action', + )) + ->condition('aid', 'system_block_ip_action') + ->execute(); + // Enable the new Ban module. + update_module_enable(array('ban')); + } + else { + // Drop old table. + db_drop_table('blocked_ips'); + } +} + /** * @} End of "defgroup updates-7.x-to-8.x". * The next series of updates should start at 9000. diff --git a/core/modules/system/system.module b/core/modules/system/system.module index e5dc7682846..f804352b6a9 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -138,8 +138,6 @@ function system_help($path, $arg) { return $output; case 'admin/config/system/actions/configure': return t('An advanced action offers additional configuration options which may be filled out below. Changing the Description field is recommended in order to better identify the precise action taking place.'); - case 'admin/config/people/ip-blocking': - return '

' . t('IP addresses listed here are blocked from your site. Blocked addresses are completely forbidden from accessing the site and instead see a brief message explaining the situation.') . '

'; case 'admin/reports/status': return '

' . t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation. It may be useful to copy and paste this information into support requests filed on drupal.org's support forums and project issue queues. Before filing a support request, ensure that your web server meets the system requirements.", array('@system-requirements' => 'http://drupal.org/requirements')) . '

'; } @@ -243,9 +241,6 @@ function system_permission() { 'access site reports' => array( 'title' => t('View site reports'), ), - 'block IP addresses' => array( - 'title' => t('Block IP addresses'), - ), ); } @@ -759,23 +754,6 @@ function system_menu() { 'file' => 'system.admin.inc', ); - // IP address blocking. - $items['admin/config/people/ip-blocking'] = array( - 'title' => 'IP address blocking', - 'description' => 'Manage blocked IP addresses.', - 'page callback' => 'system_ip_blocking', - 'access arguments' => array('block IP addresses'), - 'file' => 'system.admin.inc', - 'weight' => 10, - ); - $items['admin/config/people/ip-blocking/delete/%blocked_ip'] = array( - 'title' => 'Delete IP address', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('system_ip_blocking_delete', 5), - 'access arguments' => array('block IP addresses'), - 'file' => 'system.admin.inc', - ); - // Media settings. $items['admin/config/media'] = array( 'title' => 'Media', @@ -2003,19 +1981,6 @@ function system_stream_wrappers() { return $wrappers; } -/** - * Retrieve a blocked IP address from the database. - * - * @param $iid integer - * The ID of the blocked IP address to retrieve. - * - * @return - * The blocked IP address from the database as an array. - */ -function blocked_ip_load($iid) { - return db_query("SELECT * FROM {blocked_ips} WHERE iid = :iid", array(':iid' => $iid))->fetchAssoc(); -} - /** * Menu item access callback - only enabled themes can be accessed. */ @@ -3540,12 +3505,6 @@ function system_action_info() { 'configurable' => TRUE, 'triggers' => array('any'), ), - 'system_block_ip_action' => array( - 'type' => 'user', - 'label' => t('Ban IP address of current user'), - 'configurable' => FALSE, - 'triggers' => array('any'), - ), 'system_goto_action' => array( 'type' => 'system', 'label' => t('Redirect to URL'), @@ -3766,19 +3725,6 @@ function system_goto_action($entity, $context) { drupal_goto(token_replace($context['url'], $context)); } -/** - * Blocks the current user's IP address. - * - * @ingroup actions - */ -function system_block_ip_action() { - $ip = ip_address(); - db_insert('blocked_ips') - ->fields(array('ip' => $ip)) - ->execute(); - watchdog('action', 'Banned IP address %ip', array('%ip' => $ip)); -} - /** * Generate an array of time zones and their local time&date. *