api.py 12.8 KB
Newer Older
baltery's avatar
baltery 已提交
1 2 3 4 5
# -*- coding: utf-8 -*-
#

import json

baltery's avatar
baltery 已提交
6
from smtplib import SMTPSenderRefused
7
from rest_framework import generics
八千流 已提交
8 9
from rest_framework.views import Response, APIView
from django.conf import settings
10
from django.core.mail import send_mail, get_connection
baltery's avatar
baltery 已提交
11 12
from django.utils.translation import ugettext_lazy as _

13 14
from .utils import (
    LDAPServerUtil, LDAPCacheUtil, LDAPImportUtil, LDAPSyncUtil,
E
Eric 已提交
15
    LDAP_USE_CACHE_FLAGS, LDAPTestUtil, ObjectDict
16 17
)
from .tasks import sync_ldap_user_task
八千流 已提交
18
from common.permissions import IsOrgAdmin, IsSuperUser
19
from common.utils import get_logger
baltery's avatar
baltery 已提交
20
from .serializers import (
baltery's avatar
baltery 已提交
21
    MailTestSerializer, LDAPTestConfigSerializer, LDAPUserSerializer,
E
Eric 已提交
22 23 24
    PublicSettingSerializer, LDAPTestLoginSerializer, BaseSettingSerializer,
    BasicSettingSerializer, EmailContentSettingSerializer, EmailSettingSerializer,
    SecuritySettingSerializer, LdapSettingSerializer, TerminalSettingSerializer
baltery's avatar
baltery 已提交
25
)
26
from users.models import User
baltery's avatar
baltery 已提交
27

28 29 30
logger = get_logger(__file__)


baltery's avatar
baltery 已提交
31
class MailTestingAPI(APIView):
32
    permission_classes = (IsSuperUser,)
baltery's avatar
baltery 已提交
33 34 35 36 37 38
    serializer_class = MailTestSerializer
    success_message = _("Test mail sent to {}, please check")

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid():
39 40 41 42
            email_host = serializer.validated_data['EMAIL_HOST']
            email_port = serializer.validated_data['EMAIL_PORT']
            email_host_user = serializer.validated_data["EMAIL_HOST_USER"]
            email_host_password = serializer.validated_data['EMAIL_HOST_PASSWORD']
43
            email_from = serializer.validated_data["EMAIL_FROM"]
44
            email_recipient = serializer.validated_data["EMAIL_RECIPIENT"]
45 46 47 48 49 50 51
            email_use_ssl = serializer.validated_data['EMAIL_USE_SSL']
            email_use_tls = serializer.validated_data['EMAIL_USE_TLS']

            # 设置 settings 的值,会导致动态配置在当前进程失效
            # for k, v in serializer.validated_data.items():
            #     if k.startswith('EMAIL'):
            #         setattr(settings, k, v)
baltery's avatar
baltery 已提交
52 53 54
            try:
                subject = "Test"
                message = "Test smtp setting"
55
                email_from = email_from or email_host_user
56
                email_recipient = email_recipient or email_from
57 58 59 60 61 62
                connection = get_connection(
                    host=email_host, port=email_port,
                    uesrname=email_host_user, password=email_host_password,
                    use_tls=email_use_tls, use_ssl=email_use_ssl,
                )
                send_mail(
E
Eric 已提交
63
                    subject, message, email_from, [email_recipient],
64 65
                    connection=connection
                )
baltery's avatar
baltery 已提交
66 67 68 69 70 71 72 73 74 75 76
            except SMTPSenderRefused as e:
                resp = e.smtp_error
                if isinstance(resp, bytes):
                    for coding in ('gbk', 'utf8'):
                        try:
                            resp = resp.decode(coding)
                        except UnicodeDecodeError:
                            continue
                        else:
                            break
                return Response({"error": str(resp)}, status=401)
baltery's avatar
baltery 已提交
77
            except Exception as e:
baltery's avatar
baltery 已提交
78
                print(e)
baltery's avatar
baltery 已提交
79
                return Response({"error": str(e)}, status=401)
80
            return Response({"msg": self.success_message.format(email_recipient)})
baltery's avatar
baltery 已提交
81 82 83 84
        else:
            return Response({"error": str(serializer.errors)}, status=401)


