merged cont.
[opensuse:yast-rest-service.git] / webservice / lib / brute_force_protection.rb
1 #--
2 # Webyast Webservice framework
3 #
4 # Copyright (C) 2009, 2010 Novell, Inc. 
5 #   This library is free software; you can redistribute it and/or modify
6 # it only under the terms of version 2.1 of the GNU Lesser General Public
7 # License as published by the Free Software Foundation. 
8 #
9 #   This library is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 
12 # details. 
13 #
14 #   You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software 
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 #++
18
19 # == Brute force Protection class
20 # === Overview
21 #
22 # Singleton class thant remember fail attempts to log to REST-SERVICE.
23 # After specified time period is failed attemps cleared. 
24
25 # === Usage
26 #
27 # When user tries to login ensure that it is not blocked by BruteForceProtection.instance.blocked?
28 # When user failed to login call BruteForceProtection.instance.fail_attempt
29
30 class BruteForceProtection
31   include Singleton
32  
33   #Specifies number of failed attempts before block
34   ATTEMPTS_TO_BLOCK=10
35
36   #Specifies timeout if user failed to login
37   TIMEOUT_ON_FAIL=2
38
39   #Specifies how long is login blocked
40   BAN_TIMEOUT=10*60 #10 minutes
41
42   #Sets initial values
43   def initialize
44     @blocking_list = {}
45   end
46
47   #Returns if login is blocked
48   def blocked?(user)
49     clean_old_block
50
51     return (@blocking_list[user] && @blocking_list[user][:blocked])
52   end
53
54   def last_failed(user)
55     return 0 unless @blocking_list[user]
56     return @blocking_list[user][:last_fail]
57   end
58
59   # notification that user fail to login
60   def fail_attempt (user)
61     clean_old_block #clean old fail attempts
62     if @blocking_list[user]
63       record = @blocking_list[user]
64       record[:last_fail] = Time.now
65       record[:count] += 1
66       record[:blocked] = record[:count] >= ATTEMPTS_TO_BLOCK
67     else
68       @blocking_list[user] = {
69         :last_fail => Time.now,
70         :count => 1,
71         :blocked => ATTEMPTS_TO_BLOCK == 1
72       }
73     end
74     
75     sleep TIMEOUT_ON_FAIL #FIXME maybe unix_chkpwd is slow enought to disable sleep
76   end
77
78   private
79
80   #Cleans failed attempts if specified time pass
81   def clean_old_block
82     @blocking_list.each_value {
83       |value|
84       if (Time.now - value[:last_fail]) > BAN_TIMEOUT
85         value[:blocked] = false
86         value[:count] = 0
87       end
88     }
89   end
90
91 end