mirror of https://github.com/jumpserver/jumpserver
[Update] users认证逻辑迁移到authentication中
parent
21714cc411
commit
6700dc969f
|
@ -0,0 +1,4 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from .auth import *
|
|
@ -0,0 +1,200 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from django.core.cache import cache
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from rest_framework.permissions import AllowAny
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from common.utils import get_logger, get_request_ip
|
||||||
|
from common.permissions import IsOrgAdminOrAppUser
|
||||||
|
from orgs.mixins import RootOrgViewMixin
|
||||||
|
from authentication.signals import post_auth_success, post_auth_failed
|
||||||
|
from users.serializers import UserSerializer
|
||||||
|
from users.models import User, LoginLog
|
||||||
|
from users.utils import (
|
||||||
|
check_user_valid, check_otp_code, increase_login_failed_count,
|
||||||
|
is_block_login, clean_failed_count
|
||||||
|
)
|
||||||
|
from users.hands import Asset, SystemUser
|
||||||
|
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class UserAuthApi(RootOrgViewMixin, APIView):
|
||||||
|
permission_classes = (AllowAny,)
|
||||||
|
serializer_class = UserSerializer
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
# limit login
|
||||||
|
username = request.data.get('username')
|
||||||
|
ip = request.data.get('remote_addr', None)
|
||||||
|
ip = ip or get_request_ip(request)
|
||||||
|
|
||||||
|
if is_block_login(username, ip):
|
||||||
|
msg = _("Log in frequently and try again later")
|
||||||
|
logger.warn(msg + ': ' + username + ':' + ip)
|
||||||
|
return Response({'msg': msg}, status=401)
|
||||||
|
|
||||||
|
user, msg = self.check_user_valid(request)
|
||||||
|
if not user:
|
||||||
|
username = request.data.get('username', '')
|
||||||
|
exist = User.objects.filter(username=username).first()
|
||||||
|
reason = LoginLog.REASON_PASSWORD if exist else LoginLog.REASON_NOT_EXIST
|
||||||
|
self.send_auth_signal(success=False, username=username, reason=reason)
|
||||||
|
increase_login_failed_count(username, ip)
|
||||||
|
return Response({'msg': msg}, status=401)
|
||||||
|
|
||||||
|
if user.password_has_expired:
|
||||||
|
self.send_auth_signal(
|
||||||
|
success=False, username=username,
|
||||||
|
reason=LoginLog.REASON_PASSWORD_EXPIRED
|
||||||
|
)
|
||||||
|
msg = _("The user {} password has expired, please update.".format(
|
||||||
|
user.username))
|
||||||
|
logger.info(msg)
|
||||||
|
return Response({'msg': msg}, status=401)
|
||||||
|
|
||||||
|
if not user.otp_enabled:
|
||||||
|
self.send_auth_signal(success=True, user=user)
|
||||||
|
# 登陆成功,清除原来的缓存计数
|
||||||
|
clean_failed_count(username, ip)
|
||||||
|
token = user.create_bearer_token(request)
|
||||||
|
return Response(
|
||||||
|
{'token': token, 'user': self.serializer_class(user).data}
|
||||||
|
)
|
||||||
|
|
||||||
|
seed = uuid.uuid4().hex
|
||||||
|
cache.set(seed, user, 300)
|
||||||
|
return Response(
|
||||||
|
{
|
||||||
|
'code': 101,
|
||||||
|
'msg': _('Please carry seed value and '
|
||||||
|
'conduct MFA secondary certification'),
|
||||||
|
'otp_url': reverse('api-auth:user-otp-auth'),
|
||||||
|
'seed': seed,
|
||||||
|
'user': self.serializer_class(user).data
|
||||||
|
}, status=300
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_user_valid(request):
|
||||||
|
username = request.data.get('username', '')
|
||||||
|
password = request.data.get('password', '')
|
||||||
|
public_key = request.data.get('public_key', '')
|
||||||
|
user, msg = check_user_valid(
|
||||||
|
username=username, password=password,
|
||||||
|
public_key=public_key
|
||||||
|
)
|
||||||
|
return user, msg
|
||||||
|
|
||||||
|
def send_auth_signal(self, success=True, user=None, username='', reason=''):
|
||||||
|
if success:
|
||||||
|
post_auth_success.send(sender=self.__class__, user=user, request=self.request)
|
||||||
|
else:
|
||||||
|
post_auth_failed.send(
|
||||||
|
sender=self.__class__, username=username,
|
||||||
|
request=self.request, reason=reason
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UserConnectionTokenApi(RootOrgViewMixin, APIView):
|
||||||
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
user_id = request.data.get('user', '')
|
||||||
|
asset_id = request.data.get('asset', '')
|
||||||
|
system_user_id = request.data.get('system_user', '')
|
||||||
|
token = str(uuid.uuid4())
|
||||||
|
user = get_object_or_404(User, id=user_id)
|
||||||
|
asset = get_object_or_404(Asset, id=asset_id)
|
||||||
|
system_user = get_object_or_404(SystemUser, id=system_user_id)
|
||||||
|
value = {
|
||||||
|
'user': user_id,
|
||||||
|
'username': user.username,
|
||||||
|
'asset': asset_id,
|
||||||
|
'hostname': asset.hostname,
|
||||||
|
'system_user': system_user_id,
|
||||||
|
'system_user_name': system_user.name
|
||||||
|
}
|
||||||
|
cache.set(token, value, timeout=20)
|
||||||
|
return Response({"token": token}, status=201)
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
token = request.query_params.get('token')
|
||||||
|
user_only = request.query_params.get('user-only', None)
|
||||||
|
value = cache.get(token, None)
|
||||||
|
|
||||||
|
if not value:
|
||||||
|
return Response('', status=404)
|
||||||
|
|
||||||
|
if not user_only:
|
||||||
|
return Response(value)
|
||||||
|
else:
|
||||||
|
return Response({'user': value['user']})
|
||||||
|
|
||||||
|
def get_permissions(self):
|
||||||
|
if self.request.query_params.get('user-only', None):
|
||||||
|
self.permission_classes = (AllowAny,)
|
||||||
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
|
class UserToken(APIView):
|
||||||
|
permission_classes = (AllowAny,)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
username = request.data.get('username', '')
|
||||||
|
email = request.data.get('email', '')
|
||||||
|
password = request.data.get('password', '')
|
||||||
|
public_key = request.data.get('public_key', '')
|
||||||
|
|
||||||
|
user, msg = check_user_valid(
|
||||||
|
username=username, email=email,
|
||||||
|
password=password, public_key=public_key)
|
||||||
|
else:
|
||||||
|
user = request.user
|
||||||
|
msg = None
|
||||||
|
if user:
|
||||||
|
token = user.create_bearer_token(request)
|
||||||
|
return Response({'Token': token, 'Keyword': 'Bearer'}, status=200)
|
||||||
|
else:
|
||||||
|
return Response({'error': msg}, status=406)
|
||||||
|
|
||||||
|
|
||||||
|
class UserOtpAuthApi(RootOrgViewMixin, APIView):
|
||||||
|
permission_classes = (AllowAny,)
|
||||||
|
serializer_class = UserSerializer
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
otp_code = request.data.get('otp_code', '')
|
||||||
|
seed = request.data.get('seed', '')
|
||||||
|
user = cache.get(seed, None)
|
||||||
|
if not user:
|
||||||
|
return Response(
|
||||||
|
{'msg': _('Please verify the user name and password first')},
|
||||||
|
status=401
|
||||||
|
)
|
||||||
|
if not check_otp_code(user.otp_secret_key, otp_code):
|
||||||
|
self.send_auth_signal(success=False, username=user.username, reason=LoginLog.REASON_MFA)
|
||||||
|
return Response({'msg': _('MFA certification failed')}, status=401)
|
||||||
|
self.send_auth_signal(success=True, user=user)
|
||||||
|
token = user.create_bearer_token(request)
|
||||||
|
data = {'token': token, 'user': self.serializer_class(user).data}
|
||||||
|
return Response(data)
|
||||||
|
|
||||||
|
def send_auth_signal(self, success=True, user=None, username='', reason=''):
|
||||||
|
if success:
|
||||||
|
post_auth_success.send(sender=self.__class__, user=user, request=self.request)
|
||||||
|
else:
|
||||||
|
post_auth_failed.send(
|
||||||
|
sender=self.__class__, username=username,
|
||||||
|
request=self.request, reason=reason
|
||||||
|
)
|
|
@ -14,7 +14,7 @@ from rest_framework import authentication, exceptions
|
||||||
from rest_framework.authentication import CSRFCheck
|
from rest_framework.authentication import CSRFCheck
|
||||||
|
|
||||||
from common.utils import get_object_or_none, make_signature, http_to_unixtime
|
from common.utils import get_object_or_none, make_signature, http_to_unixtime
|
||||||
from .models import User, AccessKey, PrivateToken
|
from users.models import User, AccessKey, PrivateToken
|
||||||
|
|
||||||
|
|
||||||
def get_request_date_header(request):
|
def get_request_date_header(request):
|
|
@ -1 +1,19 @@
|
||||||
|
# coding:utf-8
|
||||||
|
#
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from .. import api
|
||||||
|
|
||||||
|
app_name = 'authentication'
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
# path('token/', api.UserToken.as_view(), name='user-token'),
|
||||||
|
path('auth/', api.UserAuthApi.as_view(), name='user-auth'),
|
||||||
|
path('connection-token/',
|
||||||
|
api.UserConnectionTokenApi.as_view(), name='connection-token'),
|
||||||
|
path('otp/auth/', api.UserOtpAuthApi.as_view(), name='user-otp-auth'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from .. import views
|
from .. import views
|
||||||
|
|
||||||
app_name = 'authentication'
|
app_name = 'authentication'
|
||||||
|
@ -9,6 +10,11 @@ app_name = 'authentication'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# openid
|
# openid
|
||||||
path('openid/login/', views.OpenIDLoginView.as_view(), name='openid-login'),
|
path('openid/login/', views.OpenIDLoginView.as_view(), name='openid-login'),
|
||||||
path('openid/login/complete/', views.OpenIDLoginCompleteView.as_view(),
|
path('openid/login/complete/',
|
||||||
name='openid-login-complete'),
|
views.OpenIDLoginCompleteView.as_view(), name='openid-login-complete'),
|
||||||
|
|
||||||
|
# login
|
||||||
|
path('login/', views.UserLoginView.as_view(), name='login'),
|
||||||
|
path('login/otp/', views.UserLoginOtpView.as_view(), name='login-otp'),
|
||||||
|
path('logout/', views.UserLogoutView.as_view(), name='logout'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from .openid import *
|
from .openid import *
|
||||||
|
from .login import *
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
#
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import os
|
import os
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.shortcuts import render
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.contrib.auth import login as auth_login, logout as auth_logout
|
from django.contrib.auth import login as auth_login, logout as auth_logout
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.http import HttpResponse
|
||||||
from django.views.generic import ListView
|
|
||||||
from django.core.files.storage import default_storage
|
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
|
||||||
from django.shortcuts import reverse, redirect
|
from django.shortcuts import reverse, redirect
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
@ -19,23 +15,20 @@ from django.views.decorators.debug import sensitive_post_parameters
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
from django.views.generic.edit import FormView
|
from django.views.generic.edit import FormView
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from formtools.wizard.views import SessionWizardView
|
|
||||||
|
|
||||||
from common.utils import get_object_or_none, get_request_ip
|
from common.utils import get_request_ip
|
||||||
from authentication.signals import post_auth_success, post_auth_failed
|
from authentication.signals import post_auth_success, post_auth_failed
|
||||||
from users.models import User, LoginLog
|
|
||||||
from users.utils import send_reset_password_mail, check_otp_code, \
|
|
||||||
redirect_user_first_login_or_index, get_user_or_tmp_user, \
|
|
||||||
set_tmp_user_to_cache, get_password_check_rules, check_password_rules, \
|
|
||||||
is_block_login, increase_login_failed_count, clean_failed_count
|
|
||||||
from users import forms
|
from users import forms
|
||||||
|
from users.models import User, LoginLog
|
||||||
|
from users.utils import (
|
||||||
|
check_otp_code, is_block_login, clean_failed_count, get_user_or_tmp_user,
|
||||||
|
set_tmp_user_to_cache, increase_login_failed_count,
|
||||||
|
redirect_user_first_login_or_index,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'UserLoginView', 'UserLoginOtpView', 'UserLogoutView',
|
'UserLoginView', 'UserLoginOtpView', 'UserLogoutView',
|
||||||
'UserForgotPasswordView', 'UserForgotPasswordSendmailSuccessView',
|
|
||||||
'UserResetPasswordView', 'UserResetPasswordSuccessView',
|
|
||||||
'UserFirstLoginView', 'LoginLogListView'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,7 +115,7 @@ class UserLoginView(FormView):
|
||||||
|
|
||||||
if user.otp_enabled and user.otp_secret_key:
|
if user.otp_enabled and user.otp_secret_key:
|
||||||
# 1,2,mfa_setting & T
|
# 1,2,mfa_setting & T
|
||||||
return reverse('users:login-otp')
|
return reverse('authentication:login-otp')
|
||||||
elif user.otp_enabled and not user.otp_secret_key:
|
elif user.otp_enabled and not user.otp_secret_key:
|
||||||
# 1,2,mfa_setting & F
|
# 1,2,mfa_setting & F
|
||||||
return reverse('users:user-otp-enable-authentication')
|
return reverse('users:user-otp-enable-authentication')
|
||||||
|
@ -169,7 +162,9 @@ class UserLoginOtpView(FormView):
|
||||||
success=False, username=user.username,
|
success=False, username=user.username,
|
||||||
reason=LoginLog.REASON_MFA
|
reason=LoginLog.REASON_MFA
|
||||||
)
|
)
|
||||||
form.add_error('otp_code', _('MFA code invalid, or ntp sync server time'))
|
form.add_error(
|
||||||
|
'otp_code', _('MFA code invalid, or ntp sync server time')
|
||||||
|
)
|
||||||
return super().form_invalid(form)
|
return super().form_invalid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
|
@ -202,7 +197,7 @@ class UserLogoutView(TemplateView):
|
||||||
'title': _('Logout success'),
|
'title': _('Logout success'),
|
||||||
'messages': _('Logout success, return login page'),
|
'messages': _('Logout success, return login page'),
|
||||||
'interval': 1,
|
'interval': 1,
|
||||||
'redirect_url': reverse('users:login'),
|
'redirect_url': reverse('authentication:login'),
|
||||||
'auto_redirect': True,
|
'auto_redirect': True,
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
|
|
|
@ -135,7 +135,7 @@ TEMPLATES = [
|
||||||
# WSGI_APPLICATION = 'jumpserver.wsgi.applications'
|
# WSGI_APPLICATION = 'jumpserver.wsgi.applications'
|
||||||
|
|
||||||
LOGIN_REDIRECT_URL = reverse_lazy('index')
|
LOGIN_REDIRECT_URL = reverse_lazy('index')
|
||||||
LOGIN_URL = reverse_lazy('users:login')
|
LOGIN_URL = reverse_lazy('authentication:login')
|
||||||
|
|
||||||
SESSION_COOKIE_DOMAIN = CONFIG.SESSION_COOKIE_DOMAIN
|
SESSION_COOKIE_DOMAIN = CONFIG.SESSION_COOKIE_DOMAIN
|
||||||
CSRF_COOKIE_DOMAIN = CONFIG.CSRF_COOKIE_DOMAIN
|
CSRF_COOKIE_DOMAIN = CONFIG.CSRF_COOKIE_DOMAIN
|
||||||
|
@ -343,10 +343,10 @@ REST_FRAMEWORK = {
|
||||||
),
|
),
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||||
# 'rest_framework.authentication.BasicAuthentication',
|
# 'rest_framework.authentication.BasicAuthentication',
|
||||||
'users.authentication.AccessKeyAuthentication',
|
'authentication.authentication.AccessKeyAuthentication',
|
||||||
'users.authentication.AccessTokenAuthentication',
|
'authentication.authentication.AccessTokenAuthentication',
|
||||||
'users.authentication.PrivateTokenAuthentication',
|
'authentication.authentication.PrivateTokenAuthentication',
|
||||||
'users.authentication.SessionAuthentication',
|
'authentication.authentication.SessionAuthentication',
|
||||||
),
|
),
|
||||||
'DEFAULT_FILTER_BACKENDS': (
|
'DEFAULT_FILTER_BACKENDS': (
|
||||||
'django_filters.rest_framework.DjangoFilterBackend',
|
'django_filters.rest_framework.DjangoFilterBackend',
|
||||||
|
|
|
@ -21,6 +21,7 @@ api_v1_patterns = [
|
||||||
path('audits/v1/', include('audits.urls.api_urls', namespace='api-audits')),
|
path('audits/v1/', include('audits.urls.api_urls', namespace='api-audits')),
|
||||||
path('orgs/v1/', include('orgs.urls.api_urls', namespace='api-orgs')),
|
path('orgs/v1/', include('orgs.urls.api_urls', namespace='api-orgs')),
|
||||||
path('settings/v1/', include('settings.urls.api_urls', namespace='api-settings')),
|
path('settings/v1/', include('settings.urls.api_urls', namespace='api-settings')),
|
||||||
|
path('authentication/v1/', include('authentication.urls.api_urls', namespace='api-auth')),
|
||||||
]))
|
]))
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -94,10 +94,10 @@
|
||||||
<li><a id="switch_user"><i class="fa fa-exchange"></i><span> {% trans 'User page' %}</span></a></li>
|
<li><a id="switch_user"><i class="fa fa-exchange"></i><span> {% trans 'User page' %}</span></a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><a href="{% url 'users:logout' %}"><i class="fa fa-sign-out"></i> {% trans 'Logout' %}</a></li>
|
<li><a href="{% url 'authentication:logout' %}"><i class="fa fa-sign-out"></i> {% trans 'Logout' %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'users:login' %}">
|
<a href="{% url 'authentication:login' %}">
|
||||||
<i class="fa fa-sign-in"></i>{% trans 'Login' %}
|
<i class="fa fa-sign-in"></i>{% trans 'Login' %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,198 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
import uuid
|
|
||||||
|
|
||||||
from django.core.cache import cache
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
|
|
||||||
from rest_framework.permissions import AllowAny
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.views import APIView
|
|
||||||
|
|
||||||
from common.utils import get_logger, get_request_ip
|
|
||||||
from common.permissions import IsOrgAdminOrAppUser
|
|
||||||
from orgs.mixins import RootOrgViewMixin
|
|
||||||
from authentication.signals import post_auth_success, post_auth_failed
|
|
||||||
from ..serializers import UserSerializer
|
|
||||||
from ..models import User, LoginLog
|
|
||||||
from ..utils import check_user_valid, check_otp_code, \
|
|
||||||
increase_login_failed_count, is_block_login, \
|
|
||||||
clean_failed_count
|
|
||||||
from ..hands import Asset, SystemUser
|
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class UserAuthApi(RootOrgViewMixin, APIView):
|
|
||||||
permission_classes = (AllowAny,)
|
|
||||||
serializer_class = UserSerializer
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
# limit login
|
|
||||||
username = request.data.get('username')
|
|
||||||
ip = request.data.get('remote_addr', None)
|
|
||||||
ip = ip or get_request_ip(request)
|
|
||||||
|
|
||||||
if is_block_login(username, ip):
|
|
||||||
msg = _("Log in frequently and try again later")
|
|
||||||
logger.warn(msg + ': ' + username + ':' + ip)
|
|
||||||
return Response({'msg': msg}, status=401)
|
|
||||||
|
|
||||||
user, msg = self.check_user_valid(request)
|
|
||||||
if not user:
|
|
||||||
username = request.data.get('username', '')
|
|
||||||
exist = User.objects.filter(username=username).first()
|
|
||||||
reason = LoginLog.REASON_PASSWORD if exist else LoginLog.REASON_NOT_EXIST
|
|
||||||
self.send_auth_signal(success=False, username=username, reason=reason)
|
|
||||||
increase_login_failed_count(username, ip)
|
|
||||||
return Response({'msg': msg}, status=401)
|
|
||||||
|
|
||||||
if user.password_has_expired:
|
|
||||||
self.send_auth_signal(
|
|
||||||
success=False, username=username,
|
|
||||||
reason=LoginLog.REASON_PASSWORD_EXPIRED
|
|
||||||
)
|
|
||||||
msg = _("The user {} password has expired, please update.".format(
|
|
||||||
user.username))
|
|
||||||
logger.info(msg)
|
|
||||||
return Response({'msg': msg}, status=401)
|
|
||||||
|
|
||||||
if not user.otp_enabled:
|
|
||||||
self.send_auth_signal(success=True, user=user)
|
|
||||||
# 登陆成功,清除原来的缓存计数
|
|
||||||
clean_failed_count(username, ip)
|
|
||||||
token = user.create_bearer_token(request)
|
|
||||||
return Response(
|
|
||||||
{'token': token, 'user': self.serializer_class(user).data}
|
|
||||||
)
|
|
||||||
|
|
||||||
seed = uuid.uuid4().hex
|
|
||||||
cache.set(seed, user, 300)
|
|
||||||
return Response(
|
|
||||||
{
|
|
||||||
'code': 101,
|
|
||||||
'msg': _('Please carry seed value and '
|
|
||||||
'conduct MFA secondary certification'),
|
|
||||||
'otp_url': reverse('api-users:user-otp-auth'),
|
|
||||||
'seed': seed,
|
|
||||||
'user': self.serializer_class(user).data
|
|
||||||
}, status=300
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_user_valid(request):
|
|
||||||
username = request.data.get('username', '')
|
|
||||||
password = request.data.get('password', '')
|
|
||||||
public_key = request.data.get('public_key', '')
|
|
||||||
user, msg = check_user_valid(
|
|
||||||
username=username, password=password,
|
|
||||||
public_key=public_key
|
|
||||||
)
|
|
||||||
return user, msg
|
|
||||||
|
|
||||||
def send_auth_signal(self, success=True, user=None, username='', reason=''):
|
|
||||||
if success:
|
|
||||||
post_auth_success.send(sender=self.__class__, user=user, request=self.request)
|
|
||||||
else:
|
|
||||||
post_auth_failed.send(
|
|
||||||
sender=self.__class__, username=username,
|
|
||||||
request=self.request, reason=reason
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UserConnectionTokenApi(RootOrgViewMixin, APIView):
|
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
user_id = request.data.get('user', '')
|
|
||||||
asset_id = request.data.get('asset', '')
|
|
||||||
system_user_id = request.data.get('system_user', '')
|
|
||||||
token = str(uuid.uuid4())
|
|
||||||
user = get_object_or_404(User, id=user_id)
|
|
||||||
asset = get_object_or_404(Asset, id=asset_id)
|
|
||||||
system_user = get_object_or_404(SystemUser, id=system_user_id)
|
|
||||||
value = {
|
|
||||||
'user': user_id,
|
|
||||||
'username': user.username,
|
|
||||||
'asset': asset_id,
|
|
||||||
'hostname': asset.hostname,
|
|
||||||
'system_user': system_user_id,
|
|
||||||
'system_user_name': system_user.name
|
|
||||||
}
|
|
||||||
cache.set(token, value, timeout=20)
|
|
||||||
return Response({"token": token}, status=201)
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
token = request.query_params.get('token')
|
|
||||||
user_only = request.query_params.get('user-only', None)
|
|
||||||
value = cache.get(token, None)
|
|
||||||
|
|
||||||
if not value:
|
|
||||||
return Response('', status=404)
|
|
||||||
|
|
||||||
if not user_only:
|
|
||||||
return Response(value)
|
|
||||||
else:
|
|
||||||
return Response({'user': value['user']})
|
|
||||||
|
|
||||||
def get_permissions(self):
|
|
||||||
if self.request.query_params.get('user-only', None):
|
|
||||||
self.permission_classes = (AllowAny,)
|
|
||||||
return super().get_permissions()
|
|
||||||
|
|
||||||
|
|
||||||
class UserToken(APIView):
|
|
||||||
permission_classes = (AllowAny,)
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
if not request.user.is_authenticated:
|
|
||||||
username = request.data.get('username', '')
|
|
||||||
email = request.data.get('email', '')
|
|
||||||
password = request.data.get('password', '')
|
|
||||||
public_key = request.data.get('public_key', '')
|
|
||||||
|
|
||||||
user, msg = check_user_valid(
|
|
||||||
username=username, email=email,
|
|
||||||
password=password, public_key=public_key)
|
|
||||||
else:
|
|
||||||
user = request.user
|
|
||||||
msg = None
|
|
||||||
if user:
|
|
||||||
token = user.create_bearer_token(request)
|
|
||||||
return Response({'Token': token, 'Keyword': 'Bearer'}, status=200)
|
|
||||||
else:
|
|
||||||
return Response({'error': msg}, status=406)
|
|
||||||
|
|
||||||
|
|
||||||
class UserOtpAuthApi(RootOrgViewMixin, APIView):
|
|
||||||
permission_classes = (AllowAny,)
|
|
||||||
serializer_class = UserSerializer
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
otp_code = request.data.get('otp_code', '')
|
|
||||||
seed = request.data.get('seed', '')
|
|
||||||
user = cache.get(seed, None)
|
|
||||||
if not user:
|
|
||||||
return Response(
|
|
||||||
{'msg': _('Please verify the user name and password first')},
|
|
||||||
status=401
|
|
||||||
)
|
|
||||||
if not check_otp_code(user.otp_secret_key, otp_code):
|
|
||||||
self.send_auth_signal(success=False, username=user.username, reason=LoginLog.REASON_MFA)
|
|
||||||
return Response({'msg': _('MFA certification failed')}, status=401)
|
|
||||||
self.send_auth_signal(success=True, user=user)
|
|
||||||
token = user.create_bearer_token(request)
|
|
||||||
data = {'token': token, 'user': self.serializer_class(user).data}
|
|
||||||
return Response(data)
|
|
||||||
|
|
||||||
def send_auth_signal(self, success=True, user=None, username='', reason=''):
|
|
||||||
if success:
|
|
||||||
post_auth_success.send(sender=self.__class__, user=user, request=self.request)
|
|
||||||
else:
|
|
||||||
post_auth_failed.send(
|
|
||||||
sender=self.__class__, username=username,
|
|
||||||
request=self.request, reason=reason
|
|
||||||
)
|
|
||||||
|
|
|
@ -15,11 +15,7 @@ router.register(r'groups', api.UserGroupViewSet, 'user-group')
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# path('token/', api.UserToken.as_view(), name='user-token'),
|
|
||||||
path('connection-token/', api.UserConnectionTokenApi.as_view(), name='connection-token'),
|
|
||||||
path('profile/', api.UserProfileApi.as_view(), name='user-profile'),
|
path('profile/', api.UserProfileApi.as_view(), name='user-profile'),
|
||||||
path('auth/', api.UserAuthApi.as_view(), name='user-auth'),
|
|
||||||
path('otp/auth/', api.UserOtpAuthApi.as_view(), name='user-otp-auth'),
|
|
||||||
path('otp/reset/', api.UserResetOTPApi.as_view(), name='my-otp-reset'),
|
path('otp/reset/', api.UserResetOTPApi.as_view(), name='my-otp-reset'),
|
||||||
path('users/<uuid:pk>/otp/reset/', api.UserResetOTPApi.as_view(), name='user-reset-otp'),
|
path('users/<uuid:pk>/otp/reset/', api.UserResetOTPApi.as_view(), name='user-reset-otp'),
|
||||||
path('users/<uuid:pk>/password/', api.UserChangePasswordApi.as_view(), name='change-user-password'),
|
path('users/<uuid:pk>/password/', api.UserChangePasswordApi.as_view(), name='change-user-password'),
|
||||||
|
|
|
@ -9,8 +9,6 @@ app_name = 'users'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Login view
|
# Login view
|
||||||
path('login/', views.UserLoginView.as_view(), name='login'),
|
path('login/', views.UserLoginView.as_view(), name='login'),
|
||||||
path('logout/', views.UserLogoutView.as_view(), name='logout'),
|
|
||||||
path('login/otp/', views.UserLoginOtpView.as_view(), name='login-otp'),
|
|
||||||
path('password/forgot/', views.UserForgotPasswordView.as_view(), name='forgot-password'),
|
path('password/forgot/', views.UserForgotPasswordView.as_view(), name='forgot-password'),
|
||||||
path('password/forgot/sendmail-success/', views.UserForgotPasswordSendmailSuccessView.as_view(), name='forgot-password-sendmail-success'),
|
path('password/forgot/sendmail-success/', views.UserForgotPasswordSendmailSuccessView.as_view(), name='forgot-password-sendmail-success'),
|
||||||
path('password/reset/', views.UserResetPasswordView.as_view(), name='reset-password'),
|
path('password/reset/', views.UserResetPasswordView.as_view(), name='reset-password'),
|
||||||
|
@ -50,5 +48,5 @@ urlpatterns = [
|
||||||
|
|
||||||
# Login log
|
# Login log
|
||||||
# Abandon
|
# Abandon
|
||||||
path('login-log/', views.LoginLogListView.as_view(), name='login-log-list'),
|
# path('login-log/', views.LoginLogListView.as_view(), name='login-log-list'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -62,7 +62,7 @@ def send_user_created_mail(user):
|
||||||
'rest_password_token': user.generate_reset_token(),
|
'rest_password_token': user.generate_reset_token(),
|
||||||
'forget_password_url': reverse('users:forgot-password', external=True),
|
'forget_password_url': reverse('users:forgot-password', external=True),
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'login_url': reverse('users:login', external=True),
|
'login_url': reverse('authentication:login', external=True),
|
||||||
}
|
}
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
try:
|
try:
|
||||||
|
@ -98,7 +98,7 @@ def send_reset_password_mail(user):
|
||||||
'rest_password_token': user.generate_reset_token(),
|
'rest_password_token': user.generate_reset_token(),
|
||||||
'forget_password_url': reverse('users:forgot-password', external=True),
|
'forget_password_url': reverse('users:forgot-password', external=True),
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'login_url': reverse('users:login', external=True),
|
'login_url': reverse('authentication:login', external=True),
|
||||||
}
|
}
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
logger.debug(message)
|
logger.debug(message)
|
||||||
|
@ -136,7 +136,7 @@ def send_password_expiration_reminder_mail(user):
|
||||||
'update_password_url': reverse('users:user-password-update', external=True),
|
'update_password_url': reverse('users:user-password-update', external=True),
|
||||||
'forget_password_url': reverse('users:forgot-password', external=True),
|
'forget_password_url': reverse('users:forgot-password', external=True),
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'login_url': reverse('users:login', external=True),
|
'login_url': reverse('authentication:login', external=True),
|
||||||
}
|
}
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
logger.debug(message)
|
logger.debug(message)
|
||||||
|
@ -158,7 +158,7 @@ def send_reset_ssh_key_mail(user):
|
||||||
</br>
|
</br>
|
||||||
""") % {
|
""") % {
|
||||||
'name': user.name,
|
'name': user.name,
|
||||||
'login_url': reverse('users:login', external=True),
|
'login_url': reverse('authentication:login', external=True),
|
||||||
}
|
}
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
logger.debug(message)
|
logger.debug(message)
|
||||||
|
|
|
@ -24,7 +24,7 @@ from .. import forms
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'UserLoginView', 'UserForgotPasswordSendmailSuccessView',
|
'UserLoginView', 'UserForgotPasswordSendmailSuccessView',
|
||||||
'UserResetPasswordSuccessView', 'UserResetPasswordSuccessView',
|
'UserResetPasswordSuccessView', 'UserResetPasswordSuccessView',
|
||||||
'UserResetPasswordView', 'UserForgotPasswordView',
|
'UserResetPasswordView', 'UserForgotPasswordView', 'UserFirstLoginView',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class UserForgotPasswordSendmailSuccessView(TemplateView):
|
||||||
'title': _('Send reset password message'),
|
'title': _('Send reset password message'),
|
||||||
'messages': _('Send reset password mail success, '
|
'messages': _('Send reset password mail success, '
|
||||||
'login your mail box and follow it '),
|
'login your mail box and follow it '),
|
||||||
'redirect_url': reverse('users:login'),
|
'redirect_url': reverse('authentication:login'),
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
@ -71,7 +71,7 @@ class UserResetPasswordSuccessView(TemplateView):
|
||||||
context = {
|
context = {
|
||||||
'title': _('Reset password success'),
|
'title': _('Reset password success'),
|
||||||
'messages': _('Reset password success, return to login page'),
|
'messages': _('Reset password success, return to login page'),
|
||||||
'redirect_url': reverse('users:login'),
|
'redirect_url': reverse('authentication:login'),
|
||||||
'auto_redirect': True,
|
'auto_redirect': True,
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
|
|
|
@ -574,7 +574,7 @@ class UserOtpSettingsSuccessView(TemplateView):
|
||||||
'title': title,
|
'title': title,
|
||||||
'messages': describe,
|
'messages': describe,
|
||||||
'interval': 1,
|
'interval': 1,
|
||||||
'redirect_url': reverse('users:login'),
|
'redirect_url': reverse('authentication:login'),
|
||||||
'auto_redirect': True,
|
'auto_redirect': True,
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
|
|
Loading…
Reference in New Issue