David Vidal
8 years ago
committed by
Pedro M. Baeza
15 changed files with 161 additions and 470 deletions
-
55auth_brute_force/README.rst
-
1auth_brute_force/__init__.py
-
27auth_brute_force/__manifest__.py
-
42auth_brute_force/__openerp__.py
-
3auth_brute_force/controllers/__init__.py
-
104auth_brute_force/controllers/controllers.py
-
76auth_brute_force/controllers/main.py
-
26auth_brute_force/data/ir_config_parameter.xml
-
150auth_brute_force/i18n/auth_brute_force.pot
-
1auth_brute_force/models/__init__.py
-
30auth_brute_force/models/res_authentication_attempt.py
-
38auth_brute_force/models/res_banned_remote.py
-
26auth_brute_force/views/action.xml
-
26auth_brute_force/views/menu.xml
-
26auth_brute_force/views/view.xml
@ -1,3 +1,4 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
|
|||
from . import models |
|||
from . import controllers |
@ -0,0 +1,27 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2015 GRAP - Sylvain LE GAL |
|||
# Copyright 2017 Tecnativa - David Vidal |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
{ |
|||
'name': 'Authentification - Brute-force Attack', |
|||
'version': '10.0.1.0.0', |
|||
'category': 'Tools', |
|||
'summary': "Tracks Authentication Attempts and Prevents Brute-force" |
|||
" Attacks module", |
|||
'author': "GRAP, " |
|||
"Tecnativa, " |
|||
"Odoo Community Association (OCA)", |
|||
'website': 'http://www.grap.coop', |
|||
'license': 'AGPL-3', |
|||
'depends': [ |
|||
'web', |
|||
], |
|||
'data': [ |
|||
'security/ir_model_access.yml', |
|||
'data/ir_config_parameter.xml', |
|||
'views/view.xml', |
|||
'views/action.xml', |
|||
'views/menu.xml', |
|||
], |
|||
'installable': True, |
|||
} |
@ -1,42 +0,0 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Tracks Authentication Attempts and Prevents Brute-force Attacks module |
|||
# Copyright (C) 2015-Today GRAP (http://www.grap.coop) |
|||
# @author Sylvain LE GAL (https://twitter.com/legalsylvain) |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
{ |
|||
'name': 'Authentification - Brute-force Attack', |
|||
'version': '8.0.1.0.0', |
|||
'category': 'base', |
|||
'summary': "Tracks Authentication Attempts and Prevents Brute-force" |
|||
" Attacks module", |
|||
'author': "GRAP,Odoo Community Association (OCA)", |
|||
'website': 'http://www.grap.coop', |
|||
'license': 'AGPL-3', |
|||
'depends': [ |
|||
'web', |
|||
], |
|||
'data': [ |
|||
'security/ir_model_access.yml', |
|||
'data/ir_config_parameter.xml', |
|||
'views/view.xml', |
|||
'views/action.xml', |
|||
'views/menu.xml', |
|||
], |
|||
} |
@ -1,2 +1,3 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from . import controllers |
|||
|
|||
from . import main |
@ -1,104 +0,0 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Tracks Authentication Attempts and Prevents Brute-force Attacks module |
|||
# Copyright (C) 2015-Today GRAP (http://www.grap.coop) |
|||
# @author Sylvain LE GAL (https://twitter.com/legalsylvain) |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
import logging |
|||
|
|||
from openerp import fields, http, registry, SUPERUSER_ID |
|||
from openerp.http import request |
|||
from openerp.addons.web.controllers.main import Home, ensure_db |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
|
|||
class LoginController(Home): |
|||
@http.route() |
|||
def web_login(self, redirect=None, **kw): |
|||
if request.httprequest.method == 'POST': |
|||
ensure_db() |
|||
remote = request.httprequest.remote_addr |
|||
# Get registry and cursor |
|||
config_obj = registry(request.session.db)['ir.config_parameter'] |
|||
attempt_obj = registry( |
|||
request.session.db)['res.authentication.attempt'] |
|||
banned_remote_obj = registry( |
|||
request.session.db)['res.banned.remote'] |
|||
cursor = attempt_obj.pool.cursor() |
|||
|
|||
# Get Settings |
|||
max_attempts_qty = int(config_obj.search_read( |
|||
cursor, SUPERUSER_ID, |
|||
[('key', '=', 'auth_brute_force.max_attempt_qty')], |
|||
['value'])[0]['value']) |
|||
|
|||
# Test if remote user is banned |
|||
banned = banned_remote_obj.search(cursor, SUPERUSER_ID, [ |
|||
('remote', '=', remote)]) |
|||
if banned: |
|||
_logger.warning( |
|||
"Authentication tried from remote '%s'. The request has" |
|||
" been ignored because the remote has been banned after" |
|||
" %d attempts without success. Login tried : '%s'." % ( |
|||
remote, max_attempts_qty, request.params['login'])) |
|||
request.params['password'] = '' |
|||
|
|||
else: |
|||
# Try to authenticate |
|||
result = request.session.authenticate( |
|||
request.session.db, request.params['login'], |
|||
request.params['password']) |
|||
|
|||
# Log attempt |
|||
cursor.commit() |
|||
attempt_obj.create(cursor, SUPERUSER_ID, { |
|||
'attempt_date': fields.Datetime.now(), |
|||
'login': request.params['login'], |
|||
'remote': remote, |
|||
'result': banned and 'banned' or ( |
|||
result and 'successfull' or 'failed'), |
|||
}) |
|||
cursor.commit() |
|||
if not banned and not result: |
|||
# Get last bad attempts quantity |
|||
attempts_qty = len(attempt_obj.search_last_failed( |
|||
cursor, SUPERUSER_ID, remote)) |
|||
|
|||
if max_attempts_qty <= attempts_qty: |
|||
# We ban the remote |
|||
_logger.warning( |
|||
"Authentication failed from remote '%s'. " |
|||
"The remote has been banned. Login tried : '%s'." % ( |
|||
remote, request.params['login'])) |
|||
banned_remote_obj.create(cursor, SUPERUSER_ID, { |
|||
'remote': remote, |
|||
'ban_date': fields.Datetime.now(), |
|||
}) |
|||
cursor.commit() |
|||
|
|||
else: |
|||
_logger.warning( |
|||
"Authentication failed from remote '%s'." |
|||
" Login tried : '%s'. Attempt %d / %d." % ( |
|||
remote, request.params['login'], attempts_qty, |
|||
max_attempts_qty)) |
|||
cursor.close() |
|||
|
|||
return super(LoginController, self).web_login(redirect=redirect, **kw) |
@ -0,0 +1,76 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2015 GRAP - Sylvain LE GAL |
|||
# Copyright 2017 Tecnativa - David Vidal |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import logging |
|||
|
|||
from odoo import fields, http, registry, SUPERUSER_ID |
|||
from odoo.api import Environment |
|||
from odoo.http import request |
|||
from odoo.addons.web.controllers.main import Home, ensure_db |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
|
|||
class LoginController(Home): |
|||
|
|||
@http.route() |
|||
def web_login(self, redirect=None, **kw): |
|||
if request.httprequest.method == 'POST': |
|||
ensure_db() |
|||
remote = request.httprequest.remote_addr |
|||
# Get registry and cursor |
|||
with registry(request.session.db).cursor() as cursor: |
|||
env = Environment(cursor, SUPERUSER_ID, {}) |
|||
config_obj = env['ir.config_parameter'] |
|||
attempt_obj = env['res.authentication.attempt'] |
|||
banned_remote_obj = env['res.banned.remote'] |
|||
# Get Settings |
|||
max_attempts_qty = int(config_obj.get_param( |
|||
'auth_brute_force.max_attempt_qty')) |
|||
# Test if remote user is banned |
|||
banned = banned_remote_obj.search([('remote', '=', remote)]) |
|||
if banned: |
|||
request.params['password'] = '' |
|||
_logger.warning( |
|||
"Authentication tried from remote '%s'. The request " |
|||
"has been ignored because the remote has been banned " |
|||
"after %d attempts without success. Login tried : '%s'" |
|||
"." % (remote, max_attempts_qty, |
|||
request.params['login'])) |
|||
else: |
|||
# Try to authenticate |
|||
result = request.session.authenticate( |
|||
request.session.db, request.params['login'], |
|||
request.params['password']) |
|||
# Log attempt |
|||
attempt_obj.create({ |
|||
'attempt_date': fields.Datetime.now(), |
|||
'login': request.params['login'], |
|||
'remote': remote, |
|||
'result': banned and 'banned' or ( |
|||
result and 'successfull' or 'failed'), |
|||
}) |
|||
cursor.commit() |
|||
if not banned and not result: |
|||
# Get last bad attempts quantity |
|||
attempts_qty = len(attempt_obj.search_last_failed(remote)) |
|||
if max_attempts_qty <= attempts_qty: |
|||
# We ban the remote |
|||
_logger.warning( |
|||
"Authentication failed from remote '%s'. " |
|||
"The remote has been banned. Login tried : '%s'" |
|||
"." % (remote, request.params['login'])) |
|||
banned_remote_obj.sudo().create({ |
|||
'remote': remote, |
|||
'ban_date': fields.Datetime.now(), |
|||
}) |
|||
cursor.commit() |
|||
else: |
|||
_logger.warning( |
|||
"Authentication failed from remote '%s'." |
|||
" Login tried : '%s'. Attempt %d / %d." % ( |
|||
remote, request.params['login'], attempts_qty, |
|||
max_attempts_qty)) |
|||
return super(LoginController, self).web_login(redirect=redirect, **kw) |
@ -1,150 +0,0 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * auth_brute_force |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2015-09-26 00:39+0000\n" |
|||
"PO-Revision-Date: 2015-09-26 00:39+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: auth_brute_force |
|||
#: code:addons/auth_brute_force/models/res_banned_remote.py:75 |
|||
#, python-format |
|||
msgid "%s %s - %s %s (ISP: %s)" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.banned.remote,active:0 |
|||
msgid "Active" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.authentication.attempt,attempt_date:0 |
|||
msgid "Attempt Date" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: model:ir.actions.act_window,name:auth_brute_force.action_res_authentication_attempt |
|||
#: model:ir.ui.menu,name:auth_brute_force.menu_res_authentication_attempt |
|||
msgid "Authentication Attempts" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.authentication.attempt,result:0 |
|||
msgid "Authentication Result" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.banned.remote,ban_date:0 |
|||
msgid "Ban Date" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: code:addons/auth_brute_force/models/res_authentication_attempt.py:34 |
|||
#: view:res.authentication.attempt:auth_brute_force.view_res_authentication_attempt_search |
|||
#: selection:res.authentication.attempt,result:0 |
|||
#, python-format |
|||
msgid "Banned" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: model:ir.actions.act_window,name:auth_brute_force.action_res_banned_remote |
|||
#: model:ir.ui.menu,name:auth_brute_force.menu_res_banned_remote |
|||
msgid "Banned Remotes" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.authentication.attempt,create_uid:0 |
|||
#: field:res.banned.remote,create_uid:0 |
|||
msgid "Created by" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.authentication.attempt,create_date:0 |
|||
#: field:res.banned.remote,create_date:0 |
|||
msgid "Created on" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: code:addons/auth_brute_force/models/res_authentication_attempt.py:33 |
|||
#: view:res.authentication.attempt:auth_brute_force.view_res_authentication_attempt_search |
|||
#: selection:res.authentication.attempt,result:0 |
|||
#, python-format |
|||
msgid "Failed" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.authentication.attempt,id:0 |
|||
#: field:res.banned.remote,id:0 |
|||
msgid "ID" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.authentication.attempt,write_uid:0 |
|||
#: field:res.banned.remote,write_uid:0 |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.authentication.attempt,write_date:0 |
|||
#: field:res.banned.remote,write_date:0 |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.banned.remote,name:0 |
|||
msgid "Name" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.banned.remote,description:0 |
|||
msgid "Remote Description" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.authentication.attempt,remote:0 |
|||
#: field:res.banned.remote,remote:0 |
|||
msgid "Remote ID" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: view:res.authentication.attempt:auth_brute_force.view_res_authentication_attempt_search |
|||
msgid "Successful" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: code:addons/auth_brute_force/models/res_authentication_attempt.py:32 |
|||
#: selection:res.authentication.attempt,result:0 |
|||
#, python-format |
|||
msgid "Successfull" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: field:res.authentication.attempt,login:0 |
|||
msgid "Tried Login" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: help:res.banned.remote,active:0 |
|||
msgid "Uncheck this box to unban the remote" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: code:addons/auth_brute_force/models/res_banned_remote.py:77 |
|||
#, python-format |
|||
msgid "Unidentified Call from %s" |
|||
msgstr "" |
|||
|
|||
#. module: auth_brute_force |
|||
#: view:res.authentication.attempt:auth_brute_force.view_res_authentication_attempt_search |
|||
msgid "Without Success" |
|||
msgstr "" |
|||
|
@ -1,3 +1,4 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
|
|||
from . import res_banned_remote |
|||
from . import res_authentication_attempt |
Write
Preview
Loading…
Cancel
Save
Reference in new issue