Coverage for src/backend/InvenTree/users/admin.py: 66%

64 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 17:47 +0000

1"""Admin classes for the 'users' app.""" 

2 

3from django import forms 

4from django.contrib import admin 

5from django.contrib.admin.widgets import FilteredSelectMultiple 

6from django.contrib.auth import get_user_model 

7from django.contrib.auth.admin import UserAdmin 

8from django.contrib.auth.models import Group 

9from django.utils.translation import gettext_lazy as _ 

10 

11from users.models import ApiToken, Owner, RuleSet 

12from users.ruleset import RULESET_CHOICES 

13 

14User = get_user_model() 

15 

16 

17@admin.register(ApiToken) 

18class ApiTokenAdmin(admin.ModelAdmin): 

19 """Admin class for the ApiToken model.""" 

20 

21 list_display = ('token', 'user', 'name', 'expiry', 'active') 

22 list_filter = ('user', 'revoked') 

23 fields = ( 

24 'token', 

25 'user', 

26 'name', 

27 'created', 

28 'last_seen', 

29 'revoked', 

30 'expiry', 

31 'metadata', 

32 ) 

33 

34 def get_fields(self, request, obj=None): 

35 """Return list of fields to display.""" 

36 fields = ['token'] if obj else ['key'] 

37 

38 fields += [ 

39 'user', 

40 'name', 

41 'created', 

42 'last_seen', 

43 'revoked', 

44 'expiry', 

45 'metadata', 

46 ] 

47 

48 return fields 

49 

50 def get_readonly_fields(self, request, obj=None): 

51 """Some fields are read-only after creation.""" 

52 ro = ['created', 'last_seen'] 

53 

54 if obj: 

55 ro += ['token', 'user', 'expiry', 'name'] 

56 

57 return ro 

58 

59 

60class RuleSetInline(admin.TabularInline): 

61 """Class for displaying inline RuleSet data in the Group admin page.""" 

62 

63 model = RuleSet 

64 can_delete = False 

65 verbose_name = 'Ruleset' 

66 verbose_plural_name = 'Rulesets' 

67 fields = ['name', *list(RuleSet.RULE_OPTIONS)] 

68 readonly_fields = ['name'] 

69 max_num = len(RULESET_CHOICES) 

70 min_num = 1 

71 extra = 0 

72 ordering = ['name'] 

73 

74 

75class InvenTreeGroupAdminForm(forms.ModelForm): 

76 """Custom admin form for the Group model. 

77 

78 Adds the ability for editing user membership directly in the group admin page. 

79 """ 

80 

81 class Meta: 

82 """Metaclass defines extra fields.""" 

83 

84 model = Group 

85 exclude = [] 

86 fields = ['name', 'users'] 

87 

88 def __init__(self, *args, **kwargs): # pragma: no cover 

89 """Populate the 'users' field with the users in the current group.""" 

90 super().__init__(*args, **kwargs) 

91 

92 if self.instance.pk: 

93 # Populate the users field with the current Group users. 

94 self.fields['users'].initial = self.instance.user_set.all() 

95 

96 # Add the users field. 

97 users = forms.ModelMultipleChoiceField( 

98 queryset=User.objects.all(), 

99 required=False, 

100 widget=FilteredSelectMultiple('users', False), 

101 label=_('Users'), 

102 help_text=_('Select which users are assigned to this group'), 

103 ) 

104 

105 def save_m2m(self): # pragma: no cover 

106 """Add the users to the Group.""" 

107 self.instance.user_set.set(self.cleaned_data['users']) 

108 

109 def save(self, *args, **kwargs): # pragma: no cover 

110 """Custom save method for Group admin form.""" 

111 # Default save 

112 instance = super().save() 

113 # Save many-to-many data 

114 self.save_m2m() 

115 return instance 

116 

117 

118class InvenTreeUserAdmin(UserAdmin): 

119 """Custom admin page for the User model. 

120 

121 - Restrict user creation and editing to superuser accounts 

122 - Hides the "permissions" view as this is handled by RuleSets 

123 """ 

124 

125 list_display = ( 

126 'username', 

127 'email', 

128 'first_name', 

129 'last_name', 

130 'is_staff', 

131 'last_login', 

132 ) # display last connection for each user in user admin panel. 

133 

134 fieldsets = ( 

135 (None, {'fields': ('username', 'password')}), 

136 (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}), 

137 ( 

138 _('Permissions'), 

139 {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups')}, 

140 ), 

141 (_('Important dates'), {'fields': ('last_login', 'date_joined')}), 

142 ) 

143 

144 def get_readonly_fields(self, request, obj=None): 

145 """Make all fields read-only for non-superusers.""" 

146 fields = super().get_readonly_fields(request, obj) 

147 

148 if not request.user.is_superuser: 

149 fields += ('is_staff', 'is_superuser') 

150 

151 return fields 

152 

153 

154@admin.register(Owner) 

155class OwnerAdmin(admin.ModelAdmin): 

156 """Custom admin interface for the Owner model.""" 

157 

158 search_fields = ['name'] 

159 

160 

161admin.site.unregister(User) 

162admin.site.register(User, InvenTreeUserAdmin)