diff --git a/apps/settings/api.py b/apps/settings/api.py index 38167a7e3..b8ceaac06 100644 --- a/apps/settings/api.py +++ b/apps/settings/api.py @@ -2,28 +2,28 @@ # import json - +from collections.abc import Iterable from smtplib import SMTPSenderRefused from rest_framework import generics from rest_framework.views import Response, APIView from django.conf import settings from django.core.mail import send_mail, get_connection from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers from .utils import ( LDAPServerUtil, LDAPCacheUtil, LDAPImportUtil, LDAPSyncUtil, - LDAP_USE_CACHE_FLAGS, LDAPTestUtil, + LDAP_USE_CACHE_FLAGS, LDAPTestUtil, ObjectDict ) from .tasks import sync_ldap_user_task from common.permissions import IsOrgAdmin, IsSuperUser from common.utils import get_logger from .serializers import ( MailTestSerializer, LDAPTestConfigSerializer, LDAPUserSerializer, - PublicSettingSerializer, LDAPTestLoginSerializer, + PublicSettingSerializer, LDAPTestLoginSerializer, SettingsSerializer ) from users.models import User - logger = get_logger(__file__) @@ -59,7 +59,7 @@ class MailTestingAPI(APIView): use_tls=email_use_tls, use_ssl=email_use_ssl, ) send_mail( - subject, message, email_from, [email_recipient], + subject, message, email_from, [email_recipient], connection=connection ) except SMTPSenderRefused as e: @@ -275,3 +275,26 @@ class PublicSettingApi(generics.RetrieveAPIView): return instance +class SettingsApi(generics.RetrieveUpdateAPIView): + serializer_class = SettingsSerializer + + def get_object(self): + instance = {category: self._get_setting_fields_obj(list(category_serializer.get_fields())) + for category, category_serializer in self.serializer_class().get_fields().items() + if isinstance(category_serializer, serializers.Serializer)} + return ObjectDict(instance) + + def perform_update(self, serializer): + serializer.save() + + 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) + + return ObjectDict() diff --git a/apps/settings/serializers/__init__.py b/apps/settings/serializers/__init__.py index 045763364..5868a76df 100644 --- a/apps/settings/serializers/__init__.py +++ b/apps/settings/serializers/__init__.py @@ -4,3 +4,4 @@ from .email import * from .ldap import * from .public import * +from .settings import * diff --git a/apps/settings/serializers/settings.py b/apps/settings/serializers/settings.py new file mode 100644 index 000000000..2e2fb2f6b --- /dev/null +++ b/apps/settings/serializers/settings.py @@ -0,0 +1,124 @@ +# coding: utf-8 + +from django.db import transaction +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers +from ..models import Setting + +__all__ = ['SettingsSerializer'] + + +class BasicSettingSerializer(serializers.Serializer): + SITE_URL = serializers.URLField(required=True) + USER_GUIDE_URL = serializers.URLField(required=False, allow_blank=True, ) + EMAIL_SUBJECT_PREFIX = serializers.CharField(max_length=1024, required=True) + + +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, write_only=True, 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(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(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, 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(serializers.Serializer): + SORT_BY_CHOICES = ( + ('hostname', _('Hostname')), + ('ip', _('IP')) + ) + + PAGE_SIZE_CHOICES = ( + ('all', _('All')), + ('auto', _('Auto')), + (10, 10), + (15, 15), + (25, 25), + (50, 50), + ) + TERMINAL_PASSWORD_AUTH = serializers.BooleanField(required=False) + TERMINAL_PUBLIC_KEY_AUTH = serializers.BooleanField(required=False) + TERMINAL_HEARTBEAT_INTERVAL = serializers.IntegerField(min_value=5, max_value=99999, required=True) + TERMINAL_ASSET_LIST_SORT_BY = serializers.ChoiceField(SORT_BY_CHOICES, required=False) + TERMINAL_ASSET_LIST_PAGE_SIZE = serializers.ChoiceField(PAGE_SIZE_CHOICES, required=False) + TERMINAL_SESSION_KEEP_DURATION = serializers.IntegerField(min_value=1, max_value=99999, required=True) + TERMINAL_TELNET_REGEX = serializers.CharField(allow_blank=True, required=False) + + +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) + SECURITY_LOGIN_LIMIT_COUNT = serializers.IntegerField(min_value=3, max_value=99999, required=True) + SECURITY_LOGIN_LIMIT_TIME = serializers.IntegerField(min_value=5, max_value=99999, required=True) + SECURITY_MAX_IDLE_TIME = serializers.IntegerField(min_value=5, max_value=99999, required=False) + SECURITY_PASSWORD_EXPIRATION_TIME = serializers.IntegerField(min_value=1, max_value=99999, required=True) + SECURITY_PASSWORD_MIN_LENGTH = serializers.IntegerField(min_value=6, max_value=30, required=True) + SECURITY_PASSWORD_UPPER_CASE = serializers.BooleanField(required=False) + 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 689e1ea82..0db9c7c54 100644 --- a/apps/settings/urls/api_urls.py +++ b/apps/settings/urls/api_urls.py @@ -14,5 +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('setting/', api.SettingsApi.as_view(), name='settings-setting'), path('public/', api.PublicSettingApi.as_view(), name='public-setting'), ] diff --git a/apps/settings/utils/__init__.py b/apps/settings/utils/__init__.py index 87bc6198f..e17c4e43c 100644 --- a/apps/settings/utils/__init__.py +++ b/apps/settings/utils/__init__.py @@ -2,3 +2,4 @@ # from .ldap import * +from .common import * diff --git a/apps/settings/utils/common.py b/apps/settings/utils/common.py new file mode 100644 index 000000000..e64ceaf7f --- /dev/null +++ b/apps/settings/utils/common.py @@ -0,0 +1,18 @@ +# coding: utf-8 + + +class ObjectDict(dict): + def __getattr__(self, name): + if name in self: + return self[name] + else: + raise AttributeError("No such attribute: " + name) + + def __setattr__(self, name, value): + self[name] = value + + def __delattr__(self, name): + if name in self: + del self[name] + else: + raise AttributeError("No such attribute: " + name)