baltery's avatar
baltery 已提交
85
class LDAPTestingConfigAPI(APIView):
86
    permission_classes = (IsSuperUser,)
baltery's avatar
baltery 已提交
87 88 89 90 91 92 93 94 95 96
    serializer_class = LDAPTestConfigSerializer

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        if not serializer.is_valid():
            return Response({"error": str(serializer.errors)}, status=401)
        config = self.get_ldap_config(serializer)
        ok, msg = LDAPTestUtil(config).test_config()
        status = 200 if ok else 401
        return Response(msg, status=status)
baltery's avatar
baltery 已提交
97

98
    @staticmethod
99 100
    def get_ldap_config(serializer):
        server_uri = serializer.validated_data["AUTH_LDAP_SERVER_URI"]
101 102 103
        bind_dn = serializer.validated_data["AUTH_LDAP_BIND_DN"]
        password = serializer.validated_data["AUTH_LDAP_BIND_PASSWORD"]
        use_ssl = serializer.validated_data.get("AUTH_LDAP_START_TLS", False)
baltery's avatar
baltery 已提交
104
        search_ou = serializer.validated_data["AUTH_LDAP_SEARCH_OU"]
105 106
        search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
        attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
baltery's avatar
baltery 已提交
107
        auth_ldap = serializer.validated_data.get('AUTH_LDAP', False)
108 109 110 111 112
        config = {
            'server_uri': server_uri,
            'bind_dn': bind_dn,
            'password': password,
            'use_ssl': use_ssl,
baltery's avatar
baltery 已提交
113
            'search_ou': search_ou,
114
            'search_filter': search_filter,
baltery's avatar
baltery 已提交
115 116
            'attr_map': attr_map,
            'auth_ldap': auth_ldap
117 118
        }
        return config
119

baltery's avatar
baltery 已提交
120 121 122 123 124

class LDAPTestingLoginAPI(APIView):
    permission_classes = (IsSuperUser,)
    serializer_class = LDAPTestLoginSerializer

baltery's avatar
baltery 已提交
125 126
    def post(self, request):
        serializer = self.serializer_class(data=request.data)
127 128
        if not serializer.is_valid():
            return Response({"error": str(serializer.errors)}, status=401)
baltery's avatar
baltery 已提交
129 130 131 132 133
        username = serializer.validated_data['username']
        password = serializer.validated_data['password']
        ok, msg = LDAPTestUtil().test_login(username, password)
        status = 200 if ok else 401
        return Response(msg, status=status)
baltery's avatar
baltery 已提交
134 135


136
class LDAPUserListApi(generics.ListAPIView):
137
    permission_classes = (IsSuperUser,)
baltery's avatar
baltery 已提交
138
    serializer_class = LDAPUserSerializer
八千流 已提交
139

140 141 142 143 144 145 146 147 148 149
    def get_queryset_from_cache(self):
        search_value = self.request.query_params.get('search')
        users = LDAPCacheUtil().search(search_value=search_value)
        return users

    def get_queryset_from_server(self):
        search_value = self.request.query_params.get('search')
        users = LDAPServerUtil().search(search_value=search_value)
        return users

150
    def get_queryset(self):
baltery's avatar
baltery 已提交
151 152
        if hasattr(self, 'swagger_fake_view'):
            return []
153 154 155 156 157
        cache_police = self.request.query_params.get('cache_police', True)
        if cache_police in LDAP_USE_CACHE_FLAGS:
            users = self.get_queryset_from_cache()
        else:
            users = self.get_queryset_from_server()
158 159
        return users

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
    @staticmethod
    def processing_queryset(queryset):
        db_username_list = User.objects.all().values_list('username', flat=True)
        for q in queryset:
            q['id'] = q['username']
            q['existing'] = q['username'] in db_username_list
        return queryset

    def sort_queryset(self, queryset):
        order_by = self.request.query_params.get('order')
        if not order_by:
            order_by = 'existing'
        if order_by.startswith('-'):
            order_by = order_by.lstrip('-')
            reverse = True
        else:
            reverse = False
        queryset = sorted(queryset, key=lambda x: x[order_by], reverse=reverse)
        return queryset

    def filter_queryset(self, queryset):
        if queryset is None:
            return queryset
        queryset = self.processing_queryset(queryset)
        queryset = self.sort_queryset(queryset)
        return queryset

187 188 189 190 191 192
    def list(self, request, *args, **kwargs):
        cache_police = self.request.query_params.get('cache_police', True)
        # 不是用缓存
        if cache_police not in LDAP_USE_CACHE_FLAGS:
            return super().list(request, *args, **kwargs)

193 194 195 196 197 198
        try:
            queryset = self.get_queryset()
        except Exception as e:
            data = {'error': str(e)}
            return Response(data=data, status=400)

199 200 201 202 203 204 205
        # 缓存有数据
        if queryset is not None:
            return super().list(request, *args, **kwargs)

        sync_util = LDAPSyncUtil()
        # 还没有同步任务
        if sync_util.task_no_start:
206 207
            # 任务外部设置 task running 状态
            sync_util.set_task_status(sync_util.TASK_STATUS_IS_RUNNING)
208 209 210 211 212 213 214 215 216 217
            task = sync_ldap_user_task.delay()
            data = {'msg': 'Cache no data, sync task {} started.'.format(task.id)}
            return Response(data=data, status=409)
        # 同步任务正在执行
        if sync_util.task_is_running:
            data = {'msg': 'synchronization is running.'}
            return Response(data=data, status=409)
        # 同步任务执行结束
        if sync_util.task_is_over:
            msg = sync_util.get_task_error_msg()
218
            data = {'error': 'Synchronization task report error: {}'.format(msg)}
219 220 221 222
            return Response(data=data, status=400)

        return super().list(request, *args, **kwargs)

八千流 已提交
223

224
class LDAPUserImportAPI(APIView):
225
    permission_classes = (IsSuperUser,)
八千流 已提交
226

227 228 229 230 231 232 233 234 235
    def get_ldap_users(self):
        username_list = self.request.data.get('username_list', [])
        cache_police = self.request.query_params.get('cache_police', True)
        if cache_police in LDAP_USE_CACHE_FLAGS:
            users = LDAPCacheUtil().search(search_users=username_list)
        else:
            users = LDAPServerUtil().search(search_users=username_list)
        return users

八千流 已提交
236
    def post(self, request):
237 238 239 240 241 242
        try:
            users = self.get_ldap_users()
        except Exception as e:
            return Response({'error': str(e)}, status=401)

        if users is None:
B
BaiJiangJie 已提交
243
            return Response({'msg': _('Get ldap users is None')}, status=401)
244

245 246
        errors = LDAPImportUtil().perform_import(users)
        if errors:
247 248 249
            return Response({'errors': errors}, status=401)

        count = users if users is None else len(users)
B
BaiJiangJie 已提交
250
        return Response({'msg': _('Imported {} users successfully').format(count)})
251

252

253
class LDAPCacheRefreshAPI(generics.RetrieveAPIView):
254
    permission_classes = (IsSuperUser,)
255 256

    def retrieve(self, request, *args, **kwargs):
257
        try:
258
            LDAPSyncUtil().clear_cache()
259
        except Exception as e:
260 261 262
            logger.error(str(e))
            return Response(data={'msg': str(e)}, status=400)
        return Response(data={'msg': 'success'})
八千流 已提交
263 264


baltery's avatar
baltery 已提交
265 266 267 268 269 270 271
class PublicSettingApi(generics.RetrieveAPIView):
    permission_classes = ()
    serializer_class = PublicSettingSerializer

    def get_object(self):
        instance = {
            "data": {
272 273
                "WINDOWS_SKIP_ALL_MANUAL_PASSWORD": settings.WINDOWS_SKIP_ALL_MANUAL_PASSWORD,
                "SECURITY_MAX_IDLE_TIME": settings.SECURITY_MAX_IDLE_TIME,
baltery's avatar
baltery 已提交
274 275 276 277 278
            }
        }
        return instance


E
Eric 已提交
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
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 EmailSettingApi(BaseSettingApi):
    serializer_class = EmailSettingSerializer
    setting_fields = ['EMAIL_HOST', 'EMAIL_PORT', 'EMAIL_HOST_USER',
                      'EMAIL_HOST_PASSWORD', 'EMAIL_FROM', 'EMAIL_RECIPIENT',
                      'EMAIL_USE_SSL', 'EMAIL_USE_TLS']


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', ]


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']


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']


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']