Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
6ce9815d
Commit
6ce9815d
authored
Nov 05, 2019
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Update] 基本完成登录逻辑
parent
9d201bbf
Show whitespace changes
Inline
Side-by-side
Showing
36 changed files
with
869 additions
and
814 deletions
+869
-814
models.py
apps/audits/models.py
+9
-0
signals_handler.py
apps/audits/signals_handler.py
+41
-2
tasks.py
apps/audits/tasks.py
+6
-0
login_log_list.html
apps/audits/templates/audits/login_log_list.html
+1
-1
utils.py
apps/audits/utils.py
+16
-0
auth.py
apps/authentication/api/auth.py
+3
-169
login_confirm.py
apps/authentication/api/login_confirm.py
+32
-1
mfa.py
apps/authentication/api/mfa.py
+46
-1
token.py
apps/authentication/api/token.py
+10
-118
errors.py
apps/authentication/errors.py
+164
-25
forms.py
apps/authentication/forms.py
+2
-36
mixins.py
apps/authentication/mixins.py
+127
-0
serializers.py
apps/authentication/serializers.py
+25
-42
signals_handlers.py
apps/authentication/signals_handlers.py
+1
-36
tasks.py
apps/authentication/tasks.py
+0
-9
login.html
apps/authentication/templates/authentication/login.html
+15
-12
login_wait_confirm.html
...tication/templates/authentication/login_wait_confirm.html
+2
-2
xpack_login.html
.../authentication/templates/authentication/xpack_login.html
+22
-13
api_urls.py
apps/authentication/urls/api_urls.py
+1
-2
utils.py
apps/authentication/utils.py
+17
-26
__init__.py
apps/authentication/views/__init__.py
+1
-1
login.py
apps/authentication/views/login.py
+36
-90
mfa.py
apps/authentication/views/mfa.py
+25
-0
utils.py
apps/authentication/views/utils.py
+8
-0
common.py
apps/common/utils/common.py
+8
-0
django.mo
apps/locale/zh/LC_MESSAGES/django.mo
+0
-0
django.po
apps/locale/zh/LC_MESSAGES/django.po
+207
-192
asset_permission_create_update.html
...perms/templates/perms/asset_permission_create_update.html
+2
-12
remote_app_permission_create_update.html
.../templates/perms/remote_app_permission_create_update.html
+2
-2
jumpserver.js
apps/static/js/jumpserver.js
+28
-0
_head_css_js.html
apps/templates/_head_css_js.html
+0
-0
user.py
apps/users/models/user.py
+4
-4
_user.html
apps/users/templates/users/_user.html
+2
-11
user_detail.html
apps/users/templates/users/user_detail.html
+1
-1
api_urls.py
apps/users/urls/api_urls.py
+0
-3
utils.py
apps/users/utils.py
+5
-3
No files found.
apps/audits/models.py
View file @
6ce9815d
...
@@ -110,5 +110,14 @@ class UserLoginLog(models.Model):
...
@@ -110,5 +110,14 @@ class UserLoginLog(models.Model):
login_logs
=
login_logs
.
filter
(
username__in
=
username_list
)
login_logs
=
login_logs
.
filter
(
username__in
=
username_list
)
return
login_logs
return
login_logs
@property
def
reason_display
(
self
):
from
authentication.errors
import
reason_choices
,
old_reason_choices
reason
=
reason_choices
.
get
(
self
.
reason
)
if
reason
:
return
reason
reason
=
old_reason_choices
.
get
(
self
.
reason
,
self
.
reason
)
return
reason
class
Meta
:
class
Meta
:
ordering
=
[
'-datetime'
,
'username'
]
ordering
=
[
'-datetime'
,
'username'
]
apps/audits/signals_handler.py
View file @
6ce9815d
...
@@ -4,15 +4,18 @@
...
@@ -4,15 +4,18 @@
from
django.db.models.signals
import
post_save
,
post_delete
from
django.db.models.signals
import
post_save
,
post_delete
from
django.dispatch
import
receiver
from
django.dispatch
import
receiver
from
django.db
import
transaction
from
django.db
import
transaction
from
django.utils
import
timezone
from
rest_framework.renderers
import
JSONRenderer
from
rest_framework.renderers
import
JSONRenderer
from
rest_framework.request
import
Request
from
jumpserver.utils
import
current_request
from
jumpserver.utils
import
current_request
from
common.utils
import
get_request_ip
,
get_logger
,
get_syslogger
from
common.utils
import
get_request_ip
,
get_logger
,
get_syslogger
from
users.models
import
User
from
users.models
import
User
from
authentication.signals
import
post_auth_failed
,
post_auth_success
from
terminal.models
import
Session
,
Command
from
terminal.models
import
Session
,
Command
from
terminal.backends.command.serializers
import
SessionCommandSerializer
from
terminal.backends.command.serializers
import
SessionCommandSerializer
from
.
import
models
from
.
import
models
,
serializers
from
.
import
serializers
from
.
tasks
import
write_login_log_async
logger
=
get_logger
(
__name__
)
logger
=
get_logger
(
__name__
)
sys_logger
=
get_syslogger
(
"audits"
)
sys_logger
=
get_syslogger
(
"audits"
)
...
@@ -99,3 +102,39 @@ def on_audits_log_create(sender, instance=None, **kwargs):
...
@@ -99,3 +102,39 @@ def on_audits_log_create(sender, instance=None, **kwargs):
data
=
json_render
.
render
(
s
.
data
)
.
decode
(
errors
=
'ignore'
)
data
=
json_render
.
render
(
s
.
data
)
.
decode
(
errors
=
'ignore'
)
msg
=
"{} - {}"
.
format
(
category
,
data
)
msg
=
"{} - {}"
.
format
(
category
,
data
)
sys_logger
.
info
(
msg
)
sys_logger
.
info
(
msg
)
def
generate_data
(
username
,
request
):
user_agent
=
request
.
META
.
get
(
'HTTP_USER_AGENT'
,
''
)
if
isinstance
(
request
,
Request
):
login_ip
=
request
.
data
.
get
(
'remote_addr'
,
None
)
login_type
=
request
.
data
.
get
(
'login_type'
,
''
)
else
:
login_ip
=
get_request_ip
(
request
)
login_type
=
'W'
data
=
{
'username'
:
username
,
'ip'
:
login_ip
,
'type'
:
login_type
,
'user_agent'
:
user_agent
,
'datetime'
:
timezone
.
now
()
}
return
data
@receiver
(
post_auth_success
)
def
on_user_auth_success
(
sender
,
user
,
request
,
**
kwargs
):
logger
.
debug
(
'User login success: {}'
.
format
(
user
.
username
))
data
=
generate_data
(
user
.
username
,
request
)
data
.
update
({
'mfa'
:
int
(
user
.
otp_enabled
),
'status'
:
True
})
write_login_log_async
.
delay
(
**
data
)
@receiver
(
post_auth_failed
)
def
on_user_auth_failed
(
sender
,
username
,
request
,
reason
,
**
kwargs
):
logger
.
debug
(
'User login failed: {}'
.
format
(
username
))
data
=
generate_data
(
username
,
request
)
data
.
update
({
'reason'
:
reason
,
'status'
:
False
})
write_login_log_async
.
delay
(
**
data
)
apps/audits/tasks.py
View file @
6ce9815d
...
@@ -7,6 +7,7 @@ from celery import shared_task
...
@@ -7,6 +7,7 @@ from celery import shared_task
from
ops.celery.decorator
import
register_as_period_task
from
ops.celery.decorator
import
register_as_period_task
from
.models
import
UserLoginLog
from
.models
import
UserLoginLog
from
.utils
import
write_login_log
@register_as_period_task
(
interval
=
3600
*
24
)
@register_as_period_task
(
interval
=
3600
*
24
)
...
@@ -19,3 +20,8 @@ def clean_login_log_period():
...
@@ -19,3 +20,8 @@ def clean_login_log_period():
days
=
90
days
=
90
expired_day
=
now
-
datetime
.
timedelta
(
days
=
days
)
expired_day
=
now
-
datetime
.
timedelta
(
days
=
days
)
UserLoginLog
.
objects
.
filter
(
datetime__lt
=
expired_day
)
.
delete
()
UserLoginLog
.
objects
.
filter
(
datetime__lt
=
expired_day
)
.
delete
()
@shared_task
def
write_login_log_async
(
*
args
,
**
kwargs
):
write_login_log
(
*
args
,
**
kwargs
)
apps/audits/templates/audits/login_log_list.html
View file @
6ce9815d
...
@@ -78,7 +78,7 @@
...
@@ -78,7 +78,7 @@
<td
class=
"text-center"
>
{{ login_log.ip }}
</td>
<td
class=
"text-center"
>
{{ login_log.ip }}
</td>
<td
class=
"text-center"
>
{{ login_log.city }}
</td>
<td
class=
"text-center"
>
{{ login_log.city }}
</td>
<td
class=
"text-center"
>
{{ login_log.get_mfa_display }}
</td>
<td
class=
"text-center"
>
{{ login_log.get_mfa_display }}
</td>
<td
class=
"text-center"
>
{
% trans login_log.reason %
}
</td>
<td
class=
"text-center"
>
{
{ login_log.reason_display }
}
</td>
<td
class=
"text-center"
>
{{ login_log.get_status_display }}
</td>
<td
class=
"text-center"
>
{{ login_log.get_status_display }}
</td>
<td
class=
"text-center"
>
{{ login_log.datetime }}
</td>
<td
class=
"text-center"
>
{{ login_log.datetime }}
</td>
</tr>
</tr>
...
...
apps/audits/utils.py
View file @
6ce9815d
import
csv
import
csv
import
codecs
import
codecs
from
django.http
import
HttpResponse
from
django.http
import
HttpResponse
from
django.utils.translation
import
ugettext
as
_
from
common.utils
import
validate_ip
,
get_ip_city
def
get_excel_response
(
filename
):
def
get_excel_response
(
filename
):
...
@@ -20,3 +23,16 @@ def write_content_to_excel(response, header=None, login_logs=None, fields=None):
...
@@ -20,3 +23,16 @@ def write_content_to_excel(response, header=None, login_logs=None, fields=None):
data
=
[
getattr
(
log
,
field
.
name
)
for
field
in
fields
]
data
=
[
getattr
(
log
,
field
.
name
)
for
field
in
fields
]
writer
.
writerow
(
data
)
writer
.
writerow
(
data
)
return
response
return
response
def
write_login_log
(
*
args
,
**
kwargs
):
from
audits.models
import
UserLoginLog
default_city
=
_
(
"Unknown"
)
ip
=
kwargs
.
get
(
'ip'
)
or
''
if
not
(
ip
and
validate_ip
(
ip
)):
ip
=
ip
[:
15
]
city
=
default_city
else
:
city
=
get_ip_city
(
ip
)
or
default_city
kwargs
.
update
({
'ip'
:
ip
,
'city'
:
city
})
UserLoginLog
.
objects
.
create
(
**
kwargs
)
apps/authentication/api/auth.py
View file @
6ce9815d
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
import
uuid
import
uuid
import
time
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
django.urls
import
reverse
from
django.shortcuts
import
get_object_or_404
from
django.shortcuts
import
get_object_or_404
from
django.utils.translation
import
ugettext
as
_
from
rest_framework.permissions
import
AllowAny
from
rest_framework.permissions
import
AllowAny
from
rest_framework.response
import
Response
from
rest_framework.response
import
Response
from
rest_framework.generics
import
CreateAPIView
from
rest_framework.views
import
APIView
from
rest_framework.views
import
APIView
from
common.utils
import
get_logger
,
get_request_ip
,
get_object_or_none
from
common.utils
import
get_logger
from
common.permissions
import
IsOrgAdminOrAppUser
,
IsValidUser
from
common.permissions
import
IsOrgAdminOrAppUser
from
orgs.mixins.api
import
RootOrgViewMixin
from
orgs.mixins.api
import
RootOrgViewMixin
from
users.serializers
import
UserSerializer
from
users.models
import
User
from
users.models
import
User
from
assets.models
import
Asset
,
SystemUser
from
assets.models
import
Asset
,
SystemUser
from
users.utils
import
(
check_otp_code
,
increase_login_failed_count
,
is_block_login
,
clean_failed_count
)
from
..
import
errors
from
..utils
import
check_user_valid
from
..serializers
import
OtpVerifySerializer
from
..signals
import
post_auth_success
,
post_auth_failed
logger
=
get_logger
(
__name__
)
logger
=
get_logger
(
__name__
)
__all__
=
[
__all__
=
[
'UserAuthApi'
,
'UserConnectionTokenApi'
,
'UserOtpAuthApi'
,
'UserConnectionTokenApi'
,
'UserOtpVerifyApi'
,
'UserOrderAcceptAuthApi'
,
]
]
class
UserAuthApi
(
RootOrgViewMixin
,
APIView
):
permission_classes
=
(
AllowAny
,)
serializer_class
=
UserSerializer
def
get_serializer_context
(
self
):
return
{
'request'
:
self
.
request
,
'view'
:
self
}
def
get_serializer
(
self
,
*
args
,
**
kwargs
):
kwargs
[
'context'
]
=
self
.
get_serializer_context
()
return
self
.
serializer_class
(
*
args
,
**
kwargs
)
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'
,
''
)
self
.
send_auth_signal
(
success
=
False
,
username
=
username
,
reason
=
msg
)
increase_login_failed_count
(
username
,
ip
)
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
,
expired_at
=
user
.
create_bearer_token
(
request
)
return
Response
(
{
'token'
:
token
,
'user'
:
self
.
get_serializer
(
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
.
get_serializer
(
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
):
class
UserConnectionTokenApi
(
RootOrgViewMixin
,
APIView
):
permission_classes
=
(
IsOrgAdminOrAppUser
,)
permission_classes
=
(
IsOrgAdminOrAppUser
,)
...
@@ -150,82 +61,5 @@ class UserConnectionTokenApi(RootOrgViewMixin, APIView):
...
@@ -150,82 +61,5 @@ class UserConnectionTokenApi(RootOrgViewMixin, APIView):
return
super
()
.
get_permissions
()
return
super
()
.
get_permissions
()
class
UserOtpAuthApi
(
RootOrgViewMixin
,
APIView
):
permission_classes
=
(
AllowAny
,)
serializer_class
=
UserSerializer
def
get_serializer_context
(
self
):
return
{
'request'
:
self
.
request
,
'view'
:
self
}
def
get_serializer
(
self
,
*
args
,
**
kwargs
):
kwargs
[
'context'
]
=
self
.
get_serializer_context
()
return
self
.
serializer_class
(
*
args
,
**
kwargs
)
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
=
errors
.
mfa_failed
)
return
Response
({
'msg'
:
_
(
'MFA certification failed'
)},
status
=
401
)
self
.
send_auth_signal
(
success
=
True
,
user
=
user
)
token
,
expired_at
=
user
.
create_bearer_token
(
request
)
data
=
{
'token'
:
token
,
'user'
:
self
.
get_serializer
(
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
)
class
UserOtpVerifyApi
(
CreateAPIView
):
permission_classes
=
(
IsValidUser
,)
serializer_class
=
OtpVerifySerializer
def
create
(
self
,
request
,
*
args
,
**
kwargs
):
serializer
=
self
.
get_serializer
(
data
=
request
.
data
)
serializer
.
is_valid
(
raise_exception
=
True
)
code
=
serializer
.
validated_data
[
"code"
]
if
request
.
user
.
check_otp
(
code
):
request
.
session
[
"MFA_VERIFY_TIME"
]
=
int
(
time
.
time
())
return
Response
({
"ok"
:
"1"
})
else
:
return
Response
({
"error"
:
"Code not valid"
},
status
=
400
)
class
UserOrderAcceptAuthApi
(
APIView
):
permission_classes
=
()
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
from
orders.models
import
LoginConfirmOrder
order_id
=
self
.
request
.
session
.
get
(
"auth_order_id"
)
logger
.
debug
(
'Login confirm order id: {}'
.
format
(
order_id
))
if
not
order_id
:
order
=
None
else
:
order
=
get_object_or_none
(
LoginConfirmOrder
,
pk
=
order_id
)
if
not
order
:
error
=
_
(
"No order found or order expired"
)
return
Response
({
"error"
:
error
,
"status"
:
"not found"
},
status
=
404
)
if
order
.
status
==
order
.
STATUS_ACCEPTED
:
self
.
request
.
session
[
"auth_confirm"
]
=
"1"
return
Response
({
"msg"
:
"ok"
})
elif
order
.
status
==
order
.
STATUS_REJECTED
:
error
=
_
(
"Order was rejected by {}"
)
.
format
(
order
.
assignee_display
)
else
:
error
=
"Order status: {}"
.
format
(
order
.
status
)
return
Response
({
"error"
:
error
,
"status"
:
order
.
status
},
status
=
400
)
apps/authentication/api/login_confirm.py
View file @
6ce9815d
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
from
rest_framework.generics
import
UpdateAPIView
from
rest_framework.generics
import
UpdateAPIView
from
rest_framework.response
import
Response
from
rest_framework.views
import
APIView
from
django.shortcuts
import
get_object_or_404
from
django.shortcuts
import
get_object_or_404
from
common.utils
import
get_logger
,
get_object_or_none
from
common.permissions
import
IsOrgAdmin
from
common.permissions
import
IsOrgAdmin
from
..models
import
LoginConfirmSetting
from
..models
import
LoginConfirmSetting
from
..serializers
import
LoginConfirmSettingSerializer
from
..serializers
import
LoginConfirmSettingSerializer
from
..
import
errors
__all__
=
[
'LoginConfirmSettingUpdateApi'
]
__all__
=
[
'LoginConfirmSettingUpdateApi'
,
'UserOrderAcceptAuthApi'
]
logger
=
get_logger
(
__name__
)
class
LoginConfirmSettingUpdateApi
(
UpdateAPIView
):
class
LoginConfirmSettingUpdateApi
(
UpdateAPIView
):
...
@@ -23,3 +28,29 @@ class LoginConfirmSettingUpdateApi(UpdateAPIView):
...
@@ -23,3 +28,29 @@ class LoginConfirmSettingUpdateApi(UpdateAPIView):
defaults
,
user
=
user
,
defaults
,
user
=
user
,
)
)
return
s
return
s
class
UserOrderAcceptAuthApi
(
APIView
):
permission_classes
=
()
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
from
orders.models
import
LoginConfirmOrder
order_id
=
self
.
request
.
session
.
get
(
"auth_order_id"
)
logger
.
debug
(
'Login confirm order id: {}'
.
format
(
order_id
))
if
not
order_id
:
order
=
None
else
:
order
=
get_object_or_none
(
LoginConfirmOrder
,
pk
=
order_id
)
try
:
if
not
order
:
raise
errors
.
LoginConfirmOrderNotFound
(
order_id
)
if
order
.
status
==
order
.
STATUS_ACCEPTED
:
self
.
request
.
session
[
"auth_confirm"
]
=
"1"
return
Response
({
"msg"
:
"ok"
})
elif
order
.
status
==
order
.
STATUS_REJECTED
:
raise
errors
.
LoginConfirmRejectedError
(
order_id
)
else
:
return
errors
.
LoginConfirmWaitError
(
order_id
)
except
errors
.
AuthFailedError
as
e
:
data
=
e
.
as_data
()
return
Response
(
data
,
status
=
400
)
apps/authentication/api/mfa.py
View file @
6ce9815d
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
import
time
from
rest_framework.permissions
import
AllowAny
from
rest_framework.permissions
import
AllowAny
from
rest_framework.generics
import
CreateAPIView
from
rest_framework.generics
import
CreateAPIView
from
rest_framework.serializers
import
ValidationError
from
rest_framework.response
import
Response
from
common.permissions
import
IsValidUser
from
..serializers
import
OtpVerifySerializer
from
..
import
serializers
from
..
import
serializers
from
..
import
errors
from
..mixins
import
AuthMixin
class
MFAChallengeApi
(
CreateAPIView
):
__all__
=
[
'MFAChallengeApi'
,
'UserOtpVerifyApi'
]
class
MFAChallengeApi
(
AuthMixin
,
CreateAPIView
):
permission_classes
=
(
AllowAny
,)
permission_classes
=
(
AllowAny
,)
serializer_class
=
serializers
.
MFAChallengeSerializer
serializer_class
=
serializers
.
MFAChallengeSerializer
def
perform_create
(
self
,
serializer
):
try
:
user
=
self
.
get_user_from_session
()
code
=
serializer
.
validated_data
.
get
(
'code'
)
valid
=
user
.
check_otp
(
code
)
if
not
valid
:
self
.
request
.
session
[
'auth_mfa'
]
=
''
raise
errors
.
MFAFailedError
(
username
=
user
.
username
,
request
=
self
.
request
)
except
errors
.
AuthFailedError
as
e
:
data
=
{
"error"
:
e
.
error
,
"msg"
:
e
.
reason
}
raise
ValidationError
(
data
)
def
create
(
self
,
request
,
*
args
,
**
kwargs
):
super
()
.
create
(
request
,
*
args
,
**
kwargs
)
return
Response
({
'msg'
:
'ok'
})
class
UserOtpVerifyApi
(
CreateAPIView
):
permission_classes
=
(
IsValidUser
,)
serializer_class
=
OtpVerifySerializer
def
create
(
self
,
request
,
*
args
,
**
kwargs
):
serializer
=
self
.
get_serializer
(
data
=
request
.
data
)
serializer
.
is_valid
(
raise_exception
=
True
)
code
=
serializer
.
validated_data
[
"code"
]
if
request
.
user
.
check_otp
(
code
):
request
.
session
[
"MFA_VERIFY_TIME"
]
=
int
(
time
.
time
())
return
Response
({
"ok"
:
"1"
})
else
:
return
Response
({
"error"
:
"Code not valid"
},
status
=
400
)
apps/authentication/api/token.py
View file @
6ce9815d
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
from
django.utils.translation
import
ugettext
as
_
from
rest_framework.permissions
import
AllowAny
from
rest_framework.permissions
import
AllowAny
from
rest_framework.response
import
Response
from
rest_framework.response
import
Response
from
rest_framework.generics
import
CreateAPIView
from
rest_framework.generics
import
CreateAPIView
from
common.utils
import
get_request_ip
,
get_logger
,
get_object_or_none
from
common.utils
import
get_logger
from
users.utils
import
(
check_otp_code
,
increase_login_failed_count
,
is_block_login
,
clean_failed_count
)
from
users.models
import
User
from
..utils
import
check_user_valid
from
..signals
import
post_auth_success
,
post_auth_failed
from
..
import
serializers
,
errors
from
..
import
serializers
,
errors
from
..mixins
import
AuthMixin
logger
=
get_logger
(
__name__
)
logger
=
get_logger
(
__name__
)
...
@@ -22,126 +16,24 @@ logger = get_logger(__name__)
...
@@ -22,126 +16,24 @@ logger = get_logger(__name__)
__all__
=
[
'TokenCreateApi'
]
__all__
=
[
'TokenCreateApi'
]
class
TokenCreateApi
(
CreateAPIView
):
class
TokenCreateApi
(
AuthMixin
,
CreateAPIView
):
permission_classes
=
(
AllowAny
,)
permission_classes
=
(
AllowAny
,)
serializer_class
=
serializers
.
BearerTokenSerializer
serializer_class
=
serializers
.
BearerTokenSerializer
def
check_session
(
self
):
def
create_session_if_need
(
self
):
pass
if
self
.
request
.
session
.
is_empty
():
self
.
request
.
session
.
create
()
def
get_request_ip
(
self
):
ip
=
self
.
request
.
data
.
get
(
'remote_addr'
,
None
)
ip
=
ip
or
get_request_ip
(
self
.
request
)
return
ip
def
check_is_block
(
self
):
username
=
self
.
request
.
data
.
get
(
"username"
)
ip
=
self
.
get_request_ip
()
if
is_block_login
(
username
,
ip
):
msg
=
errors
.
ip_blocked
logger
.
warn
(
msg
+
': '
+
username
+
':'
+
ip
)
raise
errors
.
AuthFailedError
(
msg
,
'blocked'
)
def
get_user_from_session
(
self
):
user_id
=
self
.
request
.
session
[
"user_id"
]
user
=
get_object_or_none
(
User
,
pk
=
user_id
)
if
not
user
:
error
=
"Not user in session: {}"
.
format
(
user_id
)
raise
errors
.
AuthFailedError
(
error
,
'session_error'
)
return
user
def
check_user_auth
(
self
):
request
=
self
.
request
if
request
.
session
.
get
(
"auth_password"
)
and
\
request
.
session
.
get
(
'user_id'
):
user
=
self
.
get_user_from_session
()
return
user
self
.
check_is_block
()
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
)
ip
=
self
.
get_request_ip
()
if
not
user
:
raise
errors
.
AuthFailedError
(
msg
,
error
=
'auth_failed'
,
username
=
username
)
clean_failed_count
(
username
,
ip
)
request
.
session
[
'auth_password'
]
=
1
request
.
session
[
'user_id'
]
=
str
(
user
.
id
)
return
user
def
check_user_mfa_if_need
(
self
,
user
):
if
self
.
request
.
session
.
get
(
'auth_mfa'
):
return
True
if
not
user
.
otp_enabled
or
not
user
.
otp_secret_key
:
return
True
otp_code
=
self
.
request
.
data
.
get
(
"otp_code"
)
if
not
otp_code
:
raise
errors
.
MFARequiredError
()
if
not
check_otp_code
(
user
.
otp_secret_key
,
otp_code
):
raise
errors
.
AuthFailedError
(
errors
.
mfa_failed
,
error
=
'mfa_failed'
,
username
=
user
.
username
,
)
return
True
def
check_user_login_confirm_if_need
(
self
,
user
):
from
orders.models
import
LoginConfirmOrder
confirm_setting
=
user
.
get_login_confirm_setting
()
if
self
.
request
.
session
.
get
(
'auth_confirm'
)
or
not
confirm_setting
:
return
order
=
None
if
self
.
request
.
session
.
get
(
'auth_order_id'
):
order_id
=
self
.
request
.
session
[
'auth_order_id'
]
order
=
get_object_or_none
(
LoginConfirmOrder
,
pk
=
order_id
)
if
not
order
:
order
=
confirm_setting
.
create_confirm_order
(
self
.
request
)
self
.
request
.
session
[
'auth_order_id'
]
=
str
(
order
.
id
)
if
order
.
status
==
"accepted"
:
return
elif
order
.
status
==
"rejected"
:
raise
errors
.
LoginConfirmRejectedError
()
else
:
raise
errors
.
LoginConfirmWaitError
()
def
create
(
self
,
request
,
*
args
,
**
kwargs
):
def
create
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
c
heck_session
()
self
.
c
reate_session_if_need
()
# 如果认证没有过,检查账号密码
# 如果认证没有过,检查账号密码
try
:
try
:
user
=
self
.
check_user_auth
()
user
=
self
.
check_user_auth
()
self
.
check_user_mfa_if_need
(
user
)
self
.
check_user_mfa_if_need
(
user
)
self
.
check_user_login_confirm_if_need
(
user
)
self
.
check_user_login_confirm_if_need
(
user
)
self
.
send_auth_signal
(
success
=
True
,
user
=
user
)
self
.
send_auth_signal
(
success
=
True
,
user
=
user
)
self
.
clear_auth_mark
()
resp
=
super
()
.
create
(
request
,
*
args
,
**
kwargs
)
resp
=
super
()
.
create
(
request
,
*
args
,
**
kwargs
)
return
resp
return
resp
except
errors
.
AuthFailedError
as
e
:
except
errors
.
AuthFailedError
as
e
:
if
e
.
username
:
return
Response
(
e
.
as_data
(),
status
=
401
)
increase_login_failed_count
(
e
.
username
,
self
.
get_request_ip
())
self
.
send_auth_signal
(
success
=
False
,
username
=
e
.
username
,
reason
=
e
.
reason
)
return
Response
({
'msg'
:
e
.
reason
,
'error'
:
e
.
error
},
status
=
401
)
except
errors
.
MFARequiredError
:
msg
=
_
(
"MFA required"
)
data
=
{
'msg'
:
msg
,
"choices"
:
[
"otp"
],
"error"
:
'mfa_required'
}
return
Response
(
data
,
status
=
300
)
except
errors
.
LoginConfirmRejectedError
as
e
:
pass
except
errors
.
LoginConfirmWaitError
as
e
:
pass
except
errors
.
LoginConfirmRequiredError
as
e
:
pass
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
)
apps/authentication/errors.py
View file @
6ce9815d
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.urls
import
reverse
from
django.conf
import
settings
password_failed
=
_
(
'Username/password check failed'
)
from
.signals
import
post_auth_failed
mfa_failed
=
_
(
'MFA authentication failed'
)
from
users.utils
import
(
user_not_exist
=
_
(
"Username does not exist"
)
increase_login_failed_count
,
get_login_failed_count
password_expired
=
_
(
"Password expired"
)
)
user_invalid
=
_
(
'Disabled or expired'
)
ip_blocked
=
_
(
"Log in frequently and try again later"
)
mfa_required
=
_
(
"MFA required"
)
reason_password_failed
=
'password_failed'
login_confirm_required
=
_
(
"Login confirm required"
)
reason_mfa_failed
=
'mfa_failed'
login_confirm_wait
=
_
(
"Wait login confirm"
)
reason_user_not_exist
=
'user_not_exist'
reason_password_expired
=
'password_expired'
reason_user_invalid
=
'user_invalid'
reason_user_inactive
=
'user_inactive'
reason_choices
=
{
reason_password_failed
:
_
(
'Username/password check failed'
),
reason_mfa_failed
:
_
(
'MFA authentication failed'
),
reason_user_not_exist
:
_
(
"Username does not exist"
),
reason_password_expired
:
_
(
"Password expired"
),
reason_user_invalid
:
_
(
'Disabled or expired'
),
reason_user_inactive
:
_
(
"This account is inactive."
)
}
old_reason_choices
=
{
'0'
:
'-'
,
'1'
:
reason_choices
[
reason_password_failed
],
'2'
:
reason_choices
[
reason_mfa_failed
],
'3'
:
reason_choices
[
reason_user_not_exist
],
'4'
:
reason_choices
[
reason_password_expired
],
}
session_empty_msg
=
_
(
"No session found, check your cookie"
)
invalid_login_msg
=
_
(
"The username or password you entered is incorrect, "
"please enter it again. "
"You can also try {times_try} times "
"(The account will be temporarily locked for {block_time} minutes)"
)
block_login_msg
=
_
(
"The account has been locked "
"(please contact admin to unlock it or try again after {} minutes)"
)
mfa_failed_msg
=
_
(
"MFA code invalid, or ntp sync server time"
)
mfa_required_msg
=
_
(
"MFA required"
)
login_confirm_required_msg
=
_
(
"Login confirm required"
)
login_confirm_wait_msg
=
_
(
"Wait login confirm order for accept"
)
login_confirm_rejected_msg
=
_
(
"Login confirm order was rejected"
)
login_confirm_order_not_found_msg
=
_
(
"Order not found"
)
class
AuthFailedNeedLogMixin
:
username
=
''
request
=
None
error
=
''
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
()
.
__init__
(
*
args
,
**
kwargs
)
post_auth_failed
.
send
(
sender
=
self
.
__class__
,
username
=
self
.
username
,
request
=
self
.
request
,
reason
=
self
.
error
)
class
AuthFailedNeedBlockMixin
:
username
=
''
ip
=
''
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
()
.
__init__
(
*
args
,
**
kwargs
)
increase_login_failed_count
(
self
.
username
,
self
.
ip
)
class
AuthFailedError
(
Exception
):
class
AuthFailedError
(
Exception
):
def
__init__
(
self
,
reason
,
error
=
None
,
username
=
None
):
username
=
''
self
.
reason
=
reason
msg
=
''
self
.
error
=
error
error
=
''
self
.
username
=
username
request
=
None
ip
=
''
def
__init__
(
self
,
**
kwargs
):
for
k
,
v
in
kwargs
.
items
():
setattr
(
self
,
k
,
v
)
def
as_data
(
self
):
return
{
'error'
:
self
.
error
,
'msg'
:
self
.
msg
,
}
class
CredentialError
(
AuthFailedNeedLogMixin
,
AuthFailedNeedBlockMixin
,
AuthFailedError
):
def
__init__
(
self
,
error
,
username
,
ip
,
request
):
super
()
.
__init__
(
error
=
error
,
username
=
username
,
ip
=
ip
,
request
=
request
)
times_up
=
settings
.
SECURITY_LOGIN_LIMIT_COUNT
times_failed
=
get_login_failed_count
(
username
,
ip
)
times_try
=
int
(
times_up
)
-
int
(
times_failed
)
block_time
=
settings
.
SECURITY_LOGIN_LIMIT_TIME
default_msg
=
invalid_login_msg
.
format
(
times_try
=
times_try
,
block_time
=
block_time
)
if
error
==
reason_password_failed
:
self
.
msg
=
default_msg
else
:
self
.
msg
=
reason_choices
.
get
(
error
,
default_msg
)
class
MFAFailedError
(
AuthFailedNeedLogMixin
,
AuthFailedError
):
reason
=
reason_mfa_failed
error
=
'mfa_failed'
msg
=
mfa_failed_msg
def
__init__
(
self
,
username
,
request
):
super
()
.
__init__
(
username
=
username
,
request
=
request
)
class
BlockLoginError
(
AuthFailedNeedBlockMixin
,
AuthFailedError
):
error
=
'block_login'
msg
=
block_login_msg
.
format
(
settings
.
SECURITY_LOGIN_LIMIT_TIME
)
def
__init__
(
self
,
username
,
ip
):
super
()
.
__init__
(
username
=
username
,
ip
=
ip
)
class
SessionEmptyError
(
AuthFailedError
):
msg
=
session_empty_msg
error
=
'session_empty_msg'
class
MFARequiredError
(
AuthFailedError
):
msg
=
mfa_required_msg
error
=
'mfa_required_msg'
def
as_data
(
self
):
return
{
'error'
:
self
.
error
,
'msg'
:
self
.
msg
,
'choices'
:
[
'otp'
],
'url'
:
reverse
(
'api-auth:mfa-challenge'
)
}
class
LoginConfirmRequiredError
(
AuthFailedError
):
msg
=
login_confirm_required_msg
error
=
'login_confirm_required_msg'
class
LoginConfirmError
(
AuthFailedError
):
msg
=
login_confirm_wait_msg
error
=
'login_confirm_wait_msg'
def
__init__
(
self
,
order_id
,
**
kwargs
):
self
.
order_id
=
order_id
super
()
.
__init__
(
**
kwargs
)
class
MFARequiredError
(
Exception
):
def
as_data
(
self
):
reason
=
mfa_required
return
{
error
=
'mfa_required'
"error"
:
self
.
error
,
"msg"
:
self
.
msg
,
"order_id"
:
self
.
order_id
}
class
LoginConfirm
RequiredError
(
Exception
):
class
LoginConfirm
WaitError
(
LoginConfirmError
):
reason
=
login_confirm_required
msg
=
login_confirm_wait_msg
error
=
'login_confirm_
required
'
error
=
'login_confirm_
wait_msg
'
class
LoginConfirm
WaitError
(
Exception
):
class
LoginConfirm
RejectedError
(
LoginConfirmError
):
reason
=
login_confirm_wait
msg
=
login_confirm_rejected_msg
error
=
'login_confirm_
wait
'
error
=
'login_confirm_
rejected_msg
'
class
LoginConfirm
RejectedError
(
Exception
):
class
LoginConfirm
OrderNotFound
(
LoginConfirmError
):
reason
=
login_confirm_wait
msg
=
login_confirm_order_not_found_msg
error
=
'login_confirm_
rejected
'
error
=
'login_confirm_
order_not_found_msg
'
apps/authentication/forms.py
View file @
6ce9815d
...
@@ -9,53 +9,19 @@ from django.conf import settings
...
@@ -9,53 +9,19 @@ from django.conf import settings
from
users.utils
import
get_login_failed_count
from
users.utils
import
get_login_failed_count
class
UserLoginForm
(
Authentication
Form
):
class
UserLoginForm
(
forms
.
Form
):
username
=
forms
.
CharField
(
label
=
_
(
'Username'
),
max_length
=
100
)
username
=
forms
.
CharField
(
label
=
_
(
'Username'
),
max_length
=
100
)
password
=
forms
.
CharField
(
password
=
forms
.
CharField
(
label
=
_
(
'Password'
),
widget
=
forms
.
PasswordInput
,
label
=
_
(
'Password'
),
widget
=
forms
.
PasswordInput
,
max_length
=
128
,
strip
=
False
max_length
=
128
,
strip
=
False
)
)
error_messages
=
{
'invalid_login'
:
_
(
"The username or password you entered is incorrect, "
"please enter it again."
),
'inactive'
:
_
(
"This account is inactive."
),
'limit_login'
:
_
(
"You can also try {times_try} times "
"(The account will be temporarily locked for {block_time} minutes)"
),
'block_login'
:
_
(
"The account has been locked "
"(please contact admin to unlock it or try again after {} minutes)"
)
}
def
confirm_login_allowed
(
self
,
user
):
def
confirm_login_allowed
(
self
,
user
):
if
not
user
.
is_staff
:
if
not
user
.
is_staff
:
raise
forms
.
ValidationError
(
raise
forms
.
ValidationError
(
self
.
error_messages
[
'inactive'
],
self
.
error_messages
[
'inactive'
],
code
=
'inactive'
,)
code
=
'inactive'
,
def
get_limit_login_error_message
(
self
,
username
,
ip
):
times_up
=
settings
.
SECURITY_LOGIN_LIMIT_COUNT
times_failed
=
get_login_failed_count
(
username
,
ip
)
times_try
=
int
(
times_up
)
-
int
(
times_failed
)
block_time
=
settings
.
SECURITY_LOGIN_LIMIT_TIME
if
times_try
<=
0
:
error_message
=
self
.
error_messages
[
'block_login'
]
error_message
=
error_message
.
format
(
block_time
)
else
:
error_message
=
self
.
error_messages
[
'limit_login'
]
error_message
=
error_message
.
format
(
times_try
=
times_try
,
block_time
=
block_time
,
)
)
return
error_message
def
add_limit_login_error
(
self
,
username
,
ip
):
error
=
self
.
get_limit_login_error_message
(
username
,
ip
)
self
.
add_error
(
'password'
,
error
)
class
UserLoginCaptchaForm
(
UserLoginForm
):
class
UserLoginCaptchaForm
(
UserLoginForm
):
...
...
apps/authentication/mixins.py
0 → 100644
View file @
6ce9815d
# -*- coding: utf-8 -*-
#
import
time
from
common.utils
import
get_object_or_none
,
get_request_ip
,
get_logger
from
users.models
import
User
from
users.utils
import
(
is_block_login
,
clean_failed_count
,
increase_login_failed_count
)
from
.
import
errors
from
.utils
import
check_user_valid
from
.signals
import
post_auth_success
,
post_auth_failed
logger
=
get_logger
(
__name__
)
class
AuthMixin
:
request
=
None
def
get_user_from_session
(
self
):
if
self
.
request
.
session
.
is_empty
():
raise
errors
.
SessionEmptyError
()
if
self
.
request
.
user
and
not
self
.
request
.
user
.
is_anonymous
:
return
self
.
request
.
user
user_id
=
self
.
request
.
session
.
get
(
'user_id'
)
if
not
user_id
:
user
=
None
else
:
user
=
get_object_or_none
(
User
,
pk
=
user_id
)
if
not
user
:
raise
errors
.
SessionEmptyError
()
return
user
def
get_request_ip
(
self
):
ip
=
''
if
hasattr
(
self
.
request
,
'data'
):
ip
=
self
.
request
.
data
.
get
(
'remote_addr'
,
''
)
ip
=
ip
or
get_request_ip
(
self
.
request
)
return
ip
def
check_is_block
(
self
):
if
hasattr
(
self
.
request
,
'data'
):
username
=
self
.
request
.
data
.
get
(
"username"
)
else
:
username
=
self
.
request
.
POST
.
get
(
"username"
)
ip
=
self
.
get_request_ip
()
if
is_block_login
(
username
,
ip
):
logger
.
warn
(
'Ip was blocked'
+
': '
+
username
+
':'
+
ip
)
raise
errors
.
BlockLoginError
(
username
=
username
,
ip
=
ip
)
def
check_user_auth
(
self
):
request
=
self
.
request
self
.
check_is_block
()
if
hasattr
(
request
,
'data'
):
username
=
request
.
data
.
get
(
'username'
,
''
)
password
=
request
.
data
.
get
(
'password'
,
''
)
public_key
=
request
.
data
.
get
(
'public_key'
,
''
)
else
:
username
=
request
.
POST
.
get
(
'username'
,
''
)
password
=
request
.
POST
.
get
(
'password'
,
''
)
public_key
=
request
.
POST
.
get
(
'public_key'
,
''
)
user
,
error
=
check_user_valid
(
username
=
username
,
password
=
password
,
public_key
=
public_key
)
ip
=
self
.
get_request_ip
()
if
not
user
:
raise
errors
.
CredentialError
(
username
=
username
,
error
=
error
,
ip
=
ip
,
request
=
request
)
clean_failed_count
(
username
,
ip
)
request
.
session
[
'auth_password'
]
=
1
request
.
session
[
'user_id'
]
=
str
(
user
.
id
)
return
user
def
check_user_mfa_if_need
(
self
,
user
):
if
self
.
request
.
session
.
get
(
'auth_mfa'
):
return
True
if
not
user
.
otp_enabled
or
not
user
.
otp_secret_key
:
return
True
raise
errors
.
MFARequiredError
()
def
check_user_mfa
(
self
,
code
):
user
=
self
.
get_user_from_session
()
ok
=
user
.
check_otp
(
code
)
if
ok
:
self
.
request
.
session
[
'auth_mfa'
]
=
1
self
.
request
.
session
[
'auth_mfa_time'
]
=
time
.
time
()
self
.
request
.
session
[
'auth_mfa_type'
]
=
'otp'
return
raise
errors
.
MFAFailedError
(
username
=
user
.
username
,
request
=
self
.
request
)
def
check_user_login_confirm_if_need
(
self
,
user
):
from
orders.models
import
LoginConfirmOrder
confirm_setting
=
user
.
get_login_confirm_setting
()
if
self
.
request
.
session
.
get
(
'auth_confirm'
)
or
not
confirm_setting
:
return
order
=
None
if
self
.
request
.
session
.
get
(
'auth_order_id'
):
order_id
=
self
.
request
.
session
[
'auth_order_id'
]
order
=
get_object_or_none
(
LoginConfirmOrder
,
pk
=
order_id
)
if
not
order
:
order
=
confirm_setting
.
create_confirm_order
(
self
.
request
)
self
.
request
.
session
[
'auth_order_id'
]
=
str
(
order
.
id
)
if
order
.
status
==
"accepted"
:
return
elif
order
.
status
==
"rejected"
:
raise
errors
.
LoginConfirmRejectedError
(
order
.
id
)
else
:
raise
errors
.
LoginConfirmWaitError
(
order
.
id
)
def
clear_auth_mark
(
self
):
self
.
request
.
session
[
'auth_password'
]
=
''
self
.
request
.
session
[
'auth_mfa'
]
=
''
self
.
request
.
session
[
'auth_confirm'
]
=
''
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
)
apps/authentication/serializers.py
View file @
6ce9815d
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
rest_framework
import
serializers
from
rest_framework
import
serializers
from
common.utils
import
get_object_or_none
from
users.models
import
User
from
users.models
import
User
from
.models
import
AccessKey
,
LoginConfirmSetting
from
.models
import
AccessKey
,
LoginConfirmSetting
...
@@ -24,7 +25,12 @@ class OtpVerifySerializer(serializers.Serializer):
...
@@ -24,7 +25,12 @@ class OtpVerifySerializer(serializers.Serializer):
code
=
serializers
.
CharField
(
max_length
=
6
,
min_length
=
6
)
code
=
serializers
.
CharField
(
max_length
=
6
,
min_length
=
6
)
class
BearerTokenMixin
(
serializers
.
Serializer
):
class
BearerTokenSerializer
(
serializers
.
Serializer
):
username
=
serializers
.
CharField
(
allow_null
=
True
,
required
=
False
)
password
=
serializers
.
CharField
(
write_only
=
True
,
allow_null
=
True
,
required
=
False
)
public_key
=
serializers
.
CharField
(
write_only
=
True
,
allow_null
=
True
,
required
=
False
)
token
=
serializers
.
CharField
(
read_only
=
True
)
token
=
serializers
.
CharField
(
read_only
=
True
)
keyword
=
serializers
.
SerializerMethodField
()
keyword
=
serializers
.
SerializerMethodField
()
date_expired
=
serializers
.
DateTimeField
(
read_only
=
True
)
date_expired
=
serializers
.
DateTimeField
(
read_only
=
True
)
...
@@ -33,58 +39,35 @@ class BearerTokenMixin(serializers.Serializer):
...
@@ -33,58 +39,35 @@ class BearerTokenMixin(serializers.Serializer):
def
get_keyword
(
obj
):
def
get_keyword
(
obj
):
return
'Bearer'
return
'Bearer'
def
create_response
(
self
,
username
):
def
create
(
self
,
validated_data
):
request
=
self
.
context
.
get
(
"request"
)
request
=
self
.
context
.
get
(
'request'
)
try
:
if
request
.
user
and
not
request
.
user
.
is_anonymous
:
user
=
User
.
objects
.
get
(
username
=
username
)
user
=
request
.
user
except
User
.
DoesNotExist
:
else
:
raise
serializers
.
ValidationError
(
"username
%
s not exist"
%
username
)
user_id
=
request
.
session
.
get
(
'user_id'
)
user
=
get_object_or_none
(
User
,
pk
=
user_id
)
if
not
user
:
raise
serializers
.
ValidationError
(
"user id {} not exist"
.
format
(
user_id
)
)
token
,
date_expired
=
user
.
create_bearer_token
(
request
)
token
,
date_expired
=
user
.
create_bearer_token
(
request
)
instance
=
{
instance
=
{
"username"
:
username
,
"username"
:
user
.
user
name
,
"token"
:
token
,
"token"
:
token
,
"date_expired"
:
date_expired
,
"date_expired"
:
date_expired
,
}
}
return
instance
return
instance
def
update
(
self
,
instance
,
validated_data
):
pass
class
BearerTokenSerializer
(
BearerTokenMixin
,
serializers
.
Serializer
):
username
=
serializers
.
CharField
()
password
=
serializers
.
CharField
(
write_only
=
True
,
allow_null
=
True
,
required
=
False
)
public_key
=
serializers
.
CharField
(
write_only
=
True
,
allow_null
=
True
,
required
=
False
)
def
create
(
self
,
validated_data
):
username
=
validated_data
.
get
(
"username"
)
return
self
.
create_response
(
username
)
class
MFAChallengeSerializer
(
BearerTokenMixin
,
serializers
.
Serializer
):
class
MFAChallengeSerializer
(
serializers
.
Serializer
):
req
=
serializers
.
CharField
(
write_only
=
True
)
auth_type
=
serializers
.
CharField
(
write_only
=
True
,
required
=
False
,
allow_blank
=
True
)
auth_type
=
serializers
.
CharField
(
write_only
=
True
)
code
=
serializers
.
CharField
(
write_only
=
True
)
code
=
serializers
.
CharField
(
write_only
=
True
)
def
validate_req
(
self
,
attr
):
username
=
cache
.
get
(
attr
)
if
not
username
:
raise
serializers
.
ValidationError
(
"Not valid, may be expired"
)
self
.
context
[
"username"
]
=
username
def
validate_code
(
self
,
code
):
username
=
self
.
context
[
"username"
]
user
=
User
.
objects
.
get
(
username
=
username
)
ok
=
user
.
check_otp
(
code
)
if
not
ok
:
msg
=
"Otp code not valid, may be expired"
raise
serializers
.
ValidationError
(
msg
)
def
create
(
self
,
validated_data
):
def
create
(
self
,
validated_data
):
username
=
self
.
context
[
"username"
]
pass
return
self
.
create_response
(
username
)
def
update
(
self
,
instance
,
validated_data
):
pass
class
LoginConfirmSettingSerializer
(
serializers
.
ModelSerializer
):
class
LoginConfirmSettingSerializer
(
serializers
.
ModelSerializer
):
...
...
apps/authentication/signals_handlers.py
View file @
6ce9815d
from
rest_framework.request
import
Request
from
django.http.request
import
QueryDict
from
django.http.request
import
QueryDict
from
django.conf
import
settings
from
django.conf
import
settings
from
django.dispatch
import
receiver
from
django.dispatch
import
receiver
from
django.contrib.auth.signals
import
user_logged_out
from
django.contrib.auth.signals
import
user_logged_out
from
django.utils
import
timezone
from
django_auth_ldap.backend
import
populate_user
from
django_auth_ldap.backend
import
populate_user
from
common.utils
import
get_request_ip
from
.backends.openid
import
new_client
from
.backends.openid
import
new_client
from
.backends.openid.signals
import
(
from
.backends.openid.signals
import
(
post_create_openid_user
,
post_openid_login_success
post_create_openid_user
,
post_openid_login_success
)
)
from
.tasks
import
write_login_log_async
from
.signals
import
post_auth_success
from
.signals
import
post_auth_success
,
post_auth_failed
@receiver
(
user_logged_out
)
@receiver
(
user_logged_out
)
...
@@ -52,35 +48,4 @@ def on_ldap_create_user(sender, user, ldap_user, **kwargs):
...
@@ -52,35 +48,4 @@ def on_ldap_create_user(sender, user, ldap_user, **kwargs):
user
.
save
()
user
.
save
()
def
generate_data
(
username
,
request
):
user_agent
=
request
.
META
.
get
(
'HTTP_USER_AGENT'
,
''
)
if
isinstance
(
request
,
Request
):
login_ip
=
request
.
data
.
get
(
'remote_addr'
,
None
)
login_type
=
request
.
data
.
get
(
'login_type'
,
''
)
else
:
login_ip
=
get_request_ip
(
request
)
login_type
=
'W'
data
=
{
'username'
:
username
,
'ip'
:
login_ip
,
'type'
:
login_type
,
'user_agent'
:
user_agent
,
'datetime'
:
timezone
.
now
()
}
return
data
@receiver
(
post_auth_success
)
def
on_user_auth_success
(
sender
,
user
,
request
,
**
kwargs
):
data
=
generate_data
(
user
.
username
,
request
)
data
.
update
({
'mfa'
:
int
(
user
.
otp_enabled
),
'status'
:
True
})
write_login_log_async
.
delay
(
**
data
)
@receiver
(
post_auth_failed
)
def
on_user_auth_failed
(
sender
,
username
,
request
,
reason
,
**
kwargs
):
data
=
generate_data
(
username
,
request
)
data
.
update
({
'reason'
:
reason
,
'status'
:
False
})
write_login_log_async
.
delay
(
**
data
)
apps/authentication/tasks.py
View file @
6ce9815d
...
@@ -6,17 +6,8 @@ from ops.celery.decorator import register_as_period_task
...
@@ -6,17 +6,8 @@ from ops.celery.decorator import register_as_period_task
from
django.contrib.sessions.models
import
Session
from
django.contrib.sessions.models
import
Session
from
django.utils
import
timezone
from
django.utils
import
timezone
from
.utils
import
write_login_log
@shared_task
def
write_login_log_async
(
*
args
,
**
kwargs
):
write_login_log
(
*
args
,
**
kwargs
)
@register_as_period_task
(
interval
=
3600
*
24
)
@register_as_period_task
(
interval
=
3600
*
24
)
@shared_task
@shared_task
def
clean_django_sessions
():
def
clean_django_sessions
():
Session
.
objects
.
filter
(
expire_date__lt
=
timezone
.
now
())
.
delete
()
Session
.
objects
.
filter
(
expire_date__lt
=
timezone
.
now
())
.
delete
()
apps/authentication/templates/authentication/login.html
View file @
6ce9815d
...
@@ -37,7 +37,6 @@
...
@@ -37,7 +37,6 @@
<p>
<p>
{% trans "Changes the world, starting with a little bit." %}
{% trans "Changes the world, starting with a little bit." %}
</p>
</p>
</div>
</div>
<div
class=
"col-md-6"
>
<div
class=
"col-md-6"
>
<div
class=
"ibox-content"
>
<div
class=
"ibox-content"
>
...
@@ -47,25 +46,29 @@
...
@@ -47,25 +46,29 @@
</div>
</div>
<form
class=
"m-t"
role=
"form"
method=
"post"
action=
""
>
<form
class=
"m-t"
role=
"form"
method=
"post"
action=
""
>
{% csrf_token %}
{% csrf_token %}
{% if form.non_field_errors %}
{% if block_login %}
<div
style=
"line-height: 17px;"
>
<p
class=
"red-fonts"
>
{% trans 'Log in frequently and try again later' %}
</p>
{% elif password_expired %}
<p
class=
"red-fonts"
>
{% trans 'The user password has expired' %}
</p>
{% elif form.errors %}
{% if 'captcha' in form.errors %}
<p
class=
"red-fonts"
>
{% trans 'Captcha invalid' %}
</p>
{% else %}
<p
class=
"red-fonts"
>
{{ form.non_field_errors.as_text }}
</p>
<p
class=
"red-fonts"
>
{{ form.non_field_errors.as_text }}
</p>
{% endif %}
</div>
<p
class=
"red-fonts"
>
{{ form.errors.password.as_text }}
</p>
{% elif form.errors.captcha %}
<p
class=
"red-fonts"
>
{% trans 'Captcha invalid' %}
</p>
{% endif %}
{% endif %}
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<input
type=
"text"
class=
"form-control"
name=
"{{ form.username.html_name }}"
placeholder=
"{% trans 'Username' %}"
required=
""
value=
"{% if form.username.value %}{{ form.username.value }}{% endif %}"
>
<input
type=
"text"
class=
"form-control"
name=
"{{ form.username.html_name }}"
placeholder=
"{% trans 'Username' %}"
required=
""
value=
"{% if form.username.value %}{{ form.username.value }}{% endif %}"
>
{% if form.errors.username %}
<div
class=
"help-block field-error"
>
<p
class=
"red-fonts"
>
{{ form.errors.username.as_text }}
</p>
</div>
{% endif %}
</div>
</div>
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<input
type=
"password"
class=
"form-control"
name=
"{{ form.password.html_name }}"
placeholder=
"{% trans 'Password' %}"
required=
""
>
<input
type=
"password"
class=
"form-control"
name=
"{{ form.password.html_name }}"
placeholder=
"{% trans 'Password' %}"
required=
""
>
{% if form.errors.password %}
<div
class=
"help-block field-error"
>
<p
class=
"red-fonts"
>
{{ form.errors.password.as_text }}
</p>
</div>
{% endif %}
</div>
</div>
<div>
<div>
{{ form.captcha }}
{{ form.captcha }}
...
...
apps/authentication/templates/authentication/login_wait_confirm.html
View file @
6ce9815d
...
@@ -86,7 +86,7 @@ function doRequestAuth() {
...
@@ -86,7 +86,7 @@ function doRequestAuth() {
window
.
location
=
successUrl
;
window
.
location
=
successUrl
;
},
},
error
:
function
(
text
,
data
)
{
error
:
function
(
text
,
data
)
{
if
(
data
.
status
!==
"pending
"
)
{
if
(
data
.
error
!==
"login_confirm_wait
"
)
{
if
(
!
errorMsgShow
)
{
if
(
!
errorMsgShow
)
{
infoMsgRef
.
hide
();
infoMsgRef
.
hide
();
errorMsgRef
.
show
();
errorMsgRef
.
show
();
...
@@ -97,7 +97,7 @@ function doRequestAuth() {
...
@@ -97,7 +97,7 @@ function doRequestAuth() {
clearInterval
(
checkInterval
);
clearInterval
(
checkInterval
);
$
(
".copy-btn"
).
attr
(
'disabled'
,
'disabled'
)
$
(
".copy-btn"
).
attr
(
'disabled'
,
'disabled'
)
}
}
errorMsgRef
.
html
(
data
.
error
)
errorMsgRef
.
html
(
data
.
msg
)
},
},
flash_message
:
false
flash_message
:
false
})
})
...
...
apps/authentication/templates/authentication/xpack_login.html
View file @
6ce9815d
...
@@ -48,6 +48,13 @@
...
@@ -48,6 +48,13 @@
float
:
right
;
float
:
right
;
}
}
.red-fonts
{
color
:
red
;
}
.field-error
{
text-align
:
left
;
}
</style>
</style>
</head>
</head>
...
@@ -69,30 +76,32 @@
...
@@ -69,30 +76,32 @@
<div
style=
"margin-bottom: 10px"
>
<div
style=
"margin-bottom: 10px"
>
<div>
<div>
<div
class=
"col-md-1"
></div>
<div
class=
"col-md-1"
></div>
<div
class=
"contact-form col-md-10"
style=
"margin-top:
1
0px;height: 35px"
>
<div
class=
"contact-form col-md-10"
style=
"margin-top:
2
0px;height: 35px"
>
<form
id=
"contact-form"
action=
""
method=
"post"
role=
"form"
novalidate=
"novalidate"
>
<form
id=
"contact-form"
action=
""
method=
"post"
role=
"form"
novalidate=
"novalidate"
>
{% csrf_token %}
{% csrf_token %}
{% if form.non_field_errors %}
<div
style=
"height: 70px;color: red;line-height: 17px;"
>
<div
style=
"height: 70px;color: red;line-height: 17px;"
>
{% if block_login %}
<p
class=
"red-fonts"
>
{% trans 'Log in frequently and try again later' %}
</p>
<p
class=
"red-fonts"
>
{{ form.errors.password.as_text }}
</p>
{% elif password_expired %}
<p
class=
"red-fonts"
>
{% trans 'The user password has expired' %}
</p>
{% elif form.errors %}
{% if 'captcha' in form.errors %}
<p
class=
"red-fonts"
>
{% trans 'Captcha invalid' %}
</p>
{% else %}
<p
class=
"red-fonts"
>
{{ form.non_field_errors.as_text }}
</p>
<p
class=
"red-fonts"
>
{{ form.non_field_errors.as_text }}
</p>
{% endif %}
<p
class=
"red-fonts"
>
{{ form.errors.password.as_text }}
</p>
{% endif %}
</div>
</div>
{% elif form.errors.captcha %}
<p
class=
"red-fonts"
>
{% trans 'Captcha invalid' %}
</p>
{% endif %}
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<input
type=
"text"
class=
"form-control"
name=
"{{ form.username.html_name }}"
placeholder=
"{% trans 'Username' %}"
required=
""
value=
"{% if form.username.value %}{{ form.username.value }}{% endif %}"
style=
"height: 35px"
>
<input
type=
"text"
class=
"form-control"
name=
"{{ form.username.html_name }}"
placeholder=
"{% trans 'Username' %}"
required=
""
value=
"{% if form.username.value %}{{ form.username.value }}{% endif %}"
style=
"height: 35px"
>
{% if form.errors.username %}
<div
class=
"help-block field-error"
>
<p
class=
"red-fonts"
>
{{ form.errors.username.as_text }}
</p>
</div>
{% endif %}
</div>
</div>
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<input
type=
"password"
class=
"form-control"
name=
"{{ form.password.html_name }}"
placeholder=
"{% trans 'Password' %}"
required=
""
>
<input
type=
"password"
class=
"form-control"
name=
"{{ form.password.html_name }}"
placeholder=
"{% trans 'Password' %}"
required=
""
>
{% if form.errors.password %}
<div
class=
"help-block field-error"
>
<p
class=
"red-fonts"
>
{{ form.errors.password.as_text }}
</p>
</div>
{% endif %}
</div>
</div>
<div
class=
"form-group"
style=
"height: 50px;margin-bottom: 0;font-size: 13px"
>
<div
class=
"form-group"
style=
"height: 50px;margin-bottom: 0;font-size: 13px"
>
{{ form.captcha }}
{{ form.captcha }}
...
...
apps/authentication/urls/api_urls.py
View file @
6ce9815d
...
@@ -12,12 +12,11 @@ router.register('access-keys', api.AccessKeyViewSet, 'access-key')
...
@@ -12,12 +12,11 @@ router.register('access-keys', api.AccessKeyViewSet, 'access-key')
urlpatterns
=
[
urlpatterns
=
[
# path('token/', api.UserToken.as_view(), name='user-token'),
# path('token/', api.UserToken.as_view(), name='user-token'),
path
(
'auth/'
,
api
.
UserAuth
Api
.
as_view
(),
name
=
'user-auth'
),
path
(
'auth/'
,
api
.
TokenCreate
Api
.
as_view
(),
name
=
'user-auth'
),
path
(
'tokens/'
,
api
.
TokenCreateApi
.
as_view
(),
name
=
'auth-token'
),
path
(
'tokens/'
,
api
.
TokenCreateApi
.
as_view
(),
name
=
'auth-token'
),
path
(
'mfa/challenge/'
,
api
.
MFAChallengeApi
.
as_view
(),
name
=
'mfa-challenge'
),
path
(
'mfa/challenge/'
,
api
.
MFAChallengeApi
.
as_view
(),
name
=
'mfa-challenge'
),
path
(
'connection-token/'
,
path
(
'connection-token/'
,
api
.
UserConnectionTokenApi
.
as_view
(),
name
=
'connection-token'
),
api
.
UserConnectionTokenApi
.
as_view
(),
name
=
'connection-token'
),
path
(
'otp/auth/'
,
api
.
UserOtpAuthApi
.
as_view
(),
name
=
'user-otp-auth'
),
path
(
'otp/verify/'
,
api
.
UserOtpVerifyApi
.
as_view
(),
name
=
'user-otp-verify'
),
path
(
'otp/verify/'
,
api
.
UserOtpVerifyApi
.
as_view
(),
name
=
'user-otp-verify'
),
path
(
'order/auth/'
,
api
.
UserOrderAcceptAuthApi
.
as_view
(),
name
=
'user-order-auth'
),
path
(
'order/auth/'
,
api
.
UserOrderAcceptAuthApi
.
as_view
(),
name
=
'user-order-auth'
),
path
(
'login-confirm-settings/<uuid:user_id>/'
,
api
.
LoginConfirmSettingUpdateApi
.
as_view
(),
name
=
'login-confirm-setting-update'
)
path
(
'login-confirm-settings/<uuid:user_id>/'
,
api
.
LoginConfirmSettingUpdateApi
.
as_view
(),
name
=
'login-confirm-setting-update'
)
...
...
apps/authentication/utils.py
View file @
6ce9815d
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
from
django.utils.translation
import
ugettext
as
_
,
ugettext_lazy
as
__
from
django.utils.translation
import
ugettext
as
_
from
django.contrib.auth
import
authenticate
from
django.contrib.auth
import
authenticate
from
django.utils
import
timezone
from
common.utils
import
(
from
common.utils
import
(
get_ip_city
,
get_object_or_none
,
validate_ip
,
get_request_ip
get_ip_city
,
get_object_or_none
,
validate_ip
)
)
from
users.models
import
User
from
users.models
import
User
from
.
import
errors
from
.
import
errors
def
write_login_log
(
*
args
,
**
kwargs
):
from
audits.models
import
UserLoginLog
default_city
=
_
(
"Unknown"
)
ip
=
kwargs
.
get
(
'ip'
)
or
''
if
not
(
ip
and
validate_ip
(
ip
)):
ip
=
ip
[:
15
]
city
=
default_city
else
:
city
=
get_ip_city
(
ip
)
or
default_city
kwargs
.
update
({
'ip'
:
ip
,
'city'
:
city
})
UserLoginLog
.
objects
.
create
(
**
kwargs
)
def
check_user_valid
(
**
kwargs
):
def
check_user_valid
(
**
kwargs
):
password
=
kwargs
.
pop
(
'password'
,
None
)
password
=
kwargs
.
pop
(
'password'
,
None
)
public_key
=
kwargs
.
pop
(
'public_key'
,
None
)
public_key
=
kwargs
.
pop
(
'public_key'
,
None
)
email
=
kwargs
.
pop
(
'email'
,
None
)
email
=
kwargs
.
pop
(
'email'
,
None
)
username
=
kwargs
.
pop
(
'username'
,
None
)
username
=
kwargs
.
pop
(
'username'
,
None
)
request
=
kwargs
.
get
(
'request'
)
if
username
:
if
username
:
user
=
get_object_or_none
(
User
,
username
=
username
)
user
=
get_object_or_none
(
User
,
username
=
username
)
...
@@ -38,21 +25,25 @@ def check_user_valid(**kwargs):
...
@@ -38,21 +25,25 @@ def check_user_valid(**kwargs):
user
=
None
user
=
None
if
user
is
None
:
if
user
is
None
:
return
None
,
errors
.
user_not_exist
return
None
,
errors
.
reason_user_not_exist
elif
not
user
.
is_valid
:
elif
user
.
is_expired
:
return
None
,
errors
.
user_invalid
return
None
,
errors
.
reason_password_expired
elif
not
user
.
is_active
:
return
None
,
errors
.
reason_user_inactive
elif
user
.
password_has_expired
:
elif
user
.
password_has_expired
:
return
None
,
errors
.
password_expired
return
None
,
errors
.
reason_
password_expired
if
password
and
authenticate
(
username
=
username
,
password
=
password
):
if
password
:
user
=
authenticate
(
request
,
username
=
username
,
password
=
password
)
if
user
:
return
user
,
''
return
user
,
''
if
public_key
and
user
.
public_key
:
if
public_key
and
user
.
public_key
:
public_key_saved
=
user
.
public_key
.
split
()
public_key_saved
=
user
.
public_key
.
split
()
if
len
(
public_key_saved
)
==
1
:
if
len
(
public_key_saved
)
==
1
:
if
public_key
==
public_key_saved
[
0
]:
public_key_saved
=
public_key_saved
[
0
]
return
user
,
''
else
:
elif
len
(
public_key_saved
)
>
1
:
public_key_saved
=
public_key_saved
[
1
]
if
public_key
==
public_key_saved
[
1
]
:
if
public_key
==
public_key_saved
:
return
user
,
''
return
user
,
''
return
None
,
errors
.
password_failed
return
None
,
errors
.
reason_
password_failed
apps/authentication/views/__init__.py
View file @
6ce9815d
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
from
.login
import
*
from
.login
import
*
from
.mfa
import
*
apps/authentication/views/login.py
View file @
6ce9815d
...
@@ -16,22 +16,20 @@ from django.views.decorators.debug import sensitive_post_parameters
...
@@ -16,22 +16,20 @@ from django.views.decorators.debug import sensitive_post_parameters
from
django.views.generic.base
import
TemplateView
,
RedirectView
from
django.views.generic.base
import
TemplateView
,
RedirectView
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
django.urls
import
reverse_lazy
from
common.utils
import
get_request_ip
,
get_object_or_none
from
common.utils
import
get_request_ip
,
get_object_or_none
from
users.models
import
User
from
users.models
import
User
from
users.utils
import
(
from
users.utils
import
(
check_otp_code
,
is_block_login
,
clean_failed_count
,
get_user_or_tmp_user
,
get_user_or_tmp_user
,
increase_login_failed_count
,
set_tmp_user_to_cache
,
increase_login_failed_count
,
redirect_user_first_login_or_index
redirect_user_first_login_or_index
)
)
from
..models
import
LoginConfirmSetting
from
..signals
import
post_auth_success
,
post_auth_failed
from
..signals
import
post_auth_success
,
post_auth_failed
from
..
import
forms
from
..
import
forms
,
mixins
,
errors
from
..
import
errors
__all__
=
[
__all__
=
[
'UserLoginView'
,
'UserLog
inOtpView'
,
'UserLog
outView'
,
'UserLoginView'
,
'UserLogoutView'
,
'UserLoginGuardView'
,
'UserLoginWaitConfirmView'
,
'UserLoginGuardView'
,
'UserLoginWaitConfirmView'
,
]
]
...
@@ -39,10 +37,11 @@ __all__ = [
...
@@ -39,10 +37,11 @@ __all__ = [
@method_decorator
(
sensitive_post_parameters
(),
name
=
'dispatch'
)
@method_decorator
(
sensitive_post_parameters
(),
name
=
'dispatch'
)
@method_decorator
(
csrf_protect
,
name
=
'dispatch'
)
@method_decorator
(
csrf_protect
,
name
=
'dispatch'
)
@method_decorator
(
never_cache
,
name
=
'dispatch'
)
@method_decorator
(
never_cache
,
name
=
'dispatch'
)
class
UserLoginView
(
FormView
):
class
UserLoginView
(
mixins
.
AuthMixin
,
FormView
):
form_class
=
forms
.
UserLoginForm
form_class
=
forms
.
UserLoginForm
form_class_captcha
=
forms
.
UserLoginCaptchaForm
form_class_captcha
=
forms
.
UserLoginCaptchaForm
key_prefix_captcha
=
"_LOGIN_INVALID_{}"
key_prefix_captcha
=
"_LOGIN_INVALID_{}"
redirect_field_name
=
'next'
def
get_template_names
(
self
):
def
get_template_names
(
self
):
template_name
=
'authentication/login.html'
template_name
=
'authentication/login.html'
...
@@ -69,54 +68,25 @@ class UserLoginView(FormView):
...
@@ -69,54 +68,25 @@ class UserLoginView(FormView):
request
.
session
.
set_test_cookie
()
request
.
session
.
set_test_cookie
()
return
super
()
.
get
(
request
,
*
args
,
**
kwargs
)
return
super
()
.
get
(
request
,
*
args
,
**
kwargs
)
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
# limit login authentication
ip
=
get_request_ip
(
request
)
username
=
self
.
request
.
POST
.
get
(
'username'
)
if
is_block_login
(
username
,
ip
):
return
self
.
render_to_response
(
self
.
get_context_data
(
block_login
=
True
))
return
super
()
.
post
(
request
,
*
args
,
**
kwargs
)
def
form_valid
(
self
,
form
):
def
form_valid
(
self
,
form
):
if
not
self
.
request
.
session
.
test_cookie_worked
():
if
not
self
.
request
.
session
.
test_cookie_worked
():
return
HttpResponse
(
_
(
"Please enable cookies and try again."
))
return
HttpResponse
(
_
(
"Please enable cookies and try again."
))
user
=
form
.
get_user
()
try
:
# user password expired
self
.
check_user_auth
()
if
user
.
password_has_expired
:
except
errors
.
AuthFailedError
as
e
:
reason
=
errors
.
password_expired
form
.
add_error
(
None
,
e
.
msg
)
self
.
send_auth_signal
(
success
=
False
,
username
=
user
.
username
,
reason
=
reason
)
ip
=
self
.
get_request_ip
()
return
self
.
render_to_response
(
self
.
get_context_data
(
password_expired
=
True
))
set_tmp_user_to_cache
(
self
.
request
,
user
)
username
=
form
.
cleaned_data
.
get
(
'username'
)
ip
=
get_request_ip
(
self
.
request
)
# 登陆成功,清除缓存计数
clean_failed_count
(
username
,
ip
)
self
.
request
.
session
[
'auth_password'
]
=
'1'
return
self
.
redirect_to_guard_view
()
def
form_invalid
(
self
,
form
):
# write login failed log
username
=
form
.
cleaned_data
.
get
(
'username'
)
exist
=
User
.
objects
.
filter
(
username
=
username
)
.
first
()
reason
=
errors
.
password_failed
if
exist
else
errors
.
user_not_exist
# limit user login failed count
ip
=
get_request_ip
(
self
.
request
)
increase_login_failed_count
(
username
,
ip
)
form
.
add_limit_login_error
(
username
,
ip
)
# show captcha
cache
.
set
(
self
.
key_prefix_captcha
.
format
(
ip
),
1
,
3600
)
cache
.
set
(
self
.
key_prefix_captcha
.
format
(
ip
),
1
,
3600
)
self
.
send_auth_signal
(
success
=
False
,
username
=
username
,
reason
=
reason
)
context
=
self
.
get_context_data
(
form
=
form
)
return
self
.
render_to_response
(
context
)
old_form
=
form
return
self
.
redirect_to_guard_view
()
form
=
self
.
form_class_captcha
(
data
=
form
.
data
)
form
.
_errors
=
old_form
.
errors
return
super
()
.
form_invalid
(
form
)
@staticmethod
def
redirect_to_guard_view
(
self
):
def
redirect_to_guard_view
():
guard_url
=
reverse
(
'authentication:login-guard'
)
continue_url
=
reverse
(
'authentication:login-guard'
)
args
=
self
.
request
.
META
.
get
(
'QUERY_STRING'
,
''
)
return
redirect
(
continue_url
)
if
args
and
self
.
query_string
:
guard_url
=
"
%
s?
%
s"
%
(
guard_url
,
args
)
return
redirect
(
guard_url
)
def
get_form_class
(
self
):
def
get_form_class
(
self
):
ip
=
get_request_ip
(
self
.
request
)
ip
=
get_request_ip
(
self
.
request
)
...
@@ -134,58 +104,34 @@ class UserLoginView(FormView):
...
@@ -134,58 +104,34 @@ class UserLoginView(FormView):
return
super
()
.
get_context_data
(
**
kwargs
)
return
super
()
.
get_context_data
(
**
kwargs
)
class
UserLoginOtpView
(
FormView
):
class
UserLoginGuardView
(
mixins
.
AuthMixin
,
RedirectView
):
template_name
=
'authentication/login_otp.html'
form_class
=
forms
.
UserCheckOtpCodeForm
redirect_field_name
=
'next'
def
form_valid
(
self
,
form
):
user
=
get_user_or_tmp_user
(
self
.
request
)
otp_code
=
form
.
cleaned_data
.
get
(
'otp_code'
)
otp_secret_key
=
user
.
otp_secret_key
if
check_otp_code
(
otp_secret_key
,
otp_code
):
self
.
request
.
session
[
'auth_otp'
]
=
'1'
return
UserLoginView
.
redirect_to_guard_view
()
else
:
self
.
send_auth_signal
(
success
=
False
,
username
=
user
.
username
,
reason
=
errors
.
mfa_failed
)
form
.
add_error
(
'otp_code'
,
_
(
'MFA code invalid, or ntp sync server time'
)
)
return
super
()
.
form_invalid
(
form
)
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
UserLoginGuardView
(
RedirectView
):
redirect_field_name
=
'next'
redirect_field_name
=
'next'
login_url
=
reverse_lazy
(
'authentication:login'
)
login_otp_url
=
reverse_lazy
(
'authentication:login-otp'
)
login_confirm_url
=
reverse_lazy
(
'authentication:login-wait-confirm'
)
def
format_redirect_url
(
self
,
url
):
args
=
self
.
request
.
META
.
get
(
'QUERY_STRING'
,
''
)
if
args
and
self
.
query_string
:
url
=
"
%
s?
%
s"
%
(
url
,
args
)
return
url
def
get_redirect_url
(
self
,
*
args
,
**
kwargs
):
def
get_redirect_url
(
self
,
*
args
,
**
kwargs
):
if
not
self
.
request
.
session
.
get
(
'auth_password'
):
if
not
self
.
request
.
session
.
get
(
'auth_password'
):
return
reverse
(
'authentication:login'
)
return
self
.
format_redirect_url
(
self
.
login_url
)
user
=
self
.
get_user_from_session
()
user
=
get_user_or_tmp_user
(
self
.
request
)
# 启用并设置了otp
# 启用并设置了otp
if
user
.
otp_enabled
and
user
.
otp_secret_key
and
\
if
user
.
otp_enabled
and
user
.
otp_secret_key
and
\
not
self
.
request
.
session
.
get
(
'auth_
otp
'
):
not
self
.
request
.
session
.
get
(
'auth_
mfa
'
):
return
reverse
(
'authentication:login-otp'
)
return
self
.
format_redirect_url
(
self
.
login_otp_url
)
confirm_setting
=
user
.
get_login_confirm_setting
()
confirm_setting
=
user
.
get_login_confirm_setting
()
if
confirm_setting
and
not
self
.
request
.
session
.
get
(
'auth_confirm'
):
if
confirm_setting
and
not
self
.
request
.
session
.
get
(
'auth_confirm'
):
order
=
confirm_setting
.
create_confirm_order
(
self
.
request
)
order
=
confirm_setting
.
create_confirm_order
(
self
.
request
)
self
.
request
.
session
[
'auth_order_id'
]
=
str
(
order
.
id
)
self
.
request
.
session
[
'auth_order_id'
]
=
str
(
order
.
id
)
url
=
reverse
(
'authentication:login-wait-confirm'
)
url
=
self
.
format_redirect_url
(
self
.
login_confirm_url
)
return
url
return
url
self
.
login_success
(
user
)
self
.
login_success
(
user
)
self
.
clear_auth_mark
()
# 启用但是没有设置otp
# 启用但是没有设置otp
if
user
.
otp_enabled
and
not
user
.
otp_secret_key
:
if
user
.
otp_enabled
and
not
user
.
otp_secret_key
:
# 1,2,mfa_setting & F
# 1,2,mfa_setting & F
...
...
apps/authentication/views/mfa.py
0 → 100644
View file @
6ce9815d
# -*- coding: utf-8 -*-
#
from
__future__
import
unicode_literals
from
django.views.generic.edit
import
FormView
from
..
import
forms
,
errors
,
mixins
from
.utils
import
redirect_to_guard_view
__all__
=
[
'UserLoginOtpView'
]
class
UserLoginOtpView
(
mixins
.
AuthMixin
,
FormView
):
template_name
=
'authentication/login_otp.html'
form_class
=
forms
.
UserCheckOtpCodeForm
redirect_field_name
=
'next'
def
form_valid
(
self
,
form
):
otp_code
=
form
.
cleaned_data
.
get
(
'otp_code'
)
try
:
self
.
check_user_mfa
(
otp_code
)
return
redirect_to_guard_view
()
except
errors
.
MFAFailedError
as
e
:
form
.
add_error
(
'otp_code'
,
e
.
reason
)
return
super
()
.
form_invalid
(
form
)
apps/authentication/views/utils.py
0 → 100644
View file @
6ce9815d
# -*- coding: utf-8 -*-
#
from
django.shortcuts
import
reverse
,
redirect
def
redirect_to_guard_view
():
continue_url
=
reverse
(
'authentication:login-guard'
)
return
redirect
(
continue_url
)
apps/common/utils/common.py
View file @
6ce9815d
...
@@ -153,6 +153,14 @@ def get_request_ip(request):
...
@@ -153,6 +153,14 @@ def get_request_ip(request):
return
login_ip
return
login_ip
def
get_request_ip_or_data
(
request
):
ip
=
''
if
hasattr
(
request
,
'data'
):
ip
=
request
.
data
.
get
(
'remote_addr'
,
''
)
ip
=
ip
or
get_request_ip
(
request
)
return
ip
def
validate_ip
(
ip
):
def
validate_ip
(
ip
):
try
:
try
:
ipaddress
.
ip_address
(
ip
)
ipaddress
.
ip_address
(
ip
)
...
...
apps/locale/zh/LC_MESSAGES/django.mo
View file @
6ce9815d
No preview for this file type
apps/locale/zh/LC_MESSAGES/django.po
View file @
6ce9815d
...
@@ -8,7 +8,7 @@ msgid ""
...
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-1
0-31 16:57
+0800\n"
"POT-Creation-Date: 2019-1
1-05 15:00
+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
...
@@ -96,7 +96,7 @@ msgstr "运行参数"
...
@@ -96,7 +96,7 @@ msgstr "运行参数"
#: terminal/templates/terminal/session_list.html:28
#: terminal/templates/terminal/session_list.html:28
#: terminal/templates/terminal/session_list.html:72
#: terminal/templates/terminal/session_list.html:72
#: xpack/plugins/change_auth_plan/forms.py:73
#: xpack/plugins/change_auth_plan/forms.py:73
#: xpack/plugins/change_auth_plan/models.py:41
2
#: xpack/plugins/change_auth_plan/models.py:41
9
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:46
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:46
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:54
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:54
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13
...
@@ -145,14 +145,14 @@ msgstr "资产"
...
@@ -145,14 +145,14 @@ msgstr "资产"
#: terminal/models.py:260 terminal/templates/terminal/terminal_detail.html:43
#: terminal/models.py:260 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
#: users/models/user.py:382 users/templates/users/_select_user_modal.html:13
#: users/models/user.py:382 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:6
3
#: users/templates/users/user_detail.html:6
4
#: users/templates/users/user_group_detail.html:55
#: users/templates/users/user_group_detail.html:55
#: users/templates/users/user_group_list.html:35
#: users/templates/users/user_group_list.html:35
#: users/templates/users/user_list.html:35
#: users/templates/users/user_list.html:35
#: users/templates/users/user_profile.html:51
#: users/templates/users/user_profile.html:51
#: users/templates/users/user_pubkey_update.html:57
#: users/templates/users/user_pubkey_update.html:57
#: xpack/plugins/change_auth_plan/forms.py:56
#: xpack/plugins/change_auth_plan/forms.py:56
#: xpack/plugins/change_auth_plan/models.py:6
3
#: xpack/plugins/change_auth_plan/models.py:6
4
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12
#: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:144
#: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:144
...
@@ -198,8 +198,8 @@ msgstr "参数"
...
@@ -198,8 +198,8 @@ msgstr "参数"
#: perms/templates/perms/asset_permission_detail.html:98
#: perms/templates/perms/asset_permission_detail.html:98
#: perms/templates/perms/remote_app_permission_detail.html:90
#: perms/templates/perms/remote_app_permission_detail.html:90
#: users/models/user.py:423 users/serializers/group.py:32
#: users/models/user.py:423 users/serializers/group.py:32
#: users/templates/users/user_detail.html:11
1
#: users/templates/users/user_detail.html:11
2
#: xpack/plugins/change_auth_plan/models.py:10
8
#: xpack/plugins/change_auth_plan/models.py:10
9
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113
#: xpack/plugins/cloud/models.py:80 xpack/plugins/cloud/models.py:179
#: xpack/plugins/cloud/models.py:80 xpack/plugins/cloud/models.py:179
#: xpack/plugins/gathered_user/models.py:46
#: xpack/plugins/gathered_user/models.py:46
...
@@ -261,11 +261,11 @@ msgstr "创建日期"
...
@@ -261,11 +261,11 @@ msgstr "创建日期"
#: perms/templates/perms/remote_app_permission_detail.html:94
#: perms/templates/perms/remote_app_permission_detail.html:94
#: settings/models.py:34 terminal/models.py:33
#: settings/models.py:34 terminal/models.py:33
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15
#: users/models/user.py:415 users/templates/users/user_detail.html:1
29
#: users/models/user.py:415 users/templates/users/user_detail.html:1
30
#: users/templates/users/user_group_detail.html:67
#: users/templates/users/user_group_detail.html:67
#: users/templates/users/user_group_list.html:37
#: users/templates/users/user_group_list.html:37
#: users/templates/users/user_profile.html:138
#: users/templates/users/user_profile.html:138
#: xpack/plugins/change_auth_plan/models.py:10
4
#: xpack/plugins/change_auth_plan/models.py:10
5
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19
#: xpack/plugins/cloud/models.py:77 xpack/plugins/cloud/models.py:173
#: xpack/plugins/cloud/models.py:77 xpack/plugins/cloud/models.py:173
...
@@ -313,7 +313,7 @@ msgstr "远程应用"
...
@@ -313,7 +313,7 @@ msgstr "远程应用"
#: terminal/templates/terminal/terminal_update.html:45
#: terminal/templates/terminal/terminal_update.html:45
#: users/templates/users/_user.html:50
#: users/templates/users/_user.html:50
#: users/templates/users/user_bulk_update.html:23
#: users/templates/users/user_bulk_update.html:23
#: users/templates/users/user_detail.html:17
8
#: users/templates/users/user_detail.html:17
9
#: users/templates/users/user_group_create_update.html:31
#: users/templates/users/user_group_create_update.html:31
#: users/templates/users/user_password_update.html:75
#: users/templates/users/user_password_update.html:75
#: users/templates/users/user_profile.html:209
#: users/templates/users/user_profile.html:209
...
@@ -420,7 +420,7 @@ msgstr "详情"
...
@@ -420,7 +420,7 @@ msgstr "详情"
#: perms/templates/perms/remote_app_permission_list.html:64
#: perms/templates/perms/remote_app_permission_list.html:64
#: terminal/templates/terminal/terminal_detail.html:16
#: terminal/templates/terminal/terminal_detail.html:16
#: terminal/templates/terminal/terminal_list.html:73
#: terminal/templates/terminal/terminal_list.html:73
#: users/templates/users/user_detail.html:2
5
#: users/templates/users/user_detail.html:2
6
#: users/templates/users/user_group_detail.html:28
#: users/templates/users/user_group_detail.html:28
#: users/templates/users/user_group_list.html:20
#: users/templates/users/user_group_list.html:20
#: users/templates/users/user_group_list.html:71
#: users/templates/users/user_group_list.html:71
...
@@ -467,7 +467,7 @@ msgstr "更新"
...
@@ -467,7 +467,7 @@ msgstr "更新"
#: settings/templates/settings/terminal_setting.html:93
#: settings/templates/settings/terminal_setting.html:93
#: settings/templates/settings/terminal_setting.html:115
#: settings/templates/settings/terminal_setting.html:115
#: terminal/templates/terminal/terminal_list.html:75
#: terminal/templates/terminal/terminal_list.html:75
#: users/templates/users/user_detail.html:3
0
#: users/templates/users/user_detail.html:3
1
#: users/templates/users/user_group_detail.html:32
#: users/templates/users/user_group_detail.html:32
#: users/templates/users/user_group_list.html:73
#: users/templates/users/user_group_list.html:73
#: users/templates/users/user_list.html:111
#: users/templates/users/user_list.html:111
...
@@ -606,7 +606,7 @@ msgstr "端口"
...
@@ -606,7 +606,7 @@ msgstr "端口"
#: assets/templates/assets/asset_detail.html:196
#: assets/templates/assets/asset_detail.html:196
#: assets/templates/assets/system_user_assets.html:83
#: assets/templates/assets/system_user_assets.html:83
#: perms/models/asset_permission.py:81
#: perms/models/asset_permission.py:81
#: xpack/plugins/change_auth_plan/models.py:7
4
#: xpack/plugins/change_auth_plan/models.py:7
5
#: xpack/plugins/gathered_user/models.py:31
#: xpack/plugins/gathered_user/models.py:31
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:17
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:17
msgid "Nodes"
msgid "Nodes"
...
@@ -700,21 +700,21 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
...
@@ -700,21 +700,21 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: assets/templates/assets/admin_user_list.html:45
#: assets/templates/assets/admin_user_list.html:45
#: assets/templates/assets/domain_gateway_list.html:71
#: assets/templates/assets/domain_gateway_list.html:71
#: assets/templates/assets/system_user_detail.html:62
#: assets/templates/assets/system_user_detail.html:62
#: assets/templates/assets/system_user_list.html:48 audits/models.py:8
1
#: assets/templates/assets/system_user_list.html:48 audits/models.py:8
2
#: audits/templates/audits/login_log_list.html:57 authentication/forms.py:13
#: audits/templates/audits/login_log_list.html:57 authentication/forms.py:13
#: authentication/templates/authentication/login.html:6
5
#: authentication/templates/authentication/login.html:6
0
#: authentication/templates/authentication/xpack_login.html:
92
#: authentication/templates/authentication/xpack_login.html:
87
#: ops/models/adhoc.py:189 perms/templates/perms/asset_permission_list.html:70
#: ops/models/adhoc.py:189 perms/templates/perms/asset_permission_list.html:70
#: perms/templates/perms/asset_permission_user.html:55
#: perms/templates/perms/asset_permission_user.html:55
#: perms/templates/perms/remote_app_permission_user.html:54
#: perms/templates/perms/remote_app_permission_user.html:54
#: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:13
#: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:13
#: users/models/user.py:380 users/templates/users/_select_user_modal.html:14
#: users/models/user.py:380 users/templates/users/_select_user_modal.html:14
#: users/templates/users/user_detail.html:6
7
#: users/templates/users/user_detail.html:6
8
#: users/templates/users/user_list.html:36
#: users/templates/users/user_list.html:36
#: users/templates/users/user_profile.html:47
#: users/templates/users/user_profile.html:47
#: xpack/plugins/change_auth_plan/forms.py:58
#: xpack/plugins/change_auth_plan/forms.py:58
#: xpack/plugins/change_auth_plan/models.py:6
5
#: xpack/plugins/change_auth_plan/models.py:6
6
#: xpack/plugins/change_auth_plan/models.py:4
08
#: xpack/plugins/change_auth_plan/models.py:4
15
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:65
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:65
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12
...
@@ -732,8 +732,8 @@ msgstr "密码或密钥密码"
...
@@ -732,8 +732,8 @@ msgstr "密码或密钥密码"
#: assets/templates/assets/_asset_user_auth_update_modal.html:21
#: assets/templates/assets/_asset_user_auth_update_modal.html:21
#: assets/templates/assets/_asset_user_auth_view_modal.html:27
#: assets/templates/assets/_asset_user_auth_view_modal.html:27
#: authentication/forms.py:15
#: authentication/forms.py:15
#: authentication/templates/authentication/login.html:6
8
#: authentication/templates/authentication/login.html:6
3
#: authentication/templates/authentication/xpack_login.html:9
5
#: authentication/templates/authentication/xpack_login.html:9
0
#: settings/forms.py:114 users/forms.py:15 users/forms.py:27
#: settings/forms.py:114 users/forms.py:15 users/forms.py:27
#: users/templates/users/reset_password.html:53
#: users/templates/users/reset_password.html:53
#: users/templates/users/user_password_authentication.html:18
#: users/templates/users/user_password_authentication.html:18
...
@@ -741,8 +741,8 @@ msgstr "密码或密钥密码"
...
@@ -741,8 +741,8 @@ msgstr "密码或密钥密码"
#: users/templates/users/user_profile_update.html:41
#: users/templates/users/user_profile_update.html:41
#: users/templates/users/user_pubkey_update.html:41
#: users/templates/users/user_pubkey_update.html:41
#: users/templates/users/user_update.html:20
#: users/templates/users/user_update.html:20
#: xpack/plugins/change_auth_plan/models.py:9
5
#: xpack/plugins/change_auth_plan/models.py:9
6
#: xpack/plugins/change_auth_plan/models.py:26
3
#: xpack/plugins/change_auth_plan/models.py:26
4
msgid "Password"
msgid "Password"
msgstr "密码"
msgstr "密码"
...
@@ -938,13 +938,13 @@ msgstr "版本"
...
@@ -938,13 +938,13 @@ msgstr "版本"
msgid "AuthBook"
msgid "AuthBook"
msgstr ""
msgstr ""
#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:
99
#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:
100
#: xpack/plugins/change_auth_plan/models.py:27
0
#: xpack/plugins/change_auth_plan/models.py:27
1
msgid "SSH private key"
msgid "SSH private key"
msgstr "ssh密钥"
msgstr "ssh密钥"
#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:10
2
#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:10
3
#: xpack/plugins/change_auth_plan/models.py:26
6
#: xpack/plugins/change_auth_plan/models.py:26
7
msgid "SSH public key"
msgid "SSH public key"
msgstr "ssh公钥"
msgstr "ssh公钥"
...
@@ -965,7 +965,7 @@ msgid "Contact"
...
@@ -965,7 +965,7 @@ msgid "Contact"
msgstr "联系人"
msgstr "联系人"
#: assets/models/cluster.py:22 users/models/user.py:401
#: assets/models/cluster.py:22 users/models/user.py:401
#: users/templates/users/user_detail.html:7
6
#: users/templates/users/user_detail.html:7
7
msgid "Phone"
msgid "Phone"
msgstr "手机"
msgstr "手机"
...
@@ -1121,7 +1121,7 @@ msgstr "默认资产组"
...
@@ -1121,7 +1121,7 @@ msgstr "默认资产组"
#: terminal/templates/terminal/command_list.html:65
#: terminal/templates/terminal/command_list.html:65
#: terminal/templates/terminal/session_list.html:27
#: terminal/templates/terminal/session_list.html:27
#: terminal/templates/terminal/session_list.html:71 users/forms.py:319
#: terminal/templates/terminal/session_list.html:71 users/forms.py:319
#: users/models/user.py:13
6 users/models/user.py:152
users/models/user.py:509
#: users/models/user.py:13
2 users/models/user.py:148
users/models/user.py:509
#: users/serializers/group.py:21
#: users/serializers/group.py:21
#: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:36 users/views/user.py:250
#: users/templates/users/user_group_list.html:36 users/views/user.py:250
...
@@ -1187,7 +1187,7 @@ msgstr "手动登录"
...
@@ -1187,7 +1187,7 @@ msgstr "手动登录"
#: assets/views/label.py:27 assets/views/label.py:45 assets/views/label.py:73
#: assets/views/label.py:27 assets/views/label.py:45 assets/views/label.py:73
#: assets/views/system_user.py:29 assets/views/system_user.py:46
#: assets/views/system_user.py:29 assets/views/system_user.py:46
#: assets/views/system_user.py:63 assets/views/system_user.py:79
#: assets/views/system_user.py:63 assets/views/system_user.py:79
#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:7
0
#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:7
1
msgid "Assets"
msgid "Assets"
msgstr "资产管理"
msgstr "资产管理"
...
@@ -1236,17 +1236,17 @@ msgstr "系统用户"
...
@@ -1236,17 +1236,17 @@ msgstr "系统用户"
msgid "%(value)s is not an even number"
msgid "%(value)s is not an even number"
msgstr "%(value)s is not an even number"
msgstr "%(value)s is not an even number"
#: assets/models/utils.py:43 assets/tasks/const.py:8
4
#: assets/models/utils.py:43 assets/tasks/const.py:8
7
msgid "Unreachable"
msgid "Unreachable"
msgstr "不可达"
msgstr "不可达"
#: assets/models/utils.py:44 assets/tasks/const.py:8
5
#: assets/models/utils.py:44 assets/tasks/const.py:8
8
#: assets/templates/assets/asset_list.html:99
#: assets/templates/assets/asset_list.html:99
msgid "Reachable"
msgid "Reachable"
msgstr "可连接"
msgstr "可连接"
#: assets/models/utils.py:45 assets/tasks/const.py:8
6
#: assets/models/utils.py:45 assets/tasks/const.py:8
9 audits/utils.py:29
#:
authentication/utils.py:16
xpack/plugins/license/models.py:78
#: xpack/plugins/license/models.py:78
msgid "Unknown"
msgid "Unknown"
msgstr "未知"
msgstr "未知"
...
@@ -1332,7 +1332,7 @@ msgstr "测试资产可连接性: {}"
...
@@ -1332,7 +1332,7 @@ msgstr "测试资产可连接性: {}"
#: assets/tasks/asset_user_connectivity.py:27
#: assets/tasks/asset_user_connectivity.py:27
#: assets/tasks/push_system_user.py:130
#: assets/tasks/push_system_user.py:130
#: xpack/plugins/change_auth_plan/models.py:52
1
#: xpack/plugins/change_auth_plan/models.py:52
8
msgid "The asset {} system platform {} does not support run Ansible tasks"
msgid "The asset {} system platform {} does not support run Ansible tasks"
msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务"
msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务"
...
@@ -1470,8 +1470,8 @@ msgstr "请输入密码"
...
@@ -1470,8 +1470,8 @@ msgstr "请输入密码"
#: assets/templates/assets/_asset_user_auth_update_modal.html:68
#: assets/templates/assets/_asset_user_auth_update_modal.html:68
#: assets/templates/assets/asset_detail.html:302
#: assets/templates/assets/asset_detail.html:302
#: users/templates/users/user_detail.html:36
4
#: users/templates/users/user_detail.html:36
6
#: users/templates/users/user_detail.html:39
1
#: users/templates/users/user_detail.html:39
3
#: xpack/plugins/interface/views.py:35
#: xpack/plugins/interface/views.py:35
msgid "Update successfully!"
msgid "Update successfully!"
msgstr "更新成功"
msgstr "更新成功"
...
@@ -1481,7 +1481,7 @@ msgid "Asset user auth"
...
@@ -1481,7 +1481,7 @@ msgid "Asset user auth"
msgstr "资产用户信息"
msgstr "资产用户信息"
#: assets/templates/assets/_asset_user_auth_view_modal.html:54
#: assets/templates/assets/_asset_user_auth_view_modal.html:54
#: authentication/templates/authentication/login_wait_confirm.html:11
7
#: authentication/templates/authentication/login_wait_confirm.html:11
4
msgid "Copy success"
msgid "Copy success"
msgstr "复制成功"
msgstr "复制成功"
...
@@ -1669,10 +1669,10 @@ msgstr "选择节点"
...
@@ -1669,10 +1669,10 @@ msgstr "选择节点"
#: settings/templates/settings/terminal_setting.html:168
#: settings/templates/settings/terminal_setting.html:168
#: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:112
#: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:112
#: users/templates/users/user_detail.html:271
#: users/templates/users/user_detail.html:271
#: users/templates/users/user_detail.html:44
5
#: users/templates/users/user_detail.html:44
7
#: users/templates/users/user_detail.html:47
1
#: users/templates/users/user_detail.html:47
3
#: users/templates/users/user_detail.html:49
4
#: users/templates/users/user_detail.html:49
6
#: users/templates/users/user_detail.html:5
39
#: users/templates/users/user_detail.html:5
41
#: users/templates/users/user_group_create_update.html:32
#: users/templates/users/user_group_create_update.html:32
#: users/templates/users/user_group_list.html:120
#: users/templates/users/user_group_list.html:120
#: users/templates/users/user_list.html:256
#: users/templates/users/user_list.html:256
...
@@ -1750,7 +1750,7 @@ msgstr "资产用户"
...
@@ -1750,7 +1750,7 @@ msgstr "资产用户"
#: assets/templates/assets/asset_asset_user_list.html:47
#: assets/templates/assets/asset_asset_user_list.html:47
#: assets/templates/assets/asset_detail.html:142
#: assets/templates/assets/asset_detail.html:142
#: terminal/templates/terminal/session_detail.html:85
#: terminal/templates/terminal/session_detail.html:85
#: users/templates/users/user_detail.html:14
0
#: users/templates/users/user_detail.html:14
1
#: users/templates/users/user_profile.html:150
#: users/templates/users/user_profile.html:150
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:128
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:128
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:132
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:132
...
@@ -1777,7 +1777,7 @@ msgid "Disk"
...
@@ -1777,7 +1777,7 @@ msgid "Disk"
msgstr "硬盘"
msgstr "硬盘"
#: assets/templates/assets/asset_detail.html:126
#: assets/templates/assets/asset_detail.html:126
#: users/templates/users/user_detail.html:11
5
#: users/templates/users/user_detail.html:11
6
#: users/templates/users/user_profile.html:106
#: users/templates/users/user_profile.html:106
msgid "Date joined"
msgid "Date joined"
msgstr "创建日期"
msgstr "创建日期"
...
@@ -1791,7 +1791,7 @@ msgstr "创建日期"
...
@@ -1791,7 +1791,7 @@ msgstr "创建日期"
#: perms/templates/perms/remote_app_permission_detail.html:112
#: perms/templates/perms/remote_app_permission_detail.html:112
#: terminal/templates/terminal/terminal_list.html:34
#: terminal/templates/terminal/terminal_list.html:34
#: users/templates/users/_select_user_modal.html:18
#: users/templates/users/_select_user_modal.html:18
#: users/templates/users/user_detail.html:14
6
#: users/templates/users/user_detail.html:14
7
#: users/templates/users/user_profile.html:63
#: users/templates/users/user_profile.html:63
msgid "Active"
msgid "Active"
msgstr "激活中"
msgstr "激活中"
...
@@ -1872,9 +1872,9 @@ msgstr "显示所有子节点资产"
...
@@ -1872,9 +1872,9 @@ msgstr "显示所有子节点资产"
#: assets/templates/assets/asset_list.html:417
#: assets/templates/assets/asset_list.html:417
#: assets/templates/assets/system_user_list.html:129
#: assets/templates/assets/system_user_list.html:129
#: users/templates/users/user_detail.html:4
39
#: users/templates/users/user_detail.html:4
41
#: users/templates/users/user_detail.html:46
5
#: users/templates/users/user_detail.html:46
7
#: users/templates/users/user_detail.html:53
3
#: users/templates/users/user_detail.html:53
5
#: users/templates/users/user_group_list.html:114
#: users/templates/users/user_group_list.html:114
#: users/templates/users/user_list.html:250
#: users/templates/users/user_list.html:250
#: xpack/plugins/interface/templates/interface/interface.html:97
#: xpack/plugins/interface/templates/interface/interface.html:97
...
@@ -1888,9 +1888,9 @@ msgstr "删除选择资产"
...
@@ -1888,9 +1888,9 @@ msgstr "删除选择资产"
#: assets/templates/assets/asset_list.html:421
#: assets/templates/assets/asset_list.html:421
#: assets/templates/assets/system_user_list.html:133
#: assets/templates/assets/system_user_list.html:133
#: settings/templates/settings/terminal_setting.html:166
#: settings/templates/settings/terminal_setting.html:166
#: users/templates/users/user_detail.html:44
3
#: users/templates/users/user_detail.html:44
5
#: users/templates/users/user_detail.html:4
69
#: users/templates/users/user_detail.html:4
71
#: users/templates/users/user_detail.html:53
7
#: users/templates/users/user_detail.html:53
9
#: users/templates/users/user_group_list.html:118
#: users/templates/users/user_group_list.html:118
#: users/templates/users/user_list.html:254
#: users/templates/users/user_list.html:254
#: xpack/plugins/interface/templates/interface/interface.html:101
#: xpack/plugins/interface/templates/interface/interface.html:101
...
@@ -2214,11 +2214,11 @@ msgstr "操作"
...
@@ -2214,11 +2214,11 @@ msgstr "操作"
msgid "Filename"
msgid "Filename"
msgstr "文件名"
msgstr "文件名"
#: audits/models.py:24 audits/models.py:7
7
#: audits/models.py:24 audits/models.py:7
8
#: audits/templates/audits/ftp_log_list.html:79
#: audits/templates/audits/ftp_log_list.html:79
#: ops/templates/ops/command_execution_list.html:68
#: ops/templates/ops/command_execution_list.html:68
#: ops/templates/ops/task_list.html:15
#: ops/templates/ops/task_list.html:15
#: users/templates/users/user_detail.html:51
5
#: users/templates/users/user_detail.html:51
7
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:14
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:14
#: xpack/plugins/cloud/api.py:61
#: xpack/plugins/cloud/api.py:61
msgid "Success"
msgid "Success"
...
@@ -2243,12 +2243,12 @@ msgstr "资源"
...
@@ -2243,12 +2243,12 @@ msgstr "资源"
msgid "Change by"
msgid "Change by"
msgstr "修改者"
msgstr "修改者"
#: audits/models.py:71 users/templates/users/user_detail.html:9
8
#: audits/models.py:71 users/templates/users/user_detail.html:9
9
msgid "Disabled"
msgid "Disabled"
msgstr "禁用"
msgstr "禁用"
#: audits/models.py:72 settings/models.py:33
#: audits/models.py:72 settings/models.py:33
#: users/templates/users/user_detail.html:9
6
#: users/templates/users/user_detail.html:9
7
msgid "Enabled"
msgid "Enabled"
msgstr "启用"
msgstr "启用"
...
@@ -2256,43 +2256,43 @@ msgstr "启用"
...
@@ -2256,43 +2256,43 @@ msgstr "启用"
msgid "-"
msgid "-"
msgstr ""
msgstr ""
#: audits/models.py:7
8
xpack/plugins/cloud/models.py:264
#: audits/models.py:7
9
xpack/plugins/cloud/models.py:264
#: xpack/plugins/cloud/models.py:287
#: xpack/plugins/cloud/models.py:287
msgid "Failed"
msgid "Failed"
msgstr "失败"
msgstr "失败"
#: audits/models.py:8
2
#: audits/models.py:8
3
msgid "Login type"
msgid "Login type"
msgstr "登录方式"
msgstr "登录方式"
#: audits/models.py:8
3
#: audits/models.py:8
4
msgid "Login ip"
msgid "Login ip"
msgstr "登录IP"
msgstr "登录IP"
#: audits/models.py:8
4
#: audits/models.py:8
5
msgid "Login city"
msgid "Login city"
msgstr "登录城市"
msgstr "登录城市"
#: audits/models.py:8
5
#: audits/models.py:8
6
msgid "User agent"
msgid "User agent"
msgstr "Agent"
msgstr "Agent"
#: audits/models.py:8
6
audits/templates/audits/login_log_list.html:62
#: audits/models.py:8
7
audits/templates/audits/login_log_list.html:62
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
#: users/forms.py:174 users/models/user.py:404
#: users/forms.py:174 users/models/user.py:404
#: users/templates/users/first_login.html:45
#: users/templates/users/first_login.html:45
msgid "MFA"
msgid "MFA"
msgstr "MFA"
msgstr "MFA"
#: audits/models.py:8
7
audits/templates/audits/login_log_list.html:63
#: audits/models.py:8
8
audits/templates/audits/login_log_list.html:63
#: xpack/plugins/change_auth_plan/models.py:4
16
#: xpack/plugins/change_auth_plan/models.py:4
23
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15
#: xpack/plugins/cloud/models.py:278
#: xpack/plugins/cloud/models.py:278
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69
msgid "Reason"
msgid "Reason"
msgstr "原因"
msgstr "原因"
#: audits/models.py:8
8
audits/templates/audits/login_log_list.html:64
#: audits/models.py:8
9
audits/templates/audits/login_log_list.html:64
#: orders/templates/orders/login_confirm_order_detail.html:35
#: orders/templates/orders/login_confirm_order_detail.html:35
#: orders/templates/orders/login_confirm_order_list.html:17
#: orders/templates/orders/login_confirm_order_list.html:17
#: orders/templates/orders/login_confirm_order_list.html:91
#: orders/templates/orders/login_confirm_order_list.html:91
...
@@ -2302,7 +2302,7 @@ msgstr "原因"
...
@@ -2302,7 +2302,7 @@ msgstr "原因"
msgid "Status"
msgid "Status"
msgstr "状态"
msgstr "状态"
#: audits/models.py:
89
#: audits/models.py:
90
msgid "Date login"
msgid "Date login"
msgstr "登录日期"
msgstr "登录日期"
...
@@ -2314,8 +2314,8 @@ msgstr "登录日期"
...
@@ -2314,8 +2314,8 @@ msgstr "登录日期"
#: perms/templates/perms/asset_permission_detail.html:86
#: perms/templates/perms/asset_permission_detail.html:86
#: perms/templates/perms/remote_app_permission_detail.html:78
#: perms/templates/perms/remote_app_permission_detail.html:78
#: terminal/models.py:167 terminal/templates/terminal/session_list.html:34
#: terminal/models.py:167 terminal/templates/terminal/session_list.html:34
#: xpack/plugins/change_auth_plan/models.py:2
49
#: xpack/plugins/change_auth_plan/models.py:2
50
#: xpack/plugins/change_auth_plan/models.py:4
19
#: xpack/plugins/change_auth_plan/models.py:4
26
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17
#: xpack/plugins/gathered_user/models.py:143
#: xpack/plugins/gathered_user/models.py:143
...
@@ -2391,9 +2391,7 @@ msgstr "登录日志"
...
@@ -2391,9 +2391,7 @@ msgstr "登录日志"
msgid "Command execution log"
msgid "Command execution log"
msgstr "命令执行"
msgstr "命令执行"
#: authentication/api/auth.py:58 authentication/api/token.py:45
#: authentication/api/auth.py:58
#: authentication/templates/authentication/login.html:52
#: authentication/templates/authentication/xpack_login.html:77
msgid "Log in frequently and try again later"
msgid "Log in frequently and try again later"
msgstr "登录频繁, 稍后重试"
msgstr "登录频繁, 稍后重试"
...
@@ -2409,18 +2407,6 @@ msgstr "请先进行用户名和密码验证"
...
@@ -2409,18 +2407,6 @@ msgstr "请先进行用户名和密码验证"
msgid "MFA certification failed"
msgid "MFA certification failed"
msgstr "MFA认证失败"
msgstr "MFA认证失败"
#: authentication/api/auth.py:222
msgid "No order found or order expired"
msgstr "没有找到工单,或者已过期"
#: authentication/api/auth.py:228
msgid "Order was rejected by {}"
msgstr "工单被拒绝 {}"
#: authentication/api/token.py:81
msgid "MFA required"
msgstr ""
#: authentication/backends/api.py:53
#: authentication/backends/api.py:53
msgid "Invalid signature header. No credentials provided."
msgid "Invalid signature header. No credentials provided."
msgstr ""
msgstr ""
...
@@ -2472,49 +2458,75 @@ msgstr ""
...
@@ -2472,49 +2458,75 @@ msgstr ""
msgid "Invalid token or cache refreshed."
msgid "Invalid token or cache refreshed."
msgstr ""
msgstr ""
#: authentication/
const.py:6
#: authentication/
errors.py:20
msgid "Username/password check failed"
msgid "Username/password check failed"
msgstr "用户名/密码 校验失败"
msgstr "用户名/密码 校验失败"
#: authentication/
const.py:7
#: authentication/
errors.py:21
msgid "MFA authentication failed"
msgid "MFA authentication failed"
msgstr "MFA 认证失败"
msgstr "MFA 认证失败"
#: authentication/
const.py:8
#: authentication/
errors.py:22
msgid "Username does not exist"
msgid "Username does not exist"
msgstr "用户名不存在"
msgstr "用户名不存在"
#: authentication/
const.py:9
#: authentication/
errors.py:23
msgid "Password expired"
msgid "Password expired"
msgstr "密码过期"
msgstr "密码
已
过期"
#: authentication/
const.py:10
#: authentication/
errors.py:24
msgid "Disabled or expired"
msgid "Disabled or expired"
msgstr "禁用或失效"
msgstr "禁用或失效"
#: authentication/forms.py:21
#: authentication/errors.py:25
msgid ""
"The username or password you entered is incorrect, please enter it again."
msgstr "您输入的用户名或密码不正确,请重新输入。"
#: authentication/forms.py:24
msgid "This account is inactive."
msgid "This account is inactive."
msgstr "此账户无效"
msgstr "此账户已禁用"
#: authentication/errors.py:28
msgid "No session found, check your cookie"
msgstr "会话已变更,刷新页面"
#: authentication/
forms.py:26
#: authentication/
errors.py:30
#, python-brace-format
#, python-brace-format
msgid ""
msgid ""
"The username or password you entered is incorrect, please enter it again. "
"You can also try {times_try} times (The account will be temporarily locked "
"You can also try {times_try} times (The account will be temporarily locked "
"for {block_time} minutes)"
"for {block_time} minutes)"
msgstr "您还可以尝试 {times_try} 次(账号将被临时锁定 {block_time} 分钟)"
msgstr ""
"您输入的用户名或密码不正确,请重新输入。 您还可以尝试 {times_try} 次(账号将"
"被临时 锁定 {block_time} 分钟)"
#: authentication/
forms.py:30
#: authentication/
errors.py:36
msgid ""
msgid ""
"The account has been locked (please contact admin to unlock it or try again "
"The account has been locked (please contact admin to unlock it or try again "
"after {} minutes)"
"after {} minutes)"
msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)"
msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)"
#: authentication/forms.py:66 users/forms.py:21
#: authentication/errors.py:39 users/views/user.py:393 users/views/user.py:418
msgid "MFA code invalid, or ntp sync server time"
msgstr "MFA验证码不正确,或者服务器端时间不对"
#: authentication/errors.py:41
msgid "MFA required"
msgstr ""
#: authentication/errors.py:42
msgid "Login confirm required"
msgstr "需要登录复核"
#: authentication/errors.py:43
msgid "Wait login confirm order for accept"
msgstr "等待登录复核处理"
#: authentication/errors.py:44
msgid "Login confirm order was rejected"
msgstr "登录已被拒绝"
#: authentication/errors.py:45
msgid "Order not found"
msgstr "没有发现工单"
#: authentication/forms.py:32 users/forms.py:21
msgid "MFA code"
msgid "MFA code"
msgstr "MFA 验证码"
msgstr "MFA 验证码"
...
@@ -2522,18 +2534,10 @@ msgstr "MFA 验证码"
...
@@ -2522,18 +2534,10 @@ msgstr "MFA 验证码"
msgid "Private Token"
msgid "Private Token"
msgstr "ssh密钥"
msgstr "ssh密钥"
#: authentication/models.py:43
msgid "login_confirm_setting"
msgstr "登录复核设置"
#: authentication/models.py:44 users/templates/users/user_detail.html:265
#: authentication/models.py:44 users/templates/users/user_detail.html:265
msgid "Reviewers"
msgid "Reviewers"
msgstr "审批人"
msgstr "审批人"
#: authentication/models.py:44
msgid "review_login_confirm_settings"
msgstr ""
#: authentication/models.py:53
#: authentication/models.py:53
msgid "User login confirm: {}"
msgid "User login confirm: {}"
msgstr "用户登录复核: {}"
msgstr "用户登录复核: {}"
...
@@ -2572,14 +2576,14 @@ msgid "Show"
...
@@ -2572,14 +2576,14 @@ msgid "Show"
msgstr "显示"
msgstr "显示"
#: authentication/templates/authentication/_access_key_modal.html:66
#: authentication/templates/authentication/_access_key_modal.html:66
#: users/models/user.py:33
9
users/templates/users/user_profile.html:94
#: users/models/user.py:33
5
users/templates/users/user_profile.html:94
#: users/templates/users/user_profile.html:163
#: users/templates/users/user_profile.html:163
#: users/templates/users/user_profile.html:166
#: users/templates/users/user_profile.html:166
msgid "Disable"
msgid "Disable"
msgstr "禁用"
msgstr "禁用"
#: authentication/templates/authentication/_access_key_modal.html:67
#: authentication/templates/authentication/_access_key_modal.html:67
#: users/models/user.py:3
40
users/templates/users/user_profile.html:92
#: users/models/user.py:3
36
users/templates/users/user_profile.html:92
#: users/templates/users/user_profile.html:170
#: users/templates/users/user_profile.html:170
msgid "Enable"
msgid "Enable"
msgstr "启用"
msgstr "启用"
...
@@ -2640,39 +2644,34 @@ msgid "Changes the world, starting with a little bit."
...
@@ -2640,39 +2644,34 @@ msgid "Changes the world, starting with a little bit."
msgstr "改变世界,从一点点开始。"
msgstr "改变世界,从一点点开始。"
#: authentication/templates/authentication/login.html:46
#: authentication/templates/authentication/login.html:46
#: authentication/templates/authentication/login.html:
73
#: authentication/templates/authentication/login.html:
68
#: authentication/templates/authentication/xpack_login.html:
101
#: authentication/templates/authentication/xpack_login.html:
96
#: templates/_header_bar.html:83
#: templates/_header_bar.html:83
msgid "Login"
msgid "Login"
msgstr "登录"
msgstr "登录"
#: authentication/templates/authentication/login.html:54
#: authentication/templates/authentication/login.html:52
#: authentication/templates/authentication/xpack_login.html:80
#: authentication/templates/authentication/xpack_login.html:78
msgid "The user password has expired"
msgstr "用户密码已过期"
#: authentication/templates/authentication/login.html:57
#: authentication/templates/authentication/xpack_login.html:83
msgid "Captcha invalid"
msgid "Captcha invalid"
msgstr "验证码错误"
msgstr "验证码错误"
#: authentication/templates/authentication/login.html:
84
#: authentication/templates/authentication/login.html:
79
#: authentication/templates/authentication/xpack_login.html:10
5
#: authentication/templates/authentication/xpack_login.html:10
0
#: users/templates/users/forgot_password.html:10
#: users/templates/users/forgot_password.html:10
#: users/templates/users/forgot_password.html:25
#: users/templates/users/forgot_password.html:25
msgid "Forgot password"
msgid "Forgot password"
msgstr "忘记密码"
msgstr "忘记密码"
#: authentication/templates/authentication/login.html:
91
#: authentication/templates/authentication/login.html:
86
msgid "More login options"
msgid "More login options"
msgstr "更多登录方式"
msgstr "更多登录方式"
#: authentication/templates/authentication/login.html:9
5
#: authentication/templates/authentication/login.html:9
0
msgid "Keycloak"
msgid "Keycloak"
msgstr ""
msgstr ""
#: authentication/templates/authentication/login_otp.html:46
#: authentication/templates/authentication/login_otp.html:46
#: users/templates/users/user_detail.html:9
1
#: users/templates/users/user_detail.html:9
2
#: users/templates/users/user_profile.html:87
#: users/templates/users/user_profile.html:87
msgid "MFA certification"
msgid "MFA certification"
msgstr "MFA认证"
msgstr "MFA认证"
...
@@ -2721,16 +2720,11 @@ msgstr "返回"
...
@@ -2721,16 +2720,11 @@ msgstr "返回"
msgid "Welcome back, please enter username and password to login"
msgid "Welcome back, please enter username and password to login"
msgstr "欢迎回来,请输入用户名和密码登录"
msgstr "欢迎回来,请输入用户名和密码登录"
#: authentication/views/login.py:8
2
#: authentication/views/login.py:8
0
msgid "Please enable cookies and try again."
msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie"
msgstr "设置你的浏览器支持cookie"
#: authentication/views/login.py:156 users/views/user.py:393
#: authentication/views/login.py:192
#: users/views/user.py:418
msgid "MFA code invalid, or ntp sync server time"
msgstr "MFA验证码不正确,或者服务器端时间不对"
#: authentication/views/login.py:226
msgid ""
msgid ""
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
" Don't close this page"
" Don't close this page"
...
@@ -2738,15 +2732,15 @@ msgstr ""
...
@@ -2738,15 +2732,15 @@ msgstr ""
"等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n"
"等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n"
" 不要关闭本页面"
" 不要关闭本页面"
#: authentication/views/login.py:
231
#: authentication/views/login.py:
197
msgid "No order found"
msgid "No order found"
msgstr "没有发现工单"
msgstr "没有发现工单"
#: authentication/views/login.py:2
54
#: authentication/views/login.py:2
20
msgid "Logout success"
msgid "Logout success"
msgstr "退出登录成功"
msgstr "退出登录成功"
#: authentication/views/login.py:2
55
#: authentication/views/login.py:2
21
msgid "Logout success, return login page"
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
msgstr "退出登录成功,返回到登录页面"
...
@@ -2930,8 +2924,8 @@ msgstr "完成时间"
...
@@ -2930,8 +2924,8 @@ msgstr "完成时间"
#: ops/models/adhoc.py:357 ops/templates/ops/adhoc_history.html:57
#: ops/models/adhoc.py:357 ops/templates/ops/adhoc_history.html:57
#: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:17
#: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:17
#: xpack/plugins/change_auth_plan/models.py:25
2
#: xpack/plugins/change_auth_plan/models.py:25
3
#: xpack/plugins/change_auth_plan/models.py:42
2
#: xpack/plugins/change_auth_plan/models.py:42
9
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16
#: xpack/plugins/gathered_user/models.py:146
#: xpack/plugins/gathered_user/models.py:146
...
@@ -3305,11 +3299,11 @@ msgstr ""
...
@@ -3305,11 +3299,11 @@ msgstr ""
" </div>\n"
" </div>\n"
" "
" "
#: orders/utils.py:
52
#: orders/utils.py:
48
msgid "Order has been reply"
msgid "Order has been reply"
msgstr "工单已被回复"
msgstr "工单已被回复"
#: orders/utils.py:
53
#: orders/utils.py:
49
#, python-brace-format
#, python-brace-format
msgid ""
msgid ""
"\n"
"\n"
...
@@ -3418,7 +3412,7 @@ msgstr "资产授权"
...
@@ -3418,7 +3412,7 @@ msgstr "资产授权"
#: perms/models/base.py:53
#: perms/models/base.py:53
#: perms/templates/perms/asset_permission_detail.html:90
#: perms/templates/perms/asset_permission_detail.html:90
#: perms/templates/perms/remote_app_permission_detail.html:82
#: perms/templates/perms/remote_app_permission_detail.html:82
#: users/models/user.py:420 users/templates/users/user_detail.html:10
7
#: users/models/user.py:420 users/templates/users/user_detail.html:10
8
#: users/templates/users/user_profile.html:120
#: users/templates/users/user_profile.html:120
msgid "Date expired"
msgid "Date expired"
msgstr "失效日期"
msgstr "失效日期"
...
@@ -3982,7 +3976,7 @@ msgid "Please submit the LDAP configuration before import"
...
@@ -3982,7 +3976,7 @@ msgid "Please submit the LDAP configuration before import"
msgstr "请先提交LDAP配置再进行导入"
msgstr "请先提交LDAP配置再进行导入"
#: settings/templates/settings/_ldap_list_users_modal.html:32
#: settings/templates/settings/_ldap_list_users_modal.html:32
#: users/models/user.py:384 users/templates/users/user_detail.html:7
1
#: users/models/user.py:384 users/templates/users/user_detail.html:7
2
#: users/templates/users/user_profile.html:59
#: users/templates/users/user_profile.html:59
msgid "Email"
msgid "Email"
msgstr "邮件"
msgstr "邮件"
...
@@ -4775,7 +4769,7 @@ msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
...
@@ -4775,7 +4769,7 @@ msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
#: users/forms.py:32 users/models/user.py:392
#: users/forms.py:32 users/models/user.py:392
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:8
7
#: users/templates/users/user_detail.html:8
8
#: users/templates/users/user_list.html:37
#: users/templates/users/user_list.html:37
#: users/templates/users/user_profile.html:55
#: users/templates/users/user_profile.html:55
msgid "Role"
msgid "Role"
...
@@ -4818,7 +4812,7 @@ msgstr "生成重置密码链接,通过邮件发送给用户"
...
@@ -4818,7 +4812,7 @@ msgstr "生成重置密码链接,通过邮件发送给用户"
msgid "Set password"
msgid "Set password"
msgstr "设置密码"
msgstr "设置密码"
#: users/forms.py:132 xpack/plugins/change_auth_plan/models.py:8
8
#: users/forms.py:132 xpack/plugins/change_auth_plan/models.py:8
9
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57
...
@@ -4892,28 +4886,28 @@ msgstr "选择用户"
...
@@ -4892,28 +4886,28 @@ msgstr "选择用户"
msgid "User auth from {}, go there change password"
msgid "User auth from {}, go there change password"
msgstr "用户认证源来自 {}, 请去相应系统修改密码"
msgstr "用户认证源来自 {}, 请去相应系统修改密码"
#: users/models/user.py:13
5
users/models/user.py:517
#: users/models/user.py:13
1
users/models/user.py:517
msgid "Administrator"
msgid "Administrator"
msgstr "管理员"
msgstr "管理员"
#: users/models/user.py:13
7
#: users/models/user.py:13
3
msgid "Application"
msgid "Application"
msgstr "应用程序"
msgstr "应用程序"
#: users/models/user.py:13
8
xpack/plugins/orgs/forms.py:30
#: users/models/user.py:13
4
xpack/plugins/orgs/forms.py:30
#: xpack/plugins/orgs/templates/orgs/org_list.html:14
#: xpack/plugins/orgs/templates/orgs/org_list.html:14
msgid "Auditor"
msgid "Auditor"
msgstr "审计员"
msgstr "审计员"
#: users/models/user.py:14
8
#: users/models/user.py:14
4
msgid "Org admin"
msgid "Org admin"
msgstr "组织管理员"
msgstr "组织管理员"
#: users/models/user.py:1
50
#: users/models/user.py:1
46
msgid "Org auditor"
msgid "Org auditor"
msgstr "组织审计员"
msgstr "组织审计员"
#: users/models/user.py:3
41
users/templates/users/user_profile.html:90
#: users/models/user.py:3
37
users/templates/users/user_profile.html:90
msgid "Force enable"
msgid "Force enable"
msgstr "强制启用"
msgstr "强制启用"
...
@@ -4921,11 +4915,11 @@ msgstr "强制启用"
...
@@ -4921,11 +4915,11 @@ msgstr "强制启用"
msgid "Avatar"
msgid "Avatar"
msgstr "头像"
msgstr "头像"
#: users/models/user.py:398 users/templates/users/user_detail.html:8
2
#: users/models/user.py:398 users/templates/users/user_detail.html:8
3
msgid "Wechat"
msgid "Wechat"
msgstr "微信"
msgstr "微信"
#: users/models/user.py:427 users/templates/users/user_detail.html:10
3
#: users/models/user.py:427 users/templates/users/user_detail.html:10
4
#: users/templates/users/user_list.html:39
#: users/templates/users/user_list.html:39
#: users/templates/users/user_profile.html:102
#: users/templates/users/user_profile.html:102
msgid "Source"
msgid "Source"
...
@@ -5107,7 +5101,7 @@ msgid "Always young, always with tears in my eyes. Stay foolish Stay hungry"
...
@@ -5107,7 +5101,7 @@ msgid "Always young, always with tears in my eyes. Stay foolish Stay hungry"
msgstr "永远年轻,永远热泪盈眶 stay foolish stay hungry"
msgstr "永远年轻,永远热泪盈眶 stay foolish stay hungry"
#: users/templates/users/reset_password.html:46
#: users/templates/users/reset_password.html:46
#: users/templates/users/user_detail.html:43
0
users/utils.py:83
#: users/templates/users/user_detail.html:43
2
users/utils.py:83
msgid "Reset password"
msgid "Reset password"
msgstr "重置密码"
msgstr "重置密码"
...
@@ -5176,102 +5170,102 @@ msgstr "很强"
...
@@ -5176,102 +5170,102 @@ msgstr "很强"
msgid "Create user"
msgid "Create user"
msgstr "创建用户"
msgstr "创建用户"
#: users/templates/users/user_detail.html:
19
#: users/templates/users/user_detail.html:
20
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:190
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:190
msgid "User detail"
msgid "User detail"
msgstr "用户详情"
msgstr "用户详情"
#: users/templates/users/user_detail.html:2
2
#: users/templates/users/user_detail.html:2
3
#: users/templates/users/user_granted_asset.html:21
#: users/templates/users/user_granted_asset.html:21
#: users/templates/users/user_group_detail.html:25
#: users/templates/users/user_group_detail.html:25
#: users/templates/users/user_group_granted_asset.html:21
#: users/templates/users/user_group_granted_asset.html:21
msgid "Asset granted"
msgid "Asset granted"
msgstr "授权的资产"
msgstr "授权的资产"
#: users/templates/users/user_detail.html:9
4
#: users/templates/users/user_detail.html:9
5
msgid "Force enabled"
msgid "Force enabled"
msgstr "强制启用"
msgstr "强制启用"
#: users/templates/users/user_detail.html:1
19
#: users/templates/users/user_detail.html:1
20
#: users/templates/users/user_profile.html:110
#: users/templates/users/user_profile.html:110
msgid "Last login"
msgid "Last login"
msgstr "最后登录"
msgstr "最后登录"
#: users/templates/users/user_detail.html:12
4
#: users/templates/users/user_detail.html:12
5
#: users/templates/users/user_profile.html:115
#: users/templates/users/user_profile.html:115
msgid "Last password updated"
msgid "Last password updated"
msgstr "最后更新密码"
msgstr "最后更新密码"
#: users/templates/users/user_detail.html:16
0
#: users/templates/users/user_detail.html:16
1
msgid "Force enabled MFA"
msgid "Force enabled MFA"
msgstr "强制启用MFA"
msgstr "强制启用MFA"
#: users/templates/users/user_detail.html:17
5
#: users/templates/users/user_detail.html:17
6
msgid "Reset MFA"
msgid "Reset MFA"
msgstr "重置MFA"
msgstr "重置MFA"
#: users/templates/users/user_detail.html:18
4
#: users/templates/users/user_detail.html:18
5
msgid "Send reset password mail"
msgid "Send reset password mail"
msgstr "发送重置密码邮件"
msgstr "发送重置密码邮件"
#: users/templates/users/user_detail.html:18
7
#: users/templates/users/user_detail.html:18
8
#: users/templates/users/user_detail.html:19
7
#: users/templates/users/user_detail.html:19
8
msgid "Send"
msgid "Send"
msgstr "发送"
msgstr "发送"
#: users/templates/users/user_detail.html:19
4
#: users/templates/users/user_detail.html:19
5
msgid "Send reset ssh key mail"
msgid "Send reset ssh key mail"
msgstr "发送重置密钥邮件"
msgstr "发送重置密钥邮件"
#: users/templates/users/user_detail.html:20
3
#: users/templates/users/user_detail.html:20
4
#: users/templates/users/user_detail.html:5
18
#: users/templates/users/user_detail.html:5
20
msgid "Unblock user"
msgid "Unblock user"
msgstr "解除登录限制"
msgstr "解除登录限制"
#: users/templates/users/user_detail.html:20
6
#: users/templates/users/user_detail.html:20
7
msgid "Unblock"
msgid "Unblock"
msgstr "解除"
msgstr "解除"
#: users/templates/users/user_detail.html:37
3
#: users/templates/users/user_detail.html:37
5
msgid "Goto profile page enable MFA"
msgid "Goto profile page enable MFA"
msgstr "请去个人信息页面启用自己的MFA"
msgstr "请去个人信息页面启用自己的MFA"
#: users/templates/users/user_detail.html:4
29
#: users/templates/users/user_detail.html:4
31
msgid "An e-mail has been sent to the user`s mailbox."
msgid "An e-mail has been sent to the user`s mailbox."
msgstr "已发送邮件到用户邮箱"
msgstr "已发送邮件到用户邮箱"
#: users/templates/users/user_detail.html:44
0
#: users/templates/users/user_detail.html:44
2
msgid "This will reset the user password and send a reset mail"
msgid "This will reset the user password and send a reset mail"
msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱"
msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱"
#: users/templates/users/user_detail.html:45
5
#: users/templates/users/user_detail.html:45
7
msgid ""
msgid ""
"The reset-ssh-public-key E-mail has been sent successfully. Please inform "
"The reset-ssh-public-key E-mail has been sent successfully. Please inform "
"the user to update his new ssh public key."
"the user to update his new ssh public key."
msgstr "重设密钥邮件将会发送到用户邮箱"
msgstr "重设密钥邮件将会发送到用户邮箱"
#: users/templates/users/user_detail.html:45
6
#: users/templates/users/user_detail.html:45
8
msgid "Reset SSH public key"
msgid "Reset SSH public key"
msgstr "重置SSH密钥"
msgstr "重置SSH密钥"
#: users/templates/users/user_detail.html:46
6
#: users/templates/users/user_detail.html:46
8
msgid "This will reset the user public key and send a reset mail"
msgid "This will reset the user public key and send a reset mail"
msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱"
msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱"
#: users/templates/users/user_detail.html:48
4
#: users/templates/users/user_detail.html:48
6
msgid "Successfully updated the SSH public key."
msgid "Successfully updated the SSH public key."
msgstr "更新ssh密钥成功"
msgstr "更新ssh密钥成功"
#: users/templates/users/user_detail.html:48
5
#: users/templates/users/user_detail.html:48
7
#: users/templates/users/user_detail.html:4
89
#: users/templates/users/user_detail.html:4
91
msgid "User SSH public key update"
msgid "User SSH public key update"
msgstr "ssh密钥"
msgstr "ssh密钥"
#: users/templates/users/user_detail.html:53
4
#: users/templates/users/user_detail.html:53
6
msgid "After unlocking the user, the user can log in normally."
msgid "After unlocking the user, the user can log in normally."
msgstr "解除用户登录限制后,此用户即可正常登录"
msgstr "解除用户登录限制后,此用户即可正常登录"
#: users/templates/users/user_detail.html:5
48
#: users/templates/users/user_detail.html:5
50
msgid "Reset user MFA success"
msgid "Reset user MFA success"
msgstr "重置用户MFA成功"
msgstr "重置用户MFA成功"
...
@@ -5754,8 +5748,8 @@ msgstr ""
...
@@ -5754,8 +5748,8 @@ msgstr ""
"具</a>) <br>注意: 如果同时设置了定期执行和周期执行,优先使用定期执行"
"具</a>) <br>注意: 如果同时设置了定期执行和周期执行,优先使用定期执行"
#: xpack/plugins/change_auth_plan/meta.py:9
#: xpack/plugins/change_auth_plan/meta.py:9
#: xpack/plugins/change_auth_plan/models.py:11
6
#: xpack/plugins/change_auth_plan/models.py:11
7
#: xpack/plugins/change_auth_plan/models.py:25
6
#: xpack/plugins/change_auth_plan/models.py:25
7
#: xpack/plugins/change_auth_plan/views.py:33
#: xpack/plugins/change_auth_plan/views.py:33
#: xpack/plugins/change_auth_plan/views.py:50
#: xpack/plugins/change_auth_plan/views.py:50
#: xpack/plugins/change_auth_plan/views.py:74
#: xpack/plugins/change_auth_plan/views.py:74
...
@@ -5766,20 +5760,20 @@ msgstr ""
...
@@ -5766,20 +5760,20 @@ msgstr ""
msgid "Change auth plan"
msgid "Change auth plan"
msgstr "改密计划"
msgstr "改密计划"
#: xpack/plugins/change_auth_plan/models.py:5
7
#: xpack/plugins/change_auth_plan/models.py:5
8
msgid "Custom password"
msgid "Custom password"
msgstr "自定义密码"
msgstr "自定义密码"
#: xpack/plugins/change_auth_plan/models.py:5
8
#: xpack/plugins/change_auth_plan/models.py:5
9
msgid "All assets use the same random password"
msgid "All assets use the same random password"
msgstr "所有资产使用相同的随机密码"
msgstr "所有资产使用相同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:
59
#: xpack/plugins/change_auth_plan/models.py:
60
msgid "All assets use different random password"
msgid "All assets use different random password"
msgstr "所有资产使用不同的随机密码"
msgstr "所有资产使用不同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:7
8
#: xpack/plugins/change_auth_plan/models.py:7
9
#: xpack/plugins/change_auth_plan/models.py:14
7
#: xpack/plugins/change_auth_plan/models.py:14
8
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:100
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:100
#: xpack/plugins/cloud/models.py:165 xpack/plugins/cloud/models.py:219
#: xpack/plugins/cloud/models.py:165 xpack/plugins/cloud/models.py:219
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:91
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:91
...
@@ -5788,8 +5782,8 @@ msgstr "所有资产使用不同的随机密码"
...
@@ -5788,8 +5782,8 @@ msgstr "所有资产使用不同的随机密码"
msgid "Cycle perform"
msgid "Cycle perform"
msgstr "周期执行"
msgstr "周期执行"
#: xpack/plugins/change_auth_plan/models.py:8
3
#: xpack/plugins/change_auth_plan/models.py:8
4
#: xpack/plugins/change_auth_plan/models.py:14
5
#: xpack/plugins/change_auth_plan/models.py:14
6
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:92
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:92
#: xpack/plugins/cloud/models.py:170 xpack/plugins/cloud/models.py:217
#: xpack/plugins/cloud/models.py:170 xpack/plugins/cloud/models.py:217
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83
...
@@ -5798,37 +5792,37 @@ msgstr "周期执行"
...
@@ -5798,37 +5792,37 @@ msgstr "周期执行"
msgid "Regularly perform"
msgid "Regularly perform"
msgstr "定期执行"
msgstr "定期执行"
#: xpack/plugins/change_auth_plan/models.py:9
2
#: xpack/plugins/change_auth_plan/models.py:9
3
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74
msgid "Password rules"
msgid "Password rules"
msgstr "密码规则"
msgstr "密码规则"
#: xpack/plugins/change_auth_plan/models.py:21
2
#: xpack/plugins/change_auth_plan/models.py:21
3
msgid "* For security, do not change {} user's password"
msgid "* For security, do not change {} user's password"
msgstr "* 为了安全,禁止更改 {} 用户的密码"
msgstr "* 为了安全,禁止更改 {} 用户的密码"
#: xpack/plugins/change_auth_plan/models.py:21
6
#: xpack/plugins/change_auth_plan/models.py:21
7
msgid "Assets is empty, please add the asset"
msgid "Assets is empty, please add the asset"
msgstr "资产为空,请添加资产"
msgstr "资产为空,请添加资产"
#: xpack/plugins/change_auth_plan/models.py:26
0
#: xpack/plugins/change_auth_plan/models.py:26
1
msgid "Change auth plan snapshot"
msgid "Change auth plan snapshot"
msgstr "改密计划快照"
msgstr "改密计划快照"
#: xpack/plugins/change_auth_plan/models.py:27
5
#: xpack/plugins/change_auth_plan/models.py:27
6
#: xpack/plugins/change_auth_plan/models.py:4
26
#: xpack/plugins/change_auth_plan/models.py:4
33
msgid "Change auth plan execution"
msgid "Change auth plan execution"
msgstr "改密计划执行"
msgstr "改密计划执行"
#: xpack/plugins/change_auth_plan/models.py:4
35
#: xpack/plugins/change_auth_plan/models.py:4
42
msgid "Change auth plan execution subtask"
msgid "Change auth plan execution subtask"
msgstr "改密计划执行子任务"
msgstr "改密计划执行子任务"
#: xpack/plugins/change_auth_plan/models.py:4
53
#: xpack/plugins/change_auth_plan/models.py:4
60
msgid "Authentication failed"
msgid "Authentication failed"
msgstr "认证失败"
msgstr "认证失败"
#: xpack/plugins/change_auth_plan/models.py:4
55
#: xpack/plugins/change_auth_plan/models.py:4
62
msgid "Connection timeout"
msgid "Connection timeout"
msgstr "连接超时"
msgstr "连接超时"
...
@@ -6438,6 +6432,27 @@ msgstr "密码匣子"
...
@@ -6438,6 +6432,27 @@ msgstr "密码匣子"
msgid "vault create"
msgid "vault create"
msgstr "创建"
msgstr "创建"
#~ msgid ""
#~ "The username or password you entered is incorrect, please enter it again."
#~ msgstr "您输入的用户名或密码不正确,请重新输入。"
#~ msgid ""
#~ "You can also try {times_try} times (The account will be temporarily "
#~ "locked for {block_time} minutes)"
#~ msgstr "您还可以尝试 {times_try} 次(账号将被临时锁定 {block_time} 分钟)"
#~ msgid "No order found or order expired"
#~ msgstr "没有找到工单,或者已过期"
#~ msgid "Order was rejected by {}"
#~ msgstr "工单被拒绝 {}"
#~ msgid "login_confirm_setting"
#~ msgstr "登录复核设置"
#~ msgid "The user password has expired"
#~ msgstr "用户密码已过期"
#~ msgid "Recipient"
#~ msgid "Recipient"
#~ msgstr "收件人"
#~ msgstr "收件人"
...
...
apps/perms/templates/perms/asset_permission_create_update.html
View file @
6ce9815d
...
@@ -100,16 +100,6 @@
...
@@ -100,16 +100,6 @@
<link
rel=
"stylesheet"
type=
"text/css"
href=
{%
static
"
css
/
plugins
/
daterangepicker
/
daterangepicker
.
css
"
%}
/>
<link
rel=
"stylesheet"
type=
"text/css"
href=
{%
static
"
css
/
plugins
/
daterangepicker
/
daterangepicker
.
css
"
%}
/>
<script>
<script>
var
dateOptions
=
{
singleDatePicker
:
true
,
showDropdowns
:
true
,
timePicker
:
true
,
timePicker24Hour
:
true
,
autoApply
:
true
,
locale
:
{
format
:
'YYYY-MM-DD HH:mm'
}
};
var
api_action
=
"{{ api_action }}"
;
var
api_action
=
"{{ api_action }}"
;
$
(
document
).
ready
(
function
()
{
$
(
document
).
ready
(
function
()
{
...
@@ -119,8 +109,8 @@ $(document).ready(function () {
...
@@ -119,8 +109,8 @@ $(document).ready(function () {
nodesSelect2Init
(
".nodes-select2"
);
nodesSelect2Init
(
".nodes-select2"
);
usersSelect2Init
(
".users-select2"
);
usersSelect2Init
(
".users-select2"
);
$
(
'#date_start'
).
daterangepicker
(
dateOptions
);
initDateRangePicker
(
'#date_start'
);
$
(
'#date_expired'
).
daterangepicker
(
dateOptions
);
initDateRangePicker
(
'#date_expired'
);
$
(
"#id_assets"
).
parent
().
find
(
".select2-selection"
).
on
(
'click'
,
function
(
e
)
{
$
(
"#id_assets"
).
parent
().
find
(
".select2-selection"
).
on
(
'click'
,
function
(
e
)
{
if
(
$
(
e
.
target
).
attr
(
'class'
)
!==
'select2-selection__choice__remove'
){
if
(
$
(
e
.
target
).
attr
(
'class'
)
!==
'select2-selection__choice__remove'
){
...
...
apps/perms/templates/perms/remote_app_permission_create_update.html
View file @
6ce9815d
...
@@ -115,8 +115,8 @@ $(document).ready(function () {
...
@@ -115,8 +115,8 @@ $(document).ready(function () {
closeOnSelect
:
false
closeOnSelect
:
false
});
});
usersSelect2Init
(
'.users-select2'
);
usersSelect2Init
(
'.users-select2'
);
$
(
'#date_start'
).
daterangepicker
(
dateOptions
);
initDateRangePicker
(
'#date_start'
);
$
(
'#date_expired'
).
daterangepicker
(
dateOptions
);
initDateRangePicker
(
'#date_expired'
);
})
})
.
on
(
"submit"
,
"form"
,
function
(
evt
)
{
.
on
(
"submit"
,
"form"
,
function
(
evt
)
{
evt
.
preventDefault
();
evt
.
preventDefault
();
...
...
apps/static/js/jumpserver.js
View file @
6ce9815d
...
@@ -1289,3 +1289,31 @@ function showCeleryTaskLog(taskId) {
...
@@ -1289,3 +1289,31 @@ function showCeleryTaskLog(taskId) {
var
url
=
'/ops/celery/task/taskId/log/'
.
replace
(
'taskId'
,
taskId
);
var
url
=
'/ops/celery/task/taskId/log/'
.
replace
(
'taskId'
,
taskId
);
window
.
open
(
url
,
''
,
'width=900,height=600'
)
window
.
open
(
url
,
''
,
'width=900,height=600'
)
}
}
function
initDateRangePicker
(
selector
,
options
)
{
if
(
!
options
)
{
options
=
{}
}
var
zhLocale
=
{
format
:
'YYYY-MM-DD HH:mm'
,
separator
:
' ~ '
,
applyLabel
:
"应用"
,
cancelLabel
:
"取消"
,
resetLabel
:
"重置"
,
daysOfWeek
:
[
"日"
,
"一"
,
"二"
,
"三"
,
"四"
,
"五"
,
"六"
],
//汉化处理
monthNames
:
[
"一月"
,
"二月"
,
"三月"
,
"四月"
,
"五月"
,
"六月"
,
"七月"
,
"八月"
,
"九月"
,
"十月"
,
"十一月"
,
"十二月"
],
};
var
defaultOption
=
{
singleDatePicker
:
true
,
showDropdowns
:
true
,
timePicker
:
true
,
timePicker24Hour
:
true
,
autoApply
:
true
,
};
var
userLang
=
navigator
.
language
||
navigator
.
userLanguage
;;
if
(
userLang
.
indexOf
(
'zh'
)
!==
-
1
)
{
defaultOption
.
locale
=
zhLocale
;
}
options
=
Object
.
assign
(
defaultOption
,
options
);
return
$
(
selector
).
daterangepicker
(
options
);
}
apps/templates/_head_css_js.html
View file @
6ce9815d
apps/users/models/user.py
View file @
6ce9815d
...
@@ -62,10 +62,6 @@ class AuthMixin:
...
@@ -62,10 +62,6 @@ class AuthMixin:
def
can_use_ssh_key_login
(
self
):
def
can_use_ssh_key_login
(
self
):
return
settings
.
TERMINAL_PUBLIC_KEY_AUTH
return
settings
.
TERMINAL_PUBLIC_KEY_AUTH
def
check_otp
(
self
,
code
):
from
..utils
import
check_otp_code
return
check_otp_code
(
self
.
otp_secret_key
,
code
)
def
is_public_key_valid
(
self
):
def
is_public_key_valid
(
self
):
"""
"""
Check if the user's ssh public key is valid.
Check if the user's ssh public key is valid.
...
@@ -362,6 +358,10 @@ class MFAMixin:
...
@@ -362,6 +358,10 @@ class MFAMixin:
self
.
otp_level
=
0
self
.
otp_level
=
0
self
.
otp_secret_key
=
None
self
.
otp_secret_key
=
None
def
check_otp
(
self
,
code
):
from
..utils
import
check_otp_code
return
check_otp_code
(
self
.
otp_secret_key
,
code
)
class
User
(
AuthMixin
,
TokenMixin
,
RoleMixin
,
MFAMixin
,
AbstractUser
):
class
User
(
AuthMixin
,
TokenMixin
,
RoleMixin
,
MFAMixin
,
AbstractUser
):
SOURCE_LOCAL
=
'local'
SOURCE_LOCAL
=
'local'
...
...
apps/users/templates/users/_user.html
View file @
6ce9815d
...
@@ -56,6 +56,7 @@
...
@@ -56,6 +56,7 @@
{% endblock %}
{% endblock %}
{% block custom_foot_js %}
{% block custom_foot_js %}
<script
src=
"{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"
></script>
<script
src=
"{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"
></script>
<script
src=
"{% static 'js/plugins/datepicker/bootstrap-datepicker.zh-CN.min.js' %}"
></script>
<script
type=
"text/javascript"
src=
'{% static "js/plugins/daterangepicker/moment.min.js" %}'
></script>
<script
type=
"text/javascript"
src=
'{% static "js/plugins/daterangepicker/moment.min.js" %}'
></script>
<script
type=
"text/javascript"
src=
'{% static "js/plugins/daterangepicker/daterangepicker.min.js" %}'
></script>
<script
type=
"text/javascript"
src=
'{% static "js/plugins/daterangepicker/daterangepicker.min.js" %}'
></script>
<link
rel=
"stylesheet"
type=
"text/css"
href=
{%
static
"
css
/
plugins
/
daterangepicker
/
daterangepicker
.
css
"
%}
/>
<link
rel=
"stylesheet"
type=
"text/css"
href=
{%
static
"
css
/
plugins
/
daterangepicker
/
daterangepicker
.
css
"
%}
/>
...
@@ -72,19 +73,9 @@
...
@@ -72,19 +73,9 @@
$
(
groups_id
).
closest
(
'.form-group'
).
removeClass
(
'hidden'
);
$
(
groups_id
).
closest
(
'.form-group'
).
removeClass
(
'hidden'
);
}}
}}
var
dateOptions
=
{
singleDatePicker
:
true
,
showDropdowns
:
true
,
timePicker
:
true
,
timePicker24Hour
:
true
,
autoApply
:
true
,
locale
:
{
format
:
'YYYY-MM-DD HH:mm'
}
};
$
(
document
).
ready
(
function
()
{
$
(
document
).
ready
(
function
()
{
$
(
'.select2'
).
select2
();
$
(
'.select2'
).
select2
();
$
(
'#id_date_expired'
).
daterangepicker
(
dateOptions
);
initDateRangePicker
(
'#id_date_expired'
);
var
mfa_radio
=
$
(
'#id_otp_level'
);
var
mfa_radio
=
$
(
'#id_otp_level'
);
mfa_radio
.
addClass
(
"form-inline"
);
mfa_radio
.
addClass
(
"form-inline"
);
mfa_radio
.
children
().
css
(
"margin-right"
,
"15px"
);
mfa_radio
.
children
().
css
(
"margin-right"
,
"15px"
);
...
...
apps/users/templates/users/user_detail.html
View file @
6ce9815d
...
@@ -212,7 +212,7 @@
...
@@ -212,7 +212,7 @@
</table>
</table>
</div>
</div>
</div>
</div>
{% if user
_object.is_current_org_admin or user_object
.is_superuser %}
{% if user
.is_current_org_admin or user
.is_superuser %}
<div
class=
"panel panel-info"
>
<div
class=
"panel panel-info"
>
<div
class=
"panel-heading"
>
<div
class=
"panel-heading"
>
<i
class=
"fa fa-info-circle"
></i>
{% trans 'User group' %}
<i
class=
"fa fa-info-circle"
></i>
{% trans 'User group' %}
...
...
apps/users/urls/api_urls.py
View file @
6ce9815d
...
@@ -20,9 +20,6 @@ router.register(r'users-groups-relations', api.UserUserGroupRelationViewSet, 'us
...
@@ -20,9 +20,6 @@ router.register(r'users-groups-relations', api.UserUserGroupRelationViewSet, 'us
urlpatterns
=
[
urlpatterns
=
[
path
(
'connection-token/'
,
auth_api
.
UserConnectionTokenApi
.
as_view
(),
path
(
'connection-token/'
,
auth_api
.
UserConnectionTokenApi
.
as_view
(),
name
=
'connection-token'
),
name
=
'connection-token'
),
path
(
'auth/'
,
auth_api
.
UserAuthApi
.
as_view
(),
name
=
'user-auth'
),
path
(
'otp/auth/'
,
auth_api
.
UserOtpAuthApi
.
as_view
(),
name
=
'user-otp-auth'
),
path
(
'profile/'
,
api
.
UserProfileApi
.
as_view
(),
name
=
'user-profile'
),
path
(
'profile/'
,
api
.
UserProfileApi
.
as_view
(),
name
=
'user-profile'
),
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'
),
...
...
apps/users/utils.py
View file @
6ce9815d
...
@@ -218,9 +218,11 @@ def set_tmp_user_to_cache(request, user, ttl=3600):
...
@@ -218,9 +218,11 @@ def set_tmp_user_to_cache(request, user, ttl=3600):
def
redirect_user_first_login_or_index
(
request
,
redirect_field_name
):
def
redirect_user_first_login_or_index
(
request
,
redirect_field_name
):
if
request
.
user
.
is_first_login
:
if
request
.
user
.
is_first_login
:
return
reverse
(
'users:user-first-login'
)
return
reverse
(
'users:user-first-login'
)
return
request
.
POST
.
get
(
url_in_post
=
request
.
POST
.
get
(
redirect_field_name
)
redirect_field_name
,
if
url_in_post
:
request
.
GET
.
get
(
redirect_field_name
,
reverse
(
'index'
)))
return
url_in_post
url_in_get
=
request
.
GET
.
get
(
redirect_field_name
,
reverse
(
'index'
))
return
url_in_get
def
generate_otp_uri
(
request
,
issuer
=
"Jumpserver"
):
def
generate_otp_uri
(
request
,
issuer
=
"Jumpserver"
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment