[ticket/9687] Introduce new ban manager (WIP)

PHPBB3-9687
This commit is contained in:
Oliver Schramm 2018-09-28 17:09:31 +02:00 committed by Marc Alexander
parent caddc73e06
commit 5ae1d9eac6
No known key found for this signature in database
GPG key ID: 50E0D2423696F995
9 changed files with 742 additions and 0 deletions

View file

@ -0,0 +1,20 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\ban\exception;
use phpbb\exception\runtime_exception;
class invalid_length_exception extends runtime_exception
{
}

View file

@ -0,0 +1,20 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\ban\exception;
use phpbb\exception\runtime_exception;
class type_not_found_exception extends runtime_exception
{
}

160
phpBB/phpbb/ban/manager.php Normal file
View file

@ -0,0 +1,160 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\ban;
use phpbb\ban\exception\invalid_length_exception;
use phpbb\ban\exception\type_not_found_exception;
class manager
{
protected $ban_table;
protected $db;
protected $log;
protected $sessions_keys_table;
protected $sessions_table;
protected $types;
protected $user;
protected $users_table;
public function __construct($types, \phpbb\db\driver\driver_interface $db, \phpbb\log\log_interface $log, \phpbb\user $user, $ban_table, $users_table = '', $sessions_table = '', $sessions_keys_table = '')
{
$this->ban_table = $ban_table;
$this->db = $db;
$this->log = $log;
$this->sessions_keys_table = $sessions_keys_table;
$this->sessions_table = $sessions_table;
$this->types = $types;
$this->user = $user;
$this->users_table = $users_table;
}
public function ban($mode, array $items, \DateTimeInterface $start, \DateTimeInterface $end, $reason, $display_reason = '', $logging = true)
{
if (!isset($this->types[$mode]))
{
throw new type_not_found_exception(); // TODO
}
if ($start > $end && $end->getTimestamp() !== 0)
{
throw new invalid_length_exception(); // TODO
}
/** @var \phpbb\ban\type\type_interface $ban_mode */
$ban_mode = $this->types[$mode];
$ban_items = $ban_mode->prepare_for_storage($items);
// Prevent duplicate bans
$sql = 'DELETE FROM ' . $this->ban_table . "
WHERE ban_mode = '" . $this->db->sql_escape($mode) . "'
AND " . $this->db->sql_in_set('ban_item', $ban_items);
$this->db->sql_query($sql);
$insert_array = [];
foreach ($ban_items as $ban_item)
{
$insert_array[] = [
'ban_mode' => $mode,
'ban_item' => $ban_item,
'ban_start' => $start->getTimestamp(),
'ban_end' => $end->getTimestamp(),
'ban_reason' => $reason,
'ban_reason_display' => $display_reason,
];
}
if (empty($insert_array))
{
return;
}
$result = $this->db->sql_multi_insert($this->ban_table, $insert_array);
if ($result === false)
{
// Something went wrong
// TODO throw exception
}
if ($logging)
{
// TODO logging
}
if (!$ban_mode->after_ban())
{
return;
}
$user_column = $ban_mode->get_user_column();
if (!empty($user_column) && !empty($this->users_table))
{
$ban_items_sql = [];
$ban_or_like = '';
foreach ($ban_items as $ban_item)
{
if (stripos($ban_item, '*') === false)
{
$ban_items_sql[] = $ban_item;
}
else
{
$ban_or_like .= ' OR ' . $user_column . ' ' . $this->db->sql_like_expression(str_replace('*', $this->db->get_any_char(), $ban_item));
}
}
$sql = 'SELECT user_id
FROM ' . $this->users_table . '
WHERE ' . $this->db->sql_in_set('u.' . $user_column, $ban_items) . $ban_or_like;
$result = $this->db->sql_query($sql);
$user_ids = [];
while ($row = $this->db->sql_fetchrow($result))
{
$user_ids[] = (int)$row['user_id'];
}
$this->db->sql_freeresult($result);
if (!empty($user_ids) && !empty($this->sessions_table))
{
$sql = 'DELETE FROM ' . $this->sessions_table . '
WHERE ' . $this->db->sql_in_set('session_user_id', $user_ids);
$this->db->sql_query($sql);
}
if (!empty($user_ids) && !empty($this->sessions_keys_table))
{
$sql = 'DELETE FROM ' . $this->sessions_keys_table . '
WHERE ' . $this->db->sql_in_set('user_id', $user_ids);
$this->db->sql_query($sql);
}
}
}
public function unban($mode, array $items, $reason, $logging = true)
{
}
public function check(array $user_data = [])
{
}
public function tidy()
{
}
}

View file

