pull/410/head
vapao 2021-09-19 22:53:40 +08:00
parent 105abd757f
commit 4dc4123d14
3 changed files with 94 additions and 23 deletions

View File

@ -9,6 +9,6 @@ urlpatterns = [
url(r'^$', SettingView.as_view()),
url(r'^ldap_test/$', ldap_test),
url(r'^email_test/$', email_test),
url(r'^mfa_test/$', mfa_test),
url(r'^mfa/$', MFAView.as_view()),
url(r'^about/$', get_about)
]

View File

@ -2,11 +2,13 @@
# Copyright: (c) <spug.dev@gmail.com>
# Released under the AGPL-3.0 License.
import django
from django.core.cache import cache
from django.views.generic import View
from django.conf import settings
from libs import JsonParser, Argument, json_response
from libs.utils import generate_random_str
from libs.mail import Mail
from apps.account.models import User
from libs.spug import send_login_wx_code
from apps.setting.utils import AppSetting
from apps.setting.models import Setting
import platform
@ -28,6 +30,37 @@ class SettingView(View):
return json_response(error=error)
class MFAView(View):
def get(self, request):
if not request.user.wx_token:
return json_response(error='检测到当前账户未配置微信Token请配置后再尝试启用MFA认证否则可能造成系统无法正常登录。')
code = generate_random_str(6)
send_login_wx_code(request.user.wx_token, code)
cache.set(f'{request.user.username}:code', code, 300)
return json_response()
def post(self, request):
form, error = JsonParser(
Argument('enable', type=bool, help='参数错误'),
Argument('code', required=False)
).parse(request.body)
if error is None:
if form.enable:
if not form.code:
return json_response(error='请输入验证码')
key = f'{request.user.username}:code'
code = cache.get(key)
if not code:
return json_response(error='验证码已失效,请重新获取')
if code != form.code:
ttl = cache.ttl(key)
cache.expire(key, ttl - 100)
return json_response(error='验证码错误')
cache.delete(key)
AppSetting.set('MFA', {'enable': form.enable})
return json_response(error=error)
def ldap_test(request):
form, error = JsonParser(
Argument('server'),
@ -65,9 +98,11 @@ def email_test(request):
def mfa_test(request):
for user in User.objects.filter(is_supper=True):
if not user.wx_token:
return json_response(error=f'检测到管理员账户 {user.nickname} 未配置微信Token请配置后再尝试启用MFA认证否则可能造成系统无法正常登录。')
if not request.user.wx_token:
return json_response(error='检测到当前账户未配置微信Token请配置后再尝试启用MFA认证否则可能造成系统无法正常登录。')
code = generate_random_str(6)
send_login_wx_code(request.user.wx_token, code)
cache.set(f'{request.user.username}:code', code, 300)
return json_response()

View File

@ -3,9 +3,9 @@
* Copyright (c) <spug.dev@gmail.com>
* Released under the AGPL-3.0 License.
*/
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { observer } from 'mobx-react';
import { Form, Switch, message } from 'antd';
import { Form, Switch, Input, Space, message, Button } from 'antd';
import styles from './index.module.css';
import http from 'libs/http';
import store from './store';
@ -13,6 +13,19 @@ import store from './store';
export default observer(function () {
const [verify_ip, setVerifyIP] = useState(store.settings.verify_ip);
const [mfa, setMFA] = useState(store.settings.MFA || {});
const [code, setCode] = useState();
const [visible, setVisible] = useState(false);
const [counter, setCounter] = useState(0);
const [loading, setLoading] = useState(false);
const [loading2, setLoading2] = useState(false);
useEffect(() => {
setTimeout(() => {
if (counter > 0) {
setCounter(counter - 1)
}
}, 1000)
}, [counter])
function handleChangeVerifyIP(v) {
setVerifyIP(v);
@ -25,21 +38,26 @@ export default observer(function () {
function handleChangeMFA(v) {
if (v && !store.settings.spug_key) return message.error('开启MFA认证需要先在基本设置中配置调用凭据');
if (v) {
http.get('/api/setting/mfa_test/')
.then(() => _doModify(v))
} else {
_doModify(v)
}
v ? setVisible(true) : handleMFAModify(false)
}
function _doModify(v) {
setMFA({...mfa, enable: v});
http.post('/api/setting/', {data: [{key: 'MFA', value: {...mfa, enable: v}}]})
function handleCaptcha() {
setLoading(true)
http.get('/api/setting/mfa/')
.then(() => setCounter(60))
.finally(() => setLoading(false))
}
function handleMFAModify(v) {
setLoading2(true)
http.post('/api/setting/mfa/', {enable: v, code})
.then(() => {
setMFA({enable: v});
setVisible(false);
message.success('设置成功');
store.fetchSettings()
})
.finally(() => setLoading2(false))
}
return (
@ -58,13 +76,31 @@ export default observer(function () {
<Form.Item
label="登录MFA两步认证"
style={{marginTop: 24}}
help={<span>建议开启登录时额外使用验证码进行身份验证开启前至少要确保管理员账户配置了微信Token账户管理/用户/编辑开启后未配置微信Token的账户将无法登录<a
target="_blank" rel="noopener noreferrer" href="https://spug.cc/docs/wx-token/">什么是微信Token</a></span>}>
<Switch
checkedChildren="开启"
unCheckedChildren="关闭"
onChange={handleChangeMFA}
checked={mfa.enable}/>
extra={visible ? '输入验证码,通过验证后开启。' :
<span>建议开启登录时额外使用验证码进行身份验证开启前至少要确保管理员账户配置了微信Token账户管理/编辑开启后未配置微信Token的账户将无法登录<a
target="_blank" rel="noopener noreferrer" href="https://spug.cc/docs/wx-token/">什么是微信Token</a></span>}>
{visible ? (
<div style={{display: 'flex', width: 490}}>
<Form.Item noStyle help="验证通过后开启MFA两步验证。">
<Input placeholder="请输入验证码" onChange={e => setCode(e.target.value)}/>
</Form.Item>
{counter > 0 ? (
<Button disabled style={{marginLeft: 8}}>{counter} 秒后重新获取</Button>
) : (
<Button loading={loading} style={{marginLeft: 8}} onClick={handleCaptcha}>获取验证码</Button>
)}
<Space style={{marginLeft: 48}}>
<Button onClick={() => setVisible(false)}>取消</Button>
<Button type="primary" loading={loading2} onClick={() => handleMFAModify(true)}>确认</Button>
</Space>
</div>
) : (
<Switch
checkedChildren="开启"
unCheckedChildren="关闭"
onChange={handleChangeMFA}
checked={mfa.enable}/>
)}
</Form.Item>
</Form>
</React.Fragment>