feat: user manage
This commit is contained in:
parent
5481b8a2aa
commit
06f834b7e3
@ -154,7 +154,7 @@ class PermissionConstants(Enum):
|
|||||||
USER_READ = Permission(group=Group.USER, operate=Operate.READ, role_list=[RoleConstants.ADMIN,
|
USER_READ = Permission(group=Group.USER, operate=Operate.READ, role_list=[RoleConstants.ADMIN,
|
||||||
RoleConstants.USER])
|
RoleConstants.USER])
|
||||||
USER_CREATE = Permission(group=Group.USER, operate=Operate.CREATE,
|
USER_CREATE = Permission(group=Group.USER, operate=Operate.CREATE,
|
||||||
role_list=[RoleConstants.ADMIN, RoleConstants.USER])
|
role_list=[RoleConstants.ADMIN])
|
||||||
USER_EDIT = Permission(group=Group.USER, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN])
|
USER_EDIT = Permission(group=Group.USER, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN])
|
||||||
USER_DELETE = Permission(group=Group.USER, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN])
|
USER_DELETE = Permission(group=Group.USER, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN])
|
||||||
|
|
||||||
|
|||||||
@ -4,19 +4,14 @@ from . import views
|
|||||||
|
|
||||||
app_name = "models_provider"
|
app_name = "models_provider"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('provider', views.Provide.as_view(), name='provide'),
|
path('provider', views.Provide.as_view()),
|
||||||
path('provider/model_type_list', views.Provide.ModelTypeList.as_view(), name="provider/model_type_list"),
|
path('provider/model_type_list', views.Provide.ModelTypeList.as_view()),
|
||||||
path('provider/model_list', views.Provide.ModelList.as_view(), name="provider/model_name_list"),
|
path('provider/model_list', views.Provide.ModelList.as_view()),
|
||||||
path('provider/model_params_form', views.Provide.ModelParamsForm.as_view(),
|
path('provider/model_params_form', views.Provide.ModelParamsForm.as_view()),
|
||||||
name="provider/model_params_form"),
|
path('provider/model_form', views.Provide.ModelForm.as_view()),
|
||||||
path('provider/model_form', views.Provide.ModelForm.as_view(),
|
path('workspace/<str:workspace_id>/model', views.Model.as_view()),
|
||||||
name="provider/model_form"),
|
path('workspace/<str:workspace_id>/model/<str:model_id>/model_params_form', views.Model.ModelParamsForm.as_view()),
|
||||||
path('workspace/<str:workspace_id>/model', views.Model.as_view(), name='model'),
|
path('workspace/<str:workspace_id>/model/<str:model_id>', views.Model.Operate.as_view()),
|
||||||
path('workspace/<str:workspace_id>/model/<str:model_id>/model_params_form', views.Model.ModelParamsForm.as_view(),
|
path('workspace/<str:workspace_id>/model/<str:model_id>/pause_download', views.Model.PauseDownload.as_view()),
|
||||||
name='model/model_params_form'),
|
path('workspace/<str:workspace_id>/model/<str:model_id>/meta', views.Model.ModelMeta.as_view()),
|
||||||
path('workspace/<str:workspace_id>/model/<str:model_id>', views.Model.Operate.as_view(), name='model/operate'),
|
|
||||||
path('workspace/<str:workspace_id>/model/<str:model_id>/pause_download', views.Model.PauseDownload.as_view(),
|
|
||||||
name='model/operate'),
|
|
||||||
path('workspace/<str:workspace_id>/model/<str:model_id>/meta', views.Model.ModelMeta.as_view(),
|
|
||||||
name='model/operate/meta'),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@ -14,7 +14,7 @@ from rest_framework.request import Request
|
|||||||
from common.auth import TokenAuth
|
from common.auth import TokenAuth
|
||||||
from common.auth.authentication import has_permissions
|
from common.auth.authentication import has_permissions
|
||||||
from common.constants.permission_constants import PermissionConstants
|
from common.constants.permission_constants import PermissionConstants
|
||||||
from common.result import result, DefaultResultSerializer
|
from common.result import result
|
||||||
from common.utils.common import query_params_to_single_dict
|
from common.utils.common import query_params_to_single_dict
|
||||||
from models_provider.api.model import ModelCreateAPI, GetModelApi, ModelEditApi, ModelListResponse, DefaultModelResponse
|
from models_provider.api.model import ModelCreateAPI, GetModelApi, ModelEditApi, ModelListResponse, DefaultModelResponse
|
||||||
from models_provider.api.provide import ProvideApi
|
from models_provider.api.provide import ProvideApi
|
||||||
@ -25,6 +25,7 @@ class Model(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['POST'],
|
@extend_schema(methods=['POST'],
|
||||||
|
summary=_("Create model"),
|
||||||
description=_("Create model"),
|
description=_("Create model"),
|
||||||
operation_id=_("Create model"),
|
operation_id=_("Create model"),
|
||||||
tags=[_("Model")],
|
tags=[_("Model")],
|
||||||
@ -38,7 +39,7 @@ class Model(APIView):
|
|||||||
with_valid=True))
|
with_valid=True))
|
||||||
|
|
||||||
# @extend_schema(methods=['PUT'],
|
# @extend_schema(methods=['PUT'],
|
||||||
# description=_('Update model'),
|
# summary=_('Update model'),
|
||||||
# operation_id=_('Update model'),
|
# operation_id=_('Update model'),
|
||||||
# request=ModelEditApi.get_request(),
|
# request=ModelEditApi.get_request(),
|
||||||
# responses=ModelCreateApi.get_response(),
|
# responses=ModelCreateApi.get_response(),
|
||||||
@ -50,6 +51,7 @@ class Model(APIView):
|
|||||||
# with_valid=True))
|
# with_valid=True))
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_('Query model list'),
|
||||||
description=_('Query model list'),
|
description=_('Query model list'),
|
||||||
operation_id=_('Query model list'),
|
operation_id=_('Query model list'),
|
||||||
parameters=ModelCreateAPI.get_query_params_api(),
|
parameters=ModelCreateAPI.get_query_params_api(),
|
||||||
@ -66,6 +68,7 @@ class Model(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['PUT'],
|
@extend_schema(methods=['PUT'],
|
||||||
|
summary=_('Update model'),
|
||||||
description=_('Update model'),
|
description=_('Update model'),
|
||||||
operation_id=_('Update model'),
|
operation_id=_('Update model'),
|
||||||
request=ModelEditApi.get_request(),
|
request=ModelEditApi.get_request(),
|
||||||
@ -79,6 +82,7 @@ class Model(APIView):
|
|||||||
str(request.user.id)))
|
str(request.user.id)))
|
||||||
|
|
||||||
@extend_schema(methods=['DELETE'],
|
@extend_schema(methods=['DELETE'],
|
||||||
|
summary=_('Delete model'),
|
||||||
description=_('Delete model'),
|
description=_('Delete model'),
|
||||||
operation_id=_('Delete model'),
|
operation_id=_('Delete model'),
|
||||||
parameters=GetModelApi.get_query_params_api(),
|
parameters=GetModelApi.get_query_params_api(),
|
||||||
@ -90,6 +94,7 @@ class Model(APIView):
|
|||||||
ModelSerializer.Operate(data={'id': model_id, 'user_id': request.user.id}).delete())
|
ModelSerializer.Operate(data={'id': model_id, 'user_id': request.user.id}).delete())
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_('Query model details'),
|
||||||
description=_('Query model details'),
|
description=_('Query model details'),
|
||||||
operation_id=_('Query model details'),
|
operation_id=_('Query model details'),
|
||||||
parameters=GetModelApi.get_query_params_api(),
|
parameters=GetModelApi.get_query_params_api(),
|
||||||
@ -104,6 +109,7 @@ class Model(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_('Get model parameter form'),
|
||||||
description=_('Get model parameter form'),
|
description=_('Get model parameter form'),
|
||||||
operation_id=_('Get model parameter form'),
|
operation_id=_('Get model parameter form'),
|
||||||
parameters=GetModelApi.get_query_params_api(),
|
parameters=GetModelApi.get_query_params_api(),
|
||||||
@ -115,6 +121,7 @@ class Model(APIView):
|
|||||||
ModelSerializer.ModelParams(data={'id': model_id}).get_model_params())
|
ModelSerializer.ModelParams(data={'id': model_id}).get_model_params())
|
||||||
|
|
||||||
@extend_schema(methods=['PUT'],
|
@extend_schema(methods=['PUT'],
|
||||||
|
summary=_('Save model parameter form'),
|
||||||
description=_('Save model parameter form'),
|
description=_('Save model parameter form'),
|
||||||
operation_id=_('Save model parameter form'),
|
operation_id=_('Save model parameter form'),
|
||||||
parameters=GetModelApi.get_query_params_api(),
|
parameters=GetModelApi.get_query_params_api(),
|
||||||
@ -130,6 +137,8 @@ class Model(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_(
|
||||||
|
'Query model meta information, this interface does not carry authentication information'),
|
||||||
description=_(
|
description=_(
|
||||||
'Query model meta information, this interface does not carry authentication information'),
|
'Query model meta information, this interface does not carry authentication information'),
|
||||||
operation_id=_(
|
operation_id=_(
|
||||||
@ -146,6 +155,7 @@ class Model(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['PUT'],
|
@extend_schema(methods=['PUT'],
|
||||||
|
summary=_('Pause model download'),
|
||||||
description=_('Pause model download'),
|
description=_('Pause model download'),
|
||||||
operation_id=_('Pause model download'),
|
operation_id=_('Pause model download'),
|
||||||
parameters=GetModelApi.get_query_params_api(),
|
parameters=GetModelApi.get_query_params_api(),
|
||||||
|
|||||||
@ -18,6 +18,7 @@ class Provide(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_('Get a list of model suppliers'),
|
||||||
description=_('Get a list of model suppliers'),
|
description=_('Get a list of model suppliers'),
|
||||||
operation_id=_('Get a list of model suppliers'),
|
operation_id=_('Get a list of model suppliers'),
|
||||||
responses=ProvideApi.get_response(),
|
responses=ProvideApi.get_response(),
|
||||||
@ -40,6 +41,7 @@ class Provide(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_('Get a list of model types'),
|
||||||
description=_('Get a list of model types'),
|
description=_('Get a list of model types'),
|
||||||
operation_id=_('Get a list of model types'),
|
operation_id=_('Get a list of model types'),
|
||||||
parameters=ProvideApi.ModelTypeList.get_query_params_api(),
|
parameters=ProvideApi.ModelTypeList.get_query_params_api(),
|
||||||
@ -54,6 +56,7 @@ class Provide(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_('Example of obtaining model list'),
|
||||||
description=_('Example of obtaining model list'),
|
description=_('Example of obtaining model list'),
|
||||||
operation_id=_('Example of obtaining model list'),
|
operation_id=_('Example of obtaining model list'),
|
||||||
parameters=ProvideApi.ModelList.get_query_params_api(),
|
parameters=ProvideApi.ModelList.get_query_params_api(),
|
||||||
@ -72,6 +75,7 @@ class Provide(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_('Get model default parameters'),
|
||||||
description=_('Get model default parameters'),
|
description=_('Get model default parameters'),
|
||||||
operation_id=_('Get model default parameters'),
|
operation_id=_('Get model default parameters'),
|
||||||
parameters=ProvideApi.ModelParamsForm.get_query_params_api(),
|
parameters=ProvideApi.ModelParamsForm.get_query_params_api(),
|
||||||
@ -89,6 +93,7 @@ class Provide(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_('Get the model creation form'),
|
||||||
description=_('Get the model creation form'),
|
description=_('Get the model creation form'),
|
||||||
operation_id=_('Get the model creation form'),
|
operation_id=_('Get the model creation form'),
|
||||||
parameters=ProvideApi.ModelParamsForm.get_query_params_api(),
|
parameters=ProvideApi.ModelParamsForm.get_query_params_api(),
|
||||||
|
|||||||
@ -11,7 +11,10 @@ from drf_spectacular.utils import OpenApiParameter
|
|||||||
|
|
||||||
from common.mixins.api_mixin import APIMixin
|
from common.mixins.api_mixin import APIMixin
|
||||||
from common.result import ResultSerializer
|
from common.result import ResultSerializer
|
||||||
from users.serializers.user import UserProfileResponse, CreateUserSerializer
|
from users.serializers.user import UserProfileResponse, CreateUserSerializer, UserManageSerializer, \
|
||||||
|
UserInstanceSerializer
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
class ApiUserProfileResponse(ResultSerializer):
|
class ApiUserProfileResponse(ResultSerializer):
|
||||||
@ -29,6 +32,87 @@ class UserProfileAPI(APIMixin):
|
|||||||
def get_request():
|
def get_request():
|
||||||
return CreateUserSerializer
|
return CreateUserSerializer
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_parameters():
|
||||||
|
return [OpenApiParameter(
|
||||||
|
name="user_id",
|
||||||
|
description=_('User ID'),
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
location=OpenApiParameter.PATH,
|
||||||
|
required=True,
|
||||||
|
)]
|
||||||
|
|
||||||
|
|
||||||
|
class EditUserApi(APIMixin):
|
||||||
|
@staticmethod
|
||||||
|
def get_parameters():
|
||||||
|
return [OpenApiParameter(
|
||||||
|
name="user_id",
|
||||||
|
description=_('User ID'),
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
location=OpenApiParameter.PATH,
|
||||||
|
required=True,
|
||||||
|
)]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_request():
|
||||||
|
return UserManageSerializer.UserEditInstance
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteUserApi(APIMixin):
|
||||||
|
@staticmethod
|
||||||
|
def get_parameters():
|
||||||
|
return [OpenApiParameter(
|
||||||
|
name="user_id",
|
||||||
|
description=_('User ID'),
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
location=OpenApiParameter.PATH,
|
||||||
|
required=True,
|
||||||
|
)]
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeUserPasswordApi(APIMixin):
|
||||||
|
@staticmethod
|
||||||
|
def get_request():
|
||||||
|
return UserManageSerializer.RePasswordInstance
|
||||||
|
|
||||||
|
|
||||||
|
class UserListResponse(ResultSerializer):
|
||||||
|
def get_data(self):
|
||||||
|
return serializers.ListSerializer(child=UserInstanceSerializer())
|
||||||
|
|
||||||
|
|
||||||
|
class UserPageApi(APIMixin):
|
||||||
|
@staticmethod
|
||||||
|
def get_parameters():
|
||||||
|
return [OpenApiParameter(
|
||||||
|
name="email_or_username",
|
||||||
|
description=_('Email or Username'),
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
location=OpenApiParameter.QUERY,
|
||||||
|
required=False,
|
||||||
|
)]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_response():
|
||||||
|
return UserListResponse
|
||||||
|
|
||||||
|
|
||||||
|
class UserListApi(APIMixin):
|
||||||
|
@staticmethod
|
||||||
|
def get_parameters():
|
||||||
|
return [OpenApiParameter(
|
||||||
|
name="workspace_id",
|
||||||
|
description=_('Workspace ID'),
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
location=OpenApiParameter.PATH,
|
||||||
|
required=False,
|
||||||
|
)]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_response():
|
||||||
|
return UserListResponse
|
||||||
|
|
||||||
|
|
||||||
class TestWorkspacePermissionUserApi(APIMixin):
|
class TestWorkspacePermissionUserApi(APIMixin):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@ -5,13 +5,15 @@ from django.db import migrations, models
|
|||||||
|
|
||||||
from common.constants.permission_constants import RoleConstants
|
from common.constants.permission_constants import RoleConstants
|
||||||
from common.utils.common import password_encrypt
|
from common.utils.common import password_encrypt
|
||||||
|
from maxkb.const import CONFIG
|
||||||
|
|
||||||
|
default_password = CONFIG.get('default_password', 'MaxKB@123..')
|
||||||
|
|
||||||
def insert_default_data(apps, schema_editor):
|
def insert_default_data(apps, schema_editor):
|
||||||
UserModel = apps.get_model('users', 'User')
|
UserModel = apps.get_model('users', 'User')
|
||||||
UserModel.objects.create(id='f0dd8f71-e4ee-11ee-8c84-a8a1595801ab', email='', username='admin',
|
UserModel.objects.create(id='f0dd8f71-e4ee-11ee-8c84-a8a1595801ab', email='', username='admin',
|
||||||
nick_name="系统管理员",
|
nick_name="系统管理员",
|
||||||
password=password_encrypt('MaxKB@123..'),
|
password=password_encrypt(default_password),
|
||||||
role=RoleConstants.ADMIN.name,
|
role=RoleConstants.ADMIN.name,
|
||||||
is_active=True)
|
is_active=True)
|
||||||
|
|
||||||
|
|||||||
@ -9,16 +9,23 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import QuerySet, Q
|
from django.db.models import Q, QuerySet
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
import uuid_utils.compat as uuid
|
import uuid_utils.compat as uuid
|
||||||
from common.constants.exception_code_constants import ExceptionCodeConstants
|
from common.constants.exception_code_constants import ExceptionCodeConstants
|
||||||
from common.constants.permission_constants import RoleConstants, Auth
|
from common.constants.permission_constants import RoleConstants, Auth
|
||||||
|
from common.db.search import page_search
|
||||||
|
from common.exception.app_exception import AppApiException
|
||||||
from common.utils.common import valid_license, password_encrypt
|
from common.utils.common import valid_license, password_encrypt
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
|
|
||||||
|
PASSWORD_REGEX = re.compile(
|
||||||
|
r"^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z_!@#$%^&*`~.()-+=]+$)(?![a-z0-9]+$)(?![a-z_!@#$%^&*`~()-+=]+$)"
|
||||||
|
r"(?![0-9_!@#$%^&*`~()-+=]+$)[a-zA-Z0-9_!@#$%^&*`~.()-+=]{6,20}$"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserProfileResponse(serializers.ModelSerializer):
|
class UserProfileResponse(serializers.ModelSerializer):
|
||||||
is_edit_password = serializers.BooleanField(required=True, label=_('Is Edit Password'))
|
is_edit_password = serializers.BooleanField(required=True, label=_('Is Edit Password'))
|
||||||
@ -46,15 +53,23 @@ class UserProfileSerializer(serializers.Serializer):
|
|||||||
@param auth: 认证对象
|
@param auth: 认证对象
|
||||||
@return:
|
@return:
|
||||||
"""
|
"""
|
||||||
|
return {
|
||||||
|
'id': user.id,
|
||||||
|
'username': user.username,
|
||||||
|
'nick_name': user.nick_name,
|
||||||
|
'email': user.email,
|
||||||
|
'role': auth.role_list,
|
||||||
|
'permissions': auth.permission_list,
|
||||||
|
'is_edit_password': user.role == RoleConstants.ADMIN.name and user.password == 'd880e722c47a34d8e9fce789fc62389d',
|
||||||
|
'language': user.language,
|
||||||
|
}
|
||||||
|
|
||||||
return {'id': user.id,
|
|
||||||
'username': user.username,
|
class UserInstanceSerializer(serializers.ModelSerializer):
|
||||||
'nick_name': user.nick_name,
|
class Meta:
|
||||||
'email': user.email,
|
model = User
|
||||||
'role': auth.role_list,
|
fields = ['id', 'username', 'email', 'phone', 'is_active', 'role', 'nick_name', 'create_time', 'update_time',
|
||||||
'permissions': auth.permission_list,
|
'source']
|
||||||
'is_edit_password': user.password == 'd880e722c47a34d8e9fce789fc62389d' if user.role == 'ADMIN' else False,
|
|
||||||
'language': user.language}
|
|
||||||
|
|
||||||
|
|
||||||
class UserManageSerializer(serializers.Serializer):
|
class UserManageSerializer(serializers.Serializer):
|
||||||
@ -62,54 +77,249 @@ class UserManageSerializer(serializers.Serializer):
|
|||||||
email = serializers.EmailField(
|
email = serializers.EmailField(
|
||||||
required=True,
|
required=True,
|
||||||
label=_("Email"),
|
label=_("Email"),
|
||||||
validators=[validators.EmailValidator(message=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.message,
|
validators=[validators.EmailValidator(
|
||||||
code=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.code)])
|
message=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.message,
|
||||||
|
code=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.code
|
||||||
username = serializers.CharField(required=True,
|
)]
|
||||||
label=_("Username"),
|
)
|
||||||
max_length=20,
|
username = serializers.CharField(
|
||||||
min_length=6,
|
required=True,
|
||||||
validators=[
|
label=_("Username"),
|
||||||
validators.RegexValidator(regex=re.compile("^.{6,20}$"),
|
max_length=20,
|
||||||
message=_(
|
min_length=6,
|
||||||
'Username must be 6-20 characters long'))
|
validators=[
|
||||||
])
|
validators.RegexValidator(
|
||||||
password = serializers.CharField(required=True, label=_("Password"), max_length=20, min_length=6,
|
regex=re.compile("^.{6,20}$"),
|
||||||
validators=[validators.RegexValidator(regex=re.compile(
|
message=_('Username must be 6-20 characters long')
|
||||||
"^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z_!@#$%^&*`~.()-+=]+$)(?![a-z0-9]+$)(?![a-z_!@#$%^&*`~()-+=]+$)"
|
)
|
||||||
"(?![0-9_!@#$%^&*`~()-+=]+$)[a-zA-Z0-9_!@#$%^&*`~.()-+=]{6,20}$")
|
]
|
||||||
, message=_(
|
)
|
||||||
"The password must be 6-20 characters long and must be a combination of letters, numbers, and special characters."))])
|
password = serializers.CharField(
|
||||||
|
required=True,
|
||||||
nick_name = serializers.CharField(required=False, label=_("Nick name"), max_length=64,
|
label=_("Password"),
|
||||||
allow_null=True, allow_blank=True)
|
max_length=20,
|
||||||
phone = serializers.CharField(required=False, label=_("Phone"), max_length=20,
|
min_length=6,
|
||||||
allow_null=True, allow_blank=True)
|
validators=[
|
||||||
|
validators.RegexValidator(
|
||||||
|
regex=PASSWORD_REGEX,
|
||||||
|
message=_(
|
||||||
|
"The password must be 6-20 characters long and must be a combination of letters, numbers, and special characters."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
nick_name = serializers.CharField(
|
||||||
|
required=False,
|
||||||
|
label=_("Nick name"),
|
||||||
|
max_length=64,
|
||||||
|
allow_null=True,
|
||||||
|
allow_blank=True
|
||||||
|
)
|
||||||
|
phone = serializers.CharField(
|
||||||
|
required=False,
|
||||||
|
label=_("Phone"),
|
||||||
|
max_length=20,
|
||||||
|
allow_null=True,
|
||||||
|
allow_blank=True
|
||||||
|
)
|
||||||
|
|
||||||
def is_valid(self, *, raise_exception=True):
|
def is_valid(self, *, raise_exception=True):
|
||||||
super().is_valid(raise_exception=True)
|
super().is_valid(raise_exception=True)
|
||||||
|
self._check_unique_username_and_email()
|
||||||
|
|
||||||
|
def _check_unique_username_and_email(self):
|
||||||
username = self.data.get('username')
|
username = self.data.get('username')
|
||||||
email = self.data.get('email')
|
email = self.data.get('email')
|
||||||
u = QuerySet(User).filter(Q(username=username) | Q(email=email)).first()
|
user = User.objects.filter(Q(username=username) | Q(email=email)).first()
|
||||||
if u is not None:
|
if user:
|
||||||
if u.email == email:
|
if user.email == email:
|
||||||
raise ExceptionCodeConstants.EMAIL_IS_EXIST.value.to_app_api_exception()
|
raise ExceptionCodeConstants.EMAIL_IS_EXIST.value.to_app_api_exception()
|
||||||
if u.username == username:
|
if user.username == username:
|
||||||
raise ExceptionCodeConstants.USERNAME_IS_EXIST.value.to_app_api_exception()
|
raise ExceptionCodeConstants.USERNAME_IS_EXIST.value.to_app_api_exception()
|
||||||
|
|
||||||
|
class Query(serializers.Serializer):
|
||||||
|
email_or_username = serializers.CharField(required=False, allow_null=True,
|
||||||
|
label=_('Email or username'))
|
||||||
|
|
||||||
|
def get_query_set(self):
|
||||||
|
email_or_username = self.data.get('email_or_username')
|
||||||
|
query_set = QuerySet(User)
|
||||||
|
if email_or_username is not None:
|
||||||
|
query_set = query_set.filter(
|
||||||
|
Q(username__contains=email_or_username) | Q(email__contains=email_or_username))
|
||||||
|
query_set = query_set.order_by("-create_time")
|
||||||
|
return query_set
|
||||||
|
|
||||||
|
def list(self, with_valid=True):
|
||||||
|
if with_valid:
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
return [{'id': user_model.id, 'username': user_model.username, 'email': user_model.email} for user_model in
|
||||||
|
self.get_query_set()]
|
||||||
|
|
||||||
|
def page(self, current_page: int, page_size: int, with_valid=True):
|
||||||
|
if with_valid:
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
return page_search(current_page, page_size,
|
||||||
|
self.get_query_set(),
|
||||||
|
post_records_handler=lambda u: UserInstanceSerializer(u).data)
|
||||||
|
|
||||||
@valid_license(model=User, count=2,
|
@valid_license(model=User, count=2,
|
||||||
message=_(
|
message=_(
|
||||||
'The community version supports up to 2 users. If you need more users, please contact us (https://fit2cloud.com/).'))
|
'The community version supports up to 2 users. If you need more users, please contact us (https://fit2cloud.com/).'))
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def save(self, instance, with_valid=True):
|
def save(self, instance, with_valid=True):
|
||||||
if with_valid:
|
if with_valid:
|
||||||
UserManageSerializer.UserInstance(data=instance).is_valid(raise_exception=True)
|
self.UserInstance(data=instance).is_valid(raise_exception=True)
|
||||||
|
|
||||||
user = User(id=uuid.uuid7(), email=instance.get('email'),
|
user = User(
|
||||||
phone="" if instance.get('phone') is None else instance.get('phone'),
|
id=uuid.uuid7(),
|
||||||
nick_name="" if instance.get('nick_name') is None else instance.get('nick_name')
|
email=instance.get('email'),
|
||||||
, username=instance.get('username'), password=password_encrypt(instance.get('password')),
|
phone=instance.get('phone', ''),
|
||||||
role=RoleConstants.USER.name, source="LOCAL",
|
nick_name=instance.get('nick_name', ''),
|
||||||
is_active=True)
|
username=instance.get('username'),
|
||||||
|
password=password_encrypt(instance.get('password')),
|
||||||
|
role=RoleConstants.USER.name,
|
||||||
|
source="LOCAL",
|
||||||
|
is_active=True
|
||||||
|
)
|
||||||
user.save()
|
user.save()
|
||||||
return UserProfileSerializer(user).data
|
return UserInstanceSerializer(user).data
|
||||||
|
|
||||||
|
class UserEditInstance(serializers.Serializer):
|
||||||
|
email = serializers.EmailField(
|
||||||
|
required=False,
|
||||||
|
label=_("Email"),
|
||||||
|
validators=[validators.EmailValidator(
|
||||||
|
message=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.message,
|
||||||
|
code=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.code
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
nick_name = serializers.CharField(
|
||||||
|
required=False,
|
||||||
|
label=_("Name"),
|
||||||
|
max_length=64,
|
||||||
|
allow_null=True,
|
||||||
|
allow_blank=True
|
||||||
|
)
|
||||||
|
phone = serializers.CharField(
|
||||||
|
required=False,
|
||||||
|
label=_("Phone"),
|
||||||
|
max_length=20,
|
||||||
|
allow_null=True,
|
||||||
|
allow_blank=True
|
||||||
|
)
|
||||||
|
is_active = serializers.BooleanField(
|
||||||
|
required=False,
|
||||||
|
label=_("Is Active")
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_valid(self, *, user_id=None, raise_exception=False):
|
||||||
|
super().is_valid(raise_exception=True)
|
||||||
|
self._check_unique_email(user_id)
|
||||||
|
|
||||||
|
def _check_unique_email(self, user_id):
|
||||||
|
email = self.data.get('email')
|
||||||
|
if email and User.objects.filter(email=email).exclude(id=user_id).exists():
|
||||||
|
raise AppApiException(1004, _('Email is already in use'))
|
||||||
|
|
||||||
|
class RePasswordInstance(serializers.Serializer):
|
||||||
|
password = serializers.CharField(
|
||||||
|
required=True,
|
||||||
|
label=_("Password"),
|
||||||
|
max_length=20,
|
||||||
|
min_length=6,
|
||||||
|
validators=[
|
||||||
|
validators.RegexValidator(
|
||||||
|
regex=PASSWORD_REGEX,
|
||||||
|
message=_(
|
||||||
|
"The password must be 6-20 characters long and must be a combination of letters, numbers, and special characters."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
re_password = serializers.CharField(
|
||||||
|
required=True,
|
||||||
|
label=_("Re Password"),
|
||||||
|
validators=[
|
||||||
|
validators.RegexValidator(
|
||||||
|
regex=PASSWORD_REGEX,
|
||||||
|
message=_(
|
||||||
|
"The confirmation password must be 6-20 characters long and must be a combination of letters, numbers, and special characters."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_valid(self, *, raise_exception=False):
|
||||||
|
super().is_valid(raise_exception=True)
|
||||||
|
self._check_passwords_match()
|
||||||
|
|
||||||
|
def _check_passwords_match(self):
|
||||||
|
if self.data.get('password') != self.data.get('re_password'):
|
||||||
|
raise ExceptionCodeConstants.PASSWORD_NOT_EQ_RE_PASSWORD.value.to_app_api_exception()
|
||||||
|
|
||||||
|
class Operate(serializers.Serializer):
|
||||||
|
id = serializers.UUIDField(required=True, label=_('User ID'))
|
||||||
|
|
||||||
|
def is_valid(self, *, raise_exception=False):
|
||||||
|
super().is_valid(raise_exception=True)
|
||||||
|
self._check_user_exists()
|
||||||
|
|
||||||
|
def _check_user_exists(self):
|
||||||
|
if not User.objects.filter(id=self.data.get('id')).exists():
|
||||||
|
raise AppApiException(1004, _('User does not exist'))
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def delete(self, with_valid=True):
|
||||||
|
if with_valid:
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
self._check_not_admin()
|
||||||
|
user_id = self.data.get('id')
|
||||||
|
# TODO 需要删除授权关系
|
||||||
|
User.objects.filter(id=user_id).delete()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _check_not_admin(self):
|
||||||
|
user = User.objects.filter(id=self.data.get('id')).first()
|
||||||
|
if user.role == RoleConstants.ADMIN.name:
|
||||||
|
raise AppApiException(1004, _('Unable to delete administrator'))
|
||||||
|
|
||||||
|
def edit(self, instance, with_valid=True):
|
||||||
|
if with_valid:
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
UserManageSerializer.UserEditInstance(data=instance).is_valid(user_id=self.data.get('id'),
|
||||||
|
raise_exception=True)
|
||||||
|
user = User.objects.filter(id=self.data.get('id')).first()
|
||||||
|
self._check_admin_modification(user, instance)
|
||||||
|
self._update_user_fields(user, instance)
|
||||||
|
user.save()
|
||||||
|
return UserInstanceSerializer(user).data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _check_admin_modification(user, instance):
|
||||||
|
if user.role == RoleConstants.ADMIN.name and 'is_active' in instance and instance.get(
|
||||||
|
'is_active') is not None:
|
||||||
|
raise AppApiException(1004, _('Cannot modify administrator status'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _update_user_fields(user, instance):
|
||||||
|
update_keys = ['email', 'nick_name', 'phone', 'is_active']
|
||||||
|
for key in update_keys:
|
||||||
|
if key in instance and instance.get(key) is not None:
|
||||||
|
setattr(user, key, instance.get(key))
|
||||||
|
|
||||||
|
def one(self, with_valid=True):
|
||||||
|
if with_valid:
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
user = User.objects.filter(id=self.data.get('id')).first()
|
||||||
|
return UserInstanceSerializer(user).data
|
||||||
|
|
||||||
|
def re_password(self, instance, with_valid=True):
|
||||||
|
if with_valid:
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
UserManageSerializer.RePasswordInstance(data=instance).is_valid(raise_exception=True)
|
||||||
|
user = User.objects.filter(id=self.data.get('id')).first()
|
||||||
|
user.password = password_encrypt(instance.get('password'))
|
||||||
|
user.save()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|||||||
@ -11,9 +11,9 @@ urlpatterns = [
|
|||||||
path('workspace/<str:workspace_id>/user/profile', views.TestWorkspacePermissionUserView.as_view(),
|
path('workspace/<str:workspace_id>/user/profile', views.TestWorkspacePermissionUserView.as_view(),
|
||||||
name="test_workspace_id_permission"),
|
name="test_workspace_id_permission"),
|
||||||
path("user_manage", views.UserManage.as_view(), name="user_manage"),
|
path("user_manage", views.UserManage.as_view(), name="user_manage"),
|
||||||
# path("user_manage/<str:user_id>", views.UserManage.Operate.as_view(), name="user_manage_operate"),
|
path("user_manage/<str:user_id>", views.UserManage.Operate.as_view(), name="user_manage_operate"),
|
||||||
# path("user_manage/<str:user_id>/re_password", views.UserManage.RePassword.as_view(),
|
path("user_manage/<str:user_id>/re_password", views.UserManage.RePassword.as_view(),
|
||||||
# name="user_manage_re_password"),
|
name="user_manage_re_password"),
|
||||||
# path("user_manage/<int:current_page>/<int:page_size>", views.UserManage.Page.as_view(),
|
path("user_manage/<int:current_page>/<int:page_size>", views.UserManage.Page.as_view(),
|
||||||
# name="user_manage_re_password"),
|
name="user_manage_page"),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -19,6 +19,7 @@ from users.serializers.login import LoginSerializer, CaptchaSerializer
|
|||||||
class LoginView(APIView):
|
class LoginView(APIView):
|
||||||
@extend_schema(methods=['POST'],
|
@extend_schema(methods=['POST'],
|
||||||
description=_("Log in"),
|
description=_("Log in"),
|
||||||
|
summary=_("Log in"),
|
||||||
operation_id=_("Log in"),
|
operation_id=_("Log in"),
|
||||||
tags=[_("User management")],
|
tags=[_("User management")],
|
||||||
request=LoginAPI.get_request(),
|
request=LoginAPI.get_request(),
|
||||||
@ -29,6 +30,7 @@ class LoginView(APIView):
|
|||||||
|
|
||||||
class CaptchaView(APIView):
|
class CaptchaView(APIView):
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_("Get captcha"),
|
||||||
description=_("Get captcha"),
|
description=_("Get captcha"),
|
||||||
operation_id=_("Get captcha"),
|
operation_id=_("Get captcha"),
|
||||||
tags=[_("User management")],
|
tags=[_("User management")],
|
||||||
|
|||||||
@ -15,7 +15,9 @@ from common.auth.authenticate import TokenAuth
|
|||||||
from common.auth.authentication import has_permissions
|
from common.auth.authentication import has_permissions
|
||||||
from common.constants.permission_constants import PermissionConstants, Permission, Group, Operate
|
from common.constants.permission_constants import PermissionConstants, Permission, Group, Operate
|
||||||
from common.result import result
|
from common.result import result
|
||||||
from users.api.user import UserProfileAPI, TestWorkspacePermissionUserApi
|
from models_provider.api.model import DefaultModelResponse
|
||||||
|
from users.api.user import UserProfileAPI, TestWorkspacePermissionUserApi, DeleteUserApi, EditUserApi, \
|
||||||
|
ChangeUserPasswordApi, UserPageApi, UserListApi
|
||||||
from users.serializers.user import UserProfileSerializer, UserManageSerializer
|
from users.serializers.user import UserProfileSerializer, UserManageSerializer
|
||||||
|
|
||||||
|
|
||||||
@ -23,6 +25,7 @@ class UserProfileView(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_("Get current user information"),
|
||||||
description=_("Get current user information"),
|
description=_("Get current user information"),
|
||||||
operation_id=_("Get current user information"),
|
operation_id=_("Get current user information"),
|
||||||
tags=[_("User management")],
|
tags=[_("User management")],
|
||||||
@ -35,6 +38,7 @@ class TestPermissionsUserView(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_("Get current user information"),
|
||||||
description=_("Get current user information"),
|
description=_("Get current user information"),
|
||||||
operation_id="测试",
|
operation_id="测试",
|
||||||
tags=[_("User management")],
|
tags=[_("User management")],
|
||||||
@ -48,6 +52,7 @@ class TestWorkspacePermissionUserView(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['GET'],
|
@extend_schema(methods=['GET'],
|
||||||
|
summary="针对工作空间下权限校验",
|
||||||
description="针对工作空间下权限校验",
|
description="针对工作空间下权限校验",
|
||||||
operation_id="针对工作空间下权限校验",
|
operation_id="针对工作空间下权限校验",
|
||||||
tags=[_("User management")],
|
tags=[_("User management")],
|
||||||
@ -62,6 +67,7 @@ class UserManage(APIView):
|
|||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
@extend_schema(methods=['POST'],
|
@extend_schema(methods=['POST'],
|
||||||
|
summary=_("Create user"),
|
||||||
description=_("Create user"),
|
description=_("Create user"),
|
||||||
operation_id=_("Create user"),
|
operation_id=_("Create user"),
|
||||||
tags=[_("User management")],
|
tags=[_("User management")],
|
||||||
@ -70,3 +76,73 @@ class UserManage(APIView):
|
|||||||
@has_permissions(PermissionConstants.USER_CREATE)
|
@has_permissions(PermissionConstants.USER_CREATE)
|
||||||
def post(self, request: Request):
|
def post(self, request: Request):
|
||||||
return result.success(UserManageSerializer().save(request.data))
|
return result.success(UserManageSerializer().save(request.data))
|
||||||
|
|
||||||
|
class Operate(APIView):
|
||||||
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
@extend_schema(methods=['DELETE'],
|
||||||
|
description=_("Delete user"),
|
||||||
|
summary=_("Delete user"),
|
||||||
|
operation_id=_("Delete user"),
|
||||||
|
tags=[_("User management")],
|
||||||
|
parameters=DeleteUserApi.get_parameters(),
|
||||||
|
responses=DefaultModelResponse.get_response())
|
||||||
|
@has_permissions(PermissionConstants.USER_DELETE)
|
||||||
|
def delete(self, request: Request, user_id):
|
||||||
|
return result.success(UserManageSerializer.Operate(data={'id': user_id}).delete(with_valid=True))
|
||||||
|
|
||||||
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_("Get user information"),
|
||||||
|
description=_("Get user information"),
|
||||||
|
operation_id=_("Get user information"),
|
||||||
|
tags=[_("User management")],
|
||||||
|
request=DeleteUserApi.get_parameters(),
|
||||||
|
responses=UserProfileAPI.get_response())
|
||||||
|
@has_permissions(PermissionConstants.USER_READ)
|
||||||
|
def get(self, request: Request, user_id):
|
||||||
|
return result.success(UserManageSerializer.Operate(data={'id': user_id}).one(with_valid=True))
|
||||||
|
|
||||||
|
@extend_schema(methods=['PUT'],
|
||||||
|
summary=_("Update user information"),
|
||||||
|
description=_("Update user information"),
|
||||||
|
operation_id=_("Update user information"),
|
||||||
|
tags=[_("User management")],
|
||||||
|
parameters=DeleteUserApi.get_parameters(),
|
||||||
|
request=EditUserApi.get_request(),
|
||||||
|
responses=UserProfileAPI.get_response())
|
||||||
|
@has_permissions(PermissionConstants.USER_EDIT)
|
||||||
|
def put(self, request: Request, user_id):
|
||||||
|
return result.success(
|
||||||
|
UserManageSerializer.Operate(data={'id': user_id}).edit(request.data, with_valid=True))
|
||||||
|
|
||||||
|
class RePassword(APIView):
|
||||||
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
@extend_schema(methods=['PUT'],
|
||||||
|
summary=_("Change password"),
|
||||||
|
description=_("Change password"),
|
||||||
|
operation_id=_("Change password"),
|
||||||
|
tags=[_("User management")],
|
||||||
|
parameters=DeleteUserApi.get_parameters(),
|
||||||
|
request=ChangeUserPasswordApi.get_request(),
|
||||||
|
responses=DefaultModelResponse.get_response())
|
||||||
|
def put(self, request: Request, user_id):
|
||||||
|
return result.success(
|
||||||
|
UserManageSerializer.Operate(data={'id': user_id}).re_password(request.data, with_valid=True))
|
||||||
|
|
||||||
|
class Page(APIView):
|
||||||
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
@extend_schema(methods=['GET'],
|
||||||
|
summary=_("Get user paginated list"),
|
||||||
|
description=_("Get user paginated list"),
|
||||||
|
operation_id=_("Get user paginated list"),
|
||||||
|
tags=[_("User management")],
|
||||||
|
parameters=UserPageApi.get_parameters(),
|
||||||
|
responses=UserPageApi.get_response())
|
||||||
|
@has_permissions(PermissionConstants.USER_READ)
|
||||||
|
def get(self, request: Request, current_page, page_size):
|
||||||
|
d = UserManageSerializer.Query(
|
||||||
|
data={'email_or_username': request.query_params.get('email_or_username', None),
|
||||||
|
'user_id': str(request.user.id)})
|
||||||
|
return result.success(d.page(current_page, page_size))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user