Coverage for src/model_permissions/backends.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

54 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.conf import settings 

20from django.contrib.auth.backends import ( 

21 ModelBackend as DjangoModelBackend, 

22 AllowAllUsersModelBackend as DjangoAllowAllUsersModelBackend, 

23 RemoteUserBackend as DjangoRemoteUserBackend, 

24 AllowAllUsersRemoteUserBackend as DjangoAllowAllUsersRemoteUserBackend, 

25) 

26from django.utils.module_loading import import_string 

27 

28 

29# mixins 

30 

31class BaseObjectBackendMixin(object): 

32 """Base class for object permission backends.""" 

33 

34 def get_permission_function(self, obj): 

35 """ 

36 Get the function to list the given objects permissions. 

37 

38 This checks the given object for a ``get_permissions`` method and, 

39 if it doesn't exist, the ``MODEL_PERMISSIONS`` setting to find the 

40 objects :ref:`manual-permission-function`. 

41 

42 :param obj: The model instance to get the function for. 

43 :type obj: :class:`django.db.models.Model` 

44 :return: The function to list the objects permissions. 

45 :rtype: :class:`callable` 

46 """ 

47 get_permissions = getattr(obj, 'get_permissions', None) 

48 if get_permissions is not None: 

49 return self.model_permission_wrapper 

50 

51 model_permissions = getattr(settings, 'MODEL_PERMISSIONS', {}) 

52 f = model_permissions.get(obj._meta.label, None) 

53 if f is None: 

54 return 

55 

56 if callable(f): 

57 return f 

58 

59 # assume string 

60 return import_string(f) 

61 

62 def model_permission_wrapper(self, user_obj, obj): 

63 """Object permission wrapper for the `get_permissions` model method.""" 

64 return obj.get_permissions(user_obj) 

65 

66 def get_object_permissions(self, user_obj, obj=None): 

67 """ 

68 Get object permissions for the given user and object. 

69 

70 Exit early and return an empty set if: 

71 

72 * no object is given 

73 * the user is 

74 

75 * inactive 

76 * anonymous 

77 * a superuser 

78 

79 * no permission function can be found 

80 

81 :param user_obj: User instance. 

82 :type user_obj: :class:`django.contrib.auth.models.AbstractUser` 

83 :param obj: Model instance. 

84 :type obj: :class:`django.db.models.Model` 

85 :return: Set of permissions. 

86 :rtype: :class:`set` 

87 """ 

88 if obj is None: 

89 return set() 

90 

91 get_permissions = self.get_permission_function(obj) 

92 if get_permissions is None: 

93 return set() 

94 

95 perm_cache_name = '_{}_{}_perm_cache'.format(obj._meta.label, obj.pk) 

96 if not hasattr(user_obj, perm_cache_name): 

97 perms = get_permissions(user_obj, obj) 

98 if perms is None: 

99 perms = set() 

100 setattr(user_obj, perm_cache_name, perms) 

101 return getattr(user_obj, perm_cache_name) 

102 

103 

104class ObjectModelBackendMixin(BaseObjectBackendMixin): 

105 """Mixin for dual permission authentication backends.""" 

106 

107 def get_all_permissions(self, user_obj, obj=None): 

108 """Get all permissions for the given user and object.""" 

109 perms = super().get_all_permissions(user_obj) 

110 perms.update(self.get_object_permissions(user_obj, obj=obj)) 

111 return perms 

112 

113 

114class ObjectBackendMixin(BaseObjectBackendMixin): 

115 """Mixin for object-only permission authentication backends.""" 

116 

117 def get_all_permissions(self, user_obj, obj=None): 

118 """Get all permissions for the given user and object.""" 

119 return self.get_object_permissions(user_obj, obj=obj) 

120 

121 

122# backends 

123 

124class ObjectBackend(ObjectBackendMixin, DjangoModelBackend): 

125 """An authentication backend with object permissions only.""" 

126 

127 pass 

128 

129 

130class ObjectModelBackend(ObjectModelBackendMixin, DjangoModelBackend): 

131 """An authentication backend with default and object permissions.""" 

132 

133 pass 

134 

135 

136class AllowAllUsersObjectBackend(ObjectBackendMixin, DjangoAllowAllUsersModelBackend): 

137 """Django's `AllowAllUsersModelBackend` with object permissions only.""" 

138 

139 pass 

140 

141 

142class AllowAllUsersObjectModelBackend(ObjectModelBackendMixin, DjangoAllowAllUsersModelBackend): 

143 """Django's `AllowAllUsersModelBackend` with dual permissions.""" 

144 

145 pass 

146 

147 

148class RemoteUserObjectBackend(ObjectBackendMixin, DjangoRemoteUserBackend): 

149 """Django's `RemoteUserBackend` with object permissions only.""" 

150 

151 pass 

152 

153 

154class RemoteUserBackend(ObjectModelBackendMixin, DjangoRemoteUserBackend): 

155 """Django's `RemoteUserBackend` with dual permissions.""" 

156 

157 pass 

158 

159 

160class AllowAllUsersRemoteUserObjectBackend(ObjectBackendMixin, DjangoAllowAllUsersRemoteUserBackend): 

161 """Django's `AllowAllUsersRemoteUserBackend` with object permissions only.""" 

162 

163 pass 

164 

165 

166class AllowAllUsersRemoteUserBackend(ObjectModelBackendMixin, DjangoAllowAllUsersRemoteUserBackend): 

167 """Django's `AllowAllUsersRemoteUserBackend` with dual permissions.""" 

168 

169 pass