@ -0,0 +1,95 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\ban\type;
abstract class base implements type_interface
{
/** @var \phpbb\db\driver\driver_interface */
protected $db;
/** @var array */
protected $excluded;
/** @var string */
protected $users_table;
public function __construct(\phpbb\db\driver\driver_interface $db, $users_table)
{
$this->db = $db;
$this->users_table = $users_table;
}
/**
* {@inheritDoc}
*/
public function get_user_column()
{
return null;
}
/**
* {@inheritDoc}
*/
public function after_ban()
{
return true;
}
public function after_unban()
{
}
/**
* {@inheritDoc}
*/
public function check(array $data)
{
return false;
}
public function tidy()
{
}
/**
* Queries users that are excluded from banning (like founders)
* from the database and saves them in $this->excluded array.
* Returns true on success and false on failure
*
* @return bool
*/
protected function get_excluded()
{
$user_column = $this->get_user_column();
if (empty($user_column))
{
return false;
}
$this->excluded = [];
$sql = "SELECT user_id, {$user_column}
FROM {$this->users_table}
WHERE user_type = " . USER_FOUNDER;
$result = $this->db->sql_query($sql);
while ($row = $this->db->sql_fetchrow($result))
{
$this->excluded[(int) $row['user_id']] = $row[$user_column];
}
$this->db->sql_freeresult($result);
return true;
}
}

View file

@ -0,0 +1,63 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\ban\type;
class email extends base
{
/**
* {@inheritDoc}
*/
public function get_type()
{
return 'email';
}
/**
* {@inheritDoc}
*/
public function get_user_column()
{
return 'user_email';
}
/**
* {@inheritDoc}
*/
public function prepare_for_storage(array $items)
{
if (!$this->get_excluded())
{
// TODO throw exception
}
$regex = get_preg_expression('email');
$ban_items = [];
foreach ($items as $item)
{
$item = trim($item);
if (strlen($item) > 100 || preg_match($regex, $item) || in_array($item, $this->excluded))
{
continue;
}
$ban_items[] = $item;
}
if (empty($ban_items))
{
// TODO throw exception - no valid emails defined
}
return $ban_items;
}
}

View file

@ -0,0 +1,73 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\ban\type;
/**
* Interface implemented by all ban types
*/
interface type_interface
{
/**
* Returns the type identifier for this ban type
*
* @return string
*/
public function get_type();
/**
* Returns the column in the users table which contains
* the values that should be looked for when checking a ban.
* If it returns null, the check method will be called when
* checking for bans.
*
* @return string|null
*/
public function get_user_column();
/**
* Gives the possibility to do some clean up after banning
* Returns true if affected users should be logged out and
* false otherwise
*
* @return bool
*/
public function after_ban();
public function after_unban(); // ???
/**
* In the case that get_user_column() returns null, this method
* is called when checking the ban status.
* Please note, that this method is basically called on every page,
* so the check should perform rather fast.
*
* Returns true if the person is banned and false otherwise.
*
* @param array $data The user data array
*
* @return bool
*/
public function check(array $data);
/**
* Prepares the given ban items before saving them in the database
*
* @param array $items
*
* @return array
*/
public function prepare_for_storage(array $items);
public function tidy(); // ???
}

View file

@ -0,0 +1,78 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\ban\type;
class user extends base
{
public function get_type()
{
return 'user';
}
public function get_user_column()
{
return 'user_id';
}
public function prepare_for_storage(array $items)
{
if (!$this->get_excluded())
{
// TODO throw exception
}
$sql_usernames = [];
$sql_or_like = [];
foreach ($items as $item)
{
$cleaned_username = utf8_clean_string($item);
if (stripos($cleaned_username, '*') === false)
{
$sql_usernames[] = $cleaned_username;
}
else
{
$sql_or_like[] = ['username_clean', 'LIKE', str_replace('*', $this->db->get_any_char(), $cleaned_username)];
}
}
$sql_array = [
'SELECT' => 'user_id',
'FROM' => [
$this->users_table => '',
],
'WHERE' => ['AND',
[
['OR',
array_merge([
['username_clean', 'IN', $sql_usernames]
], $sql_or_like),
],
['user_id', 'NOT_IN', array_map('intval', $this->excluded)],
],
],
];
$sql = $this->db->sql_build_query('SELECT', $sql_array);
$result = $this->db->sql_query($sql);
$ban_items = [];
while ($row = $this->db->sql_fetchrow($result))
{
$ban_items[] = (int) $row['user_id'];
}
$this->db->sql_freeresult($result);
return $ban_items;
}
}

View file

