Sylvain LE GAL
9 years ago
committed by
Pedro M. Baeza
18 changed files with 893 additions and 0 deletions
-
114auth_brute_force/README.rst
-
3auth_brute_force/__init__.py
-
42auth_brute_force/__openerp__.py
-
2auth_brute_force/controllers/__init__.py
-
104auth_brute_force/controllers/controllers.py
-
29auth_brute_force/data/ir_config_parameter.xml
-
150auth_brute_force/i18n/auth_brute_force.pot
-
120auth_brute_force/i18n/fr.po
-
3auth_brute_force/models/__init__.py
-
58auth_brute_force/models/res_authentication_attempt.py
-
71auth_brute_force/models/res_banned_remote.py
-
28auth_brute_force/security/ir_model_access.yml
-
BINauth_brute_force/static/description/icon.png
-
BINauth_brute_force/static/description/screenshot_attempts_list.png
-
BINauth_brute_force/static/description/screenshot_custom_ban.png
-
39auth_brute_force/views/action.xml
-
32auth_brute_force/views/menu.xml
-
98auth_brute_force/views/view.xml
@ -0,0 +1,114 @@ |
|||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
||||
|
:alt: License |
||||
|
|
||||
|
=============================================================== |
||||
|
Tracks Authentication Attempts and Prevents Brute-force Attacks |
||||
|
=============================================================== |
||||
|
|
||||
|
This module registers each request done by users trying to authenticate into |
||||
|
Odoo. If the authentication fails, a counter is increased for the given remote |
||||
|
IP. After a defined number of attempts, Odoo will ban the remote IP and |
||||
|
ignore new requests. |
||||
|
This module applies security through obscurity |
||||
|
(https://en.wikipedia.org/wiki/Security_through_obscurity), |
||||
|
When a user is banned, the request is now considered as an attack. So, the UI |
||||
|
will **not** indicate to the user that his IP is banned and the regular message |
||||
|
'Wrong login/password' is displayed. |
||||
|
|
||||
|
This module realizes a call to a web API (http://ip-api.com) to try to have |
||||
|
extra information about remote IP. |
||||
|
|
||||
|
Known issue / Roadmap |
||||
|
--------------------- |
||||
|
The ID used to identify a remote request is the IP provided in the request |
||||
|
(key 'REMOTE_ADDR'). |
||||
|
Depending of server and / or user network configuration, the idenfication |
||||
|
of the user can be wrong, and mainly in the following cases: |
||||
|
|
||||
|
* if the Odoo server is behind an Apache / NGinx proxy without redirection, |
||||
|
all the request will be have the value '127.0.0.1' for the REMOTE_ADDR key; |
||||
|
* If some users are behind the same Internet Service Provider, if a user is |
||||
|
banned, all the other users will be banned too; |
||||
|
|
||||
|
Configuration |
||||
|
------------- |
||||
|
|
||||
|
Once installed, you can change the ir.config_parameter value for the key |
||||
|
'auth_brute_force.max_attempt_qty' (10 by default) that define the max number |
||||
|
of attempts allowed before the user was banned. |
||||
|
|
||||
|
Usage |
||||
|
----- |
||||
|
|
||||
|
Admin user have the possibility to unblock a banned IP. |
||||
|
|
||||
|
Logging |
||||
|
------- |
||||
|
|
||||
|
This module generates some WARNING logs, in the three following cases: |
||||
|
|
||||
|
* Authentication failed from remote '127.0.0.1'. Login tried : 'admin'. |
||||
|
Attempt 1 / 10. |
||||
|
|
||||
|
* Authentication failed from remote '127.0.0.1'. The remote has been banned. |
||||
|
Login tried : 'admin'. |
||||
|
|
||||
|
* Authentication tried from remote '127.0.0.1'. The request has been ignored |
||||
|
because the remote has been banned after 10 attempts without success. Login |
||||
|
tried : 'admin'. |
||||
|
|
||||
|
Screenshot |
||||
|
---------- |
||||
|
|
||||
|
**List of Attempts** |
||||
|
|
||||
|
.. image:: /auth_brute_force/static/description/screenshot_attempts_list.png |
||||
|
|
||||
|
**Detail of a banned IP** |
||||
|
|
||||
|
.. image:: /auth_brute_force/static/description/screenshot_custom_ban.png |
||||
|
|
||||
|
|
||||
|
Usage |
||||
|
===== |
||||
|
|
||||
|
* go to ... |
||||
|
|
||||
|
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas |
||||
|
:alt: Try me on Runbot |
||||
|
:target: https://runbot.odoo-community.org/runbot/149/8.0 |
||||
|
|
||||
|
For further information, please visit: |
||||
|
|
||||
|
* https://www.odoo.com/forum/help-1 |
||||
|
|
||||
|
Bug Tracker |
||||
|
=========== |
||||
|
|
||||
|
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_. |
||||
|
In case of trouble, please check there if your issue has already been reported. |
||||
|
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback |
||||
|
`here <https://github.com/OCA/web/issues/new?body=module:%20auth_brute_force%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. |
||||
|
|
||||
|
Credits |
||||
|
======= |
||||
|
|
||||
|
Contributors |
||||
|
------------ |
||||
|
|
||||
|
* Sylvain LE GAL (https://twitter.com/legalsylvain) |
||||
|
|
||||
|
Maintainer |
||||
|
---------- |
||||
|
|
||||
|
.. image:: http://odoo-community.org/logo.png |
||||
|
:alt: Odoo Community Association |
||||
|
:target: http://odoo-community.org |
||||
|
|
||||
|
This module is maintained by the OCA. |
||||
|
|
||||
|
OCA, or the Odoo Community Association, is a nonprofit organization whose |
||||
|
mission is to support the collaborative development of Odoo features and |
||||
|
promote its widespread use. |
||||
|
|
||||
|
To contribute to this module, please visit http://odoo-community.org. |
@ -0,0 +1,3 @@ |
|||||
|
# -*- encoding: utf-8 -*- |
||||
|
from . import models |
||||
|
from . import controllers |
@ -0,0 +1,42 @@ |
|||||
|
# -*- 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', |
||||
|
], |
||||
|
} |
@ -0,0 +1,2 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from . import controllers |
@ -0,0 +1,104 @@ |
|||||
|
# -*- 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,29 @@ |
|||||
|
<?xml version="1.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/>. --> |
||||
|
<!-- ********************************************************************** --> |
||||
|
<openerp> |
||||
|
<data noupdate="1"> |
||||
|
|
||||
|
<record id="max_attempt_qty" model="ir.config_parameter"> |
||||
|
<field name="key">auth_brute_force.max_attempt_qty</field> |
||||
|
<field name="value">10</field> |
||||
|
</record> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,150 @@ |
|||||
|
# 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 "" |
||||
|
|
@ -0,0 +1,120 @@ |
|||||
|
# 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:34+0000\n" |
||||
|
"PO-Revision-Date: 2015-09-26 00:34+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 "%s %s - %s %s (FAI : %s)" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: field:res.banned.remote,active:0 |
||||
|
msgid "Active" |
||||
|
msgstr "Active" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: field:res.authentication.attempt,attempt_date:0 |
||||
|
msgid "Attempt Date" |
||||
|
msgstr "Date de la tentative" |
||||
|
|
||||
|
#. 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 "Tentative d'authentification" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: field:res.authentication.attempt,result:0 |
||||
|
msgid "Authentication Result" |
||||
|
msgstr "Résultat de l'authentification" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: field:res.banned.remote,ban_date:0 |
||||
|
msgid "Ban Date" |
||||
|
msgstr "Ban Date" |
||||
|
|
||||
|
#. 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 "Banni" |
||||
|
|
||||
|
#. 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 "Clients distants bannis" |
||||
|
|
||||
|
#. 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 "Echoué" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: field:res.banned.remote,name:0 |
||||
|
msgid "Name" |
||||
|
msgstr "Nom" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: field:res.banned.remote,description:0 |
||||
|
msgid "Description" |
||||
|
msgstr "Description" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: field:res.authentication.attempt,remote:0 |
||||
|
#: field:res.banned.remote,remote:0 |
||||
|
msgid "Remote ID" |
||||
|
msgstr "ID du client Distant" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: view:res.authentication.attempt:auth_brute_force.view_res_authentication_attempt_search |
||||
|
msgid "Successful" |
||||
|
msgstr "Réussie" |
||||
|
|
||||
|
#. 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 "Réussie" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: field:res.authentication.attempt,login:0 |
||||
|
msgid "Tried Login" |
||||
|
msgstr "Idenfiant essayé" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: help:res.banned.remote,active:0 |
||||
|
msgid "Uncheck this box to unban the remote" |
||||
|
msgstr "Décochez cette case afin d'annuler l'exclusion du client distant" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: code:addons/auth_brute_force/models/res_banned_remote.py:77 |
||||
|
#, python-format |
||||
|
msgid "Unidentified Call from %s" |
||||
|
msgstr "Appel non identifié depuis %s" |
||||
|
|
||||
|
#. module: auth_brute_force |
||||
|
#: view:res.authentication.attempt:auth_brute_force.view_res_authentication_attempt_search |
||||
|
msgid "Without Success" |
||||
|
msgstr "Sans succès" |
||||
|
|
@ -0,0 +1,3 @@ |
|||||
|
# -*- encoding: utf-8 -*- |
||||
|
from . import res_banned_remote |
||||
|
from . import res_authentication_attempt |
@ -0,0 +1,58 @@ |
|||||
|
# -*- 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/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
from openerp import models, fields, api |
||||
|
from openerp.tools.translate import _ |
||||
|
|
||||
|
|
||||
|
class ResAuthenticationAttempt(models.Model): |
||||
|
_name = 'res.authentication.attempt' |
||||
|
_order = 'attempt_date desc' |
||||
|
|
||||
|
_ATTEMPT_RESULT = [ |
||||
|
('successfull', _('Successfull')), |
||||
|
('failed', _('Failed')), |
||||
|
('banned', _('Banned')), |
||||
|
] |
||||
|
|
||||
|
# Column Section |
||||
|
attempt_date = fields.Datetime(string='Attempt Date') |
||||
|
|
||||
|
login = fields.Char(string='Tried Login') |
||||
|
|
||||
|
remote = fields.Char(string='Remote ID') |
||||
|
|
||||
|
result = fields.Selection( |
||||
|
selection=_ATTEMPT_RESULT, string='Authentication Result') |
||||
|
|
||||
|
# Custom Section |
||||
|
@api.model |
||||
|
def search_last_failed(self, remote): |
||||
|
last_ok = self.search( |
||||
|
[('result', '=', 'successfull'), ('remote', '=', remote)], |
||||
|
order='attempt_date desc', limit=1) |
||||
|
if last_ok: |
||||
|
return self.search([ |
||||
|
('remote', '=', remote), |
||||
|
('attempt_date', '>', last_ok.attempt_date)]) |
||||
|
else: |
||||
|
return self.search([('remote', '=', remote)]) |
@ -0,0 +1,71 @@ |
|||||
|
# -*- 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 urllib |
||||
|
import json |
||||
|
|
||||
|
from openerp import models, fields, api |
||||
|
|
||||
|
|
||||
|
class ResBannedRemote(models.Model): |
||||
|
_name = 'res.banned.remote' |
||||
|
_rec_name = 'remote' |
||||
|
|
||||
|
_GEOLOCALISATION_URL = "http://ip-api.com/json/{}" |
||||
|
|
||||
|
# Default Section |
||||
|
def _default_ban_date(self): |
||||
|
return fields.Datetime.now() |
||||
|
|
||||
|
# Column Section |
||||
|
description = fields.Text( |
||||
|
string='Description', compute='_compute_description', store=True) |
||||
|
|
||||
|
ban_date = fields.Datetime( |
||||
|
string='Ban Date', required=True, default=_default_ban_date) |
||||
|
|
||||
|
remote = fields.Char(string='Remote ID', required=True) |
||||
|
|
||||
|
active = fields.Boolean( |
||||
|
string='Active', help="Uncheck this box to unban the remote", |
||||
|
default=True) |
||||
|
|
||||
|
attempt_ids = fields.Many2many( |
||||
|
comodel_name='res.authentication.attempt', string='Attempts', |
||||
|
compute='_compute_attempt_ids') |
||||
|
|
||||
|
# Compute Section |
||||
|
@api.multi |
||||
|
@api.depends('remote') |
||||
|
def _compute_description(self): |
||||
|
for item in self: |
||||
|
url = self._GEOLOCALISATION_URL.format(item.remote) |
||||
|
res = json.loads(urllib.urlopen(url).read()) |
||||
|
item.description = '' |
||||
|
for k, v in res.iteritems(): |
||||
|
item.description += '%s : %s\n' % (k, v) |
||||
|
|
||||
|
@api.multi |
||||
|
def _compute_attempt_ids(self): |
||||
|
for item in self: |
||||
|
attempt_obj = self.env['res.authentication.attempt'] |
||||
|
item.attempt_ids = attempt_obj.search_last_failed(item.remote).ids |
@ -0,0 +1,28 @@ |
|||||
|
# -*- encoding: utf-8 -*- |
||||
|
- !record {model: ir.model.access, id: access_res_authentication_attempt_all}: |
||||
|
model_id: model_res_authentication_attempt |
||||
|
name: Authentication Attempt All Users |
||||
|
perm_read: true |
||||
|
|
||||
|
- !record {model: ir.model.access, id: access_res_banned_remote_all}: |
||||
|
model_id: model_res_banned_remote |
||||
|
name: Banned Remote All Users |
||||
|
perm_read: true |
||||
|
|
||||
|
- !record {model: ir.model.access, id: access_res_authentication_attempt_manager}: |
||||
|
group_id: base.group_system |
||||
|
model_id: model_res_authentication_attempt |
||||
|
name: Authentication Attempt Manager |
||||
|
perm_create: true |
||||
|
perm_read: true |
||||
|
perm_write: true |
||||
|
perm_unlink: true |
||||
|
|
||||
|
- !record {model: ir.model.access, id: access_res_banned_remote_manager}: |
||||
|
group_id: base.group_system |
||||
|
model_id: model_res_banned_remote |
||||
|
name: Banned Remote Manager |
||||
|
perm_create: true |
||||
|
perm_read: true |
||||
|
perm_write: true |
||||
|
perm_unlink: true |
After Width: 128 | Height: 128 | Size: 9.2 KiB |
After Width: 915 | Height: 262 | Size: 29 KiB |
After Width: 601 | Height: 331 | Size: 31 KiB |
@ -0,0 +1,39 @@ |
|||||
|
<?xml version="1.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/>. --> |
||||
|
<!-- ********************************************************************** --> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
|
||||
|
<record id="action_res_authentication_attempt" model="ir.actions.act_window"> |
||||
|
<field name="name">Authentication Attempts</field> |
||||
|
<field name="res_model">res.authentication.attempt</field> |
||||
|
<field name="view_type">form</field> |
||||
|
<field name="view_mode">tree,graph</field> |
||||
|
<field name="context">{"search_default_filter_no_success":1}</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="action_res_banned_remote" model="ir.actions.act_window"> |
||||
|
<field name="name">Banned Remotes</field> |
||||
|
<field name="res_model">res.banned.remote</field> |
||||
|
<field name="view_type">form</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
</record> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,32 @@ |
|||||
|
<?xml version="1.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/>. --> |
||||
|
<!-- ********************************************************************** --> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
|
||||
|
<menuitem id="menu_res_authentication_attempt" |
||||
|
parent="base.menu_users" |
||||
|
action="action_res_authentication_attempt"/> |
||||
|
|
||||
|
<menuitem id="menu_res_banned_remote" |
||||
|
parent="base.menu_users" |
||||
|
action="action_res_banned_remote"/> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,98 @@ |
|||||
|
<?xml version="1.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/>. --> |
||||
|
<!-- ********************************************************************** --> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
|
||||
|
<!-- Model: res.authentication.attempt --> |
||||
|
<record id="view_res_authentication_attempt_tree" model="ir.ui.view"> |
||||
|
<field name="model">res.authentication.attempt</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree colors="orange: result == 'failed';red: result == 'banned'"> |
||||
|
<field name="attempt_date" /> |
||||
|
<field name="remote" /> |
||||
|
<field name="login" /> |
||||
|
<field name="result" /> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="view_res_authentication_attempt_graph" model="ir.ui.view"> |
||||
|
<field name="model">res.authentication.attempt</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<graph> |
||||
|
<field name="attempt_date" /> |
||||
|
<field name="result" /> |
||||
|
</graph> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="view_res_authentication_attempt_search" model="ir.ui.view"> |
||||
|
<field name="model">res.authentication.attempt</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<search> |
||||
|
<field name="login"/> |
||||
|
<filter name="filter_no_success" string="Without Success" domain="[('result','!=', 'successfull')]"/> |
||||
|
<filter name="filter_banned" string="Banned" domain="[('result','=', 'banned')]"/> |
||||
|
<filter name="filter_failed" string="Failed" domain="[('result','=', 'failed')]"/> |
||||
|
<filter name="filter_successful" string="Successful" domain="[('result','=', 'successfull')]"/> |
||||
|
</search> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<!-- Model: res.banned.remote --> |
||||
|
<record id="view_res_banned_remote_tree" model="ir.ui.view"> |
||||
|
<field name="model">res.banned.remote</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree colors="gray: active==False"> |
||||
|
<field name="remote" /> |
||||
|
<field name="ban_date" /> |
||||
|
<field name="active" invisible="1" /> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="view_res_banned_remote_form" model="ir.ui.view"> |
||||
|
<field name="model">res.banned.remote</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form> |
||||
|
<sheet> |
||||
|
<group> |
||||
|
<field name="remote" /> |
||||
|
<field name="ban_date" /> |
||||
|
<field name="active" /> |
||||
|
<field name="description" /> |
||||
|
<field name="attempt_ids" /> |
||||
|
</group> |
||||
|
</sheet> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="view_res_banned_remote_search" model="ir.ui.view"> |
||||
|
<field name="model">res.banned.remote</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<search> |
||||
|
<field name="remote"/> |
||||
|
</search> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue