@@ -29,8 +29,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
29
using System . Collections . Generic ;
30
30
using System . ComponentModel ;
31
31
using System . Diagnostics . CodeAnalysis ;
32
+ using System . Globalization ;
32
33
using System . IO ;
33
34
using System . Linq ;
35
+ using System . Net ;
34
36
using System . Text ;
35
37
using System . Text . RegularExpressions ;
36
38
using System . Xml ;
@@ -43,7 +45,7 @@ namespace DigitalRuby.IPBanCore
43
45
/// <summary>
44
46
/// Configuration for ip ban app
45
47
/// </summary>
46
- public class IPBanConfig : IIsWhitelisted
48
+ public sealed class IPBanConfig : IIsWhitelisted
47
49
{
48
50
/// <summary>
49
51
/// Allow temporary change of config
@@ -124,6 +126,7 @@ public void Dispose()
124
126
private readonly bool clearBannedIPAddressesOnRestart ;
125
127
private readonly bool clearFailedLoginsOnSuccessfulLogin ;
126
128
private readonly bool processInternalIPAddresses ;
129
+ private readonly string truncateUserNameChars ;
127
130
private readonly HashSet < string > userNameWhitelist = new ( StringComparer . Ordinal ) ;
128
131
private readonly int userNameWhitelistMaximumEditDistance = 2 ;
129
132
private readonly Regex userNameWhitelistRegex ;
@@ -190,6 +193,8 @@ private IPBanConfig(XmlDocument doc, IDnsLookup dns = null, IDnsServerList dnsLi
190
193
TryGetConfig < bool > ( "ClearBannedIPAddressesOnRestart" , ref clearBannedIPAddressesOnRestart ) ;
191
194
TryGetConfig < bool > ( "ClearFailedLoginsOnSuccessfulLogin" , ref clearFailedLoginsOnSuccessfulLogin ) ;
192
195
TryGetConfig < bool > ( "ProcessInternalIPAddresses" , ref processInternalIPAddresses ) ;
196
+ TryGetConfig < string > ( "TruncateUserNameChars" , ref truncateUserNameChars ) ;
197
+ IPBanRegexParser . Instance . TruncateUserNameChars = truncateUserNameChars ;
193
198
GetConfig < TimeSpan > ( "ExpireTime" , ref expireTime , TimeSpan . Zero , maxBanTimeSpan ) ;
194
199
if ( expireTime . TotalMinutes < 1.0 )
195
200
{
@@ -403,138 +408,6 @@ private void ParseFirewallBlockRules()
403
408
}
404
409
}
405
410
406
- /// <summary>
407
- /// Validate a regex - returns an error otherwise empty string if success
408
- /// </summary>
409
- /// <param name="regex">Regex to validate, can be null or empty</param>
410
- /// <param name="options">Regex options</param>
411
- /// <param name="throwException">True to throw the exception instead of returning the string, false otherwise</param>
412
- /// <returns>Null if success, otherwise an error string indicating the problem</returns>
413
- public static string ValidateRegex ( string regex , RegexOptions options = RegexOptions . IgnoreCase | RegexOptions . CultureInvariant , bool throwException = false )
414
- {
415
- try
416
- {
417
- if ( regex != null )
418
- {
419
- _ = new Regex ( regex , options ) ;
420
- }
421
- return null ;
422
- }
423
- catch ( Exception ex )
424
- {
425
- if ( throwException )
426
- {
427
- throw ;
428
- }
429
- return ex . Message ;
430
- }
431
- }
432
-
433
- private static readonly Dictionary < string , Regex > regexCacheCompiled = new ( ) ;
434
- private static readonly Dictionary < string , Regex > regexCacheNotCompiled = new ( ) ;
435
-
436
- /// <summary>
437
- /// Get a regex from text
438
- /// </summary>
439
- /// <param name="text">Text</param>
440
- /// <param name="multiline">Whether to use multi-line regex, default is false which is single line</param>
441
- /// <returns>Regex or null if text is null or whitespace</returns>
442
- public static Regex ParseRegex ( string text , bool multiline = false )
443
- {
444
- const int maxCacheSize = 200 ;
445
-
446
- text = ( text ?? string . Empty ) . Trim ( ) ;
447
- if ( text . Length == 0 )
448
- {
449
- return null ;
450
- }
451
-
452
- string [ ] lines = text . Split ( '\n ' , StringSplitOptions . TrimEntries | StringSplitOptions . RemoveEmptyEntries ) ;
453
- StringBuilder sb = new ( ) ;
454
- foreach ( string line in lines )
455
- {
456
- sb . Append ( line ) ;
457
- }
458
- RegexOptions options = RegexOptions . IgnoreCase | RegexOptions . CultureInvariant | RegexOptions . Compiled ;
459
- if ( multiline )
460
- {
461
- options |= RegexOptions . Multiline ;
462
- }
463
- string sbText = sb . ToString ( ) ;
464
- string cacheKey = ( ( uint ) options ) . ToString ( "X8" ) + ":" + sbText ;
465
-
466
- // allow up to maxCacheSize compiled dynamic regular expression, with minimal config changes/reload, this should last the lifetime of an app
467
- lock ( regexCacheCompiled )
468
- {
469
- if ( regexCacheCompiled . TryGetValue ( cacheKey , out Regex value ) )
470
- {
471
- return value ;
472
- }
473
- else if ( regexCacheCompiled . Count < maxCacheSize )
474
- {
475
- value = new Regex ( sbText , options ) ;
476
- regexCacheCompiled . Add ( cacheKey , value ) ;
477
- return value ;
478
- }
479
- }
480
-
481
- // have to fall-back to non-compiled regex to avoid run-away memory usage
482
- try
483
- {
484
- lock ( regexCacheNotCompiled )
485
- {
486
- if ( regexCacheNotCompiled . TryGetValue ( cacheKey , out Regex value ) )
487
- {
488
- return value ;
489
- }
490
-
491
- // strip compiled flag
492
- options &= ( ~ RegexOptions . Compiled ) ;
493
- value = new Regex ( sbText , options ) ;
494
- regexCacheNotCompiled . Add ( cacheKey , value ) ;
495
- return value ;
496
- }
497
- }
498
- finally
499
- {
500
- // clear non-compield regex cache if it exceeds max size
501
- lock ( regexCacheNotCompiled )
502
- {
503
- if ( regexCacheNotCompiled . Count > maxCacheSize )
504
- {
505
- regexCacheNotCompiled . Clear ( ) ;
506
- }
507
- }
508
- }
509
- }
510
-
511
- /// <summary>
512
- /// Clean a multi-line string to make it more readable
513
- /// </summary>
514
- /// <param name="text">Multi-line string</param>
515
- /// <returns>Cleaned multi-line string</returns>
516
- public static string CleanMultilineString ( string text )
517
- {
518
- text = ( text ?? string . Empty ) . Trim ( ) ;
519
- if ( text . Length == 0 )
520
- {
521
- return string . Empty ;
522
- }
523
-
524
- string [ ] lines = text . Split ( '\n ' , StringSplitOptions . RemoveEmptyEntries ) ;
525
- StringBuilder sb = new ( ) ;
526
- foreach ( string line in lines )
527
- {
528
- string trimmedLine = line . Trim ( ) ;
529
- if ( trimmedLine . Length != 0 )
530
- {
531
- sb . Append ( trimmedLine ) ;
532
- sb . Append ( '\n ' ) ;
533
- }
534
- }
535
- return sb . ToString ( ) . Trim ( ) ;
536
- }
537
-
538
411
/// <inheritdoc />
539
412
public override string ToString ( )
540
413
{
@@ -1005,6 +878,11 @@ appSettingsOverride is not null &&
1005
878
/// </summary>
1006
879
public IIPBanFilter BlacklistFilter => blacklistFilter ;
1007
880
881
+ /// <summary>
882
+ /// Characters to truncate user names at, empty for no truncation
883
+ /// </summary>
884
+ public string TruncateUserNameChars { get { return truncateUserNameChars ; } }
885
+
1008
886
/// <summary>
1009
887
/// White list user names. Any user name found not in the list is banned, unless the list is empty, in which case no checking is done.
1010
888
/// If not empty, Any user name within 'UserNameWhitelistMinimumEditDistance' in the config is also not banned.
0 commit comments