secret token thanks to @HarlyquinForest
This commit is contained in:
@@ -3,6 +3,7 @@ class User {
|
||||
constructor() {
|
||||
this.username = "";
|
||||
this.password = "";
|
||||
this.LoginSecret = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +181,7 @@ class AllSetting {
|
||||
this.tgBotBackup = false;
|
||||
this.tgCpu = "";
|
||||
this.xrayTemplateConfig = "";
|
||||
this.secretEnable = false;
|
||||
|
||||
this.timeLocation = "Asia/Tehran";
|
||||
|
||||
|
||||
+16
-5
@@ -11,15 +11,17 @@ import (
|
||||
)
|
||||
|
||||
type LoginForm struct {
|
||||
Username string `json:"username" form:"username"`
|
||||
Password string `json:"password" form:"password"`
|
||||
Username string `json:"username" form:"username"`
|
||||
Password string `json:"password" form:"password"`
|
||||
LoginSecret string `json:"loginSecret" form:"loginSecret"`
|
||||
}
|
||||
|
||||
type IndexController struct {
|
||||
BaseController
|
||||
|
||||
userService service.UserService
|
||||
tgbot service.Tgbot
|
||||
settingService service.SettingService
|
||||
userService service.UserService
|
||||
tgbot service.Tgbot
|
||||
}
|
||||
|
||||
func NewIndexController(g *gin.RouterGroup) *IndexController {
|
||||
@@ -32,6 +34,7 @@ func (a *IndexController) initRouter(g *gin.RouterGroup) {
|
||||
g.GET("/", a.index)
|
||||
g.POST("/login", a.login)
|
||||
g.GET("/logout", a.logout)
|
||||
g.POST("/getSecretStatus", a.getSecretStatus)
|
||||
}
|
||||
|
||||
func (a *IndexController) index(c *gin.Context) {
|
||||
@@ -57,7 +60,7 @@ func (a *IndexController) login(c *gin.Context) {
|
||||
pureJsonMsg(c, false, I18n(c, "pages.login.toasts.emptyPassword"))
|
||||
return
|
||||
}
|
||||
user := a.userService.CheckUser(form.Username, form.Password)
|
||||
user := a.userService.CheckUser(form.Username, form.Password, form.LoginSecret)
|
||||
timeStr := time.Now().Format("2006-01-02 15:04:05")
|
||||
if user == nil {
|
||||
a.tgbot.UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 0)
|
||||
@@ -82,3 +85,11 @@ func (a *IndexController) logout(c *gin.Context) {
|
||||
session.ClearSession(c)
|
||||
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
|
||||
}
|
||||
|
||||
func (a *IndexController) getSecretStatus(c *gin.Context) {
|
||||
status, err := a.settingService.GetSecretStatus()
|
||||
if err == nil {
|
||||
jsonObj(c, status, nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ type updateUserForm struct {
|
||||
NewPassword string `json:"newPassword" form:"newPassword"`
|
||||
}
|
||||
|
||||
type updateSecretForm struct {
|
||||
LoginSecret string `json:"loginSecret" form:"loginSecret"`
|
||||
}
|
||||
|
||||
type SettingController struct {
|
||||
settingService service.SettingService
|
||||
userService service.UserService
|
||||
@@ -38,6 +42,8 @@ func (a *SettingController) initRouter(g *gin.RouterGroup) {
|
||||
g.POST("/updateUser", a.updateUser)
|
||||
g.POST("/restartPanel", a.restartPanel)
|
||||
g.GET("/getDefaultJsonConfig", a.getDefaultJsonConfig)
|
||||
g.POST("/updateUserSecret", a.updateSecret)
|
||||
g.POST("/getUserSecret", a.getUserSecret)
|
||||
}
|
||||
|
||||
func (a *SettingController) getAllSetting(c *gin.Context) {
|
||||
@@ -128,3 +134,25 @@ func (a *SettingController) restartPanel(c *gin.Context) {
|
||||
err := a.panelService.RestartPanel(time.Second * 3)
|
||||
jsonMsg(c, I18n(c, "pages.setting.restartPanel"), err)
|
||||
}
|
||||
|
||||
func (a *SettingController) updateSecret(c *gin.Context) {
|
||||
form := &updateSecretForm{}
|
||||
err := c.ShouldBind(form)
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.setting.toasts.modifySetting"), err)
|
||||
}
|
||||
user := session.GetLoginUser(c)
|
||||
err = a.userService.UpdateUserSecret(user.Id, form.LoginSecret)
|
||||
if err == nil {
|
||||
user.LoginSecret = form.LoginSecret
|
||||
session.SetLoginUser(c, user)
|
||||
}
|
||||
jsonMsg(c, I18n(c, "pages.setting.toasts.modifyUser"), err)
|
||||
}
|
||||
func (a *SettingController) getUserSecret(c *gin.Context) {
|
||||
loginUser := session.GetLoginUser(c)
|
||||
user := a.userService.GetUserSecret(loginUser.Id)
|
||||
if user != nil {
|
||||
jsonObj(c, user, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ type AllSetting struct {
|
||||
TgCpu int `json:"tgCpu" form:"tgCpu"`
|
||||
XrayTemplateConfig string `json:"xrayTemplateConfig" form:"xrayTemplateConfig"`
|
||||
TimeLocation string `json:"timeLocation" form:"timeLocation"`
|
||||
SecretEnable bool `json:"secretEnable" form:"secretEnable"`
|
||||
}
|
||||
|
||||
func (s *AllSetting) CheckValid() error {
|
||||
|
||||
@@ -57,6 +57,11 @@
|
||||
<a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)"/>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="secretEnable">
|
||||
<a-input type="text" placeholder='{{ i18n "secretToken" }}' v-model.trim="user.loginSecret" @keydown.enter.native="login">
|
||||
<a-icon slot="prefix" type="key" style="color: rgba(0,0,0,.25)"/>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button block @click="login" :loading="loading">{{ i18n "login" }}</a-button>
|
||||
</a-form-item>
|
||||
@@ -98,10 +103,12 @@
|
||||
data: {
|
||||
loading: false,
|
||||
user: new User(),
|
||||
secretEnable: false,
|
||||
lang : ""
|
||||
},
|
||||
created(){
|
||||
this.lang = getLang();
|
||||
this.secretEnable = this.getSecretStatus();
|
||||
},
|
||||
methods: {
|
||||
async login() {
|
||||
@@ -111,6 +118,15 @@
|
||||
if (msg.success) {
|
||||
location.href = basePath + 'xui/';
|
||||
}
|
||||
},
|
||||
async getSecretStatus() {
|
||||
this.loading= true;
|
||||
const msg = await HttpUtil.post('/getSecretStatus');
|
||||
this.loading = false;
|
||||
if (msg.success){
|
||||
this.secretEnable = msg.obj;
|
||||
return msg.obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+83
-15
@@ -91,8 +91,39 @@
|
||||
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-form :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65); padding: 20px;': 'background: white; padding: 20px;'">
|
||||
<a-list-item style="padding: 20px">
|
||||
<a-row>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.setting.loginSecurity" }}' description='{{ i18n "pages.setting.loginSecurityDesc" }}'/>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<template>
|
||||
<a-switch @change="toggleToken(allSetting.secretEnable)" v-model="allSetting.secretEnable"></a-switch>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
<a-list-item style="padding: 20px">
|
||||
<a-row>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.setting.secretToken" }}' description='{{ i18n "pages.setting.secretTokenDesc" }}'/>
|
||||
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<svg
|
||||
@click="getNewSecret"
|
||||
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="anticon anticon-question-circle" viewBox="0 0 16 16"> <path d="M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41zm-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9z"/> <path fill-rule="evenodd" d="M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5.002 5.002 0 0 0 8 3zM3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9H3.1z"/>
|
||||
</svg>
|
||||
<template>
|
||||
<a-textarea type="text" id='token' :disabled="!allSetting.secretEnable" v-model="user.loginSecret"></a-textarea>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
<a-button type="primary" @click="updateSecret">{{ i18n "confirm" }}</a-button>
|
||||
</a-form>
|
||||
</a-tab-pane>
|
||||
|
||||
<a-tab-pane key="3" tab='{{ i18n "pages.setting.xrayConfiguration"}}'>
|
||||
<a-list item-layout="horizontal" :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
|
||||
<a-divider>{{ i18n "pages.setting.actions"}}</a-divider>
|
||||
@@ -205,7 +236,7 @@
|
||||
oldAllSetting: new AllSetting(),
|
||||
allSetting: new AllSetting(),
|
||||
saveBtnDisable: true,
|
||||
user: {},
|
||||
user: new User(),
|
||||
lang: getLang(),
|
||||
ipv4Settings: {
|
||||
tag: "IPv4",
|
||||
@@ -262,31 +293,33 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loading(spinning = true) {
|
||||
this.spinning = spinning;
|
||||
loading(spinning = true , obj) {
|
||||
if(obj == null)
|
||||
this.spinning = spinning;
|
||||
},
|
||||
async getAllSetting() {
|
||||
this.loading(true);
|
||||
this.loading(true,{});
|
||||
const msg = await HttpUtil.post("/xui/setting/all");
|
||||
this.loading(false);
|
||||
this.loading(false,null);
|
||||
if (msg.success) {
|
||||
this.oldAllSetting = new AllSetting(msg.obj);
|
||||
this.allSetting = new AllSetting(msg.obj);
|
||||
this.saveBtnDisable = true;
|
||||
}
|
||||
await this.getUserSecret();
|
||||
},
|
||||
async updateAllSetting() {
|
||||
this.loading(true);
|
||||
this.loading(true,{});
|
||||
const msg = await HttpUtil.post("/xui/setting/update", this.allSetting);
|
||||
this.loading(false);
|
||||
this.loading(false,null);
|
||||
if (msg.success) {
|
||||
await this.getAllSetting();
|
||||
}
|
||||
},
|
||||
async updateUser() {
|
||||
this.loading(true);
|
||||
this.loading(true,{});
|
||||
const msg = await HttpUtil.post("/xui/setting/updateUser", this.user);
|
||||
this.loading(false);
|
||||
this.loading(false,null);
|
||||
if (msg.success) {
|
||||
this.user = {};
|
||||
}
|
||||
@@ -301,19 +334,54 @@
|
||||
onOk: () => resolve(),
|
||||
});
|
||||
});
|
||||
this.loading(true);
|
||||
this.loading(true,{});
|
||||
const msg = await HttpUtil.post("/xui/setting/restartPanel");
|
||||
this.loading(false);
|
||||
this.loading(false,null);
|
||||
if (msg.success) {
|
||||
this.loading(true);
|
||||
this.loading(true,{});
|
||||
await PromiseUtil.sleep(5000);
|
||||
location.reload();
|
||||
}
|
||||
},
|
||||
async getUserSecret(){
|
||||
const user_msg = await HttpUtil.post("/xui/setting/getUserSecret", this.user);
|
||||
if (user_msg.success){
|
||||
this.user = user_msg.obj;
|
||||
}
|
||||
this.loading(false);
|
||||
},
|
||||
async updateSecret(){
|
||||
this.loading(true,{});
|
||||
const msg = await HttpUtil.post("/xui/setting/updateUserSecret", this.user);
|
||||
if (msg.success){
|
||||
this.user = msg.obj;
|
||||
}
|
||||
this.loading(false,null);
|
||||
await this.updateAllSetting();
|
||||
},
|
||||
async getNewSecret(){
|
||||
this.loading(true,{});
|
||||
await PromiseUtil.sleep(1000);
|
||||
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
|
||||
var string = '';
|
||||
var len = 64;
|
||||
for(var ii=0; ii<len; ii++){
|
||||
string += chars[Math.floor(Math.random() * chars.length)];
|
||||
}
|
||||
this.user.loginSecret = string;
|
||||
document.getElementById('token').value =this.user.loginSecret;
|
||||
this.loading(false,null);
|
||||
},
|
||||
async toggleToken(value){
|
||||
if(value)
|
||||
this.getNewSecret();
|
||||
else
|
||||
this.user.loginSecret = "";
|
||||
},
|
||||
async resetXrayConfigToDefault() {
|
||||
this.loading(true);
|
||||
this.loading(true,{});
|
||||
const msg = await HttpUtil.get("/xui/setting/getDefaultJsonConfig");
|
||||
this.loading(false);
|
||||
this.loading(false,null);
|
||||
if (msg.success) {
|
||||
this.templateSettings = JSON.parse(JSON.stringify(msg.obj, null, 2));
|
||||
this.saveBtnDisable = true;
|
||||
|
||||
+16
-1
@@ -38,6 +38,7 @@ var defaultValueMap = map[string]string{
|
||||
"tgRunTime": "@daily",
|
||||
"tgBotBackup": "false",
|
||||
"tgCpu": "0",
|
||||
"secretEnable": "false",
|
||||
}
|
||||
|
||||
type SettingService struct {
|
||||
@@ -129,7 +130,13 @@ func (s *SettingService) GetAllSetting() (*entity.AllSetting, error) {
|
||||
|
||||
func (s *SettingService) ResetSettings() error {
|
||||
db := database.GetDB()
|
||||
return db.Where("1 = 1").Delete(model.Setting{}).Error
|
||||
err := db.Where("1 = 1").Delete(model.Setting{}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Model(model.User{}).
|
||||
Where("1 = 1").
|
||||
Update("login_secret", "").Error
|
||||
}
|
||||
|
||||
func (s *SettingService) getSetting(key string) (*model.Setting, error) {
|
||||
@@ -288,6 +295,14 @@ func (s *SettingService) SetgetTrafficDiff(value int) error {
|
||||
return s.setInt("trafficDiff", value)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSecretStatus() (bool, error) {
|
||||
return s.getBool("secretEnable")
|
||||
}
|
||||
|
||||
func (s *SettingService) SetSecretStatus(value bool) error {
|
||||
return s.setBool("secretEnable", value)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSecret() ([]byte, error) {
|
||||
secret, err := s.getString("secret")
|
||||
if secret == defaultValueMap["secret"] {
|
||||
|
||||
+31
-2
@@ -25,12 +25,12 @@ func (s *UserService) GetFirstUser() (*model.User, error) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *UserService) CheckUser(username string, password string) *model.User {
|
||||
func (s *UserService) CheckUser(username string, password string, secret string) *model.User {
|
||||
db := database.GetDB()
|
||||
|
||||
user := &model.User{}
|
||||
err := db.Model(model.User{}).
|
||||
Where("username = ? and password = ?", username, password).
|
||||
Where("username = ? and password = ? and login_secret = ?", username, password, secret).
|
||||
First(user).
|
||||
Error
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
@@ -50,6 +50,35 @@ func (s *UserService) UpdateUser(id int, username string, password string) error
|
||||
Error
|
||||
}
|
||||
|
||||
func (s *UserService) UpdateUserSecret(id int, secret string) error {
|
||||
db := database.GetDB()
|
||||
return db.Model(model.User{}).
|
||||
Where("id = ?", id).
|
||||
Update("login_secret", secret).
|
||||
Error
|
||||
}
|
||||
|
||||
func (s *UserService) RemoveUserSecret() error {
|
||||
db := database.GetDB()
|
||||
return db.Model(model.User{}).
|
||||
Where("1 = 1").
|
||||
Update("login_secret", "").
|
||||
Error
|
||||
}
|
||||
|
||||
func (s *UserService) GetUserSecret(id int) *model.User {
|
||||
db := database.GetDB()
|
||||
user := &model.User{}
|
||||
err := db.Model(model.User{}).
|
||||
Where("id = ?", id).
|
||||
First(user).
|
||||
Error
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
func (s *UserService) UpdateFirstUser(username string, password string) error {
|
||||
if username == "" {
|
||||
return errors.New("username can not be empty")
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"install" = "Install"
|
||||
"clients" = "Clients"
|
||||
"usage" = "Usage"
|
||||
"secretToken" = "Secret token"
|
||||
|
||||
[menu]
|
||||
"dashboard" = "System Status"
|
||||
@@ -288,6 +289,10 @@
|
||||
"tgNotifyCpuDesc" = "This telegram bot will send you a notification if CPU usage is more than this percentage (unit:%)"
|
||||
"timeZonee" = "Time Zone"
|
||||
"timeZoneDesc" = "The scheduled task runs according to the time in the time zone, and restarts the panel to take effect"
|
||||
"loginSecurity" = "Login security"
|
||||
"loginSecurityDesc" = "Toggle additional step in user login page"
|
||||
"secretToken" = "Secret Token"
|
||||
"secretTokenDesc" = "Copy this secret token and keep it in a safe place; without this you won't be able to login. This can not be recovered from x-ui command tool neither"
|
||||
|
||||
[pages.setting.toasts]
|
||||
"modifySetting" = "Modify setting"
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"install" = "نصب"
|
||||
"clients" = "کاربران"
|
||||
"usage" = "استفاده"
|
||||
"secretToken" = "توکن امنیتی"
|
||||
|
||||
[menu]
|
||||
"dashboard" = "وضعیت سیستم"
|
||||
@@ -286,6 +287,10 @@
|
||||
"tgNotifyCpuDesc" = "این ربات تلگرام در صورت استفاده پردازنده بیشتر از این درصد برای شما پیام ارسال می کند.(واحد: درصد)"
|
||||
"timeZonee" = "منظقه زمانی"
|
||||
"timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقه زمانی اجرا می شوند. پنل را مجدداً راه اندازی می کند تا اعمال شود"
|
||||
"loginSecurity" = "لاگین ایمن"
|
||||
"loginSecurityDesc" = "افزودن یک مرحله دیگر به فرآیند لاگین"
|
||||
"secretToken" = "توکن امنیتی"
|
||||
"secretTokenDesc" = "این کد امنیتی را نزد خود در این جای امن نگه داری، بدون این کد امکان ورود به پنل را نخواهید داشت. امکان بازیابی آن وجود ندارد!"
|
||||
|
||||
[pages.setting.toasts]
|
||||
"modifySetting" = "ویرایش تنظیمات"
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"install" = "安装"
|
||||
"clients" = "客户端"
|
||||
"usage" = "用法"
|
||||
"secretToken" = "秘密令牌"
|
||||
|
||||
[menu]
|
||||
"dashboard" = "系统状态"
|
||||
@@ -286,6 +287,10 @@
|
||||
"tgNotifyCpuDesc" = "如果 CPU 使用率超过此百分比(单位:%),此 talegram bot 将向您发送通知"
|
||||
"timeZonee" = "时区"
|
||||
"timeZoneDesc" = "定时任务按照该时区的时间运行,重启面板生效"
|
||||
"loginSecurity" = "登录安全"
|
||||
"loginSecurityDesc" = "在用户登录页面中切换附加步骤"
|
||||
"secretToken" = "秘密令牌"
|
||||
"secretTokenDesc" = "复制此秘密令牌并将其保存在安全的地方;没有这个你将无法登录。这也无法从 x-ui 命令工具中恢复"
|
||||
|
||||
[pages.setting.toasts]
|
||||
"modifySetting" = "修改设置"
|
||||
|
||||
Reference in New Issue
Block a user