Merge lp:~fabien-morin/unifield-web/fm-us-3366 into lp:unifield-web

Proposed by jftempo
Status: Merged
Merged at revision: 4892
Proposed branch: lp:~fabien-morin/unifield-web/fm-us-3366
Merge into: lp:unifield-web
Diff against target: 130 lines (+60/-3)
3 files modified
addons/openerp/controllers/utils.py (+6/-3)
addons/openerp/po/messages/fr.po (+4/-0)
addons/openerp/utils/rpc.py (+50/-0)
To merge this branch: bzr merge lp:~fabien-morin/unifield-web/fm-us-3366
Reviewer Review Type Date Requested Status
UniField Dev Team Pending
Review via email: mp+330432@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'addons/openerp/controllers/utils.py'
2--- addons/openerp/controllers/utils.py 2016-11-17 15:29:41 +0000
3+++ addons/openerp/controllers/utils.py 2017-09-08 14:23:30 +0000
4@@ -192,14 +192,14 @@
5 if action == 'login' and login_ret == -2:
6 return login(cherrypy.request.path_info, message=_('Database newer than UniField version'),
7 db=db, user=user, action=action, origArgs=get_orig_args(kw))
8- if action == 'login' and login_ret == -3:
9+ elif action == 'login' and login_ret == -3:
10 nb_mod = rpc.session.number_update_modules(db) or ''
11 return login(cherrypy.request.path_info, message=_('The server is updating %s modules, please wait ...') % (nb_mod,),
12 db=db, user=user, action=action, origArgs=get_orig_args(kw))
13- if action == 'login' and login_ret == -4:
14+ elif action == 'login' and login_ret == -4:
15 return login(cherrypy.request.path_info, message=_('A script during patch failed! Login is forbidden for the moment. Please contact your administrator'),
16 db=db, user=user, action=action, origArgs=get_orig_args(kw))
17- if action == 'login' and login_ret == -5: # must change password
18+ elif action == 'login' and login_ret == -5: # must change password
19 if 'confirm_password' in kw:
20 message = rpc.session.change_password(db, user, password, kw['new_password'], kw['confirm_password'])
21 if message is not True:
22@@ -220,6 +220,9 @@
23 db=db, user=user, password=password, action=action, origArgs=get_orig_args(kw))
24 clear_change_password_fields(kw)
25 return result
26+ elif action == 'login' and login_ret == -6:
27+ return login(cherrypy.request.path_info, message=_('Your IP have been blocked because of too many bad login attempts. It will be unblocked after %s minutes.') % rpc.BLOCKED_MIN_COUNT,
28+ db=db, user=user, action=action, origArgs=get_orig_args(kw))
29 elif login_ret <= 0:
30 # Bad login attempt
31 if action == 'login':
32
33=== modified file 'addons/openerp/po/messages/fr.po'
34--- addons/openerp/po/messages/fr.po 2017-01-02 14:59:00 +0000
35+++ addons/openerp/po/messages/fr.po 2017-09-08 14:23:30 +0000
36@@ -517,6 +517,10 @@
37 msgid "Bad username or password"
38 msgstr "Mauvais nom d'utilisateur ou mot de passe"
39
40+#: controllers/utils.py:127 controllers/templates/login_ajax.mako:111
41+msgid "Your IP have been blocked because of too many bad login attempts. It will be unblocked after %s minutes."
42+msgstr "Votre IP à été bloquée à cause d'un trop grand nombre de connexion infructueuses. Elle sera débloquée dans %s minutes."
43+
44 #: controllers/view_log.py:32
45 msgid "ID"
46 msgstr "ID"
47
48=== modified file 'addons/openerp/utils/rpc.py'
49--- addons/openerp/utils/rpc.py 2017-01-02 14:59:00 +0000
50+++ addons/openerp/utils/rpc.py 2017-09-08 14:23:30 +0000
51@@ -28,8 +28,14 @@
52
53 from tiny_socket import TinySocket
54 from tiny_socket import TinySocketError
55+from datetime import datetime, timedelta
56+from collections import OrderedDict
57 import cherrypy
58
59+MAX_IP_COLLECTION = 50
60+BLOCKED_MIN_COUNT = 5
61+MAX_BAD_LOGIN = 5
62+
63
64 class NotLoggedIn(openobject.errors.TinyError, openobject.errors.AuthenticationError): pass
65
66@@ -226,6 +232,8 @@
67 """
68
69 __slots__ = ['host', 'port', 'protocol', 'storage', 'gateway']
70+ connection_attempt_dict = OrderedDict() # an OrderedDict is needed here to
71+ # be able to remove old entries
72
73 def __init__(self, host, port, protocol='socket', storage={}):
74 """Create new instance of RPCSession.
75@@ -300,13 +308,55 @@
76 def number_update_modules(self, db):
77 return self.execute_noauth('common', 'number_update_modules', db)
78
79+ def unban(self, ip_address):
80+ if ip_address in self.connection_attempt_dict:
81+ self.connection_attempt_dict.pop(ip_address)
82+
83+ def is_banned(self, ip_address):
84+ '''Return True if the ip is banned and cannot retry login before a
85+ certain amount of time
86+ '''
87+ if ip_address in self.connection_attempt_dict and\
88+ self.connection_attempt_dict[ip_address].get('fail_login_count') >= MAX_BAD_LOGIN:
89+ current_time = datetime.now()
90+ last_attempt_time = self.connection_attempt_dict[ip_address]['last_attempt_time']
91+
92+ if current_time - last_attempt_time > timedelta(minutes=BLOCKED_MIN_COUNT):
93+ # the ip ban is over, delete it from the dict
94+ self.unban(ip_address)
95+ return False
96+ return True
97+ else:
98+ return False
99+
100+ def fail_login_attempt(self, ip_address):
101+ '''Increase the counter of fail login attemps'''
102+ if ip_address in self.connection_attempt_dict:
103+ self.connection_attempt_dict[ip_address]['fail_login_count'] += 1
104+ else:
105+ if len(self.connection_attempt_dict) >= MAX_IP_COLLECTION:
106+ # the max size of the dict has been reach. To prevent fulfill
107+ # the memory by multi-ip login attempt, remove the first imput
108+ # element (FIFO) before to add a new one.
109+ self.connection_attempt_dict.popitem(last=False)
110+ self.connection_attempt_dict[ip_address] = {'fail_login_count': 1}
111+ self.connection_attempt_dict[ip_address]['last_attempt_time'] = datetime.now()
112+
113 def login(self, db, user, password):
114
115 if not (db and user and password):
116 return -1
117
118+ client_ip = cherrypy.request.wsgi_environ.get('HTTP_X_FORWARDED_FOR', 'NO_IP_FOUND')
119+ if self.is_banned(client_ip):
120+ return -6
121+
122 try:
123 uid = self.execute_noauth('common', 'login', db, user, password)
124+ if uid is False:
125+ self.fail_login_attempt(client_ip)
126+ else:
127+ self.unban(client_ip)
128 except Exception, e:
129 if e.title == 'updater.py':
130 return -2

Subscribers

People subscribed via source and target branches