diff --git a/apps/settings/api.py b/apps/settings/api.py index 69f0129f8..f6021f3f2 100644 --- a/apps/settings/api.py +++ b/apps/settings/api.py @@ -2,7 +2,7 @@ # import json - +from collections.abc import Iterable from smtplib import SMTPSenderRefused from rest_framework import generics from rest_framework.views import Response, APIView @@ -19,9 +19,7 @@ from common.permissions import IsOrgAdmin, IsSuperUser from common.utils import get_logger from .serializers import ( MailTestSerializer, LDAPTestConfigSerializer, LDAPUserSerializer, - PublicSettingSerializer, LDAPTestLoginSerializer, BaseSettingSerializer, - BasicSettingSerializer, EmailContentSettingSerializer, EmailSettingSerializer, - SecuritySettingSerializer, LdapSettingSerializer, TerminalSettingSerializer + PublicSettingSerializer, LDAPTestLoginSerializer, SettingsSerializer ) from users.models import User @@ -276,58 +274,60 @@ class PublicSettingApi(generics.RetrieveAPIView): return instance -class BaseSettingApi(generics.RetrieveUpdateAPIView): - permission_classes = (IsSuperUser,) - serializer_class = BaseSettingSerializer - setting_fields = [] - - def get_object(self): - instance = {field: getattr(settings, field) for field in self.setting_fields} - return ObjectDict(instance) - - def perform_update(self, serializer): - serializer.save() - - -class BasicSettingApi(BaseSettingApi): - serializer_class = BasicSettingSerializer - setting_fields = ['SITE_URL', 'USER_GUIDE_URL', 'EMAIL_SUBJECT_PREFIX'] +class SettingsApi(generics.RetrieveUpdateAPIView): + serializer_class = SettingsSerializer + BASIC_CATEGORY = ['SITE_URL', 'USER_GUIDE_URL', 'EMAIL_SUBJECT_PREFIX'] - -class EmailSettingApi(BaseSettingApi): - serializer_class = EmailSettingSerializer - setting_fields = ['EMAIL_HOST', 'EMAIL_PORT', 'EMAIL_HOST_USER', + EMAIL_CATEGORY = ['EMAIL_HOST', 'EMAIL_PORT', 'EMAIL_HOST_USER', 'EMAIL_HOST_PASSWORD', 'EMAIL_FROM', 'EMAIL_RECIPIENT', 'EMAIL_USE_SSL', 'EMAIL_USE_TLS'] + EMAIL_CONTENT_CATEGORY = ['EMAIL_CUSTOM_USER_CREATED_SUBJECT', 'EMAIL_CUSTOM_USER_CREATED_HONORIFIC', + 'EMAIL_CUSTOM_USER_CREATED_BODY', 'EMAIL_CUSTOM_USER_CREATED_SIGNATURE', ] + + LDAP_CATEGORY = ['AUTH_LDAP_SERVER_URI', 'AUTH_LDAP_BIND_DN', + 'AUTH_LDAP_BIND_PASSWORD', 'AUTH_LDAP_SEARCH_OU', + 'AUTH_LDAP_SEARCH_FILTER', 'AUTH_LDAP_USER_ATTR_MAP', + 'AUTH_LDAP'] + + TERMINAL_CATEGORY = ['TERMINAL_PASSWORD_AUTH', 'TERMINAL_PUBLIC_KEY_AUTH', + 'TERMINAL_HEARTBEAT_INTERVAL', 'TERMINAL_ASSET_LIST_SORT_BY', + 'TERMINAL_ASSET_LIST_PAGE_SIZE', 'TERMINAL_SESSION_KEEP_DURATION', + 'TERMINAL_TELNET_REGEX'] + + SECURITY_CATEGORY = ['SECURITY_MFA_AUTH', 'SECURITY_COMMAND_EXECUTION', + 'SECURITY_SERVICE_ACCOUNT_REGISTRATION', 'SECURITY_LOGIN_LIMIT_COUNT', + 'SECURITY_LOGIN_LIMIT_TIME', 'SECURITY_MAX_IDLE_TIME', + 'SECURITY_PASSWORD_EXPIRATION_TIME', 'SECURITY_PASSWORD_MIN_LENGTH', + 'SECURITY_PASSWORD_UPPER_CASE', 'SECURITY_PASSWORD_LOWER_CASE', + 'SECURITY_PASSWORD_NUMBER', 'SECURITY_PASSWORD_SPECIAL_CHAR'] + + SETTING_CATEGORIES = { + "basic": BASIC_CATEGORY, + 'email': EMAIL_CATEGORY, + 'email_content': EMAIL_CONTENT_CATEGORY, + 'ldap': LDAP_CATEGORY, + 'terminal': TERMINAL_CATEGORY, + 'security': SECURITY_CATEGORY + } -class EmailContentSettingApi(BaseSettingApi): - serializer_class = EmailContentSettingSerializer - setting_fields = ['EMAIL_CUSTOM_USER_CREATED_SUBJECT', 'EMAIL_CUSTOM_USER_CREATED_HONORIFIC', - 'EMAIL_CUSTOM_USER_CREATED_BODY', 'EMAIL_CUSTOM_USER_CREATED_SIGNATURE', ] - + def get_object(self): + instance = {category_name: self._get_setting_fields_obj(category_fields) + for category_name, category_fields in self.SETTING_CATEGORIES.items()} -class LdapSettingApi(BaseSettingApi): - serializer_class = LdapSettingSerializer - setting_fields = ['AUTH_LDAP_SERVER_URI', 'AUTH_LDAP_BIND_DN', - 'AUTH_LDAP_BIND_PASSWORD', 'AUTH_LDAP_SEARCH_OU', - 'AUTH_LDAP_SEARCH_FILTER', 'AUTH_LDAP_USER_ATTR_MAP', - 'AUTH_LDAP'] + return ObjectDict(instance) + def perform_update(self, serializer): + serializer.save() -class TerminalSettingApi(BaseSettingApi): - serializer_class = TerminalSettingSerializer - setting_fields = ['TERMINAL_PASSWORD_AUTH', 'TERMINAL_PUBLIC_KEY_AUTH', - 'TERMINAL_HEARTBEAT_INTERVAL', 'TERMINAL_ASSET_LIST_SORT_BY', - 'TERMINAL_ASSET_LIST_PAGE_SIZE', 'TERMINAL_SESSION_KEEP_DURATION', - 'TERMINAL_TELNET_REGEX'] + def _get_setting_fields_obj(self, category_fields): + if isinstance(category_fields, Iterable): + fields_data = {field_name: getattr(settings, field_name) + for field_name in category_fields} + return ObjectDict(fields_data) + if isinstance(category_fields, str): + fields_data = {category_fields: getattr(settings, category_fields)} + return ObjectDict(fields_data) -class SecuritySettingApi(BaseSettingApi): - serializer_class = SecuritySettingSerializer - setting_fields = ['SECURITY_MFA_AUTH', 'SECURITY_COMMAND_EXECUTION', - 'SECURITY_SERVICE_ACCOUNT_REGISTRATION', 'SECURITY_LOGIN_LIMIT_COUNT', - 'SECURITY_LOGIN_LIMIT_TIME', 'SECURITY_MAX_IDLE_TIME', - 'SECURITY_PASSWORD_EXPIRATION_TIME', 'SECURITY_PASSWORD_MIN_LENGTH', - 'SECURITY_PASSWORD_UPPER_CASE', 'SECURITY_PASSWORD_LOWER_CASE', - 'SECURITY_PASSWORD_NUMBER', 'SECURITY_PASSWORD_SPECIAL_CHAR'] + return ObjectDict() diff --git a/apps/settings/serializers/settings.py b/apps/settings/serializers/settings.py index de22ba2eb..d8dc8d708 100644 --- a/apps/settings/serializers/settings.py +++ b/apps/settings/serializers/settings.py @@ -5,77 +5,48 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from ..models import Setting -__all__ = ['BaseSettingSerializer', 'BasicSettingSerializer', 'EmailSettingSerializer', - "EmailContentSettingSerializer", 'LdapSettingSerializer', - 'TerminalSettingSerializer', 'SecuritySettingSerializer'] +__all__ = ['SettingsSerializer'] -class BaseSettingSerializer(serializers.Serializer): - encrypt_fields = ["EMAIL_HOST_PASSWORD", "AUTH_LDAP_BIND_PASSWORD"] - - def create(self, validated_data): - pass - - def update(self, instance, validated_data): - self.update_validated_settings(validated_data) - for field_name, field_value in validated_data.items(): - setattr(instance, field_name, field_value) - return instance - - def update_validated_settings(self, validated_data, category='default'): - with transaction.atomic(): - for field_name, field_value in validated_data.items(): - try: - setting = Setting.objects.get(name=field_name) - except Setting.DoesNotExist: - setting = Setting() - encrypted = True if field_name in self.encrypt_fields else False - setting.name = field_name - setting.category = category - setting.encrypted = encrypted - setting.cleaned_value = field_value - setting.save() - - -class BasicSettingSerializer(BaseSettingSerializer): +class BasicSettingSerializer(serializers.Serializer): SITE_URL = serializers.URLField(required=True) - USER_GUIDE_URL = serializers.URLField(required=False) + USER_GUIDE_URL = serializers.URLField(required=False, allow_blank=True, ) EMAIL_SUBJECT_PREFIX = serializers.CharField(max_length=1024, required=True) -class EmailSettingSerializer(BaseSettingSerializer): +class EmailSettingSerializer(serializers.Serializer): encrypt_fields = ["EMAIL_HOST_PASSWORD", ] EMAIL_HOST = serializers.CharField(max_length=1024, required=True) EMAIL_PORT = serializers.CharField(max_length=5, required=True) EMAIL_HOST_USER = serializers.CharField(max_length=128, required=True) EMAIL_HOST_PASSWORD = serializers.CharField(max_length=1024, required=False, write_only=True) - EMAIL_FROM = serializers.CharField(max_length=128, required=False) - EMAIL_RECIPIENT = serializers.CharField(max_length=128, allow_blank='', required=False) + EMAIL_FROM = serializers.CharField(max_length=128, allow_blank=True, required=False) + EMAIL_RECIPIENT = serializers.CharField(max_length=128, allow_blank=True, required=False) EMAIL_USE_SSL = serializers.BooleanField(required=False) EMAIL_USE_TLS = serializers.BooleanField(required=False) -class EmailContentSettingSerializer(BaseSettingSerializer): - EMAIL_CUSTOM_USER_CREATED_SUBJECT = serializers.CharField(max_length=1024, required=False, ) - EMAIL_CUSTOM_USER_CREATED_HONORIFIC = serializers.CharField(max_length=1024, required=False, ) - EMAIL_CUSTOM_USER_CREATED_BODY = serializers.CharField(max_length=4096, required=False) - EMAIL_CUSTOM_USER_CREATED_SIGNATURE = serializers.CharField(max_length=512, required=False) +class EmailContentSettingSerializer(serializers.Serializer): + EMAIL_CUSTOM_USER_CREATED_SUBJECT = serializers.CharField(max_length=1024, allow_blank=True, required=False, ) + EMAIL_CUSTOM_USER_CREATED_HONORIFIC = serializers.CharField(max_length=1024, allow_blank=True, required=False, ) + EMAIL_CUSTOM_USER_CREATED_BODY = serializers.CharField(max_length=4096, allow_blank=True, required=False) + EMAIL_CUSTOM_USER_CREATED_SIGNATURE = serializers.CharField(max_length=512, allow_blank=True, required=False) -class LdapSettingSerializer(BaseSettingSerializer): +class LdapSettingSerializer(serializers.Serializer): encrypt_fields = ["AUTH_LDAP_BIND_PASSWORD", ] AUTH_LDAP_SERVER_URI = serializers.CharField(required=True) AUTH_LDAP_BIND_DN = serializers.CharField(required=False) AUTH_LDAP_BIND_PASSWORD = serializers.CharField(max_length=1024, write_only=True) - AUTH_LDAP_SEARCH_OU = serializers.CharField(max_length=1024, required=False) + AUTH_LDAP_SEARCH_OU = serializers.CharField(max_length=1024, allow_blank=True, required=False) AUTH_LDAP_SEARCH_FILTER = serializers.CharField(max_length=1024, required=True) AUTH_LDAP_USER_ATTR_MAP = serializers.CharField(max_length=1024, required=True) AUTH_LDAP = serializers.BooleanField(required=False) -class TerminalSettingSerializer(BaseSettingSerializer): +class TerminalSettingSerializer(serializers.Serializer): SORT_BY_CHOICES = ( ('hostname', _('Hostname')), ('ip', _('IP')) @@ -95,10 +66,10 @@ class TerminalSettingSerializer(BaseSettingSerializer): TERMINAL_ASSET_LIST_SORT_BY = serializers.ChoiceField(SORT_BY_CHOICES) TERMINAL_ASSET_LIST_PAGE_SIZE = serializers.ChoiceField(PAGE_SIZE_CHOICES) TERMINAL_SESSION_KEEP_DURATION = serializers.IntegerField(min_value=1, max_value=99999, required=True) - TERMINAL_TELNET_REGEX = serializers.CharField(required=False) + TERMINAL_TELNET_REGEX = serializers.CharField(required=False, allow_blank=True) -class SecuritySettingSerializer(BaseSettingSerializer): +class SecuritySettingSerializer(serializers.Serializer): SECURITY_MFA_AUTH = serializers.BooleanField(required=False) SECURITY_COMMAND_EXECUTION = serializers.BooleanField(required=False) SECURITY_SERVICE_ACCOUNT_REGISTRATION = serializers.BooleanField(required=True) @@ -111,3 +82,43 @@ class SecuritySettingSerializer(BaseSettingSerializer): SECURITY_PASSWORD_LOWER_CASE = serializers.BooleanField(required=False) SECURITY_PASSWORD_NUMBER = serializers.BooleanField(required=False) SECURITY_PASSWORD_SPECIAL_CHAR = serializers.BooleanField(required=False) + + +class SettingsSerializer(serializers.Serializer): + basic = BasicSettingSerializer(required=False) + email = EmailSettingSerializer(required=False) + email_content = EmailContentSettingSerializer(required=False) + ldap = LdapSettingSerializer(required=False) + terminal = TerminalSettingSerializer(required=False) + security = SecuritySettingSerializer(required=False) + + encrypt_fields = ["EMAIL_HOST_PASSWORD", "AUTH_LDAP_BIND_PASSWORD"] + + def create(self, validated_data): + pass + + def update(self, instance, validated_data): + for category, category_data in validated_data.items(): + if not category_data: + continue + self.update_validated_settings(category_data) + for field_name, field_value in category_data.items(): + setattr(getattr(instance, category), field_name, field_value) + + return instance + + def update_validated_settings(self, validated_data, category='default'): + if not validated_data: + return + with transaction.atomic(): + for field_name, field_value in validated_data.items(): + try: + setting = Setting.objects.get(name=field_name) + except Setting.DoesNotExist: + setting = Setting() + encrypted = True if field_name in self.encrypt_fields else False + setting.name = field_name + setting.category = category + setting.encrypted = encrypted + setting.cleaned_value = field_value + setting.save() diff --git a/apps/settings/urls/api_urls.py b/apps/settings/urls/api_urls.py index 093bc9c15..0db9c7c54 100644 --- a/apps/settings/urls/api_urls.py +++ b/apps/settings/urls/api_urls.py @@ -14,11 +14,6 @@ urlpatterns = [ path('ldap/users/import/', api.LDAPUserImportAPI.as_view(), name='ldap-user-import'), path('ldap/cache/refresh/', api.LDAPCacheRefreshAPI.as_view(), name='ldap-cache-refresh'), - path('basic/', api.BasicSettingApi.as_view(), name='basic-settings'), - path('email/', api.EmailSettingApi.as_view(), name='email-settings'), - path('email-content/', api.EmailContentSettingApi.as_view(), name='email-content-settings'), - path('ldap/', api.LdapSettingApi.as_view(), name='ldap-settings'), - path('terminal/', api.TerminalSettingApi.as_view(), name='terminal-settings'), - path('security/', api.SecuritySettingApi.as_view(), name='security-settings'), + path('setting/', api.SettingsApi.as_view(), name='settings-setting'), path('public/', api.PublicSettingApi.as_view(), name='public-setting'), ]