PURPOSE
Have user groups specific to eLearning to separate them from generic website
editors. Indeed this is a specific work area and require more than pure design
skills.
SPECIFICATIONS
Two user groups are added in this commit
* group_website_slides_officer
+ can read any course/slide
+ can create a course (will be responsible)
+ can write on its courses
+ can upload slides to the courses he is responsible
+ can publish slides to the courses he is responsible
- cannot unlink courses/slides (you should archive them)
* implies group_website_publisher in order to see the top navbar in
frontend with the publish toggle button and the new content button;
* group_website_slides_manager: has full rights
+ can read any course/slide;
+ can create a course;
+ can add slide to any courses;
+ can publish any courses/slides;
+ can unlink any courses/slides;
+ has access to reporting menu entries;
Rights for other groups are untouched for courses / slides. As a reminder
we still support responsibles being outside of eLearning groups, for example
if an employee is a teacher that handles a course:
* people in upload_group_ids of a channel can upload slides, even if they
don't belong to any eLearning group;
* people responsible of a course can upload and publish slides even if they
don't belong to any eLearning group (done through controller as they
don't have real rights on underlying models);
We choose to keep the survey rights separated from eLearning ones. Indeed
it is easy in odoo to update the imply_ids field of groups if necessary
to link survey / eLearning groups; default user template can also be updated
easily. Standard Odoo should therefore correctly separate those groups. The
only specific rights is that eLearning officers can read all surveys, allowing
to choose them for certification-based courses.
LINKS
Task ID 2062462
PR #39485
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
301 lines
14 KiB
Python
301 lines
14 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo.addons.website_slides.tests import common
|
|
from odoo.exceptions import AccessError, UserError
|
|
from odoo.tests import tagged
|
|
from odoo.tests.common import users
|
|
from odoo.tools import mute_logger
|
|
|
|
|
|
@tagged('security')
|
|
class TestAccess(common.SlidesCase):
|
|
|
|
@mute_logger('odoo.models', 'odoo.addons.base.models.ir_rule')
|
|
def test_access_channel_invite(self):
|
|
""" Invite channels don't give enroll if not member """
|
|
self.channel.write({'enroll': 'invite'})
|
|
|
|
self.channel.with_user(self.user_officer).read(['name'])
|
|
self.channel.with_user(self.user_manager).read(['name'])
|
|
self.channel.with_user(self.user_emp).read(['name'])
|
|
self.channel.with_user(self.user_portal).read(['name'])
|
|
self.channel.with_user(self.user_public).read(['name'])
|
|
|
|
self.slide.with_user(self.user_officer).read(['name'])
|
|
self.slide.with_user(self.user_manager).read(['name'])
|
|
|
|
with self.assertRaises(AccessError):
|
|
self.slide.with_user(self.user_emp).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.with_user(self.user_portal).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.with_user(self.user_portal).read(['name'])
|
|
|
|
# if member -> can read
|
|
membership = self.env['slide.channel.partner'].create({
|
|
'channel_id': self.channel.id,
|
|
'partner_id': self.user_emp.partner_id.id,
|
|
})
|
|
self.channel.with_user(self.user_emp).read(['name'])
|
|
self.slide.with_user(self.user_emp).read(['name'])
|
|
|
|
# not member anymore -> cannot read
|
|
membership.unlink()
|
|
self.channel.with_user(self.user_emp).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.with_user(self.user_emp).read(['name'])
|
|
|
|
@mute_logger('odoo.models', 'odoo.addons.base.models.ir_rule')
|
|
def test_access_channel_public(self):
|
|
""" Public channels don't give enroll if not member """
|
|
self.channel.write({'enroll': 'public'})
|
|
|
|
self.channel.with_user(self.user_officer).read(['name'])
|
|
self.channel.with_user(self.user_manager).read(['name'])
|
|
self.channel.with_user(self.user_emp).read(['name'])
|
|
self.channel.with_user(self.user_portal).read(['name'])
|
|
self.channel.with_user(self.user_public).read(['name'])
|
|
|
|
self.slide.with_user(self.user_officer).read(['name'])
|
|
self.slide.with_user(self.user_manager).read(['name'])
|
|
|
|
with self.assertRaises(AccessError):
|
|
self.slide.with_user(self.user_emp).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.with_user(self.user_portal).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.with_user(self.user_public).read(['name'])
|
|
|
|
@mute_logger('odoo.models', 'odoo.addons.base.models.ir_rule')
|
|
def test_access_channel_publish(self):
|
|
""" Unpublished channels and their content are visible only to eLearning people """
|
|
self.channel.write({'is_published': False, 'enroll': 'public'})
|
|
self.channel.flush(['is_published', 'website_published', 'enroll'])
|
|
|
|
# channel available only to eLearning
|
|
self.channel.invalidate_cache(['name'])
|
|
self.channel.with_user(self.user_officer).read(['name'])
|
|
self.channel.invalidate_cache(['name'])
|
|
self.channel.with_user(self.user_manager).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.channel.invalidate_cache(['name'])
|
|
self.channel.with_user(self.user_emp).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.channel.invalidate_cache(['name'])
|
|
self.channel.with_user(self.user_portal).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.channel.invalidate_cache(['name'])
|
|
self.channel.with_user(self.user_public).read(['name'])
|
|
|
|
# slide available only to eLearning
|
|
self.channel.invalidate_cache(['name'])
|
|
self.slide.with_user(self.user_officer).read(['name'])
|
|
self.channel.invalidate_cache(['name'])
|
|
self.slide.with_user(self.user_manager).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.invalidate_cache(['name'])
|
|
self.slide.with_user(self.user_emp).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.invalidate_cache(['name'])
|
|
self.slide.with_user(self.user_portal).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.invalidate_cache(['name'])
|
|
self.slide.with_user(self.user_public).read(['name'])
|
|
|
|
# even members cannot see unpublished content
|
|
self.env['slide.channel.partner'].create({
|
|
'channel_id': self.channel.id,
|
|
'partner_id': self.user_emp.partner_id.id,
|
|
})
|
|
with self.assertRaises(AccessError):
|
|
self.channel.invalidate_cache(['name'])
|
|
self.channel.with_user(self.user_emp).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.invalidate_cache(['name'])
|
|
self.slide.with_user(self.user_emp).read(['name'])
|
|
|
|
# publish channel but content unpublished (even if can be previewed) still unavailable
|
|
self.channel.write({'is_published': True})
|
|
self.slide.write({
|
|
'is_preview': True,
|
|
'is_published': False,
|
|
})
|
|
self.channel.flush(['website_published'])
|
|
self.slide.flush(['is_preview', 'website_published'])
|
|
|
|
self.slide.invalidate_cache(['name'])
|
|
self.slide.with_user(self.user_officer).read(['name'])
|
|
self.slide.invalidate_cache(['name'])
|
|
self.slide.with_user(self.user_manager).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.invalidate_cache(['name'])
|
|
self.slide.with_user(self.user_emp).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.invalidate_cache(['name'])
|
|
self.slide.with_user(self.user_portal).read(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.slide.invalidate_cache(['name'])
|
|
self.slide.with_user(self.user_public).read(['name'])
|
|
|
|
@mute_logger('odoo.models', 'odoo.addons.base.models.ir_rule')
|
|
def test_access_slide_preview(self):
|
|
""" Slides with preview flag are always visible even to non members if published """
|
|
self.channel.write({'enroll': 'invite'})
|
|
self.slide.write({'is_preview': True})
|
|
self.slide.flush(['is_preview'])
|
|
|
|
self.slide.with_user(self.user_officer).read(['name'])
|
|
self.slide.with_user(self.user_manager).read(['name'])
|
|
self.slide.with_user(self.user_emp).read(['name'])
|
|
self.slide.with_user(self.user_portal).read(['name'])
|
|
self.slide.with_user(self.user_public).read(['name'])
|
|
|
|
|
|
@tagged('functional', 'security')
|
|
class TestRemoveMembership(common.SlidesCase):
|
|
|
|
def setUp(self):
|
|
super(TestRemoveMembership, self).setUp()
|
|
self.channel_partner = self.env['slide.channel.partner'].create({
|
|
'channel_id': self.channel.id,
|
|
'partner_id': self.customer.id,
|
|
})
|
|
|
|
self.slide_partner = self.env['slide.slide.partner'].create({
|
|
'slide_id': self.slide.id,
|
|
'channel_id': self.channel.id,
|
|
'partner_id': self.customer.id
|
|
})
|
|
|
|
def test_security_unlink(self):
|
|
# Only the publisher can unlink channel_partner (and slide_partner by extension)
|
|
with self.assertRaises(AccessError):
|
|
self.channel_partner.with_user(self.user_public).unlink()
|
|
with self.assertRaises(AccessError):
|
|
self.channel_partner.with_user(self.user_portal).unlink()
|
|
with self.assertRaises(AccessError):
|
|
self.channel_partner.with_user(self.user_emp).unlink()
|
|
|
|
def test_slide_partner_remove(self):
|
|
id_slide_partner = self.slide_partner.id
|
|
id_channel_partner = self.channel_partner.id
|
|
self.channel_partner.with_user(self.user_officer).unlink()
|
|
self.assertFalse(self.env['slide.channel.partner'].search([('id', '=', '%d' % id_channel_partner)]))
|
|
# Slide(s) related to the channel and the partner is unlink too.
|
|
self.assertFalse(self.env['slide.slide.partner'].search([('id', '=', '%d' % id_slide_partner)]))
|
|
|
|
|
|
@tagged('functional')
|
|
class TestAccessFeatures(common.SlidesCase):
|
|
|
|
@mute_logger('odoo.models', 'odoo.addons.base.models.ir_rule')
|
|
def test_channel_auto_subscription(self):
|
|
user_employees = self.env['res.users'].search([('groups_id', 'in', self.ref('base.group_user'))])
|
|
|
|
channel = self.env['slide.channel'].with_user(self.user_officer).create({
|
|
'name': 'Test',
|
|
'enroll': 'invite',
|
|
'is_published': True,
|
|
'enroll_group_ids': [(4, self.ref('base.group_user'))]
|
|
})
|
|
channel.invalidate_cache(['partner_ids'])
|
|
self.assertEqual(channel.partner_ids, user_employees.mapped('partner_id'))
|
|
|
|
new_user = self.env['res.users'].create({
|
|
'name': 'NewUser',
|
|
'login': 'NewUser',
|
|
'groups_id': [(6, 0, [self.ref('base.group_user')])]
|
|
})
|
|
channel.invalidate_cache()
|
|
self.assertEqual(channel.partner_ids, user_employees.mapped('partner_id') | new_user.partner_id)
|
|
|
|
new_user_2 = self.env['res.users'].create({
|
|
'name': 'NewUser2',
|
|
'login': 'NewUser2',
|
|
'groups_id': [(5, 0)]
|
|
})
|
|
channel.invalidate_cache()
|
|
self.assertEqual(channel.partner_ids, user_employees.mapped('partner_id') | new_user.partner_id)
|
|
new_user_2.write({'groups_id': [(4, self.ref('base.group_user'))]})
|
|
channel.invalidate_cache()
|
|
self.assertEqual(channel.partner_ids, user_employees.mapped('partner_id') | new_user.partner_id | new_user_2.partner_id)
|
|
|
|
new_user_3 = self.env['res.users'].create({
|
|
'name': 'NewUser3',
|
|
'login': 'NewUser3',
|
|
'groups_id': [(5, 0)]
|
|
})
|
|
channel.invalidate_cache()
|
|
self.assertEqual(channel.partner_ids, user_employees.mapped('partner_id') | new_user.partner_id | new_user_2.partner_id)
|
|
self.env.ref('base.group_user').write({'users': [(4, new_user_3.id)]})
|
|
channel.invalidate_cache()
|
|
self.assertEqual(channel.partner_ids, user_employees.mapped('partner_id') | new_user.partner_id | new_user_2.partner_id | new_user_3.partner_id)
|
|
|
|
@mute_logger('odoo.models', 'odoo.addons.base.models.ir_rule')
|
|
def test_channel_access_fields_employee(self):
|
|
channel_manager = self.channel.with_user(self.user_manager)
|
|
channel_emp = self.channel.with_user(self.user_emp)
|
|
channel_portal = self.channel.with_user(self.user_portal)
|
|
self.assertFalse(channel_emp.can_upload)
|
|
self.assertFalse(channel_emp.can_publish)
|
|
self.assertFalse(channel_portal.can_upload)
|
|
self.assertFalse(channel_portal.can_publish)
|
|
|
|
# allow employees to upload
|
|
channel_manager.write({'upload_group_ids': [(4, self.ref('base.group_user'))]})
|
|
self.assertTrue(channel_emp.can_upload)
|
|
self.assertFalse(channel_emp.can_publish)
|
|
self.assertFalse(channel_portal.can_upload)
|
|
self.assertFalse(channel_portal.can_publish)
|
|
|
|
@mute_logger('odoo.models', 'odoo.addons.base.models.ir_rule')
|
|
def test_channel_access_fields_officer(self):
|
|
self.assertEqual(self.channel.user_id, self.user_officer)
|
|
|
|
channel_officer = self.channel.with_user(self.user_officer)
|
|
self.assertTrue(channel_officer.can_upload)
|
|
self.assertTrue(channel_officer.can_publish)
|
|
|
|
channel_officer.write({'upload_group_ids': [(4, self.ref('base.group_system'))]})
|
|
self.assertTrue(channel_officer.can_upload)
|
|
self.assertTrue(channel_officer.can_publish)
|
|
|
|
channel_manager = self.channel.with_user(self.user_manager)
|
|
channel_manager.write({
|
|
'upload_group_ids': [(5, 0)],
|
|
'user_id': self.user_manager.id
|
|
})
|
|
self.assertFalse(channel_officer.can_upload)
|
|
self.assertFalse(channel_officer.can_publish)
|
|
self.assertTrue(channel_manager.can_upload)
|
|
self.assertTrue(channel_manager.can_publish)
|
|
|
|
@mute_logger('odoo.models', 'odoo.addons.base.models.ir_rule')
|
|
def test_channel_access_fields_manager(self):
|
|
channel_manager = self.channel.with_user(self.user_manager)
|
|
self.assertTrue(channel_manager.can_upload)
|
|
self.assertTrue(channel_manager.can_publish)
|
|
|
|
# test upload group limitation: member of group_system OR responsible OR manager
|
|
channel_manager.write({'upload_group_ids': [(4, self.ref('base.group_system'))]})
|
|
self.assertFalse(channel_manager.can_upload)
|
|
self.assertFalse(channel_manager.can_publish)
|
|
channel_manager.write({'user_id': self.user_manager.id})
|
|
self.assertTrue(channel_manager.can_upload)
|
|
self.assertTrue(channel_manager.can_publish)
|
|
|
|
# Needs the manager to write on channel as user_officer is not the responsible anymore
|
|
channel_manager.write({'upload_group_ids': [(5, 0)]})
|
|
self.assertTrue(channel_manager.can_upload)
|
|
self.assertTrue(channel_manager.can_publish)
|
|
channel_manager.write({'user_id': self.user_officer.id})
|
|
self.assertTrue(channel_manager.can_upload)
|
|
self.assertTrue(channel_manager.can_publish)
|
|
|
|
# superuser should always be able to publish even if he's not the responsible
|
|
channel_superuser = self.channel.sudo()
|
|
channel_superuser.invalidate_cache(['can_upload', 'can_publish'])
|
|
self.assertTrue(channel_superuser.can_upload)
|
|
self.assertTrue(channel_superuser.can_publish)
|