mirror of https://github.com/jumpserver/jumpserver
Merge branch 'lina' of github.com:jumpserver/jumpserver into lina
commit
0bdc425c55
|
@ -2,27 +2,34 @@
|
|||
#
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
from rest_framework.mixins import ListModelMixin
|
||||
from django.db.models import Q
|
||||
|
||||
from common.mixins.api import CommonApiMixin
|
||||
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor, IsOrgAdmin
|
||||
from common.drf.filters import DatetimeRangeFilter
|
||||
from orgs.mixins.api import OrgModelViewSet
|
||||
from common.drf.filters import DatetimeRangeFilter, current_user_filter
|
||||
from common.api import CommonGenericViewSet
|
||||
from orgs.mixins.api import OrgGenericViewSet
|
||||
from orgs.utils import current_org
|
||||
from .models import FTPLog, UserLoginLog
|
||||
from .serializers import FTPLogSerializer, UserLoginLogSerializer
|
||||
from ops.models import CommandExecution
|
||||
from .models import FTPLog, UserLoginLog, OperateLog, PasswordChangeLog
|
||||
from .serializers import FTPLogSerializer, UserLoginLogSerializer, CommandExecutionSerializer
|
||||
from .serializers import OperateLogSerializer, PasswordChangeLogSerializer
|
||||
from .filters import CurrentOrgMembersFilter
|
||||
|
||||
|
||||
class FTPLogViewSet(OrgModelViewSet):
|
||||
class FTPLogViewSet(ListModelMixin, OrgGenericViewSet):
|
||||
model = FTPLog
|
||||
serializer_class = FTPLogSerializer
|
||||
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
|
||||
http_method_names = ['get', 'post', 'head', 'options']
|
||||
extra_filter_backends = [DatetimeRangeFilter]
|
||||
date_range_filter_fields = [
|
||||
('date_start', ('date_from', 'date_to'))
|
||||
]
|
||||
filterset_fields = ['user', 'asset', 'system_user']
|
||||
search_fields = ['filename']
|
||||
|
||||
|
||||
class UserLoginLogViewSet(CommonApiMixin,
|
||||
ListModelMixin,
|
||||
GenericViewSet):
|
||||
class UserLoginLogViewSet(ListModelMixin,
|
||||
CommonGenericViewSet):
|
||||
queryset = UserLoginLog.objects.all()
|
||||
permission_classes = [IsOrgAdmin | IsOrgAuditor]
|
||||
serializer_class = UserLoginLogSerializer
|
||||
|
@ -44,3 +51,47 @@ class UserLoginLogViewSet(CommonApiMixin,
|
|||
users = self.get_org_members()
|
||||
queryset = queryset.filter(username__in=users)
|
||||
return queryset
|
||||
|
||||
|
||||
class OperateLogViewSet(ListModelMixin, OrgGenericViewSet):
|
||||
model = OperateLog
|
||||
serializer_class = OperateLogSerializer
|
||||
permission_classes = [IsOrgAdmin | IsOrgAuditor]
|
||||
extra_filter_backends = [DatetimeRangeFilter]
|
||||
date_range_filter_fields = [
|
||||
('datetime', ('date_from', 'date_to'))
|
||||
]
|
||||
filterset_fields = ['user', 'action', 'resource_type']
|
||||
search_fields = ['filename']
|
||||
ordering_fields = ['-datetime']
|
||||
|
||||
|
||||
class PasswordChangeLogViewSet(ListModelMixin, CommonGenericViewSet):
|
||||
queryset = PasswordChangeLog.objects.all()
|
||||
permission_classes = [IsOrgAdmin | IsOrgAuditor]
|
||||
serializer_class = PasswordChangeLogSerializer
|
||||
extra_filter_backends = [DatetimeRangeFilter]
|
||||
date_range_filter_fields = [
|
||||
('datetime', ('date_from', 'date_to'))
|
||||
]
|
||||
filterset_fields = ['user']
|
||||
ordering_fields = ['-datetime']
|
||||
|
||||
def get_queryset(self):
|
||||
users = current_org.get_org_members()
|
||||
queryset = super().get_queryset().filter(
|
||||
user__in=[user.__str__() for user in users]
|
||||
)
|
||||
return queryset
|
||||
|
||||
|
||||
class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet):
|
||||
model = CommandExecution
|
||||
serializer_class = CommandExecutionSerializer
|
||||
permission_classes = [IsOrgAdmin | IsOrgAuditor]
|
||||
extra_filter_backends = [DatetimeRangeFilter, current_user_filter(), CurrentOrgMembersFilter]
|
||||
date_range_filter_fields = [
|
||||
('date_start', ('date_from', 'date_to'))
|
||||
]
|
||||
search_fields = ['command']
|
||||
ordering_fields = ['-date_created']
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
from rest_framework import filters
|
||||
from rest_framework.compat import coreapi, coreschema
|
||||
|
||||
from orgs.utils import current_org
|
||||
|
||||
|
||||
__all__ = ['CurrentOrgMembersFilter']
|
||||
|
||||
|
||||
class CurrentOrgMembersFilter(filters.BaseFilterBackend):
|
||||
def get_schema_fields(self, view):
|
||||
return [
|
||||
coreapi.Field(
|
||||
name='user', location='query', required=False, type='string',
|
||||
schema=coreschema.String(
|
||||
title='user',
|
||||
description='user'
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
def _get_user_list(self):
|
||||
users = current_org.get_org_members(exclude=('Auditor',))
|
||||
return users
|
||||
|
||||
def filter_queryset(self, request, queryset, view):
|
||||
user_id = request.GET.get('user')
|
||||
if user_id:
|
||||
queryset = queryset.filter(user=user_id)
|
||||
else:
|
||||
queryset = queryset.filter(user__in=self._get_user_list())
|
||||
return queryset
|
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 2.2.10 on 2020-05-08 13:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('audits', '0007_auto_20191202_1010'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ftplog',
|
||||
name='date_start',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='Date start'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='operatelog',
|
||||
name='datetime',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='Datetime'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='passwordchangelog',
|
||||
name='datetime',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='Datetime'),
|
||||
),
|
||||
]
|
|
@ -22,7 +22,7 @@ class FTPLog(OrgModelMixin):
|
|||
operate = models.CharField(max_length=16, verbose_name=_("Operate"))
|
||||
filename = models.CharField(max_length=1024, verbose_name=_("Filename"))
|
||||
is_success = models.BooleanField(default=True, verbose_name=_("Success"))
|
||||
date_start = models.DateTimeField(auto_now_add=True)
|
||||
date_start = models.DateTimeField(auto_now_add=True, verbose_name=_('Date start'))
|
||||
|
||||
|
||||
class OperateLog(OrgModelMixin):
|
||||
|
@ -40,7 +40,7 @@ class OperateLog(OrgModelMixin):
|
|||
resource_type = models.CharField(max_length=64, verbose_name=_("Resource Type"))
|
||||
resource = models.CharField(max_length=128, verbose_name=_("Resource"))
|
||||
remote_addr = models.CharField(max_length=128, verbose_name=_("Remote addr"), blank=True, null=True)
|
||||
datetime = models.DateTimeField(auto_now=True)
|
||||
datetime = models.DateTimeField(auto_now=True, verbose_name=_('Datetime'))
|
||||
|
||||
def __str__(self):
|
||||
return "<{}> {} <{}>".format(self.user, self.action, self.resource)
|
||||
|
@ -51,7 +51,7 @@ class PasswordChangeLog(models.Model):
|
|||
user = models.CharField(max_length=128, verbose_name=_('User'))
|
||||
change_by = models.CharField(max_length=128, verbose_name=_("Change by"))
|
||||
remote_addr = models.CharField(max_length=128, verbose_name=_("Remote addr"), blank=True, null=True)
|
||||
datetime = models.DateTimeField(auto_now=True)
|
||||
datetime = models.DateTimeField(auto_now=True, verbose_name=_('Datetime'))
|
||||
|
||||
def __str__(self):
|
||||
return "{} change {}'s password".format(self.change_by, self.user)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from terminal.models import Session
|
||||
from ops.models import CommandExecution
|
||||
from . import models
|
||||
|
||||
|
||||
|
@ -11,31 +12,40 @@ class FTPLogSerializer(serializers.ModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = models.FTPLog
|
||||
fields = '__all__'
|
||||
fields = (
|
||||
'user', 'remote_addr', 'asset', 'system_user',
|
||||
'operate', 'filename', 'is_success', 'date_start'
|
||||
)
|
||||
|
||||
|
||||
class UserLoginLogSerializer(serializers.ModelSerializer):
|
||||
type_display = serializers.ReadOnlyField(source='get_type_display')
|
||||
status_display = serializers.ReadOnlyField(source='get_status_display')
|
||||
mfa_display = serializers.ReadOnlyField(source='get_mfa_display')
|
||||
|
||||
class Meta:
|
||||
model = models.UserLoginLog
|
||||
fields = (
|
||||
'username', 'type', 'type_display', 'ip', 'city', 'user_agent',
|
||||
'mfa', 'reason', 'status', 'status_display', 'datetime'
|
||||
'mfa', 'reason', 'status', 'status_display', 'datetime', 'mfa_display'
|
||||
)
|
||||
|
||||
|
||||
class OperateLogSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.OperateLog
|
||||
fields = '__all__'
|
||||
fields = (
|
||||
'user', 'action', 'resource_type', 'resource',
|
||||
'remote_addr', 'datetime'
|
||||
)
|
||||
|
||||
|
||||
class PasswordChangeLogSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.PasswordChangeLog
|
||||
fields = '__all__'
|
||||
fields = (
|
||||
'user', 'change_by', 'remote_addr', 'datetime'
|
||||
)
|
||||
|
||||
|
||||
class SessionAuditSerializer(serializers.ModelSerializer):
|
||||
|
@ -43,3 +53,15 @@ class SessionAuditSerializer(serializers.ModelSerializer):
|
|||
model = Session
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class CommandExecutionSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = CommandExecution
|
||||
fields = (
|
||||
'hosts', 'run_as', 'command', 'user', 'is_finished',
|
||||
'date_start', 'result', 'is_success'
|
||||
)
|
||||
extra_kwargs = {
|
||||
'result': {'label': _('Result')},
|
||||
'is_success': {'label': _('Is success')},
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@ app_name = "audits"
|
|||
router = DefaultRouter()
|
||||
router.register(r'ftp-logs', api.FTPLogViewSet, 'ftp-log')
|
||||
router.register(r'login-logs', api.UserLoginLogViewSet, 'login-log')
|
||||
router.register(r'operate-logs', api.OperateLogViewSet, 'operate-log')
|
||||
router.register(r'password-change-logs', api.PasswordChangeLogViewSet, 'password-change-log')
|
||||
router.register(r'command-execution-logs', api.CommandExecutionViewSet, 'command-execution-log')
|
||||
|
||||
urlpatterns = [
|
||||
]
|
||||
|
|
|
@ -9,10 +9,12 @@ from django.views.decorators.csrf import csrf_exempt
|
|||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import generics, serializers
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from .http import HttpResponseTemporaryRedirect
|
||||
from .const import KEY_CACHE_RESOURCES_ID
|
||||
from .utils import get_logger
|
||||
from .mixins import CommonApiMixin
|
||||
|
||||
__all__ = [
|
||||
'LogTailApi', 'ResourcesIDCacheApi',
|
||||
|
@ -100,3 +102,7 @@ def redirect_plural_name_api(request, *args, **kwargs):
|
|||
full_path = org_full_path.replace(resource, resource+"s", 1)
|
||||
logger.debug("Redirect {} => {}".format(org_full_path, full_path))
|
||||
return HttpResponseTemporaryRedirect(full_path)
|
||||
|
||||
|
||||
class CommonGenericViewSet(CommonApiMixin, GenericViewSet):
|
||||
pass
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import coreapi
|
||||
from rest_framework import filters
|
||||
from rest_framework.fields import DateTimeField
|
||||
from rest_framework.serializers import ValidationError
|
||||
|
@ -146,3 +145,10 @@ class CustomFilter(filters.BaseFilterBackend):
|
|||
|
||||
def filter_queryset(self, request, queryset, view):
|
||||
return queryset
|
||||
|
||||
|
||||
def current_user_filter(user_field='user'):
|
||||
class CurrentUserFilter(filters.BaseFilterBackend):
|
||||
def filter_queryset(self, request, queryset, view):
|
||||
return queryset.filter(**{user_field: request.user})
|
||||
return CurrentUserFilter
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,50 @@
|
|||
# Generated by Django 2.2.10 on 2020-05-08 13:05
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ops', '0017_auto_20200306_1747'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='commandexecution',
|
||||
name='date_created',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='Date created'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='commandexecution',
|
||||
name='date_finished',
|
||||
field=models.DateTimeField(null=True, verbose_name='Date finished'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='commandexecution',
|
||||
name='date_start',
|
||||
field=models.DateTimeField(null=True, verbose_name='Date start'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='commandexecution',
|
||||
name='hosts',
|
||||
field=models.ManyToManyField(to='assets.Asset', verbose_name='Hosts'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='commandexecution',
|
||||
name='is_finished',
|
||||
field=models.BooleanField(default=False, verbose_name='Is finished'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='commandexecution',
|
||||
name='run_as',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.SystemUser', verbose_name='Run as'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='commandexecution',
|
||||
name='user',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||
),
|
||||
]
|
|
@ -18,15 +18,15 @@ from ..inventory import JMSInventory
|
|||
|
||||
class CommandExecution(OrgModelMixin):
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
hosts = models.ManyToManyField('assets.Asset')
|
||||
run_as = models.ForeignKey('assets.SystemUser', on_delete=models.CASCADE)
|
||||
hosts = models.ManyToManyField('assets.Asset', verbose_name=_('Hosts'))
|
||||
run_as = models.ForeignKey('assets.SystemUser', on_delete=models.CASCADE, verbose_name=_('Run as'))
|
||||
command = models.TextField(verbose_name=_("Command"))
|
||||
_result = models.TextField(blank=True, null=True, verbose_name=_('Result'))
|
||||
user = models.ForeignKey('users.User', on_delete=models.CASCADE, null=True)
|
||||
is_finished = models.BooleanField(default=False)
|
||||
date_created = models.DateTimeField(auto_now_add=True)
|
||||
date_start = models.DateTimeField(null=True)
|
||||
date_finished = models.DateTimeField(null=True)
|
||||
user = models.ForeignKey('users.User', on_delete=models.CASCADE, null=True, verbose_name=_('User'))
|
||||
is_finished = models.BooleanField(default=False, verbose_name=_('Is finished'))
|
||||
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created'))
|
||||
date_start = models.DateTimeField(null=True, verbose_name=_('Date start'))
|
||||
date_finished = models.DateTimeField(null=True, verbose_name=_('Date finished'))
|
||||
|
||||
def __str__(self):
|
||||
return self.command[:10]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from rest_framework.viewsets import ModelViewSet, GenericViewSet
|
||||
from rest_framework_bulk import BulkModelViewSet
|
||||
from common.mixins import CommonApiMixin
|
||||
|
||||
|
@ -44,6 +44,10 @@ class OrgModelViewSet(CommonApiMixin, OrgQuerySetMixin, ModelViewSet):
|
|||
pass
|
||||
|
||||
|
||||
class OrgGenericViewSet(CommonApiMixin, OrgQuerySetMixin, GenericViewSet):
|
||||
pass
|
||||
|
||||
|
||||
class OrgBulkModelViewSet(CommonApiMixin, OrgQuerySetMixin, BulkModelViewSet):
|
||||
def allow_bulk_destroy(self, qs, filtered):
|
||||
qs_count = qs.count()
|
||||
|
|
|
@ -72,13 +72,13 @@ class MailTestingAPI(APIView):
|
|||
continue
|
||||
else:
|
||||
break
|
||||
return Response({"error": str(resp)}, status=401)
|
||||
return Response({"error": str(resp)}, status=400)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return Response({"error": str(e)}, status=401)
|
||||
return Response({"error": str(e)}, status=400)
|
||||
return Response({"msg": self.success_message.format(email_recipient)})
|
||||
else:
|
||||
return Response({"error": str(serializer.errors)}, status=401)
|
||||
return Response({"error": str(serializer.errors)}, status=400)
|
||||
|
||||
|
||||
class LDAPTestingConfigAPI(APIView):
|
||||
|
@ -88,10 +88,10 @@ class LDAPTestingConfigAPI(APIView):
|
|||
def post(self, request):
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response({"error": str(serializer.errors)}, status=401)
|
||||
return Response({"error": str(serializer.errors)}, status=400)
|
||||
config = self.get_ldap_config(serializer)
|
||||
ok, msg = LDAPTestUtil(config).test_config()
|
||||
status = 200 if ok else 401
|
||||
status = 200 if ok else 400
|
||||
return Response(msg, status=status)
|
||||
|
||||
@staticmethod
|
||||
|
@ -124,11 +124,11 @@ class LDAPTestingLoginAPI(APIView):
|
|||
def post(self, request):
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response({"error": str(serializer.errors)}, status=401)
|
||||
return Response({"error": str(serializer.errors)}, status=400)
|
||||
username = serializer.validated_data['username']
|
||||
password = serializer.validated_data['password']
|
||||
ok, msg = LDAPTestUtil().test_login(username, password)
|
||||
status = 200 if ok else 401
|
||||
status = 200 if ok else 400
|
||||
return Response(msg, status=status)
|
||||
|
||||
|
||||
|
@ -236,14 +236,14 @@ class LDAPUserImportAPI(APIView):
|
|||
try:
|
||||
users = self.get_ldap_users()
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=401)
|
||||
return Response({'error': str(e)}, status=400)
|
||||
|
||||
if users is None:
|
||||
return Response({'msg': _('Get ldap users is None')}, status=401)
|
||||
return Response({'msg': _('Get ldap users is None')}, status=400)
|
||||
|
||||
errors = LDAPImportUtil().perform_import(users)
|
||||
if errors:
|
||||
return Response({'errors': errors}, status=401)
|
||||
return Response({'errors': errors}, status=400)
|
||||
|
||||
count = users if users is None else len(users)
|
||||
return Response({'msg': _('Imported {} users successfully').format(count)})
|
||||
|
@ -276,6 +276,7 @@ class PublicSettingApi(generics.RetrieveAPIView):
|
|||
|
||||
|
||||
class SettingsApi(generics.RetrieveUpdateAPIView):
|
||||
permission_classes = (IsSuperUser,)
|
||||
serializer_class = SettingsSerializer
|
||||
|
||||
def get_object(self):
|
||||
|
|
|
@ -42,7 +42,7 @@ class LdapSettingSerializer(serializers.Serializer):
|
|||
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_USER_ATTR_MAP = serializers.DictField(required=True)
|
||||
AUTH_LDAP = serializers.BooleanField(required=False)
|
||||
|
||||
|
||||
|
@ -75,7 +75,7 @@ class SecuritySettingSerializer(serializers.Serializer):
|
|||
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_MAX_IDLE_TIME = serializers.IntegerField(min_value=1, 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)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.10 on 2020-05-08 13:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0025_auto_20200206_1216'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='source',
|
||||
field=models.CharField(choices=[('local', 'Local'), ('ldap', 'LDAP/AD'), ('openid', 'OpenID'), ('radius', 'Radius'), ('cas', 'CAS')], default='ldap', max_length=30, verbose_name='Source'),
|
||||
),
|
||||
]
|
Loading…
Reference in New Issue