diff --git a/auth_brute_force/README.rst b/auth_brute_force/README.rst new file mode 100644 index 000000000..60b0a73aa --- /dev/null +++ b/auth_brute_force/README.rst @@ -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 `_. +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 `_. + +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. diff --git a/auth_brute_force/__init__.py b/auth_brute_force/__init__.py new file mode 100644 index 000000000..b8166bd36 --- /dev/null +++ b/auth_brute_force/__init__.py @@ -0,0 +1,3 @@ +# -*- encoding: utf-8 -*- +from . import models +from . import controllers diff --git a/auth_brute_force/__openerp__.py b/auth_brute_force/__openerp__.py new file mode 100644 index 000000000..b05790164 --- /dev/null +++ b/auth_brute_force/__openerp__.py @@ -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 . +# +############################################################################## + +{ + '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', + ], +} diff --git a/auth_brute_force/controllers/__init__.py b/auth_brute_force/controllers/__init__.py new file mode 100644 index 000000000..153a9e31e --- /dev/null +++ b/auth_brute_force/controllers/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import controllers diff --git a/auth_brute_force/controllers/controllers.py b/auth_brute_force/controllers/controllers.py new file mode 100644 index 000000000..f752eee95 --- /dev/null +++ b/auth_brute_force/controllers/controllers.py @@ -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 . +# +############################################################################## + +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) diff --git a/auth_brute_force/data/ir_config_parameter.xml b/auth_brute_force/data/ir_config_parameter.xml new file mode 100644 index 000000000..0eab93cd2 --- /dev/null +++ b/auth_brute_force/data/ir_config_parameter.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + auth_brute_force.max_attempt_qty + 10 + + + + diff --git a/auth_brute_force/i18n/auth_brute_force.pot b/auth_brute_force/i18n/auth_brute_force.pot new file mode 100644 index 000000000..52f9bf6f4 --- /dev/null +++ b/auth_brute_force/i18n/auth_brute_force.pot @@ -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 "" + diff --git a/auth_brute_force/i18n/fr.po b/auth_brute_force/i18n/fr.po new file mode 100644 index 000000000..7e28872e4 --- /dev/null +++ b/auth_brute_force/i18n/fr.po @@ -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" + diff --git a/auth_brute_force/models/__init__.py b/auth_brute_force/models/__init__.py new file mode 100644 index 000000000..85cc3145c --- /dev/null +++ b/auth_brute_force/models/__init__.py @@ -0,0 +1,3 @@ +# -*- encoding: utf-8 -*- +from . import res_banned_remote +from . import res_authentication_attempt diff --git a/auth_brute_force/models/res_authentication_attempt.py b/auth_brute_force/models/res_authentication_attempt.py new file mode 100644 index 000000000..84e735bd3 --- /dev/null +++ b/auth_brute_force/models/res_authentication_attempt.py @@ -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 . +# +############################################################################## + +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)]) diff --git a/auth_brute_force/models/res_banned_remote.py b/auth_brute_force/models/res_banned_remote.py new file mode 100644 index 000000000..661dfc8a5 --- /dev/null +++ b/auth_brute_force/models/res_banned_remote.py @@ -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 . +# +############################################################################## + +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 diff --git a/auth_brute_force/security/ir_model_access.yml b/auth_brute_force/security/ir_model_access.yml new file mode 100644 index 000000000..57919b774 --- /dev/null +++ b/auth_brute_force/security/ir_model_access.yml @@ -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 diff --git a/auth_brute_force/static/description/icon.png b/auth_brute_force/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/auth_brute_force/static/description/icon.png differ diff --git a/auth_brute_force/static/description/screenshot_attempts_list.png b/auth_brute_force/static/description/screenshot_attempts_list.png new file mode 100644 index 000000000..7ee6f940f Binary files /dev/null and b/auth_brute_force/static/description/screenshot_attempts_list.png differ diff --git a/auth_brute_force/static/description/screenshot_custom_ban.png b/auth_brute_force/static/description/screenshot_custom_ban.png new file mode 100644 index 000000000..8607f6402 Binary files /dev/null and b/auth_brute_force/static/description/screenshot_custom_ban.png differ diff --git a/auth_brute_force/views/action.xml b/auth_brute_force/views/action.xml new file mode 100644 index 000000000..7b19a7e90 --- /dev/null +++ b/auth_brute_force/views/action.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + Authentication Attempts + res.authentication.attempt + form + tree,graph + {"search_default_filter_no_success":1} + + + + Banned Remotes + res.banned.remote + form + tree,form + + + + diff --git a/auth_brute_force/views/menu.xml b/auth_brute_force/views/menu.xml new file mode 100644 index 000000000..99661eeb4 --- /dev/null +++ b/auth_brute_force/views/menu.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/auth_brute_force/views/view.xml b/auth_brute_force/views/view.xml new file mode 100644 index 000000000..7b7de28c3 --- /dev/null +++ b/auth_brute_force/views/view.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + res.authentication.attempt + + + + + + + + + + + + res.authentication.attempt + + + + + + + + + + res.authentication.attempt + + + + + + + + + + + + + + res.banned.remote + + + + + + + + + + + res.banned.remote + +
+ + + + + + + + + +
+
+
+ + + res.banned.remote + + + + + + + +
+