From 07a8735c4ac41c98689f0a730f60f99dea2a67ba Mon Sep 17 00:00:00 2001 From: sebalix Date: Fri, 30 Oct 2015 17:24:16 +0100 Subject: [PATCH 1/6] [IMP] Module 'auditlog' - Log HTTP user sessions and requests --- auditlog/README.rst | 2 - auditlog/__openerp__.py | 2 + auditlog/models/__init__.py | 2 + auditlog/models/http_request.py | 63 +++++++++++++++++++ auditlog/models/http_session.py | 58 +++++++++++++++++ auditlog/models/log.py | 4 ++ auditlog/models/rule.py | 4 ++ auditlog/security/ir.model.access.csv | 4 ++ auditlog/views/auditlog_view.xml | 10 +++ auditlog/views/http_request_view.xml | 89 +++++++++++++++++++++++++++ auditlog/views/http_session_view.xml | 72 ++++++++++++++++++++++ 11 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 auditlog/models/http_request.py create mode 100644 auditlog/models/http_session.py create mode 100644 auditlog/views/http_request_view.xml create mode 100644 auditlog/views/http_session_view.xml diff --git a/auditlog/README.rst b/auditlog/README.rst index 3502d5978..70d38a77d 100644 --- a/auditlog/README.rst +++ b/auditlog/README.rst @@ -22,8 +22,6 @@ Known issues / Roadmap ====================== * log only operations triggered by some users (currently it logs all users) - * group logs by HTTP query (thanks to werzeug)? - * group HTTP query by user session? Bug Tracker diff --git a/auditlog/__openerp__.py b/auditlog/__openerp__.py index 0299346a2..c7fabd46f 100644 --- a/auditlog/__openerp__.py +++ b/auditlog/__openerp__.py @@ -31,6 +31,8 @@ 'data': [ 'security/ir.model.access.csv', 'views/auditlog_view.xml', + 'views/http_session_view.xml', + 'views/http_request_view.xml', ], 'application': True, 'installable': True, diff --git a/auditlog/models/__init__.py b/auditlog/models/__init__.py index eb562a4c0..e71197ade 100644 --- a/auditlog/models/__init__.py +++ b/auditlog/models/__init__.py @@ -20,4 +20,6 @@ ############################################################################## from . import rule +from . import http_session +from . import http_request from . import log diff --git a/auditlog/models/http_request.py b/auditlog/models/http_request.py new file mode 100644 index 000000000..357c1a7d4 --- /dev/null +++ b/auditlog/models/http_request.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2015 ABF OSIELL (). +# +# 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.http import request + + +class AuditlogHTTPRequest(models.Model): + _name = 'auditlog.http.request' + _description = u"Auditlog - HTTP request log" + _order = "create_date DESC" + + name = fields.Char(u"Path") + root_url = fields.Char(u"Root URL") + user_id = fields.Many2one( + 'res.users', string=u"User") + http_session_id = fields.Many2one( + 'auditlog.http.session', string=u"Session") + user_context = fields.Char(u"Context") + log_ids = fields.One2many( + 'auditlog.log', 'http_request_id', string=u"Logs") + + @api.model + def current_http_request(self): + """Create a log corresponding to the current HTTP request, and returns + its ID. This method can be called several times during the + HTTP query/response cycle, it will only log the request on the + first call. + If no HTTP request is available, returns `False`. + """ + http_session_model = self.env['auditlog.http.session'] + httprequest = request.httprequest + if httprequest: + if hasattr(httprequest, 'auditlog_http_request_id'): + return httprequest.auditlog_http_request_id + vals = { + 'name': httprequest.path, + 'root_url': httprequest.url_root, + 'user_id': request.uid, + 'http_session_id': http_session_model.current_http_session(), + 'user_context': request.context, + } + httprequest.auditlog_http_request_id = self.create(vals).id + return httprequest.auditlog_http_request_id + return False diff --git a/auditlog/models/http_session.py b/auditlog/models/http_session.py new file mode 100644 index 000000000..deb2ee27c --- /dev/null +++ b/auditlog/models/http_session.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2015 ABF OSIELL (). +# +# 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.http import request + + +class AuditlogtHTTPSession(models.Model): + _name = 'auditlog.http.session' + _description = u"Auditlog - HTTP User session log" + _order = "create_date DESC" + + name = fields.Char(u"Session ID") + user_id = fields.Many2one( + 'res.users', string=u"User") + http_request_ids = fields.One2many( + 'auditlog.http.request', 'http_session_id', string=u"HTTP Requests") + + @api.model + def current_http_session(self): + """Create a log corresponding to the current HTTP user session, and + returns its ID. This method can be called several times during the + HTTP query/response cycle, it will only log the user session on the + first call. + If no HTTP user session is available, returns `False`. + """ + httpsession = request.httpsession + if httpsession: + existing_session = self.search( + [('name', '=', httpsession.sid), + ('user_id', '=', request.uid)]) + if existing_session: + return existing_session.id + vals = { + 'name': httpsession.sid, + 'user_id': request.uid, + } + httpsession.auditlog_http_session_id = self.create(vals).id + return httpsession.auditlog_http_session_id + return False diff --git a/auditlog/models/log.py b/auditlog/models/log.py index b222c470e..bff2fdba2 100644 --- a/auditlog/models/log.py +++ b/auditlog/models/log.py @@ -36,6 +36,10 @@ class auditlog_log(models.Model): method = fields.Char(u"Method", size=64) line_ids = fields.One2many( 'auditlog.log.line', 'log_id', string=u"Fields updated") + http_session_id = fields.Many2one( + 'auditlog.http.session', string=u"Session") + http_request_id = fields.Many2one( + 'auditlog.http.request', string=u"HTTP Request") class auditlog_log_line(models.Model): diff --git a/auditlog/models/rule.py b/auditlog/models/rule.py index 4f18c4a3e..36701dd08 100644 --- a/auditlog/models/rule.py +++ b/auditlog/models/rule.py @@ -306,6 +306,8 @@ class auditlog_rule(models.Model): if new_values is None: new_values = EMPTY_DICT log_model = self.env['auditlog.log'] + http_request_model = self.env['auditlog.http.request'] + http_session_model = self.env['auditlog.http.session'] for res_id in res_ids: model_model = self.env[res_model] name = model_model.browse(res_id).name_get() @@ -316,6 +318,8 @@ class auditlog_rule(models.Model): 'res_id': res_id, 'method': method, 'user_id': uid, + 'http_request_id': http_request_model.current_http_request(), + 'http_session_id': http_session_model.current_http_session(), } vals.update(additional_log_values or {}) log = log_model.create(vals) diff --git a/auditlog/security/ir.model.access.csv b/auditlog/security/ir.model.access.csv index 1bb8381d0..32744cc21 100644 --- a/auditlog/security/ir.model.access.csv +++ b/auditlog/security/ir.model.access.csv @@ -2,7 +2,11 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_auditlog_rule_user,auditlog_rule_user,model_auditlog_rule,base.group_user,0,0,0,0 access_auditlog_log_user,auditlog_log_user,model_auditlog_log,base.group_user,0,0,0,0 access_auditlog_log_line_user,auditlog_log_line_user,model_auditlog_log_line,base.group_user,0,0,0,0 +access_auditlog_http_session_user,auditlog_http_session_user,model_auditlog_http_session,base.group_user,0,0,0,0 +access_auditlog_http_request_user,auditlog_http_request_user,model_auditlog_http_request,base.group_user,0,0,0,0 access_auditlog_rule_manager,auditlog_rule_manager,model_auditlog_rule,base.group_erp_manager,1,1,1,1 access_auditlog_log_manager,auditlog_log_manager,model_auditlog_log,base.group_erp_manager,1,1,1,1 access_auditlog_log_line_manager,auditlog_log_line_manager,model_auditlog_log_line,base.group_erp_manager,1,1,1,1 +access_auditlog_http_session_manager,auditlog_http_session_manager,model_auditlog_http_session,base.group_erp_manager,1,1,1,1 +access_auditlog_http_request_manager,auditlog_http_request_manager,model_auditlog_http_request,base.group_erp_manager,1,1,1,1 diff --git a/auditlog/views/auditlog_view.xml b/auditlog/views/auditlog_view.xml index 5bf2132cb..db0a5de81 100644 --- a/auditlog/views/auditlog_view.xml +++ b/auditlog/views/auditlog_view.xml @@ -117,6 +117,10 @@ + + + +
@@ -182,6 +186,12 @@ + + diff --git a/auditlog/views/http_request_view.xml b/auditlog/views/http_request_view.xml new file mode 100644 index 000000000..bf7962c89 --- /dev/null +++ b/auditlog/views/http_request_view.xml @@ -0,0 +1,89 @@ + + + + + + auditlog.http.request.form + auditlog.http.request + form + + + + + + + + + + + + + + + + + + + + + + + auditlog.http.request.tree + auditlog.http.request + tree + + + + + + + + + + + + auditlog.http.request.search + auditlog.http.request + search + + + + + + + + + + + + + + + + + + + + HTTP Requests + ir.actions.act_window + auditlog.http.request + form + + + + + + + diff --git a/auditlog/views/http_session_view.xml b/auditlog/views/http_session_view.xml new file mode 100644 index 000000000..6b641680d --- /dev/null +++ b/auditlog/views/http_session_view.xml @@ -0,0 +1,72 @@ + + + + + + auditlog.http.session.form + auditlog.http.session + form + +
+ + + + + + + + + + +
+
+
+ + + auditlog.http.session.tree + auditlog.http.session + tree + + + + + + + + + + + auditlog.http.session.search + auditlog.http.session + search + + + + + + + + + + + + + + + User sessions + ir.actions.act_window + auditlog.http.session + form + + + + + +
+
From 9eeffae0d4e0393c855aecd455f444053fdcb2dc Mon Sep 17 00:00:00 2001 From: sebalix Date: Fri, 30 Oct 2015 17:36:52 +0100 Subject: [PATCH 2/6] [IMP] Module 'auditlog' - Bump the version to 1.1.0 --- auditlog/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auditlog/__openerp__.py b/auditlog/__openerp__.py index c7fabd46f..b7c7c646d 100644 --- a/auditlog/__openerp__.py +++ b/auditlog/__openerp__.py @@ -21,7 +21,7 @@ { 'name': "Audit Log", - 'version': "8.0.1.0.0", + 'version': "8.0.1.1.0", 'author': "ABF OSIELL,Odoo Community Association (OCA)", 'website': "http://www.osiell.com", 'category': "Tools", From b74fe4c027e210209fc56711c167780f991d04b1 Mon Sep 17 00:00:00 2001 From: sebalix Date: Fri, 30 Oct 2015 17:49:05 +0100 Subject: [PATCH 3/6] [FIX] Module 'auditlog' - Cleanup XML views (types removed) --- auditlog/views/http_request_view.xml | 3 --- auditlog/views/http_session_view.xml | 3 --- 2 files changed, 6 deletions(-) diff --git a/auditlog/views/http_request_view.xml b/auditlog/views/http_request_view.xml index bf7962c89..0011eb55d 100644 --- a/auditlog/views/http_request_view.xml +++ b/auditlog/views/http_request_view.xml @@ -5,7 +5,6 @@ auditlog.http.request.form auditlog.http.request - form
@@ -30,7 +29,6 @@ auditlog.http.request.tree auditlog.http.request - tree @@ -44,7 +42,6 @@ auditlog.http.request.search auditlog.http.request - search diff --git a/auditlog/views/http_session_view.xml b/auditlog/views/http_session_view.xml index 6b641680d..4fc2e3bdc 100644 --- a/auditlog/views/http_session_view.xml +++ b/auditlog/views/http_session_view.xml @@ -5,7 +5,6 @@ auditlog.http.session.form auditlog.http.session - form @@ -25,7 +24,6 @@ auditlog.http.session.tree auditlog.http.session - tree @@ -38,7 +36,6 @@ auditlog.http.session.search auditlog.http.session - search From ede721167b346b654b18e4cd604c59549468c3d6 Mon Sep 17 00:00:00 2001 From: sebalix Date: Fri, 30 Oct 2015 18:17:05 +0100 Subject: [PATCH 4/6] [FIX] Module 'auditlog' - A log can be created with no current HTTP request (unit tests, ir.cron...) --- auditlog/models/http_request.py | 2 ++ auditlog/models/http_session.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/auditlog/models/http_request.py b/auditlog/models/http_request.py index 357c1a7d4..77f9aa09f 100644 --- a/auditlog/models/http_request.py +++ b/auditlog/models/http_request.py @@ -46,6 +46,8 @@ class AuditlogHTTPRequest(models.Model): first call. If no HTTP request is available, returns `False`. """ + if not request: + return False http_session_model = self.env['auditlog.http.session'] httprequest = request.httprequest if httprequest: diff --git a/auditlog/models/http_session.py b/auditlog/models/http_session.py index deb2ee27c..2bbf736e6 100644 --- a/auditlog/models/http_session.py +++ b/auditlog/models/http_session.py @@ -42,6 +42,8 @@ class AuditlogtHTTPSession(models.Model): first call. If no HTTP user session is available, returns `False`. """ + if not request: + return False httpsession = request.httpsession if httpsession: existing_session = self.search( From a89f278f12347cb516df9182f48dbbcb141052a0 Mon Sep 17 00:00:00 2001 From: sebalix Date: Sat, 31 Oct 2015 11:39:18 +0100 Subject: [PATCH 5/6] [FIX] Module 'auditlog' - 'display_name' fields added for the user session and HTTP requests models --- auditlog/models/http_request.py | 12 ++++++++++++ auditlog/models/http_session.py | 12 ++++++++++++ auditlog/views/http_request_view.xml | 6 +----- auditlog/views/http_session_view.xml | 4 ++-- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/auditlog/models/http_request.py b/auditlog/models/http_request.py index 77f9aa09f..7d6b19d48 100644 --- a/auditlog/models/http_request.py +++ b/auditlog/models/http_request.py @@ -27,7 +27,9 @@ class AuditlogHTTPRequest(models.Model): _name = 'auditlog.http.request' _description = u"Auditlog - HTTP request log" _order = "create_date DESC" + _rec_name = 'display_name' + display_name = fields.Char(u"Name", compute="_display_name") name = fields.Char(u"Path") root_url = fields.Char(u"Root URL") user_id = fields.Many2one( @@ -38,6 +40,16 @@ class AuditlogHTTPRequest(models.Model): log_ids = fields.One2many( 'auditlog.log', 'http_request_id', string=u"Logs") + @api.multi + def _display_name(self): + for httprequest in self: + create_date = fields.Datetime.from_string(httprequest.create_date) + tz_create_date = fields.Datetime.context_timestamp( + httprequest, create_date) + httprequest.display_name = u"%s (%s)" % ( + httprequest.name or '?', + fields.Datetime.to_string(tz_create_date)) + @api.model def current_http_request(self): """Create a log corresponding to the current HTTP request, and returns diff --git a/auditlog/models/http_session.py b/auditlog/models/http_session.py index 2bbf736e6..046a10fa7 100644 --- a/auditlog/models/http_session.py +++ b/auditlog/models/http_session.py @@ -27,13 +27,25 @@ class AuditlogtHTTPSession(models.Model): _name = 'auditlog.http.session' _description = u"Auditlog - HTTP User session log" _order = "create_date DESC" + _rec_name = 'display_name' + display_name = fields.Char(u"Name", compute="_display_name") name = fields.Char(u"Session ID") user_id = fields.Many2one( 'res.users', string=u"User") http_request_ids = fields.One2many( 'auditlog.http.request', 'http_session_id', string=u"HTTP Requests") + @api.multi + def _display_name(self): + for httpsession in self: + create_date = fields.Datetime.from_string(httpsession.create_date) + tz_create_date = fields.Datetime.context_timestamp( + httpsession, create_date) + httpsession.display_name = u"%s (%s)" % ( + httpsession.user_id and httpsession.user_id.name or '?', + fields.Datetime.to_string(tz_create_date)) + @api.model def current_http_session(self): """Create a log corresponding to the current HTTP user session, and diff --git a/auditlog/views/http_request_view.xml b/auditlog/views/http_request_view.xml index 0011eb55d..2169ed0f9 100644 --- a/auditlog/views/http_request_view.xml +++ b/auditlog/views/http_request_view.xml @@ -12,11 +12,8 @@ - - - - + @@ -33,7 +30,6 @@ - diff --git a/auditlog/views/http_session_view.xml b/auditlog/views/http_session_view.xml index 4fc2e3bdc..942da68af 100644 --- a/auditlog/views/http_session_view.xml +++ b/auditlog/views/http_session_view.xml @@ -10,8 +10,8 @@ - + @@ -27,8 +27,8 @@ - + From 7e2e7a493340ed9f14419d348f682e175e7c4967 Mon Sep 17 00:00:00 2001 From: sebalix Date: Sat, 31 Oct 2015 20:31:05 +0100 Subject: [PATCH 6/6] [FIX] Module 'auditlog' - Fix pylint check --- auditlog/__openerp__.py | 1 + auditlog/models/log.py | 4 ++-- auditlog/models/rule.py | 10 +++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/auditlog/__openerp__.py b/auditlog/__openerp__.py index b7c7c646d..517fa4f0f 100644 --- a/auditlog/__openerp__.py +++ b/auditlog/__openerp__.py @@ -23,6 +23,7 @@ 'name': "Audit Log", 'version': "8.0.1.1.0", 'author': "ABF OSIELL,Odoo Community Association (OCA)", + 'license': "AGPL-3", 'website': "http://www.osiell.com", 'category': "Tools", 'depends': [ diff --git a/auditlog/models/log.py b/auditlog/models/log.py index bff2fdba2..e8b35ad44 100644 --- a/auditlog/models/log.py +++ b/auditlog/models/log.py @@ -22,7 +22,7 @@ from openerp import models, fields -class auditlog_log(models.Model): +class AuditlogLog(models.Model): _name = 'auditlog.log' _description = "Auditlog - Log" _order = "create_date desc" @@ -42,7 +42,7 @@ class auditlog_log(models.Model): 'auditlog.http.request', string=u"HTTP Request") -class auditlog_log_line(models.Model): +class AuditlogLogLine(models.Model): _name = 'auditlog.log.line' _description = "Auditlog - Log details (fields updated)" diff --git a/auditlog/models/rule.py b/auditlog/models/rule.py index 36701dd08..7fa4403ec 100644 --- a/auditlog/models/rule.py +++ b/auditlog/models/rule.py @@ -58,7 +58,7 @@ class DictDiffer(object): if self.past_dict[o] == self.current_dict[o]) -class auditlog_rule(models.Model): +class AuditlogRule(models.Model): _name = 'auditlog.rule' _description = "Auditlog - Rule" @@ -110,7 +110,7 @@ class auditlog_rule(models.Model): def _register_hook(self, cr, ids=None): """Get all rules and apply them to log method calls.""" - super(auditlog_rule, self)._register_hook(cr) + super(AuditlogRule, self)._register_hook(cr) if not hasattr(self.pool, '_auditlog_field_cache'): self.pool._auditlog_field_cache = {} if not hasattr(self.pool, '_auditlog_model_cache'): @@ -182,7 +182,7 @@ class auditlog_rule(models.Model): # errors occurs with the `_register_hook()` BaseModel method. def create(self, cr, uid, vals, context=None): """Update the registry when a new rule is created.""" - res_id = super(auditlog_rule, self).create( + res_id = super(AuditlogRule, self).create( cr, uid, vals, context=context) if self._register_hook(cr, [res_id]): modules.registry.RegistryManager.signal_registry_change(cr.dbname) @@ -194,7 +194,7 @@ class auditlog_rule(models.Model): """Update the registry when existing rules are updated.""" if isinstance(ids, (int, long)): ids = [ids] - super(auditlog_rule, self).write(cr, uid, ids, vals, context=context) + super(AuditlogRule, self).write(cr, uid, ids, vals, context=context) if self._register_hook(cr, ids): modules.registry.RegistryManager.signal_registry_change(cr.dbname) return True @@ -203,7 +203,7 @@ class auditlog_rule(models.Model): def unlink(self): """Unsubscribe rules before removing them.""" self.unsubscribe() - return super(auditlog_rule, self).unlink() + return super(AuditlogRule, self).unlink() def _make_create(self): """Instanciate a create method that log its calls."""