2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-17 20:45:44 +00:00

Added first UI components for user managment (#5875)

* Added first UI components for user managment
Ref #4962

* Add user roles to table and serializer

* added key to AddItem actions

* added ordering to group

* style text

* do not show unnecessary options

* fix admi / superuser usage

* switched to use BooleanColumn

* Added active column

* added user role change action

* added user active change action

* Added api change log

* fixed logical error

* added admin center to navigation

* added groups to user serializer

* added groups to the uI

* Added user drawer

* fixed active state

* remove actions as they are not usable after refactor

* move functions to drawer

* added drawer lock state

* added edit toggle

* merge fix

* renamed values

* remove empty roles section

* fix settings header

* make title shorter to reducelayout shift when switching to server settings
This commit is contained in:
Matthias Mair
2023-11-13 02:48:57 +01:00
committed by GitHub
parent 41296e4574
commit 33c02fcd78
11 changed files with 631 additions and 53 deletions

View File

@ -2,10 +2,15 @@
# InvenTree API version
INVENTREE_API_VERSION = 149
INVENTREE_API_VERSION = 150
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """
v150 -> 2023-11-07: https://github.com/inventree/InvenTree/pull/5875
- Extended user API endpoints to enable ordering
- Extended user API endpoints to enable user role changes
- Added endpoint to create a new user
v149 -> 2023-11-07 : https://github.com/inventree/InvenTree/pull/5876
- Add 'building' quantity to BomItem serializer
- Add extra ordering options for the BomItem list API

View File

@ -6,6 +6,7 @@ from decimal import Decimal
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.exceptions import ValidationError as DjangoValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
@ -15,7 +16,7 @@ from djmoney.contrib.django_rest_framework.fields import MoneyField
from djmoney.money import Money
from djmoney.utils import MONEY_CLASSES, get_currency_field_name
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.fields import empty
from rest_framework.serializers import DecimalField
from rest_framework.utils import model_meta
@ -302,6 +303,72 @@ class UserSerializer(InvenTreeModelSerializer):
]
class ExendedUserSerializer(UserSerializer):
"""Serializer for a User with a bit more info."""
from users.serializers import GroupSerializer
groups = GroupSerializer(read_only=True, many=True)
class Meta(UserSerializer.Meta):
"""Metaclass defines serializer fields."""
fields = UserSerializer.Meta.fields + [
'groups',
'is_staff',
'is_superuser',
'is_active'
]
read_only_fields = UserSerializer.Meta.read_only_fields + [
'groups',
]
def validate(self, attrs):
"""Expanded validation for changing user role."""
# Check if is_staff or is_superuser is in attrs
role_change = 'is_staff' in attrs or 'is_superuser' in attrs
request_user = self.context['request'].user
if role_change:
if request_user.is_superuser:
# Superusers can change any role
pass
elif request_user.is_staff and 'is_superuser' not in attrs:
# Staff can change any role except is_superuser
pass
else:
raise PermissionDenied(_("You do not have permission to change this user role."))
return super().validate(attrs)
class UserCreateSerializer(ExendedUserSerializer):
"""Serializer for creating a new User."""
def validate(self, attrs):
"""Expanded valiadation for auth."""
# Check that the user trying to create a new user is a superuser
if not self.context['request'].user.is_superuser:
raise serializers.ValidationError(_("Only superusers can create new users"))
# Generate a random password
password = User.objects.make_random_password(length=14)
attrs.update({'password': password})
return super().validate(attrs)
def create(self, validated_data):
"""Send an e email to the user after creation."""
instance = super().create(validated_data)
# Make sure the user cannot login until they have set a password
instance.set_unusable_password()
# Send the user an onboarding email (from current site)
current_site = Site.objects.get_current()
domain = current_site.domain
instance.email_user(
subject=_(f"Welcome to {current_site.name}"),
message=_(f"Your account has been created.\n\nPlease use the password reset function to get access (at https://{domain})."),
)
return instance
class InvenTreeAttachmentSerializerField(serializers.FileField):
"""Override the DRF native FileField serializer, to remove the leading server path.

View File

@ -6,14 +6,14 @@ import logging
from django.contrib.auth.models import Group, User
from django.urls import include, path, re_path
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import exceptions, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from InvenTree.filters import InvenTreeSearchFilter
from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateAPI
from InvenTree.serializers import UserSerializer
from InvenTree.filters import SEARCH_ORDER_FILTER
from InvenTree.mixins import (ListAPI, ListCreateAPI, RetrieveAPI,
RetrieveUpdateAPI, RetrieveUpdateDestroyAPI)
from InvenTree.serializers import ExendedUserSerializer, UserCreateSerializer
from users.models import ApiToken, Owner, RuleSet, check_user_role
from users.serializers import GroupSerializer, OwnerSerializer
@ -112,11 +112,11 @@ class RoleDetails(APIView):
return Response(data)
class UserDetail(RetrieveAPI):
class UserDetail(RetrieveUpdateDestroyAPI):
"""Detail endpoint for a single user."""
queryset = User.objects.all()
serializer_class = UserSerializer
serializer_class = ExendedUserSerializer
permission_classes = [
permissions.IsAuthenticated
]
@ -130,19 +130,15 @@ class MeUserDetail(RetrieveUpdateAPI, UserDetail):
return self.request.user
class UserList(ListAPI):
class UserList(ListCreateAPI):
"""List endpoint for detail on all users."""
queryset = User.objects.all()
serializer_class = UserSerializer
serializer_class = UserCreateSerializer
permission_classes = [
permissions.IsAuthenticated,
]
filter_backends = [
DjangoFilterBackend,
InvenTreeSearchFilter,
]
filter_backends = SEARCH_ORDER_FILTER
search_fields = [
'first_name',
@ -150,8 +146,18 @@ class UserList(ListAPI):
'username',
]
ordering_fields = [
'email',
'username',
'first_name',
'last_name',
'is_staff',
'is_superuser',
'is_active',
]
class GroupDetail(RetrieveAPI):
class GroupDetail(RetrieveUpdateDestroyAPI):
"""Detail endpoint for a particular auth group"""
queryset = Group.objects.all()
@ -161,7 +167,7 @@ class GroupDetail(RetrieveAPI):
]
class GroupList(ListAPI):
class GroupList(ListCreateAPI):
"""List endpoint for all auth groups"""
queryset = Group.objects.all()
@ -170,15 +176,16 @@ class GroupList(ListAPI):
permissions.IsAuthenticated,
]
filter_backends = [
DjangoFilterBackend,
InvenTreeSearchFilter,
]
filter_backends = SEARCH_ORDER_FILTER
search_fields = [
'name',
]
ordering_fields = [
'name',
]
class GetAuthToken(APIView):
"""Return authentication token for an authenticated user."""