@ -0,0 +1,174 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\db\migration\data\v330;
class ban_table_p1 extends \phpbb\db\migration\migration
{
static public function depends_on()
{
return array('\phpbb\db\migration\data\v320\default_data_type_ids');
}
public function update_schema()
{
return array(
'add_tables' => array(
$this->table_prefix . 'bans' => array(
'COLUMNS' => array(
'ban_id' => array('ULINT', null, 'auto_increment'),
'ban_mode' => array('VCHAR', ''),
'ban_item' => array('STEXT_UNI', ''),
'ban_start' => array('TIMESTAMP', 0),
'ban_end' => array('TIMESTAMP', 0),
'ban_reason' => array('VCHAR_UNI', ''),
'ban_reason_display' => array('VCHAR_UNI', ''),
),
'PRIMARY_KEY' => 'ban_id',
'KEYS' => array(
'ban_end' => array('INDEX', 'ban_end'),
),
),
),
);
}
public function revert_schema()
{
return array(
'drop_tables' => array(
$this->table_prefix . 'bans',
),
);
}
public function update_data()
{
return array(
array('custom', array(array($this, 'old_to_new'))),
);
}
public function revert_data()
{
return array(
array('custom', array(array($this, 'new_to_old'))),
);
}
public function old_to_new($start)
{
$start = (int) $start;
$limit = 500;
$processed_rows = 0;
$sql = 'SELECT *
FROM ' . $this->table_prefix . "banlist";
$result = $this->db->sql_query_limit($sql, $limit, $start);
$bans = [];
while ($row = $this->db->sql_fetchrow($result))
{
$processed_rows++;
if ($row['ban_exclude'])
{
continue;
}
$row['ban_userid'] = (int) $row['ban_userid'];
$item = $mode = '';
if ($row['ban_ip'] !== '')
{
$mode = 'ip';
$item = $row['ban_ip'];
}
else if ($row['ban_email'] !== '')
{
$mode = 'email';
$item = $row['ban_email'];
}
else if ($row['ban_userid'] !== 0)
{
$mode = 'user';
$item = $row['ban_userid'];
}
if ($mode === '' || $item === '')
{
continue;
}
$bans[] = [
'ban_mode' => $mode,
'ban_item' => $item,
'ban_start' => $row['ban_start'],
'ban_end' => $row['ban_end'],
'ban_reason' => $row['ban_reason'],
'ban_reason_display' => $row['ban_give_reason'],
];
}
$this->db->sql_freeresult($result);
if ($processed_rows > 0)
{
$this->db->sql_multi_insert($this->table_prefix . 'bans', $bans);
}
else if ($processed_rows < $limit)
{
return;
}
return $limit + $start;
}
public function new_to_old($start)
{
$start = (int) $start;
$limit = 500;
$processed_rows = 0;
$sql = 'SELECT *
FROM ' . $this->table_prefix . "bans";
$result = $this->db->sql_query_limit($sql, $limit, $start);
$bans = [];
while ($row = $this->db->sql_fetchrow($result))
{
$processed_rows++;
$bans[] = [
'ban_userid' => ($row['ban_mode'] === 'user') ? (int)$row['ban_item'] : 0,
'ban_ip' => ($row['ban_mode'] === 'ip') ? $row['ban_item'] : '',
'ban_email' => ($row['ban_mode'] === 'email') ? $row['ban_item'] : '',
'ban_start' => $row['ban_start'],
'ban_end' => $row['ban_end'],
'ban_exclude' => false,
'ban_reason' => $row['ban_reason'],
'ban_give_reason' => $row['ban_reason_display'],
];
}
$this->db->sql_freeresult($result);
if ($processed_rows > 0)
{
$this->db->sql_multi_insert($this->table_prefix . 'banlist', $bans);
}
else if ($processed_rows < $limit)
{
return;
}
return $limit + $start;
}
}

View file

@ -0,0 +1,59 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\db\migration\data\v330;
class ban_table_p2 extends \phpbb\db\migration\migration
{
static public function depends_on()
{
return array('\phpbb\db\migration\data\v330\ban_table_p1');
}
public function update_schema()
{
return array(
'drop_tables' => array(
$this->table_prefix . 'banlist',
),
);
}
public function revert_schema()
{
return array(
'add_tables' => array(
$this->table_prefix . 'banlist' => array(
'COLUMNS' => array(
'ban_id' => array('ULINT', NULL, 'auto_increment'),
'ban_userid' => array('ULINT', 0),
'ban_ip' => array('VCHAR:40', ''),
'ban_email' => array('VCHAR_UNI:100', ''),
'ban_start' => array('TIMESTAMP', 0),
'ban_end' => array('TIMESTAMP', 0),
'ban_exclude' => array('BOOL', 0),
'ban_reason' => array('VCHAR_UNI', ''),
'ban_give_reason' => array('VCHAR_UNI', ''),
),
'PRIMARY_KEY' => 'ban_id',
'KEYS' => array(
'ban_end' => array('INDEX', 'ban_end'),
'ban_user' => array('INDEX', array('ban_userid', 'ban_exclude')),
'ban_email' => array('INDEX', array('ban_email', 'ban_exclude')),
'ban_ip' => array('INDEX', array('ban_ip', 'ban_exclude')),
),
),
),
);
}
}