mirror of https://github.com/jumpserver/jumpserver
commit
4642804077
|
@ -126,9 +126,12 @@ class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet):
|
|||
class CommandExecutionHostRelationViewSet(OrgRelationMixin, OrgBulkModelViewSet):
|
||||
serializer_class = CommandExecutionHostsRelationSerializer
|
||||
m2m_field = CommandExecution.hosts.field
|
||||
filterset_fields = [
|
||||
'id', 'asset', 'commandexecution'
|
||||
]
|
||||
filterset_fields = {
|
||||
'id': ['exact'],
|
||||
'asset': ['exact'],
|
||||
'asset__hostname': ['icontains'],
|
||||
'commandexecution': ['exact'],
|
||||
}
|
||||
search_fields = ('asset__hostname', )
|
||||
http_method_names = ['options', 'get']
|
||||
rbac_perms = {
|
||||
|
|
|
@ -22,7 +22,10 @@ class TicketStatusApi(mixins.AuthMixin, APIView):
|
|||
self.request.session['auth_third_party_done'] = 1
|
||||
return Response({"msg": "ok"})
|
||||
except errors.LoginConfirmOtherError as e:
|
||||
self.send_auth_signal(success=False, user=request.user, username=request.user.name, reason=e.as_data().get('msg'))
|
||||
reason = e.msg
|
||||
username = e.username
|
||||
self.send_auth_signal(success=False, username=username, reason=reason)
|
||||
# 若为三方登录,此时应退出登录
|
||||
auth_logout(request)
|
||||
return Response(e.as_data(), status=200)
|
||||
except errors.NeedMoreInfoError as e:
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
from django.urls import path
|
||||
import django_cas_ng.views
|
||||
|
||||
from .views import CASLoginView
|
||||
|
||||
urlpatterns = [
|
||||
path('login/', django_cas_ng.views.LoginView.as_view(), name='cas-login'),
|
||||
path('login/', CASLoginView.as_view(), name='cas-login'),
|
||||
path('logout/', django_cas_ng.views.LogoutView.as_view(), name='cas-logout'),
|
||||
path('callback/', django_cas_ng.views.CallbackView.as_view(), name='cas-proxy-callback'),
|
||||
]
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
from django_cas_ng.views import LoginView
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponseRedirect
|
||||
|
||||
__all__ = ['LoginView']
|
||||
|
||||
|
||||
class CASLoginView(LoginView):
|
||||
def get(self, request):
|
||||
try:
|
||||
return super().get(request)
|
||||
except PermissionDenied:
|
||||
return HttpResponseRedirect('/')
|
||||
|
||||
|
|
@ -56,7 +56,8 @@ class BlockGlobalIpLoginError(AuthFailedError):
|
|||
error = 'block_global_ip_login'
|
||||
|
||||
def __init__(self, username, ip, **kwargs):
|
||||
self.msg = const.block_ip_login_msg.format(settings.SECURITY_LOGIN_IP_LIMIT_TIME)
|
||||
if not self.msg:
|
||||
self.msg = const.block_ip_login_msg.format(settings.SECURITY_LOGIN_IP_LIMIT_TIME)
|
||||
LoginIpBlockUtil(ip).set_block_if_need()
|
||||
super().__init__(username=username, ip=ip, **kwargs)
|
||||
|
||||
|
@ -66,22 +67,21 @@ class CredentialError(
|
|||
BlockGlobalIpLoginError, AuthFailedError
|
||||
):
|
||||
def __init__(self, error, username, ip, request):
|
||||
super().__init__(error=error, username=username, ip=ip, request=request)
|
||||
util = LoginBlockUtil(username, ip)
|
||||
times_remainder = util.get_remainder_times()
|
||||
block_time = settings.SECURITY_LOGIN_LIMIT_TIME
|
||||
|
||||
if times_remainder < 1:
|
||||
self.msg = const.block_user_login_msg.format(settings.SECURITY_LOGIN_LIMIT_TIME)
|
||||
return
|
||||
|
||||
default_msg = const.invalid_login_msg.format(
|
||||
times_try=times_remainder, block_time=block_time
|
||||
)
|
||||
if error == const.reason_password_failed:
|
||||
self.msg = default_msg
|
||||
else:
|
||||
self.msg = const.reason_choices.get(error, default_msg)
|
||||
default_msg = const.invalid_login_msg.format(
|
||||
times_try=times_remainder, block_time=block_time
|
||||
)
|
||||
if error == const.reason_password_failed:
|
||||
self.msg = default_msg
|
||||
else:
|
||||
self.msg = const.reason_choices.get(error, default_msg)
|
||||
# 先处理 msg 在 super,记录日志时原因才准确
|
||||
super().__init__(error=error, username=username, ip=ip, request=request)
|
||||
|
||||
|
||||
class MFAFailedError(AuthFailedNeedLogMixin, AuthFailedError):
|
||||
|
|
|
@ -69,10 +69,16 @@ class LoginConfirmWaitError(LoginConfirmBaseError):
|
|||
class LoginConfirmOtherError(LoginConfirmBaseError):
|
||||
error = 'login_confirm_error'
|
||||
|
||||
def __init__(self, ticket_id, status):
|
||||
def __init__(self, ticket_id, status, username):
|
||||
self.username = username
|
||||
msg = const.login_confirm_error_msg.format(status)
|
||||
super().__init__(ticket_id=ticket_id, msg=msg)
|
||||
|
||||
def as_data(self):
|
||||
ret = super().as_data()
|
||||
ret['data']['username'] = self.username
|
||||
return ret
|
||||
|
||||
|
||||
class PasswordTooSimple(NeedRedirectError):
|
||||
default_code = 'passwd_too_simple'
|
||||
|
|
|
@ -377,7 +377,10 @@ class AuthACLMixin:
|
|||
raise errors.LoginConfirmWaitError(ticket.id)
|
||||
else:
|
||||
# rejected, closed
|
||||
raise errors.LoginConfirmOtherError(ticket.id, ticket.get_state_display())
|
||||
ticket_id = ticket.id
|
||||
status = ticket.get_state_display()
|
||||
username = ticket.applicant.username
|
||||
raise errors.LoginConfirmOtherError(ticket_id, status, username)
|
||||
|
||||
def get_ticket(self):
|
||||
from tickets.models import ApplyLoginTicket
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import ipaddress
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common.utils import validate_ip, get_ip_city, get_request_ip
|
||||
from common.utils import get_logger
|
||||
|
@ -23,8 +25,9 @@ def check_different_city_login_if_need(user, request):
|
|||
else:
|
||||
city = get_ip_city(ip) or DEFAULT_CITY
|
||||
|
||||
city_white = ['LAN', ]
|
||||
if city not in city_white:
|
||||
city_white = [_('LAN'), 'LAN']
|
||||
is_private = ipaddress.ip_address(ip).is_private
|
||||
if not is_private:
|
||||
last_user_login = UserLoginLog.objects.exclude(city__in=city_white) \
|
||||
.filter(username=user.username, status=True).first()
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ class SimpleMetadataWithFilters(SimpleMetadata):
|
|||
|
||||
default = getattr(field, 'default', None)
|
||||
if default is not None and default != empty:
|
||||
if isinstance(default, (str, int, bool, datetime.datetime, list)):
|
||||
if isinstance(default, (str, int, bool, float, datetime.datetime, list)):
|
||||
field_info['default'] = default
|
||||
|
||||
for attr in self.attrs:
|
||||
|
|
|
@ -40,12 +40,11 @@ class OrgManager(models.Manager):
|
|||
set_current_org(org)
|
||||
return self
|
||||
|
||||
|
||||
def bulk_create(self, objs, batch_size=None, ignore_conflicts=False):
|
||||
org = get_current_org()
|
||||
for obj in objs:
|
||||
if org.is_root():
|
||||
if not self.org_id:
|
||||
if not obj.org_id:
|
||||
raise ValidationError('Please save in a organization')
|
||||
else:
|
||||
obj.org_id = org.id
|
||||
|
|
|
@ -17,39 +17,39 @@ class SettingImageField(serializers.ImageField):
|
|||
|
||||
class OAuth2SettingSerializer(serializers.Serializer):
|
||||
AUTH_OAUTH2 = serializers.BooleanField(
|
||||
default=False, required=False, label=_('Enable OAuth2 Auth')
|
||||
default=False, label=_('Enable OAuth2 Auth')
|
||||
)
|
||||
AUTH_OAUTH2_LOGO_PATH = SettingImageField(
|
||||
allow_null=True, required=False, label=_('Logo')
|
||||
)
|
||||
AUTH_OAUTH2_PROVIDER = serializers.CharField(
|
||||
required=False, max_length=16, label=_('Service provider')
|
||||
required=True, max_length=16, label=_('Service provider')
|
||||
)
|
||||
AUTH_OAUTH2_CLIENT_ID = serializers.CharField(
|
||||
required=False, max_length=1024, label=_('Client Id')
|
||||
required=True, max_length=1024, label=_('Client Id')
|
||||
)
|
||||
AUTH_OAUTH2_CLIENT_SECRET = EncryptedField(
|
||||
required=False, max_length=1024, label=_('Client Secret')
|
||||
)
|
||||
AUTH_OAUTH2_SCOPE = serializers.CharField(
|
||||
required=False, max_length=1024, label=_('Scope'), allow_blank=True
|
||||
required=True, max_length=1024, label=_('Scope'), allow_blank=True
|
||||
)
|
||||
AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT = serializers.CharField(
|
||||
required=False, max_length=1024, label=_('Provider auth endpoint')
|
||||
required=True, max_length=1024, label=_('Provider auth endpoint')
|
||||
)
|
||||
AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT = serializers.CharField(
|
||||
required=False, max_length=1024, label=_('Provider token endpoint')
|
||||
required=True, max_length=1024, label=_('Provider token endpoint')
|
||||
)
|
||||
AUTH_OAUTH2_ACCESS_TOKEN_METHOD = serializers.ChoiceField(
|
||||
default='GET', label=_('Client authentication method'),
|
||||
choices=(('GET', 'GET'), ('POST', 'POST'))
|
||||
)
|
||||
AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT = serializers.CharField(
|
||||
required=False, max_length=1024, label=_('Provider userinfo endpoint')
|
||||
required=True, max_length=1024, label=_('Provider userinfo endpoint')
|
||||
)
|
||||
AUTH_OAUTH2_USER_ATTR_MAP = serializers.DictField(
|
||||
required=False, label=_('User attr map')
|
||||
required=True, label=_('User attr map')
|
||||
)
|
||||
AUTH_OAUTH2_ALWAYS_UPDATE_USER = serializers.BooleanField(
|
||||
required=False, label=_('Always update user')
|
||||
default=True, label=_('Always update user')
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from rest_framework import serializers
|
||||
|
||||
from common.drf.fields import EncryptedField
|
||||
from common.validators import PhoneValidator
|
||||
from common.sdk.sms import BACKENDS
|
||||
|
||||
__all__ = [
|
||||
|
@ -23,7 +24,10 @@ class SignTmplPairSerializer(serializers.Serializer):
|
|||
|
||||
|
||||
class BaseSMSSettingSerializer(serializers.Serializer):
|
||||
SMS_TEST_PHONE = serializers.CharField(max_length=256, required=False, allow_blank=True, label=_('Test phone'))
|
||||
SMS_TEST_PHONE = serializers.CharField(
|
||||
max_length=256, required=False, validators=[PhoneValidator(), ],
|
||||
allow_blank=True, label=_('Test phone')
|
||||
)
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super().to_representation(instance)
|
||||
|
|
|
@ -87,7 +87,7 @@ class BaseTicketMessage(UserMessage):
|
|||
@property
|
||||
def spec_items(self):
|
||||
fields = self.ticket._meta.local_fields + self.ticket._meta.local_many_to_many
|
||||
excludes = ['ticket_ptr']
|
||||
excludes = ['ticket_ptr', 'flow']
|
||||
item_names = [field.name for field in fields if field.name not in excludes]
|
||||
return self._get_fields_items(item_names)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<html lang="en">
|
||||
<body>
|
||||
<p> {{ approve_info }}</p>
|
||||
{% if content %}
|
||||
<br>
|
||||
<div style="width:100%; overflow-x:scroll;">
|
||||
<table style="width:1000px; text-align:left">
|
||||
|
@ -20,5 +21,6 @@
|
|||
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue