Coverage for src/model_permissions/views.py: 100.00%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

49 statements  

1""" 

2django-model-permissions - simple object permissions for django. 

3 

4Copyright (C) 2018 Mathias Stelzer 

5 

6This program is free software: you can redistribute it and/or modify 

7it under the terms of the GNU General Public License as published by 

8the Free Software Foundation, either version 3 of the License, or 

9(at your option) any later version. 

10 

11This program is distributed in the hope that it will be useful, 

12but WITHOUT ANY WARRANTY; without even the implied warranty of 

13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

14GNU General Public License for more details. 

15 

16You should have received a copy of the GNU General Public License 

17along with this program. If not, see <http://www.gnu.org/licenses/>. 

18""" 

19from django.contrib.auth.context_processors import PermLookupDict, PermWrapper 

20from django.core.exceptions import PermissionDenied 

21from django.views.generic import ( 

22 DetailView as DjangoDetailView, 

23 UpdateView as DjangoUpdateView, 

24 DeleteView as DjangoDeleteView, 

25) 

26 

27 

28class ObjPermLookupDict(PermLookupDict): 

29 """Object permissions lookup dictionary.""" 

30 

31 def __init__(self, user, app_label, obj=None): 

32 """Initialize object permission lookup dictionary.""" 

33 super(ObjPermLookupDict, self).__init__(user, app_label) 

34 self.object = obj 

35 

36 def __getitem__(self, perm_name): 

37 """Check whether the user has the given permission for the object.""" 

38 return self.user.has_perm('{}.{}'.format(self.app_label, perm_name), self.object) 

39 

40 

41class ObjPermWrapper(PermWrapper): 

42 """Object permissions wrapper to traverse a list of permissions.""" 

43 

44 def __init__(self, user, obj=None): 

45 """Initialize object permissions wrapper.""" 

46 super(ObjPermWrapper, self).__init__(user) 

47 self.object = obj 

48 

49 def __getitem__(self, app_label): 

50 """Get a lookup dictionary for the object.""" 

51 return ObjPermLookupDict(self.user, app_label, self.object) 

52 

53 

54class PermissionContextMixin(object): 

55 """ 

56 View mixin to insert permissions in the template context. 

57 

58 If the `get_context_data` method is called and the context contains 

59 an `'object'` element, this will insert a `'object_perms'` permission 

60 wrapper. 

61 

62 The `get_context_object_name` method will be used too, if it exists. 

63 """ 

64 

65 def get_context_data(self, **kwargs): 

66 """Add object permissions to template context.""" 

67 kwargs = super().get_context_data(**kwargs) 

68 if getattr(self, 'object', None): 

69 permission_wrapper = ObjPermWrapper(self.request.user, self.object) 

70 kwargs['object_perms'] = permission_wrapper 

71 if hasattr(self, 'get_context_object_name'): 

72 context_object_name = self.get_context_object_name(self.object) 

73 if context_object_name: 

74 kwargs[context_object_name + '_perms'] = permission_wrapper 

75 return kwargs 

76 

77 

78class RequirePermissionMixin(object): 

79 """View mixin to require object permissions for access.""" 

80 

81 required_permission = None 

82 

83 # noinspection PyMethodMayBeStatic 

84 def get_required_permission_object(self, obj): 

85 """Overwrite to check the required_permission against another model object.""" 

86 return obj 

87 

88 def check_required_permission(self, permission_object): 

89 """ 

90 Check the `required_permission` on the given object. 

91 

92 :raises: :class:`django.core.exceptions.PermissionDenied` if the 

93 current user does not have the `required_permission`. 

94 """ 

95 if not self.has_perm(self.required_permission, permission_object): 

96 raise PermissionDenied() 

97 

98 def get_object(self, queryset=None): 

99 """ 

100 Get the object and check the `required_permission` on it. 

101 

102 :raises: :class:`django.core.exceptions.PermissionDenied` if the 

103 current user does not have the `required_permission`. 

104 """ 

105 obj = super().get_object(queryset=queryset) 

106 

107 if self.required_permission is not None: 

108 perm_obj = self.get_required_permission_object(obj) 

109 self.check_required_permission(perm_obj) 

110 

111 return obj 

112 

113 def has_perm(self, perm, obj=None): 

114 """Check the given permission on the given object.""" 

115 return self.request.user.has_perm(perm, obj=obj) 

116 

117 

118class PermissionMixin(RequirePermissionMixin, PermissionContextMixin): 

119 """View mixin to enable permissions.""" 

120 

121 pass 

122 

123 

124class DetailView(PermissionMixin, DjangoDetailView): 

125 """DetailView with permissions.""" 

126 

127 pass 

128 

129 

130class UpdateView(PermissionMixin, DjangoUpdateView): 

131 """UpdateView with permissions.""" 

132 

133 pass 

134 

135 

136class DeleteView(PermissionMixin, DjangoDeleteView): 

137 """DeleteView with permissions.""" 

138 

139 pass