Passed
Push — 2.x ( d2822b...c87f85 )
by Ramon
05:06
created

senaite.core.adapters.localroles   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 91
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 8
eloc 41
dl 0
loc 91
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A DynamicLocalRoleAdapter.getRoles() 0 9 1
A DynamicLocalRoleAdapter.getRolesInContext() 0 32 4
A DynamicLocalRoleAdapter.getAllRoles() 0 10 3
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE.
4
#
5
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2022 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
import six
22
from bika.lims import api
23
from bika.lims import logger
24
from borg.localrole.default_adapter import DefaultLocalRoleAdapter
25
from collections import defaultdict
26
from senaite.core.interfaces import IDynamicLocalRoles
27
from zope.component import getAdapters
28
29
30
class DynamicLocalRoleAdapter(DefaultLocalRoleAdapter):
31
    """Gives additional Member local roles based on current user and context
32
    This enables giving additional permissions on items out of the user's
33
    current traverse path
34
    """
35
36
    _roles_in_context = defaultdict(dict)
37
38
    def getRolesInContext(self, context, principal_id):
39
        """Returns the dynamically calculated 'local' roles for the given
40
        principal and context
41
        @param context: context to calculate roles for the given principal
42
        @param principal_id: User login id
43
        @return List of dynamically calculated local-roles for user and context
44
        """
45
        if not api.is_object(context):
46
            # We only apply dynamic local roles to valid objects
47
            return []
48
49
        # This function is called a lot within same request, do some cache
50
        context_uid = api.get_uid(context)
51
        roles = self._roles_in_context.get(context_uid, {})
52
        if principal_id in roles:
53
            return roles.get(principal_id)
54
55
        # Look for adapters
56
        roles = set()
57
        path = api.get_path(context)
58
        adapters = getAdapters((context,), IDynamicLocalRoles)
59
        for name, adapter in adapters:
60
            local_roles = adapter.getRoles(principal_id)
61
            logger.info(u"{}::{}::{}: {}".format(name, path, principal_id,
62
                                                 repr(local_roles)))
63
            roles.update(local_roles)
64
65
        # Store in cache
66
        self._roles_in_context[context_uid].update({
67
            principal_id: list(roles)
68
        })
69
        return self._roles_in_context[context_uid][principal_id]
70
71
    def getRoles(self, principal_id):
72
        """Returns both non-local and local roles for the given principal in
73
        current context
74
        @param principal_id: User login id
75
        @return: list of non-local and local roles for the user and context
76
        """
77
        default_roles = self._rolemap.get(principal_id, [])
78
        dynamic_roles = self.getRolesInContext(self.context, principal_id)
79
        return list(set(default_roles + dynamic_roles))
80
81
    def getAllRoles(self):
82
        roles = {}
83
        # Iterate through all members to extract their dynamic local role for
84
        # current context
85
        mtool = api.get_tool("portal_membership")
86
        for principal_id in mtool.listMemberIds():
87
            user_roles = self.getRoles(principal_id)
88
            if user_roles:
89
                roles.update({principal_id: user_roles})
90
        return six.iteritems(roles)
91