修改docker默认时区为上海
修复了记住登录无效的问题 修复了ssh下载文件名称不正确的问题 授权凭证增加了密钥类型
This commit is contained in:
@ -1,13 +1,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"next-terminal/pkg/global"
|
||||
"next-terminal/pkg/model"
|
||||
"next-terminal/pkg/totp"
|
||||
"next-terminal/pkg/utils"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
@ -15,6 +13,7 @@ import (
|
||||
type LoginAccount struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Remember bool `json:"remember"`
|
||||
TOTP string `json:"totp"`
|
||||
}
|
||||
|
||||
@ -28,6 +27,12 @@ type ChangePassword struct {
|
||||
OldPassword string `json:"oldPassword"`
|
||||
}
|
||||
|
||||
type Authorization struct {
|
||||
Token string
|
||||
Remember bool
|
||||
User model.User
|
||||
}
|
||||
|
||||
func LoginEndpoint(c echo.Context) error {
|
||||
var loginAccount LoginAccount
|
||||
if err := c.Bind(&loginAccount); err != nil {
|
||||
@ -43,15 +48,24 @@ func LoginEndpoint(c echo.Context) error {
|
||||
return Fail(c, -1, "您输入的账号或密码不正确")
|
||||
}
|
||||
|
||||
log.Println(user, loginAccount)
|
||||
|
||||
if !totp.Validate(loginAccount.TOTP, user.TOTPSecret) {
|
||||
return Fail(c, -2, "您的TOTP不匹配")
|
||||
}
|
||||
|
||||
token := utils.UUID()
|
||||
|
||||
global.Cache.Set(token, user, time.Minute*time.Duration(30))
|
||||
authorization := Authorization{
|
||||
Token: token,
|
||||
Remember: loginAccount.Remember,
|
||||
User: user,
|
||||
}
|
||||
|
||||
if authorization.Remember {
|
||||
// 记住登录有效期两周
|
||||
global.Cache.Set(token, authorization, time.Hour*time.Duration(24*14))
|
||||
} else {
|
||||
global.Cache.Set(token, authorization, time.Hour*time.Duration(2))
|
||||
}
|
||||
|
||||
model.UpdateUserById(&model.User{Online: true}, user.ID)
|
||||
|
||||
|
@ -112,11 +112,8 @@ func AssetTcpingEndpoint(c echo.Context) (err error) {
|
||||
}
|
||||
|
||||
active := utils.Tcping(item.IP, item.Port)
|
||||
asset := model.Asset{
|
||||
Active: active,
|
||||
}
|
||||
|
||||
model.UpdateAssetById(&asset, item.ID)
|
||||
model.UpdateAssetActiveById(active, item.ID)
|
||||
return Success(c, active)
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,31 @@ func CredentialCreateEndpoint(c echo.Context) error {
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
|
||||
switch item.Type {
|
||||
case model.Custom:
|
||||
item.PrivateKey = "-"
|
||||
item.Passphrase = "-"
|
||||
if len(item.Username) == 0 {
|
||||
item.Username = "-"
|
||||
}
|
||||
if len(item.Password) == 0 {
|
||||
item.Password = "-"
|
||||
}
|
||||
case model.PrivateKey:
|
||||
item.Password = "-"
|
||||
if len(item.Username) == 0 {
|
||||
item.Username = "-"
|
||||
}
|
||||
if len(item.PrivateKey) == 0 {
|
||||
item.PrivateKey = "-"
|
||||
}
|
||||
if len(item.Passphrase) == 0 {
|
||||
item.Passphrase = "-"
|
||||
}
|
||||
default:
|
||||
return Fail(c, -1, "类型错误")
|
||||
}
|
||||
|
||||
if err := model.CreateNewCredential(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -49,6 +74,31 @@ func CredentialUpdateEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
switch item.Type {
|
||||
case model.Custom:
|
||||
item.PrivateKey = "-"
|
||||
item.Passphrase = "-"
|
||||
if len(item.Username) == 0 {
|
||||
item.Username = "-"
|
||||
}
|
||||
if len(item.Password) == 0 {
|
||||
item.Password = "-"
|
||||
}
|
||||
case model.PrivateKey:
|
||||
item.Password = "-"
|
||||
if len(item.Username) == 0 {
|
||||
item.Username = "-"
|
||||
}
|
||||
if len(item.PrivateKey) == 0 {
|
||||
item.PrivateKey = "-"
|
||||
}
|
||||
if len(item.Passphrase) == 0 {
|
||||
item.Passphrase = "-"
|
||||
}
|
||||
default:
|
||||
return Fail(c, -1, "类型错误")
|
||||
}
|
||||
|
||||
model.UpdateCredentialById(&item, id)
|
||||
|
||||
return Success(c, nil)
|
||||
|
@ -34,12 +34,19 @@ func Auth(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
}
|
||||
|
||||
token := GetToken(c)
|
||||
user, found := global.Cache.Get(token)
|
||||
authorization, found := global.Cache.Get(token)
|
||||
if !found {
|
||||
logrus.Debugf("您的登录信息已失效,请重新登录后再试。")
|
||||
return Fail(c, 403, "您的登录信息已失效,请重新登录后再试。")
|
||||
}
|
||||
global.Cache.Set(token, user, time.Minute*time.Duration(30))
|
||||
|
||||
if authorization.(Authorization).Remember {
|
||||
// 记住登录有效期两周
|
||||
global.Cache.Set(token, authorization, time.Hour*time.Duration(24*14))
|
||||
} else {
|
||||
global.Cache.Set(token, authorization, time.Hour*time.Duration(2))
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ func GetCurrentAccount(c echo.Context) (model.User, bool) {
|
||||
token := GetToken(c)
|
||||
get, b := global.Cache.Get(token)
|
||||
if b {
|
||||
return get.(model.User), true
|
||||
return get.(Authorization).User, true
|
||||
}
|
||||
return model.User{}, false
|
||||
}
|
||||
|
@ -185,8 +185,14 @@ func SessionCreateEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
session.Username = credential.Username
|
||||
session.Password = credential.Password
|
||||
if credential.Type == model.Custom {
|
||||
session.Username = credential.Username
|
||||
session.Password = credential.Password
|
||||
} else {
|
||||
session.Username = credential.Username
|
||||
session.PrivateKey = credential.PrivateKey
|
||||
session.Passphrase = credential.Passphrase
|
||||
}
|
||||
}
|
||||
|
||||
if err := model.CreateNewSession(session); err != nil {
|
||||
@ -223,12 +229,11 @@ func SessionUploadEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
dstFile, err := tun.SftpClient.Create(remoteFile)
|
||||
defer dstFile.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer dstFile.Close()
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, _ := src.Read(buf)
|
||||
@ -282,7 +287,9 @@ func SessionDownloadEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
defer dstFile.Close()
|
||||
c.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", remoteFile))
|
||||
// 获取带后缀的文件名称
|
||||
filenameWithSuffix := path.Base(remoteFile)
|
||||
c.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filenameWithSuffix))
|
||||
|
||||
var buff bytes.Buffer
|
||||
if _, err := dstFile.WriteTo(&buff); err != nil {
|
||||
|
@ -49,6 +49,7 @@ func (w *NextWriter) Read() ([]byte, int, error) {
|
||||
func SSHEndpoint(c echo.Context) error {
|
||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||
if err != nil {
|
||||
logrus.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
@ -58,11 +59,13 @@ func SSHEndpoint(c echo.Context) error {
|
||||
|
||||
sshClient, err := CreateSshClient(assetId)
|
||||
if err != nil {
|
||||
logrus.Errorf("创建SSH客户端失败:%v", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
session, err := sshClient.NewSession()
|
||||
if err != nil {
|
||||
logrus.Errorf("创建SSH会话失败:%v", err.Error())
|
||||
return err
|
||||
}
|
||||
defer session.Close()
|
||||
@ -123,36 +126,62 @@ func CreateSshClient(assetId string) (*ssh.Client, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
accountType = asset.AccountType
|
||||
username = asset.Username
|
||||
password = asset.Password
|
||||
privateKey = asset.PrivateKey
|
||||
passphrase = asset.Passphrase
|
||||
)
|
||||
|
||||
var authMethod ssh.AuthMethod
|
||||
if asset.AccountType == "credential" {
|
||||
if accountType == "credential" {
|
||||
|
||||
credential, err := model.FindCredentialById(asset.CredentialId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
asset.Username = credential.Username
|
||||
asset.Password = credential.Password
|
||||
authMethod = ssh.Password(asset.Password)
|
||||
} else if asset.AccountType == "private-key" {
|
||||
accountType = credential.Type
|
||||
username = credential.Username
|
||||
password = credential.Password
|
||||
privateKey = credential.PrivateKey
|
||||
passphrase = credential.Passphrase
|
||||
}
|
||||
|
||||
if username == "-" {
|
||||
username = ""
|
||||
}
|
||||
if password == "-" {
|
||||
password = ""
|
||||
}
|
||||
if privateKey == "-" {
|
||||
privateKey = ""
|
||||
}
|
||||
if passphrase == "-" {
|
||||
passphrase = ""
|
||||
}
|
||||
|
||||
if accountType == model.PrivateKey {
|
||||
var key ssh.Signer
|
||||
if len(asset.Passphrase) > 0 {
|
||||
key, err = ssh.ParsePrivateKeyWithPassphrase([]byte(asset.PrivateKey), []byte(asset.Passphrase))
|
||||
if len(passphrase) > 0 {
|
||||
key, err = ssh.ParsePrivateKeyWithPassphrase([]byte(privateKey), []byte(passphrase))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
key, err = ssh.ParsePrivateKey([]byte(asset.PrivateKey))
|
||||
key, err = ssh.ParsePrivateKey([]byte(privateKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
authMethod = ssh.PublicKeys(key)
|
||||
} else {
|
||||
authMethod = ssh.Password(asset.Password)
|
||||
authMethod = ssh.Password(password)
|
||||
}
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
Timeout: 1 * time.Second,
|
||||
User: asset.Username,
|
||||
User: username,
|
||||
Auth: []ssh.AuthMethod{authMethod},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ func TunEndpoint(c echo.Context) error {
|
||||
|
||||
width := c.QueryParam("width")
|
||||
height := c.QueryParam("height")
|
||||
dpi := c.QueryParam("dpi")
|
||||
sessionId := c.QueryParam("sessionId")
|
||||
connectionId := c.QueryParam("connectionId")
|
||||
|
||||
@ -38,6 +39,7 @@ func TunEndpoint(c echo.Context) error {
|
||||
configuration := guacd.NewConfiguration()
|
||||
configuration.SetParameter("width", width)
|
||||
configuration.SetParameter("height", height)
|
||||
configuration.SetParameter("dpi", dpi)
|
||||
|
||||
propertyMap := model.FindAllPropertiesMap()
|
||||
|
||||
@ -73,8 +75,6 @@ func TunEndpoint(c echo.Context) error {
|
||||
configuration.SetParameter("security", "any")
|
||||
configuration.SetParameter("ignore-cert", "true")
|
||||
configuration.SetParameter("create-drive-path", "true")
|
||||
|
||||
configuration.SetParameter("dpi", "96")
|
||||
configuration.SetParameter("resize-method", "reconnect")
|
||||
configuration.SetParameter(guacd.EnableDrive, propertyMap[guacd.EnableDrive])
|
||||
configuration.SetParameter(guacd.DriveName, propertyMap[guacd.DriveName])
|
||||
@ -99,9 +99,7 @@ func TunEndpoint(c echo.Context) error {
|
||||
configuration.SetParameter("password", session.Password)
|
||||
}
|
||||
|
||||
fontSize, _ := strconv.Atoi(propertyMap[guacd.FontSize])
|
||||
fontSize = fontSize * 2
|
||||
configuration.SetParameter(guacd.FontSize, strconv.Itoa(fontSize))
|
||||
configuration.SetParameter(guacd.FontSize, propertyMap[guacd.FontSize])
|
||||
configuration.SetParameter(guacd.FontName, propertyMap[guacd.FontName])
|
||||
configuration.SetParameter(guacd.ColorScheme, propertyMap[guacd.ColorScheme])
|
||||
break
|
||||
|
@ -134,8 +134,10 @@ func NewTunnel(address string, config Configuration) (ret *Tunnel, err error) {
|
||||
|
||||
width := config.GetParameter("width")
|
||||
height := config.GetParameter("height")
|
||||
dpi := config.GetParameter("dpi")
|
||||
|
||||
// send size
|
||||
if err := ret.WriteInstructionAndFlush(NewInstruction("size", width, height, "96")); err != nil {
|
||||
if err := ret.WriteInstructionAndFlush(NewInstruction("size", width, height, dpi)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
|
||||
func RunTicker() {
|
||||
var ch chan int
|
||||
//定时任务
|
||||
// 定时任务,每隔一小时删除一次未使用的会话信息
|
||||
ticker := time.NewTicker(time.Minute * 60)
|
||||
go func() {
|
||||
for range ticker.C {
|
||||
|
@ -85,6 +85,11 @@ func UpdateAssetById(o *Asset, id string) {
|
||||
global.DB.Updates(o)
|
||||
}
|
||||
|
||||
func UpdateAssetActiveById(active bool, id string) {
|
||||
sql := "update assets set active = ? where id = ?"
|
||||
global.DB.Exec(sql, active, id)
|
||||
}
|
||||
|
||||
func DeleteAssetById(id string) {
|
||||
global.DB.Where("id = ?", id).Delete(&Asset{})
|
||||
}
|
||||
|
@ -5,12 +5,21 @@ import (
|
||||
"next-terminal/pkg/utils"
|
||||
)
|
||||
|
||||
// 密码
|
||||
const Custom = "custom"
|
||||
|
||||
// 密钥
|
||||
const PrivateKey = "private-key"
|
||||
|
||||
type Credential struct {
|
||||
ID string `gorm:"primary_key" json:"id"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
ID string `gorm:"primary_key" json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Passphrase string `json:"passphrase"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
}
|
||||
|
||||
func (r *Credential) TableName() string {
|
||||
|
Reference in New Issue
Block a user