Passed
Push — master ( b69fb2...088870 )
by Alexander
02:33
created

MissingPermissionsChecker._check_for_missing_decorator()   B

Complexity

Conditions 8

Size

Total Lines 18
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 18
rs 7.3333
c 0
b 0
f 0
cc 8
nop 2
1
# Copyright (c) 2019 Alexander Todorov <[email protected]>
2
3
# Licensed under the GPL 2.0: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
4
5
import astroid
6
7
from pylint import interfaces
8
from pylint import checkers
9
10
11
class MissingPermissionsChecker(checkers.BaseChecker):
12
    """
13
        Will inspect functions and classes inside a views.py module for the
14
        presence of permissions decorator. May generate lots of false negatives
15
        but we're ok with that. Better inspect than forget to add permission
16
        decorator!
17
    """
18
    inside_views_module = False
19
20
    __implements__ = (interfaces.IAstroidChecker,)
21
22
    name = 'nested-definition-checker'
23
24
    msgs = {'R4511': ("View is missing @permission_required decorator!",
25
                      'missing-permission-required',
26
                      "All views must require permissions!")}
27
28
    def visit_module(self, node):
29
        self.inside_views_module = node.name.endswith('views')
30
31
    def visit_functiondef(self, node):
32
        if not self.inside_views_module:
33
            return
34
35
        arg0 = None
36
        if node.args.args:
37
            arg0 = node.args.args[0]
38
39
        if arg0 and arg0.name != 'request':
40
            return
41
        # this function is a confirmed view so start checking
42
        self._check_for_missing_decorator(node)
43
44
    def visit_classdef(self, node):
45
        if not self.inside_views_module:
46
            return
47
48
        # class based views always inherit from something
49
        # tip: we can be more precise what base classes are allowed in order
50
        # to identify cbv more correctly! Leaving it like that for now and will
51
        # revisit later if we start to see many false negatives!
52
        if not node.bases:
53
            return
54
55
        # for now we check all classes in views.py modules
56
        # until we learn how to recognize class based views
57
        self._check_for_missing_decorator(node)
58
59
    def _check_for_missing_decorator(self, node):
60
        if not node.decorators:
61
            self.add_message('missing-permission-required', node=node)
62
            return
63
64
        found_permissions_required = False
65
        for decorator in node.decorators.nodes:
66
            if isinstance(decorator, astroid.Call):
67
                if decorator.func.name == 'permission_required':
68
                    found_permissions_required = True
69
                    break
70
                elif decorator.func.name == 'method_decorator' and \
71
                        decorator.args[0].func.name == 'permission_required':
72
                    found_permissions_required = True
73
                    break
74
75
        if not found_permissions_required:
76
            self.add_message('missing-permission-required', node=node)
77