Added message submissions rate-limiting support

This commit is contained in:
Ludovic Marcotte
2013-06-11 11:21:58 -04:00
parent cfee5aa3f4
commit afb7bc309c
7 changed files with 191 additions and 21 deletions
Binary file not shown.
+4 -1
View File
@@ -5,7 +5,10 @@ Enhancements
- updated CKEditor to version 4.1.1 (#2333)
- new failed login attemps rate-limiting options. See the new
SOGoMaximumFailedLoginCount, SOGoMaximumFailedLoginInterval and
SOGoFailedLoginBlockInterval defaults
SOGoFailedLoginBlockInterval defaults
- new message submissions rate-limiting options. See the new
SOGoMaximumMessageSubmissionCount, SOGoMaximumRecipientCount,
SOGoMaximumSubmissionInterval and SOGoMessageSubmissionBlockInterval defaults
Bug fixes
- Fixed decoding of the charset parameter when using single quotes (#2306)
+6
View File
@@ -103,6 +103,12 @@
- (NSDictionary *) failedCountForLogin: (NSString *) login;
- (void) setMessageSubmissionsCount: (int) theCount
recipientsCount: (int) theRecipientsCount
forLogin: (NSString *) theLogin;
- (NSDictionary *) messageSubmissionsCountForLogin: (NSString *) theLogin;
//
// CAS support
//
+71 -14
View File
@@ -24,23 +24,24 @@
/*
* [ Cache Structure ]
*
* users value = instances of SOGoUser > flushed after the completion of every SOGo requests
* groups value = instances of SOGoGroup > flushed after the completion of every SOGo requests
* imap4Connections value =
* localCache value = any value of what's in memcached - this is used to NOT query memcached within the same sogod instance
* users value = instances of SOGoUser > flushed after the completion of every SOGo requests
* groups value = instances of SOGoGroup > flushed after the completion of every SOGo requests
* imap4Connections value =
* localCache value = any value of what's in memcached - this is used to NOT query memcached within the same sogod instance
*
* [ Distributed (using memcached) cache structure ]
*
* <uid>+defaults value = NSDictionary instance > user's defaults
* <uid>+settings value = NSDictionary instance > user's settings
* <uid>+attributes value = NSMutableDictionary instance > user's LDAP attributes
* <object path>+acl value = NSDictionary instance > ACLs on an object at specified path
* <groupname>+<domain> value = NSString instance (array components separated by ",") or group member logins for a specific group in domain
* cas-id:< > value =
* cas-ticket:< > value =
* cas-pgtiou:< > value =
* session:< > value =
* <uid>+failedlogins value = NSDictionary instance holding the failed count and the date of the first failed authentication
* <uid>+defaults value = NSDictionary instance > user's defaults
* <uid>+settings value = NSDictionary instance > user's settings
* <uid>+attributes value = NSMutableDictionary instance > user's LDAP attributes
* <object path>+acl value = NSDictionary instance > ACLs on an object at specified path
* <groupname>+<domain> value = NSString instance (array components separated by ",") or group member logins for a specific group in domain
* cas-id:< > value =
* cas-ticket:< > value =
* cas-pgtiou:< > value =
* session:< > value =
* <uid>+failedlogins value = NSDictionary instance holding the failed count and the date of the first failed authentication
* <uid>+messagesubmissions value = NSDictionary instance holding the number of messages sent, and number of recipients
*/
@@ -545,6 +546,62 @@ static memcached_st *handle = NULL;
return d;
}
//
//
//
- (void) setMessageSubmissionsCount: (int) theCount
recipientsCount: (int) theRecipientsCount
forLogin: (NSString *) theLogin
{
NSNumber *messages_count, *recipients_count;
NSMutableDictionary *d;
if (theCount)
{
messages_count = [NSNumber numberWithInt: theCount];
recipients_count = [NSNumber numberWithInt: theRecipientsCount];
d = [NSMutableDictionary dictionaryWithDictionary: [self messageSubmissionsCountForLogin: theLogin]];
if (![d objectForKey: @"InitialDate"])
{
[d setObject: [NSNumber numberWithUnsignedInt: [[NSCalendarDate date] timeIntervalSince1970]] forKey: @"InitialDate"];
}
[d setObject: messages_count forKey: @"MessagesCount"];
[d setObject: recipients_count forKey: @"RecipientsCount"];
[self _cacheValues: [d jsonRepresentation]
ofType: @"messagesubmissions"
forKey: theLogin];
}
else
{
[self removeValueForKey: [NSString stringWithFormat: @"%@+messagesubmissions", theLogin]];
}
}
//
// MessagesCount ->
// RecipientsCount ->
// InitialDate ->
//
- (NSDictionary *) messageSubmissionsCountForLogin: (NSString *) theLogin
{
NSDictionary *d;
NSString *s;
s = [self _valuesOfType: @"messagesubmissions" forKey: theLogin];
d = nil;
if (s)
{
d = [s objectFromJSONString];
}
return d;
}
//
// CAS session support
+6 -1
View File
@@ -88,7 +88,12 @@
- (int) maximumFailedLoginCount;
- (int) maximumFailedLoginInterval;
- (int) failedLoginBlockInternval;
- (int) failedLoginBlockInterval;
- (int) maximumMessageSubmissionCount;
- (int) maximumRecipientCount;
- (int) maximumSubmissionInterval;
- (int) messageSubmissionBlockInterval;
@end
+40
View File
@@ -513,6 +513,9 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
return [self boolForKey: @"SOGoEnablePublicAccess"];
}
//
//
//
- (int) maximumFailedLoginCount
{
return [self integerForKey: @"SOGoMaximumFailedLoginCount"];
@@ -542,4 +545,41 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
return v;
}
//
//
//
- (int) maximumMessageSubmissionCount
{
return [self integerForKey: @"SOGoMaximumMessageSubmissionCount"];
}
- (int) maximumRecipientCount
{
return [self integerForKey: @"SOGoMaximumRecipientCount"];
}
- (int) maximumSubmissionInterval
{
int v;
v = [self integerForKey: @"SOGoMaximumSubmissionInterval"];
if (!v)
v = 30;
return v;
}
- (int) messageSubmissionBlockInterval
{
int v;
v = [self integerForKey: @"SOGoMessageSubmissionBlockInterval"];
if (!v)
v = 300;
return v;
}
@end
+64 -5
View File
@@ -46,6 +46,8 @@
#import <NGMime/NGMimeMultipartBody.h>
#import <NGMime/NGMimeType.h>
#import <SOGo/SOGoCache.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserFolder.h>
@@ -659,12 +661,57 @@ static NSArray *infoKeys = nil;
return error;
}
//
//
//
- (WOResponse *) sendAction
{
SOGoDraftObject *co;
NSDictionary *jsonResponse;
NSException *error;
NSMutableArray *errorMsg;
NSDictionary *messageSubmissions;
SOGoSystemDefaults *dd;
int messages_count, recipients_count;
messageSubmissions = [[SOGoCache sharedCache] messageSubmissionsCountForLogin: [[context activeUser] login]];
dd = [SOGoSystemDefaults sharedSystemDefaults];
messages_count = recipients_count = 0;
if (messageSubmissions)
{
unsigned int current_time, start_time, delta, block_time;
current_time = [[NSCalendarDate date] timeIntervalSince1970];
start_time = [[messageSubmissions objectForKey: @"InitialDate"] unsignedIntValue];
delta = current_time - start_time;
block_time = [dd messageSubmissionBlockInterval];
messages_count = [[messageSubmissions objectForKey: @"MessagesCount"] intValue];
recipients_count = [[messageSubmissions objectForKey: @"RecipientsCount"] intValue];
if ((messages_count >= [dd maximumMessageSubmissionCount] || recipients_count >= [dd maximumRecipientCount]) &&
delta >= [dd maximumSubmissionInterval] &&
delta <= block_time )
{
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
@"failure", @"status",
[self labelForKey: @"Tried to send too many mails. Please wait."],
@"message",
nil];
return [self responseWithStatus: 200
andString: [jsonResponse jsonRepresentation]];
}
if (delta > block_time)
{
[[SOGoCache sharedCache] setMessageSubmissionsCount: 0
recipientsCount: 0
forLogin: [[context activeUser] login]];
}
}
co = [self clientObject];
@@ -691,11 +738,23 @@ static NSArray *infoKeys = nil;
nil];
}
else
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
@"success", @"status",
[co sourceFolder], @"sourceFolder",
[NSNumber numberWithInt: [co IMAP4ID]], @"messageID",
nil];
{
jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys:
@"success", @"status",
[co sourceFolder], @"sourceFolder",
[NSNumber numberWithInt: [co IMAP4ID]], @"messageID",
nil];
recipients_count += [[co allRecipients] count];
messages_count += 1;
if ([dd maximumMessageSubmissionCount] > 0 && [dd maximumRecipientCount] > 0)
{
[[SOGoCache sharedCache] setMessageSubmissionsCount: messages_count
recipientsCount: recipients_count
forLogin: [[context activeUser] login]];
}
}
return [self responseWithStatus: 200
andString: [jsonResponse jsonRepresentation]];