Skip to content
Permalink
Browse files

Merge pull request #5319 from vanilla/refactor/floodcontrol-trait

Move flood control to a trait and make the models use it
  • Loading branch information...
initvector committed Mar 30, 2017
2 parents e9c5fdf + 40d630e commit 5dab3798639d65d39793253f61c24804679b244a
@@ -209,11 +209,11 @@ public function save($FormPostValues, $Conversation = null, $Options = array())
$this->fireEvent('BeforeSaveValidation');
// Determine if spam check should be skipped.
$SkipSpamCheck = (!empty($Options['NewConversation']));
$this->setFloodControlEnabled(empty($Options['NewConversation']));
// Validate the form posted values
$MessageID = false;
if ($this->validate($FormPostValues) && !$this->checkForSpam('ConversationMessage', $SkipSpamCheck)) {
if ($this->validate($FormPostValues) && !$this->isUserSpamming(Gdn::session()->UserID, $this->floodGate)) {
$Fields = $this->Validation->schemaValidationFields(); // All fields on the form that relate to the schema
touchValue('Format', $Fields, c('Garden.InputFormatter', 'Html'));
@@ -548,7 +548,7 @@ public function save($formPostValues, $settings = []) {
$ConversationID = false;
if ($this->validate($formPostValues)
&& $MessageModel->validate($formPostValues)
&& !$this->checkForSpam('Conversation')
&& !$this->isUserSpamming(Gdn::session()->UserID, $this->floodGate)
) {
$Fields = $this->Validation->validationFields(); // All fields on the form that relate to the schema
@@ -13,6 +13,13 @@
*/
abstract class ConversationsModel extends Gdn_Model {
use \Vanilla\FloodControlTrait;
/**
* @var \Vanilla\CacheInterface Object used to store the FloodControl data.
*/
protected $floodGate;
/**
* Class constructor. Defines the related database table name.
*
@@ -21,6 +28,7 @@ abstract class ConversationsModel extends Gdn_Model {
*/
public function __construct($Name = '') {
parent::__construct($Name);
$this->floodGate = FloodControlHelper::configure($this, $this->Name);
}
/**
@@ -29,90 +37,27 @@ public function __construct($Name = '') {
* Users cannot post more than $SpamCount comments within $SpamTime
* seconds or their account will be locked for $SpamLock seconds.
*
* @deprecated
*
* @since 2.2
* @return bool Whether spam check is positive (TRUE = spammer).
*/
public function checkForSpam($Type, $SkipSpamCheck = false) {
// If spam checking is disabled or user is an admin, skip
$SpamCheckEnabled = val('SpamCheck', $this, true);
if ($SkipSpamCheck == true || $SpamCheckEnabled === false || checkPermission('Garden.Moderation.Manage')) {
return false;
}
$Spam = false;
public function checkForSpam($type, $skipSpamCheck = false) {
deprecated(__CLASS__.' '.__METHOD__, 'FloodControlTrait::isUserSpamming()');
// Validate $Type
if (!in_array($Type, array('Conversation', 'ConversationMessage'))) {
trigger_error(errorMessage(sprintf('Spam check type unknown: %s', $Type), 'ConversationsModel', 'CheckForSpam'), E_USER_ERROR);
}
// Get spam config settings
$SpamCount = c("Conversations.$Type.SpamCount", 1);
if (!is_numeric($SpamCount) || $SpamCount < 1) {
$SpamCount = 1; // 1 spam minimum
}
$SpamTime = c("Conversations.$Type.SpamTime", 30);
if (!is_numeric($SpamTime) || $SpamTime < 30) {
$SpamTime = 30; // 30 second minimum spam span
}
$SpamLock = c("Conversations.$Type.SpamLock", 60);
if (!is_numeric($SpamLock) || $SpamLock < 60) {
$SpamLock = 60; // 60 second minimum lockout
}
// Check for a spam lock first.
$Now = time();
$TimeSpamLock = (int)Gdn::session()->getAttribute("Time{$Type}SpamLock", 0);
$WaitTime = $SpamLock - ($Now - $TimeSpamLock);
if ($WaitTime > 0) {
$Spam = true;
$this->Validation->addValidationResult(
'Body',
'@'.sprintf(
t('A spam block is now in effect on your account. You must wait at least %3$s seconds before attempting to post again.'),
$SpamCount,
$SpamTime,
$WaitTime
)
);
return $Spam;
if ($skipSpamCheck) {
return false;
}
$CountSpamCheck = Gdn::session()->getAttribute('Count'.$Type.'SpamCheck', 0);
$TimeSpamCheck = (int)Gdn::session()->getAttribute('Time'.$Type.'SpamCheck', 0);
$SecondsSinceSpamCheck = time() - $TimeSpamCheck;
$session = Gdn::session();
// Apply a spam lock if necessary
$Attributes = array();
if ($SecondsSinceSpamCheck < $SpamTime && $CountSpamCheck >= $SpamCount) {
$Spam = true;
$this->Validation->addValidationResult(
'Body',
'@'.sprintf(
t('You have posted %1$s times within %2$s seconds. A spam block is now in effect on your account. You must wait at least %3$s seconds before attempting to post again.'),
$SpamCount,
$SpamTime,
$SpamLock
)
);
// Update the 'waiting period' every time they try to post again
$Attributes["Time{$Type}SpamLock"] = $Now;
$Attributes['Count'.$Type.'SpamCheck'] = 0;
} else {
if ($SecondsSinceSpamCheck > $SpamTime) {
$Attributes['Count'.$Type.'SpamCheck'] = 1;
$Attributes['Time'.$Type.'SpamCheck'] = $Now;
} else {
$Attributes['Count'.$Type.'SpamCheck'] = $CountSpamCheck + 1;
}
}
// Update the user profile after every comment
$UserModel = Gdn::userModel();
if (Gdn::session()->UserID) {
$UserModel->saveAttribute(Gdn::session()->UserID, $Attributes);
// Validate $type
if (!in_array($type, array('Conversation', 'ConversationMessage'))) {
trigger_error(ErrorMessage(sprintf('Spam check type unknown: %s', $type), $this->Name, 'checkForSpam'), E_USER_ERROR);
}
return $Spam;
$storageObject = FloodControlHelper::configure($this, $type);
return $this->isUserSpamming($session->User->UserID, $storageObject);
}
/**
@@ -13,6 +13,8 @@
*/
class ActivityModel extends Gdn_Model {
use \Vanilla\FloodControlTrait;
/** Activity notification level: Everyone. */
const NOTIFY_PUBLIC = -1;
@@ -1103,6 +1105,11 @@ public function comment($Comment) {
$Comment['ActivityID'] = $CommentActivityID;
}
$storageObject = FloodControlHelper::configure($this, 'ActivityComment');
if ($this->isUserSpamming(Gdn::session()->User->UserID, $storageObject)) {
return false;
}
// Check for spam.
$Spam = SpamModel::isSpam('ActivityComment', $Comment);
if ($Spam) {
@@ -1476,6 +1483,11 @@ public function save($Data, $Preference = false, $Options = []) {
$ActivityID = val('ActivityID', $Activity);
if (!$ActivityID) {
if (!$Delete) {
$storageObject = FloodControlHelper::configure($this, 'Activity');
if ($this->isUserSpamming(Gdn::session()->User->UserID, $storageObject)) {
return false;
}
$this->addInsertFields($Activity);
touchValue('DateUpdated', $Activity, $Activity['DateInserted']);
@@ -242,7 +242,13 @@ public function floodControl() {
'Vanilla.Discussion.SpamLock',
'Vanilla.Comment.SpamCount',
'Vanilla.Comment.SpamTime',
'Vanilla.Comment.SpamLock'
'Vanilla.Comment.SpamLock',
'Vanilla.Activity.SpamCount',
'Vanilla.Activity.SpamTime',
'Vanilla.Activity.SpamLock',
'Vanilla.ActivityComment.SpamCount',
'Vanilla.ActivityComment.SpamTime',
'Vanilla.ActivityComment.SpamLock',
);
if ($IsConversationsEnabled) {
$ConfigurationFields = array_merge(
@@ -285,6 +291,19 @@ public function floodControl() {
$ConfigurationModel->Validation->applyRule('Vanilla.Comment.SpamLock', 'Required');
$ConfigurationModel->Validation->applyRule('Vanilla.Comment.SpamLock', 'Integer');
$ConfigurationModel->Validation->applyRule('Vanilla.Activity.SpamCount', 'Required');
$ConfigurationModel->Validation->applyRule('Vanilla.Activity.SpamCount', 'Integer');
$ConfigurationModel->Validation->applyRule('Vanilla.Activity.SpamTime', 'Required');
$ConfigurationModel->Validation->applyRule('Vanilla.Activity.SpamTime', 'Integer');
$ConfigurationModel->Validation->applyRule('Vanilla.Activity.SpamLock', 'Required');
$ConfigurationModel->Validation->applyRule('Vanilla.Activity.SpamLock', 'Integer');
$ConfigurationModel->Validation->applyRule('Vanilla.ActivityComment.SpamCount', 'Required');
$ConfigurationModel->Validation->applyRule('Vanilla.ActivityComment.SpamCount', 'Integer');
$ConfigurationModel->Validation->applyRule('Vanilla.ActivityComment.SpamTime', 'Required');
$ConfigurationModel->Validation->applyRule('Vanilla.ActivityComment.SpamTime', 'Integer');
$ConfigurationModel->Validation->applyRule('Vanilla.ActivityComment.SpamLock', 'Required');
$ConfigurationModel->Validation->applyRule('Vanilla.ActivityComment.SpamLock', 'Integer');
if ($IsConversationsEnabled) {
$ConfigurationModel->Validation->applyRule('Conversations.Conversation.SpamCount', 'Required');
@@ -750,7 +769,7 @@ public function categories($parent = '') {
}
$this->addJsFile('categoryfilter.js', 'vanilla');
$this->setData('ParentID', $parentID);
$this->setData('Categories', $categories);
$this->setData('_Limit', $perPage);
@@ -11,7 +11,9 @@
/**
* Manages discussion comments data.
*/
class CommentModel extends VanillaModel {
class CommentModel extends Gdn_Model {
use \Vanilla\FloodControlTrait;
/** Threshold. */
const COMMENT_THRESHOLD_SMALL = 1000;
@@ -31,6 +33,11 @@ class CommentModel extends VanillaModel {
/** @var bool */
public $pageCache;
/**
* @var \Vanilla\CacheInterface Object used to store the FloodControl data.
*/
protected $floodGate;
/**
* Class constructor. Defines the related database table name.
*
@@ -39,6 +46,7 @@ class CommentModel extends VanillaModel {
*/
public function __construct() {
parent::__construct('Comment');
$this->floodGate = FloodControlHelper::configure($this, 'Comment');
$this->pageCache = Gdn::cache()->activeEnabled() && c('Properties.CommentModel.pageCache', false);
$this->fireEvent('AfterConstruct');
}
@@ -814,8 +822,6 @@ protected function _WhereFromOrderBy($Part, $Comment, $Op = '') {
* @since 2.0.0
*/
public function save($FormPostValues, $Settings = false) {
$Session = Gdn::session();
// Define the primary key in this model's table.
$this->defineSchema();
@@ -852,8 +858,16 @@ public function save($FormPostValues, $Settings = false) {
// Validate the form posted values
if ($this->validate($FormPostValues, $Insert)) {
// Backward compatible check for flood control
if (!val('SpamCheck', $this, true)) {
deprecated('DiscussionModel->SpamCheck attribute', 'FloodControlTrait->setFloodControlEnabled()');
$this->setFloodControlEnabled(false);
}
$isUserSpamming = $this->isUserSpamming(Gdn::session()->UserID, $this->floodGate);
// If the post is new and it validates, check for spam
if (!$Insert || !$this->CheckForSpam('Comment')) {
if (!$Insert || !$isUserSpamming) {
$Fields = $this->Validation->SchemaValidationFields();
unset($Fields[$this->PrimaryKey]);
@@ -11,9 +11,10 @@
/**
* Manages discussions data.
*/
class DiscussionModel extends VanillaModel {
class DiscussionModel extends Gdn_Model {
use StaticInitializer;
use \Vanilla\FloodControlTrait;
/** Cache key. */
const CACHE_DISCUSSIONVIEWS = 'discussion.%s.countviews';
@@ -79,6 +80,11 @@ class DiscussionModel extends VanillaModel {
*/
protected $filters = [];
/**
* @var \Vanilla\CacheInterface Object used to store the FloodControl data.
*/
protected $floodGate;
/**
* Class constructor. Defines the related database table name.
*
@@ -87,6 +93,7 @@ class DiscussionModel extends VanillaModel {
*/
public function __construct() {
parent::__construct('Discussion');
$this->floodGate = FloodControlHelper::configure($this, 'Discussion');
}
/**
@@ -1804,8 +1811,6 @@ public function setField($RowID, $Property, $Value = false) {
* @return int $DiscussionID Unique ID of the discussion.
*/
public function save($FormPostValues, $Settings = false) {
$Session = Gdn::session();
// Define the primary key in this model's table.
$this->defineSchema();
@@ -1898,8 +1903,16 @@ public function save($FormPostValues, $Settings = false) {
}
if (count($ValidationResults) == 0) {
// Backward compatible check for flood control
if (!val('SpamCheck', $this, true)) {
deprecated('DiscussionModel->SpamCheck attribute', 'FloodControlTrait->setFloodControlEnabled()');
$this->setFloodControlEnabled(false);
}
$isUserSpamming = $this->isUserSpamming(Gdn::session()->UserID, $this->floodGate);
// If the post is new and it validates, make sure the user isn't spamming
if (!$Insert || !$this->checkForSpam('Discussion')) {
if (!$Insert || !$isUserSpamming) {
// Get all fields on the form that relate to the schema
$Fields = $this->Validation->schemaValidationFields();
@@ -11,7 +11,7 @@
/**
* Manages unpublished drafts of comments and discussions.
*/
class DraftModel extends VanillaModel {
class DraftModel extends Gdn_Model {
/**
* Class constructor. Defines the related database table name.
Oops, something went wrong.

0 comments on commit 5dab379

Please sign in to comment.
You can’t perform that action at this time.