Merge pull request #205 from Eficent/11.0-knowledge-document-security

[knowledge][11.0.3.0.0] - add security for access to ir.attachment.
This commit is contained in:
Jordi Ballester Alomar 2019-03-11 20:30:11 +01:00 committed by GitHub
commit f731c04534
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 111 additions and 70 deletions

View File

@ -1,7 +1,7 @@
///* Copyright 2014 Therp BV (<http://therp.nl>) /* Copyright 2014 Therp BV (<http://therp.nl>)
// * License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */ * License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
odoo.define('attachment_preview', function(require) { odoo.define('attachment_preview', function (require) {
'use strict'; 'use strict';
var core = require('web.core'); var core = require('web.core');
@ -14,16 +14,15 @@ odoo.define('attachment_preview', function(require) {
var Widget = require('web.Widget'); var Widget = require('web.Widget');
var AttachmentPreviewMixin = { var AttachmentPreviewMixin = {
canPreview: function(extension) { canPreview: function (extension) {
return $.inArray( return $.inArray(
extension, extension,
[ ['odt', 'odp', 'ods', 'fodt', 'pdf', 'ott', 'fodp', 'otp',
'odt', 'odp', 'ods', 'fodt', 'pdf', 'ott', 'fodp', 'otp', 'fods', 'ots',
'fods', 'ots'
]) > -1; ]) > -1;
}, },
getUrl: function(attachment_id, attachment_url, attachment_extension, attachment_title) { getUrl: function (attachment_id, attachment_url, attachment_extension, attachment_title) {
var url = (window.location.origin || '') + var url = (window.location.origin || '') +
'/attachment_preview/static/lib/ViewerJS/index.html' + '/attachment_preview/static/lib/ViewerJS/index.html' +
'?type=' + encodeURIComponent(attachment_extension) + '?type=' + encodeURIComponent(attachment_extension) +
@ -33,7 +32,7 @@ odoo.define('attachment_preview', function(require) {
return url; return url;
}, },
showPreview: function(attachment_id, attachment_url, attachment_extension, attachment_title, split_screen) { showPreview: function (attachment_id, attachment_url, attachment_extension, attachment_title, split_screen) {
var url = this.getUrl(attachment_id, attachment_url, attachment_extension, attachment_title); var url = this.getUrl(attachment_id, attachment_url, attachment_extension, attachment_title);
if (split_screen) { if (split_screen) {
this.trigger_up('onAttachmentPreview', {url: url}); this.trigger_up('onAttachmentPreview', {url: url});
@ -46,51 +45,51 @@ odoo.define('attachment_preview', function(require) {
Sidebar.include(AttachmentPreviewMixin); Sidebar.include(AttachmentPreviewMixin);
Sidebar.include({ Sidebar.include({
events: _.extend({}, Sidebar.prototype.events, { events: _.extend({}, Sidebar.prototype.events, {
'click .o_sidebar_preview_attachment': '_onPreviewAttachment' 'click .o_sidebar_preview_attachment': '_onPreviewAttachment',
}), }),
previewableAttachments: null, previewableAttachments: null,
_redraw: function () { _redraw: function () {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.getPreviewableAttachments().done(function(atts) { this.getPreviewableAttachments().done(function (atts) {
this.previewableAttachments = atts; this.previewableAttachments = atts;
this.updatePreviewButtons(atts); this.updatePreviewButtons(atts);
this.trigger_up('setPreviewableAttachments', {attachments: atts}); this.trigger_up('setPreviewableAttachments', {attachments: atts});
}.bind(this)); }.bind(this));
}, },
_onPreviewAttachment: function(event) { _onPreviewAttachment: function (event) {
event.preventDefault(); event.preventDefault();
var self = this, var self = this,
$target = $(event.currentTarget), $target = $(event.currentTarget),
split_screen = $target.attr('data-target') != 'new', split_screen = $target.attr('data-target') !== 'new',
attachment_id = parseInt($target.attr('data-id'), 10), attachment_id = parseInt($target.attr('data-id'), 10),
attachment_url = $target.attr('data-url'), attachment_url = $target.attr('data-url'),
attachment_extension = $target.attr('data-extension'), attachment_extension = $target.attr('data-extension'),
attachment_title = $target.attr('data-original-title'); attachment_title = $target.attr('data-original-title');
if(attachment_extension) { if (attachment_extension) {
this.showPreview(attachment_id, attachment_url, attachment_extension, attachment_title, split_screen); this.showPreview(attachment_id, attachment_url, attachment_extension, attachment_title, split_screen);
} else { } else {
this._rpc({ this._rpc({
model: 'ir.attachment', model: 'ir.attachment',
method: 'get_attachment_extension', method: 'get_attachment_extension',
args: [attachment_id] args: [attachment_id],
}).then(function(extension) { }).then(function (extension) {
self.showPreview(attachment_id, attachment_url, extension, null, split_screen); self.showPreview(attachment_id, attachment_url, extension, null, split_screen);
}); });
} }
}, },
getPreviewableAttachments: function() { getPreviewableAttachments: function () {
var self = this; var self = this;
var deferred = $.Deferred(); var deferred = $.Deferred();
var $items = this.$el.find('.o_sidebar_preview_attachment'); var $items = this.$el.find('.o_sidebar_preview_attachment');
var attachments = _.object($items.map(function() { var attachments = _.object($items.map(function () {
return parseInt($(this).attr('data-id'), 10); return parseInt($(this).attr('data-id'), 10);
}), $items.map(function() { }), $items.map(function () {
return { return {
url: $(this).attr('data-url'), url: $(this).attr('data-url'),
extension: $(this).attr('data-extension'), extension: $(this).attr('data-extension'),
@ -102,14 +101,14 @@ odoo.define('attachment_preview', function(require) {
model: 'ir.attachment', model: 'ir.attachment',
method: 'get_attachment_extension', method: 'get_attachment_extension',
args: [ args: [
_.map(_.keys(attachments), function(id) { _.map(_.keys(attachments), function (id) {
return parseInt(id, 10); return parseInt(id, 10);
}) }),
] ],
}).then(function(extensions) { }).then(function (extensions) {
var reviewableAttachments = _.map(_.keys(_.pick(extensions, function(extension, id) { var reviewableAttachments = _.map(_.keys(_.pick(extensions, function (extension, id) {
return self.canPreview(extension); return self.canPreview(extension);
})), function(id) { })), function (id) {
return { return {
id: id, id: id,
url: attachments[id]['url'], url: attachments[id]['url'],
@ -120,18 +119,18 @@ odoo.define('attachment_preview', function(require) {
attachments[id]['url'], attachments[id]['url'],
extensions[id], extensions[id],
id + ' - ' + attachments[id]['title'] id + ' - ' + attachments[id]['title']
) ),
} };
}); });
deferred.resolve(reviewableAttachments); deferred.resolve(reviewableAttachments);
}, function() { }, function () {
deferred.reject(); deferred.reject();
}); });
return deferred.promise(); return deferred.promise();
}, },
updatePreviewButtons: function(previewableAttachments) { updatePreviewButtons: function (previewableAttachments) {
this.$el.find('.o_sidebar_preview_attachment').each(function() { this.$el.find('.o_sidebar_preview_attachment').each(function () {
var $this = $(this); var $this = $(this);
var id = $this.attr('data-id'); var id = $this.attr('data-id');
var att = _.findWhere(previewableAttachments, {id: id}); var att = _.findWhere(previewableAttachments, {id: id});
@ -147,20 +146,20 @@ odoo.define('attachment_preview', function(require) {
basic_fields.FieldBinaryFile.include(AttachmentPreviewMixin); basic_fields.FieldBinaryFile.include(AttachmentPreviewMixin);
basic_fields.FieldBinaryFile.include({ basic_fields.FieldBinaryFile.include({
events: _.extend({}, basic_fields.FieldBinaryFile.prototype.events, { events: _.extend({}, basic_fields.FieldBinaryFile.prototype.events, {
'click .fa-search': '_onPreview' 'click .fa-search': '_onPreview',
}), }),
_renderReadonly: function () { _renderReadonly: function () {
var self = this; var self = this;
this._super.apply(this, arguments); this._super.apply(this, arguments);
this._getBinaryExtension().done(function(extension) { this._getBinaryExtension().done(function (extension) {
if(self.canPreview(extension)) { if (self.canPreview(extension)) {
self._renderPreviewButton(extension); self._renderPreviewButton(extension);
} }
}); });
}, },
_renderPreviewButton: function(extension) { _renderPreviewButton: function (extension) {
this.$previewBtn = $("<span/>"); this.$previewBtn = $("<span/>");
this.$previewBtn.addClass('fa fa-search'); this.$previewBtn.addClass('fa fa-search');
this.$previewBtn.attr('title', _.str.sprintf(_t('Preview %s'), this.field.string)); this.$previewBtn.attr('title', _.str.sprintf(_t('Preview %s'), this.field.string));
@ -176,12 +175,12 @@ odoo.define('attachment_preview', function(require) {
this.model, this.model,
this.recordData.id, this.recordData.id,
this.name, this.name,
this.attrs.filename this.attrs.filename,
] ],
}); });
}, },
_onPreview: function(event) { _onPreview: function (event) {
this.showPreview( this.showPreview(
null, null,
_.str.sprintf( _.str.sprintf(
@ -194,7 +193,7 @@ odoo.define('attachment_preview', function(require) {
_.str.sprintf(_t('Preview %s'), this.field.string), _.str.sprintf(_t('Preview %s'), this.field.string),
false false
); );
} },
}); });
var AttachmentPreviewWidget = Widget.extend({ var AttachmentPreviewWidget = Widget.extend({
@ -237,7 +236,7 @@ odoo.define('attachment_preview', function(require) {
window.open(this.attachments[this.activeIndex].previewUrl); window.open(this.attachments[this.activeIndex].previewUrl);
}, },
next: function() { next: function () {
var index = this.activeIndex + 1; var index = this.activeIndex + 1;
if (index >= this.attachments.length) { if (index >= this.attachments.length) {
index = 0; index = 0;
@ -247,7 +246,7 @@ odoo.define('attachment_preview', function(require) {
this.loadPreview(); this.loadPreview();
}, },
previous: function() { previous: function () {
var index = this.activeIndex - 1; var index = this.activeIndex - 1;
if (index < 0) { if (index < 0) {
index = this.attachments.length - 1; index = this.attachments.length - 1;
@ -267,13 +266,13 @@ odoo.define('attachment_preview', function(require) {
this.trigger('hidden'); this.trigger('hidden');
}, },
updatePaginator: function() { updatePaginator: function () {
var value = _.str.sprintf('%s / %s', this.activeIndex + 1, this.attachments.length); var value = _.str.sprintf('%s / %s', this.activeIndex + 1, this.attachments.length);
this.$current.html(value); this.$current.html(value);
}, },
loadPreview: function() { loadPreview: function () {
if (this.attachments.length == 0) { if (this.attachments.length === 0) {
this.$iframe.attr('src', 'about:blank'); this.$iframe.attr('src', 'about:blank');
return; return;
} }
@ -282,18 +281,18 @@ odoo.define('attachment_preview', function(require) {
this.$iframe.attr('src', att.previewUrl); this.$iframe.attr('src', att.previewUrl);
}, },
setAttachments: function(attachments) { setAttachments: function (attachments) {
this.attachments = attachments; this.attachments = attachments;
this.activeIndex = 0; this.activeIndex = 0;
this.updatePaginator(); this.updatePaginator();
this.loadPreview(); this.loadPreview();
} },
}); });
FormRenderer.include({ FormRenderer.include({
attachmentPreviewWidget: null, attachmentPreviewWidget: null,
init: function() { init: function () {
var res = this._super.apply(this, arguments); var res = this._super.apply(this, arguments);
this.attachmentPreviewWidget = new AttachmentPreviewWidget(this); this.attachmentPreviewWidget = new AttachmentPreviewWidget(this);
this.attachmentPreviewWidget.on('hidden', this, this._attachmentPreviewWidgetHidden); this.attachmentPreviewWidget.on('hidden', this, this._attachmentPreviewWidgetHidden);
@ -305,7 +304,7 @@ odoo.define('attachment_preview', function(require) {
this.attachmentPreviewWidget.insertAfter(this.$el); this.attachmentPreviewWidget.insertAfter(this.$el);
}, },
_attachmentPreviewWidgetHidden: function() { _attachmentPreviewWidgetHidden: function () {
this.$el.removeClass('attachment_preview'); this.$el.removeClass('attachment_preview');
}, },
@ -320,29 +319,29 @@ odoo.define('attachment_preview', function(require) {
on_detach_callback: function () { on_detach_callback: function () {
this.attachmentPreviewWidget.hide(); this.attachmentPreviewWidget.hide();
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
} },
}); });
FormController.include({ FormController.include({
custom_events: _.extend({}, FormController.prototype.custom_events, { custom_events: _.extend({}, FormController.prototype.custom_events, {
onAttachmentPreview: '_onAttachmentPreview', onAttachmentPreview: '_onAttachmentPreview',
setPreviewableAttachments: '_setPreviewableAttachments' setPreviewableAttachments: '_setPreviewableAttachments',
}), }),
_onAttachmentPreview: function (event) { _onAttachmentPreview: function (event) {
this.renderer.showAttachmentPreviewWidget(); this.renderer.showAttachmentPreviewWidget();
}, },
_setPreviewableAttachments: function(event) { _setPreviewableAttachments: function (event) {
this.renderer.attachmentPreviewWidget.setAttachments( this.renderer.attachmentPreviewWidget.setAttachments(
event.data.attachments event.data.attachments
); );
} },
}); });
return { return {
AttachmentPreviewMixin: AttachmentPreviewMixin, AttachmentPreviewMixin: AttachmentPreviewMixin,
AttachmentPreviewWidget: AttachmentPreviewWidget AttachmentPreviewWidget: AttachmentPreviewWidget,
}; };
}); });

View File

@ -3,9 +3,9 @@
// This file contains tweaks for viewerjs itself and is not meant to be run in // This file contains tweaks for viewerjs itself and is not meant to be run in
// OpenERP's context // OpenERP's context
(function(original_Viewer) { (function (original_Viewer) {
window.Viewer = function(plugin, parameters) { window.Viewer = function (plugin, parameters) {
if(!plugin) { if (!plugin) {
// eslint-disable-next-line no-alert // eslint-disable-next-line no-alert
alert('Unsupported file type'); alert('Unsupported file type');
} }

View File

@ -39,7 +39,7 @@
<field name="approved_date" readonly="1" attrs="{'invisible':[('state','not in',['approved'])]}"/> <field name="approved_date" readonly="1" attrs="{'invisible':[('state','not in',['approved'])]}"/>
</group> </group>
</xpath> </xpath>
<!-- Readonly fields --> <!-- Readonly fields -->
<field name="content" position="attributes"><attribute name="attrs">{'readonly': [('state', 'not in', ['draft'])]}</attribute></field> <field name="content" position="attributes"><attribute name="attrs">{'readonly': [('state', 'not in', ['draft'])]}</attribute></field>
<field name="page_id" position="attributes"><attribute name="attrs">{'readonly': [('state', 'not in', ['draft'])]}</attribute></field> <field name="page_id" position="attributes"><attribute name="attrs">{'readonly': [('state', 'not in', ['draft'])]}</attribute></field>
<field name="name" position="attributes"><attribute name="attrs">{'readonly': [('state', 'not in', ['draft'])]}</attribute></field> <field name="name" position="attributes"><attribute name="attrs">{'readonly': [('state', 'not in', ['draft'])]}</attribute></field>

View File

@ -4,7 +4,7 @@
{ {
'name': 'Project Wiki', 'name': 'Project Wiki',
'description': 'This module links document pages to projects', 'summary': 'This module links document pages to projects',
'version': '11.0.1.0.0', 'version': '11.0.1.0.0',
"development_status": "Beta", "development_status": "Beta",
'category': 'Project', 'category': 'Project',

View File

@ -1,2 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_ir_attachment_add_url,access_ir_attachment_add_url,model_ir_attachment_add_url,,1,1,1,1 access_ir_attachment_add_url,access_ir_attachment_add_url,model_ir_attachment_add_url,,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ir_attachment_add_url access_ir_attachment_add_url model_ir_attachment_add_url 1 1 1 1

View File

@ -21,7 +21,7 @@ odoo.define('document_url', function (require) {
self.$el.find("a[href]").attr('target', '_blank'); self.$el.find("a[href]").attr('target', '_blank');
self.$el self.$el
.find('.oe_sidebar_add_attachment, .o_sidebar_add_attachment') .find('.oe_sidebar_add_attachment, .o_sidebar_add_attachment')
.after(QWeb.render('AddUrlDocumentItem', {widget: self})) .after(QWeb.render('AddUrlDocumentItem', {widget: self}));
self.$el.find('.o_sidebar_add_url').on('click', function (e) { self.$el.find('.o_sidebar_add_url').on('click', function (e) {
self.on_url_doc(); self.on_url_doc();
}); });
@ -29,7 +29,7 @@ odoo.define('document_url', function (require) {
on_url_doc: function (event) { on_url_doc: function (event) {
var self = this; var self = this;
var env = self.env var env = self.env;
var view = self.getParent(); var view = self.getParent();
var ids = self.env.activeIds; var ids = self.env.activeIds;
if (!_.isEmpty(ids)) { if (!_.isEmpty(ids)) {
@ -41,7 +41,7 @@ odoo.define('document_url', function (require) {
if (env.domain) { if (env.domain) {
activeIdsContext.active_domain = env.domain; activeIdsContext.active_domain = env.domain;
} }
var context = new Context(env.context, activeIdsContext) var context = new Context(env.context, activeIdsContext);
context = pyeval.eval('context', context); context = pyeval.eval('context', context);
self._rpc({ self._rpc({
route: "/web/action/load", route: "/web/action/load",

View File

@ -2,10 +2,11 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ {
"name": "Knowledge Management System", "name": "Knowledge Management System",
"version": "11.0.2.0.0", "version": "11.0.3.0.0",
"author": "OpenERP SA," "author": "OpenERP SA,"
"MONK Software, " "MONK Software, "
"Tecnativa, " "Tecnativa, "
"Eficent, "
"Odoo Community Association (OCA)", "Odoo Community Association (OCA)",
"category": "Knowledge", "category": "Knowledge",
"license": "AGPL-3", "license": "AGPL-3",

View File

@ -9,11 +9,17 @@ class KnowledgeConfigSettings(models.TransientModel):
_inherit = 'res.config.settings' _inherit = 'res.config.settings'
module_document = fields.Boolean( module_document = fields.Boolean(
'Manage documents', 'Attachments List and Document Indexation',
help='Document indexation, full text search of attachements.\n' help='Document indexation, full text search of attachements.\n'
'- This installs the module document.' '- This installs the module document.'
) )
group_ir_attachment_user = fields.Boolean(
string='Central access to Documents',
help="When you set this field all users will be able to manage "
"attachments centrally, from the Knowledge/Documents menu.",
implied_group='knowledge.group_ir_attachment_user')
module_document_page = fields.Boolean( module_document_page = fields.Boolean(
'Manage document pages (Wiki)', 'Manage document pages (Wiki)',
help='Provide document page and category as a wiki.\n' help='Provide document page and category as a wiki.\n'

View File

@ -0,0 +1,20 @@
To set up this module, you need to go to:
* Knowledge / Configuration / Settings
From this menu you'll have a central access to install the apps that belong
to Knowledge.
* Check *Attachments List and Document Indexation* if you want to install the
module that allows users to attach documents to any model.
* Check *Manage attachments centrally* if you want all users to be able to
access to the all attachments to which they have read permissions, from the
menu *Knowledge / Documents*
If you want to grant Central Access to Documents only to some users:
#. Go to *Settings/Activate the developer mode*. Only a user with
*Administration / Settings* permissions can do that.
#. Go to *Settings / Users & Companies / Users* and set the checkbox
*Central access to Documents* to the selected users.

View File

@ -6,3 +6,4 @@
* Fayez Qandeel * Fayez Qandeel
* Vicent Cubells <vicent.cubells@tecnativa.com> * Vicent Cubells <vicent.cubells@tecnativa.com>
* Iván Todorovich <ivan.todorovich@gmail.com> * Iván Todorovich <ivan.todorovich@gmail.com>
* Jordi Ballester <jordi.ballester@eficent.com>

View File

@ -1,2 +0,0 @@
* Migrate related modules to v11 and add options in the settings as soon as
they are installable.

View File

@ -1,3 +1,5 @@
To use this module, you need to: This module adds a new top level menu *Knowledge*
* Go to Knowledge / Configuration / Settings Users with permission *Central access to Documents* can access in
*Knowledge/Documents* to all the documents attached to records of any model
for which they have read permission.

View File

@ -7,4 +7,10 @@
<field name="users" eval="[(4, ref('base.user_root'))]"/> <field name="users" eval="[(4, ref('base.user_root'))]"/>
</record> </record>
<record id="group_ir_attachment_user" model="res.groups">
<field name="name">Central access to Documents</field>
<field name="category_id" ref="base.module_category_hidden"/>
<field name="implied_ids" eval="[(4, ref('group_document_user'))]"/>
</record>
</odoo> </odoo>

View File

@ -37,9 +37,9 @@
<menuitem <menuitem
id="menu_document_section" id="menu_document_section"
name="Documents" name="Documents"
groups="knowledge.group_document_user" groups="knowledge.group_ir_attachment_user"
parent="menu_document_root" parent="menu_document_root"
sequence="10"/> sequence="150"/>
<menuitem <menuitem
id="menu_document" id="menu_document"

View File

@ -21,6 +21,14 @@
<label for="module_document"/> <label for="module_document"/>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-12 o_setting_box">
<div class="o_setting_left_pane">
<field name="group_ir_attachment_user"/>
</div>
<div class="o_setting_right_pane">
<label for="group_ir_attachment_user"/>
</div>
</div>
<div class="col-xs-12 col-md-12 o_setting_box"> <div class="col-xs-12 col-md-12 o_setting_box">
<div class="o_setting_left_pane"> <div class="o_setting_left_pane">
<field name="module_document_page"/> <field name="module_document_page"/>
@ -75,7 +83,7 @@
name="Configuration" name="Configuration"
parent="menu_document_root" parent="menu_document_root"
groups="base.group_system" groups="base.group_system"
sequence="50"/> sequence="200"/>
<menuitem id="menu_knowledge_configuration" <menuitem id="menu_knowledge_configuration"
name="Settings" name="Settings"
parent="menu_document_configuration" parent="menu_document_configuration"