修复 「1.2.2 用户管理-用户列表勾选单一用户会全选 」 close #216
This commit is contained in:
parent
29c066ca3a
commit
d35b348a33
11
.gitignore
vendored
11
.gitignore
vendored
@ -21,13 +21,6 @@ web/build
|
|||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
|
||||||
# playground
|
|
||||||
playground/data
|
|
||||||
playground/drive
|
|
||||||
playground/recording
|
|
||||||
|
|
||||||
/log
|
|
||||||
|
|
||||||
|
|
||||||
# next terminal
|
# next terminal
|
||||||
/recording
|
/data/
|
||||||
|
/logs/
|
||||||
|
@ -21,7 +21,6 @@ LABEL MAINTAINER="helloworld1024@foxmail.com"
|
|||||||
|
|
||||||
ENV TZ Asia/Shanghai
|
ENV TZ Asia/Shanghai
|
||||||
ENV DB sqlite
|
ENV DB sqlite
|
||||||
ENV CONTAINER "true"
|
|
||||||
ENV SQLITE_FILE './data/sqlite/next-terminal.db'
|
ENV SQLITE_FILE './data/sqlite/next-terminal.db'
|
||||||
ENV SERVER_PORT 8088
|
ENV SERVER_PORT 8088
|
||||||
ENV SERVER_ADDR 0.0.0.0:$SERVER_PORT
|
ENV SERVER_ADDR 0.0.0.0:$SERVER_PORT
|
||||||
|
4
go.mod
4
go.mod
@ -21,6 +21,7 @@ require (
|
|||||||
github.com/spf13/viper v1.7.1
|
github.com/spf13/viper v1.7.1
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/stretchr/testify v1.6.1
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||||
golang.org/x/text v0.3.6
|
golang.org/x/text v0.3.6
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
gorm.io/driver/mysql v1.0.3
|
gorm.io/driver/mysql v1.0.3
|
||||||
@ -29,12 +30,14 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.7 // indirect
|
github.com/fsnotify/fsnotify v1.4.7 // indirect
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
@ -56,7 +59,6 @@ require (
|
|||||||
github.com/subosito/gotenv v1.2.0 // indirect
|
github.com/subosito/gotenv v1.2.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
|
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
|
||||||
gopkg.in/ini.v1 v1.51.0 // indirect
|
gopkg.in/ini.v1 v1.51.0 // indirect
|
||||||
|
9
go.sum
9
go.sum
@ -11,6 +11,8 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl
|
|||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
@ -44,6 +46,8 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||||
|
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
@ -53,8 +57,12 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
|||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gliderlabs/ssh v0.3.3 h1:mBQ8NiOgDkINJrZtoizkC3nDNYgSaWtxyem6S2XHBtA=
|
github.com/gliderlabs/ssh v0.3.3 h1:mBQ8NiOgDkINJrZtoizkC3nDNYgSaWtxyem6S2XHBtA=
|
||||||
github.com/gliderlabs/ssh v0.3.3/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914=
|
github.com/gliderlabs/ssh v0.3.3/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.1 h1:fU/0xli6HY02ocbMuozHAYsaHLcnkLjvho2r5a34BUU=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.1/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
@ -250,6 +258,7 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
|
||||||
|
52
main.go
52
main.go
@ -1,62 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"next-terminal/server/app"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"next-terminal/server/api"
|
|
||||||
"next-terminal/server/config"
|
|
||||||
"next-terminal/server/constant"
|
|
||||||
"next-terminal/server/repository"
|
|
||||||
"next-terminal/server/task"
|
|
||||||
|
|
||||||
"github.com/labstack/gommon/log"
|
"github.com/labstack/gommon/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := Run()
|
err := app.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run() error {
|
|
||||||
|
|
||||||
fmt.Printf(constant.Banner, constant.Version)
|
|
||||||
|
|
||||||
if config.GlobalCfg.Debug {
|
|
||||||
jsonBytes, err := json.MarshalIndent(config.GlobalCfg, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("当前配置为: %v\n", string(jsonBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
db := api.SetupDB()
|
|
||||||
e := api.SetupRoutes(db)
|
|
||||||
|
|
||||||
if config.GlobalCfg.ResetPassword != "" {
|
|
||||||
return api.ResetPassword(config.GlobalCfg.ResetPassword)
|
|
||||||
}
|
|
||||||
if config.GlobalCfg.ResetTotp != "" {
|
|
||||||
return api.ResetTotp(config.GlobalCfg.ResetTotp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.GlobalCfg.NewEncryptionKey != "" {
|
|
||||||
return api.ChangeEncryptionKey(config.GlobalCfg.EncryptionKey, config.GlobalCfg.NewEncryptionKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionRepo := repository.NewSessionRepository(db)
|
|
||||||
propertyRepo := repository.NewPropertyRepository(db)
|
|
||||||
loginLogRepo := repository.NewLoginLogRepository(db)
|
|
||||||
jobLogRepo := repository.NewJobLogRepository(db)
|
|
||||||
ticker := task.NewTicker(sessionRepo, propertyRepo, loginLogRepo, jobLogRepo)
|
|
||||||
ticker.SetupTicker()
|
|
||||||
|
|
||||||
if config.GlobalCfg.Server.Cert != "" && config.GlobalCfg.Server.Key != "" {
|
|
||||||
return e.StartTLS(config.GlobalCfg.Server.Addr, config.GlobalCfg.Server.Cert, config.GlobalCfg.Server.Key)
|
|
||||||
} else {
|
|
||||||
return e.Start(config.GlobalCfg.Server.Addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -1,49 +1,30 @@
|
|||||||
version: '3.3'
|
version: '3.3'
|
||||||
services:
|
services:
|
||||||
|
guacd:
|
||||||
|
image: dushixiang/guacd:latest
|
||||||
|
volumes:
|
||||||
|
- ../data:/usr/local/next-terminal/data
|
||||||
|
ports:
|
||||||
|
- "4822:4822"
|
||||||
|
restart:
|
||||||
|
always
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql:8.0
|
image: mysql:8.0
|
||||||
container_name: mysql
|
|
||||||
environment:
|
environment:
|
||||||
MYSQL_DATABASE: next-terminal
|
MYSQL_DATABASE: next-terminal
|
||||||
MYSQL_USER: next-terminal
|
MYSQL_USER: next-terminal
|
||||||
MYSQL_PASSWORD: next-terminal
|
MYSQL_PASSWORD: next-terminal
|
||||||
MYSQL_ROOT_PASSWORD: next-terminal
|
MYSQL_ROOT_PASSWORD: next-terminal
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/mysql_data:/var/lib/mysql
|
- ../data/mysql:/var/lib/mysql
|
||||||
ports:
|
ports:
|
||||||
- "3306:3306"
|
- "3306:3306"
|
||||||
restart:
|
restart:
|
||||||
always
|
always
|
||||||
networks:
|
|
||||||
next-terminal:
|
|
||||||
ipv4_address: 172.77.77.2
|
|
||||||
|
|
||||||
# next-terminal:
|
|
||||||
# container_name: next-terminal
|
|
||||||
# image: "dushixiang/next-terminal:latest"
|
|
||||||
# environment:
|
|
||||||
# DB: "mysql"
|
|
||||||
# MYSQL_HOSTNAME: "mysql"
|
|
||||||
# MYSQL_PORT: 3306
|
|
||||||
# MYSQL_USERNAME: "next-terminal"
|
|
||||||
# MYSQL_PASSWORD: "next-terminal"
|
|
||||||
# MYSQL_DATABASE: "next-terminal"
|
|
||||||
# ports:
|
|
||||||
# - "8088:8088"
|
|
||||||
# volumes:
|
|
||||||
# - ./drive:/usr/local/next-terminal/drive
|
|
||||||
# - ./recording:/usr/local/next-terminal/recording
|
|
||||||
# depends_on:
|
|
||||||
# - mysql
|
|
||||||
# networks:
|
|
||||||
# next-terminal:
|
|
||||||
# ipv4_address: 172.77.77.3
|
|
||||||
# restart:
|
|
||||||
# always
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
next-terminal:
|
next-terminal:
|
||||||
ipam:
|
ipam:
|
||||||
driver: default
|
driver: default
|
||||||
config:
|
config:
|
||||||
- subnet: "172.77.77.0/24"
|
- subnet: 172.77.77.0/24
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AccessGatewayCreateEndpoint(c echo.Context) error {
|
type AccessGatewayApi struct{}
|
||||||
|
|
||||||
|
func (api AccessGatewayApi) AccessGatewayCreateEndpoint(c echo.Context) error {
|
||||||
var item model.AccessGateway
|
var item model.AccessGateway
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -19,16 +24,16 @@ func AccessGatewayCreateEndpoint(c echo.Context) error {
|
|||||||
item.ID = utils.UUID()
|
item.ID = utils.UUID()
|
||||||
item.Created = utils.NowJsonTime()
|
item.Created = utils.NowJsonTime()
|
||||||
|
|
||||||
if err := accessGatewayRepository.Create(&item); err != nil {
|
if err := repository.GatewayRepository.Create(context.TODO(), &item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// 连接网关
|
// 连接网关
|
||||||
accessGatewayService.ReConnect(&item)
|
service.GatewayService.ReConnect(&item)
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccessGatewayAllEndpoint(c echo.Context) error {
|
func (api AccessGatewayApi) AccessGatewayAllEndpoint(c echo.Context) error {
|
||||||
gateways, err := accessGatewayRepository.FindAll()
|
gateways, err := repository.GatewayRepository.FindAll(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -39,7 +44,7 @@ func AccessGatewayAllEndpoint(c echo.Context) error {
|
|||||||
return Success(c, simpleGateways)
|
return Success(c, simpleGateways)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccessGatewayPagingEndpoint(c echo.Context) error {
|
func (api AccessGatewayApi) AccessGatewayPagingEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
ip := c.QueryParam("ip")
|
ip := c.QueryParam("ip")
|
||||||
@ -48,12 +53,12 @@ func AccessGatewayPagingEndpoint(c echo.Context) error {
|
|||||||
order := c.QueryParam("order")
|
order := c.QueryParam("order")
|
||||||
field := c.QueryParam("field")
|
field := c.QueryParam("field")
|
||||||
|
|
||||||
items, total, err := accessGatewayRepository.Find(pageIndex, pageSize, ip, name, order, field)
|
items, total, err := repository.GatewayRepository.Find(context.TODO(), pageIndex, pageSize, ip, name, order, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i := 0; i < len(items); i++ {
|
for i := 0; i < len(items); i++ {
|
||||||
g, err := accessGatewayService.GetGatewayById(items[i].ID)
|
g, err := service.GatewayService.GetGatewayById(items[i].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -61,13 +66,13 @@ func AccessGatewayPagingEndpoint(c echo.Context) error {
|
|||||||
items[i].Message = g.Message
|
items[i].Message = g.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccessGatewayUpdateEndpoint(c echo.Context) error {
|
func (api AccessGatewayApi) AccessGatewayUpdateEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
var item model.AccessGateway
|
var item model.AccessGateway
|
||||||
@ -75,30 +80,30 @@ func AccessGatewayUpdateEndpoint(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := accessGatewayRepository.UpdateById(&item, id); err != nil {
|
if err := repository.GatewayRepository.UpdateById(context.TODO(), &item, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
accessGatewayService.ReConnect(&item)
|
service.GatewayService.ReConnect(&item)
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccessGatewayDeleteEndpoint(c echo.Context) error {
|
func (api AccessGatewayApi) AccessGatewayDeleteEndpoint(c echo.Context) error {
|
||||||
ids := c.Param("id")
|
ids := c.Param("id")
|
||||||
split := strings.Split(ids, ",")
|
split := strings.Split(ids, ",")
|
||||||
for i := range split {
|
for i := range split {
|
||||||
id := split[i]
|
id := split[i]
|
||||||
if err := accessGatewayRepository.DeleteById(id); err != nil {
|
if err := repository.GatewayRepository.DeleteById(context.TODO(), id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
accessGatewayService.DisconnectById(id)
|
service.GatewayService.DisconnectById(id)
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccessGatewayGetEndpoint(c echo.Context) error {
|
func (api AccessGatewayApi) AccessGatewayGetEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
item, err := accessGatewayRepository.FindById(id)
|
item, err := repository.GatewayRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -106,13 +111,13 @@ func AccessGatewayGetEndpoint(c echo.Context) error {
|
|||||||
return Success(c, item)
|
return Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccessGatewayReconnectEndpoint(c echo.Context) error {
|
func (api AccessGatewayApi) AccessGatewayReconnectEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
item, err := accessGatewayRepository.FindById(id)
|
item, err := repository.GatewayRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
accessGatewayService.ReConnect(&item)
|
service.GatewayService.ReConnect(&item)
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
@ -1,58 +1,34 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"next-terminal/server/constant"
|
"context"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"next-terminal/server/config"
|
"next-terminal/server/config"
|
||||||
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/dto"
|
||||||
"next-terminal/server/global/cache"
|
"next-terminal/server/global/cache"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
"next-terminal/server/totp"
|
"next-terminal/server/totp"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
type AccountApi struct{}
|
||||||
RememberEffectiveTime = time.Hour * time.Duration(24*14)
|
|
||||||
NotRememberEffectiveTime = time.Hour * time.Duration(2)
|
|
||||||
)
|
|
||||||
|
|
||||||
type LoginAccount struct {
|
func (api AccountApi) LoginEndpoint(c echo.Context) error {
|
||||||
Username string `json:"username"`
|
var loginAccount dto.LoginAccount
|
||||||
Password string `json:"password"`
|
|
||||||
Remember bool `json:"remember"`
|
|
||||||
TOTP string `json:"totp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfirmTOTP struct {
|
|
||||||
Secret string `json:"secret"`
|
|
||||||
TOTP string `json:"totp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChangePassword struct {
|
|
||||||
NewPassword string `json:"newPassword"`
|
|
||||||
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 {
|
if err := c.Bind(&loginAccount); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 存储登录失败次数信息
|
// 存储登录失败次数信息
|
||||||
loginFailCountKey := c.RealIP() + loginAccount.Username
|
loginFailCountKey := c.RealIP() + loginAccount.Username
|
||||||
v, ok := cache.GlobalCache.Get(loginFailCountKey)
|
v, ok := cache.LoginFailedKeyManager.Get(loginFailCountKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
v = 1
|
v = 1
|
||||||
}
|
}
|
||||||
@ -61,12 +37,12 @@ func LoginEndpoint(c echo.Context) error {
|
|||||||
return Fail(c, -1, "登录失败次数过多,请等待5分钟后再试")
|
return Fail(c, -1, "登录失败次数过多,请等待5分钟后再试")
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := userRepository.FindByUsername(loginAccount.Username)
|
user, err := repository.UserRepository.FindByUsername(context.TODO(), loginAccount.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
count++
|
count++
|
||||||
cache.GlobalCache.Set(loginFailCountKey, count, time.Minute*time.Duration(5))
|
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||||
// 保存登录日志
|
// 保存登录日志
|
||||||
if err := SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||||
@ -78,9 +54,9 @@ func LoginEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
if err := utils.Encoder.Match([]byte(user.Password), []byte(loginAccount.Password)); err != nil {
|
if err := utils.Encoder.Match([]byte(user.Password), []byte(loginAccount.Password)); err != nil {
|
||||||
count++
|
count++
|
||||||
cache.GlobalCache.Set(loginFailCountKey, count, time.Minute*time.Duration(5))
|
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||||
// 保存登录日志
|
// 保存登录日志
|
||||||
if err := SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||||
@ -90,73 +66,49 @@ func LoginEndpoint(c echo.Context) error {
|
|||||||
return Fail(c, 0, "")
|
return Fail(c, 0, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := LoginSuccess(loginAccount, user)
|
token, err := api.LoginSuccess(loginAccount, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// 保存登录日志
|
// 保存登录日志
|
||||||
if err := SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, true, loginAccount.Remember, token, ""); err != nil {
|
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, true, loginAccount.Remember, token, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, token)
|
return Success(c, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveLoginLog(clientIP, clientUserAgent string, username string, success, remember bool, id, reason string) error {
|
func (api AccountApi) LoginSuccess(loginAccount dto.LoginAccount, user model.User) (string, error) {
|
||||||
loginLog := model.LoginLog{
|
token := utils.LongUUID()
|
||||||
Username: username,
|
|
||||||
ClientIP: clientIP,
|
|
||||||
ClientUserAgent: clientUserAgent,
|
|
||||||
LoginTime: utils.NowJsonTime(),
|
|
||||||
Reason: reason,
|
|
||||||
Remember: remember,
|
|
||||||
}
|
|
||||||
if success {
|
|
||||||
loginLog.State = "1"
|
|
||||||
loginLog.ID = id
|
|
||||||
} else {
|
|
||||||
loginLog.State = "0"
|
|
||||||
loginLog.ID = utils.UUID()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := loginLogRepository.Create(&loginLog); err != nil {
|
authorization := dto.Authorization{
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoginSuccess(loginAccount LoginAccount, user model.User) (token string, err error) {
|
|
||||||
token = strings.Join([]string{utils.UUID(), utils.UUID(), utils.UUID(), utils.UUID()}, "")
|
|
||||||
|
|
||||||
authorization := Authorization{
|
|
||||||
Token: token,
|
Token: token,
|
||||||
|
Type: constant.LoginToken,
|
||||||
Remember: loginAccount.Remember,
|
Remember: loginAccount.Remember,
|
||||||
User: user,
|
User: &user,
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheKey := userService.BuildCacheKeyByToken(token)
|
|
||||||
|
|
||||||
if authorization.Remember {
|
if authorization.Remember {
|
||||||
// 记住登录有效期两周
|
// 记住登录有效期两周
|
||||||
cache.GlobalCache.Set(cacheKey, authorization, RememberEffectiveTime)
|
cache.TokenManager.Set(token, authorization, cache.RememberMeExpiration)
|
||||||
} else {
|
} else {
|
||||||
cache.GlobalCache.Set(cacheKey, authorization, NotRememberEffectiveTime)
|
cache.TokenManager.Set(token, authorization, cache.NotRememberExpiration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改登录状态
|
// 修改登录状态
|
||||||
err = userRepository.Update(&model.User{Online: true, ID: user.ID})
|
err := repository.UserRepository.Update(context.TODO(), &model.User{Online: true, ID: user.ID})
|
||||||
return token, err
|
return token, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func loginWithTotpEndpoint(c echo.Context) error {
|
func (api AccountApi) LoginWithTotpEndpoint(c echo.Context) error {
|
||||||
var loginAccount LoginAccount
|
var loginAccount dto.LoginAccount
|
||||||
if err := c.Bind(&loginAccount); err != nil {
|
if err := c.Bind(&loginAccount); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 存储登录失败次数信息
|
// 存储登录失败次数信息
|
||||||
loginFailCountKey := c.RealIP() + loginAccount.Username
|
loginFailCountKey := c.RealIP() + loginAccount.Username
|
||||||
v, ok := cache.GlobalCache.Get(loginFailCountKey)
|
v, ok := cache.LoginFailedKeyManager.Get(loginFailCountKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
v = 1
|
v = 1
|
||||||
}
|
}
|
||||||
@ -165,12 +117,12 @@ func loginWithTotpEndpoint(c echo.Context) error {
|
|||||||
return Fail(c, -1, "登录失败次数过多,请等待5分钟后再试")
|
return Fail(c, -1, "登录失败次数过多,请等待5分钟后再试")
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := userRepository.FindByUsername(loginAccount.Username)
|
user, err := repository.UserRepository.FindByUsername(context.TODO(), loginAccount.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
count++
|
count++
|
||||||
cache.GlobalCache.Set(loginFailCountKey, count, time.Minute*time.Duration(5))
|
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||||
// 保存登录日志
|
// 保存登录日志
|
||||||
if err := SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||||
@ -182,9 +134,9 @@ func loginWithTotpEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
if err := utils.Encoder.Match([]byte(user.Password), []byte(loginAccount.Password)); err != nil {
|
if err := utils.Encoder.Match([]byte(user.Password), []byte(loginAccount.Password)); err != nil {
|
||||||
count++
|
count++
|
||||||
cache.GlobalCache.Set(loginFailCountKey, count, time.Minute*time.Duration(5))
|
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||||
// 保存登录日志
|
// 保存登录日志
|
||||||
if err := SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||||
@ -192,42 +144,42 @@ func loginWithTotpEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
if !totp.Validate(loginAccount.TOTP, user.TOTPSecret) {
|
if !totp.Validate(loginAccount.TOTP, user.TOTPSecret) {
|
||||||
count++
|
count++
|
||||||
cache.GlobalCache.Set(loginFailCountKey, count, time.Minute*time.Duration(5))
|
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||||
// 保存登录日志
|
// 保存登录日志
|
||||||
if err := SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "双因素认证授权码不正确"); err != nil {
|
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "双因素认证授权码不正确"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return FailWithData(c, -1, "您输入双因素认证授权码不正确", count)
|
return FailWithData(c, -1, "您输入双因素认证授权码不正确", count)
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := LoginSuccess(loginAccount, user)
|
token, err := api.LoginSuccess(loginAccount, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// 保存登录日志
|
// 保存登录日志
|
||||||
if err := SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, true, loginAccount.Remember, token, ""); err != nil {
|
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, true, loginAccount.Remember, token, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, token)
|
return Success(c, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LogoutEndpoint(c echo.Context) error {
|
func (api AccountApi) LogoutEndpoint(c echo.Context) error {
|
||||||
token := GetToken(c)
|
token := GetToken(c)
|
||||||
err := userService.LogoutByToken(token)
|
err := service.UserService.LogoutByToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfirmTOTPEndpoint(c echo.Context) error {
|
func (api AccountApi) ConfirmTOTPEndpoint(c echo.Context) error {
|
||||||
if config.GlobalCfg.Demo {
|
if config.GlobalCfg.Demo {
|
||||||
return Fail(c, 0, "演示模式禁止开启两步验证")
|
return Fail(c, 0, "演示模式禁止开启两步验证")
|
||||||
}
|
}
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
|
|
||||||
var confirmTOTP ConfirmTOTP
|
var confirmTOTP dto.ConfirmTOTP
|
||||||
if err := c.Bind(&confirmTOTP); err != nil {
|
if err := c.Bind(&confirmTOTP); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -241,14 +193,14 @@ func ConfirmTOTPEndpoint(c echo.Context) error {
|
|||||||
ID: account.ID,
|
ID: account.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := userRepository.Update(u); err != nil {
|
if err := repository.UserRepository.Update(context.TODO(), u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReloadTOTPEndpoint(c echo.Context) error {
|
func (api AccountApi) ReloadTOTPEndpoint(c echo.Context) error {
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
|
|
||||||
key, err := totp.NewTOTP(totp.GenerateOpts{
|
key, err := totp.NewTOTP(totp.GenerateOpts{
|
||||||
@ -275,25 +227,25 @@ func ReloadTOTPEndpoint(c echo.Context) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResetTOTPEndpoint(c echo.Context) error {
|
func (api AccountApi) ResetTOTPEndpoint(c echo.Context) error {
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
u := &model.User{
|
u := &model.User{
|
||||||
TOTPSecret: "-",
|
TOTPSecret: "-",
|
||||||
ID: account.ID,
|
ID: account.ID,
|
||||||
}
|
}
|
||||||
if err := userRepository.Update(u); err != nil {
|
if err := repository.UserRepository.Update(context.TODO(), u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChangePasswordEndpoint(c echo.Context) error {
|
func (api AccountApi) ChangePasswordEndpoint(c echo.Context) error {
|
||||||
if config.GlobalCfg.Demo {
|
if config.GlobalCfg.Demo {
|
||||||
return Fail(c, 0, "演示模式禁止修改密码")
|
return Fail(c, 0, "演示模式禁止修改密码")
|
||||||
}
|
}
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
|
|
||||||
var changePassword ChangePassword
|
var changePassword dto.ChangePassword
|
||||||
if err := c.Bind(&changePassword); err != nil {
|
if err := c.Bind(&changePassword); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -311,11 +263,11 @@ func ChangePasswordEndpoint(c echo.Context) error {
|
|||||||
ID: account.ID,
|
ID: account.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := userRepository.Update(u); err != nil {
|
if err := repository.UserRepository.Update(context.TODO(), u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return LogoutEndpoint(c)
|
return api.LogoutEndpoint(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccountInfo struct {
|
type AccountInfo struct {
|
||||||
@ -326,10 +278,10 @@ type AccountInfo struct {
|
|||||||
EnableTotp bool `json:"enableTotp"`
|
EnableTotp bool `json:"enableTotp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func InfoEndpoint(c echo.Context) error {
|
func (api AccountApi) InfoEndpoint(c echo.Context) error {
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
|
|
||||||
user, err := userRepository.FindById(account.ID)
|
user, err := repository.UserRepository.FindById(context.TODO(), account.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -344,7 +296,7 @@ func InfoEndpoint(c echo.Context) error {
|
|||||||
return Success(c, info)
|
return Success(c, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccountAssetEndpoint(c echo.Context) error {
|
func (api AccountApi) AccountAssetEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
name := c.QueryParam("name")
|
name := c.QueryParam("name")
|
||||||
@ -359,26 +311,26 @@ func AccountAssetEndpoint(c echo.Context) error {
|
|||||||
field := c.QueryParam("field")
|
field := c.QueryParam("field")
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
|
|
||||||
items, total, err := assetRepository.Find(pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId, ip, order, field)
|
items, total, err := repository.AssetRepository.Find(context.TODO(), pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId, ip, order, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccountStorageEndpoint(c echo.Context) error {
|
func (api AccountApi) AccountStorageEndpoint(c echo.Context) error {
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
storageId := account.ID
|
storageId := account.ID
|
||||||
storage, err := storageRepository.FindById(storageId)
|
storage, err := repository.StorageRepository.FindById(context.TODO(), storageId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
structMap := utils.StructToMap(storage)
|
structMap := utils.StructToMap(storage)
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
drivePath := service.StorageService.GetBaseDrivePath()
|
||||||
dirSize, err := utils.DirSize(path.Join(drivePath, storageId))
|
dirSize, err := utils.DirSize(path.Join(drivePath, storageId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
structMap["usedSize"] = -1
|
structMap["usedSize"] = -1
|
||||||
@ -388,3 +340,20 @@ func AccountStorageEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
return Success(c, structMap)
|
return Success(c, structMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api AccountApi) AccessTokenGetEndpoint(c echo.Context) error {
|
||||||
|
account, _ := GetCurrentAccount(c)
|
||||||
|
accessToken, err := repository.AccessTokenRepository.FindByUserId(context.TODO(), account.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Success(c, accessToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api AccountApi) AccessTokenGenEndpoint(c echo.Context) error {
|
||||||
|
account, _ := GetCurrentAccount(c)
|
||||||
|
if err := service.AccessTokenService.GenAccessToken(account.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Success(c, nil)
|
||||||
|
}
|
||||||
|
@ -2,23 +2,24 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/dto"
|
||||||
"next-terminal/server/global/cache"
|
"next-terminal/server/global/cache"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type H map[string]interface{}
|
type Map map[string]interface{}
|
||||||
|
|
||||||
func Fail(c echo.Context, code int, message string) error {
|
func Fail(c echo.Context, code int, message string) error {
|
||||||
return c.JSON(200, H{
|
return c.JSON(200, Map{
|
||||||
"code": code,
|
"code": code,
|
||||||
"message": message,
|
"message": message,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func FailWithData(c echo.Context, code int, message string, data interface{}) error {
|
func FailWithData(c echo.Context, code int, message string, data interface{}) error {
|
||||||
return c.JSON(200, H{
|
return c.JSON(200, Map{
|
||||||
"code": code,
|
"code": code,
|
||||||
"message": message,
|
"message": message,
|
||||||
"data": data,
|
"data": data,
|
||||||
@ -26,20 +27,13 @@ func FailWithData(c echo.Context, code int, message string, data interface{}) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Success(c echo.Context, data interface{}) error {
|
func Success(c echo.Context, data interface{}) error {
|
||||||
return c.JSON(200, H{
|
return c.JSON(200, Map{
|
||||||
"code": 1,
|
"code": 1,
|
||||||
"message": "success",
|
"message": "success",
|
||||||
"data": data,
|
"data": data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotFound(c echo.Context, message string) error {
|
|
||||||
return c.JSON(200, H{
|
|
||||||
"code": -1,
|
|
||||||
"message": message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetToken(c echo.Context) string {
|
func GetToken(c echo.Context) string {
|
||||||
token := c.Request().Header.Get(constant.Token)
|
token := c.Request().Header.Get(constant.Token)
|
||||||
if len(token) > 0 {
|
if len(token) > 0 {
|
||||||
@ -48,14 +42,13 @@ func GetToken(c echo.Context) string {
|
|||||||
return c.QueryParam(constant.Token)
|
return c.QueryParam(constant.Token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCurrentAccount(c echo.Context) (model.User, bool) {
|
func GetCurrentAccount(c echo.Context) (*model.User, bool) {
|
||||||
token := GetToken(c)
|
token := GetToken(c)
|
||||||
cacheKey := userService.BuildCacheKeyByToken(token)
|
get, b := cache.TokenManager.Get(token)
|
||||||
get, b := cache.GlobalCache.Get(cacheKey)
|
|
||||||
if b {
|
if b {
|
||||||
return get.(Authorization).User, true
|
return get.(dto.Authorization).User, true
|
||||||
}
|
}
|
||||||
return model.User{}, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func HasPermission(c echo.Context, owner string) bool {
|
func HasPermission(c echo.Context, owner string) bool {
|
||||||
|
@ -2,58 +2,40 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"context"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"next-terminal/server/config"
|
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AssetCreateEndpoint(c echo.Context) error {
|
type AssetApi struct{}
|
||||||
|
|
||||||
|
func (assetApi AssetApi) AssetCreateEndpoint(c echo.Context) error {
|
||||||
m := echo.Map{}
|
m := echo.Map{}
|
||||||
if err := c.Bind(&m); err != nil {
|
if err := c.Bind(&m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, _ := json.Marshal(m)
|
|
||||||
var item model.Asset
|
|
||||||
if err := json.Unmarshal(data, &item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
item.Owner = account.ID
|
m["owner"] = account.ID
|
||||||
item.ID = utils.UUID()
|
|
||||||
item.Created = utils.NowJsonTime()
|
|
||||||
item.Active = true
|
|
||||||
|
|
||||||
if err := assetRepository.Create(&item); err != nil {
|
if _, err := service.AssetService.Create(m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := assetRepository.UpdateAttributes(item.ID, item.Protocol, m); err != nil {
|
return Success(c, nil)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
func (assetApi AssetApi) AssetImportEndpoint(c echo.Context) error {
|
||||||
active, _ := assetService.CheckStatus(item.AccessGatewayId, item.IP, item.Port)
|
|
||||||
|
|
||||||
if item.Active != active {
|
|
||||||
_ = assetRepository.UpdateActiveById(active, item.ID)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return Success(c, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
func AssetImportEndpoint(c echo.Context) error {
|
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
|
|
||||||
file, err := c.FormFile("file")
|
file, err := c.FormFile("file")
|
||||||
@ -66,7 +48,9 @@ func AssetImportEndpoint(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer src.Close()
|
defer func() {
|
||||||
|
_ = src.Close()
|
||||||
|
}()
|
||||||
reader := csv.NewReader(bufio.NewReader(src))
|
reader := csv.NewReader(bufio.NewReader(src))
|
||||||
records, err := reader.ReadAll()
|
records, err := reader.ReadAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -107,7 +91,7 @@ func AssetImportEndpoint(c echo.Context) error {
|
|||||||
asset.Tags = tags
|
asset.Tags = tags
|
||||||
}
|
}
|
||||||
|
|
||||||
err := assetRepository.Create(&asset)
|
err := repository.AssetRepository.Create(context.TODO(), &asset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorCount++
|
errorCount++
|
||||||
m[strconv.Itoa(i)] = err.Error()
|
m[strconv.Itoa(i)] = err.Error()
|
||||||
@ -124,7 +108,7 @@ func AssetImportEndpoint(c echo.Context) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssetPagingEndpoint(c echo.Context) error {
|
func (assetApi AssetApi) AssetPagingEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
name := c.QueryParam("name")
|
name := c.QueryParam("name")
|
||||||
@ -140,26 +124,26 @@ func AssetPagingEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
|
|
||||||
items, total, err := assetRepository.Find(pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId, ip, order, field)
|
items, total, err := repository.AssetRepository.Find(context.TODO(), pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId, ip, order, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssetAllEndpoint(c echo.Context) error {
|
func (assetApi AssetApi) AssetAllEndpoint(c echo.Context) error {
|
||||||
protocol := c.QueryParam("protocol")
|
protocol := c.QueryParam("protocol")
|
||||||
items, _ := assetRepository.FindByProtocol(protocol)
|
items, _ := repository.AssetRepository.FindByProtocol(context.TODO(), protocol)
|
||||||
return Success(c, items)
|
return Success(c, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssetUpdateEndpoint(c echo.Context) error {
|
func (assetApi AssetApi) AssetUpdateEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
if err := PreCheckAssetPermission(c, id); err != nil {
|
if err := assetApi.PreCheckAssetPermission(c, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,67 +151,20 @@ func AssetUpdateEndpoint(c echo.Context) error {
|
|||||||
if err := c.Bind(&m); err != nil {
|
if err := c.Bind(&m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := service.AssetService.UpdateById(id, m); err != nil {
|
||||||
data, _ := json.Marshal(m)
|
|
||||||
var item model.Asset
|
|
||||||
if err := json.Unmarshal(data, &item); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch item.AccountType {
|
|
||||||
case "credential":
|
|
||||||
item.Username = "-"
|
|
||||||
item.Password = "-"
|
|
||||||
item.PrivateKey = "-"
|
|
||||||
item.Passphrase = "-"
|
|
||||||
case "private-key":
|
|
||||||
item.Password = "-"
|
|
||||||
item.CredentialId = "-"
|
|
||||||
if len(item.Username) == 0 {
|
|
||||||
item.Username = "-"
|
|
||||||
}
|
|
||||||
if len(item.Passphrase) == 0 {
|
|
||||||
item.Passphrase = "-"
|
|
||||||
}
|
|
||||||
case "custom":
|
|
||||||
item.PrivateKey = "-"
|
|
||||||
item.Passphrase = "-"
|
|
||||||
item.CredentialId = "-"
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(item.Tags) == 0 {
|
|
||||||
item.Tags = "-"
|
|
||||||
}
|
|
||||||
|
|
||||||
if item.Description == "" {
|
|
||||||
item.Description = "-"
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := assetRepository.Encrypt(&item, config.GlobalCfg.EncryptionPassword); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := assetRepository.UpdateById(&item, id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := assetRepository.UpdateAttributes(id, item.Protocol, m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssetDeleteEndpoint(c echo.Context) error {
|
func (assetApi AssetApi) AssetDeleteEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
split := strings.Split(id, ",")
|
split := strings.Split(id, ",")
|
||||||
for i := range split {
|
for i := range split {
|
||||||
if err := PreCheckAssetPermission(c, split[i]); err != nil {
|
if err := assetApi.PreCheckAssetPermission(c, split[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := assetRepository.DeleteById(split[i]); err != nil {
|
if err := service.AssetService.DeleteById(split[i]); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 删除资产与用户的关系
|
|
||||||
if err := resourceSharerRepository.DeleteResourceSharerByResourceId(split[i]); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,17 +172,17 @@ func AssetDeleteEndpoint(c echo.Context) error {
|
|||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssetGetEndpoint(c echo.Context) (err error) {
|
func (assetApi AssetApi) AssetGetEndpoint(c echo.Context) (err error) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
if err := PreCheckAssetPermission(c, id); err != nil {
|
if err := assetApi.PreCheckAssetPermission(c, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var item model.Asset
|
var item model.Asset
|
||||||
if item, err = assetRepository.FindByIdAndDecrypt(id); err != nil {
|
if item, err = service.AssetService.FindByIdAndDecrypt(context.TODO(), id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
attributeMap, err := assetRepository.FindAssetAttrMapByAssetId(id)
|
attributeMap, err := repository.AssetRepository.FindAssetAttrMapByAssetId(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -257,18 +194,18 @@ func AssetGetEndpoint(c echo.Context) (err error) {
|
|||||||
return Success(c, itemMap)
|
return Success(c, itemMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssetTcpingEndpoint(c echo.Context) (err error) {
|
func (assetApi AssetApi) AssetTcpingEndpoint(c echo.Context) (err error) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
var item model.Asset
|
var item model.Asset
|
||||||
if item, err = assetRepository.FindById(id); err != nil {
|
if item, err = repository.AssetRepository.FindById(context.TODO(), id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
active, err := assetService.CheckStatus(item.AccessGatewayId, item.IP, item.Port)
|
active, err := service.AssetService.CheckStatus(item.AccessGatewayId, item.IP, item.Port)
|
||||||
|
|
||||||
if item.Active != active {
|
if item.Active != active {
|
||||||
if err := assetRepository.UpdateActiveById(active, item.ID); err != nil {
|
if err := repository.AssetRepository.UpdateActiveById(context.TODO(), active, item.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -278,36 +215,36 @@ func AssetTcpingEndpoint(c echo.Context) (err error) {
|
|||||||
message = err.Error()
|
message = err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"active": active,
|
"active": active,
|
||||||
"message": message,
|
"message": message,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssetTagsEndpoint(c echo.Context) (err error) {
|
func (assetApi AssetApi) AssetTagsEndpoint(c echo.Context) (err error) {
|
||||||
var items []string
|
var items []string
|
||||||
if items, err = assetRepository.FindTags(); err != nil {
|
if items, err = repository.AssetRepository.FindTags(context.TODO()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, items)
|
return Success(c, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssetChangeOwnerEndpoint(c echo.Context) (err error) {
|
func (assetApi AssetApi) AssetChangeOwnerEndpoint(c echo.Context) (err error) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
if err := PreCheckAssetPermission(c, id); err != nil {
|
if err := assetApi.PreCheckAssetPermission(c, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
owner := c.QueryParam("owner")
|
owner := c.QueryParam("owner")
|
||||||
if err := assetRepository.UpdateById(&model.Asset{Owner: owner}, id); err != nil {
|
if err := repository.AssetRepository.UpdateById(context.TODO(), &model.Asset{Owner: owner}, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func PreCheckAssetPermission(c echo.Context, id string) error {
|
func (assetApi AssetApi) PreCheckAssetPermission(c echo.Context, id string) error {
|
||||||
item, err := assetRepository.FindById(id)
|
item, err := repository.AssetRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -5,133 +5,21 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"next-terminal/server/config"
|
"next-terminal/server/dto"
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/service"
|
||||||
"next-terminal/server/global/security"
|
|
||||||
"next-terminal/server/model"
|
|
||||||
"next-terminal/server/utils"
|
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Backup struct {
|
type BackupApi struct{}
|
||||||
Users []model.User `json:"users"`
|
|
||||||
UserGroups []model.UserGroup `json:"user_groups"`
|
|
||||||
|
|
||||||
Storages []model.Storage `json:"storages"`
|
func (api BackupApi) BackupExportEndpoint(c echo.Context) error {
|
||||||
Strategies []model.Strategy `json:"strategies"`
|
err, backup := service.BackupService.Export()
|
||||||
AccessSecurities []model.AccessSecurity `json:"access_securities"`
|
|
||||||
AccessGateways []model.AccessGateway `json:"access_gateways"`
|
|
||||||
Commands []model.Command `json:"commands"`
|
|
||||||
Credentials []model.Credential `json:"credentials"`
|
|
||||||
Assets []map[string]interface{} `json:"assets"`
|
|
||||||
ResourceSharers []model.ResourceSharer `json:"resource_sharers"`
|
|
||||||
Jobs []model.Job `json:"jobs"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func BackupExportEndpoint(c echo.Context) error {
|
|
||||||
users, err := userRepository.FindAll()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i := range users {
|
|
||||||
users[i].Password = ""
|
|
||||||
}
|
|
||||||
userGroups, err := userGroupRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(userGroups) > 0 {
|
|
||||||
for i := range userGroups {
|
|
||||||
members, err := userGroupRepository.FindMembersById(userGroups[i].ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
userGroups[i].Members = members
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
storages, err := storageRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
strategies, err := strategyRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
jobs, err := jobRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
accessSecurities, err := accessSecurityRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
accessGateways, err := accessGatewayRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
commands, err := commandRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
credentials, err := credentialRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(credentials) > 0 {
|
|
||||||
for i := range credentials {
|
|
||||||
if err := credentialRepository.Decrypt(&credentials[i], config.GlobalCfg.EncryptionPassword); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assets, err := assetRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var assetMaps = make([]map[string]interface{}, 0)
|
|
||||||
if len(assets) > 0 {
|
|
||||||
for i := range assets {
|
|
||||||
asset := assets[i]
|
|
||||||
if err := assetRepository.Decrypt(&asset, config.GlobalCfg.EncryptionPassword); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
attributeMap, err := assetRepository.FindAssetAttrMapByAssetId(asset.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
itemMap := utils.StructToMap(asset)
|
|
||||||
for key := range attributeMap {
|
|
||||||
itemMap[key] = attributeMap[key]
|
|
||||||
}
|
|
||||||
itemMap["created"] = asset.Created.Format("2006-01-02 15:04:05")
|
|
||||||
assetMaps = append(assetMaps, itemMap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceSharers, err := resourceSharerRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
backup := Backup{
|
|
||||||
Users: users,
|
|
||||||
UserGroups: userGroups,
|
|
||||||
Storages: storages,
|
|
||||||
Strategies: strategies,
|
|
||||||
Jobs: jobs,
|
|
||||||
AccessSecurities: accessSecurities,
|
|
||||||
AccessGateways: accessGateways,
|
|
||||||
Commands: commands,
|
|
||||||
Credentials: credentials,
|
|
||||||
Assets: assetMaps,
|
|
||||||
ResourceSharers: resourceSharers,
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonBytes, err := json.Marshal(backup)
|
jsonBytes, err := json.Marshal(backup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -141,200 +29,13 @@ func BackupExportEndpoint(c echo.Context) error {
|
|||||||
return c.Stream(http.StatusOK, echo.MIMEOctetStream, bytes.NewReader(jsonBytes))
|
return c.Stream(http.StatusOK, echo.MIMEOctetStream, bytes.NewReader(jsonBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func BackupImportEndpoint(c echo.Context) error {
|
func (api BackupApi) BackupImportEndpoint(c echo.Context) error {
|
||||||
var backup Backup
|
var backup dto.Backup
|
||||||
if err := c.Bind(&backup); err != nil {
|
if err := c.Bind(&backup); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := service.BackupService.Import(&backup); err != nil {
|
||||||
var userIdMapping = make(map[string]string, 0)
|
|
||||||
if len(backup.Users) > 0 {
|
|
||||||
for _, item := range backup.Users {
|
|
||||||
if userRepository.ExistByUsername(item.Username) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
oldId := item.ID
|
|
||||||
newId := utils.UUID()
|
|
||||||
item.ID = newId
|
|
||||||
item.Password = utils.GenPassword()
|
|
||||||
if err := userRepository.Create(&item); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
userIdMapping[oldId] = newId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var userGroupIdMapping = make(map[string]string, 0)
|
|
||||||
if len(backup.UserGroups) > 0 {
|
|
||||||
for _, item := range backup.UserGroups {
|
|
||||||
oldId := item.ID
|
|
||||||
newId := utils.UUID()
|
|
||||||
item.ID = newId
|
|
||||||
|
|
||||||
var members = make([]string, 0)
|
|
||||||
if len(item.Members) > 0 {
|
|
||||||
for _, member := range item.Members {
|
|
||||||
members = append(members, userIdMapping[member])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := userGroupRepository.Create(&item, members); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
userGroupIdMapping[oldId] = newId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(backup.Storages) > 0 {
|
|
||||||
for _, item := range backup.Storages {
|
|
||||||
item.ID = utils.UUID()
|
|
||||||
item.Owner = userIdMapping[item.Owner]
|
|
||||||
if err := storageRepository.Create(&item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var strategyIdMapping = make(map[string]string, 0)
|
|
||||||
if len(backup.Strategies) > 0 {
|
|
||||||
for _, item := range backup.Strategies {
|
|
||||||
oldId := item.ID
|
|
||||||
newId := utils.UUID()
|
|
||||||
item.ID = newId
|
|
||||||
if err := strategyRepository.Create(&item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
strategyIdMapping[oldId] = newId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(backup.AccessSecurities) > 0 {
|
|
||||||
for _, item := range backup.AccessSecurities {
|
|
||||||
item.ID = utils.UUID()
|
|
||||||
if err := accessSecurityRepository.Create(&item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 更新内存中的安全规则
|
|
||||||
rule := &security.Security{
|
|
||||||
ID: item.ID,
|
|
||||||
IP: item.IP,
|
|
||||||
Rule: item.Rule,
|
|
||||||
Priority: item.Priority,
|
|
||||||
}
|
|
||||||
security.GlobalSecurityManager.Add <- rule
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var accessGatewayIdMapping = make(map[string]string, 0)
|
|
||||||
if len(backup.AccessGateways) > 0 {
|
|
||||||
for _, item := range backup.AccessGateways {
|
|
||||||
oldId := item.ID
|
|
||||||
newId := utils.UUID()
|
|
||||||
item.ID = newId
|
|
||||||
if err := accessGatewayRepository.Create(&item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
accessGatewayIdMapping[oldId] = newId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(backup.Commands) > 0 {
|
|
||||||
for _, item := range backup.Commands {
|
|
||||||
item.ID = utils.UUID()
|
|
||||||
if err := commandRepository.Create(&item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var credentialIdMapping = make(map[string]string, 0)
|
|
||||||
if len(backup.Credentials) > 0 {
|
|
||||||
for _, item := range backup.Credentials {
|
|
||||||
oldId := item.ID
|
|
||||||
newId := utils.UUID()
|
|
||||||
item.ID = newId
|
|
||||||
if err := credentialRepository.Create(&item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
credentialIdMapping[oldId] = newId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var assetIdMapping = make(map[string]string, 0)
|
|
||||||
if len(backup.Assets) > 0 {
|
|
||||||
for _, m := range backup.Assets {
|
|
||||||
data, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var item model.Asset
|
|
||||||
if err := json.Unmarshal(data, &item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if item.CredentialId != "" && item.CredentialId != "-" {
|
|
||||||
item.CredentialId = credentialIdMapping[item.CredentialId]
|
|
||||||
}
|
|
||||||
if item.AccessGatewayId != "" && item.AccessGatewayId != "-" {
|
|
||||||
item.AccessGatewayId = accessGatewayIdMapping[item.AccessGatewayId]
|
|
||||||
}
|
|
||||||
|
|
||||||
oldId := item.ID
|
|
||||||
newId := utils.UUID()
|
|
||||||
item.ID = newId
|
|
||||||
if err := assetRepository.Create(&item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := assetRepository.UpdateAttributes(item.ID, item.Protocol, m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
active, _ := assetService.CheckStatus(item.AccessGatewayId, item.IP, item.Port)
|
|
||||||
|
|
||||||
if item.Active != active {
|
|
||||||
_ = assetRepository.UpdateActiveById(active, item.ID)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
assetIdMapping[oldId] = newId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(backup.ResourceSharers) > 0 {
|
|
||||||
for _, item := range backup.ResourceSharers {
|
|
||||||
|
|
||||||
userGroupId := userGroupIdMapping[item.UserGroupId]
|
|
||||||
userId := userIdMapping[item.UserId]
|
|
||||||
strategyId := strategyIdMapping[item.StrategyId]
|
|
||||||
resourceId := assetIdMapping[item.ResourceId]
|
|
||||||
|
|
||||||
if err := resourceSharerRepository.AddSharerResources(userGroupId, userId, strategyId, item.ResourceType, []string{resourceId}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(backup.Jobs) > 0 {
|
|
||||||
for _, item := range backup.Jobs {
|
|
||||||
if item.Func == constant.FuncCheckAssetStatusJob {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceIds := strings.Split(item.ResourceIds, ",")
|
|
||||||
if len(resourceIds) > 0 {
|
|
||||||
var newResourceIds = make([]string, 0)
|
|
||||||
for _, resourceId := range resourceIds {
|
|
||||||
newResourceIds = append(newResourceIds, assetIdMapping[resourceId])
|
|
||||||
}
|
|
||||||
item.ResourceIds = strings.Join(newResourceIds, ",")
|
|
||||||
}
|
|
||||||
if err := jobService.Create(&item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CommandCreateEndpoint(c echo.Context) error {
|
type CommandApi struct{}
|
||||||
|
|
||||||
|
func (api CommandApi) CommandCreateEndpoint(c echo.Context) error {
|
||||||
var item model.Command
|
var item model.Command
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -22,20 +26,23 @@ func CommandCreateEndpoint(c echo.Context) error {
|
|||||||
item.ID = utils.UUID()
|
item.ID = utils.UUID()
|
||||||
item.Created = utils.NowJsonTime()
|
item.Created = utils.NowJsonTime()
|
||||||
|
|
||||||
if err := commandRepository.Create(&item); err != nil {
|
if err := repository.CommandRepository.Create(context.TODO(), &item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, item)
|
return Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommandAllEndpoint(c echo.Context) error {
|
func (api CommandApi) CommandAllEndpoint(c echo.Context) error {
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
items, _ := commandRepository.FindByUser(account)
|
items, err := repository.CommandRepository.FindByUser(context.TODO(), account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return Success(c, items)
|
return Success(c, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommandPagingEndpoint(c echo.Context) error {
|
func (api CommandApi) CommandPagingEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
name := c.QueryParam("name")
|
name := c.QueryParam("name")
|
||||||
@ -45,20 +52,20 @@ func CommandPagingEndpoint(c echo.Context) error {
|
|||||||
order := c.QueryParam("order")
|
order := c.QueryParam("order")
|
||||||
field := c.QueryParam("field")
|
field := c.QueryParam("field")
|
||||||
|
|
||||||
items, total, err := commandRepository.Find(pageIndex, pageSize, name, content, order, field, account)
|
items, total, err := repository.CommandRepository.Find(context.TODO(), pageIndex, pageSize, name, content, order, field, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommandUpdateEndpoint(c echo.Context) error {
|
func (api CommandApi) CommandUpdateEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
if err := PreCheckCommandPermission(c, id); err != nil {
|
if err := api.PreCheckCommandPermission(c, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,61 +74,57 @@ func CommandUpdateEndpoint(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := commandRepository.UpdateById(&item, id); err != nil {
|
if err := repository.CommandRepository.UpdateById(context.TODO(), &item, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommandDeleteEndpoint(c echo.Context) error {
|
func (api CommandApi) CommandDeleteEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
split := strings.Split(id, ",")
|
split := strings.Split(id, ",")
|
||||||
for i := range split {
|
for i := range split {
|
||||||
if err := PreCheckCommandPermission(c, split[i]); err != nil {
|
if err := api.PreCheckCommandPermission(c, split[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := commandRepository.DeleteById(split[i]); err != nil {
|
if err := repository.CommandRepository.DeleteById(context.TODO(), split[i]); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 删除资产与用户的关系
|
|
||||||
if err := resourceSharerRepository.DeleteResourceSharerByResourceId(split[i]); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommandGetEndpoint(c echo.Context) (err error) {
|
func (api CommandApi) CommandGetEndpoint(c echo.Context) (err error) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
if err := PreCheckCommandPermission(c, id); err != nil {
|
if err := api.PreCheckCommandPermission(c, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var item model.Command
|
var item model.Command
|
||||||
if item, err = commandRepository.FindById(id); err != nil {
|
if item, err = repository.CommandRepository.FindById(context.TODO(), id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, item)
|
return Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommandChangeOwnerEndpoint(c echo.Context) (err error) {
|
func (api CommandApi) CommandChangeOwnerEndpoint(c echo.Context) (err error) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
if err := PreCheckCommandPermission(c, id); err != nil {
|
if err := api.PreCheckCommandPermission(c, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
owner := c.QueryParam("owner")
|
owner := c.QueryParam("owner")
|
||||||
if err := commandRepository.UpdateById(&model.Command{Owner: owner}, id); err != nil {
|
if err := repository.CommandRepository.UpdateById(context.TODO(), &model.Command{Owner: owner}, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func PreCheckCommandPermission(c echo.Context, id string) error {
|
func (api CommandApi) PreCheckCommandPermission(c echo.Context, id string) error {
|
||||||
item, err := commandRepository.FindById(id)
|
item, err := repository.CommandRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -9,17 +10,23 @@ import (
|
|||||||
"next-terminal/server/config"
|
"next-terminal/server/config"
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CredentialAllEndpoint(c echo.Context) error {
|
type CredentialApi struct{}
|
||||||
account, _ := GetCurrentAccount(c)
|
|
||||||
items, _ := credentialRepository.FindByUser(account)
|
func (api CredentialApi) CredentialAllEndpoint(c echo.Context) error {
|
||||||
|
items, err := repository.CredentialRepository.FindByUser(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return Success(c, items)
|
return Success(c, items)
|
||||||
}
|
}
|
||||||
func CredentialCreateEndpoint(c echo.Context) error {
|
func (api CredentialApi) CredentialCreateEndpoint(c echo.Context) error {
|
||||||
var item model.Credential
|
var item model.Credential
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -56,14 +63,15 @@ func CredentialCreateEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
item.Encrypted = true
|
item.Encrypted = true
|
||||||
if err := credentialRepository.Create(&item); err != nil {
|
|
||||||
|
if err := service.CredentialService.Create(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, item)
|
return Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CredentialPagingEndpoint(c echo.Context) error {
|
func (api CredentialApi) CredentialPagingEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
name := c.QueryParam("name")
|
name := c.QueryParam("name")
|
||||||
@ -72,21 +80,21 @@ func CredentialPagingEndpoint(c echo.Context) error {
|
|||||||
field := c.QueryParam("field")
|
field := c.QueryParam("field")
|
||||||
|
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
items, total, err := credentialRepository.Find(pageIndex, pageSize, name, order, field, account)
|
items, total, err := repository.CredentialRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func CredentialUpdateEndpoint(c echo.Context) error {
|
func (api CredentialApi) CredentialUpdateEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
if err := PreCheckCredentialPermission(c, id); err != nil {
|
if err := api.PreCheckCredentialPermission(c, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,25 +150,21 @@ func CredentialUpdateEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
item.Encrypted = true
|
item.Encrypted = true
|
||||||
|
|
||||||
if err := credentialRepository.UpdateById(&item, id); err != nil {
|
if err := repository.CredentialRepository.UpdateById(context.TODO(), &item, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CredentialDeleteEndpoint(c echo.Context) error {
|
func (api CredentialApi) CredentialDeleteEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
split := strings.Split(id, ",")
|
split := strings.Split(id, ",")
|
||||||
for i := range split {
|
for i := range split {
|
||||||
if err := PreCheckCredentialPermission(c, split[i]); err != nil {
|
if err := api.PreCheckCredentialPermission(c, split[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := credentialRepository.DeleteById(split[i]); err != nil {
|
if err := repository.CredentialRepository.DeleteById(context.TODO(), split[i]); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 删除资产与用户的关系
|
|
||||||
if err := resourceSharerRepository.DeleteResourceSharerByResourceId(split[i]); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,13 +172,13 @@ func CredentialDeleteEndpoint(c echo.Context) error {
|
|||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CredentialGetEndpoint(c echo.Context) error {
|
func (api CredentialApi) CredentialGetEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
if err := PreCheckCredentialPermission(c, id); err != nil {
|
if err := api.PreCheckCredentialPermission(c, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
item, err := credentialRepository.FindByIdAndDecrypt(id)
|
item, err := service.CredentialService.FindByIdAndDecrypt(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -186,22 +190,22 @@ func CredentialGetEndpoint(c echo.Context) error {
|
|||||||
return Success(c, item)
|
return Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CredentialChangeOwnerEndpoint(c echo.Context) error {
|
func (api CredentialApi) CredentialChangeOwnerEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
if err := PreCheckCredentialPermission(c, id); err != nil {
|
if err := api.PreCheckCredentialPermission(c, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
owner := c.QueryParam("owner")
|
owner := c.QueryParam("owner")
|
||||||
if err := credentialRepository.UpdateById(&model.Credential{Owner: owner}, id); err != nil {
|
if err := repository.CredentialRepository.UpdateById(context.TODO(), &model.Credential{Owner: owner}, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func PreCheckCredentialPermission(c echo.Context, id string) error {
|
func (api CredentialApi) PreCheckCredentialPermission(c echo.Context, id string) error {
|
||||||
item, err := credentialRepository.FindById(id)
|
item, err := repository.CredentialRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,10 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"next-terminal/server/config"
|
"next-terminal/server/config"
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
@ -14,6 +13,8 @@ import (
|
|||||||
"next-terminal/server/guacd"
|
"next-terminal/server/guacd"
|
||||||
"next-terminal/server/log"
|
"next-terminal/server/log"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
@ -31,18 +32,27 @@ const (
|
|||||||
AssetNotActive int = 805
|
AssetNotActive int = 805
|
||||||
)
|
)
|
||||||
|
|
||||||
func TunEndpoint(c echo.Context) error {
|
var UpGrader = websocket.Upgrader{
|
||||||
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
Subprotocols: []string{"guacamole"},
|
||||||
|
}
|
||||||
|
|
||||||
|
type GuacamoleApi struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
ctx := context.TODO()
|
||||||
width := c.QueryParam("width")
|
width := c.QueryParam("width")
|
||||||
height := c.QueryParam("height")
|
height := c.QueryParam("height")
|
||||||
dpi := c.QueryParam("dpi")
|
dpi := c.QueryParam("dpi")
|
||||||
sessionId := c.QueryParam("sessionId")
|
sessionId := c.Param("id")
|
||||||
connectionId := c.QueryParam("connectionId")
|
connectionId := c.QueryParam("connectionId")
|
||||||
|
|
||||||
intWidth, _ := strconv.Atoi(width)
|
intWidth, _ := strconv.Atoi(width)
|
||||||
@ -50,12 +60,12 @@ func TunEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
configuration := guacd.NewConfiguration()
|
configuration := guacd.NewConfiguration()
|
||||||
|
|
||||||
propertyMap := propertyRepository.FindAllMap()
|
propertyMap := repository.PropertyRepository.FindAllMap(ctx)
|
||||||
|
|
||||||
var s model.Session
|
var s model.Session
|
||||||
|
|
||||||
if len(connectionId) > 0 {
|
if len(connectionId) > 0 {
|
||||||
s, err = sessionRepository.FindByConnectionId(connectionId)
|
s, err = repository.SessionRepository.FindByConnectionId(ctx, connectionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -71,28 +81,28 @@ func TunEndpoint(c echo.Context) error {
|
|||||||
configuration.SetParameter("width", width)
|
configuration.SetParameter("width", width)
|
||||||
configuration.SetParameter("height", height)
|
configuration.SetParameter("height", height)
|
||||||
configuration.SetParameter("dpi", dpi)
|
configuration.SetParameter("dpi", dpi)
|
||||||
s, err = sessionRepository.FindByIdAndDecrypt(sessionId)
|
s, err = service.SessionService.FindByIdAndDecrypt(ctx, sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
setConfig(propertyMap, s, configuration)
|
api.setConfig(propertyMap, s, configuration)
|
||||||
var (
|
var (
|
||||||
ip = s.IP
|
ip = s.IP
|
||||||
port = s.Port
|
port = s.Port
|
||||||
)
|
)
|
||||||
if s.AccessGatewayId != "" && s.AccessGatewayId != "-" {
|
if s.AccessGatewayId != "" && s.AccessGatewayId != "-" {
|
||||||
g, err := accessGatewayService.GetGatewayAndReconnectById(s.AccessGatewayId)
|
g, err := service.GatewayService.GetGatewayAndReconnectById(s.AccessGatewayId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
disconnect(ws, AccessGatewayUnAvailable, "获取接入网关失败:"+err.Error())
|
utils.Disconnect(ws, AccessGatewayUnAvailable, "获取接入网关失败:"+err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !g.Connected {
|
if !g.Connected {
|
||||||
disconnect(ws, AccessGatewayUnAvailable, "接入网关不可用:"+g.Message)
|
utils.Disconnect(ws, AccessGatewayUnAvailable, "接入网关不可用:"+g.Message)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
exposedIP, exposedPort, err := g.OpenSshTunnel(s.ID, ip, port)
|
exposedIP, exposedPort, err := g.OpenSshTunnel(s.ID, ip, port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
disconnect(ws, AccessGatewayCreateError, "创建SSH隧道失败:"+err.Error())
|
utils.Disconnect(ws, AccessGatewayCreateError, "创建SSH隧道失败:"+err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer g.CloseSshTunnel(s.ID)
|
defer g.CloseSshTunnel(s.ID)
|
||||||
@ -101,7 +111,7 @@ func TunEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
active, err := utils.Tcping(ip, port)
|
active, err := utils.Tcping(ip, port)
|
||||||
if !active {
|
if !active {
|
||||||
disconnect(ws, AssetNotActive, "目标资产不在线: "+err.Error())
|
utils.Disconnect(ws, AssetNotActive, "目标资产不在线: "+err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,12 +119,12 @@ func TunEndpoint(c echo.Context) error {
|
|||||||
configuration.SetParameter("port", strconv.Itoa(port))
|
configuration.SetParameter("port", strconv.Itoa(port))
|
||||||
|
|
||||||
// 加载资产配置的属性,优先级比全局配置的高,因此最后加载,覆盖掉全局配置
|
// 加载资产配置的属性,优先级比全局配置的高,因此最后加载,覆盖掉全局配置
|
||||||
attributes, err := assetRepository.FindAssetAttrMapByAssetId(s.AssetId)
|
attributes, err := repository.AssetRepository.FindAssetAttrMapByAssetId(ctx, s.AssetId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(attributes) > 0 {
|
if len(attributes) > 0 {
|
||||||
setAssetConfig(attributes, s, configuration)
|
api.setAssetConfig(attributes, s, configuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for name := range configuration.Parameters {
|
for name := range configuration.Parameters {
|
||||||
@ -130,7 +140,7 @@ func TunEndpoint(c echo.Context) error {
|
|||||||
guacdTunnel, err := guacd.NewTunnel(addr, configuration)
|
guacdTunnel, err := guacd.NewTunnel(addr, configuration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if connectionId == "" {
|
if connectionId == "" {
|
||||||
disconnect(ws, NewTunnelError, err.Error())
|
utils.Disconnect(ws, NewTunnelError, err.Error())
|
||||||
}
|
}
|
||||||
log.Printf("[%v:%v] 建立连接失败: %v", sessionId, connectionId, err.Error())
|
log.Printf("[%v:%v] 建立连接失败: %v", sessionId, connectionId, err.Error())
|
||||||
return err
|
return err
|
||||||
@ -144,7 +154,7 @@ func TunEndpoint(c echo.Context) error {
|
|||||||
GuacdTunnel: guacdTunnel,
|
GuacdTunnel: guacdTunnel,
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.ConnectionId) == 0 {
|
if connectionId == "" {
|
||||||
if configuration.Protocol == constant.SSH {
|
if configuration.Protocol == constant.SSH {
|
||||||
nextTerminal, err := CreateNextTerminalBySession(s)
|
nextTerminal, err := CreateNextTerminalBySession(s)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -168,14 +178,14 @@ func TunEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
// 创建新会话
|
// 创建新会话
|
||||||
log.Debugf("[%v:%v] 创建新会话: %v", sessionId, connectionId, sess.ConnectionId)
|
log.Debugf("[%v:%v] 创建新会话: %v", sessionId, connectionId, sess.ConnectionId)
|
||||||
if err := sessionRepository.UpdateById(&sess, sessionId); err != nil {
|
if err := repository.SessionRepository.UpdateById(ctx, &sess, sessionId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 要监控会话
|
// 要监控会话
|
||||||
forObsSession := session.GlobalSessionManager.GetById(sessionId)
|
forObsSession := session.GlobalSessionManager.GetById(sessionId)
|
||||||
if forObsSession == nil {
|
if forObsSession == nil {
|
||||||
disconnect(ws, NotFoundSession, "获取会话失败")
|
utils.Disconnect(ws, NotFoundSession, "获取会话失败")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
nextSession.ID = utils.UUID()
|
nextSession.ID = utils.UUID()
|
||||||
@ -183,56 +193,8 @@ func TunEndpoint(c echo.Context) error {
|
|||||||
log.Debugf("[%v:%v] 观察者[%v]加入会话[%v]", sessionId, connectionId, nextSession.ID, s.ConnectionId)
|
log.Debugf("[%v:%v] 观察者[%v]加入会话[%v]", sessionId, connectionId, nextSession.ID, s.ConnectionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
guacamoleHandler := NewGuacamoleHandler(ws, guacdTunnel)
|
||||||
tick := time.NewTicker(time.Millisecond * time.Duration(60))
|
guacamoleHandler.Start()
|
||||||
defer tick.Stop()
|
|
||||||
var buf []byte
|
|
||||||
dataChan := make(chan []byte)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
GuacdLoop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
log.Debugf("[%v:%v] WebSocket 已关闭,即将关闭 Guacd 连接...", sessionId, connectionId)
|
|
||||||
break GuacdLoop
|
|
||||||
default:
|
|
||||||
instruction, err := guacdTunnel.Read()
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("[%v:%v] Guacd 读取失败,即将退出循环...", sessionId, connectionId)
|
|
||||||
disconnect(ws, TunnelClosed, "远程连接已关闭")
|
|
||||||
break GuacdLoop
|
|
||||||
}
|
|
||||||
if len(instruction) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dataChan <- instruction
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Debugf("[%v:%v] Guacd 连接已关闭,退出 Guacd 循环。", sessionId, connectionId)
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
tickLoop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
break tickLoop
|
|
||||||
case <-tick.C:
|
|
||||||
if len(buf) > 0 {
|
|
||||||
err = ws.WriteMessage(websocket.TextMessage, buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("[%v:%v] WebSocket写入失败,即将关闭Guacd连接...", sessionId, connectionId)
|
|
||||||
break tickLoop
|
|
||||||
}
|
|
||||||
buf = []byte{}
|
|
||||||
}
|
|
||||||
case data := <-dataChan:
|
|
||||||
buf = append(buf, data...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Debugf("[%v:%v] Guacd连接已关闭,退出定时器循环。", sessionId, connectionId)
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_, message, err := ws.ReadMessage()
|
_, message, err := ws.ReadMessage()
|
||||||
@ -250,20 +212,20 @@ func TunEndpoint(c echo.Context) error {
|
|||||||
log.Debugf("[%v:%v] 观察者[%v]退出会话", sessionId, connectionId, observerId)
|
log.Debugf("[%v:%v] 观察者[%v]退出会话", sessionId, connectionId, observerId)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CloseSessionById(sessionId, Normal, "用户正常退出")
|
service.SessionService.CloseSessionById(sessionId, Normal, "用户正常退出")
|
||||||
}
|
}
|
||||||
cancel()
|
guacamoleHandler.Stop()
|
||||||
break
|
return nil
|
||||||
}
|
}
|
||||||
_, err = guacdTunnel.WriteAndFlush(message)
|
_, err = guacdTunnel.WriteAndFlush(message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
CloseSessionById(sessionId, TunnelClosed, "远程连接已关闭")
|
service.SessionService.CloseSessionById(sessionId, TunnelClosed, "远程连接已关闭")
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func setAssetConfig(attributes map[string]string, s model.Session, configuration *guacd.Configuration) {
|
func (api GuacamoleApi) setAssetConfig(attributes map[string]string, s model.Session, configuration *guacd.Configuration) {
|
||||||
for key, value := range attributes {
|
for key, value := range attributes {
|
||||||
if guacd.DrivePath == key {
|
if guacd.DrivePath == key {
|
||||||
// 忽略该参数
|
// 忽略该参数
|
||||||
@ -275,7 +237,7 @@ func setAssetConfig(attributes map[string]string, s model.Session, configuration
|
|||||||
// 默认空间ID和用户ID相同
|
// 默认空间ID和用户ID相同
|
||||||
storageId = s.Creator
|
storageId = s.Creator
|
||||||
}
|
}
|
||||||
realPath := path.Join(storageService.GetBaseDrivePath(), storageId)
|
realPath := path.Join(service.StorageService.GetBaseDrivePath(), storageId)
|
||||||
configuration.SetParameter(guacd.EnableDrive, "true")
|
configuration.SetParameter(guacd.EnableDrive, "true")
|
||||||
configuration.SetParameter(guacd.DriveName, "Next Terminal Filesystem")
|
configuration.SetParameter(guacd.DriveName, "Next Terminal Filesystem")
|
||||||
configuration.SetParameter(guacd.DrivePath, realPath)
|
configuration.SetParameter(guacd.DrivePath, realPath)
|
||||||
@ -286,7 +248,7 @@ func setAssetConfig(attributes map[string]string, s model.Session, configuration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setConfig(propertyMap map[string]string, s model.Session, configuration *guacd.Configuration) {
|
func (api GuacamoleApi) setConfig(propertyMap map[string]string, s model.Session, configuration *guacd.Configuration) {
|
||||||
if propertyMap[guacd.EnableRecording] == "true" {
|
if propertyMap[guacd.EnableRecording] == "true" {
|
||||||
configuration.SetParameter(guacd.RecordingPath, path.Join(config.GlobalCfg.Guacd.Recording, s.ID))
|
configuration.SetParameter(guacd.RecordingPath, path.Join(config.GlobalCfg.Guacd.Recording, s.ID))
|
||||||
configuration.SetParameter(guacd.CreateRecordingPath, "true")
|
configuration.SetParameter(guacd.CreateRecordingPath, "true")
|
||||||
@ -312,7 +274,8 @@ func setConfig(propertyMap map[string]string, s model.Session, configuration *gu
|
|||||||
configuration.SetParameter(guacd.EnableMenuAnimations, propertyMap[guacd.EnableMenuAnimations])
|
configuration.SetParameter(guacd.EnableMenuAnimations, propertyMap[guacd.EnableMenuAnimations])
|
||||||
configuration.SetParameter(guacd.DisableBitmapCaching, propertyMap[guacd.DisableBitmapCaching])
|
configuration.SetParameter(guacd.DisableBitmapCaching, propertyMap[guacd.DisableBitmapCaching])
|
||||||
configuration.SetParameter(guacd.DisableOffscreenCaching, propertyMap[guacd.DisableOffscreenCaching])
|
configuration.SetParameter(guacd.DisableOffscreenCaching, propertyMap[guacd.DisableOffscreenCaching])
|
||||||
configuration.SetParameter(guacd.DisableGlyphCaching, propertyMap[guacd.DisableGlyphCaching])
|
configuration.SetParameter(guacd.ColorDepth, propertyMap[guacd.ColorDepth])
|
||||||
|
configuration.SetParameter(guacd.ForceLossless, propertyMap[guacd.ForceLossless])
|
||||||
case "ssh":
|
case "ssh":
|
||||||
if len(s.PrivateKey) > 0 && s.PrivateKey != "-" {
|
if len(s.PrivateKey) > 0 && s.PrivateKey != "-" {
|
||||||
configuration.SetParameter("username", s.Username)
|
configuration.SetParameter("username", s.Username)
|
||||||
@ -350,12 +313,3 @@ func setConfig(propertyMap map[string]string, s model.Session, configuration *gu
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func disconnect(ws *websocket.Conn, code int, reason string) {
|
|
||||||
// guacd 无法处理中文字符,所以进行了base64编码。
|
|
||||||
encodeReason := base64.StdEncoding.EncodeToString([]byte(reason))
|
|
||||||
err := guacd.NewInstruction("error", encodeReason, strconv.Itoa(code))
|
|
||||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(err.String()))
|
|
||||||
disconnect := guacd.NewInstruction("disconnect")
|
|
||||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(disconnect.String()))
|
|
||||||
}
|
|
84
server/api/guacamole_handler.go
Normal file
84
server/api/guacamole_handler.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"next-terminal/server/guacd"
|
||||||
|
"next-terminal/server/log"
|
||||||
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GuacamoleHandler struct {
|
||||||
|
ws *websocket.Conn
|
||||||
|
tunnel *guacd.Tunnel
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
dataChan chan []byte
|
||||||
|
tick *time.Ticker
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGuacamoleHandler(ws *websocket.Conn, tunnel *guacd.Tunnel) *GuacamoleHandler {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
tick := time.NewTicker(time.Millisecond * time.Duration(60))
|
||||||
|
return &GuacamoleHandler{
|
||||||
|
ws: ws,
|
||||||
|
tunnel: tunnel,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
dataChan: make(chan []byte),
|
||||||
|
tick: tick,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r GuacamoleHandler) Start() {
|
||||||
|
go r.readFormTunnel()
|
||||||
|
go r.writeToWebsocket()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r GuacamoleHandler) Stop() {
|
||||||
|
r.tick.Stop()
|
||||||
|
r.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r GuacamoleHandler) readFormTunnel() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-r.ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
instruction, err := r.tunnel.Read()
|
||||||
|
if err != nil {
|
||||||
|
utils.Disconnect(r.ws, TunnelClosed, "远程连接已关闭")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(instruction) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.dataChan <- instruction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r GuacamoleHandler) writeToWebsocket() {
|
||||||
|
var buf []byte
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-r.ctx.Done():
|
||||||
|
return
|
||||||
|
case <-r.tick.C:
|
||||||
|
if len(buf) > 0 {
|
||||||
|
err := r.ws.WriteMessage(websocket.TextMessage, buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("WebSocket写入失败,即将关闭Guacd连接...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf = []byte{}
|
||||||
|
}
|
||||||
|
case data := <-r.dataChan:
|
||||||
|
buf = append(buf, data...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,22 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func JobCreateEndpoint(c echo.Context) error {
|
type JobApi struct{}
|
||||||
|
|
||||||
|
func (api JobApi) JobCreateEndpoint(c echo.Context) error {
|
||||||
var item model.Job
|
var item model.Job
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -19,13 +25,13 @@ func JobCreateEndpoint(c echo.Context) error {
|
|||||||
item.ID = utils.UUID()
|
item.ID = utils.UUID()
|
||||||
item.Created = utils.NowJsonTime()
|
item.Created = utils.NowJsonTime()
|
||||||
|
|
||||||
if err := jobService.Create(&item); err != nil {
|
if err := service.JobService.Create(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func JobPagingEndpoint(c echo.Context) error {
|
func (api JobApi) JobPagingEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
name := c.QueryParam("name")
|
name := c.QueryParam("name")
|
||||||
@ -34,18 +40,18 @@ func JobPagingEndpoint(c echo.Context) error {
|
|||||||
order := c.QueryParam("order")
|
order := c.QueryParam("order")
|
||||||
field := c.QueryParam("field")
|
field := c.QueryParam("field")
|
||||||
|
|
||||||
items, total, err := jobRepository.Find(pageIndex, pageSize, name, status, order, field)
|
items, total, err := repository.JobRepository.Find(context.TODO(), pageIndex, pageSize, name, status, order, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func JobUpdateEndpoint(c echo.Context) error {
|
func (api JobApi) JobUpdateEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
var item model.Job
|
var item model.Job
|
||||||
@ -53,37 +59,37 @@ func JobUpdateEndpoint(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
item.ID = id
|
item.ID = id
|
||||||
if err := jobService.UpdateById(&item); err != nil {
|
if err := service.JobService.UpdateById(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func JobChangeStatusEndpoint(c echo.Context) error {
|
func (api JobApi) JobChangeStatusEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
status := c.QueryParam("status")
|
status := c.QueryParam("status")
|
||||||
if err := jobService.ChangeStatusById(id, status); err != nil {
|
if err := service.JobService.ChangeStatusById(id, status); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func JobExecEndpoint(c echo.Context) error {
|
func (api JobApi) JobExecEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
if err := jobService.ExecJobById(id); err != nil {
|
if err := service.JobService.ExecJobById(id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func JobDeleteEndpoint(c echo.Context) error {
|
func (api JobApi) JobDeleteEndpoint(c echo.Context) error {
|
||||||
ids := c.Param("id")
|
ids := c.Param("id")
|
||||||
|
|
||||||
split := strings.Split(ids, ",")
|
split := strings.Split(ids, ",")
|
||||||
for i := range split {
|
for i := range split {
|
||||||
jobId := split[i]
|
jobId := split[i]
|
||||||
if err := jobService.DeleteJobById(jobId); err != nil {
|
if err := service.JobService.DeleteJobById(jobId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,10 +97,10 @@ func JobDeleteEndpoint(c echo.Context) error {
|
|||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func JobGetEndpoint(c echo.Context) error {
|
func (api JobApi) JobGetEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
item, err := jobRepository.FindById(id)
|
item, err := repository.JobRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -102,10 +108,10 @@ func JobGetEndpoint(c echo.Context) error {
|
|||||||
return Success(c, item)
|
return Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func JobGetLogsEndpoint(c echo.Context) error {
|
func (api JobApi) JobGetLogsEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
items, err := jobLogRepository.FindByJobId(id)
|
items, err := repository.JobLogRepository.FindByJobId(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -113,9 +119,9 @@ func JobGetLogsEndpoint(c echo.Context) error {
|
|||||||
return Success(c, items)
|
return Success(c, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
func JobDeleteLogsEndpoint(c echo.Context) error {
|
func (api JobApi) JobDeleteLogsEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
if err := jobLogRepository.DeleteByJobId(id); err != nil {
|
if err := repository.JobLogRepository.DeleteByJobId(context.TODO(), id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
|
@ -1,43 +1,49 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoginLogPagingEndpoint(c echo.Context) error {
|
type LoginLogApi struct{}
|
||||||
|
|
||||||
|
func (api LoginLogApi) LoginLogPagingEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
username := c.QueryParam("username")
|
username := c.QueryParam("username")
|
||||||
clientIp := c.QueryParam("clientIp")
|
clientIp := c.QueryParam("clientIp")
|
||||||
state := c.QueryParam("state")
|
state := c.QueryParam("state")
|
||||||
|
|
||||||
items, total, err := loginLogRepository.Find(pageIndex, pageSize, username, clientIp, state)
|
items, total, err := repository.LoginLogRepository.Find(context.TODO(), pageIndex, pageSize, username, clientIp, state)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoginLogDeleteEndpoint(c echo.Context) error {
|
func (api LoginLogApi) LoginLogDeleteEndpoint(c echo.Context) error {
|
||||||
ids := c.Param("id")
|
ids := c.Param("id")
|
||||||
tokens := strings.Split(ids, ",")
|
tokens := strings.Split(ids, ",")
|
||||||
if err := userService.DeleteLoginLogs(tokens); err != nil {
|
if err := service.UserService.DeleteLoginLogs(tokens); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoginLogClearEndpoint(c echo.Context) error {
|
func (api LoginLogApi) LoginLogClearEndpoint(c echo.Context) error {
|
||||||
loginLogs, err := loginLogRepository.FindAllLoginLogs()
|
loginLogs, err := repository.LoginLogRepository.FindAllLoginLogs(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -46,7 +52,7 @@ func LoginLogClearEndpoint(c echo.Context) error {
|
|||||||
tokens = append(tokens, loginLogs[i].ID)
|
tokens = append(tokens, loginLogs[i].ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := userService.DeleteLoginLogs(tokens); err != nil {
|
if err := service.UserService.DeleteLoginLogs(tokens); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
|
@ -1,39 +1,30 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/dto"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Counter struct {
|
type OverviewApi struct{}
|
||||||
User int64 `json:"user"`
|
|
||||||
Asset int64 `json:"asset"`
|
|
||||||
Credential int64 `json:"credential"`
|
|
||||||
OnlineSession int64 `json:"onlineSession"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func OverviewCounterEndPoint(c echo.Context) error {
|
|
||||||
account, _ := GetCurrentAccount(c)
|
|
||||||
|
|
||||||
|
func (api OverviewApi) OverviewCounterEndPoint(c echo.Context) error {
|
||||||
var (
|
var (
|
||||||
countUser int64
|
countUser int64
|
||||||
countOnlineSession int64
|
countOnlineSession int64
|
||||||
credential int64
|
credential int64
|
||||||
asset int64
|
asset int64
|
||||||
)
|
)
|
||||||
if constant.TypeUser == account.Type {
|
countUser, _ = repository.UserRepository.CountOnlineUser(context.TODO())
|
||||||
countUser, _ = userRepository.CountOnlineUser()
|
countOnlineSession, _ = repository.SessionRepository.CountOnlineSession(context.TODO())
|
||||||
countOnlineSession, _ = sessionRepository.CountOnlineSession()
|
credential, _ = repository.CredentialRepository.Count(context.TODO())
|
||||||
credential, _ = credentialRepository.CountByUserId(account.ID)
|
asset, _ = repository.AssetRepository.Count(context.TODO())
|
||||||
asset, _ = assetRepository.CountByUserId(account.ID)
|
|
||||||
} else {
|
counter := dto.Counter{
|
||||||
countUser, _ = userRepository.CountOnlineUser()
|
|
||||||
countOnlineSession, _ = sessionRepository.CountOnlineSession()
|
|
||||||
credential, _ = credentialRepository.Count()
|
|
||||||
asset, _ = assetRepository.Count()
|
|
||||||
}
|
|
||||||
counter := Counter{
|
|
||||||
User: countUser,
|
User: countUser,
|
||||||
OnlineSession: countOnlineSession,
|
OnlineSession: countOnlineSession,
|
||||||
Credential: credential,
|
Credential: credential,
|
||||||
@ -43,8 +34,7 @@ func OverviewCounterEndPoint(c echo.Context) error {
|
|||||||
return Success(c, counter)
|
return Success(c, counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OverviewAssetEndPoint(c echo.Context) error {
|
func (api OverviewApi) OverviewAssetEndPoint(c echo.Context) error {
|
||||||
account, _ := GetCurrentAccount(c)
|
|
||||||
var (
|
var (
|
||||||
ssh int64
|
ssh int64
|
||||||
rdp int64
|
rdp int64
|
||||||
@ -52,19 +42,13 @@ func OverviewAssetEndPoint(c echo.Context) error {
|
|||||||
telnet int64
|
telnet int64
|
||||||
kubernetes int64
|
kubernetes int64
|
||||||
)
|
)
|
||||||
if constant.TypeUser == account.Type {
|
|
||||||
ssh, _ = assetRepository.CountByUserIdAndProtocol(account.ID, constant.SSH)
|
ssh, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.SSH)
|
||||||
rdp, _ = assetRepository.CountByUserIdAndProtocol(account.ID, constant.RDP)
|
rdp, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.RDP)
|
||||||
vnc, _ = assetRepository.CountByUserIdAndProtocol(account.ID, constant.VNC)
|
vnc, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.VNC)
|
||||||
telnet, _ = assetRepository.CountByUserIdAndProtocol(account.ID, constant.Telnet)
|
telnet, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.Telnet)
|
||||||
kubernetes, _ = assetRepository.CountByUserIdAndProtocol(account.ID, constant.K8s)
|
kubernetes, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.K8s)
|
||||||
} else {
|
|
||||||
ssh, _ = assetRepository.CountByProtocol(constant.SSH)
|
|
||||||
rdp, _ = assetRepository.CountByProtocol(constant.RDP)
|
|
||||||
vnc, _ = assetRepository.CountByProtocol(constant.VNC)
|
|
||||||
telnet, _ = assetRepository.CountByProtocol(constant.Telnet)
|
|
||||||
kubernetes, _ = assetRepository.CountByProtocol(constant.K8s)
|
|
||||||
}
|
|
||||||
m := echo.Map{
|
m := echo.Map{
|
||||||
"ssh": ssh,
|
"ssh": ssh,
|
||||||
"rdp": rdp,
|
"rdp": rdp,
|
||||||
@ -75,9 +59,8 @@ func OverviewAssetEndPoint(c echo.Context) error {
|
|||||||
return Success(c, m)
|
return Success(c, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OverviewAccessEndPoint(c echo.Context) error {
|
func (api OverviewApi) OverviewAccessEndPoint(c echo.Context) error {
|
||||||
account, _ := GetCurrentAccount(c)
|
access, err := repository.SessionRepository.OverviewAccess(context.TODO())
|
||||||
access, err := sessionRepository.OverviewAccess(account)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,29 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func PropertyGetEndpoint(c echo.Context) error {
|
type PropertyApi struct{}
|
||||||
properties := propertyRepository.FindAllMap()
|
|
||||||
|
func (api PropertyApi) PropertyGetEndpoint(c echo.Context) error {
|
||||||
|
properties := repository.PropertyRepository.FindAllMap(context.TODO())
|
||||||
return Success(c, properties)
|
return Success(c, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PropertyUpdateEndpoint(c echo.Context) error {
|
func (api PropertyApi) PropertyUpdateEndpoint(c echo.Context) error {
|
||||||
var item map[string]interface{}
|
var item map[string]interface{}
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for key := range item {
|
if err := service.PropertyService.Update(item); err != nil {
|
||||||
value := fmt.Sprintf("%v", item[key])
|
|
||||||
if value == "" {
|
|
||||||
value = "-"
|
|
||||||
}
|
|
||||||
|
|
||||||
property := model.Property{
|
|
||||||
Name: key,
|
|
||||||
Value: value,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := propertyRepository.FindByName(key)
|
|
||||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
if err := propertyRepository.Create(&property); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if err := propertyRepository.UpdateByName(&property, key); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
@ -1,55 +1,48 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"next-terminal/server/dto"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RU struct {
|
type ResourceSharerApi struct{}
|
||||||
UserGroupId string `json:"userGroupId"`
|
|
||||||
UserId string `json:"userId"`
|
|
||||||
StrategyId string `json:"strategyId"`
|
|
||||||
ResourceType string `json:"resourceType"`
|
|
||||||
ResourceIds []string `json:"resourceIds"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UR struct {
|
func (api ResourceSharerApi) RSGetSharersEndPoint(c echo.Context) error {
|
||||||
ResourceId string `json:"resourceId"`
|
|
||||||
ResourceType string `json:"resourceType"`
|
|
||||||
UserIds []string `json:"userIds"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func RSGetSharersEndPoint(c echo.Context) error {
|
|
||||||
resourceId := c.QueryParam("resourceId")
|
resourceId := c.QueryParam("resourceId")
|
||||||
resourceType := c.QueryParam("resourceType")
|
resourceType := c.QueryParam("resourceType")
|
||||||
userId := c.QueryParam("userId")
|
userId := c.QueryParam("userId")
|
||||||
userGroupId := c.QueryParam("userGroupId")
|
userGroupId := c.QueryParam("userGroupId")
|
||||||
userIds, err := resourceSharerRepository.Find(resourceId, resourceType, userId, userGroupId)
|
userIds, err := repository.ResourceSharerRepository.Find(context.TODO(), resourceId, resourceType, userId, userGroupId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, userIds)
|
return Success(c, userIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResourceRemoveByUserIdAssignEndPoint(c echo.Context) error {
|
func (api ResourceSharerApi) ResourceRemoveByUserIdAssignEndPoint(c echo.Context) error {
|
||||||
var ru RU
|
var ru dto.RU
|
||||||
if err := c.Bind(&ru); err != nil {
|
if err := c.Bind(&ru); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := resourceSharerRepository.DeleteByUserIdAndResourceTypeAndResourceIdIn(ru.UserGroupId, ru.UserId, ru.ResourceType, ru.ResourceIds); err != nil {
|
if err := repository.ResourceSharerRepository.DeleteByUserIdAndResourceTypeAndResourceIdIn(context.TODO(), ru.UserGroupId, ru.UserId, ru.ResourceType, ru.ResourceIds); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResourceAddByUserIdAssignEndPoint(c echo.Context) error {
|
func (api ResourceSharerApi) ResourceAddByUserIdAssignEndPoint(c echo.Context) error {
|
||||||
var ru RU
|
var ru dto.RU
|
||||||
if err := c.Bind(&ru); err != nil {
|
if err := c.Bind(&ru); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := resourceSharerRepository.AddSharerResources(ru.UserGroupId, ru.UserId, ru.StrategyId, ru.ResourceType, ru.ResourceIds); err != nil {
|
if err := repository.ResourceSharerRepository.AddSharerResources(ru.UserGroupId, ru.UserId, ru.StrategyId, ru.ResourceType, ru.ResourceIds); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,484 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"next-terminal/server/config"
|
|
||||||
"next-terminal/server/global/cache"
|
|
||||||
"next-terminal/server/log"
|
|
||||||
"next-terminal/server/model"
|
|
||||||
"next-terminal/server/repository"
|
|
||||||
"next-terminal/server/service"
|
|
||||||
"next-terminal/server/utils"
|
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
|
||||||
"github.com/labstack/echo/v4/middleware"
|
|
||||||
"gorm.io/driver/mysql"
|
|
||||||
"gorm.io/driver/sqlite"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"gorm.io/gorm/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
userRepository *repository.UserRepository
|
|
||||||
userGroupRepository *repository.UserGroupRepository
|
|
||||||
resourceSharerRepository *repository.ResourceSharerRepository
|
|
||||||
assetRepository *repository.AssetRepository
|
|
||||||
credentialRepository *repository.CredentialRepository
|
|
||||||
propertyRepository *repository.PropertyRepository
|
|
||||||
commandRepository *repository.CommandRepository
|
|
||||||
sessionRepository *repository.SessionRepository
|
|
||||||
accessSecurityRepository *repository.AccessSecurityRepository
|
|
||||||
accessGatewayRepository *repository.AccessGatewayRepository
|
|
||||||
jobRepository *repository.JobRepository
|
|
||||||
jobLogRepository *repository.JobLogRepository
|
|
||||||
loginLogRepository *repository.LoginLogRepository
|
|
||||||
storageRepository *repository.StorageRepository
|
|
||||||
strategyRepository *repository.StrategyRepository
|
|
||||||
|
|
||||||
jobService *service.JobService
|
|
||||||
propertyService *service.PropertyService
|
|
||||||
userService *service.UserService
|
|
||||||
sessionService *service.SessionService
|
|
||||||
mailService *service.MailService
|
|
||||||
assetService *service.AssetService
|
|
||||||
credentialService *service.CredentialService
|
|
||||||
storageService *service.StorageService
|
|
||||||
accessGatewayService *service.AccessGatewayService
|
|
||||||
)
|
|
||||||
|
|
||||||
func SetupRoutes(db *gorm.DB) *echo.Echo {
|
|
||||||
|
|
||||||
InitRepository(db)
|
|
||||||
InitService()
|
|
||||||
|
|
||||||
cache.GlobalCache.OnEvicted(userService.OnEvicted)
|
|
||||||
|
|
||||||
if err := InitDBData(); err != nil {
|
|
||||||
log.Errorf("初始化数据异常: %v", err.Error())
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ReloadData(); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
e := echo.New()
|
|
||||||
e.HideBanner = true
|
|
||||||
//e.Logger = log.GetEchoLogger()
|
|
||||||
//e.Use(log.Hook())
|
|
||||||
e.File("/", "web/build/index.html")
|
|
||||||
e.File("/asciinema.html", "web/build/asciinema.html")
|
|
||||||
e.File("/", "web/build/index.html")
|
|
||||||
e.File("/favicon.ico", "web/build/favicon.ico")
|
|
||||||
e.Static("/static", "web/build/static")
|
|
||||||
|
|
||||||
e.Use(middleware.Recover())
|
|
||||||
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
|
||||||
Skipper: middleware.DefaultSkipper,
|
|
||||||
AllowOrigins: []string{"*"},
|
|
||||||
AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete},
|
|
||||||
}))
|
|
||||||
e.Use(ErrorHandler)
|
|
||||||
e.Use(TcpWall)
|
|
||||||
e.Use(Auth)
|
|
||||||
|
|
||||||
e.POST("/login", LoginEndpoint)
|
|
||||||
e.POST("/loginWithTotp", loginWithTotpEndpoint)
|
|
||||||
|
|
||||||
e.GET("/tunnel", TunEndpoint)
|
|
||||||
e.GET("/ssh", SSHEndpoint)
|
|
||||||
e.GET("/ssh-monitor", SshMonitor)
|
|
||||||
e.POST("/logout", LogoutEndpoint)
|
|
||||||
e.POST("/change-password", ChangePasswordEndpoint)
|
|
||||||
e.GET("/reload-totp", ReloadTOTPEndpoint)
|
|
||||||
e.POST("/reset-totp", ResetTOTPEndpoint)
|
|
||||||
e.POST("/confirm-totp", ConfirmTOTPEndpoint)
|
|
||||||
e.GET("/info", InfoEndpoint)
|
|
||||||
|
|
||||||
account := e.Group("/account")
|
|
||||||
{
|
|
||||||
account.GET("/assets", AccountAssetEndpoint)
|
|
||||||
account.GET("/storage", AccountStorageEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
users := e.Group("/users", Admin)
|
|
||||||
{
|
|
||||||
users.POST("", UserCreateEndpoint)
|
|
||||||
users.GET("/paging", UserPagingEndpoint)
|
|
||||||
users.PUT("/:id", UserUpdateEndpoint)
|
|
||||||
users.PATCH("/:id/status", UserUpdateStatusEndpoint)
|
|
||||||
users.DELETE("/:id", UserDeleteEndpoint)
|
|
||||||
users.GET("/:id", UserGetEndpoint)
|
|
||||||
users.POST("/:id/change-password", UserChangePasswordEndpoint)
|
|
||||||
users.POST("/:id/reset-totp", UserResetTotpEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
userGroups := e.Group("/user-groups", Admin)
|
|
||||||
{
|
|
||||||
userGroups.POST("", UserGroupCreateEndpoint)
|
|
||||||
userGroups.GET("/paging", UserGroupPagingEndpoint)
|
|
||||||
userGroups.PUT("/:id", UserGroupUpdateEndpoint)
|
|
||||||
userGroups.DELETE("/:id", UserGroupDeleteEndpoint)
|
|
||||||
userGroups.GET("/:id", UserGroupGetEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
assets := e.Group("/assets", Admin)
|
|
||||||
{
|
|
||||||
assets.GET("", AssetAllEndpoint)
|
|
||||||
assets.POST("", AssetCreateEndpoint)
|
|
||||||
assets.POST("/import", AssetImportEndpoint)
|
|
||||||
assets.GET("/paging", AssetPagingEndpoint)
|
|
||||||
assets.POST("/:id/tcping", AssetTcpingEndpoint)
|
|
||||||
assets.PUT("/:id", AssetUpdateEndpoint)
|
|
||||||
assets.GET("/:id", AssetGetEndpoint)
|
|
||||||
assets.DELETE("/:id", AssetDeleteEndpoint)
|
|
||||||
assets.POST("/:id/change-owner", AssetChangeOwnerEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
e.GET("/tags", AssetTagsEndpoint)
|
|
||||||
|
|
||||||
commands := e.Group("/commands")
|
|
||||||
{
|
|
||||||
commands.GET("", CommandAllEndpoint)
|
|
||||||
commands.GET("/paging", CommandPagingEndpoint)
|
|
||||||
commands.POST("", CommandCreateEndpoint)
|
|
||||||
commands.PUT("/:id", CommandUpdateEndpoint)
|
|
||||||
commands.DELETE("/:id", CommandDeleteEndpoint)
|
|
||||||
commands.GET("/:id", CommandGetEndpoint)
|
|
||||||
commands.POST("/:id/change-owner", CommandChangeOwnerEndpoint, Admin)
|
|
||||||
}
|
|
||||||
|
|
||||||
credentials := e.Group("/credentials", Admin)
|
|
||||||
{
|
|
||||||
credentials.GET("", CredentialAllEndpoint)
|
|
||||||
credentials.GET("/paging", CredentialPagingEndpoint)
|
|
||||||
credentials.POST("", CredentialCreateEndpoint)
|
|
||||||
credentials.PUT("/:id", CredentialUpdateEndpoint)
|
|
||||||
credentials.DELETE("/:id", CredentialDeleteEndpoint)
|
|
||||||
credentials.GET("/:id", CredentialGetEndpoint)
|
|
||||||
credentials.POST("/:id/change-owner", CredentialChangeOwnerEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
sessions := e.Group("/sessions")
|
|
||||||
{
|
|
||||||
sessions.GET("/paging", Admin(SessionPagingEndpoint))
|
|
||||||
sessions.POST("/:id/disconnect", Admin(SessionDisconnectEndpoint))
|
|
||||||
sessions.DELETE("/:id", Admin(SessionDeleteEndpoint))
|
|
||||||
sessions.GET("/:id/recording", Admin(SessionRecordingEndpoint))
|
|
||||||
sessions.GET("/:id", Admin(SessionGetEndpoint))
|
|
||||||
sessions.POST("/:id/reviewed", Admin(SessionReviewedEndpoint))
|
|
||||||
sessions.POST("/:id/unreviewed", Admin(SessionUnViewedEndpoint))
|
|
||||||
sessions.POST("/clear", Admin(SessionClearEndpoint))
|
|
||||||
sessions.POST("/reviewed", Admin(SessionReviewedAllEndpoint))
|
|
||||||
|
|
||||||
sessions.POST("", SessionCreateEndpoint)
|
|
||||||
sessions.POST("/:id/connect", SessionConnectEndpoint)
|
|
||||||
sessions.POST("/:id/resize", SessionResizeEndpoint)
|
|
||||||
sessions.GET("/:id/stats", SessionStatsEndpoint)
|
|
||||||
|
|
||||||
sessions.POST("/:id/ls", SessionLsEndpoint)
|
|
||||||
sessions.GET("/:id/download", SessionDownloadEndpoint)
|
|
||||||
sessions.POST("/:id/upload", SessionUploadEndpoint)
|
|
||||||
sessions.POST("/:id/edit", SessionEditEndpoint)
|
|
||||||
sessions.POST("/:id/mkdir", SessionMkDirEndpoint)
|
|
||||||
sessions.POST("/:id/rm", SessionRmEndpoint)
|
|
||||||
sessions.POST("/:id/rename", SessionRenameEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceSharers := e.Group("/resource-sharers", Admin)
|
|
||||||
{
|
|
||||||
resourceSharers.GET("", RSGetSharersEndPoint)
|
|
||||||
resourceSharers.POST("/remove-resources", ResourceRemoveByUserIdAssignEndPoint)
|
|
||||||
resourceSharers.POST("/add-resources", ResourceAddByUserIdAssignEndPoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
loginLogs := e.Group("login-logs", Admin)
|
|
||||||
{
|
|
||||||
loginLogs.GET("/paging", LoginLogPagingEndpoint)
|
|
||||||
loginLogs.DELETE("/:id", LoginLogDeleteEndpoint)
|
|
||||||
loginLogs.POST("/clear", LoginLogClearEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
e.GET("/properties", Admin(PropertyGetEndpoint))
|
|
||||||
e.PUT("/properties", Admin(PropertyUpdateEndpoint))
|
|
||||||
|
|
||||||
overview := e.Group("overview", Admin)
|
|
||||||
{
|
|
||||||
overview.GET("/counter", OverviewCounterEndPoint)
|
|
||||||
overview.GET("/asset", OverviewAssetEndPoint)
|
|
||||||
overview.GET("/access", OverviewAccessEndPoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
jobs := e.Group("/jobs", Admin)
|
|
||||||
{
|
|
||||||
jobs.POST("", JobCreateEndpoint)
|
|
||||||
jobs.GET("/paging", JobPagingEndpoint)
|
|
||||||
jobs.PUT("/:id", JobUpdateEndpoint)
|
|
||||||
jobs.POST("/:id/change-status", JobChangeStatusEndpoint)
|
|
||||||
jobs.POST("/:id/exec", JobExecEndpoint)
|
|
||||||
jobs.DELETE("/:id", JobDeleteEndpoint)
|
|
||||||
jobs.GET("/:id", JobGetEndpoint)
|
|
||||||
jobs.GET("/:id/logs", JobGetLogsEndpoint)
|
|
||||||
jobs.DELETE("/:id/logs", JobDeleteLogsEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
securities := e.Group("/securities", Admin)
|
|
||||||
{
|
|
||||||
securities.POST("", SecurityCreateEndpoint)
|
|
||||||
securities.GET("/paging", SecurityPagingEndpoint)
|
|
||||||
securities.PUT("/:id", SecurityUpdateEndpoint)
|
|
||||||
securities.DELETE("/:id", SecurityDeleteEndpoint)
|
|
||||||
securities.GET("/:id", SecurityGetEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
storages := e.Group("/storages")
|
|
||||||
{
|
|
||||||
storages.GET("/paging", StoragePagingEndpoint, Admin)
|
|
||||||
storages.POST("", StorageCreateEndpoint, Admin)
|
|
||||||
storages.DELETE("/:id", StorageDeleteEndpoint, Admin)
|
|
||||||
storages.PUT("/:id", StorageUpdateEndpoint, Admin)
|
|
||||||
storages.GET("/shares", StorageSharesEndpoint, Admin)
|
|
||||||
storages.GET("/:id", StorageGetEndpoint, Admin)
|
|
||||||
|
|
||||||
storages.POST("/:storageId/ls", StorageLsEndpoint)
|
|
||||||
storages.GET("/:storageId/download", StorageDownloadEndpoint)
|
|
||||||
storages.POST("/:storageId/upload", StorageUploadEndpoint)
|
|
||||||
storages.POST("/:storageId/mkdir", StorageMkDirEndpoint)
|
|
||||||
storages.POST("/:storageId/rm", StorageRmEndpoint)
|
|
||||||
storages.POST("/:storageId/rename", StorageRenameEndpoint)
|
|
||||||
storages.POST("/:storageId/edit", StorageEditEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
strategies := e.Group("/strategies", Admin)
|
|
||||||
{
|
|
||||||
strategies.GET("", StrategyAllEndpoint)
|
|
||||||
strategies.GET("/paging", StrategyPagingEndpoint)
|
|
||||||
strategies.POST("", StrategyCreateEndpoint)
|
|
||||||
strategies.DELETE("/:id", StrategyDeleteEndpoint)
|
|
||||||
strategies.PUT("/:id", StrategyUpdateEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
accessGateways := e.Group("/access-gateways", Admin)
|
|
||||||
{
|
|
||||||
accessGateways.GET("", AccessGatewayAllEndpoint)
|
|
||||||
accessGateways.POST("", AccessGatewayCreateEndpoint)
|
|
||||||
accessGateways.GET("/paging", AccessGatewayPagingEndpoint)
|
|
||||||
accessGateways.PUT("/:id", AccessGatewayUpdateEndpoint)
|
|
||||||
accessGateways.DELETE("/:id", AccessGatewayDeleteEndpoint)
|
|
||||||
accessGateways.GET("/:id", AccessGatewayGetEndpoint)
|
|
||||||
accessGateways.POST("/:id/reconnect", AccessGatewayReconnectEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
backup := e.Group("/backup", Admin)
|
|
||||||
{
|
|
||||||
backup.GET("/export", BackupExportEndpoint)
|
|
||||||
backup.POST("/import", BackupImportEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReloadData() error {
|
|
||||||
if err := ReloadAccessSecurity(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ReloadToken(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitRepository(db *gorm.DB) {
|
|
||||||
userRepository = repository.NewUserRepository(db)
|
|
||||||
userGroupRepository = repository.NewUserGroupRepository(db)
|
|
||||||
resourceSharerRepository = repository.NewResourceSharerRepository(db)
|
|
||||||
assetRepository = repository.NewAssetRepository(db)
|
|
||||||
credentialRepository = repository.NewCredentialRepository(db)
|
|
||||||
propertyRepository = repository.NewPropertyRepository(db)
|
|
||||||
commandRepository = repository.NewCommandRepository(db)
|
|
||||||
sessionRepository = repository.NewSessionRepository(db)
|
|
||||||
accessSecurityRepository = repository.NewAccessSecurityRepository(db)
|
|
||||||
accessGatewayRepository = repository.NewAccessGatewayRepository(db)
|
|
||||||
jobRepository = repository.NewJobRepository(db)
|
|
||||||
jobLogRepository = repository.NewJobLogRepository(db)
|
|
||||||
loginLogRepository = repository.NewLoginLogRepository(db)
|
|
||||||
storageRepository = repository.NewStorageRepository(db)
|
|
||||||
strategyRepository = repository.NewStrategyRepository(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitService() {
|
|
||||||
propertyService = service.NewPropertyService(propertyRepository)
|
|
||||||
userService = service.NewUserService(userRepository, loginLogRepository)
|
|
||||||
sessionService = service.NewSessionService(sessionRepository)
|
|
||||||
mailService = service.NewMailService(propertyRepository)
|
|
||||||
assetService = service.NewAssetService(assetRepository)
|
|
||||||
jobService = service.NewJobService(jobRepository, jobLogRepository, assetRepository, credentialRepository, assetService)
|
|
||||||
credentialService = service.NewCredentialService(credentialRepository)
|
|
||||||
storageService = service.NewStorageService(storageRepository, userRepository, propertyRepository)
|
|
||||||
accessGatewayService = service.NewAccessGatewayService(accessGatewayRepository)
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitDBData() (err error) {
|
|
||||||
if err := propertyService.DeleteDeprecatedProperty(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := accessGatewayService.ReConnectAll(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := propertyService.InitProperties(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := userService.InitUser(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := jobService.InitJob(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := userService.FixUserOnlineState(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := sessionService.FixSessionState(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := sessionService.EmptyPassword(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := credentialService.Encrypt(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := assetService.Encrypt(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := storageService.InitStorages(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResetPassword(username string) error {
|
|
||||||
user, err := userRepository.FindByUsername(username)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
password := "next-terminal"
|
|
||||||
passwd, err := utils.Encoder.Encode([]byte(password))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
u := &model.User{
|
|
||||||
Password: string(passwd),
|
|
||||||
ID: user.ID,
|
|
||||||
}
|
|
||||||
if err := userRepository.Update(u); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Debugf("用户「%v」密码初始化为: %v", user.Username, password)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResetTotp(username string) error {
|
|
||||||
user, err := userRepository.FindByUsername(username)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
u := &model.User{
|
|
||||||
TOTPSecret: "-",
|
|
||||||
ID: user.ID,
|
|
||||||
}
|
|
||||||
if err := userRepository.Update(u); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Debugf("用户「%v」已重置TOTP", user.Username)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ChangeEncryptionKey(oldEncryptionKey, newEncryptionKey string) error {
|
|
||||||
|
|
||||||
oldPassword := []byte(fmt.Sprintf("%x", md5.Sum([]byte(oldEncryptionKey))))
|
|
||||||
newPassword := []byte(fmt.Sprintf("%x", md5.Sum([]byte(newEncryptionKey))))
|
|
||||||
|
|
||||||
credentials, err := credentialRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for i := range credentials {
|
|
||||||
credential := credentials[i]
|
|
||||||
if err := credentialRepository.Decrypt(&credential, oldPassword); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := credentialRepository.Encrypt(&credential, newPassword); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := credentialRepository.UpdateById(&credential, credential.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assets, err := assetRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for i := range assets {
|
|
||||||
asset := assets[i]
|
|
||||||
if err := assetRepository.Decrypt(&asset, oldPassword); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := assetRepository.Encrypt(&asset, newPassword); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := assetRepository.UpdateById(&asset, asset.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Infof("encryption key has being changed.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetupDB() *gorm.DB {
|
|
||||||
|
|
||||||
var logMode logger.Interface
|
|
||||||
if config.GlobalCfg.Debug {
|
|
||||||
logMode = logger.Default.LogMode(logger.Info)
|
|
||||||
} else {
|
|
||||||
logMode = logger.Default.LogMode(logger.Silent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("当前数据库模式为:%v\n", config.GlobalCfg.DB)
|
|
||||||
var err error
|
|
||||||
var db *gorm.DB
|
|
||||||
if config.GlobalCfg.DB == "mysql" {
|
|
||||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=60s",
|
|
||||||
config.GlobalCfg.Mysql.Username,
|
|
||||||
config.GlobalCfg.Mysql.Password,
|
|
||||||
config.GlobalCfg.Mysql.Hostname,
|
|
||||||
config.GlobalCfg.Mysql.Port,
|
|
||||||
config.GlobalCfg.Mysql.Database,
|
|
||||||
)
|
|
||||||
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
|
|
||||||
Logger: logMode,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
db, err = gorm.Open(sqlite.Open(config.GlobalCfg.Sqlite.File), &gorm.Config{
|
|
||||||
Logger: logMode,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("连接数据库异常: %v", err.Error())
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.AutoMigrate(&model.User{}, &model.Asset{}, &model.AssetAttribute{}, &model.Session{}, &model.Command{},
|
|
||||||
&model.Credential{}, &model.Property{}, &model.ResourceSharer{}, &model.UserGroup{}, &model.UserGroupMember{},
|
|
||||||
&model.LoginLog{}, &model.Job{}, &model.JobLog{}, &model.AccessSecurity{}, &model.AccessGateway{},
|
|
||||||
&model.Storage{}, &model.Strategy{}); err != nil {
|
|
||||||
log.Errorf("初始化数据库表结构异常: %v", err.Error())
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
return db
|
|
||||||
}
|
|
@ -1,17 +1,22 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"next-terminal/server/global/security"
|
"next-terminal/server/global/security"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SecurityCreateEndpoint(c echo.Context) error {
|
type SecurityApi struct{}
|
||||||
|
|
||||||
|
func (api SecurityApi) SecurityCreateEndpoint(c echo.Context) error {
|
||||||
var item model.AccessSecurity
|
var item model.AccessSecurity
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -20,7 +25,7 @@ func SecurityCreateEndpoint(c echo.Context) error {
|
|||||||
item.ID = utils.UUID()
|
item.ID = utils.UUID()
|
||||||
item.Source = "管理员添加"
|
item.Source = "管理员添加"
|
||||||
|
|
||||||
if err := accessSecurityRepository.Create(&item); err != nil {
|
if err := repository.SecurityRepository.Create(context.TODO(), &item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// 更新内存中的安全规则
|
// 更新内存中的安全规则
|
||||||
@ -35,29 +40,7 @@ func SecurityCreateEndpoint(c echo.Context) error {
|
|||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReloadAccessSecurity() error {
|
func (api SecurityApi) SecurityPagingEndpoint(c echo.Context) error {
|
||||||
rules, err := accessSecurityRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(rules) > 0 {
|
|
||||||
// 先清空
|
|
||||||
security.GlobalSecurityManager.Clear()
|
|
||||||
// 再添加到全局的安全管理器中
|
|
||||||
for i := 0; i < len(rules); i++ {
|
|
||||||
rule := &security.Security{
|
|
||||||
ID: rules[i].ID,
|
|
||||||
IP: rules[i].IP,
|
|
||||||
Rule: rules[i].Rule,
|
|
||||||
Priority: rules[i].Priority,
|
|
||||||
}
|
|
||||||
security.GlobalSecurityManager.Add <- rule
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SecurityPagingEndpoint(c echo.Context) error {
|
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
ip := c.QueryParam("ip")
|
ip := c.QueryParam("ip")
|
||||||
@ -66,18 +49,18 @@ func SecurityPagingEndpoint(c echo.Context) error {
|
|||||||
order := c.QueryParam("order")
|
order := c.QueryParam("order")
|
||||||
field := c.QueryParam("field")
|
field := c.QueryParam("field")
|
||||||
|
|
||||||
items, total, err := accessSecurityRepository.Find(pageIndex, pageSize, ip, rule, order, field)
|
items, total, err := repository.SecurityRepository.Find(context.TODO(), pageIndex, pageSize, ip, rule, order, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func SecurityUpdateEndpoint(c echo.Context) error {
|
func (api SecurityApi) SecurityUpdateEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
var item model.AccessSecurity
|
var item model.AccessSecurity
|
||||||
@ -85,7 +68,7 @@ func SecurityUpdateEndpoint(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := accessSecurityRepository.UpdateById(&item, id); err != nil {
|
if err := repository.SecurityRepository.UpdateById(context.TODO(), &item, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// 更新内存中的安全规则
|
// 更新内存中的安全规则
|
||||||
@ -101,13 +84,13 @@ func SecurityUpdateEndpoint(c echo.Context) error {
|
|||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SecurityDeleteEndpoint(c echo.Context) error {
|
func (api SecurityApi) SecurityDeleteEndpoint(c echo.Context) error {
|
||||||
ids := c.Param("id")
|
ids := c.Param("id")
|
||||||
|
|
||||||
split := strings.Split(ids, ",")
|
split := strings.Split(ids, ",")
|
||||||
for i := range split {
|
for i := range split {
|
||||||
id := split[i]
|
id := split[i]
|
||||||
if err := accessSecurityRepository.DeleteById(id); err != nil {
|
if err := repository.SecurityRepository.DeleteById(context.TODO(), id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// 更新内存中的安全规则
|
// 更新内存中的安全规则
|
||||||
@ -117,10 +100,10 @@ func SecurityDeleteEndpoint(c echo.Context) error {
|
|||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SecurityGetEndpoint(c echo.Context) error {
|
func (api SecurityApi) SecurityGetEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
item, err := accessSecurityRepository.FindById(id)
|
item, err := repository.SecurityRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -11,23 +12,22 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
"next-terminal/server/global/session"
|
"next-terminal/server/global/session"
|
||||||
"next-terminal/server/guacd"
|
|
||||||
"next-terminal/server/log"
|
"next-terminal/server/log"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
"next-terminal/server/service"
|
"next-terminal/server/service"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func SessionPagingEndpoint(c echo.Context) error {
|
type SessionApi struct{}
|
||||||
|
|
||||||
|
func (api SessionApi) SessionPagingEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
status := c.QueryParam("status")
|
status := c.QueryParam("status")
|
||||||
@ -37,7 +37,7 @@ func SessionPagingEndpoint(c echo.Context) error {
|
|||||||
protocol := c.QueryParam("protocol")
|
protocol := c.QueryParam("protocol")
|
||||||
reviewed := c.QueryParam("reviewed")
|
reviewed := c.QueryParam("reviewed")
|
||||||
|
|
||||||
items, total, err := sessionRepository.Find(pageIndex, pageSize, status, userId, clientIp, assetId, protocol, reviewed)
|
items, total, err := repository.SessionRepository.Find(context.TODO(), pageIndex, pageSize, status, userId, clientIp, assetId, protocol, reviewed)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -63,15 +63,15 @@ func SessionPagingEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionDeleteEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionDeleteEndpoint(c echo.Context) error {
|
||||||
sessionIds := strings.Split(c.Param("id"), ",")
|
sessionIds := strings.Split(c.Param("id"), ",")
|
||||||
err := sessionRepository.DeleteByIds(sessionIds)
|
err := repository.SessionRepository.DeleteByIds(context.TODO(), sessionIds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -79,38 +79,38 @@ func SessionDeleteEndpoint(c echo.Context) error {
|
|||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionClearEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionClearEndpoint(c echo.Context) error {
|
||||||
err := sessionService.ClearOfflineSession()
|
err := service.SessionService.ClearOfflineSession()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionReviewedEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionReviewedEndpoint(c echo.Context) error {
|
||||||
sessionIds := strings.Split(c.Param("id"), ",")
|
sessionIds := strings.Split(c.Param("id"), ",")
|
||||||
if err := sessionRepository.UpdateReadByIds(true, sessionIds); err != nil {
|
if err := repository.SessionRepository.UpdateReadByIds(context.TODO(), true, sessionIds); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionUnViewedEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionUnViewedEndpoint(c echo.Context) error {
|
||||||
sessionIds := strings.Split(c.Param("id"), ",")
|
sessionIds := strings.Split(c.Param("id"), ",")
|
||||||
if err := sessionRepository.UpdateReadByIds(false, sessionIds); err != nil {
|
if err := repository.SessionRepository.UpdateReadByIds(context.TODO(), false, sessionIds); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionReviewedAllEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionReviewedAllEndpoint(c echo.Context) error {
|
||||||
if err := sessionService.ReviewedAll(); err != nil {
|
if err := service.SessionService.ReviewedAll(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionConnectEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionConnectEndpoint(c echo.Context) error {
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
|
|
||||||
s := model.Session{}
|
s := model.Session{}
|
||||||
@ -118,112 +118,37 @@ func SessionConnectEndpoint(c echo.Context) error {
|
|||||||
s.Status = constant.Connected
|
s.Status = constant.Connected
|
||||||
s.ConnectedTime = utils.NowJsonTime()
|
s.ConnectedTime = utils.NowJsonTime()
|
||||||
|
|
||||||
if err := sessionRepository.UpdateById(&s, sessionId); err != nil {
|
if err := repository.SessionRepository.UpdateById(context.TODO(), &s, sessionId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
o, err := sessionRepository.FindById(sessionId)
|
o, err := repository.SessionRepository.FindById(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
asset, err := assetRepository.FindById(o.AssetId)
|
asset, err := repository.AssetRepository.FindById(context.TODO(), o.AssetId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !asset.Active {
|
if !asset.Active {
|
||||||
asset.Active = true
|
asset.Active = true
|
||||||
_ = assetRepository.UpdateById(&asset, asset.ID)
|
_ = repository.AssetRepository.UpdateById(context.TODO(), &asset, asset.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionDisconnectEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionDisconnectEndpoint(c echo.Context) error {
|
||||||
sessionIds := c.Param("id")
|
sessionIds := c.Param("id")
|
||||||
|
|
||||||
split := strings.Split(sessionIds, ",")
|
split := strings.Split(sessionIds, ",")
|
||||||
for i := range split {
|
for i := range split {
|
||||||
CloseSessionById(split[i], ForcedDisconnect, "管理员强制关闭了此会话")
|
service.SessionService.CloseSessionById(split[i], ForcedDisconnect, "管理员强制关闭了此会话")
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
var mutex sync.Mutex
|
func (api SessionApi) SessionResizeEndpoint(c echo.Context) error {
|
||||||
|
|
||||||
func CloseSessionById(sessionId string, code int, reason string) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
|
||||||
if nextSession != nil {
|
|
||||||
log.Debugf("[%v] 会话关闭,原因:%v", sessionId, reason)
|
|
||||||
WriteCloseMessage(nextSession.WebSocket, nextSession.Mode, code, reason)
|
|
||||||
|
|
||||||
if nextSession.Observer != nil {
|
|
||||||
obs := nextSession.Observer.All()
|
|
||||||
for _, ob := range obs {
|
|
||||||
WriteCloseMessage(ob.WebSocket, ob.Mode, code, reason)
|
|
||||||
log.Debugf("[%v] 强制踢出会话的观察者: %v", sessionId, ob.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
session.GlobalSessionManager.Del <- sessionId
|
|
||||||
|
|
||||||
DisDBSess(sessionId, code, reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteCloseMessage(ws *websocket.Conn, mode string, code int, reason string) {
|
|
||||||
switch mode {
|
|
||||||
case constant.Guacd:
|
|
||||||
if ws != nil {
|
|
||||||
err := guacd.NewInstruction("error", "", strconv.Itoa(code))
|
|
||||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(err.String()))
|
|
||||||
disconnect := guacd.NewInstruction("disconnect")
|
|
||||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(disconnect.String()))
|
|
||||||
}
|
|
||||||
case constant.Naive:
|
|
||||||
if ws != nil {
|
|
||||||
msg := `0` + reason
|
|
||||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(msg))
|
|
||||||
}
|
|
||||||
case constant.Terminal:
|
|
||||||
// 这里是关闭观察者的ssh会话
|
|
||||||
if ws != nil {
|
|
||||||
msg := `0` + reason
|
|
||||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func DisDBSess(sessionId string, code int, reason string) {
|
|
||||||
s, err := sessionRepository.FindById(sessionId)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Status == constant.Disconnected {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Status == constant.Connecting {
|
|
||||||
// 会话还未建立成功,无需保留数据
|
|
||||||
_ = sessionRepository.DeleteById(sessionId)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ss := model.Session{}
|
|
||||||
ss.ID = sessionId
|
|
||||||
ss.Status = constant.Disconnected
|
|
||||||
ss.DisconnectedTime = utils.NowJsonTime()
|
|
||||||
ss.Code = code
|
|
||||||
ss.Message = reason
|
|
||||||
ss.Password = "-"
|
|
||||||
ss.PrivateKey = "-"
|
|
||||||
ss.Passphrase = "-"
|
|
||||||
|
|
||||||
_ = sessionRepository.UpdateById(&ss, sessionId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SessionResizeEndpoint(c echo.Context) error {
|
|
||||||
width := c.QueryParam("width")
|
width := c.QueryParam("width")
|
||||||
height := c.QueryParam("height")
|
height := c.QueryParam("height")
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
@ -235,13 +160,13 @@ func SessionResizeEndpoint(c echo.Context) error {
|
|||||||
intWidth, _ := strconv.Atoi(width)
|
intWidth, _ := strconv.Atoi(width)
|
||||||
intHeight, _ := strconv.Atoi(height)
|
intHeight, _ := strconv.Atoi(height)
|
||||||
|
|
||||||
if err := sessionRepository.UpdateWindowSizeById(intWidth, intHeight, sessionId); err != nil {
|
if err := repository.SessionRepository.UpdateWindowSizeById(context.TODO(), intWidth, intHeight, sessionId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionCreateEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionCreateEndpoint(c echo.Context) error {
|
||||||
assetId := c.QueryParam("assetId")
|
assetId := c.QueryParam("assetId")
|
||||||
mode := c.QueryParam("mode")
|
mode := c.QueryParam("mode")
|
||||||
|
|
||||||
@ -253,106 +178,11 @@ func SessionCreateEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
user, _ := GetCurrentAccount(c)
|
user, _ := GetCurrentAccount(c)
|
||||||
|
|
||||||
asset, err := assetRepository.FindById(assetId)
|
s, err := service.SessionService.Create(c.RealIP(), assetId, mode, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
upload = "1"
|
|
||||||
download = "1"
|
|
||||||
_delete = "1"
|
|
||||||
rename = "1"
|
|
||||||
edit = "1"
|
|
||||||
fileSystem = "1"
|
|
||||||
)
|
|
||||||
if asset.Owner != user.ID && constant.TypeUser == user.Type {
|
|
||||||
// 普通用户访问非自己创建的资产需要校验权限
|
|
||||||
resourceSharers, err := resourceSharerRepository.FindByResourceIdAndUserId(assetId, user.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(resourceSharers) == 0 {
|
|
||||||
return errors.New("您没有权限访问此资产")
|
|
||||||
}
|
|
||||||
strategyId := resourceSharers[0].StrategyId
|
|
||||||
if strategyId != "" {
|
|
||||||
strategy, err := strategyRepository.FindById(strategyId)
|
|
||||||
if err != nil {
|
|
||||||
if !errors.Is(gorm.ErrRecordNotFound, err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
upload = strategy.Upload
|
|
||||||
download = strategy.Download
|
|
||||||
_delete = strategy.Delete
|
|
||||||
rename = strategy.Rename
|
|
||||||
edit = strategy.Edit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var storageId = ""
|
|
||||||
if constant.RDP == asset.Protocol {
|
|
||||||
attr, err := assetRepository.FindAssetAttrMapByAssetId(assetId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if "true" == attr[guacd.EnableDrive] {
|
|
||||||
fileSystem = "1"
|
|
||||||
storageId = attr[guacd.DrivePath]
|
|
||||||
if storageId == "" {
|
|
||||||
storageId = user.ID
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fileSystem = "0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s := &model.Session{
|
|
||||||
ID: utils.UUID(),
|
|
||||||
AssetId: asset.ID,
|
|
||||||
Username: asset.Username,
|
|
||||||
Password: asset.Password,
|
|
||||||
PrivateKey: asset.PrivateKey,
|
|
||||||
Passphrase: asset.Passphrase,
|
|
||||||
Protocol: asset.Protocol,
|
|
||||||
IP: asset.IP,
|
|
||||||
Port: asset.Port,
|
|
||||||
Status: constant.NoConnect,
|
|
||||||
Creator: user.ID,
|
|
||||||
ClientIP: c.RealIP(),
|
|
||||||
Mode: mode,
|
|
||||||
Upload: upload,
|
|
||||||
Download: download,
|
|
||||||
Delete: _delete,
|
|
||||||
Rename: rename,
|
|
||||||
Edit: edit,
|
|
||||||
StorageId: storageId,
|
|
||||||
AccessGatewayId: asset.AccessGatewayId,
|
|
||||||
Reviewed: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
if asset.AccountType == "credential" {
|
|
||||||
credential, err := credentialRepository.FindById(asset.CredentialId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if credential.Type == constant.Custom {
|
|
||||||
s.Username = credential.Username
|
|
||||||
s.Password = credential.Password
|
|
||||||
} else {
|
|
||||||
s.Username = credential.Username
|
|
||||||
s.PrivateKey = credential.PrivateKey
|
|
||||||
s.Passphrase = credential.Passphrase
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := sessionRepository.Create(s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return Success(c, echo.Map{
|
return Success(c, echo.Map{
|
||||||
"id": s.ID,
|
"id": s.ID,
|
||||||
"upload": s.Upload,
|
"upload": s.Upload,
|
||||||
@ -361,13 +191,15 @@ func SessionCreateEndpoint(c echo.Context) error {
|
|||||||
"rename": s.Rename,
|
"rename": s.Rename,
|
||||||
"edit": s.Edit,
|
"edit": s.Edit,
|
||||||
"storageId": s.StorageId,
|
"storageId": s.StorageId,
|
||||||
"fileSystem": fileSystem,
|
"fileSystem": s.FileSystem,
|
||||||
|
"copy": s.Copy,
|
||||||
|
"paste": s.Paste,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionUploadEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionUploadEndpoint(c echo.Context) error {
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
s, err := sessionRepository.FindById(sessionId)
|
s, err := repository.SessionRepository.FindById(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -414,15 +246,18 @@ func SessionUploadEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
} else if "rdp" == s.Protocol {
|
} else if "rdp" == s.Protocol {
|
||||||
return StorageUpload(c, file, s.StorageId)
|
if err := service.StorageService.StorageUpload(c, file, s.StorageId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionEditEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionEditEndpoint(c echo.Context) error {
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
s, err := sessionRepository.FindById(sessionId)
|
s, err := repository.SessionRepository.FindById(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -453,14 +288,17 @@ func SessionEditEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
} else if "rdp" == s.Protocol {
|
} else if "rdp" == s.Protocol {
|
||||||
return StorageEdit(c, file, fileContent, s.StorageId)
|
if err := service.StorageService.StorageEdit(file, fileContent, s.StorageId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionDownloadEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionDownloadEndpoint(c echo.Context) error {
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
s, err := sessionRepository.FindById(sessionId)
|
s, err := repository.SessionRepository.FindById(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -492,15 +330,15 @@ func SessionDownloadEndpoint(c echo.Context) error {
|
|||||||
return c.Stream(http.StatusOK, echo.MIMEOctetStream, bytes.NewReader(buff.Bytes()))
|
return c.Stream(http.StatusOK, echo.MIMEOctetStream, bytes.NewReader(buff.Bytes()))
|
||||||
} else if "rdp" == s.Protocol {
|
} else if "rdp" == s.Protocol {
|
||||||
storageId := s.StorageId
|
storageId := s.StorageId
|
||||||
return StorageDownload(c, remoteFile, storageId)
|
return service.StorageService.StorageDownload(c, remoteFile, storageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionLsEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionLsEndpoint(c echo.Context) error {
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
s, err := sessionRepository.FindByIdAndDecrypt(sessionId)
|
s, err := service.SessionService.FindByIdAndDecrypt(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -550,15 +388,19 @@ func SessionLsEndpoint(c echo.Context) error {
|
|||||||
return Success(c, files)
|
return Success(c, files)
|
||||||
} else if "rdp" == s.Protocol {
|
} else if "rdp" == s.Protocol {
|
||||||
storageId := s.StorageId
|
storageId := s.StorageId
|
||||||
return StorageLs(c, remoteDir, storageId)
|
err, files := service.StorageService.StorageLs(remoteDir, storageId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Success(c, files)
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New("当前协议不支持此操作")
|
return errors.New("当前协议不支持此操作")
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionMkDirEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionMkDirEndpoint(c echo.Context) error {
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
s, err := sessionRepository.FindById(sessionId)
|
s, err := repository.SessionRepository.FindById(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -576,14 +418,18 @@ func SessionMkDirEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
} else if "rdp" == s.Protocol {
|
} else if "rdp" == s.Protocol {
|
||||||
return StorageMkDir(c, remoteDir, s.StorageId)
|
storageId := s.StorageId
|
||||||
|
if err := service.StorageService.StorageMkDir(remoteDir, storageId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
return errors.New("当前协议不支持此操作")
|
return errors.New("当前协议不支持此操作")
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionRmEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionRmEndpoint(c echo.Context) error {
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
s, err := sessionRepository.FindById(sessionId)
|
s, err := repository.SessionRepository.FindById(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -628,15 +474,19 @@ func SessionRmEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
} else if "rdp" == s.Protocol {
|
} else if "rdp" == s.Protocol {
|
||||||
return StorageRm(c, file, s.StorageId)
|
storageId := s.StorageId
|
||||||
|
if err := service.StorageService.StorageRm(file, storageId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New("当前协议不支持此操作")
|
return errors.New("当前协议不支持此操作")
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionRenameEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionRenameEndpoint(c echo.Context) error {
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
s, err := sessionRepository.FindById(sessionId)
|
s, err := repository.SessionRepository.FindById(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -659,14 +509,18 @@ func SessionRenameEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
} else if "rdp" == s.Protocol {
|
} else if "rdp" == s.Protocol {
|
||||||
return StorageRename(c, oldName, newName, s.StorageId)
|
storageId := s.StorageId
|
||||||
|
if err := service.StorageService.StorageRename(oldName, newName, storageId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
return errors.New("当前协议不支持此操作")
|
return errors.New("当前协议不支持此操作")
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionRecordingEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionRecordingEndpoint(c echo.Context) error {
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
s, err := sessionRepository.FindById(sessionId)
|
s, err := repository.SessionRepository.FindById(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -677,24 +531,24 @@ func SessionRecordingEndpoint(c echo.Context) error {
|
|||||||
} else {
|
} else {
|
||||||
recording = s.Recording + "/recording"
|
recording = s.Recording + "/recording"
|
||||||
}
|
}
|
||||||
_ = sessionRepository.UpdateReadByIds(true, []string{sessionId})
|
_ = repository.SessionRepository.UpdateReadByIds(context.TODO(), true, []string{sessionId})
|
||||||
|
|
||||||
log.Debugf("读取录屏文件:%v,是否存在: %v, 是否为文件: %v", recording, utils.FileExists(recording), utils.IsFile(recording))
|
log.Debugf("读取录屏文件:%v,是否存在: %v, 是否为文件: %v", recording, utils.FileExists(recording), utils.IsFile(recording))
|
||||||
return c.File(recording)
|
return c.File(recording)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionGetEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionGetEndpoint(c echo.Context) error {
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
s, err := sessionRepository.FindById(sessionId)
|
s, err := repository.SessionRepository.FindById(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, s)
|
return Success(c, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionStatsEndpoint(c echo.Context) error {
|
func (api SessionApi) SessionStatsEndpoint(c echo.Context) error {
|
||||||
sessionId := c.Param("id")
|
sessionId := c.Param("id")
|
||||||
s, err := sessionRepository.FindByIdAndDecrypt(sessionId)
|
s, err := service.SessionService.FindByIdAndDecrypt(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,435 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"next-terminal/server/config"
|
|
||||||
"next-terminal/server/constant"
|
|
||||||
"next-terminal/server/global/session"
|
|
||||||
"next-terminal/server/guacd"
|
|
||||||
"next-terminal/server/log"
|
|
||||||
"next-terminal/server/model"
|
|
||||||
"next-terminal/server/term"
|
|
||||||
"next-terminal/server/utils"
|
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/labstack/echo/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
var UpGrader = websocket.Upgrader{
|
|
||||||
CheckOrigin: func(r *http.Request) bool {
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
Subprotocols: []string{"guacamole"},
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
Closed = 0
|
|
||||||
Connected = 1
|
|
||||||
Data = 2
|
|
||||||
Resize = 3
|
|
||||||
Ping = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
Type int `json:"type"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Message) ToString() string {
|
|
||||||
if r.Content != "" {
|
|
||||||
return strconv.Itoa(r.Type) + r.Content
|
|
||||||
} else {
|
|
||||||
return strconv.Itoa(r.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMessage(_type int, content string) Message {
|
|
||||||
return Message{Content: content, Type: _type}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseMessage(value string) (message Message, err error) {
|
|
||||||
if value == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_type, err := strconv.Atoi(value[:1])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var content = value[1:]
|
|
||||||
message = NewMessage(_type, content)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type WindowSize struct {
|
|
||||||
Cols int `json:"cols"`
|
|
||||||
Rows int `json:"rows"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func SSHEndpoint(c echo.Context) (err error) {
|
|
||||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer ws.Close()
|
|
||||||
|
|
||||||
sessionId := c.QueryParam("sessionId")
|
|
||||||
cols, _ := strconv.Atoi(c.QueryParam("cols"))
|
|
||||||
rows, _ := strconv.Atoi(c.QueryParam("rows"))
|
|
||||||
|
|
||||||
s, err := sessionRepository.FindByIdAndDecrypt(sessionId)
|
|
||||||
if err != nil {
|
|
||||||
return WriteMessage(ws, NewMessage(Closed, "获取会话失败"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := permissionCheck(c, s.AssetId); err != nil {
|
|
||||||
return WriteMessage(ws, NewMessage(Closed, err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
username = s.Username
|
|
||||||
password = s.Password
|
|
||||||
privateKey = s.PrivateKey
|
|
||||||
passphrase = s.Passphrase
|
|
||||||
ip = s.IP
|
|
||||||
port = s.Port
|
|
||||||
)
|
|
||||||
|
|
||||||
if s.AccessGatewayId != "" && s.AccessGatewayId != "-" {
|
|
||||||
g, err := accessGatewayService.GetGatewayAndReconnectById(s.AccessGatewayId)
|
|
||||||
if err != nil {
|
|
||||||
return WriteMessage(ws, NewMessage(Closed, "获取接入网关失败:"+err.Error()))
|
|
||||||
}
|
|
||||||
if !g.Connected {
|
|
||||||
return WriteMessage(ws, NewMessage(Closed, "接入网关不可用:"+g.Message))
|
|
||||||
}
|
|
||||||
exposedIP, exposedPort, err := g.OpenSshTunnel(s.ID, ip, port)
|
|
||||||
if err != nil {
|
|
||||||
return WriteMessage(ws, NewMessage(Closed, "创建隧道失败:"+err.Error()))
|
|
||||||
}
|
|
||||||
defer g.CloseSshTunnel(s.ID)
|
|
||||||
ip = exposedIP
|
|
||||||
port = exposedPort
|
|
||||||
}
|
|
||||||
|
|
||||||
recording := ""
|
|
||||||
var isRecording = false
|
|
||||||
property, err := propertyRepository.FindByName(guacd.EnableRecording)
|
|
||||||
if err == nil && property.Value == "true" {
|
|
||||||
isRecording = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if isRecording {
|
|
||||||
recording = path.Join(config.GlobalCfg.Guacd.Recording, sessionId, "recording.cast")
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes, err := assetRepository.FindAssetAttrMapByAssetId(s.AssetId)
|
|
||||||
if err != nil {
|
|
||||||
return WriteMessage(ws, NewMessage(Closed, "获取资产属性失败:"+err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
var xterm = "xterm-256color"
|
|
||||||
var nextTerminal *term.NextTerminal
|
|
||||||
if "true" == attributes[constant.SocksProxyEnable] {
|
|
||||||
nextTerminal, err = term.NewNextTerminalUseSocks(ip, port, username, password, privateKey, passphrase, rows, cols, recording, xterm, true, attributes[constant.SocksProxyHost], attributes[constant.SocksProxyPort], attributes[constant.SocksProxyUsername], attributes[constant.SocksProxyPassword])
|
|
||||||
} else {
|
|
||||||
nextTerminal, err = term.NewNextTerminal(ip, port, username, password, privateKey, passphrase, rows, cols, recording, xterm, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return WriteMessage(ws, NewMessage(Closed, "创建SSH客户端失败:"+err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := nextTerminal.RequestPty(xterm, rows, cols); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := nextTerminal.Shell(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := model.Session{
|
|
||||||
ConnectionId: sessionId,
|
|
||||||
Width: cols,
|
|
||||||
Height: rows,
|
|
||||||
Status: constant.Connecting,
|
|
||||||
Recording: recording,
|
|
||||||
}
|
|
||||||
if sess.Recording == "" {
|
|
||||||
// 未录屏时无需审计
|
|
||||||
sess.Reviewed = true
|
|
||||||
}
|
|
||||||
// 创建新会话
|
|
||||||
log.Debugf("创建新会话 %v", sess.ConnectionId)
|
|
||||||
if err := sessionRepository.UpdateById(&sess, sessionId); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := WriteMessage(ws, NewMessage(Connected, "")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nextSession := &session.Session{
|
|
||||||
ID: s.ID,
|
|
||||||
Protocol: s.Protocol,
|
|
||||||
Mode: s.Mode,
|
|
||||||
WebSocket: ws,
|
|
||||||
GuacdTunnel: nil,
|
|
||||||
NextTerminal: nextTerminal,
|
|
||||||
Observer: session.NewObserver(s.ID),
|
|
||||||
}
|
|
||||||
go nextSession.Observer.Run()
|
|
||||||
session.GlobalSessionManager.Add <- nextSession
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
tick := time.NewTicker(time.Millisecond * time.Duration(60))
|
|
||||||
defer tick.Stop()
|
|
||||||
|
|
||||||
var buf []byte
|
|
||||||
dataChan := make(chan rune)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
SshLoop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
log.Debugf("WebSocket已关闭,即将关闭SSH连接...")
|
|
||||||
break SshLoop
|
|
||||||
default:
|
|
||||||
r, size, err := nextTerminal.StdoutReader.ReadRune()
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("SSH 读取失败,即将退出循环...")
|
|
||||||
_ = WriteMessage(ws, NewMessage(Closed, ""))
|
|
||||||
break SshLoop
|
|
||||||
}
|
|
||||||
if size > 0 {
|
|
||||||
dataChan <- r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Debugf("SSH 连接已关闭,退出循环。")
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
tickLoop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
break tickLoop
|
|
||||||
case <-tick.C:
|
|
||||||
if len(buf) > 0 {
|
|
||||||
s := string(buf)
|
|
||||||
// 录屏
|
|
||||||
if isRecording {
|
|
||||||
_ = nextTerminal.Recorder.WriteData(s)
|
|
||||||
}
|
|
||||||
// 监控
|
|
||||||
if len(nextSession.Observer.All()) > 0 {
|
|
||||||
obs := nextSession.Observer.All()
|
|
||||||
for _, ob := range obs {
|
|
||||||
_ = WriteMessage(ob.WebSocket, NewMessage(Data, s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := WriteMessage(ws, NewMessage(Data, s)); err != nil {
|
|
||||||
log.Debugf("WebSocket写入失败,即将退出循环...")
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
buf = []byte{}
|
|
||||||
}
|
|
||||||
case data := <-dataChan:
|
|
||||||
if data != utf8.RuneError {
|
|
||||||
p := make([]byte, utf8.RuneLen(data))
|
|
||||||
utf8.EncodeRune(p, data)
|
|
||||||
buf = append(buf, p...)
|
|
||||||
} else {
|
|
||||||
buf = append(buf, []byte("@")...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Debugf("SSH 连接已关闭,退出定时器循环。")
|
|
||||||
}()
|
|
||||||
|
|
||||||
//var enterKeys []rune
|
|
||||||
//enterIndex := 0
|
|
||||||
for {
|
|
||||||
_, message, err := ws.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
// web socket会话关闭后主动关闭ssh会话
|
|
||||||
log.Debugf("WebSocket已关闭")
|
|
||||||
CloseSessionById(sessionId, Normal, "用户正常退出")
|
|
||||||
cancel()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := ParseMessage(string(message))
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("消息解码失败: %v, 原始字符串:%v", err, string(message))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch msg.Type {
|
|
||||||
case Resize:
|
|
||||||
decodeString, err := base64.StdEncoding.DecodeString(msg.Content)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("Base64解码失败: %v,原始字符串:%v", err, msg.Content)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var winSize WindowSize
|
|
||||||
err = json.Unmarshal(decodeString, &winSize)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("解析SSH会话窗口大小失败: %v,原始字符串:%v", err, msg.Content)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := nextTerminal.WindowChange(winSize.Rows, winSize.Cols); err != nil {
|
|
||||||
log.Warnf("更改SSH会话窗口大小失败: %v", err)
|
|
||||||
}
|
|
||||||
_ = sessionRepository.UpdateWindowSizeById(winSize.Rows, winSize.Cols, sessionId)
|
|
||||||
case Data:
|
|
||||||
input := []byte(msg.Content)
|
|
||||||
//hexInput := hex.EncodeToString(input)
|
|
||||||
//switch hexInput {
|
|
||||||
//case "0d": // 回车
|
|
||||||
// DealCommand(enterKeys)
|
|
||||||
// // 清空输入的字符
|
|
||||||
// enterKeys = enterKeys[:0]
|
|
||||||
// enterIndex = 0
|
|
||||||
//case "7f": // backspace
|
|
||||||
// enterIndex--
|
|
||||||
// if enterIndex < 0 {
|
|
||||||
// enterIndex = 0
|
|
||||||
// }
|
|
||||||
// temp := enterKeys[:enterIndex]
|
|
||||||
// if len(enterKeys) > enterIndex {
|
|
||||||
// enterKeys = append(temp, enterKeys[enterIndex+1:]...)
|
|
||||||
// } else {
|
|
||||||
// enterKeys = temp
|
|
||||||
// }
|
|
||||||
//case "1b5b337e": // del
|
|
||||||
// temp := enterKeys[:enterIndex]
|
|
||||||
// if len(enterKeys) > enterIndex {
|
|
||||||
// enterKeys = append(temp, enterKeys[enterIndex+1:]...)
|
|
||||||
// } else {
|
|
||||||
// enterKeys = temp
|
|
||||||
// }
|
|
||||||
// enterIndex--
|
|
||||||
// if enterIndex < 0 {
|
|
||||||
// enterIndex = 0
|
|
||||||
// }
|
|
||||||
//case "1b5b41":
|
|
||||||
//case "1b5b42":
|
|
||||||
// break
|
|
||||||
//case "1b5b43": // ->
|
|
||||||
// enterIndex++
|
|
||||||
// if enterIndex > len(enterKeys) {
|
|
||||||
// enterIndex = len(enterKeys)
|
|
||||||
// }
|
|
||||||
//case "1b5b44": // <-
|
|
||||||
// enterIndex--
|
|
||||||
// if enterIndex < 0 {
|
|
||||||
// enterIndex = 0
|
|
||||||
// }
|
|
||||||
//default:
|
|
||||||
// enterKeys = utils.InsertSlice(enterIndex, []rune(msg.Content), enterKeys)
|
|
||||||
// enterIndex++
|
|
||||||
//}
|
|
||||||
_, err := nextTerminal.Write(input)
|
|
||||||
if err != nil {
|
|
||||||
CloseSessionById(sessionId, TunnelClosed, "远程连接已关闭")
|
|
||||||
}
|
|
||||||
case Ping:
|
|
||||||
_, _, err := nextTerminal.SshClient.Conn.SendRequest("helloworld1024@foxmail.com", true, nil)
|
|
||||||
if err != nil {
|
|
||||||
CloseSessionById(sessionId, TunnelClosed, "远程连接已关闭")
|
|
||||||
} else {
|
|
||||||
_ = WriteMessage(ws, NewMessage(Ping, ""))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func permissionCheck(c echo.Context, assetId string) error {
|
|
||||||
user, _ := GetCurrentAccount(c)
|
|
||||||
if constant.TypeUser == user.Type {
|
|
||||||
// 检测是否有访问权限
|
|
||||||
assetIds, err := resourceSharerRepository.FindAssetIdsByUserId(user.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !utils.Contains(assetIds, assetId) {
|
|
||||||
return errors.New("您没有权限访问此资产")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteMessage(ws *websocket.Conn, msg Message) error {
|
|
||||||
message := []byte(msg.ToString())
|
|
||||||
return ws.WriteMessage(websocket.TextMessage, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateNextTerminalBySession(session model.Session) (*term.NextTerminal, error) {
|
|
||||||
var (
|
|
||||||
username = session.Username
|
|
||||||
password = session.Password
|
|
||||||
privateKey = session.PrivateKey
|
|
||||||
passphrase = session.Passphrase
|
|
||||||
ip = session.IP
|
|
||||||
port = session.Port
|
|
||||||
)
|
|
||||||
return term.NewNextTerminal(ip, port, username, password, privateKey, passphrase, 10, 10, "", "", false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SshMonitor(c echo.Context) error {
|
|
||||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer ws.Close()
|
|
||||||
|
|
||||||
sessionId := c.QueryParam("sessionId")
|
|
||||||
s, err := sessionRepository.FindById(sessionId)
|
|
||||||
if err != nil {
|
|
||||||
return WriteMessage(ws, NewMessage(Closed, "获取会话失败"))
|
|
||||||
}
|
|
||||||
|
|
||||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
|
||||||
if nextSession == nil {
|
|
||||||
return WriteMessage(ws, NewMessage(Closed, "会话已离线"))
|
|
||||||
}
|
|
||||||
|
|
||||||
obId := utils.UUID()
|
|
||||||
obSession := &session.Session{
|
|
||||||
ID: obId,
|
|
||||||
Protocol: s.Protocol,
|
|
||||||
Mode: s.Mode,
|
|
||||||
WebSocket: ws,
|
|
||||||
}
|
|
||||||
nextSession.Observer.Add <- obSession
|
|
||||||
log.Debugf("会话 %v 观察者 %v 进入", sessionId, obId)
|
|
||||||
|
|
||||||
for {
|
|
||||||
_, _, err := ws.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("会话 %v 观察者 %v 退出", sessionId, obId)
|
|
||||||
nextSession.Observer.Del <- obId
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,10 +1,8 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"mime/multipart"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -12,12 +10,16 @@ import (
|
|||||||
|
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StoragePagingEndpoint(c echo.Context) error {
|
type StorageApi struct{}
|
||||||
|
|
||||||
|
func (api StorageApi) StoragePagingEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
name := c.QueryParam("name")
|
name := c.QueryParam("name")
|
||||||
@ -25,12 +27,12 @@ func StoragePagingEndpoint(c echo.Context) error {
|
|||||||
order := c.QueryParam("order")
|
order := c.QueryParam("order")
|
||||||
field := c.QueryParam("field")
|
field := c.QueryParam("field")
|
||||||
|
|
||||||
items, total, err := storageRepository.Find(pageIndex, pageSize, name, order, field)
|
items, total, err := repository.StorageRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
drivePath := service.StorageService.GetBaseDrivePath()
|
||||||
|
|
||||||
for i := range items {
|
for i := range items {
|
||||||
item := items[i]
|
item := items[i]
|
||||||
@ -42,13 +44,13 @@ func StoragePagingEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageCreateEndpoint(c echo.Context) error {
|
func (api StorageApi) StorageCreateEndpoint(c echo.Context) error {
|
||||||
var item model.Storage
|
var item model.Storage
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -60,24 +62,24 @@ func StorageCreateEndpoint(c echo.Context) error {
|
|||||||
item.Created = utils.NowJsonTime()
|
item.Created = utils.NowJsonTime()
|
||||||
item.Owner = account.ID
|
item.Owner = account.ID
|
||||||
// 创建对应的目录文件夹
|
// 创建对应的目录文件夹
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
drivePath := service.StorageService.GetBaseDrivePath()
|
||||||
if err := os.MkdirAll(path.Join(drivePath, item.ID), os.ModePerm); err != nil {
|
if err := os.MkdirAll(path.Join(drivePath, item.ID), os.ModePerm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := storageRepository.Create(&item); err != nil {
|
if err := repository.StorageRepository.Create(context.TODO(), &item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageUpdateEndpoint(c echo.Context) error {
|
func (api StorageApi) StorageUpdateEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
var item model.Storage
|
var item model.Storage
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
drivePath := service.StorageService.GetBaseDrivePath()
|
||||||
dirSize, err := utils.DirSize(path.Join(drivePath, item.ID))
|
dirSize, err := utils.DirSize(path.Join(drivePath, item.ID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -87,7 +89,7 @@ func StorageUpdateEndpoint(c echo.Context) error {
|
|||||||
return errors.New("空间大小不能小于已使用大小")
|
return errors.New("空间大小不能小于已使用大小")
|
||||||
}
|
}
|
||||||
|
|
||||||
storage, err := storageRepository.FindById(id)
|
storage, err := repository.StorageRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -95,20 +97,20 @@ func StorageUpdateEndpoint(c echo.Context) error {
|
|||||||
storage.LimitSize = item.LimitSize
|
storage.LimitSize = item.LimitSize
|
||||||
storage.IsShare = item.IsShare
|
storage.IsShare = item.IsShare
|
||||||
|
|
||||||
if err := storageRepository.UpdateById(&storage, id); err != nil {
|
if err := repository.StorageRepository.UpdateById(context.TODO(), &storage, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageGetEndpoint(c echo.Context) error {
|
func (api StorageApi) StorageGetEndpoint(c echo.Context) error {
|
||||||
storageId := c.Param("id")
|
storageId := c.Param("id")
|
||||||
storage, err := storageRepository.FindById(storageId)
|
storage, err := repository.StorageRepository.FindById(context.TODO(), storageId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
structMap := utils.StructToMap(storage)
|
structMap := utils.StructToMap(storage)
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
drivePath := service.StorageService.GetBaseDrivePath()
|
||||||
dirSize, err := utils.DirSize(path.Join(drivePath, storageId))
|
dirSize, err := utils.DirSize(path.Join(drivePath, storageId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
structMap["usedSize"] = -1
|
structMap["usedSize"] = -1
|
||||||
@ -119,28 +121,28 @@ func StorageGetEndpoint(c echo.Context) error {
|
|||||||
return Success(c, structMap)
|
return Success(c, structMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageSharesEndpoint(c echo.Context) error {
|
func (api StorageApi) StorageSharesEndpoint(c echo.Context) error {
|
||||||
storages, err := storageRepository.FindShares()
|
storages, err := repository.StorageRepository.FindShares(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, storages)
|
return Success(c, storages)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageDeleteEndpoint(c echo.Context) error {
|
func (api StorageApi) StorageDeleteEndpoint(c echo.Context) error {
|
||||||
ids := c.Param("id")
|
ids := c.Param("id")
|
||||||
split := strings.Split(ids, ",")
|
split := strings.Split(ids, ",")
|
||||||
for i := range split {
|
for i := range split {
|
||||||
id := split[i]
|
id := split[i]
|
||||||
if err := storageService.DeleteStorageById(id, false); err != nil {
|
if err := service.StorageService.DeleteStorageById(id, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PermissionCheck(c echo.Context, id string) error {
|
func (api StorageApi) PermissionCheck(c echo.Context, id string) error {
|
||||||
storage, err := storageRepository.FindById(id)
|
storage, err := repository.StorageRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -153,49 +155,31 @@ func PermissionCheck(c echo.Context, id string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageLsEndpoint(c echo.Context) error {
|
func (api StorageApi) StorageLsEndpoint(c echo.Context) error {
|
||||||
storageId := c.Param("storageId")
|
storageId := c.Param("storageId")
|
||||||
if err := PermissionCheck(c, storageId); err != nil {
|
if err := api.PermissionCheck(c, storageId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
remoteDir := c.FormValue("dir")
|
remoteDir := c.FormValue("dir")
|
||||||
return StorageLs(c, remoteDir, storageId)
|
err, files := service.StorageService.StorageLs(remoteDir, storageId)
|
||||||
}
|
|
||||||
|
|
||||||
func StorageLs(c echo.Context, remoteDir, storageId string) error {
|
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
|
||||||
if strings.Contains(remoteDir, "../") {
|
|
||||||
return Fail(c, -1, "非法请求 :(")
|
|
||||||
}
|
|
||||||
files, err := storageService.Ls(path.Join(drivePath, storageId), remoteDir)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, files)
|
return Success(c, files)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageDownloadEndpoint(c echo.Context) error {
|
func (api StorageApi) StorageDownloadEndpoint(c echo.Context) error {
|
||||||
storageId := c.Param("storageId")
|
storageId := c.Param("storageId")
|
||||||
if err := PermissionCheck(c, storageId); err != nil {
|
if err := api.PermissionCheck(c, storageId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
remoteFile := c.QueryParam("file")
|
remoteFile := c.QueryParam("file")
|
||||||
return StorageDownload(c, remoteFile, storageId)
|
return service.StorageService.StorageDownload(c, remoteFile, storageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageDownload(c echo.Context, remoteFile, storageId string) error {
|
func (api StorageApi) StorageUploadEndpoint(c echo.Context) error {
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
|
||||||
if strings.Contains(remoteFile, "../") {
|
|
||||||
return Fail(c, -1, "非法请求 :(")
|
|
||||||
}
|
|
||||||
// 获取带后缀的文件名称
|
|
||||||
filenameWithSuffix := path.Base(remoteFile)
|
|
||||||
return c.Attachment(path.Join(path.Join(drivePath, storageId), remoteFile), filenameWithSuffix)
|
|
||||||
}
|
|
||||||
|
|
||||||
func StorageUploadEndpoint(c echo.Context) error {
|
|
||||||
storageId := c.Param("storageId")
|
storageId := c.Param("storageId")
|
||||||
if err := PermissionCheck(c, storageId); err != nil {
|
if err := api.PermissionCheck(c, storageId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
file, err := c.FormFile("file")
|
file, err := c.FormFile("file")
|
||||||
@ -203,150 +187,58 @@ func StorageUploadEndpoint(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return StorageUpload(c, file, storageId)
|
if err := service.StorageService.StorageUpload(c, file, storageId); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
func StorageUpload(c echo.Context, file *multipart.FileHeader, storageId string) error {
|
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
|
||||||
storage, _ := storageRepository.FindById(storageId)
|
|
||||||
if storage.LimitSize > 0 {
|
|
||||||
dirSize, err := utils.DirSize(path.Join(drivePath, storageId))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if dirSize+file.Size > storage.LimitSize {
|
|
||||||
return errors.New("可用空间不足")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := file.Filename
|
|
||||||
src, err := file.Open()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteDir := c.QueryParam("dir")
|
|
||||||
remoteFile := path.Join(remoteDir, filename)
|
|
||||||
|
|
||||||
if strings.Contains(remoteDir, "../") {
|
|
||||||
return Fail(c, -1, "非法请求 :(")
|
|
||||||
}
|
|
||||||
if strings.Contains(remoteFile, "../") {
|
|
||||||
return Fail(c, -1, "非法请求 :(")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断文件夹不存在时自动创建
|
|
||||||
dir := path.Join(path.Join(drivePath, storageId), remoteDir)
|
|
||||||
if !utils.FileExists(dir) {
|
|
||||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Destination
|
|
||||||
dst, err := os.Create(path.Join(path.Join(drivePath, storageId), remoteFile))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer dst.Close()
|
|
||||||
|
|
||||||
// Copy
|
|
||||||
if _, err = io.Copy(dst, src); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageMkDirEndpoint(c echo.Context) error {
|
func (api StorageApi) StorageMkDirEndpoint(c echo.Context) error {
|
||||||
storageId := c.Param("storageId")
|
storageId := c.Param("storageId")
|
||||||
if err := PermissionCheck(c, storageId); err != nil {
|
if err := api.PermissionCheck(c, storageId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
remoteDir := c.QueryParam("dir")
|
remoteDir := c.QueryParam("dir")
|
||||||
return StorageMkDir(c, remoteDir, storageId)
|
if err := service.StorageService.StorageMkDir(remoteDir, storageId); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
func StorageMkDir(c echo.Context, remoteDir, storageId string) error {
|
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
|
||||||
if strings.Contains(remoteDir, "../") {
|
|
||||||
return Fail(c, -1, ":) 非法请求")
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(path.Join(path.Join(drivePath, storageId), remoteDir), os.ModePerm); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageRmEndpoint(c echo.Context) error {
|
func (api StorageApi) StorageRmEndpoint(c echo.Context) error {
|
||||||
storageId := c.Param("storageId")
|
storageId := c.Param("storageId")
|
||||||
if err := PermissionCheck(c, storageId); err != nil {
|
if err := api.PermissionCheck(c, storageId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// 文件夹或者文件
|
// 文件夹或者文件
|
||||||
file := c.FormValue("file")
|
file := c.FormValue("file")
|
||||||
return StorageRm(c, file, storageId)
|
if err := service.StorageService.StorageRm(file, storageId); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
func StorageRm(c echo.Context, file, storageId string) error {
|
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
|
||||||
if strings.Contains(file, "../") {
|
|
||||||
return Fail(c, -1, ":) 非法请求")
|
|
||||||
}
|
|
||||||
if err := os.RemoveAll(path.Join(path.Join(drivePath, storageId), file)); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageRenameEndpoint(c echo.Context) error {
|
func (api StorageApi) StorageRenameEndpoint(c echo.Context) error {
|
||||||
storageId := c.Param("storageId")
|
storageId := c.Param("storageId")
|
||||||
if err := PermissionCheck(c, storageId); err != nil {
|
if err := api.PermissionCheck(c, storageId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
oldName := c.QueryParam("oldName")
|
oldName := c.QueryParam("oldName")
|
||||||
newName := c.QueryParam("newName")
|
newName := c.QueryParam("newName")
|
||||||
return StorageRename(c, oldName, newName, storageId)
|
if err := service.StorageService.StorageRename(oldName, newName, storageId); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
func StorageRename(c echo.Context, oldName, newName, storageId string) error {
|
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
|
||||||
if strings.Contains(oldName, "../") {
|
|
||||||
return Fail(c, -1, ":) 非法请求")
|
|
||||||
}
|
|
||||||
if strings.Contains(newName, "../") {
|
|
||||||
return Fail(c, -1, ":) 非法请求")
|
|
||||||
}
|
|
||||||
if err := os.Rename(path.Join(path.Join(drivePath, storageId), oldName), path.Join(path.Join(drivePath, storageId), newName)); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StorageEditEndpoint(c echo.Context) error {
|
func (api StorageApi) StorageEditEndpoint(c echo.Context) error {
|
||||||
storageId := c.Param("storageId")
|
storageId := c.Param("storageId")
|
||||||
if err := PermissionCheck(c, storageId); err != nil {
|
if err := api.PermissionCheck(c, storageId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
file := c.FormValue("file")
|
file := c.FormValue("file")
|
||||||
fileContent := c.FormValue("fileContent")
|
fileContent := c.FormValue("fileContent")
|
||||||
return StorageEdit(c, file, fileContent, storageId)
|
if err := service.StorageService.StorageEdit(file, fileContent, storageId); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
func StorageEdit(c echo.Context, file string, fileContent string, storageId string) error {
|
|
||||||
drivePath := storageService.GetBaseDrivePath()
|
|
||||||
if strings.Contains(file, "../") {
|
|
||||||
return Fail(c, -1, ":) 非法请求")
|
|
||||||
}
|
|
||||||
realFilePath := path.Join(path.Join(drivePath, storageId), file)
|
|
||||||
dstFile, err := os.OpenFile(realFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer dstFile.Close()
|
|
||||||
write := bufio.NewWriter(dstFile)
|
|
||||||
if _, err := write.WriteString(fileContent); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := write.Flush(); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
|
@ -1,24 +1,29 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StrategyAllEndpoint(c echo.Context) error {
|
type StrategyApi struct{}
|
||||||
items, err := strategyRepository.FindAll()
|
|
||||||
|
func (api StrategyApi) StrategyAllEndpoint(c echo.Context) error {
|
||||||
|
items, err := repository.StrategyRepository.FindAll(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, items)
|
return Success(c, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StrategyPagingEndpoint(c echo.Context) error {
|
func (api StrategyApi) StrategyPagingEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
name := c.QueryParam("name")
|
name := c.QueryParam("name")
|
||||||
@ -26,18 +31,18 @@ func StrategyPagingEndpoint(c echo.Context) error {
|
|||||||
order := c.QueryParam("order")
|
order := c.QueryParam("order")
|
||||||
field := c.QueryParam("field")
|
field := c.QueryParam("field")
|
||||||
|
|
||||||
items, total, err := strategyRepository.Find(pageIndex, pageSize, name, order, field)
|
items, total, err := repository.StrategyRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func StrategyCreateEndpoint(c echo.Context) error {
|
func (api StrategyApi) StrategyCreateEndpoint(c echo.Context) error {
|
||||||
var item model.Strategy
|
var item model.Strategy
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -45,32 +50,32 @@ func StrategyCreateEndpoint(c echo.Context) error {
|
|||||||
item.ID = utils.UUID()
|
item.ID = utils.UUID()
|
||||||
item.Created = utils.NowJsonTime()
|
item.Created = utils.NowJsonTime()
|
||||||
|
|
||||||
if err := strategyRepository.Create(&item); err != nil {
|
if err := repository.StrategyRepository.Create(context.TODO(), &item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func StrategyDeleteEndpoint(c echo.Context) error {
|
func (api StrategyApi) StrategyDeleteEndpoint(c echo.Context) error {
|
||||||
ids := c.Param("id")
|
ids := c.Param("id")
|
||||||
split := strings.Split(ids, ",")
|
split := strings.Split(ids, ",")
|
||||||
for i := range split {
|
for i := range split {
|
||||||
id := split[i]
|
id := split[i]
|
||||||
if err := strategyRepository.DeleteById(id); err != nil {
|
if err := repository.StrategyRepository.DeleteById(context.TODO(), id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StrategyUpdateEndpoint(c echo.Context) error {
|
func (api StrategyApi) StrategyUpdateEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
var item model.Strategy
|
var item model.Strategy
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := strategyRepository.UpdateById(&item, id); err != nil {
|
if err := repository.StrategyRepository.UpdateById(context.TODO(), &item, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
|
288
server/api/term.go
Normal file
288
server/api/term.go
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"next-terminal/server/config"
|
||||||
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/dto"
|
||||||
|
"next-terminal/server/global/session"
|
||||||
|
"next-terminal/server/guacd"
|
||||||
|
"next-terminal/server/log"
|
||||||
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
|
"next-terminal/server/term"
|
||||||
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Closed = 0
|
||||||
|
Connected = 1
|
||||||
|
Data = 2
|
||||||
|
Resize = 3
|
||||||
|
Ping = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
type WebTerminalApi struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||||
|
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = ws.Close()
|
||||||
|
}()
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
sessionId := c.QueryParam("sessionId")
|
||||||
|
cols, _ := strconv.Atoi(c.QueryParam("cols"))
|
||||||
|
rows, _ := strconv.Atoi(c.QueryParam("rows"))
|
||||||
|
|
||||||
|
s, err := service.SessionService.FindByIdAndDecrypt(ctx, sessionId)
|
||||||
|
if err != nil {
|
||||||
|
return WriteMessage(ws, dto.NewMessage(Closed, "获取会话失败"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := api.permissionCheck(c, s.AssetId); err != nil {
|
||||||
|
return WriteMessage(ws, dto.NewMessage(Closed, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
username = s.Username
|
||||||
|
password = s.Password
|
||||||
|
privateKey = s.PrivateKey
|
||||||
|
passphrase = s.Passphrase
|
||||||
|
ip = s.IP
|
||||||
|
port = s.Port
|
||||||
|
)
|
||||||
|
|
||||||
|
if s.AccessGatewayId != "" && s.AccessGatewayId != "-" {
|
||||||
|
g, err := service.GatewayService.GetGatewayAndReconnectById(s.AccessGatewayId)
|
||||||
|
if err != nil {
|
||||||
|
return WriteMessage(ws, dto.NewMessage(Closed, "获取接入网关失败:"+err.Error()))
|
||||||
|
}
|
||||||
|
if !g.Connected {
|
||||||
|
return WriteMessage(ws, dto.NewMessage(Closed, "接入网关不可用:"+g.Message))
|
||||||
|
}
|
||||||
|
exposedIP, exposedPort, err := g.OpenSshTunnel(s.ID, ip, port)
|
||||||
|
if err != nil {
|
||||||
|
return WriteMessage(ws, dto.NewMessage(Closed, "创建隧道失败:"+err.Error()))
|
||||||
|
}
|
||||||
|
defer g.CloseSshTunnel(s.ID)
|
||||||
|
ip = exposedIP
|
||||||
|
port = exposedPort
|
||||||
|
}
|
||||||
|
|
||||||
|
recording := ""
|
||||||
|
var isRecording = false
|
||||||
|
property, err := repository.PropertyRepository.FindByName(ctx, guacd.EnableRecording)
|
||||||
|
if err == nil && property.Value == "true" {
|
||||||
|
isRecording = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isRecording {
|
||||||
|
recording = path.Join(config.GlobalCfg.Guacd.Recording, sessionId, "recording.cast")
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes, err := repository.AssetRepository.FindAssetAttrMapByAssetId(ctx, s.AssetId)
|
||||||
|
if err != nil {
|
||||||
|
return WriteMessage(ws, dto.NewMessage(Closed, "获取资产属性失败:"+err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
var xterm = "xterm-256color"
|
||||||
|
var nextTerminal *term.NextTerminal
|
||||||
|
if "true" == attributes[constant.SocksProxyEnable] {
|
||||||
|
nextTerminal, err = term.NewNextTerminalUseSocks(ip, port, username, password, privateKey, passphrase, rows, cols, recording, xterm, true, attributes[constant.SocksProxyHost], attributes[constant.SocksProxyPort], attributes[constant.SocksProxyUsername], attributes[constant.SocksProxyPassword])
|
||||||
|
} else {
|
||||||
|
nextTerminal, err = term.NewNextTerminal(ip, port, username, password, privateKey, passphrase, rows, cols, recording, xterm, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return WriteMessage(ws, dto.NewMessage(Closed, "创建SSH客户端失败:"+err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := nextTerminal.RequestPty(xterm, rows, cols); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := nextTerminal.Shell(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := model.Session{
|
||||||
|
ConnectionId: sessionId,
|
||||||
|
Width: cols,
|
||||||
|
Height: rows,
|
||||||
|
Status: constant.Connecting,
|
||||||
|
Recording: recording,
|
||||||
|
}
|
||||||
|
if sess.Recording == "" {
|
||||||
|
// 未录屏时无需审计
|
||||||
|
sess.Reviewed = true
|
||||||
|
}
|
||||||
|
// 创建新会话
|
||||||
|
log.Debugf("创建新会话 %v", sess.ConnectionId)
|
||||||
|
if err := repository.SessionRepository.UpdateById(ctx, &sess, sessionId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := WriteMessage(ws, dto.NewMessage(Connected, "")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nextSession := &session.Session{
|
||||||
|
ID: s.ID,
|
||||||
|
Protocol: s.Protocol,
|
||||||
|
Mode: s.Mode,
|
||||||
|
WebSocket: ws,
|
||||||
|
GuacdTunnel: nil,
|
||||||
|
NextTerminal: nextTerminal,
|
||||||
|
Observer: session.NewObserver(s.ID),
|
||||||
|
}
|
||||||
|
go nextSession.Observer.Run()
|
||||||
|
session.GlobalSessionManager.Add <- nextSession
|
||||||
|
|
||||||
|
termHandler := NewTermHandler(sessionId, isRecording, ws, nextTerminal)
|
||||||
|
termHandler.Start()
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, message, err := ws.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
// web socket会话关闭后主动关闭ssh会话
|
||||||
|
log.Debugf("WebSocket已关闭")
|
||||||
|
service.SessionService.CloseSessionById(sessionId, Normal, "用户正常退出")
|
||||||
|
termHandler.Stop()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := dto.ParseMessage(string(message))
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("消息解码失败: %v, 原始字符串:%v", err, string(message))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch msg.Type {
|
||||||
|
case Resize:
|
||||||
|
decodeString, err := base64.StdEncoding.DecodeString(msg.Content)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Base64解码失败: %v,原始字符串:%v", err, msg.Content)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var winSize dto.WindowSize
|
||||||
|
err = json.Unmarshal(decodeString, &winSize)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("解析SSH会话窗口大小失败: %v,原始字符串:%v", err, msg.Content)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := nextTerminal.WindowChange(winSize.Rows, winSize.Cols); err != nil {
|
||||||
|
log.Warnf("更改SSH会话窗口大小失败: %v", err)
|
||||||
|
}
|
||||||
|
_ = repository.SessionRepository.UpdateWindowSizeById(ctx, winSize.Rows, winSize.Cols, sessionId)
|
||||||
|
case Data:
|
||||||
|
input := []byte(msg.Content)
|
||||||
|
_, err := nextTerminal.Write(input)
|
||||||
|
if err != nil {
|
||||||
|
service.SessionService.CloseSessionById(sessionId, TunnelClosed, "远程连接已关闭")
|
||||||
|
}
|
||||||
|
case Ping:
|
||||||
|
_, _, err := nextTerminal.SshClient.Conn.SendRequest("helloworld1024@foxmail.com", true, nil)
|
||||||
|
if err != nil {
|
||||||
|
service.SessionService.CloseSessionById(sessionId, TunnelClosed, "远程连接已关闭")
|
||||||
|
} else {
|
||||||
|
_ = WriteMessage(ws, dto.NewMessage(Ping, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api WebTerminalApi) SshMonitorEndpoint(c echo.Context) error {
|
||||||
|
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = ws.Close()
|
||||||
|
}()
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
sessionId := c.QueryParam("sessionId")
|
||||||
|
s, err := repository.SessionRepository.FindById(ctx, sessionId)
|
||||||
|
if err != nil {
|
||||||
|
return WriteMessage(ws, dto.NewMessage(Closed, "获取会话失败"))
|
||||||
|
}
|
||||||
|
|
||||||
|
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||||
|
if nextSession == nil {
|
||||||
|
return WriteMessage(ws, dto.NewMessage(Closed, "会话已离线"))
|
||||||
|
}
|
||||||
|
|
||||||
|
obId := utils.UUID()
|
||||||
|
obSession := &session.Session{
|
||||||
|
ID: obId,
|
||||||
|
Protocol: s.Protocol,
|
||||||
|
Mode: s.Mode,
|
||||||
|
WebSocket: ws,
|
||||||
|
}
|
||||||
|
nextSession.Observer.Add <- obSession
|
||||||
|
log.Debugf("会话 %v 观察者 %v 进入", sessionId, obId)
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, _, err := ws.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("会话 %v 观察者 %v 退出", sessionId, obId)
|
||||||
|
nextSession.Observer.Del <- obId
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api WebTerminalApi) permissionCheck(c echo.Context, assetId string) error {
|
||||||
|
user, _ := GetCurrentAccount(c)
|
||||||
|
if constant.TypeUser == user.Type {
|
||||||
|
// 检测是否有访问权限
|
||||||
|
assetIds, err := repository.ResourceSharerRepository.FindAssetIdsByUserId(context.TODO(), user.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.Contains(assetIds, assetId) {
|
||||||
|
return errors.New("您没有权限访问此资产")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteMessage(ws *websocket.Conn, msg dto.Message) error {
|
||||||
|
message := []byte(msg.ToString())
|
||||||
|
return ws.WriteMessage(websocket.TextMessage, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateNextTerminalBySession(session model.Session) (*term.NextTerminal, error) {
|
||||||
|
var (
|
||||||
|
username = session.Username
|
||||||
|
password = session.Password
|
||||||
|
privateKey = session.PrivateKey
|
||||||
|
passphrase = session.Passphrase
|
||||||
|
ip = session.IP
|
||||||
|
port = session.Port
|
||||||
|
)
|
||||||
|
return term.NewNextTerminal(ip, port, username, password, privateKey, passphrase, 10, 10, "", "", false)
|
||||||
|
}
|
104
server/api/term_handler.go
Normal file
104
server/api/term_handler.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"next-terminal/server/dto"
|
||||||
|
"next-terminal/server/global/session"
|
||||||
|
"next-terminal/server/term"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TermHandler struct {
|
||||||
|
sessionId string
|
||||||
|
isRecording bool
|
||||||
|
ws *websocket.Conn
|
||||||
|
nextTerminal *term.NextTerminal
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
dataChan chan rune
|
||||||
|
tick *time.Ticker
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTermHandler(sessionId string, isRecording bool, ws *websocket.Conn, nextTerminal *term.NextTerminal) *TermHandler {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
tick := time.NewTicker(time.Millisecond * time.Duration(60))
|
||||||
|
return &TermHandler{
|
||||||
|
sessionId: sessionId,
|
||||||
|
isRecording: isRecording,
|
||||||
|
ws: ws,
|
||||||
|
nextTerminal: nextTerminal,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
dataChan: make(chan rune),
|
||||||
|
tick: tick,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r TermHandler) Start() {
|
||||||
|
go r.readFormTunnel()
|
||||||
|
go r.writeToWebsocket()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r TermHandler) Stop() {
|
||||||
|
r.tick.Stop()
|
||||||
|
r.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r TermHandler) readFormTunnel() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-r.ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
rn, size, err := r.nextTerminal.StdoutReader.ReadRune()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if size > 0 {
|
||||||
|
r.dataChan <- rn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r TermHandler) writeToWebsocket() {
|
||||||
|
var buf []byte
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-r.ctx.Done():
|
||||||
|
return
|
||||||
|
case <-r.tick.C:
|
||||||
|
if len(buf) > 0 {
|
||||||
|
s := string(buf)
|
||||||
|
if err := WriteMessage(r.ws, dto.NewMessage(Data, s)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 录屏
|
||||||
|
if r.isRecording {
|
||||||
|
_ = r.nextTerminal.Recorder.WriteData(s)
|
||||||
|
}
|
||||||
|
nextSession := session.GlobalSessionManager.GetById(r.sessionId)
|
||||||
|
// 监控
|
||||||
|
if nextSession != nil && len(nextSession.Observer.All()) > 0 {
|
||||||
|
obs := nextSession.Observer.All()
|
||||||
|
for _, ob := range obs {
|
||||||
|
_ = WriteMessage(ob.WebSocket, dto.NewMessage(Data, s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf = []byte{}
|
||||||
|
}
|
||||||
|
case data := <-r.dataChan:
|
||||||
|
if data != utf8.RuneError {
|
||||||
|
p := make([]byte, utf8.RuneLen(data))
|
||||||
|
utf8.EncodeRune(p, data)
|
||||||
|
buf = append(buf, p...)
|
||||||
|
} else {
|
||||||
|
buf = append(buf, []byte("@")...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,41 +1,33 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/dto"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserGroup struct {
|
type UserGroupApi struct{}
|
||||||
Id string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Members []string `json:"members"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func UserGroupCreateEndpoint(c echo.Context) error {
|
func (userGroupApi UserGroupApi) UserGroupCreateEndpoint(c echo.Context) error {
|
||||||
var item UserGroup
|
var item dto.UserGroup
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
userGroup := model.UserGroup{
|
if _, err := service.UserGroupService.Create(item.Name, item.Members); err != nil {
|
||||||
ID: utils.UUID(),
|
|
||||||
Created: utils.NowJsonTime(),
|
|
||||||
Name: item.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := userGroupRepository.Create(&userGroup, item.Members); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, item)
|
return Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserGroupPagingEndpoint(c echo.Context) error {
|
func (userGroupApi UserGroupApi) UserGroupPagingEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
name := c.QueryParam("name")
|
name := c.QueryParam("name")
|
||||||
@ -43,41 +35,38 @@ func UserGroupPagingEndpoint(c echo.Context) error {
|
|||||||
order := c.QueryParam("order")
|
order := c.QueryParam("order")
|
||||||
field := c.QueryParam("field")
|
field := c.QueryParam("field")
|
||||||
|
|
||||||
items, total, err := userGroupRepository.Find(pageIndex, pageSize, name, order, field)
|
items, total, err := repository.UserGroupRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserGroupUpdateEndpoint(c echo.Context) error {
|
func (userGroupApi UserGroupApi) UserGroupUpdateEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
var item UserGroup
|
var item dto.UserGroup
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
userGroup := model.UserGroup{
|
|
||||||
Name: item.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := userGroupRepository.Update(&userGroup, item.Members, id); err != nil {
|
if err := service.UserGroupService.Update(id, item.Name, item.Members); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserGroupDeleteEndpoint(c echo.Context) error {
|
func (userGroupApi UserGroupApi) UserGroupDeleteEndpoint(c echo.Context) error {
|
||||||
ids := c.Param("id")
|
ids := c.Param("id")
|
||||||
split := strings.Split(ids, ",")
|
split := strings.Split(ids, ",")
|
||||||
for i := range split {
|
for i := range split {
|
||||||
userId := split[i]
|
userId := split[i]
|
||||||
if err := userGroupRepository.DeleteById(userId); err != nil {
|
if err := service.UserGroupService.DeleteById(userId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,20 +74,20 @@ func UserGroupDeleteEndpoint(c echo.Context) error {
|
|||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserGroupGetEndpoint(c echo.Context) error {
|
func (userGroupApi UserGroupApi) UserGroupGetEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
item, err := userGroupRepository.FindById(id)
|
item, err := repository.UserGroupRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
members, err := userGroupRepository.FindMembersById(id)
|
members, err := repository.UserGroupMemberRepository.FindUserIdsByUserGroupId(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
userGroup := UserGroup{
|
userGroup := dto.UserGroup{
|
||||||
Id: item.ID,
|
Id: item.ID,
|
||||||
Name: item.Name,
|
Name: item.Name,
|
||||||
Members: members,
|
Members: members,
|
||||||
|
@ -1,56 +1,35 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"context"
|
||||||
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"next-terminal/server/constant"
|
|
||||||
"next-terminal/server/global/cache"
|
|
||||||
"next-terminal/server/log"
|
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func UserCreateEndpoint(c echo.Context) (err error) {
|
type UserApi struct{}
|
||||||
|
|
||||||
|
func (userApi UserApi) UserCreateEndpoint(c echo.Context) (err error) {
|
||||||
var item model.User
|
var item model.User
|
||||||
if err := c.Bind(&item); err != nil {
|
if err := c.Bind(&item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if userRepository.ExistByUsername(item.Username) {
|
|
||||||
return Fail(c, -1, "username is already in use")
|
|
||||||
}
|
|
||||||
|
|
||||||
password := item.Password
|
if err := service.UserService.CreateUser(item); err != nil {
|
||||||
|
|
||||||
var pass []byte
|
|
||||||
if pass, err = utils.Encoder.Encode([]byte(password)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.Password = string(pass)
|
|
||||||
|
|
||||||
item.ID = utils.UUID()
|
|
||||||
item.Created = utils.NowJsonTime()
|
|
||||||
item.Status = constant.StatusEnabled
|
|
||||||
|
|
||||||
if err := userRepository.Create(&item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = storageService.CreateStorageByUser(&item)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.Mail != "" {
|
|
||||||
go mailService.SendMail(item.Mail, "[Next Terminal] 注册通知", "你好,"+item.Nickname+"。管理员为你注册了账号:"+item.Username+" 密码:"+password)
|
|
||||||
}
|
|
||||||
return Success(c, item)
|
return Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserPagingEndpoint(c echo.Context) error {
|
func (userApi UserApi) UserPagingEndpoint(c echo.Context) error {
|
||||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
username := c.QueryParam("username")
|
username := c.QueryParam("username")
|
||||||
@ -60,19 +39,18 @@ func UserPagingEndpoint(c echo.Context) error {
|
|||||||
order := c.QueryParam("order")
|
order := c.QueryParam("order")
|
||||||
field := c.QueryParam("field")
|
field := c.QueryParam("field")
|
||||||
|
|
||||||
account, _ := GetCurrentAccount(c)
|
items, total, err := repository.UserRepository.Find(context.TODO(), pageIndex, pageSize, username, nickname, mail, order, field)
|
||||||
items, total, err := userRepository.Find(pageIndex, pageSize, username, nickname, mail, order, field, account)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, H{
|
return Success(c, Map{
|
||||||
"total": total,
|
"total": total,
|
||||||
"items": items,
|
"items": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserUpdateEndpoint(c echo.Context) error {
|
func (userApi UserApi) UserUpdateEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
@ -86,14 +64,14 @@ func UserUpdateEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
item.ID = id
|
item.ID = id
|
||||||
|
|
||||||
if err := userRepository.Update(&item); err != nil {
|
if err := repository.UserRepository.Update(context.TODO(), &item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserUpdateStatusEndpoint(c echo.Context) error {
|
func (userApi UserApi) UserUpdateStatusEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
status := c.QueryParam("status")
|
status := c.QueryParam("status")
|
||||||
account, _ := GetCurrentAccount(c)
|
account, _ := GetCurrentAccount(c)
|
||||||
@ -101,14 +79,14 @@ func UserUpdateStatusEndpoint(c echo.Context) error {
|
|||||||
return Fail(c, -1, "不能操作自身账户")
|
return Fail(c, -1, "不能操作自身账户")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := userService.UpdateStatusById(id, status); err != nil {
|
if err := service.UserService.UpdateStatusById(id, status); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserDeleteEndpoint(c echo.Context) error {
|
func (userApi UserApi) UserDeleteEndpoint(c echo.Context) error {
|
||||||
ids := c.Param("id")
|
ids := c.Param("id")
|
||||||
account, found := GetCurrentAccount(c)
|
account, found := GetCurrentAccount(c)
|
||||||
if !found {
|
if !found {
|
||||||
@ -120,16 +98,7 @@ func UserDeleteEndpoint(c echo.Context) error {
|
|||||||
if account.ID == userId {
|
if account.ID == userId {
|
||||||
return Fail(c, -1, "不允许删除自身账户")
|
return Fail(c, -1, "不允许删除自身账户")
|
||||||
}
|
}
|
||||||
// 下线该用户
|
if err := service.UserService.DeleteUserById(userId); err != nil {
|
||||||
if err := userService.LogoutById(userId); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 删除用户
|
|
||||||
if err := userRepository.DeleteById(userId); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 删除用户的默认磁盘空间
|
|
||||||
if err := storageService.DeleteStorageById(userId, true); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,10 +106,10 @@ func UserDeleteEndpoint(c echo.Context) error {
|
|||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserGetEndpoint(c echo.Context) error {
|
func (userApi UserApi) UserGetEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
item, err := userRepository.FindById(id)
|
item, err := repository.UserRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -148,14 +117,14 @@ func UserGetEndpoint(c echo.Context) error {
|
|||||||
return Success(c, item)
|
return Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserChangePasswordEndpoint(c echo.Context) error {
|
func (userApi UserApi) UserChangePasswordEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
password := c.FormValue("password")
|
password := c.FormValue("password")
|
||||||
if password == "" {
|
if password == "" {
|
||||||
return Fail(c, -1, "请输入密码")
|
return Fail(c, -1, "请输入密码")
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := userRepository.FindById(id)
|
user, err := repository.UserRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -168,61 +137,25 @@ func UserChangePasswordEndpoint(c echo.Context) error {
|
|||||||
Password: string(passwd),
|
Password: string(passwd),
|
||||||
ID: id,
|
ID: id,
|
||||||
}
|
}
|
||||||
if err := userRepository.Update(u); err != nil {
|
if err := repository.UserRepository.Update(context.TODO(), u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.Mail != "" {
|
if user.Mail != "" {
|
||||||
go mailService.SendMail(user.Mail, "[Next Terminal] 密码修改通知", "你好,"+user.Nickname+"。管理员已将你的密码修改为:"+password)
|
go service.MailService.SendMail(user.Mail, "[Next Terminal] 密码修改通知", "你好,"+user.Nickname+"。管理员已将你的密码修改为:"+password)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserResetTotpEndpoint(c echo.Context) error {
|
func (userApi UserApi) UserResetTotpEndpoint(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
u := &model.User{
|
u := &model.User{
|
||||||
TOTPSecret: "-",
|
TOTPSecret: "-",
|
||||||
ID: id,
|
ID: id,
|
||||||
}
|
}
|
||||||
if err := userRepository.Update(u); err != nil {
|
if err := repository.UserRepository.Update(context.TODO(), u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Success(c, "")
|
return Success(c, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReloadToken() error {
|
|
||||||
loginLogs, err := loginLogRepository.FindAliveLoginLogs()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range loginLogs {
|
|
||||||
loginLog := loginLogs[i]
|
|
||||||
token := loginLog.ID
|
|
||||||
user, err := userRepository.FindByUsername(loginLog.Username)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(gorm.ErrRecordNotFound, err) {
|
|
||||||
_ = loginLogRepository.DeleteById(token)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
authorization := Authorization{
|
|
||||||
Token: token,
|
|
||||||
Remember: loginLog.Remember,
|
|
||||||
User: user,
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheKey := userService.BuildCacheKeyByToken(token)
|
|
||||||
|
|
||||||
if authorization.Remember {
|
|
||||||
// 记住登录有效期两周
|
|
||||||
cache.GlobalCache.Set(cacheKey, authorization, RememberEffectiveTime)
|
|
||||||
} else {
|
|
||||||
cache.GlobalCache.Set(cacheKey, authorization, NotRememberEffectiveTime)
|
|
||||||
}
|
|
||||||
log.Debugf("重新加载用户「%v」授权Token「%v」到缓存", user.Nickname, token)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
118
server/app/app.go
Normal file
118
server/app/app.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"next-terminal/server/cli"
|
||||||
|
"next-terminal/server/config"
|
||||||
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/service"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
var app *App
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
Server *echo.Echo
|
||||||
|
}
|
||||||
|
|
||||||
|
func newApp() *App {
|
||||||
|
return &App{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
setupCache()
|
||||||
|
app = newApp()
|
||||||
|
if err := app.InitDBData(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := app.ReloadData(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
app.Server = setupRoutes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app App) InitDBData() (err error) {
|
||||||
|
if err := service.PropertyService.DeleteDeprecatedProperty(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.GatewayService.ReConnectAll(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.PropertyService.InitProperties(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.UserService.InitUser(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.JobService.InitJob(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.UserService.FixUserOnlineState(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.SessionService.FixSessionState(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.SessionService.EmptyPassword(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.CredentialService.EncryptAll(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.AssetService.EncryptAll(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.StorageService.InitStorages(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app App) ReloadData() error {
|
||||||
|
if err := service.SecurityService.ReloadAccessSecurity(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.UserService.ReloadToken(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.AccessTokenService.Reload(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run() error {
|
||||||
|
|
||||||
|
fmt.Printf(constant.Banner, constant.Version)
|
||||||
|
|
||||||
|
if config.GlobalCfg.Debug {
|
||||||
|
jsonBytes, err := json.MarshalIndent(config.GlobalCfg, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("当前配置为: %v\n", string(jsonBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
_cli := cli.NewCli()
|
||||||
|
|
||||||
|
if config.GlobalCfg.ResetPassword != "" {
|
||||||
|
return _cli.ResetPassword(config.GlobalCfg.ResetPassword)
|
||||||
|
}
|
||||||
|
if config.GlobalCfg.ResetTotp != "" {
|
||||||
|
return _cli.ResetTotp(config.GlobalCfg.ResetTotp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.GlobalCfg.NewEncryptionKey != "" {
|
||||||
|
return _cli.ChangeEncryptionKey(config.GlobalCfg.EncryptionKey, config.GlobalCfg.NewEncryptionKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.GlobalCfg.Server.Cert != "" && config.GlobalCfg.Server.Key != "" {
|
||||||
|
return app.Server.StartTLS(config.GlobalCfg.Server.Addr, config.GlobalCfg.Server.Cert, config.GlobalCfg.Server.Key)
|
||||||
|
} else {
|
||||||
|
return app.Server.Start(config.GlobalCfg.Server.Addr)
|
||||||
|
}
|
||||||
|
}
|
10
server/app/cache.go
Normal file
10
server/app/cache.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"next-terminal/server/global/cache"
|
||||||
|
"next-terminal/server/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupCache() {
|
||||||
|
cache.TokenManager.OnEvicted(service.UserService.OnEvicted)
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
package api
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
|
"next-terminal/server/api"
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/dto"
|
||||||
"next-terminal/server/global/cache"
|
"next-terminal/server/global/cache"
|
||||||
"next-terminal/server/global/security"
|
"next-terminal/server/global/security"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
@ -21,10 +22,10 @@ func ErrorHandler(next echo.HandlerFunc) echo.HandlerFunc {
|
|||||||
|
|
||||||
if he, ok := err.(*echo.HTTPError); ok {
|
if he, ok := err.(*echo.HTTPError); ok {
|
||||||
message := fmt.Sprintf("%v", he.Message)
|
message := fmt.Sprintf("%v", he.Message)
|
||||||
return Fail(c, he.Code, message)
|
return api.Fail(c, he.Code, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Fail(c, 0, err.Error())
|
return api.Fail(c, 0, err.Error())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -74,7 +75,7 @@ func TcpWall(next echo.HandlerFunc) echo.HandlerFunc {
|
|||||||
}
|
}
|
||||||
if s.Rule == constant.AccessRuleReject {
|
if s.Rule == constant.AccessRuleReject {
|
||||||
if c.Request().Header.Get("X-Requested-With") != "" || c.Request().Header.Get(constant.Token) != "" {
|
if c.Request().Header.Get("X-Requested-With") != "" || c.Request().Header.Get(constant.Token) != "" {
|
||||||
return Fail(c, 0, "您的访问请求被拒绝 :(")
|
return api.Fail(c, 0, "您的访问请求被拒绝 :(")
|
||||||
} else {
|
} else {
|
||||||
return c.HTML(666, "您的访问请求被拒绝 :(")
|
return c.HTML(666, "您的访问请求被拒绝 :(")
|
||||||
}
|
}
|
||||||
@ -85,9 +86,9 @@ func TcpWall(next echo.HandlerFunc) echo.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Auth(next echo.HandlerFunc) echo.HandlerFunc {
|
var anonymousUrls = []string{"/login", "/static", "/favicon.ico", "/logo.svg", "/asciinema"}
|
||||||
|
|
||||||
anonymousUrls := []string{"/login", "/static", "/favicon.ico", "/logo.svg", "/asciinema"}
|
func Auth(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
|
|
||||||
@ -102,21 +103,46 @@ func Auth(next echo.HandlerFunc) echo.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
token := GetToken(c)
|
token := api.GetToken(c)
|
||||||
if token == "" {
|
if token == "" {
|
||||||
return Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
return api.Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
||||||
}
|
|
||||||
cacheKey := userService.BuildCacheKeyByToken(token)
|
|
||||||
authorization, found := cache.GlobalCache.Get(cacheKey)
|
|
||||||
if !found {
|
|
||||||
return Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if authorization.(Authorization).Remember {
|
v, found := cache.TokenManager.Get(token)
|
||||||
|
if !found {
|
||||||
|
return api.Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
||||||
|
}
|
||||||
|
|
||||||
|
authorization := v.(dto.Authorization)
|
||||||
|
|
||||||
|
if strings.EqualFold(constant.LoginToken, authorization.Type) {
|
||||||
|
if authorization.Remember {
|
||||||
// 记住登录有效期两周
|
// 记住登录有效期两周
|
||||||
cache.GlobalCache.Set(cacheKey, authorization, time.Hour*time.Duration(24*14))
|
cache.TokenManager.Set(token, authorization, cache.RememberMeExpiration)
|
||||||
} else {
|
} else {
|
||||||
cache.GlobalCache.Set(cacheKey, authorization, time.Hour*time.Duration(2))
|
cache.TokenManager.Set(token, authorization, cache.NotRememberExpiration)
|
||||||
|
}
|
||||||
|
} else if strings.EqualFold(constant.ShareSession, authorization.Type) {
|
||||||
|
id := c.Param("id")
|
||||||
|
uri = strings.Split(uri, "?")[0]
|
||||||
|
allowUrls := []string{
|
||||||
|
"/share-sessions/" + id,
|
||||||
|
"/sessions",
|
||||||
|
"/sessions/" + id + "/tunnel",
|
||||||
|
"/sessions/" + id + "/connect",
|
||||||
|
"/sessions/" + id + "/resize",
|
||||||
|
"/sessions/" + id + "/stats",
|
||||||
|
"/sessions/" + id + "/ls",
|
||||||
|
"/sessions/" + id + "/download",
|
||||||
|
"/sessions/" + id + "/upload",
|
||||||
|
"/sessions/" + id + "/edit",
|
||||||
|
"/sessions/" + id + "/mkdir",
|
||||||
|
"/sessions/" + id + "/rm",
|
||||||
|
"/sessions/" + id + "/rename",
|
||||||
|
}
|
||||||
|
if !utils.Contains(allowUrls, uri) {
|
||||||
|
return api.Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(c)
|
return next(c)
|
||||||
@ -126,13 +152,13 @@ func Auth(next echo.HandlerFunc) echo.HandlerFunc {
|
|||||||
func Admin(next echo.HandlerFunc) echo.HandlerFunc {
|
func Admin(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
|
|
||||||
account, found := GetCurrentAccount(c)
|
account, found := api.GetCurrentAccount(c)
|
||||||
if !found {
|
if !found {
|
||||||
return Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
return api.Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.Type != constant.TypeAdmin {
|
if account.Type != constant.TypeAdmin {
|
||||||
return Fail(c, 403, "permission denied")
|
return api.Fail(c, 403, "permission denied")
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(c)
|
return next(c)
|
254
server/app/server.go
Normal file
254
server/app/server.go
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"next-terminal/server/api"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/labstack/echo/v4/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupRoutes() *echo.Echo {
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
e.HideBanner = true
|
||||||
|
//e.Logger = log.GetEchoLogger()
|
||||||
|
//e.Use(log.Hook())
|
||||||
|
e.File("/", "web/build/index.html")
|
||||||
|
e.File("/asciinema.html", "web/build/asciinema.html")
|
||||||
|
e.File("/", "web/build/index.html")
|
||||||
|
e.File("/favicon.ico", "web/build/favicon.ico")
|
||||||
|
e.File("/logo.png", "web/build/logo.png")
|
||||||
|
e.Static("/static", "web/build/static")
|
||||||
|
|
||||||
|
e.Use(middleware.Recover())
|
||||||
|
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
||||||
|
Skipper: middleware.DefaultSkipper,
|
||||||
|
AllowOrigins: []string{"*"},
|
||||||
|
AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete},
|
||||||
|
}))
|
||||||
|
e.Use(ErrorHandler)
|
||||||
|
e.Use(TcpWall)
|
||||||
|
e.Use(Auth)
|
||||||
|
|
||||||
|
accountApi := new(api.AccountApi)
|
||||||
|
guacamoleApi := new(api.GuacamoleApi)
|
||||||
|
webTerminalApi := new(api.WebTerminalApi)
|
||||||
|
UserApi := new(api.UserApi)
|
||||||
|
UserGroupApi := new(api.UserGroupApi)
|
||||||
|
AssetApi := new(api.AssetApi)
|
||||||
|
CommandApi := new(api.CommandApi)
|
||||||
|
CredentialApi := new(api.CredentialApi)
|
||||||
|
SessionApi := new(api.SessionApi)
|
||||||
|
ResourceSharerApi := new(api.ResourceSharerApi)
|
||||||
|
LoginLogApi := new(api.LoginLogApi)
|
||||||
|
PropertyApi := new(api.PropertyApi)
|
||||||
|
OverviewApi := new(api.OverviewApi)
|
||||||
|
JobApi := new(api.JobApi)
|
||||||
|
SecurityApi := new(api.SecurityApi)
|
||||||
|
StorageApi := new(api.StorageApi)
|
||||||
|
StrategyApi := new(api.StrategyApi)
|
||||||
|
AccessGatewayApi := new(api.AccessGatewayApi)
|
||||||
|
BackupApi := new(api.BackupApi)
|
||||||
|
|
||||||
|
e.POST("/login", accountApi.LoginEndpoint)
|
||||||
|
e.POST("/loginWithTotp", accountApi.LoginWithTotpEndpoint)
|
||||||
|
|
||||||
|
e.GET("/ssh", webTerminalApi.SshEndpoint)
|
||||||
|
e.GET("/ssh-monitor", webTerminalApi.SshMonitorEndpoint)
|
||||||
|
|
||||||
|
account := e.Group("/account")
|
||||||
|
{
|
||||||
|
account.GET("/info", accountApi.InfoEndpoint)
|
||||||
|
account.GET("/assets", accountApi.AccountAssetEndpoint)
|
||||||
|
account.GET("/storage", accountApi.AccountStorageEndpoint)
|
||||||
|
account.POST("/logout", accountApi.LogoutEndpoint)
|
||||||
|
account.POST("/change-password", accountApi.ChangePasswordEndpoint)
|
||||||
|
account.GET("/reload-totp", accountApi.ReloadTOTPEndpoint)
|
||||||
|
account.POST("/reset-totp", accountApi.ResetTOTPEndpoint)
|
||||||
|
account.POST("/confirm-totp", accountApi.ConfirmTOTPEndpoint)
|
||||||
|
account.GET("/access-token", accountApi.AccessTokenGetEndpoint)
|
||||||
|
account.POST("/access-token", accountApi.AccessTokenGenEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
users := e.Group("/users", Admin)
|
||||||
|
{
|
||||||
|
users.POST("", UserApi.UserCreateEndpoint)
|
||||||
|
users.GET("/paging", UserApi.UserPagingEndpoint)
|
||||||
|
users.PUT("/:id", UserApi.UserUpdateEndpoint)
|
||||||
|
users.PATCH("/:id/status", UserApi.UserUpdateStatusEndpoint)
|
||||||
|
users.DELETE("/:id", UserApi.UserDeleteEndpoint)
|
||||||
|
users.GET("/:id", UserApi.UserGetEndpoint)
|
||||||
|
users.POST("/:id/change-password", UserApi.UserChangePasswordEndpoint)
|
||||||
|
users.POST("/:id/reset-totp", UserApi.UserResetTotpEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
userGroups := e.Group("/user-groups", Admin)
|
||||||
|
{
|
||||||
|
userGroups.POST("", UserGroupApi.UserGroupCreateEndpoint)
|
||||||
|
userGroups.GET("/paging", UserGroupApi.UserGroupPagingEndpoint)
|
||||||
|
userGroups.PUT("/:id", UserGroupApi.UserGroupUpdateEndpoint)
|
||||||
|
userGroups.DELETE("/:id", UserGroupApi.UserGroupDeleteEndpoint)
|
||||||
|
userGroups.GET("/:id", UserGroupApi.UserGroupGetEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
assets := e.Group("/assets", Admin)
|
||||||
|
{
|
||||||
|
assets.GET("", AssetApi.AssetAllEndpoint)
|
||||||
|
assets.POST("", AssetApi.AssetCreateEndpoint)
|
||||||
|
assets.POST("/import", AssetApi.AssetImportEndpoint)
|
||||||
|
assets.GET("/paging", AssetApi.AssetPagingEndpoint)
|
||||||
|
assets.POST("/:id/tcping", AssetApi.AssetTcpingEndpoint)
|
||||||
|
assets.PUT("/:id", AssetApi.AssetUpdateEndpoint)
|
||||||
|
assets.GET("/:id", AssetApi.AssetGetEndpoint)
|
||||||
|
assets.DELETE("/:id", AssetApi.AssetDeleteEndpoint)
|
||||||
|
assets.POST("/:id/change-owner", AssetApi.AssetChangeOwnerEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.GET("/tags", AssetApi.AssetTagsEndpoint)
|
||||||
|
|
||||||
|
commands := e.Group("/commands")
|
||||||
|
{
|
||||||
|
commands.GET("", CommandApi.CommandAllEndpoint)
|
||||||
|
commands.GET("/paging", CommandApi.CommandPagingEndpoint)
|
||||||
|
commands.POST("", CommandApi.CommandCreateEndpoint)
|
||||||
|
commands.PUT("/:id", CommandApi.CommandUpdateEndpoint)
|
||||||
|
commands.DELETE("/:id", CommandApi.CommandDeleteEndpoint)
|
||||||
|
commands.GET("/:id", CommandApi.CommandGetEndpoint)
|
||||||
|
commands.POST("/:id/change-owner", CommandApi.CommandChangeOwnerEndpoint, Admin)
|
||||||
|
}
|
||||||
|
|
||||||
|
credentials := e.Group("/credentials", Admin)
|
||||||
|
{
|
||||||
|
credentials.GET("", CredentialApi.CredentialAllEndpoint)
|
||||||
|
credentials.GET("/paging", CredentialApi.CredentialPagingEndpoint)
|
||||||
|
credentials.POST("", CredentialApi.CredentialCreateEndpoint)
|
||||||
|
credentials.PUT("/:id", CredentialApi.CredentialUpdateEndpoint)
|
||||||
|
credentials.DELETE("/:id", CredentialApi.CredentialDeleteEndpoint)
|
||||||
|
credentials.GET("/:id", CredentialApi.CredentialGetEndpoint)
|
||||||
|
credentials.POST("/:id/change-owner", CredentialApi.CredentialChangeOwnerEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
sessions := e.Group("/sessions")
|
||||||
|
{
|
||||||
|
sessions.GET("/paging", Admin(SessionApi.SessionPagingEndpoint))
|
||||||
|
sessions.POST("/:id/disconnect", Admin(SessionApi.SessionDisconnectEndpoint))
|
||||||
|
sessions.DELETE("/:id", Admin(SessionApi.SessionDeleteEndpoint))
|
||||||
|
sessions.GET("/:id/recording", Admin(SessionApi.SessionRecordingEndpoint))
|
||||||
|
sessions.GET("/:id", Admin(SessionApi.SessionGetEndpoint))
|
||||||
|
sessions.POST("/:id/reviewed", Admin(SessionApi.SessionReviewedEndpoint))
|
||||||
|
sessions.POST("/:id/unreviewed", Admin(SessionApi.SessionUnViewedEndpoint))
|
||||||
|
sessions.POST("/clear", Admin(SessionApi.SessionClearEndpoint))
|
||||||
|
sessions.POST("/reviewed", Admin(SessionApi.SessionReviewedAllEndpoint))
|
||||||
|
|
||||||
|
sessions.POST("", SessionApi.SessionCreateEndpoint)
|
||||||
|
sessions.POST("/:id/connect", SessionApi.SessionConnectEndpoint)
|
||||||
|
sessions.GET("/:id/tunnel", guacamoleApi.Guacamole)
|
||||||
|
sessions.POST("/:id/resize", SessionApi.SessionResizeEndpoint)
|
||||||
|
sessions.GET("/:id/stats", SessionApi.SessionStatsEndpoint)
|
||||||
|
|
||||||
|
sessions.POST("/:id/ls", SessionApi.SessionLsEndpoint)
|
||||||
|
sessions.GET("/:id/download", SessionApi.SessionDownloadEndpoint)
|
||||||
|
sessions.POST("/:id/upload", SessionApi.SessionUploadEndpoint)
|
||||||
|
sessions.POST("/:id/edit", SessionApi.SessionEditEndpoint)
|
||||||
|
sessions.POST("/:id/mkdir", SessionApi.SessionMkDirEndpoint)
|
||||||
|
sessions.POST("/:id/rm", SessionApi.SessionRmEndpoint)
|
||||||
|
sessions.POST("/:id/rename", SessionApi.SessionRenameEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceSharers := e.Group("/resource-sharers", Admin)
|
||||||
|
{
|
||||||
|
resourceSharers.GET("", ResourceSharerApi.RSGetSharersEndPoint)
|
||||||
|
resourceSharers.POST("/remove-resources", ResourceSharerApi.ResourceRemoveByUserIdAssignEndPoint)
|
||||||
|
resourceSharers.POST("/add-resources", ResourceSharerApi.ResourceAddByUserIdAssignEndPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
loginLogs := e.Group("login-logs", Admin)
|
||||||
|
{
|
||||||
|
loginLogs.GET("/paging", LoginLogApi.LoginLogPagingEndpoint)
|
||||||
|
loginLogs.DELETE("/:id", LoginLogApi.LoginLogDeleteEndpoint)
|
||||||
|
loginLogs.POST("/clear", LoginLogApi.LoginLogClearEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
properties := e.Group("properties", Admin)
|
||||||
|
{
|
||||||
|
properties.GET("", PropertyApi.PropertyGetEndpoint)
|
||||||
|
properties.PUT("", PropertyApi.PropertyUpdateEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
overview := e.Group("overview", Admin)
|
||||||
|
{
|
||||||
|
overview.GET("/counter", OverviewApi.OverviewCounterEndPoint)
|
||||||
|
overview.GET("/asset", OverviewApi.OverviewAssetEndPoint)
|
||||||
|
overview.GET("/access", OverviewApi.OverviewAccessEndPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
jobs := e.Group("/jobs", Admin)
|
||||||
|
{
|
||||||
|
jobs.POST("", JobApi.JobCreateEndpoint)
|
||||||
|
jobs.GET("/paging", JobApi.JobPagingEndpoint)
|
||||||
|
jobs.PUT("/:id", JobApi.JobUpdateEndpoint)
|
||||||
|
jobs.POST("/:id/change-status", JobApi.JobChangeStatusEndpoint)
|
||||||
|
jobs.POST("/:id/exec", JobApi.JobExecEndpoint)
|
||||||
|
jobs.DELETE("/:id", JobApi.JobDeleteEndpoint)
|
||||||
|
jobs.GET("/:id", JobApi.JobGetEndpoint)
|
||||||
|
jobs.GET("/:id/logs", JobApi.JobGetLogsEndpoint)
|
||||||
|
jobs.DELETE("/:id/logs", JobApi.JobDeleteLogsEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
securities := e.Group("/securities", Admin)
|
||||||
|
{
|
||||||
|
securities.POST("", SecurityApi.SecurityCreateEndpoint)
|
||||||
|
securities.GET("/paging", SecurityApi.SecurityPagingEndpoint)
|
||||||
|
securities.PUT("/:id", SecurityApi.SecurityUpdateEndpoint)
|
||||||
|
securities.DELETE("/:id", SecurityApi.SecurityDeleteEndpoint)
|
||||||
|
securities.GET("/:id", SecurityApi.SecurityGetEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
storages := e.Group("/storages")
|
||||||
|
{
|
||||||
|
storages.GET("/paging", StorageApi.StoragePagingEndpoint, Admin)
|
||||||
|
storages.POST("", StorageApi.StorageCreateEndpoint, Admin)
|
||||||
|
storages.DELETE("/:id", StorageApi.StorageDeleteEndpoint, Admin)
|
||||||
|
storages.PUT("/:id", StorageApi.StorageUpdateEndpoint, Admin)
|
||||||
|
storages.GET("/shares", StorageApi.StorageSharesEndpoint, Admin)
|
||||||
|
storages.GET("/:id", StorageApi.StorageGetEndpoint, Admin)
|
||||||
|
|
||||||
|
storages.POST("/:storageId/ls", StorageApi.StorageLsEndpoint)
|
||||||
|
storages.GET("/:storageId/download", StorageApi.StorageDownloadEndpoint)
|
||||||
|
storages.POST("/:storageId/upload", StorageApi.StorageUploadEndpoint)
|
||||||
|
storages.POST("/:storageId/mkdir", StorageApi.StorageMkDirEndpoint)
|
||||||
|
storages.POST("/:storageId/rm", StorageApi.StorageRmEndpoint)
|
||||||
|
storages.POST("/:storageId/rename", StorageApi.StorageRenameEndpoint)
|
||||||
|
storages.POST("/:storageId/edit", StorageApi.StorageEditEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
strategies := e.Group("/strategies", Admin)
|
||||||
|
{
|
||||||
|
strategies.GET("", StrategyApi.StrategyAllEndpoint)
|
||||||
|
strategies.GET("/paging", StrategyApi.StrategyPagingEndpoint)
|
||||||
|
strategies.POST("", StrategyApi.StrategyCreateEndpoint)
|
||||||
|
strategies.DELETE("/:id", StrategyApi.StrategyDeleteEndpoint)
|
||||||
|
strategies.PUT("/:id", StrategyApi.StrategyUpdateEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
accessGateways := e.Group("/access-gateways", Admin)
|
||||||
|
{
|
||||||
|
accessGateways.GET("", AccessGatewayApi.AccessGatewayAllEndpoint)
|
||||||
|
accessGateways.POST("", AccessGatewayApi.AccessGatewayCreateEndpoint)
|
||||||
|
accessGateways.GET("/paging", AccessGatewayApi.AccessGatewayPagingEndpoint)
|
||||||
|
accessGateways.PUT("/:id", AccessGatewayApi.AccessGatewayUpdateEndpoint)
|
||||||
|
accessGateways.DELETE("/:id", AccessGatewayApi.AccessGatewayDeleteEndpoint)
|
||||||
|
accessGateways.GET("/:id", AccessGatewayApi.AccessGatewayGetEndpoint)
|
||||||
|
accessGateways.POST("/:id/reconnect", AccessGatewayApi.AccessGatewayReconnectEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
backup := e.Group("/backup", Admin)
|
||||||
|
{
|
||||||
|
backup.GET("/export", BackupApi.BackupExportEndpoint)
|
||||||
|
backup.POST("/import", BackupApi.BackupImportEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
104
server/cli/cli.go
Normal file
104
server/cli/cli.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"next-terminal/server/env"
|
||||||
|
"next-terminal/server/service"
|
||||||
|
|
||||||
|
"next-terminal/server/log"
|
||||||
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cli struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCli() *Cli {
|
||||||
|
return &Cli{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli Cli) ResetPassword(username string) error {
|
||||||
|
user, err := repository.UserRepository.FindByUsername(context.TODO(), username)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
password := "next-terminal"
|
||||||
|
passwd, err := utils.Encoder.Encode([]byte(password))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u := &model.User{
|
||||||
|
Password: string(passwd),
|
||||||
|
ID: user.ID,
|
||||||
|
}
|
||||||
|
if err := repository.UserRepository.Update(context.TODO(), u); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("用户「%v」密码初始化为: %v", user.Username, password)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli Cli) ResetTotp(username string) error {
|
||||||
|
user, err := repository.UserRepository.FindByUsername(context.TODO(), username)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u := &model.User{
|
||||||
|
TOTPSecret: "-",
|
||||||
|
ID: user.ID,
|
||||||
|
}
|
||||||
|
if err := repository.UserRepository.Update(context.TODO(), u); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("用户「%v」已重置TOTP", user.Username)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli Cli) ChangeEncryptionKey(oldEncryptionKey, newEncryptionKey string) error {
|
||||||
|
|
||||||
|
oldPassword := []byte(fmt.Sprintf("%x", md5.Sum([]byte(oldEncryptionKey))))
|
||||||
|
newPassword := []byte(fmt.Sprintf("%x", md5.Sum([]byte(newEncryptionKey))))
|
||||||
|
|
||||||
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := context.WithValue(context.TODO(), "db", tx)
|
||||||
|
credentials, err := repository.CredentialRepository.FindAll(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range credentials {
|
||||||
|
credential := credentials[i]
|
||||||
|
if err := service.CredentialService.Decrypt(&credential, oldPassword); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.CredentialService.Encrypt(&credential, newPassword); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := repository.CredentialRepository.UpdateById(c, &credential, credential.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assets, err := repository.AssetRepository.FindAll(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range assets {
|
||||||
|
asset := assets[i]
|
||||||
|
if err := service.AssetService.Decrypt(&asset, oldPassword); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := service.AssetService.Encrypt(&asset, newPassword); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := repository.AssetRepository.UpdateById(c, &asset, asset.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Infof("encryption key has being changed.")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "v1.2.2"
|
Version = "v1.2.3"
|
||||||
Banner = `
|
Banner = `
|
||||||
_______ __ ___________ .__ .__
|
_______ __ ___________ .__ .__
|
||||||
\ \ ____ ___ ____/ |_ \__ ___/__________ _____ |__| ____ _____ | |
|
\ \ ____ ___ ____/ |_ \__ ___/__________ _____ |__| ____ _____ | |
|
||||||
@ -20,6 +20,8 @@ const (
|
|||||||
const Token = "X-Auth-Token"
|
const Token = "X-Auth-Token"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
DB = "db"
|
||||||
|
|
||||||
SSH = "ssh"
|
SSH = "ssh"
|
||||||
RDP = "rdp"
|
RDP = "rdp"
|
||||||
VNC = "vnc"
|
VNC = "vnc"
|
||||||
@ -57,6 +59,8 @@ const (
|
|||||||
TypeUser = "user" // 普通用户
|
TypeUser = "user" // 普通用户
|
||||||
TypeAdmin = "admin" // 管理员
|
TypeAdmin = "admin" // 管理员
|
||||||
|
|
||||||
|
SourceLdap = "ldap" // 从LDAP同步的用户
|
||||||
|
|
||||||
StatusEnabled = "enabled"
|
StatusEnabled = "enabled"
|
||||||
StatusDisabled = "disabled"
|
StatusDisabled = "disabled"
|
||||||
|
|
||||||
@ -65,10 +69,16 @@ const (
|
|||||||
SocksProxyPort = "socks-proxy-port"
|
SocksProxyPort = "socks-proxy-port"
|
||||||
SocksProxyUsername = "socks-proxy-username"
|
SocksProxyUsername = "socks-proxy-username"
|
||||||
SocksProxyPassword = "socks-proxy-password"
|
SocksProxyPassword = "socks-proxy-password"
|
||||||
|
|
||||||
|
LoginToken = "login-token"
|
||||||
|
AccessToken = "access-token"
|
||||||
|
ShareSession = "share-session"
|
||||||
|
|
||||||
|
Anonymous = "anonymous"
|
||||||
)
|
)
|
||||||
|
|
||||||
var SSHParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, SshMode, SocksProxyEnable, SocksProxyHost, SocksProxyPort, SocksProxyUsername, SocksProxyPassword}
|
var SSHParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, SshMode, SocksProxyEnable, SocksProxyHost, SocksProxyPort, SocksProxyUsername, SocksProxyPassword}
|
||||||
var RDPParameterNames = []string{guacd.Domain, guacd.RemoteApp, guacd.RemoteAppDir, guacd.RemoteAppArgs, guacd.EnableDrive, guacd.DrivePath}
|
var RDPParameterNames = []string{guacd.Domain, guacd.RemoteApp, guacd.RemoteAppDir, guacd.RemoteAppArgs, guacd.EnableDrive, guacd.DrivePath, guacd.ColorDepth, guacd.ForceLossless}
|
||||||
var VNCParameterNames = []string{guacd.ColorDepth, guacd.Cursor, guacd.SwapRedBlue, guacd.DestHost, guacd.DestPort}
|
var VNCParameterNames = []string{guacd.ColorDepth, guacd.Cursor, guacd.SwapRedBlue, guacd.DestHost, guacd.DestPort}
|
||||||
var TelnetParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, guacd.UsernameRegex, guacd.PasswordRegex, guacd.LoginSuccessRegex, guacd.LoginFailureRegex}
|
var TelnetParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, guacd.UsernameRegex, guacd.PasswordRegex, guacd.LoginSuccessRegex, guacd.LoginFailureRegex}
|
||||||
var KubernetesParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, guacd.Namespace, guacd.Pod, guacd.Container, guacd.UesSSL, guacd.ClientCert, guacd.ClientKey, guacd.CaCert, guacd.IgnoreCert}
|
var KubernetesParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, guacd.Namespace, guacd.Pod, guacd.Container, guacd.UesSSL, guacd.ClientCert, guacd.ClientKey, guacd.CaCert, guacd.IgnoreCert}
|
||||||
|
7
server/constant/errors.go
Normal file
7
server/constant/errors.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package constant
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNameAlreadyUsed = errors.New("name already used")
|
||||||
|
)
|
27
server/dto/auth.go
Normal file
27
server/dto/auth.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import "next-terminal/server/model"
|
||||||
|
|
||||||
|
type Authorization struct {
|
||||||
|
Token string
|
||||||
|
Remember bool
|
||||||
|
Type string // LoginToken: 登录令牌, AccessToken: 授权令牌, ShareSession: 会话分享, AccessSession: 只允许访问特定的会话
|
||||||
|
User *model.User
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginAccount struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Remember bool `json:"remember"`
|
||||||
|
TOTP string `json:"totp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfirmTOTP struct {
|
||||||
|
Secret string `json:"secret"`
|
||||||
|
TOTP string `json:"totp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangePassword struct {
|
||||||
|
NewPassword string `json:"newPassword"`
|
||||||
|
OldPassword string `json:"oldPassword"`
|
||||||
|
}
|
8
server/dto/dashboard.go
Normal file
8
server/dto/dashboard.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
type Counter struct {
|
||||||
|
User int64 `json:"user"`
|
||||||
|
Asset int64 `json:"asset"`
|
||||||
|
Credential int64 `json:"credential"`
|
||||||
|
OnlineSession int64 `json:"onlineSession"`
|
||||||
|
}
|
7
server/dto/identity.go
Normal file
7
server/dto/identity.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
type UserGroup struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Members []string `json:"members"`
|
||||||
|
}
|
32
server/dto/resource.go
Normal file
32
server/dto/resource.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import "next-terminal/server/model"
|
||||||
|
|
||||||
|
type RU struct {
|
||||||
|
UserGroupId string `json:"userGroupId"`
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
StrategyId string `json:"strategyId"`
|
||||||
|
ResourceType string `json:"resourceType"`
|
||||||
|
ResourceIds []string `json:"resourceIds"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UR struct {
|
||||||
|
ResourceId string `json:"resourceId"`
|
||||||
|
ResourceType string `json:"resourceType"`
|
||||||
|
UserIds []string `json:"userIds"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Backup struct {
|
||||||
|
Users []model.User `json:"users"`
|
||||||
|
UserGroups []model.UserGroup `json:"user_groups"`
|
||||||
|
|
||||||
|
Storages []model.Storage `json:"storages"`
|
||||||
|
Strategies []model.Strategy `json:"strategies"`
|
||||||
|
AccessSecurities []model.AccessSecurity `json:"access_securities"`
|
||||||
|
AccessGateways []model.AccessGateway `json:"access_gateways"`
|
||||||
|
Commands []model.Command `json:"commands"`
|
||||||
|
Credentials []model.Credential `json:"credentials"`
|
||||||
|
Assets []map[string]interface{} `json:"assets"`
|
||||||
|
ResourceSharers []model.ResourceSharer `json:"resource_sharers"`
|
||||||
|
Jobs []model.Job `json:"jobs"`
|
||||||
|
}
|
11
server/dto/session.go
Normal file
11
server/dto/session.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
type ExternalSession struct {
|
||||||
|
AssetId string `json:"assetId"`
|
||||||
|
FileSystem string `json:"fileSystem"`
|
||||||
|
Upload string `json:"upload"`
|
||||||
|
Download string `json:"download"`
|
||||||
|
Delete string `json:"delete"`
|
||||||
|
Rename string `json:"rename"`
|
||||||
|
Edit string `json:"edit"`
|
||||||
|
}
|
39
server/dto/ssh.go
Normal file
39
server/dto/ssh.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Type int `json:"type"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Message) ToString() string {
|
||||||
|
if r.Content != "" {
|
||||||
|
return strconv.Itoa(r.Type) + r.Content
|
||||||
|
} else {
|
||||||
|
return strconv.Itoa(r.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMessage(_type int, content string) Message {
|
||||||
|
return Message{Content: content, Type: _type}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseMessage(value string) (message Message, err error) {
|
||||||
|
if value == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_type, err := strconv.Atoi(value[:1])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var content = value[1:]
|
||||||
|
message = NewMessage(_type, content)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type WindowSize struct {
|
||||||
|
Cols int `json:"cols"`
|
||||||
|
Rows int `json:"rows"`
|
||||||
|
}
|
55
server/env/db.go
vendored
Normal file
55
server/env/db.go
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package env
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"next-terminal/server/config"
|
||||||
|
"next-terminal/server/model"
|
||||||
|
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupDB() *gorm.DB {
|
||||||
|
|
||||||
|
var logMode logger.Interface
|
||||||
|
if config.GlobalCfg.Debug {
|
||||||
|
logMode = logger.Default.LogMode(logger.Info)
|
||||||
|
} else {
|
||||||
|
logMode = logger.Default.LogMode(logger.Silent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("当前数据库模式为:%v\n", config.GlobalCfg.DB)
|
||||||
|
var err error
|
||||||
|
var db *gorm.DB
|
||||||
|
if config.GlobalCfg.DB == "mysql" {
|
||||||
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=60s",
|
||||||
|
config.GlobalCfg.Mysql.Username,
|
||||||
|
config.GlobalCfg.Mysql.Password,
|
||||||
|
config.GlobalCfg.Mysql.Hostname,
|
||||||
|
config.GlobalCfg.Mysql.Port,
|
||||||
|
config.GlobalCfg.Mysql.Database,
|
||||||
|
)
|
||||||
|
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||||
|
Logger: logMode,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
db, err = gorm.Open(sqlite.Open(config.GlobalCfg.Sqlite.File), &gorm.Config{
|
||||||
|
Logger: logMode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("连接数据库异常: %v", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.AutoMigrate(&model.User{}, &model.Asset{}, &model.AssetAttribute{}, &model.Session{}, &model.Command{},
|
||||||
|
&model.Credential{}, &model.Property{}, &model.ResourceSharer{}, &model.UserGroup{}, &model.UserGroupMember{},
|
||||||
|
&model.LoginLog{}, &model.Job{}, &model.JobLog{}, &model.AccessSecurity{}, &model.AccessGateway{},
|
||||||
|
&model.Storage{}, &model.Strategy{}, &model.AccessToken{}); err != nil {
|
||||||
|
panic(fmt.Errorf("初始化数据库表结构异常: %v", err.Error()))
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
}
|
19
server/env/env.go
vendored
Normal file
19
server/env/env.go
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package env
|
||||||
|
|
||||||
|
import "gorm.io/gorm"
|
||||||
|
|
||||||
|
var env *Env
|
||||||
|
|
||||||
|
type Env struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
env = &Env{
|
||||||
|
db: setupDB(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDB() *gorm.DB {
|
||||||
|
return env.db
|
||||||
|
}
|
13
server/global/cache/cache.go
vendored
13
server/global/cache/cache.go
vendored
@ -6,8 +6,17 @@ import (
|
|||||||
"github.com/patrickmn/go-cache"
|
"github.com/patrickmn/go-cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
var GlobalCache *cache.Cache
|
const (
|
||||||
|
NoExpiration = -1
|
||||||
|
RememberMeExpiration = time.Hour * time.Duration(24*14)
|
||||||
|
NotRememberExpiration = time.Hour * time.Duration(2)
|
||||||
|
LoginLockExpiration = time.Minute * time.Duration(5)
|
||||||
|
)
|
||||||
|
|
||||||
|
var TokenManager *cache.Cache
|
||||||
|
var LoginFailedKeyManager *cache.Cache
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
GlobalCache = cache.New(5*time.Minute, 10*time.Minute)
|
TokenManager = cache.New(5*time.Minute, 10*time.Minute)
|
||||||
|
LoginFailedKeyManager = cache.New(5*time.Minute, 10*time.Minute)
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"next-terminal/server/config"
|
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
@ -17,7 +16,6 @@ import (
|
|||||||
type Gateway struct {
|
type Gateway struct {
|
||||||
ID string // 接入网关ID
|
ID string // 接入网关ID
|
||||||
Connected bool // 是否已连接
|
Connected bool // 是否已连接
|
||||||
LocalHost string // 隧道映射到本地的IP地址
|
|
||||||
SshClient *ssh.Client
|
SshClient *ssh.Client
|
||||||
Message string // 失败原因
|
Message string // 失败原因
|
||||||
|
|
||||||
@ -28,10 +26,9 @@ type Gateway struct {
|
|||||||
exit chan bool
|
exit chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGateway(id, localhost string, connected bool, message string, client *ssh.Client) *Gateway {
|
func NewGateway(id string, connected bool, message string, client *ssh.Client) *Gateway {
|
||||||
return &Gateway{
|
return &Gateway{
|
||||||
ID: id,
|
ID: id,
|
||||||
LocalHost: localhost,
|
|
||||||
Connected: connected,
|
Connected: connected,
|
||||||
Message: message,
|
Message: message,
|
||||||
SshClient: client,
|
SshClient: client,
|
||||||
@ -80,26 +77,13 @@ func (g *Gateway) OpenSshTunnel(id, ip string, port int) (exposedIP string, expo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
localHost := g.LocalHost
|
|
||||||
if localHost == "" {
|
|
||||||
if config.GlobalCfg.Container {
|
|
||||||
localIp, err := utils.GetLocalIp()
|
|
||||||
if err != nil {
|
|
||||||
hostname, err := os.Hostname()
|
hostname, err := os.Hostname()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
} else {
|
|
||||||
localHost = hostname
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
localHost = localIp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
localHost = "localhost"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
localAddr := fmt.Sprintf("%s:%d", localHost, localPort)
|
localAddr := fmt.Sprintf("%s:%d", hostname, localPort)
|
||||||
listener, err := net.Listen("tcp", localAddr)
|
listener, err := net.Listen("tcp", localAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
@ -108,7 +92,7 @@ func (g *Gateway) OpenSshTunnel(id, ip string, port int) (exposedIP string, expo
|
|||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
tunnel := &Tunnel{
|
tunnel := &Tunnel{
|
||||||
ID: id,
|
ID: id,
|
||||||
LocalHost: g.LocalHost,
|
LocalHost: hostname,
|
||||||
LocalPort: localPort,
|
LocalPort: localPort,
|
||||||
Gateway: g,
|
Gateway: g,
|
||||||
RemoteHost: ip,
|
RemoteHost: ip,
|
||||||
|
@ -10,8 +10,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Host = "host"
|
|
||||||
Port = "port"
|
|
||||||
EnableRecording = "enable-recording"
|
EnableRecording = "enable-recording"
|
||||||
RecordingPath = "recording-path"
|
RecordingPath = "recording-path"
|
||||||
CreateRecordingPath = "create-recording-path"
|
CreateRecordingPath = "create-recording-path"
|
||||||
@ -33,7 +31,9 @@ const (
|
|||||||
EnableMenuAnimations = "enable-menu-animations"
|
EnableMenuAnimations = "enable-menu-animations"
|
||||||
DisableBitmapCaching = "disable-bitmap-caching"
|
DisableBitmapCaching = "disable-bitmap-caching"
|
||||||
DisableOffscreenCaching = "disable-offscreen-caching"
|
DisableOffscreenCaching = "disable-offscreen-caching"
|
||||||
|
// DisableGlyphCaching Deprecated
|
||||||
DisableGlyphCaching = "disable-glyph-caching"
|
DisableGlyphCaching = "disable-glyph-caching"
|
||||||
|
ForceLossless = "force-lossless"
|
||||||
|
|
||||||
Domain = "domain"
|
Domain = "domain"
|
||||||
RemoteApp = "remote-app"
|
RemoteApp = "remote-app"
|
||||||
@ -62,7 +62,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const Delimiter = ';'
|
const Delimiter = ';'
|
||||||
const Version = "VERSION_1_3_0"
|
const Version = "VERSION_1_4_0"
|
||||||
|
|
||||||
type Configuration struct {
|
type Configuration struct {
|
||||||
ConnectionID string
|
ConnectionID string
|
||||||
|
@ -8,7 +8,6 @@ type AccessGateway struct {
|
|||||||
Name string `gorm:"type:varchar(500)" json:"name"`
|
Name string `gorm:"type:varchar(500)" json:"name"`
|
||||||
IP string `gorm:"type:varchar(500)" json:"ip"`
|
IP string `gorm:"type:varchar(500)" json:"ip"`
|
||||||
Port int `gorm:"type:int(5)" json:"port"`
|
Port int `gorm:"type:int(5)" json:"port"`
|
||||||
Localhost string `gorm:"type:varchar(200)" json:"localhost"` // 隧道映射到本地的地址
|
|
||||||
AccountType string `gorm:"type:varchar(50)" json:"accountType"`
|
AccountType string `gorm:"type:varchar(50)" json:"accountType"`
|
||||||
Username string `gorm:"type:varchar(200)" json:"username"`
|
Username string `gorm:"type:varchar(200)" json:"username"`
|
||||||
Password string `gorm:"type:varchar(500)" json:"password"`
|
Password string `gorm:"type:varchar(500)" json:"password"`
|
||||||
|
14
server/model/access_token.go
Normal file
14
server/model/access_token.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "next-terminal/server/utils"
|
||||||
|
|
||||||
|
type AccessToken struct {
|
||||||
|
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||||
|
UserId string `gorm:"index,type:varchar(200)" json:"userId"`
|
||||||
|
Token string `gorm:"index,type:varchar(128)" json:"token"`
|
||||||
|
Created utils.JsonTime `json:"created"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *AccessToken) TableName() string {
|
||||||
|
return "access_token"
|
||||||
|
}
|
@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LoginLog struct {
|
type LoginLog struct {
|
||||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
ID string `gorm:"primary_key,type:varchar(128)" json:"id"`
|
||||||
Username string `gorm:"index,type:varchar(200)" json:"username"`
|
Username string `gorm:"index,type:varchar(200)" json:"username"`
|
||||||
ClientIP string `gorm:"type:varchar(200)" json:"clientIp"`
|
ClientIP string `gorm:"type:varchar(200)" json:"clientIp"`
|
||||||
ClientUserAgent string `gorm:"type:varchar(500)" json:"clientUserAgent"`
|
ClientUserAgent string `gorm:"type:varchar(500)" json:"clientUserAgent"`
|
||||||
|
@ -26,12 +26,15 @@ type Session struct {
|
|||||||
ConnectedTime utils.JsonTime `json:"connectedTime"`
|
ConnectedTime utils.JsonTime `json:"connectedTime"`
|
||||||
DisconnectedTime utils.JsonTime `json:"disconnectedTime"`
|
DisconnectedTime utils.JsonTime `json:"disconnectedTime"`
|
||||||
Mode string `gorm:"type:varchar(10)" json:"mode"`
|
Mode string `gorm:"type:varchar(10)" json:"mode"`
|
||||||
Upload string `gorm:"type:varchar(1)" json:"upload"` // 1 = true, 0 = false
|
FileSystem string `gorm:"type:varchar(1)" json:"fileSystem"` // 1 = true, 0 = false
|
||||||
|
Upload string `gorm:"type:varchar(1)" json:"upload"`
|
||||||
Download string `gorm:"type:varchar(1)" json:"download"`
|
Download string `gorm:"type:varchar(1)" json:"download"`
|
||||||
Delete string `gorm:"type:varchar(1)" json:"delete"`
|
Delete string `gorm:"type:varchar(1)" json:"delete"`
|
||||||
Rename string `gorm:"type:varchar(1)" json:"rename"`
|
Rename string `gorm:"type:varchar(1)" json:"rename"`
|
||||||
Edit string `gorm:"type:varchar(1)" json:"edit"`
|
Edit string `gorm:"type:varchar(1)" json:"edit"`
|
||||||
CreateDir string `gorm:"type:varchar(1)" json:"createDir"`
|
CreateDir string `gorm:"type:varchar(1)" json:"createDir"`
|
||||||
|
Copy string `gorm:"type:varchar(1)" json:"copy"`
|
||||||
|
Paste string `gorm:"type:varchar(1)" json:"paste"`
|
||||||
StorageId string `gorm:"type:varchar(36)" json:"storageId"`
|
StorageId string `gorm:"type:varchar(36)" json:"storageId"`
|
||||||
AccessGatewayId string `gorm:"type:varchar(36)" json:"accessGatewayId"`
|
AccessGatewayId string `gorm:"type:varchar(36)" json:"accessGatewayId"`
|
||||||
Reviewed bool `gorm:"type:tinyint(1)" json:"reviewed"`
|
Reviewed bool `gorm:"type:tinyint(1)" json:"reviewed"`
|
||||||
|
@ -11,6 +11,8 @@ type Strategy struct {
|
|||||||
Rename string `gorm:"type:varchar(1)" json:"rename"`
|
Rename string `gorm:"type:varchar(1)" json:"rename"`
|
||||||
Edit string `gorm:"type:varchar(1)" json:"edit"`
|
Edit string `gorm:"type:varchar(1)" json:"edit"`
|
||||||
CreateDir string `gorm:"type:varchar(1)" json:"createDir"`
|
CreateDir string `gorm:"type:varchar(1)" json:"createDir"`
|
||||||
|
Copy string `gorm:"type:varchar(1)" json:"copy"`
|
||||||
|
Paste string `gorm:"type:varchar(1)" json:"paste"`
|
||||||
Created utils.JsonTime `json:"created"`
|
Created utils.JsonTime `json:"created"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ type User struct {
|
|||||||
Created utils.JsonTime `json:"created"`
|
Created utils.JsonTime `json:"created"`
|
||||||
Type string `gorm:"type:varchar(20)" json:"type"`
|
Type string `gorm:"type:varchar(20)" json:"type"`
|
||||||
Mail string `gorm:"type:varchar(500)" json:"mail"`
|
Mail string `gorm:"type:varchar(500)" json:"mail"`
|
||||||
|
Source string `gorm:"type:varchar(20)" json:"source"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserForPage struct {
|
type UserForPage struct {
|
||||||
@ -27,6 +28,7 @@ type UserForPage struct {
|
|||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Created utils.JsonTime `json:"created"`
|
Created utils.JsonTime `json:"created"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
Source string `json:"source"`
|
||||||
SharerAssetCount int64 `json:"sharerAssetCount"`
|
SharerAssetCount int64 `json:"sharerAssetCount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"next-terminal/server/model"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AccessGatewayRepository struct {
|
|
||||||
DB *gorm.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAccessGatewayRepository(db *gorm.DB) *AccessGatewayRepository {
|
|
||||||
accessGatewayRepository = &AccessGatewayRepository{DB: db}
|
|
||||||
return accessGatewayRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessGatewayRepository) Find(pageIndex, pageSize int, ip, name, order, field string) (o []model.AccessGatewayForPage, total int64, err error) {
|
|
||||||
t := model.AccessGateway{}
|
|
||||||
db := r.DB.Table(t.TableName())
|
|
||||||
dbCounter := r.DB.Table(t.TableName())
|
|
||||||
|
|
||||||
if len(ip) > 0 {
|
|
||||||
db = db.Where("ip like ?", "%"+ip+"%")
|
|
||||||
dbCounter = dbCounter.Where("ip like ?", "%"+ip+"%")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(name) > 0 {
|
|
||||||
db = db.Where("name like ?", "%"+name+"%")
|
|
||||||
dbCounter = dbCounter.Where("name like ?", "%"+name+"%")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = dbCounter.Count(&total).Error
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if order == "descend" {
|
|
||||||
order = "desc"
|
|
||||||
} else {
|
|
||||||
order = "asc"
|
|
||||||
}
|
|
||||||
|
|
||||||
if field == "ip" {
|
|
||||||
field = "ip"
|
|
||||||
} else if field == "name" {
|
|
||||||
field = "name"
|
|
||||||
} else {
|
|
||||||
field = "created"
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.Order(field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error
|
|
||||||
if o == nil {
|
|
||||||
o = make([]model.AccessGatewayForPage, 0)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessGatewayRepository) Create(o *model.AccessGateway) error {
|
|
||||||
return r.DB.Create(o).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessGatewayRepository) UpdateById(o *model.AccessGateway, id string) error {
|
|
||||||
o.ID = id
|
|
||||||
return r.DB.Updates(o).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessGatewayRepository) DeleteById(id string) error {
|
|
||||||
return r.DB.Where("id = ?", id).Delete(model.AccessGateway{}).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessGatewayRepository) FindById(id string) (o model.AccessGateway, err error) {
|
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessGatewayRepository) FindAll() (o []model.AccessGateway, err error) {
|
|
||||||
err = r.DB.Find(&o).Error
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"next-terminal/server/model"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AccessSecurityRepository struct {
|
|
||||||
DB *gorm.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAccessSecurityRepository(db *gorm.DB) *AccessSecurityRepository {
|
|
||||||
accessSecurityRepository = &AccessSecurityRepository{DB: db}
|
|
||||||
return accessSecurityRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessSecurityRepository) FindAll() (o []model.AccessSecurity, err error) {
|
|
||||||
err = r.DB.Order("priority asc").Find(&o).Error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessSecurityRepository) Find(pageIndex, pageSize int, ip, rule, order, field string) (o []model.AccessSecurity, total int64, err error) {
|
|
||||||
t := model.AccessSecurity{}
|
|
||||||
db := r.DB.Table(t.TableName())
|
|
||||||
dbCounter := r.DB.Table(t.TableName())
|
|
||||||
|
|
||||||
if len(ip) > 0 {
|
|
||||||
db = db.Where("ip like ?", "%"+ip+"%")
|
|
||||||
dbCounter = dbCounter.Where("ip like ?", "%"+ip+"%")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rule) > 0 {
|
|
||||||
db = db.Where("rule = ?", rule)
|
|
||||||
dbCounter = dbCounter.Where("rule = ?", rule)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = dbCounter.Count(&total).Error
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if order == "descend" {
|
|
||||||
order = "desc"
|
|
||||||
} else {
|
|
||||||
order = "asc"
|
|
||||||
}
|
|
||||||
|
|
||||||
if field == "ip" {
|
|
||||||
field = "ip"
|
|
||||||
} else if field == "rule" {
|
|
||||||
field = "rule"
|
|
||||||
} else {
|
|
||||||
field = "priority"
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.Order(field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error
|
|
||||||
if o == nil {
|
|
||||||
o = make([]model.AccessSecurity, 0)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessSecurityRepository) Create(o *model.AccessSecurity) error {
|
|
||||||
return r.DB.Create(o).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessSecurityRepository) UpdateById(o *model.AccessSecurity, id string) error {
|
|
||||||
o.ID = id
|
|
||||||
return r.DB.Updates(o).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessSecurityRepository) DeleteById(id string) error {
|
|
||||||
return r.DB.Where("id = ?", id).Delete(model.AccessSecurity{}).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessSecurityRepository) FindById(id string) (o *model.AccessSecurity, err error) {
|
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
|
||||||
return
|
|
||||||
}
|
|
29
server/repository/access_token.go
Normal file
29
server/repository/access_token.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"next-terminal/server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type accessTokenRepository struct {
|
||||||
|
baseRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo accessTokenRepository) FindByUserId(ctx context.Context, userId string) (o model.AccessToken, err error) {
|
||||||
|
err = repo.GetDB(ctx).Where("user_id = ?", userId).First(&o).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo accessTokenRepository) DeleteByUserId(ctx context.Context, userId string) error {
|
||||||
|
return repo.GetDB(ctx).Where("user_id = ?", userId).Delete(&model.AccessToken{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo accessTokenRepository) Create(ctx context.Context, o *model.AccessToken) error {
|
||||||
|
return repo.GetDB(ctx).Create(o).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo accessTokenRepository) FindAll(ctx context.Context) (o []model.AccessToken, err error) {
|
||||||
|
err = repo.GetDB(ctx).Find(&o).Error
|
||||||
|
return
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -11,47 +11,41 @@ import (
|
|||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AssetRepository struct {
|
type assetRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAssetRepository(db *gorm.DB) *AssetRepository {
|
func (r assetRepository) FindAll(c context.Context) (o []model.Asset, err error) {
|
||||||
assetRepository = &AssetRepository{DB: db}
|
err = r.GetDB(c).Find(&o).Error
|
||||||
return assetRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AssetRepository) FindAll() (o []model.Asset, err error) {
|
|
||||||
err = r.DB.Find(&o).Error
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) FindByIds(assetIds []string) (o []model.Asset, err error) {
|
func (r assetRepository) FindByIds(c context.Context, assetIds []string) (o []model.Asset, err error) {
|
||||||
err = r.DB.Where("id in ?", assetIds).Find(&o).Error
|
err = r.GetDB(c).Where("id in ?", assetIds).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) FindByProtocol(protocol string) (o []model.Asset, err error) {
|
func (r assetRepository) FindByProtocol(c context.Context, protocol string) (o []model.Asset, err error) {
|
||||||
err = r.DB.Where("protocol = ?", protocol).Find(&o).Error
|
err = r.GetDB(c).Where("protocol = ?", protocol).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) FindByProtocolAndIds(protocol string, assetIds []string) (o []model.Asset, err error) {
|
func (r assetRepository) FindByProtocolAndIds(c context.Context, protocol string, assetIds []string) (o []model.Asset, err error) {
|
||||||
err = r.DB.Where("protocol = ? and id in ?", protocol, assetIds).Find(&o).Error
|
err = r.GetDB(c).Where("protocol = ? and id in ?", protocol, assetIds).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) FindByProtocolAndUser(protocol string, account model.User) (o []model.Asset, err error) {
|
func (r assetRepository) FindByProtocolAndUser(c context.Context, protocol string, account model.User) (o []model.Asset, err error) {
|
||||||
db := r.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created,assets.tags,assets.description, users.nickname as owner_name").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id")
|
db := r.GetDB(c).Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created,assets.tags,assets.description, users.nickname as owner_name").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id")
|
||||||
|
|
||||||
if constant.TypeUser == account.Type {
|
if constant.TypeUser == account.Type {
|
||||||
owner := account.ID
|
owner := account.ID
|
||||||
db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner)
|
db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner)
|
||||||
|
|
||||||
// 查询用户所在用户组列表
|
// 查询用户所在用户组列表
|
||||||
userGroupIds, err := userGroupRepository.FindUserGroupIdsByUserId(account.ID)
|
userGroupIds, err := UserGroupMemberRepository.FindUserGroupIdsByUserId(c, account.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -68,9 +62,9 @@ func (r AssetRepository) FindByProtocolAndUser(protocol string, account model.Us
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) Find(pageIndex, pageSize int, name, protocol, tags string, account model.User, owner, sharer, userGroupId, ip, order, field string) (o []model.AssetForPage, total int64, err error) {
|
func (r assetRepository) Find(c context.Context, pageIndex, pageSize int, name, protocol, tags string, account *model.User, owner, sharer, userGroupId, ip, order, field string) (o []model.AssetForPage, total int64, err error) {
|
||||||
db := r.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created,assets.tags,assets.description, users.nickname as owner_name").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id")
|
db := r.GetDB(c).Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created,assets.tags,assets.description, users.nickname as owner_name").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id")
|
||||||
dbCounter := r.DB.Table("assets").Select("DISTINCT assets.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id")
|
dbCounter := r.GetDB(c).Table("assets").Select("DISTINCT assets.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id")
|
||||||
|
|
||||||
if constant.TypeUser == account.Type {
|
if constant.TypeUser == account.Type {
|
||||||
owner := account.ID
|
owner := account.ID
|
||||||
@ -78,7 +72,7 @@ func (r AssetRepository) Find(pageIndex, pageSize int, name, protocol, tags stri
|
|||||||
dbCounter = dbCounter.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner)
|
dbCounter = dbCounter.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner)
|
||||||
|
|
||||||
// 查询用户所在用户组列表
|
// 查询用户所在用户组列表
|
||||||
userGroupIds, err := userGroupRepository.FindUserGroupIdsByUserId(account.ID)
|
userGroupIds, err := UserGroupMemberRepository.FindUserGroupIdsByUserId(c, account.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@ -155,7 +149,7 @@ func (r AssetRepository) Find(pageIndex, pageSize int, name, protocol, tags stri
|
|||||||
} else {
|
} else {
|
||||||
for i := 0; i < len(o); i++ {
|
for i := 0; i < len(o); i++ {
|
||||||
if o[i].Protocol == "ssh" {
|
if o[i].Protocol == "ssh" {
|
||||||
attributes, err := r.FindAttrById(o[i].ID)
|
attributes, err := r.FindAttrById(c, o[i].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -172,134 +166,50 @@ func (r AssetRepository) Find(pageIndex, pageSize int, name, protocol, tags stri
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) Encrypt(item *model.Asset, password []byte) error {
|
func (r assetRepository) Create(c context.Context, o *model.Asset) (err error) {
|
||||||
if item.Password != "" && item.Password != "-" {
|
return r.GetDB(c).Create(o).Error
|
||||||
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Password), password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.Password = base64.StdEncoding.EncodeToString(encryptedCBC)
|
|
||||||
}
|
|
||||||
if item.PrivateKey != "" && item.PrivateKey != "-" {
|
|
||||||
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.PrivateKey), password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.PrivateKey = base64.StdEncoding.EncodeToString(encryptedCBC)
|
|
||||||
}
|
|
||||||
if item.Passphrase != "" && item.Passphrase != "-" {
|
|
||||||
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Passphrase), password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.Passphrase = base64.StdEncoding.EncodeToString(encryptedCBC)
|
|
||||||
}
|
|
||||||
item.Encrypted = true
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) Create(o *model.Asset) (err error) {
|
func (r assetRepository) FindById(c context.Context, id string) (o model.Asset, err error) {
|
||||||
if err := r.Encrypt(o, config.GlobalCfg.EncryptionPassword); err != nil {
|
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = r.DB.Create(o).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AssetRepository) FindById(id string) (o model.Asset, err error) {
|
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) Decrypt(item *model.Asset, password []byte) error {
|
func (r assetRepository) UpdateById(c context.Context, o *model.Asset, id string) error {
|
||||||
if item.Encrypted {
|
|
||||||
if item.Password != "" && item.Password != "-" {
|
|
||||||
origData, err := base64.StdEncoding.DecodeString(item.Password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.Password = string(decryptedCBC)
|
|
||||||
}
|
|
||||||
if item.PrivateKey != "" && item.PrivateKey != "-" {
|
|
||||||
origData, err := base64.StdEncoding.DecodeString(item.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.PrivateKey = string(decryptedCBC)
|
|
||||||
}
|
|
||||||
if item.Passphrase != "" && item.Passphrase != "-" {
|
|
||||||
origData, err := base64.StdEncoding.DecodeString(item.Passphrase)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.Passphrase = string(decryptedCBC)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AssetRepository) FindByIdAndDecrypt(id string) (o model.Asset, err error) {
|
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
|
||||||
if err == nil {
|
|
||||||
err = r.Decrypt(&o, config.GlobalCfg.EncryptionPassword)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AssetRepository) UpdateById(o *model.Asset, id string) error {
|
|
||||||
o.ID = id
|
o.ID = id
|
||||||
return r.DB.Updates(o).Error
|
return r.GetDB(c).Updates(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) UpdateActiveById(active bool, id string) error {
|
func (r assetRepository) UpdateActiveById(c context.Context, active bool, id string) error {
|
||||||
sql := "update assets set active = ? where id = ?"
|
sql := "update assets set active = ? where id = ?"
|
||||||
return r.DB.Exec(sql, active, id).Error
|
return r.GetDB(c).Exec(sql, active, id).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) DeleteById(id string) (err error) {
|
func (r assetRepository) DeleteById(c context.Context, assetId string) (err error) {
|
||||||
return r.DB.Transaction(func(tx *gorm.DB) error {
|
return r.GetDB(c).Where("id = ?", assetId).Delete(&model.Asset{}).Error
|
||||||
err = tx.Where("id = ?", id).Delete(&model.Asset{}).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 删除资产属性
|
|
||||||
err = tx.Where("asset_id = ?", id).Delete(&model.AssetAttribute{}).Error
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) Count() (total int64, err error) {
|
func (r assetRepository) DeleteAttrByAssetId(c context.Context, assetId string) error {
|
||||||
err = r.DB.Find(&model.Asset{}).Count(&total).Error
|
return r.GetDB(c).Where("asset_id = ?", assetId).Delete(&model.AssetAttribute{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r assetRepository) Count(c context.Context) (total int64, err error) {
|
||||||
|
err = r.GetDB(c).Find(&model.Asset{}).Count(&total).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) CountByProtocol(protocol string) (total int64, err error) {
|
func (r assetRepository) CountByProtocol(c context.Context, protocol string) (total int64, err error) {
|
||||||
err = r.DB.Find(&model.Asset{}).Where("protocol = ?", protocol).Count(&total).Error
|
err = r.GetDB(c).Find(&model.Asset{}).Where("protocol = ?", protocol).Count(&total).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) CountByUserId(userId string) (total int64, err error) {
|
func (r assetRepository) CountByUserId(c context.Context, userId string) (total int64, err error) {
|
||||||
db := r.DB.Joins("left join resource_sharers on assets.id = resource_sharers.resource_id")
|
db := r.GetDB(c).Joins("left join resource_sharers on assets.id = resource_sharers.resource_id")
|
||||||
|
|
||||||
db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", userId, userId)
|
db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", userId, userId)
|
||||||
|
|
||||||
// 查询用户所在用户组列表
|
// 查询用户所在用户组列表
|
||||||
userGroupIds, err := userGroupRepository.FindUserGroupIdsByUserId(userId)
|
userGroupIds, err := UserGroupMemberRepository.FindUserGroupIdsByUserId(c, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -311,13 +221,13 @@ func (r AssetRepository) CountByUserId(userId string) (total int64, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) CountByUserIdAndProtocol(userId, protocol string) (total int64, err error) {
|
func (r assetRepository) CountByUserIdAndProtocol(c context.Context, userId, protocol string) (total int64, err error) {
|
||||||
db := r.DB.Joins("left join resource_sharers on assets.id = resource_sharers.resource_id")
|
db := r.GetDB(c).Joins("left join resource_sharers on assets.id = resource_sharers.resource_id")
|
||||||
|
|
||||||
db = db.Where("( assets.owner = ? or resource_sharers.user_id = ? ) and assets.protocol = ?", userId, userId, protocol)
|
db = db.Where("( assets.owner = ? or resource_sharers.user_id = ? ) and assets.protocol = ?", userId, userId, protocol)
|
||||||
|
|
||||||
// 查询用户所在用户组列表
|
// 查询用户所在用户组列表
|
||||||
userGroupIds, err := userGroupRepository.FindUserGroupIdsByUserId(userId)
|
userGroupIds, err := UserGroupMemberRepository.FindUserGroupIdsByUserId(c, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -329,9 +239,9 @@ func (r AssetRepository) CountByUserIdAndProtocol(userId, protocol string) (tota
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) FindTags() (o []string, err error) {
|
func (r assetRepository) FindTags(c context.Context) (o []string, err error) {
|
||||||
var assets []model.Asset
|
var assets []model.Asset
|
||||||
err = r.DB.Not("tags = '' or tags = '-' ").Find(&assets).Error
|
err = r.GetDB(c).Not("tags = '' or tags = '-' ").Find(&assets).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -350,7 +260,7 @@ func (r AssetRepository) FindTags() (o []string, err error) {
|
|||||||
return utils.Distinct(o), nil
|
return utils.Distinct(o), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) UpdateAttributes(assetId, protocol string, m echo.Map) error {
|
func (r assetRepository) UpdateAttributes(c context.Context, assetId, protocol string, m echo.Map) error {
|
||||||
var data []model.AssetAttribute
|
var data []model.AssetAttribute
|
||||||
var parameterNames []string
|
var parameterNames []string
|
||||||
switch protocol {
|
switch protocol {
|
||||||
@ -373,13 +283,11 @@ func (r AssetRepository) UpdateAttributes(assetId, protocol string, m echo.Map)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.DB.Transaction(func(tx *gorm.DB) error {
|
err := r.GetDB(c).Where("asset_id = ?", assetId).Delete(&model.AssetAttribute{}).Error
|
||||||
err := tx.Where("asset_id = ?", assetId).Delete(&model.AssetAttribute{}).Error
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return tx.CreateInBatches(&data, len(data)).Error
|
return r.GetDB(c).CreateInBatches(&data, len(data)).Error
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func genAttribute(assetId, name string, m echo.Map) model.AssetAttribute {
|
func genAttribute(assetId, name string, m echo.Map) model.AssetAttribute {
|
||||||
@ -393,20 +301,20 @@ func genAttribute(assetId, name string, m echo.Map) model.AssetAttribute {
|
|||||||
return attribute
|
return attribute
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) FindAttrById(assetId string) (o []model.AssetAttribute, err error) {
|
func (r assetRepository) FindAttrById(c context.Context, assetId string) (o []model.AssetAttribute, err error) {
|
||||||
err = r.DB.Where("asset_id = ?", assetId).Find(&o).Error
|
err = r.GetDB(c).Where("asset_id = ?", assetId).Find(&o).Error
|
||||||
if o == nil {
|
if o == nil {
|
||||||
o = make([]model.AssetAttribute, 0)
|
o = make([]model.AssetAttribute, 0)
|
||||||
}
|
}
|
||||||
return o, err
|
return o, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetRepository) FindAssetAttrMapByAssetId(assetId string) (map[string]string, error) {
|
func (r assetRepository) FindAssetAttrMapByAssetId(c context.Context, assetId string) (map[string]string, error) {
|
||||||
asset, err := r.FindById(assetId)
|
asset, err := r.FindById(c, assetId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
attributes, err := r.FindAttrById(assetId)
|
attributes, err := r.FindAttrById(c, assetId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -424,7 +332,7 @@ func (r AssetRepository) FindAssetAttrMapByAssetId(assetId string) (map[string]s
|
|||||||
case "kubernetes":
|
case "kubernetes":
|
||||||
parameterNames = constant.KubernetesParameterNames
|
parameterNames = constant.KubernetesParameterNames
|
||||||
}
|
}
|
||||||
propertiesMap := propertyRepository.FindAllMap()
|
propertiesMap := PropertyRepository.FindAllMap(c)
|
||||||
var attributeMap = make(map[string]string)
|
var attributeMap = make(map[string]string)
|
||||||
for name := range propertiesMap {
|
for name := range propertiesMap {
|
||||||
if utils.Contains(parameterNames, name) {
|
if utils.Contains(parameterNames, name) {
|
||||||
|
26
server/repository/base.go
Normal file
26
server/repository/base.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/env"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type baseRepository struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *baseRepository) GetDB(c context.Context) *gorm.DB {
|
||||||
|
db := c.Value(constant.DB)
|
||||||
|
if db == nil {
|
||||||
|
return env.GetDB()
|
||||||
|
}
|
||||||
|
switch val := db.(type) {
|
||||||
|
case gorm.DB:
|
||||||
|
return &val
|
||||||
|
default:
|
||||||
|
return env.GetDB()
|
||||||
|
}
|
||||||
|
}
|
@ -1,24 +1,19 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandRepository struct {
|
type commandRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommandRepository(db *gorm.DB) *CommandRepository {
|
func (r commandRepository) Find(c context.Context, pageIndex, pageSize int, name, content, order, field string, account *model.User) (o []model.CommandForPage, total int64, err error) {
|
||||||
commandRepository = &CommandRepository{DB: db}
|
db := r.GetDB(c).Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on commands.owner = users.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id")
|
||||||
return commandRepository
|
dbCounter := r.GetDB(c).Table("commands").Select("DISTINCT commands.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id")
|
||||||
}
|
|
||||||
|
|
||||||
func (r CommandRepository) Find(pageIndex, pageSize int, name, content, order, field string, account model.User) (o []model.CommandForPage, total int64, err error) {
|
|
||||||
db := r.DB.Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on commands.owner = users.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id")
|
|
||||||
dbCounter := r.DB.Table("commands").Select("DISTINCT commands.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id")
|
|
||||||
|
|
||||||
if constant.TypeUser == account.Type {
|
if constant.TypeUser == account.Type {
|
||||||
owner := account.ID
|
owner := account.ID
|
||||||
@ -60,29 +55,29 @@ func (r CommandRepository) Find(pageIndex, pageSize int, name, content, order, f
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CommandRepository) Create(o *model.Command) (err error) {
|
func (r commandRepository) Create(c context.Context, o *model.Command) (err error) {
|
||||||
if err = r.DB.Create(o).Error; err != nil {
|
if err = r.GetDB(c).Create(o).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CommandRepository) FindById(id string) (o model.Command, err error) {
|
func (r commandRepository) FindById(c context.Context, id string) (o model.Command, err error) {
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CommandRepository) UpdateById(o *model.Command, id string) error {
|
func (r commandRepository) UpdateById(c context.Context, o *model.Command, id string) error {
|
||||||
o.ID = id
|
o.ID = id
|
||||||
return r.DB.Updates(o).Error
|
return r.GetDB(c).Updates(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CommandRepository) DeleteById(id string) error {
|
func (r commandRepository) DeleteById(c context.Context, id string) error {
|
||||||
return r.DB.Where("id = ?", id).Delete(&model.Command{}).Error
|
return r.GetDB(c).Where("id = ?", id).Delete(&model.Command{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CommandRepository) FindByUser(account model.User) (o []model.CommandForPage, err error) {
|
func (r commandRepository) FindByUser(c context.Context, account *model.User) (o []model.CommandForPage, err error) {
|
||||||
db := r.DB.Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on commands.owner = users.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id")
|
db := r.GetDB(c).Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on commands.owner = users.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id")
|
||||||
|
|
||||||
if constant.TypeUser == account.Type {
|
if constant.TypeUser == account.Type {
|
||||||
owner := account.ID
|
owner := account.ID
|
||||||
@ -95,7 +90,7 @@ func (r CommandRepository) FindByUser(account model.User) (o []model.CommandForP
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CommandRepository) FindAll() (o []model.Command, err error) {
|
func (r commandRepository) FindAll(c context.Context) (o []model.Command, err error) {
|
||||||
err = r.DB.Find(&o).Error
|
err = r.GetDB(c).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,25 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"context"
|
||||||
|
|
||||||
"next-terminal/server/config"
|
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
"next-terminal/server/utils"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CredentialRepository struct {
|
type credentialRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCredentialRepository(db *gorm.DB) *CredentialRepository {
|
func (r credentialRepository) FindByUser(c context.Context) (o []model.CredentialSimpleVo, err error) {
|
||||||
credentialRepository = &CredentialRepository{DB: db}
|
db := r.GetDB(c).Table("credentials").Select("DISTINCT credentials.id,credentials.name").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id")
|
||||||
return credentialRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r CredentialRepository) FindByUser(account model.User) (o []model.CredentialSimpleVo, err error) {
|
|
||||||
db := r.DB.Table("credentials").Select("DISTINCT credentials.id,credentials.name").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id")
|
|
||||||
if account.Type == constant.TypeUser {
|
|
||||||
db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", account.ID, account.ID)
|
|
||||||
}
|
|
||||||
err = db.Find(&o).Error
|
err = db.Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CredentialRepository) Find(pageIndex, pageSize int, name, order, field string, account model.User) (o []model.CredentialForPage, total int64, err error) {
|
func (r credentialRepository) Find(c context.Context, pageIndex, pageSize int, name, order, field string, account *model.User) (o []model.CredentialForPage, total int64, err error) {
|
||||||
db := r.DB.Table("credentials").Select("credentials.id,credentials.name,credentials.type,credentials.username,credentials.owner,credentials.created,users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on credentials.owner = users.id").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id").Group("credentials.id")
|
db := r.GetDB(c).Table("credentials").Select("credentials.id,credentials.name,credentials.type,credentials.username,credentials.owner,credentials.created,users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on credentials.owner = users.id").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id").Group("credentials.id")
|
||||||
dbCounter := r.DB.Table("credentials").Select("DISTINCT credentials.id").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id").Group("credentials.id")
|
dbCounter := r.GetDB(c).Table("credentials").Select("DISTINCT credentials.id").Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id").Group("credentials.id")
|
||||||
|
|
||||||
if constant.TypeUser == account.Type {
|
if constant.TypeUser == account.Type {
|
||||||
owner := account.ID
|
owner := account.ID
|
||||||
@ -68,127 +56,48 @@ func (r CredentialRepository) Find(pageIndex, pageSize int, name, order, field s
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CredentialRepository) Create(o *model.Credential) (err error) {
|
func (r credentialRepository) Create(c context.Context, o *model.Credential) (err error) {
|
||||||
if err := r.Encrypt(o, config.GlobalCfg.EncryptionPassword); err != nil {
|
return r.GetDB(c).Create(o).Error
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = r.DB.Create(o).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CredentialRepository) FindById(id string) (o model.Credential, err error) {
|
func (r credentialRepository) FindById(c context.Context, id string) (o model.Credential, err error) {
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CredentialRepository) Encrypt(item *model.Credential, password []byte) error {
|
func (r credentialRepository) UpdateById(c context.Context, o *model.Credential, id string) error {
|
||||||
if item.Password != "-" {
|
|
||||||
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Password), password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.Password = base64.StdEncoding.EncodeToString(encryptedCBC)
|
|
||||||
}
|
|
||||||
if item.PrivateKey != "-" {
|
|
||||||
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.PrivateKey), password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.PrivateKey = base64.StdEncoding.EncodeToString(encryptedCBC)
|
|
||||||
}
|
|
||||||
if item.Passphrase != "-" {
|
|
||||||
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Passphrase), password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.Passphrase = base64.StdEncoding.EncodeToString(encryptedCBC)
|
|
||||||
}
|
|
||||||
item.Encrypted = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r CredentialRepository) Decrypt(item *model.Credential, password []byte) error {
|
|
||||||
if item.Encrypted {
|
|
||||||
if item.Password != "" && item.Password != "-" {
|
|
||||||
origData, err := base64.StdEncoding.DecodeString(item.Password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.Password = string(decryptedCBC)
|
|
||||||
}
|
|
||||||
if item.PrivateKey != "" && item.PrivateKey != "-" {
|
|
||||||
origData, err := base64.StdEncoding.DecodeString(item.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.PrivateKey = string(decryptedCBC)
|
|
||||||
}
|
|
||||||
if item.Passphrase != "" && item.Passphrase != "-" {
|
|
||||||
origData, err := base64.StdEncoding.DecodeString(item.Passphrase)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.Passphrase = string(decryptedCBC)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r CredentialRepository) FindByIdAndDecrypt(id string) (o model.Credential, err error) {
|
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
|
||||||
if err == nil {
|
|
||||||
err = r.Decrypt(&o, config.GlobalCfg.EncryptionPassword)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r CredentialRepository) UpdateById(o *model.Credential, id string) error {
|
|
||||||
o.ID = id
|
o.ID = id
|
||||||
return r.DB.Updates(o).Error
|
return r.GetDB(c).Updates(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CredentialRepository) DeleteById(id string) error {
|
func (r credentialRepository) DeleteById(c context.Context, id string) error {
|
||||||
return r.DB.Where("id = ?", id).Delete(&model.Credential{}).Error
|
return r.GetDB(c).Where("id = ?", id).Delete(&model.Credential{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CredentialRepository) Count() (total int64, err error) {
|
func (r credentialRepository) Count(c context.Context) (total int64, err error) {
|
||||||
err = r.DB.Find(&model.Credential{}).Count(&total).Error
|
err = r.GetDB(c).Find(&model.Credential{}).Count(&total).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CredentialRepository) CountByUserId(userId string) (total int64, err error) {
|
//func (r credentialRepository) CountByUserId(c context.Context, userId string) (total int64, err error) {
|
||||||
db := r.DB.Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id")
|
// db := r.GetDB(c).Joins("left join resource_sharers on credentials.id = resource_sharers.resource_id")
|
||||||
|
//
|
||||||
|
// db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", userId, userId)
|
||||||
|
//
|
||||||
|
// // 查询用户所在用户组列表
|
||||||
|
// userGroupIds, err := userGroupRepository.FindUserGroupIdsByUserId(c, userId)
|
||||||
|
// if err != nil {
|
||||||
|
// return 0, err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if len(userGroupIds) > 0 {
|
||||||
|
// db = db.Or("resource_sharers.user_group_id in ?", userGroupIds)
|
||||||
|
// }
|
||||||
|
// err = db.Find(&model.Credential{}).Count(&total).Error
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
|
||||||
db = db.Where("credentials.owner = ? or resource_sharers.user_id = ?", userId, userId)
|
func (r credentialRepository) FindAll(c context.Context) (o []model.Credential, err error) {
|
||||||
|
err = r.GetDB(c).Find(&o).Error
|
||||||
// 查询用户所在用户组列表
|
|
||||||
userGroupIds, err := userGroupRepository.FindUserGroupIdsByUserId(userId)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(userGroupIds) > 0 {
|
|
||||||
db = db.Or("resource_sharers.user_group_id in ?", userGroupIds)
|
|
||||||
}
|
|
||||||
err = db.Find(&model.Credential{}).Count(&total).Error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r CredentialRepository) FindAll() (o []model.Credential, err error) {
|
|
||||||
err = r.DB.Find(&o).Error
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
package repository
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 定义了相关模型的持久化层,方便相互之间调用
|
|
||||||
*/
|
|
||||||
var (
|
|
||||||
userRepository *UserRepository
|
|
||||||
userGroupRepository *UserGroupRepository
|
|
||||||
resourceSharerRepository *ResourceSharerRepository
|
|
||||||
assetRepository *AssetRepository
|
|
||||||
credentialRepository *CredentialRepository
|
|
||||||
propertyRepository *PropertyRepository
|
|
||||||
commandRepository *CommandRepository
|
|
||||||
sessionRepository *SessionRepository
|
|
||||||
accessSecurityRepository *AccessSecurityRepository
|
|
||||||
accessGatewayRepository *AccessGatewayRepository
|
|
||||||
jobRepository *JobRepository
|
|
||||||
jobLogRepository *JobLogRepository
|
|
||||||
loginLogRepository *LoginLogRepository
|
|
||||||
storageRepository *StorageRepository
|
|
||||||
strategyRepository *StrategyRepository
|
|
||||||
)
|
|
75
server/repository/gateway.go
Normal file
75
server/repository/gateway.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"next-terminal/server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type gatewayRepository struct {
|
||||||
|
baseRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r gatewayRepository) Find(c context.Context, pageIndex, pageSize int, ip, name, order, field string) (o []model.AccessGatewayForPage, total int64, err error) {
|
||||||
|
t := model.AccessGateway{}
|
||||||
|
db := r.GetDB(c).Table(t.TableName())
|
||||||
|
dbCounter := r.GetDB(c).Table(t.TableName())
|
||||||
|
|
||||||
|
if len(ip) > 0 {
|
||||||
|
db = db.Where("ip like ?", "%"+ip+"%")
|
||||||
|
dbCounter = dbCounter.Where("ip like ?", "%"+ip+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(name) > 0 {
|
||||||
|
db = db.Where("name like ?", "%"+name+"%")
|
||||||
|
dbCounter = dbCounter.Where("name like ?", "%"+name+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbCounter.Count(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if order == "descend" {
|
||||||
|
order = "desc"
|
||||||
|
} else {
|
||||||
|
order = "asc"
|
||||||
|
}
|
||||||
|
|
||||||
|
if field == "ip" {
|
||||||
|
field = "ip"
|
||||||
|
} else if field == "name" {
|
||||||
|
field = "name"
|
||||||
|
} else {
|
||||||
|
field = "created"
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.Order(field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error
|
||||||
|
if o == nil {
|
||||||
|
o = make([]model.AccessGatewayForPage, 0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r gatewayRepository) Create(c context.Context, o *model.AccessGateway) error {
|
||||||
|
return r.GetDB(c).Create(o).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r gatewayRepository) UpdateById(c context.Context, o *model.AccessGateway, id string) error {
|
||||||
|
o.ID = id
|
||||||
|
return r.GetDB(c).Updates(o).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r gatewayRepository) DeleteById(c context.Context, id string) error {
|
||||||
|
return r.GetDB(c).Where("id = ?", id).Delete(model.AccessGateway{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r gatewayRepository) FindById(c context.Context, id string) (o model.AccessGateway, err error) {
|
||||||
|
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r gatewayRepository) FindAll(c context.Context) (o []model.AccessGateway, err error) {
|
||||||
|
err = r.GetDB(c).Find(&o).Error
|
||||||
|
return
|
||||||
|
}
|
@ -1,25 +1,20 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type JobRepository struct {
|
type jobRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJobRepository(db *gorm.DB) *JobRepository {
|
func (r jobRepository) Find(c context.Context, pageIndex, pageSize int, name, status, order, field string) (o []model.Job, total int64, err error) {
|
||||||
jobRepository = &JobRepository{DB: db}
|
|
||||||
return jobRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r JobRepository) Find(pageIndex, pageSize int, name, status, order, field string) (o []model.Job, total int64, err error) {
|
|
||||||
job := model.Job{}
|
job := model.Job{}
|
||||||
db := r.DB.Table(job.TableName())
|
db := r.GetDB(c).Table(job.TableName())
|
||||||
dbCounter := r.DB.Table(job.TableName())
|
dbCounter := r.GetDB(c).Table(job.TableName())
|
||||||
|
|
||||||
if len(name) > 0 {
|
if len(name) > 0 {
|
||||||
db = db.Where("name like ?", "%"+name+"%")
|
db = db.Where("name like ?", "%"+name+"%")
|
||||||
@ -57,45 +52,36 @@ func (r JobRepository) Find(pageIndex, pageSize int, name, status, order, field
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobRepository) FindByFunc(function string) (o []model.Job, err error) {
|
func (r jobRepository) FindByFunc(c context.Context, function string) (o []model.Job, err error) {
|
||||||
db := r.DB
|
db := r.GetDB(c)
|
||||||
err = db.Where("func = ?", function).Find(&o).Error
|
err = db.Where("func = ?", function).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobRepository) FindAll() (o []model.Job, err error) {
|
func (r jobRepository) FindAll(c context.Context) (o []model.Job, err error) {
|
||||||
db := r.DB
|
db := r.GetDB(c)
|
||||||
err = db.Find(&o).Error
|
err = db.Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobRepository) Create(o *model.Job) (err error) {
|
func (r jobRepository) Create(c context.Context, o *model.Job) (err error) {
|
||||||
return r.DB.Create(o).Error
|
return r.GetDB(c).Create(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobRepository) UpdateById(o *model.Job) (err error) {
|
func (r jobRepository) UpdateById(c context.Context, o *model.Job) (err error) {
|
||||||
return r.DB.Updates(o).Error
|
return r.GetDB(c).Updates(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobRepository) UpdateLastUpdatedById(id string) (err error) {
|
func (r jobRepository) UpdateLastUpdatedById(c context.Context, id string) (err error) {
|
||||||
err = r.DB.Updates(model.Job{ID: id, Updated: utils.NowJsonTime()}).Error
|
err = r.GetDB(c).Updates(model.Job{ID: id, Updated: utils.NowJsonTime()}).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobRepository) FindById(id string) (o model.Job, err error) {
|
func (r jobRepository) FindById(c context.Context, id string) (o model.Job, err error) {
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobRepository) DeleteJobById(id string) error {
|
func (r jobRepository) DeleteJobById(c context.Context, id string) error {
|
||||||
//job, err := r.FindById(id)
|
return r.GetDB(c).Where("id = ?", id).Delete(model.Job{}).Error
|
||||||
//if err != nil {
|
|
||||||
// return err
|
|
||||||
//}
|
|
||||||
//if job.Status == constant.JobStatusRunning {
|
|
||||||
// if err := r.ChangeStatusById(id, constant.JobStatusNotRunning); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
return r.DB.Where("id = ?", id).Delete(model.Job{}).Error
|
|
||||||
}
|
}
|
||||||
|
@ -1,45 +1,39 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type JobLogRepository struct {
|
type jobLogRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJobLogRepository(db *gorm.DB) *JobLogRepository {
|
func (r jobLogRepository) Create(c context.Context, o *model.JobLog) error {
|
||||||
jobLogRepository = &JobLogRepository{DB: db}
|
return r.GetDB(c).Create(o).Error
|
||||||
return jobLogRepository
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobLogRepository) Create(o *model.JobLog) error {
|
func (r jobLogRepository) FindByJobId(c context.Context, jobId string) (o []model.JobLog, err error) {
|
||||||
return r.DB.Create(o).Error
|
err = r.GetDB(c).Where("job_id = ?", jobId).Order("timestamp asc").Find(&o).Error
|
||||||
}
|
|
||||||
|
|
||||||
func (r JobLogRepository) FindByJobId(jobId string) (o []model.JobLog, err error) {
|
|
||||||
err = r.DB.Where("job_id = ?", jobId).Order("timestamp asc").Find(&o).Error
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobLogRepository) FindOutTimeLog(dayLimit int) (o []model.JobLog, err error) {
|
func (r jobLogRepository) FindOutTimeLog(c context.Context, dayLimit int) (o []model.JobLog, err error) {
|
||||||
limitTime := time.Now().Add(time.Duration(-dayLimit*24) * time.Hour)
|
limitTime := time.Now().Add(time.Duration(-dayLimit*24) * time.Hour)
|
||||||
err = r.DB.Where("timestamp < ?", limitTime).Find(&o).Error
|
err = r.GetDB(c).Where("timestamp < ?", limitTime).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobLogRepository) DeleteByJobId(jobId string) error {
|
func (r jobLogRepository) DeleteByJobId(c context.Context, jobId string) error {
|
||||||
return r.DB.Where("job_id = ?", jobId).Delete(model.JobLog{}).Error
|
return r.GetDB(c).Where("job_id = ?", jobId).Delete(model.JobLog{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobLogRepository) DeleteByIdIn(ids []string) error {
|
func (r jobLogRepository) DeleteByIdIn(c context.Context, ids []string) error {
|
||||||
return r.DB.Where("id in ?", ids).Delete(&model.JobLog{}).Error
|
return r.GetDB(c).Where("id in ?", ids).Delete(&model.JobLog{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobLogRepository) DeleteById(id string) error {
|
func (r jobLogRepository) DeleteById(c context.Context, id string) error {
|
||||||
return r.DB.Where("id = ?", id).Delete(&model.JobLog{}).Error
|
return r.GetDB(c).Where("id = ?", id).Delete(&model.JobLog{}).Error
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,20 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginLogRepository struct {
|
type loginLogRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLoginLogRepository(db *gorm.DB) *LoginLogRepository {
|
func (r loginLogRepository) Find(c context.Context, pageIndex, pageSize int, username, clientIp, state string) (o []model.LoginLog, total int64, err error) {
|
||||||
loginLogRepository = &LoginLogRepository{DB: db}
|
|
||||||
return loginLogRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r LoginLogRepository) Find(pageIndex, pageSize int, username, clientIp, state string) (o []model.LoginLog, total int64, err error) {
|
|
||||||
m := model.LoginLog{}
|
m := model.LoginLog{}
|
||||||
db := r.DB.Table(m.TableName())
|
db := r.GetDB(c).Table(m.TableName())
|
||||||
dbCounter := r.DB.Table(m.TableName())
|
dbCounter := r.GetDB(c).Table(m.TableName())
|
||||||
|
|
||||||
if username != "" {
|
if username != "" {
|
||||||
db = db.Where("username like ?", "%"+username+"%")
|
db = db.Where("username like ?", "%"+username+"%")
|
||||||
@ -49,44 +43,44 @@ func (r LoginLogRepository) Find(pageIndex, pageSize int, username, clientIp, st
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r LoginLogRepository) FindAliveLoginLogs() (o []model.LoginLog, err error) {
|
func (r loginLogRepository) FindAliveLoginLogs(c context.Context) (o []model.LoginLog, err error) {
|
||||||
err = r.DB.Where("state = '1' and logout_time is null").Find(&o).Error
|
err = r.GetDB(c).Where("state = '1' and logout_time is null").Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r LoginLogRepository) FindAllLoginLogs() (o []model.LoginLog, err error) {
|
func (r loginLogRepository) FindAllLoginLogs(c context.Context) (o []model.LoginLog, err error) {
|
||||||
err = r.DB.Find(&o).Error
|
err = r.GetDB(c).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r LoginLogRepository) FindAliveLoginLogsByUsername(username string) (o []model.LoginLog, err error) {
|
func (r loginLogRepository) FindAliveLoginLogsByUsername(c context.Context, username string) (o []model.LoginLog, err error) {
|
||||||
err = r.DB.Where("state = '1' and logout_time is null and username = ?", username).Find(&o).Error
|
err = r.GetDB(c).Where("state = '1' and logout_time is null and username = ?", username).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r LoginLogRepository) FindOutTimeLog(dayLimit int) (o []model.LoginLog, err error) {
|
func (r loginLogRepository) FindOutTimeLog(c context.Context, dayLimit int) (o []model.LoginLog, err error) {
|
||||||
limitTime := time.Now().Add(time.Duration(-dayLimit*24) * time.Hour)
|
limitTime := time.Now().Add(time.Duration(-dayLimit*24) * time.Hour)
|
||||||
err = r.DB.Where("(state = '0' and login_time < ?) or (state = '1' and logout_time < ?) or (state is null and logout_time < ?)", limitTime, limitTime, limitTime).Find(&o).Error
|
err = r.GetDB(c).Where("(state = '0' and login_time < ?) or (state = '1' and logout_time < ?) or (state is null and logout_time < ?)", limitTime, limitTime, limitTime).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r LoginLogRepository) Create(o *model.LoginLog) (err error) {
|
func (r loginLogRepository) Create(c context.Context, o *model.LoginLog) (err error) {
|
||||||
return r.DB.Create(o).Error
|
return r.GetDB(c).Create(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r LoginLogRepository) DeleteByIdIn(ids []string) (err error) {
|
func (r loginLogRepository) DeleteByIdIn(c context.Context, ids []string) (err error) {
|
||||||
return r.DB.Where("id in ?", ids).Delete(&model.LoginLog{}).Error
|
return r.GetDB(c).Where("id in ?", ids).Delete(&model.LoginLog{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r LoginLogRepository) DeleteById(id string) (err error) {
|
func (r loginLogRepository) DeleteById(c context.Context, id string) (err error) {
|
||||||
return r.DB.Where("id = ?", id).Delete(&model.LoginLog{}).Error
|
return r.GetDB(c).Where("id = ?", id).Delete(&model.LoginLog{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r LoginLogRepository) FindById(id string) (o model.LoginLog, err error) {
|
func (r loginLogRepository) FindById(c context.Context, id string) (o model.LoginLog, err error) {
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r LoginLogRepository) Update(o *model.LoginLog) error {
|
func (r loginLogRepository) Update(c context.Context, o *model.LoginLog) error {
|
||||||
return r.DB.Updates(o).Error
|
return r.GetDB(c).Updates(o).Error
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,43 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"next-terminal/server/model"
|
"context"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"next-terminal/server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PropertyRepository struct {
|
type propertyRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPropertyRepository(db *gorm.DB) *PropertyRepository {
|
func (r propertyRepository) FindAll(c context.Context) (o []model.Property) {
|
||||||
propertyRepository = &PropertyRepository{DB: db}
|
if r.GetDB(c).Find(&o).Error != nil {
|
||||||
return propertyRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r PropertyRepository) FindAll() (o []model.Property) {
|
|
||||||
if r.DB.Find(&o).Error != nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r PropertyRepository) Create(o *model.Property) (err error) {
|
func (r propertyRepository) Create(c context.Context, o *model.Property) (err error) {
|
||||||
err = r.DB.Create(o).Error
|
err = r.GetDB(c).Create(o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r PropertyRepository) UpdateByName(o *model.Property, name string) error {
|
func (r propertyRepository) UpdateByName(c context.Context, o *model.Property, name string) error {
|
||||||
o.Name = name
|
o.Name = name
|
||||||
return r.DB.Updates(o).Error
|
return r.GetDB(c).Updates(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r PropertyRepository) DeleteByName(name string) error {
|
func (r propertyRepository) DeleteByName(c context.Context, name string) error {
|
||||||
return r.DB.Where("name = ?", name).Delete(model.Property{}).Error
|
return r.GetDB(c).Where("name = ?", name).Delete(model.Property{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r PropertyRepository) FindByName(name string) (o model.Property, err error) {
|
func (r propertyRepository) FindByName(c context.Context, name string) (o model.Property, err error) {
|
||||||
err = r.DB.Where("name = ?", name).First(&o).Error
|
err = r.GetDB(c).Where("name = ?", name).First(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r PropertyRepository) FindAllMap() map[string]string {
|
func (r propertyRepository) FindAllMap(c context.Context) map[string]string {
|
||||||
properties := r.FindAll()
|
properties := r.FindAll(c)
|
||||||
propertyMap := make(map[string]string)
|
propertyMap := make(map[string]string)
|
||||||
for i := range properties {
|
for i := range properties {
|
||||||
propertyMap[properties[i].Name] = properties[i].Value
|
propertyMap[properties[i].Name] = properties[i].Value
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
@ -9,17 +11,12 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResourceSharerRepository struct {
|
type resourceSharerRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewResourceSharerRepository(db *gorm.DB) *ResourceSharerRepository {
|
func (r *resourceSharerRepository) OverwriteUserIdsByResourceId(c context.Context, resourceId, resourceType string, userIds []string) (err error) {
|
||||||
resourceSharerRepository = &ResourceSharerRepository{DB: db}
|
db := r.GetDB(c).Begin()
|
||||||
return resourceSharerRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ResourceSharerRepository) OverwriteUserIdsByResourceId(resourceId, resourceType string, userIds []string) (err error) {
|
|
||||||
db := r.DB.Begin()
|
|
||||||
|
|
||||||
var owner string
|
var owner string
|
||||||
// 检查资产是否存在
|
// 检查资产是否存在
|
||||||
@ -71,8 +68,8 @@ func (r *ResourceSharerRepository) OverwriteUserIdsByResourceId(resourceId, reso
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResourceSharerRepository) DeleteByUserIdAndResourceTypeAndResourceIdIn(userGroupId, userId, resourceType string, resourceIds []string) error {
|
func (r *resourceSharerRepository) DeleteByUserIdAndResourceTypeAndResourceIdIn(c context.Context, userGroupId, userId, resourceType string, resourceIds []string) error {
|
||||||
db := r.DB
|
db := r.GetDB(c)
|
||||||
if userGroupId != "" {
|
if userGroupId != "" {
|
||||||
db = db.Where("user_group_id = ?", userGroupId)
|
db = db.Where("user_group_id = ?", userGroupId)
|
||||||
}
|
}
|
||||||
@ -92,12 +89,20 @@ func (r *ResourceSharerRepository) DeleteByUserIdAndResourceTypeAndResourceIdIn(
|
|||||||
return db.Delete(&model.ResourceSharer{}).Error
|
return db.Delete(&model.ResourceSharer{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResourceSharerRepository) DeleteResourceSharerByResourceId(resourceId string) error {
|
func (r *resourceSharerRepository) DeleteByResourceId(c context.Context, resourceId string) error {
|
||||||
return r.DB.Where("resource_id = ?", resourceId).Delete(&model.ResourceSharer{}).Error
|
return r.GetDB(c).Where("resource_id = ?", resourceId).Delete(&model.ResourceSharer{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResourceSharerRepository) AddSharerResources(userGroupId, userId, strategyId, resourceType string, resourceIds []string) error {
|
func (r *resourceSharerRepository) DeleteByUserId(c context.Context, userId string) error {
|
||||||
return r.DB.Transaction(func(tx *gorm.DB) (err error) {
|
return r.GetDB(c).Where("user_id = ?", userId).Delete(&model.ResourceSharer{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *resourceSharerRepository) DeleteByUserGroupId(c context.Context, userGroupId string) error {
|
||||||
|
return r.GetDB(c).Where("user_group_id = ?", userGroupId).Delete(&model.ResourceSharer{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *resourceSharerRepository) AddSharerResources(userGroupId, userId, strategyId, resourceType string, resourceIds []string) error {
|
||||||
|
return r.GetDB(context.TODO()).Transaction(func(tx *gorm.DB) (err error) {
|
||||||
|
|
||||||
for i := range resourceIds {
|
for i := range resourceIds {
|
||||||
resourceId := resourceIds[i]
|
resourceId := resourceIds[i]
|
||||||
@ -149,22 +154,22 @@ func (r *ResourceSharerRepository) AddSharerResources(userGroupId, userId, strat
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResourceSharerRepository) FindAssetIdsByUserId(userId string) (assetIds []string, err error) {
|
func (r *resourceSharerRepository) FindAssetIdsByUserId(c context.Context, userId string) (assetIds []string, err error) {
|
||||||
// 查询当前用户创建的资产
|
// 查询当前用户创建的资产
|
||||||
var ownerAssetIds, sharerAssetIds []string
|
var ownerAssetIds, sharerAssetIds []string
|
||||||
asset := model.Asset{}
|
asset := model.Asset{}
|
||||||
err = r.DB.Table(asset.TableName()).Select("id").Where("owner = ?", userId).Find(&ownerAssetIds).Error
|
err = r.GetDB(c).Table(asset.TableName()).Select("id").Where("owner = ?", userId).Find(&ownerAssetIds).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询其他用户授权给该用户的资产
|
// 查询其他用户授权给该用户的资产
|
||||||
groupIds, err := userGroupRepository.FindUserGroupIdsByUserId(userId)
|
groupIds, err := UserGroupMemberRepository.FindUserGroupIdsByUserId(c, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
db := r.DB.Table("resource_sharers").Select("resource_id").Where("user_id = ?", userId)
|
db := r.GetDB(c).Table("resource_sharers").Select("resource_id").Where("user_id = ?", userId)
|
||||||
if len(groupIds) > 0 {
|
if len(groupIds) > 0 {
|
||||||
db = db.Or("user_group_id in ?", groupIds)
|
db = db.Or("user_group_id in ?", groupIds)
|
||||||
}
|
}
|
||||||
@ -187,13 +192,13 @@ func (r *ResourceSharerRepository) FindAssetIdsByUserId(userId string) (assetIds
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResourceSharerRepository) FindByResourceIdAndUserId(assetId, userId string) (resourceSharers []model.ResourceSharer, err error) {
|
func (r *resourceSharerRepository) FindByResourceIdAndUserId(c context.Context, assetId, userId string) (resourceSharers []model.ResourceSharer, err error) {
|
||||||
// 查询其他用户授权给该用户的资产
|
// 查询其他用户授权给该用户的资产
|
||||||
groupIds, err := userGroupRepository.FindUserGroupIdsByUserId(userId)
|
groupIds, err := UserGroupMemberRepository.FindUserGroupIdsByUserId(c, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
db := r.DB.Where("( resource_id = ? and user_id = ? )", assetId, userId)
|
db := r.GetDB(c).Where("( resource_id = ? and user_id = ? )", assetId, userId)
|
||||||
if len(groupIds) > 0 {
|
if len(groupIds) > 0 {
|
||||||
db = db.Or("user_group_id in ?", groupIds)
|
db = db.Or("user_group_id in ?", groupIds)
|
||||||
}
|
}
|
||||||
@ -201,8 +206,8 @@ func (r *ResourceSharerRepository) FindByResourceIdAndUserId(assetId, userId str
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResourceSharerRepository) Find(resourceId, resourceType, userId, userGroupId string) (resourceSharers []model.ResourceSharer, err error) {
|
func (r *resourceSharerRepository) Find(c context.Context, resourceId, resourceType, userId, userGroupId string) (resourceSharers []model.ResourceSharer, err error) {
|
||||||
db := r.DB
|
db := r.GetDB(c)
|
||||||
if resourceId != "" {
|
if resourceId != "" {
|
||||||
db = db.Where("resource_id = ?")
|
db = db.Where("resource_id = ?")
|
||||||
}
|
}
|
||||||
@ -219,7 +224,7 @@ func (r *ResourceSharerRepository) Find(resourceId, resourceType, userId, userGr
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResourceSharerRepository) FindAll() (o []model.ResourceSharer, err error) {
|
func (r *resourceSharerRepository) FindAll(c context.Context) (o []model.ResourceSharer, err error) {
|
||||||
err = r.DB.Find(&o).Error
|
err = r.GetDB(c).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
75
server/repository/security.go
Normal file
75
server/repository/security.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"next-terminal/server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type securityRepository struct {
|
||||||
|
baseRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r securityRepository) FindAll(c context.Context) (o []model.AccessSecurity, err error) {
|
||||||
|
err = r.GetDB(c).Order("priority asc").Find(&o).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r securityRepository) Find(c context.Context, pageIndex, pageSize int, ip, rule, order, field string) (o []model.AccessSecurity, total int64, err error) {
|
||||||
|
t := model.AccessSecurity{}
|
||||||
|
db := r.GetDB(c).Table(t.TableName())
|
||||||
|
dbCounter := r.GetDB(c).Table(t.TableName())
|
||||||
|
|
||||||
|
if len(ip) > 0 {
|
||||||
|
db = db.Where("ip like ?", "%"+ip+"%")
|
||||||
|
dbCounter = dbCounter.Where("ip like ?", "%"+ip+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rule) > 0 {
|
||||||
|
db = db.Where("rule = ?", rule)
|
||||||
|
dbCounter = dbCounter.Where("rule = ?", rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbCounter.Count(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if order == "descend" {
|
||||||
|
order = "desc"
|
||||||
|
} else {
|
||||||
|
order = "asc"
|
||||||
|
}
|
||||||
|
|
||||||
|
if field == "ip" {
|
||||||
|
field = "ip"
|
||||||
|
} else if field == "rule" {
|
||||||
|
field = "rule"
|
||||||
|
} else {
|
||||||
|
field = "priority"
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.Order(field + " " + order).Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error
|
||||||
|
if o == nil {
|
||||||
|
o = make([]model.AccessSecurity, 0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r securityRepository) Create(c context.Context, o *model.AccessSecurity) error {
|
||||||
|
return r.GetDB(c).Create(o).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r securityRepository) UpdateById(c context.Context, o *model.AccessSecurity, id string) error {
|
||||||
|
o.ID = id
|
||||||
|
return r.GetDB(c).Updates(o).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r securityRepository) DeleteById(c context.Context, id string) error {
|
||||||
|
return r.GetDB(c).Where("id = ?", id).Delete(model.AccessSecurity{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r securityRepository) FindById(c context.Context, id string) (o *model.AccessSecurity, err error) {
|
||||||
|
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
|
||||||
|
return
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"time"
|
"time"
|
||||||
@ -9,23 +9,15 @@ import (
|
|||||||
"next-terminal/server/config"
|
"next-terminal/server/config"
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
"next-terminal/server/utils"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SessionRepository struct {
|
type sessionRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSessionRepository(db *gorm.DB) *SessionRepository {
|
func (r sessionRepository) Find(c context.Context, pageIndex, pageSize int, status, userId, clientIp, assetId, protocol, reviewed string) (results []model.SessionForPage, total int64, err error) {
|
||||||
sessionRepository = &SessionRepository{DB: db}
|
|
||||||
return sessionRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r SessionRepository) Find(pageIndex, pageSize int, status, userId, clientIp, assetId, protocol, reviewed string) (results []model.SessionForPage, total int64, err error) {
|
db := r.GetDB(c)
|
||||||
|
|
||||||
db := r.DB
|
|
||||||
var params []interface{}
|
var params []interface{}
|
||||||
|
|
||||||
params = append(params, status)
|
params = append(params, status)
|
||||||
@ -77,152 +69,102 @@ func (r SessionRepository) Find(pageIndex, pageSize int, status, userId, clientI
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) FindByStatus(status string) (o []model.Session, err error) {
|
func (r sessionRepository) FindByStatus(c context.Context, status string) (o []model.Session, err error) {
|
||||||
err = r.DB.Where("status = ?", status).Find(&o).Error
|
err = r.GetDB(c).Where("status = ?", status).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) FindByStatusIn(statuses []string) (o []model.Session, err error) {
|
func (r sessionRepository) FindByStatusIn(c context.Context, statuses []string) (o []model.Session, err error) {
|
||||||
err = r.DB.Where("status in ?", statuses).Find(&o).Error
|
err = r.GetDB(c).Where("status in ?", statuses).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) FindOutTimeSessions(dayLimit int) (o []model.Session, err error) {
|
func (r sessionRepository) FindOutTimeSessions(c context.Context, dayLimit int) (o []model.Session, err error) {
|
||||||
limitTime := time.Now().Add(time.Duration(-dayLimit*24) * time.Hour)
|
limitTime := time.Now().Add(time.Duration(-dayLimit*24) * time.Hour)
|
||||||
err = r.DB.Where("status = ? and connected_time < ?", constant.Disconnected, limitTime).Find(&o).Error
|
err = r.GetDB(c).Where("status = ? and connected_time < ?", constant.Disconnected, limitTime).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) Create(o *model.Session) (err error) {
|
func (r sessionRepository) Create(c context.Context, o *model.Session) (err error) {
|
||||||
err = r.DB.Create(o).Error
|
err = r.GetDB(c).Create(o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) FindById(id string) (o model.Session, err error) {
|
func (r sessionRepository) FindById(c context.Context, id string) (o model.Session, err error) {
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) FindByIdAndDecrypt(id string) (o model.Session, err error) {
|
func (r sessionRepository) FindByConnectionId(c context.Context, connectionId string) (o model.Session, err error) {
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
err = r.GetDB(c).Where("connection_id = ?", connectionId).First(&o).Error
|
||||||
if err == nil {
|
|
||||||
err = r.Decrypt(&o)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) Decrypt(item *model.Session) error {
|
func (r sessionRepository) UpdateById(c context.Context, o *model.Session, id string) error {
|
||||||
if item.Password != "" && item.Password != "-" {
|
|
||||||
origData, err := base64.StdEncoding.DecodeString(item.Password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
decryptedCBC, err := utils.AesDecryptCBC(origData, config.GlobalCfg.EncryptionPassword)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.Password = string(decryptedCBC)
|
|
||||||
}
|
|
||||||
if item.PrivateKey != "" && item.PrivateKey != "-" {
|
|
||||||
origData, err := base64.StdEncoding.DecodeString(item.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
decryptedCBC, err := utils.AesDecryptCBC(origData, config.GlobalCfg.EncryptionPassword)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.PrivateKey = string(decryptedCBC)
|
|
||||||
}
|
|
||||||
if item.Passphrase != "" && item.Passphrase != "-" {
|
|
||||||
origData, err := base64.StdEncoding.DecodeString(item.Passphrase)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
decryptedCBC, err := utils.AesDecryptCBC(origData, config.GlobalCfg.EncryptionPassword)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item.Passphrase = string(decryptedCBC)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r SessionRepository) FindByConnectionId(connectionId string) (o model.Session, err error) {
|
|
||||||
err = r.DB.Where("connection_id = ?", connectionId).First(&o).Error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r SessionRepository) UpdateById(o *model.Session, id string) error {
|
|
||||||
o.ID = id
|
o.ID = id
|
||||||
return r.DB.Updates(o).Error
|
return r.GetDB(c).Updates(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) UpdateWindowSizeById(width, height int, id string) error {
|
func (r sessionRepository) UpdateWindowSizeById(c context.Context, width, height int, id string) error {
|
||||||
session := model.Session{}
|
session := model.Session{}
|
||||||
session.Width = width
|
session.Width = width
|
||||||
session.Height = height
|
session.Height = height
|
||||||
|
|
||||||
return r.UpdateById(&session, id)
|
return r.UpdateById(c, &session, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) DeleteById(id string) error {
|
func (r sessionRepository) DeleteById(c context.Context, id string) error {
|
||||||
return r.DB.Where("id = ?", id).Delete(&model.Session{}).Error
|
return r.GetDB(c).Where("id = ?", id).Delete(&model.Session{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) DeleteByIds(sessionIds []string) error {
|
func (r sessionRepository) DeleteByIds(c context.Context, sessionIds []string) error {
|
||||||
recordingPath := config.GlobalCfg.Guacd.Recording
|
recordingPath := config.GlobalCfg.Guacd.Recording
|
||||||
for i := range sessionIds {
|
for i := range sessionIds {
|
||||||
if err := os.RemoveAll(path.Join(recordingPath, sessionIds[i])); err != nil {
|
if err := os.RemoveAll(path.Join(recordingPath, sessionIds[i])); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := r.DeleteById(sessionIds[i]); err != nil {
|
if err := r.DeleteById(c, sessionIds[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) DeleteByStatus(status string) error {
|
func (r sessionRepository) DeleteByStatus(c context.Context, status string) error {
|
||||||
return r.DB.Where("status = ?", status).Delete(&model.Session{}).Error
|
return r.GetDB(c).Where("status = ?", status).Delete(&model.Session{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) CountOnlineSession() (total int64, err error) {
|
func (r sessionRepository) CountOnlineSession(c context.Context) (total int64, err error) {
|
||||||
err = r.DB.Where("status = ?", constant.Connected).Find(&model.Session{}).Count(&total).Error
|
err = r.GetDB(c).Where("status = ?", constant.Connected).Find(&model.Session{}).Count(&total).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) EmptyPassword() error {
|
func (r sessionRepository) EmptyPassword(c context.Context) error {
|
||||||
sql := "update sessions set password = '-',private_key = '-', passphrase = '-' where 1=1"
|
sql := "update sessions set password = '-',private_key = '-', passphrase = '-' where 1=1"
|
||||||
return r.DB.Exec(sql).Error
|
return r.GetDB(c).Exec(sql).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) CountByStatus(status string) (total int64, err error) {
|
func (r sessionRepository) CountByStatus(c context.Context, status string) (total int64, err error) {
|
||||||
err = r.DB.Find(&model.Session{}).Where("status = ?", status).Count(&total).Error
|
err = r.GetDB(c).Find(&model.Session{}).Where("status = ?", status).Count(&total).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) OverviewAccess(account model.User) (o []model.SessionForAccess, err error) {
|
func (r sessionRepository) OverviewAccess(c context.Context) (o []model.SessionForAccess, err error) {
|
||||||
db := r.DB
|
db := r.GetDB(c)
|
||||||
if constant.TypeUser == account.Type {
|
|
||||||
sql := "SELECT s.asset_id, s.ip, s.port, s.protocol, s.username, count(s.asset_id) AS access_count FROM sessions AS s where s.creator = ? GROUP BY s.asset_id, s.ip, s.port, s.protocol, s.username ORDER BY access_count DESC limit 10"
|
|
||||||
err = db.Raw(sql, []string{account.ID}).Scan(&o).Error
|
|
||||||
} else {
|
|
||||||
sql := "SELECT s.asset_id, s.ip, s.port, s.protocol, s.username, count(s.asset_id) AS access_count FROM sessions AS s GROUP BY s.asset_id, s.ip, s.port, s.protocol, s.username ORDER BY access_count DESC limit 10"
|
sql := "SELECT s.asset_id, s.ip, s.port, s.protocol, s.username, count(s.asset_id) AS access_count FROM sessions AS s GROUP BY s.asset_id, s.ip, s.port, s.protocol, s.username ORDER BY access_count DESC limit 10"
|
||||||
err = db.Raw(sql).Scan(&o).Error
|
err = db.Raw(sql).Scan(&o).Error
|
||||||
}
|
|
||||||
if o == nil {
|
if o == nil {
|
||||||
o = make([]model.SessionForAccess, 0)
|
o = make([]model.SessionForAccess, 0)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) UpdateReadByIds(reviewed bool, ids []string) error {
|
func (r sessionRepository) UpdateReadByIds(c context.Context, reviewed bool, ids []string) error {
|
||||||
sql := "update sessions set reviewed = ? where id in ?"
|
sql := "update sessions set reviewed = ? where id in ?"
|
||||||
return r.DB.Exec(sql, reviewed, ids).Error
|
return r.GetDB(c).Exec(sql, reviewed, ids).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionRepository) FindAllUnReviewed() (o []model.Session, err error) {
|
func (r sessionRepository) FindAllUnReviewed(c context.Context) (o []model.Session, err error) {
|
||||||
err = r.DB.Where("reviewed = false or reviewed is null").Find(&o).Error
|
err = r.GetDB(c).Where("reviewed = false or reviewed is null").Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,19 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"next-terminal/server/model"
|
"context"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"next-terminal/server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StorageRepository struct {
|
type storageRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStorageRepository(db *gorm.DB) *StorageRepository {
|
func (r storageRepository) Find(c context.Context, pageIndex, pageSize int, name, order, field string) (o []model.StorageForPage, total int64, err error) {
|
||||||
storageRepository = &StorageRepository{DB: db}
|
|
||||||
return storageRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r StorageRepository) Find(pageIndex, pageSize int, name, order, field string) (o []model.StorageForPage, total int64, err error) {
|
|
||||||
m := model.Storage{}
|
m := model.Storage{}
|
||||||
db := r.DB.Table(m.TableName()).Select("storages.id,storages.name,storages.is_share,storages.limit_size,storages.is_default,storages.owner,storages.created, users.nickname as owner_name").Joins("left join users on storages.owner = users.id")
|
db := r.GetDB(c).Table(m.TableName()).Select("storages.id,storages.name,storages.is_share,storages.limit_size,storages.is_default,storages.owner,storages.created, users.nickname as owner_name").Joins("left join users on storages.owner = users.id")
|
||||||
dbCounter := r.DB.Table(m.TableName())
|
dbCounter := r.GetDB(c).Table(m.TableName())
|
||||||
|
|
||||||
if len(name) > 0 {
|
if len(name) > 0 {
|
||||||
db = db.Where("name like ?", "%"+name+"%")
|
db = db.Where("name like ?", "%"+name+"%")
|
||||||
@ -49,37 +44,37 @@ func (r StorageRepository) Find(pageIndex, pageSize int, name, order, field stri
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StorageRepository) FindShares() (o []model.Storage, err error) {
|
func (r storageRepository) FindShares(c context.Context) (o []model.Storage, err error) {
|
||||||
m := model.Storage{}
|
m := model.Storage{}
|
||||||
db := r.DB.Table(m.TableName()).Where("is_share = 1")
|
db := r.GetDB(c).Table(m.TableName()).Where("is_share = 1")
|
||||||
err = db.Find(&o).Error
|
err = db.Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StorageRepository) DeleteById(id string) error {
|
func (r storageRepository) DeleteById(c context.Context, id string) error {
|
||||||
return r.DB.Where("id = ?", id).Delete(model.Storage{}).Error
|
return r.GetDB(c).Where("id = ?", id).Delete(model.Storage{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StorageRepository) Create(m *model.Storage) error {
|
func (r storageRepository) Create(c context.Context, m *model.Storage) error {
|
||||||
return r.DB.Create(m).Error
|
return r.GetDB(c).Create(m).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StorageRepository) UpdateById(o *model.Storage, id string) error {
|
func (r storageRepository) UpdateById(c context.Context, o *model.Storage, id string) error {
|
||||||
o.ID = id
|
o.ID = id
|
||||||
return r.DB.Updates(o).Error
|
return r.GetDB(c).Updates(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StorageRepository) FindByOwnerIdAndDefault(owner string, isDefault bool) (m model.Storage, err error) {
|
func (r storageRepository) FindByOwnerIdAndDefault(c context.Context, owner string, isDefault bool) (m model.Storage, err error) {
|
||||||
err = r.DB.Where("owner = ? and is_default = ?", owner, isDefault).First(&m).Error
|
err = r.GetDB(c).Where("owner = ? and is_default = ?", owner, isDefault).First(&m).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StorageRepository) FindById(id string) (m model.Storage, err error) {
|
func (r storageRepository) FindById(c context.Context, id string) (m model.Storage, err error) {
|
||||||
err = r.DB.Where("id = ?", id).First(&m).Error
|
err = r.GetDB(c).Where("id = ?", id).First(&m).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StorageRepository) FindAll() (o []model.Storage, err error) {
|
func (r storageRepository) FindAll(c context.Context) (o []model.Storage, err error) {
|
||||||
err = r.DB.Find(&o).Error
|
err = r.GetDB(c).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,24 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"next-terminal/server/model"
|
"context"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"next-terminal/server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StrategyRepository struct {
|
type strategyRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStrategyRepository(db *gorm.DB) *StrategyRepository {
|
func (r strategyRepository) FindAll(c context.Context) (o []model.Strategy, err error) {
|
||||||
strategyRepository = &StrategyRepository{DB: db}
|
err = r.GetDB(c).Order("name desc").Find(&o).Error
|
||||||
return strategyRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r StrategyRepository) FindAll() (o []model.Strategy, err error) {
|
|
||||||
err = r.DB.Order("name desc").Find(&o).Error
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StrategyRepository) Find(pageIndex, pageSize int, name, order, field string) (o []model.Strategy, total int64, err error) {
|
func (r strategyRepository) Find(c context.Context, pageIndex, pageSize int, name, order, field string) (o []model.Strategy, total int64, err error) {
|
||||||
m := model.Strategy{}
|
m := model.Strategy{}
|
||||||
db := r.DB.Table(m.TableName())
|
db := r.GetDB(c).Table(m.TableName())
|
||||||
dbCounter := r.DB.Table(m.TableName())
|
dbCounter := r.GetDB(c).Table(m.TableName())
|
||||||
|
|
||||||
if len(name) > 0 {
|
if len(name) > 0 {
|
||||||
db = db.Where("name like ?", "%"+name+"%")
|
db = db.Where("name like ?", "%"+name+"%")
|
||||||
@ -54,20 +49,20 @@ func (r StrategyRepository) Find(pageIndex, pageSize int, name, order, field str
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StrategyRepository) DeleteById(id string) error {
|
func (r strategyRepository) DeleteById(c context.Context, id string) error {
|
||||||
return r.DB.Where("id = ?", id).Delete(model.Strategy{}).Error
|
return r.GetDB(c).Where("id = ?", id).Delete(model.Strategy{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StrategyRepository) Create(m *model.Strategy) error {
|
func (r strategyRepository) Create(c context.Context, m *model.Strategy) error {
|
||||||
return r.DB.Create(m).Error
|
return r.GetDB(c).Create(m).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StrategyRepository) UpdateById(o *model.Strategy, id string) error {
|
func (r strategyRepository) UpdateById(c context.Context, o *model.Strategy, id string) error {
|
||||||
o.ID = id
|
o.ID = id
|
||||||
return r.DB.Updates(o).Error
|
return r.GetDB(c).Updates(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StrategyRepository) FindById(id string) (m model.Strategy, err error) {
|
func (r strategyRepository) FindById(c context.Context, id string) (m model.Strategy, err error) {
|
||||||
err = r.DB.Where("id = ?", id).First(&m).Error
|
err = r.GetDB(c).Where("id = ?", id).First(&m).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,23 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"next-terminal/server/constant"
|
"context"
|
||||||
"next-terminal/server/model"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"next-terminal/server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserRepository struct {
|
type userRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserRepository(db *gorm.DB) *UserRepository {
|
func (r userRepository) FindAll(c context.Context) (o []model.User, err error) {
|
||||||
userRepository = &UserRepository{DB: db}
|
err = r.GetDB(c).Find(&o).Error
|
||||||
return userRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r UserRepository) FindAll() (o []model.User, err error) {
|
|
||||||
err = r.DB.Find(&o).Error
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserRepository) Find(pageIndex, pageSize int, username, nickname, mail, order, field string, account model.User) (o []model.UserForPage, total int64, err error) {
|
func (r userRepository) Find(c context.Context, pageIndex, pageSize int, username, nickname, mail, order, field string) (o []model.UserForPage, total int64, err error) {
|
||||||
db := r.DB.Table("users").Select("users.id,users.username,users.nickname,users.mail,users.online,users.created,users.type,users.status, count(resource_sharers.user_id) as sharer_asset_count, users.totp_secret").Joins("left join resource_sharers on users.id = resource_sharers.user_id and resource_sharers.resource_type = 'asset'").Group("users.id")
|
db := r.GetDB(c).Table("users").Select("users.id,users.username,users.nickname,users.mail,users.online,users.created,users.type,users.status,users.source, count(resource_sharers.user_id) as sharer_asset_count, users.totp_secret").Joins("left join resource_sharers on users.id = resource_sharers.user_id and resource_sharers.resource_type = 'asset'").Group("users.id")
|
||||||
dbCounter := r.DB.Table("users")
|
dbCounter := r.GetDB(c).Table("users")
|
||||||
|
|
||||||
if constant.TypeUser == account.Type {
|
|
||||||
// 普通用户只能查看到普通用户
|
|
||||||
db = db.Where("users.type = ?", constant.TypeUser)
|
|
||||||
dbCounter = dbCounter.Where("type = ?", constant.TypeUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(username) > 0 {
|
if len(username) > 0 {
|
||||||
db = db.Where("users.username like ?", "%"+username+"%")
|
db = db.Where("users.username like ?", "%"+username+"%")
|
||||||
@ -80,19 +68,19 @@ func (r UserRepository) Find(pageIndex, pageSize int, username, nickname, mail,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserRepository) FindById(id string) (o model.User, err error) {
|
func (r userRepository) FindById(c context.Context, id string) (o model.User, err error) {
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserRepository) FindByUsername(username string) (o model.User, err error) {
|
func (r userRepository) FindByUsername(c context.Context, username string) (o model.User, err error) {
|
||||||
err = r.DB.Where("username = ?", username).First(&o).Error
|
err = r.GetDB(c).Where("username = ?", username).First(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserRepository) ExistByUsername(username string) (exist bool) {
|
func (r userRepository) ExistByUsername(c context.Context, username string) (exist bool) {
|
||||||
count := int64(0)
|
count := int64(0)
|
||||||
err := r.DB.Table("users").Where("username = ?", username).Count(&count).Error
|
err := r.GetDB(c).Table("users").Where("username = ?", username).Count(&count).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -100,51 +88,38 @@ func (r UserRepository) ExistByUsername(username string) (exist bool) {
|
|||||||
return count > 0
|
return count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserRepository) FindOnlineUsers() (o []model.User, err error) {
|
func (r userRepository) FindOnlineUsers(c context.Context) (o []model.User, err error) {
|
||||||
err = r.DB.Where("online = ?", true).Find(&o).Error
|
err = r.GetDB(c).Where("online = ?", true).Find(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserRepository) Create(o *model.User) error {
|
func (r userRepository) Create(c context.Context, o *model.User) error {
|
||||||
return r.DB.Create(o).Error
|
return r.GetDB(c).Create(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserRepository) Update(o *model.User) error {
|
func (r userRepository) Update(c context.Context, o *model.User) error {
|
||||||
return r.DB.Updates(o).Error
|
return r.GetDB(c).Updates(o).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserRepository) UpdateOnlineByUsername(username string, online bool) error {
|
func (r userRepository) UpdateOnlineByUsername(c context.Context, username string, online bool) error {
|
||||||
sql := "update users set online = ? where username = ?"
|
sql := "update users set online = ? where username = ?"
|
||||||
return r.DB.Exec(sql, online, username).Error
|
return r.GetDB(c).Exec(sql, online, username).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserRepository) DeleteById(id string) error {
|
func (r userRepository) DeleteById(c context.Context, id string) error {
|
||||||
return r.DB.Transaction(func(tx *gorm.DB) (err error) {
|
return r.GetDB(c).Where("id = ?", id).Delete(&model.User{}).Error
|
||||||
// 删除用户
|
|
||||||
err = tx.Where("id = ?", id).Delete(&model.User{}).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 删除用户组中的用户关系
|
|
||||||
err = tx.Where("user_id = ?", id).Delete(&model.UserGroupMember{}).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 删除用户分享到的资产
|
|
||||||
err = tx.Where("user_id = ?", id).Delete(&model.ResourceSharer{}).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserRepository) CountOnlineUser() (total int64, err error) {
|
func (r userRepository) DeleteBySource(c context.Context, source string) error {
|
||||||
err = r.DB.Where("online = ?", true).Find(&model.User{}).Count(&total).Error
|
return r.GetDB(c).Where("source = ?", source).Delete(&model.User{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r userRepository) CountOnlineUser(c context.Context) (total int64, err error) {
|
||||||
|
err = r.GetDB(c).Where("online = ?", true).Find(&model.User{}).Count(&total).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserRepository) Count() (total int64, err error) {
|
func (r userRepository) Count(c context.Context) (total int64, err error) {
|
||||||
err = r.DB.Find(&model.User{}).Count(&total).Error
|
err = r.GetDB(c).Find(&model.User{}).Count(&total).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,23 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"next-terminal/server/model"
|
"context"
|
||||||
"next-terminal/server/utils"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"next-terminal/server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserGroupRepository struct {
|
type userGroupRepository struct {
|
||||||
DB *gorm.DB
|
baseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserGroupRepository(db *gorm.DB) *UserGroupRepository {
|
func (r userGroupRepository) FindAll(c context.Context) (o []model.UserGroup, err error) {
|
||||||
userGroupRepository = &UserGroupRepository{DB: db}
|
err = r.GetDB(c).Find(&o).Error
|
||||||
return userGroupRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r UserGroupRepository) FindAll() (o []model.UserGroup, err error) {
|
|
||||||
err = r.DB.Find(&o).Error
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserGroupRepository) Find(pageIndex, pageSize int, name, order, field string) (o []model.UserGroupForPage, total int64, err error) {
|
func (r userGroupRepository) Find(c context.Context, pageIndex, pageSize int, name, order, field string) (o []model.UserGroupForPage, total int64, err error) {
|
||||||
db := r.DB.Table("user_groups").Select("user_groups.id, user_groups.name, user_groups.created, count(resource_sharers.user_group_id) as asset_count").Joins("left join resource_sharers on user_groups.id = resource_sharers.user_group_id and resource_sharers.resource_type = 'asset'").Group("user_groups.id")
|
db := r.GetDB(c).Table("user_groups").Select("user_groups.id, user_groups.name, user_groups.created, count(resource_sharers.user_group_id) as asset_count").Joins("left join resource_sharers on user_groups.id = resource_sharers.user_group_id and resource_sharers.resource_type = 'asset'").Group("user_groups.id")
|
||||||
dbCounter := r.DB.Table("user_groups")
|
dbCounter := r.GetDB(c).Table("user_groups")
|
||||||
if len(name) > 0 {
|
if len(name) > 0 {
|
||||||
db = db.Where("user_groups.name like ?", "%"+name+"%")
|
db = db.Where("user_groups.name like ?", "%"+name+"%")
|
||||||
dbCounter = dbCounter.Where("name like ?", "%"+name+"%")
|
dbCounter = dbCounter.Where("name like ?", "%"+name+"%")
|
||||||
@ -53,94 +47,29 @@ func (r UserGroupRepository) Find(pageIndex, pageSize int, name, order, field st
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserGroupRepository) FindById(id string) (o model.UserGroup, err error) {
|
func (r userGroupRepository) FindById(c context.Context, id string) (o model.UserGroup, err error) {
|
||||||
err = r.DB.Where("id = ?", id).First(&o).Error
|
err = r.GetDB(c).Where("id = ?", id).First(&o).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserGroupRepository) FindUserGroupIdsByUserId(userId string) (o []string, err error) {
|
func (r userGroupRepository) FindByName(c context.Context, name string) (o model.UserGroup, err error) {
|
||||||
// 先查询用户所在的用户
|
err = r.GetDB(c).Where("name = ?", name).First(&o).Error
|
||||||
err = r.DB.Table("user_group_members").Select("user_group_id").Where("user_id = ?", userId).Find(&o).Error
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserGroupRepository) FindMembersById(userGroupId string) (o []string, err error) {
|
func (r userGroupRepository) Create(c context.Context, o *model.UserGroup) (err error) {
|
||||||
err = r.DB.Table("user_group_members").Select("user_id").Where("user_group_id = ?", userGroupId).Find(&o).Error
|
return r.GetDB(c).Create(o).Error
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
func (r userGroupRepository) Update(c context.Context, o *model.UserGroup) error {
|
||||||
func (r UserGroupRepository) Create(o *model.UserGroup, members []string) (err error) {
|
return r.GetDB(c).Updates(o).Error
|
||||||
return r.DB.Transaction(func(tx *gorm.DB) error {
|
}
|
||||||
err = tx.Create(o).Error
|
|
||||||
if err != nil {
|
func (r userGroupRepository) DeleteById(c context.Context, id string) (err error) {
|
||||||
return err
|
return r.GetDB(c).Where("id = ?", id).Delete(&model.UserGroup{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
if members != nil {
|
func (r userGroupRepository) FindAllUserGroupMembers() (c context.Context, o []model.UserGroupMember, err error) {
|
||||||
userGroupId := o.ID
|
err = r.GetDB(c).Find(&o).Error
|
||||||
err = AddUserGroupMembers(tx, members, userGroupId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r UserGroupRepository) Update(o *model.UserGroup, members []string, id string) error {
|
|
||||||
return r.DB.Transaction(func(tx *gorm.DB) error {
|
|
||||||
o.ID = id
|
|
||||||
err := tx.Updates(o).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.Where("user_group_id = ?", id).Delete(&model.UserGroupMember{}).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if members != nil {
|
|
||||||
userGroupId := o.ID
|
|
||||||
err = AddUserGroupMembers(tx, members, userGroupId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r UserGroupRepository) DeleteById(id string) (err error) {
|
|
||||||
err = r.DB.Where("id = ?", id).Delete(&model.UserGroup{}).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return r.DB.Where("user_group_id = ?", id).Delete(&model.UserGroupMember{}).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddUserGroupMembers(tx *gorm.DB, userIds []string, userGroupId string) error {
|
|
||||||
userRepository := NewUserRepository(tx)
|
|
||||||
for i := range userIds {
|
|
||||||
userId := userIds[i]
|
|
||||||
_, err := userRepository.FindById(userId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
userGroupMember := model.UserGroupMember{
|
|
||||||
ID: utils.Sign([]string{userGroupId, userId}),
|
|
||||||
UserId: userId,
|
|
||||||
UserGroupId: userGroupId,
|
|
||||||
}
|
|
||||||
err = tx.Create(&userGroupMember).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r UserGroupRepository) FindAllUserGroupMembers() (o []model.UserGroupMember, err error) {
|
|
||||||
err = r.DB.Find(&o).Error
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
34
server/repository/user_group_member.go
Normal file
34
server/repository/user_group_member.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"next-terminal/server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type userGroupMemberRepository struct {
|
||||||
|
baseRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r userGroupMemberRepository) FindUserIdsByUserGroupId(c context.Context, userGroupId string) (o []string, err error) {
|
||||||
|
err = r.GetDB(c).Table("user_group_members").Select("user_id").Where("user_group_id = ?", userGroupId).Find(&o).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r userGroupMemberRepository) FindUserGroupIdsByUserId(c context.Context, userId string) (o []string, err error) {
|
||||||
|
// 先查询用户所在的用户
|
||||||
|
err = r.GetDB(c).Table("user_group_members").Select("user_group_id").Where("user_id = ?", userId).Find(&o).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r userGroupMemberRepository) Create(c context.Context, o *model.UserGroupMember) error {
|
||||||
|
return r.GetDB(c).Create(o).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r userGroupMemberRepository) DeleteByUserId(c context.Context, userId string) error {
|
||||||
|
return r.GetDB(c).Where("user_id = ?", userId).Delete(&model.UserGroupMember{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r userGroupMemberRepository) DeleteByUserGroupId(c context.Context, userGroupId string) error {
|
||||||
|
return r.GetDB(c).Where("user_group_id = ?", userGroupId).Delete(&model.UserGroupMember{}).Error
|
||||||
|
}
|
21
server/repository/var.go
Normal file
21
server/repository/var.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
var (
|
||||||
|
PropertyRepository = new(propertyRepository)
|
||||||
|
UserRepository = new(userRepository)
|
||||||
|
UserGroupRepository = new(userGroupRepository)
|
||||||
|
UserGroupMemberRepository = new(userGroupMemberRepository)
|
||||||
|
ResourceSharerRepository = new(resourceSharerRepository)
|
||||||
|
AssetRepository = new(assetRepository)
|
||||||
|
CredentialRepository = new(credentialRepository)
|
||||||
|
CommandRepository = new(commandRepository)
|
||||||
|
SessionRepository = new(sessionRepository)
|
||||||
|
SecurityRepository = new(securityRepository)
|
||||||
|
GatewayRepository = new(gatewayRepository)
|
||||||
|
JobRepository = new(jobRepository)
|
||||||
|
JobLogRepository = new(jobLogRepository)
|
||||||
|
LoginLogRepository = new(loginLogRepository)
|
||||||
|
StorageRepository = new(storageRepository)
|
||||||
|
StrategyRepository = new(strategyRepository)
|
||||||
|
AccessTokenRepository = new(accessTokenRepository)
|
||||||
|
)
|
@ -1,75 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"next-terminal/server/global/gateway"
|
|
||||||
"next-terminal/server/log"
|
|
||||||
"next-terminal/server/model"
|
|
||||||
"next-terminal/server/repository"
|
|
||||||
"next-terminal/server/term"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AccessGatewayService struct {
|
|
||||||
accessGatewayRepository *repository.AccessGatewayRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAccessGatewayService(accessGatewayRepository *repository.AccessGatewayRepository) *AccessGatewayService {
|
|
||||||
accessGatewayService = &AccessGatewayService{accessGatewayRepository: accessGatewayRepository}
|
|
||||||
return accessGatewayService
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessGatewayService) GetGatewayAndReconnectById(accessGatewayId string) (g *gateway.Gateway, err error) {
|
|
||||||
g = gateway.GlobalGatewayManager.GetById(accessGatewayId)
|
|
||||||
if g == nil || !g.Connected {
|
|
||||||
accessGateway, err := r.accessGatewayRepository.FindById(accessGatewayId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
g = r.ReConnect(&accessGateway)
|
|
||||||
}
|
|
||||||
return g, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessGatewayService) GetGatewayById(accessGatewayId string) (g *gateway.Gateway, err error) {
|
|
||||||
g = gateway.GlobalGatewayManager.GetById(accessGatewayId)
|
|
||||||
if g == nil {
|
|
||||||
accessGateway, err := r.accessGatewayRepository.FindById(accessGatewayId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
g = r.ReConnect(&accessGateway)
|
|
||||||
}
|
|
||||||
return g, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessGatewayService) ReConnectAll() error {
|
|
||||||
gateways, err := r.accessGatewayRepository.FindAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(gateways) > 0 {
|
|
||||||
for i := range gateways {
|
|
||||||
r.ReConnect(&gateways[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessGatewayService) ReConnect(m *model.AccessGateway) *gateway.Gateway {
|
|
||||||
log.Debugf("重建接入网关「%v」中...", m.Name)
|
|
||||||
r.DisconnectById(m.ID)
|
|
||||||
sshClient, err := term.NewSshClient(m.IP, m.Port, m.Username, m.Password, m.PrivateKey, m.Passphrase)
|
|
||||||
var g *gateway.Gateway
|
|
||||||
if err != nil {
|
|
||||||
g = gateway.NewGateway(m.ID, m.Localhost, false, err.Error(), nil)
|
|
||||||
} else {
|
|
||||||
g = gateway.NewGateway(m.ID, m.Localhost, true, "", sshClient)
|
|
||||||
}
|
|
||||||
gateway.GlobalGatewayManager.Add <- g
|
|
||||||
log.Debugf("重建接入网关「%v」完成", m.Name)
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AccessGatewayService) DisconnectById(accessGatewayId string) {
|
|
||||||
gateway.GlobalGatewayManager.Del <- accessGatewayId
|
|
||||||
}
|
|
86
server/service/access_token.go
Normal file
86
server/service/access_token.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/dto"
|
||||||
|
"next-terminal/server/env"
|
||||||
|
"next-terminal/server/global/cache"
|
||||||
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type accessTokenService struct {
|
||||||
|
baseService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service accessTokenService) FindByUserId(userId string) (model.AccessToken, error) {
|
||||||
|
return repository.AccessTokenRepository.FindByUserId(context.TODO(), userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service accessTokenService) GenAccessToken(userId string) error {
|
||||||
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
ctx := service.Context(tx)
|
||||||
|
|
||||||
|
user, err := repository.UserRepository.FindById(ctx, userId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oldAccessToken, err := repository.AccessTokenRepository.FindByUserId(ctx, userId)
|
||||||
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if oldAccessToken.Token != "" {
|
||||||
|
cache.TokenManager.Delete(oldAccessToken.Token)
|
||||||
|
}
|
||||||
|
if err := repository.AccessTokenRepository.DeleteByUserId(ctx, userId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
token := "forever-" + utils.UUID()
|
||||||
|
accessToken := &model.AccessToken{
|
||||||
|
ID: utils.UUID(),
|
||||||
|
UserId: userId,
|
||||||
|
Token: token,
|
||||||
|
Created: utils.NowJsonTime(),
|
||||||
|
}
|
||||||
|
|
||||||
|
authorization := dto.Authorization{
|
||||||
|
Token: token,
|
||||||
|
Remember: false,
|
||||||
|
Type: constant.AccessToken,
|
||||||
|
User: &user,
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.TokenManager.Set(token, authorization, cache.NoExpiration)
|
||||||
|
|
||||||
|
return repository.AccessTokenRepository.Create(ctx, accessToken)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service accessTokenService) Reload() error {
|
||||||
|
accessTokens, err := repository.AccessTokenRepository.FindAll(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, accessToken := range accessTokens {
|
||||||
|
user, err := repository.UserRepository.FindById(context.TODO(), accessToken.UserId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
authorization := dto.Authorization{
|
||||||
|
Token: accessToken.Token,
|
||||||
|
Remember: false,
|
||||||
|
Type: constant.AccessToken,
|
||||||
|
User: &user,
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.TokenManager.Set(accessToken.Token, authorization, cache.NoExpiration)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,21 +1,26 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"next-terminal/server/config"
|
"next-terminal/server/config"
|
||||||
|
"next-terminal/server/env"
|
||||||
|
"next-terminal/server/model"
|
||||||
"next-terminal/server/repository"
|
"next-terminal/server/repository"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AssetService struct {
|
type assetService struct {
|
||||||
assetRepository *repository.AssetRepository
|
baseService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAssetService(assetRepository *repository.AssetRepository) *AssetService {
|
func (s assetService) EncryptAll() error {
|
||||||
return &AssetService{assetRepository: assetRepository}
|
items, err := repository.AssetRepository.FindAll(context.TODO())
|
||||||
}
|
|
||||||
|
|
||||||
func (r AssetService) Encrypt() error {
|
|
||||||
items, err := r.assetRepository.FindAll()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -24,19 +29,95 @@ func (r AssetService) Encrypt() error {
|
|||||||
if item.Encrypted {
|
if item.Encrypted {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := r.assetRepository.Encrypt(&item, config.GlobalCfg.EncryptionPassword); err != nil {
|
if err := s.Encrypt(&item, config.GlobalCfg.EncryptionPassword); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := r.assetRepository.UpdateById(&item, item.ID); err != nil {
|
if err := repository.AssetRepository.UpdateById(context.TODO(), &item, item.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AssetService) CheckStatus(accessGatewayId string, ip string, port int) (active bool, err error) {
|
func (s assetService) Decrypt(item *model.Asset, password []byte) error {
|
||||||
|
if item.Encrypted {
|
||||||
|
if item.Password != "" && item.Password != "-" {
|
||||||
|
origData, err := base64.StdEncoding.DecodeString(item.Password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.Password = string(decryptedCBC)
|
||||||
|
}
|
||||||
|
if item.PrivateKey != "" && item.PrivateKey != "-" {
|
||||||
|
origData, err := base64.StdEncoding.DecodeString(item.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.PrivateKey = string(decryptedCBC)
|
||||||
|
}
|
||||||
|
if item.Passphrase != "" && item.Passphrase != "-" {
|
||||||
|
origData, err := base64.StdEncoding.DecodeString(item.Passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.Passphrase = string(decryptedCBC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s assetService) Encrypt(item *model.Asset, password []byte) error {
|
||||||
|
if item.Password != "" && item.Password != "-" {
|
||||||
|
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Password), password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.Password = base64.StdEncoding.EncodeToString(encryptedCBC)
|
||||||
|
}
|
||||||
|
if item.PrivateKey != "" && item.PrivateKey != "-" {
|
||||||
|
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.PrivateKey), password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.PrivateKey = base64.StdEncoding.EncodeToString(encryptedCBC)
|
||||||
|
}
|
||||||
|
if item.Passphrase != "" && item.Passphrase != "-" {
|
||||||
|
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Passphrase), password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.Passphrase = base64.StdEncoding.EncodeToString(encryptedCBC)
|
||||||
|
}
|
||||||
|
item.Encrypted = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s assetService) FindByIdAndDecrypt(c context.Context, id string) (model.Asset, error) {
|
||||||
|
asset, err := repository.AssetRepository.FindById(c, id)
|
||||||
|
if err != nil {
|
||||||
|
return model.Asset{}, err
|
||||||
|
}
|
||||||
|
if err := s.Decrypt(&asset, config.GlobalCfg.EncryptionPassword); err != nil {
|
||||||
|
return model.Asset{}, err
|
||||||
|
}
|
||||||
|
return asset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s assetService) CheckStatus(accessGatewayId string, ip string, port int) (active bool, err error) {
|
||||||
if accessGatewayId != "" && accessGatewayId != "-" {
|
if accessGatewayId != "" && accessGatewayId != "-" {
|
||||||
g, e1 := accessGatewayService.GetGatewayAndReconnectById(accessGatewayId)
|
g, e1 := GatewayService.GetGatewayAndReconnectById(accessGatewayId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, e1
|
return false, e1
|
||||||
}
|
}
|
||||||
@ -58,3 +139,118 @@ func (r AssetService) CheckStatus(accessGatewayId string, ip string, port int) (
|
|||||||
}
|
}
|
||||||
return active, err
|
return active, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s assetService) Create(m echo.Map) (model.Asset, error) {
|
||||||
|
|
||||||
|
data, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return model.Asset{}, err
|
||||||
|
}
|
||||||
|
var item model.Asset
|
||||||
|
if err := json.Unmarshal(data, &item); err != nil {
|
||||||
|
return model.Asset{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
item.ID = utils.UUID()
|
||||||
|
item.Created = utils.NowJsonTime()
|
||||||
|
item.Active = true
|
||||||
|
|
||||||
|
return item, env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := s.Context(tx)
|
||||||
|
|
||||||
|
if err := s.Encrypt(&item, config.GlobalCfg.EncryptionPassword); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := repository.AssetRepository.Create(c, &item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repository.AssetRepository.UpdateAttributes(c, item.ID, item.Protocol, m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
active, _ := s.CheckStatus(item.AccessGatewayId, item.IP, item.Port)
|
||||||
|
|
||||||
|
if item.Active != active {
|
||||||
|
_ = repository.AssetRepository.UpdateActiveById(context.TODO(), active, item.ID)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s assetService) DeleteById(id string) error {
|
||||||
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := s.Context(tx)
|
||||||
|
// 删除资产
|
||||||
|
if err := repository.AssetRepository.DeleteById(c, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除资产属性
|
||||||
|
if err := repository.AssetRepository.DeleteAttrByAssetId(c, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除资产与用户的关系
|
||||||
|
if err := repository.ResourceSharerRepository.DeleteByResourceId(c, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s assetService) UpdateById(id string, m echo.Map) error {
|
||||||
|
data, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var item model.Asset
|
||||||
|
if err := json.Unmarshal(data, &item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch item.AccountType {
|
||||||
|
case "credential":
|
||||||
|
item.Username = "-"
|
||||||
|
item.Password = "-"
|
||||||
|
item.PrivateKey = "-"
|
||||||
|
item.Passphrase = "-"
|
||||||
|
case "private-key":
|
||||||
|
item.Password = "-"
|
||||||
|
item.CredentialId = "-"
|
||||||
|
if len(item.Username) == 0 {
|
||||||
|
item.Username = "-"
|
||||||
|
}
|
||||||
|
if len(item.Passphrase) == 0 {
|
||||||
|
item.Passphrase = "-"
|
||||||
|
}
|
||||||
|
case "custom":
|
||||||
|
item.PrivateKey = "-"
|
||||||
|
item.Passphrase = "-"
|
||||||
|
item.CredentialId = "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(item.Tags) == 0 {
|
||||||
|
item.Tags = "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
if item.Description == "" {
|
||||||
|
item.Description = "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Encrypt(&item, config.GlobalCfg.EncryptionPassword); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := s.Context(tx)
|
||||||
|
|
||||||
|
if err := repository.AssetRepository.UpdateById(c, &item, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := repository.AssetRepository.UpdateAttributes(c, id, item.Protocol, m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
326
server/service/backup.go
Normal file
326
server/service/backup.go
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"next-terminal/server/config"
|
||||||
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/dto"
|
||||||
|
"next-terminal/server/env"
|
||||||
|
"next-terminal/server/global/security"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type backupService struct {
|
||||||
|
baseService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service backupService) Export() (error, *dto.Backup) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
users, err := repository.UserRepository.FindAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
for i := range users {
|
||||||
|
users[i].Password = ""
|
||||||
|
}
|
||||||
|
userGroups, err := repository.UserGroupRepository.FindAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
if len(userGroups) > 0 {
|
||||||
|
for i := range userGroups {
|
||||||
|
members, err := repository.UserGroupMemberRepository.FindUserIdsByUserGroupId(ctx, userGroups[i].ID)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
userGroups[i].Members = members
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storages, err := repository.StorageRepository.FindAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
strategies, err := repository.StrategyRepository.FindAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
jobs, err := repository.JobRepository.FindAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
accessSecurities, err := repository.SecurityRepository.FindAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
accessGateways, err := repository.GatewayRepository.FindAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
commands, err := repository.CommandRepository.FindAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
credentials, err := repository.CredentialRepository.FindAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
if len(credentials) > 0 {
|
||||||
|
for i := range credentials {
|
||||||
|
if err := CredentialService.Decrypt(&credentials[i], config.GlobalCfg.EncryptionPassword); err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assets, err := repository.AssetRepository.FindAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
var assetMaps = make([]map[string]interface{}, 0)
|
||||||
|
if len(assets) > 0 {
|
||||||
|
for i := range assets {
|
||||||
|
asset := assets[i]
|
||||||
|
if err := AssetService.Decrypt(&asset, config.GlobalCfg.EncryptionPassword); err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
attributeMap, err := repository.AssetRepository.FindAssetAttrMapByAssetId(ctx, asset.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
itemMap := utils.StructToMap(asset)
|
||||||
|
for key := range attributeMap {
|
||||||
|
itemMap[key] = attributeMap[key]
|
||||||
|
}
|
||||||
|
itemMap["created"] = asset.Created.Format("2006-01-02 15:04:05")
|
||||||
|
assetMaps = append(assetMaps, itemMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceSharers, err := repository.ResourceSharerRepository.FindAll(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
backup := dto.Backup{
|
||||||
|
Users: users,
|
||||||
|
UserGroups: userGroups,
|
||||||
|
Storages: storages,
|
||||||
|
Strategies: strategies,
|
||||||
|
Jobs: jobs,
|
||||||
|
AccessSecurities: accessSecurities,
|
||||||
|
AccessGateways: accessGateways,
|
||||||
|
Commands: commands,
|
||||||
|
Credentials: credentials,
|
||||||
|
Assets: assetMaps,
|
||||||
|
ResourceSharers: resourceSharers,
|
||||||
|
}
|
||||||
|
return nil, &backup
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service backupService) Import(backup *dto.Backup) error {
|
||||||
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := service.Context(tx)
|
||||||
|
var userIdMapping = make(map[string]string)
|
||||||
|
if len(backup.Users) > 0 {
|
||||||
|
for _, item := range backup.Users {
|
||||||
|
oldId := item.ID
|
||||||
|
if repository.UserRepository.ExistByUsername(c, item.Username) {
|
||||||
|
delete(userIdMapping, oldId)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newId := utils.UUID()
|
||||||
|
item.ID = newId
|
||||||
|
item.Password = utils.GenPassword()
|
||||||
|
if err := repository.UserRepository.Create(c, &item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
userIdMapping[oldId] = newId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var userGroupIdMapping = make(map[string]string)
|
||||||
|
if len(backup.UserGroups) > 0 {
|
||||||
|
for _, item := range backup.UserGroups {
|
||||||
|
oldId := item.ID
|
||||||
|
|
||||||
|
var members = make([]string, 0)
|
||||||
|
if len(item.Members) > 0 {
|
||||||
|
for _, member := range item.Members {
|
||||||
|
members = append(members, userIdMapping[member])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userGroup, err := UserGroupService.Create(item.Name, members)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(constant.ErrNameAlreadyUsed, err) {
|
||||||
|
// 删除名称重复的用户组
|
||||||
|
delete(userGroupIdMapping, oldId)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userGroupIdMapping[oldId] = userGroup.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(backup.Storages) > 0 {
|
||||||
|
for _, item := range backup.Storages {
|
||||||
|
owner := userIdMapping[item.Owner]
|
||||||
|
if owner == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item.ID = utils.UUID()
|
||||||
|
item.Owner = owner
|
||||||
|
item.Created = utils.NowJsonTime()
|
||||||
|
if err := repository.StorageRepository.Create(c, &item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var strategyIdMapping = make(map[string]string)
|
||||||
|
if len(backup.Strategies) > 0 {
|
||||||
|
for _, item := range backup.Strategies {
|
||||||
|
oldId := item.ID
|
||||||
|
newId := utils.UUID()
|
||||||
|
item.ID = newId
|
||||||
|
item.Created = utils.NowJsonTime()
|
||||||
|
if err := repository.StrategyRepository.Create(c, &item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
strategyIdMapping[oldId] = newId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(backup.AccessSecurities) > 0 {
|
||||||
|
for _, item := range backup.AccessSecurities {
|
||||||
|
item.ID = utils.UUID()
|
||||||
|
if err := repository.SecurityRepository.Create(c, &item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 更新内存中的安全规则
|
||||||
|
rule := &security.Security{
|
||||||
|
ID: item.ID,
|
||||||
|
IP: item.IP,
|
||||||
|
Rule: item.Rule,
|
||||||
|
Priority: item.Priority,
|
||||||
|
}
|
||||||
|
security.GlobalSecurityManager.Add <- rule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var accessGatewayIdMapping = make(map[string]string, 0)
|
||||||
|
if len(backup.AccessGateways) > 0 {
|
||||||
|
for _, item := range backup.AccessGateways {
|
||||||
|
oldId := item.ID
|
||||||
|
newId := utils.UUID()
|
||||||
|
item.ID = newId
|
||||||
|
item.Created = utils.NowJsonTime()
|
||||||
|
if err := repository.GatewayRepository.Create(c, &item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
accessGatewayIdMapping[oldId] = newId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(backup.Commands) > 0 {
|
||||||
|
for _, item := range backup.Commands {
|
||||||
|
item.ID = utils.UUID()
|
||||||
|
item.Created = utils.NowJsonTime()
|
||||||
|
if err := repository.CommandRepository.Create(c, &item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var credentialIdMapping = make(map[string]string, 0)
|
||||||
|
if len(backup.Credentials) > 0 {
|
||||||
|
for _, item := range backup.Credentials {
|
||||||
|
oldId := item.ID
|
||||||
|
newId := utils.UUID()
|
||||||
|
item.ID = newId
|
||||||
|
if err := CredentialService.Create(&item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
credentialIdMapping[oldId] = newId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var assetIdMapping = make(map[string]string, 0)
|
||||||
|
if len(backup.Assets) > 0 {
|
||||||
|
for _, m := range backup.Assets {
|
||||||
|
data, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m := echo.Map{}
|
||||||
|
if err := json.Unmarshal(data, &m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
credentialId := m["credentialId"].(string)
|
||||||
|
accessGatewayId := m["accessGatewayId"].(string)
|
||||||
|
if credentialId != "" && credentialId != "-" {
|
||||||
|
m["credentialId"] = credentialIdMapping[credentialId]
|
||||||
|
}
|
||||||
|
if accessGatewayId != "" && accessGatewayId != "-" {
|
||||||
|
m["accessGatewayId"] = accessGatewayIdMapping[accessGatewayId]
|
||||||
|
}
|
||||||
|
|
||||||
|
oldId := m["id"].(string)
|
||||||
|
asset, err := AssetService.Create(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
assetIdMapping[oldId] = asset.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(backup.ResourceSharers) > 0 {
|
||||||
|
for _, item := range backup.ResourceSharers {
|
||||||
|
|
||||||
|
userGroupId := userGroupIdMapping[item.UserGroupId]
|
||||||
|
userId := userIdMapping[item.UserId]
|
||||||
|
strategyId := strategyIdMapping[item.StrategyId]
|
||||||
|
resourceId := assetIdMapping[item.ResourceId]
|
||||||
|
|
||||||
|
if err := repository.ResourceSharerRepository.AddSharerResources(userGroupId, userId, strategyId, item.ResourceType, []string{resourceId}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(backup.Jobs) > 0 {
|
||||||
|
for _, item := range backup.Jobs {
|
||||||
|
if item.Func == constant.FuncCheckAssetStatusJob {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceIds := strings.Split(item.ResourceIds, ",")
|
||||||
|
if len(resourceIds) > 0 {
|
||||||
|
var newResourceIds = make([]string, 0)
|
||||||
|
for _, resourceId := range resourceIds {
|
||||||
|
newResourceIds = append(newResourceIds, assetIdMapping[resourceId])
|
||||||
|
}
|
||||||
|
item.ResourceIds = strings.Join(newResourceIds, ",")
|
||||||
|
}
|
||||||
|
if err := JobService.Create(&item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
16
server/service/base.go
Normal file
16
server/service/base.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"next-terminal/server/constant"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type baseService struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service baseService) Context(db *gorm.DB) context.Context {
|
||||||
|
return context.WithValue(context.TODO(), constant.DB, db)
|
||||||
|
}
|
@ -1,20 +1,21 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
|
||||||
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"next-terminal/server/config"
|
"next-terminal/server/config"
|
||||||
"next-terminal/server/repository"
|
"next-terminal/server/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CredentialService struct {
|
type credentialService struct {
|
||||||
credentialRepository *repository.CredentialRepository
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCredentialService(credentialRepository *repository.CredentialRepository) *CredentialService {
|
func (s credentialService) EncryptAll() error {
|
||||||
return &CredentialService{credentialRepository: credentialRepository}
|
items, err := repository.CredentialRepository.FindAll(context.TODO())
|
||||||
}
|
|
||||||
|
|
||||||
func (r CredentialService) Encrypt() error {
|
|
||||||
items, err := r.credentialRepository.FindAll()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -23,12 +24,96 @@ func (r CredentialService) Encrypt() error {
|
|||||||
if item.Encrypted {
|
if item.Encrypted {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := r.credentialRepository.Encrypt(&item, config.GlobalCfg.EncryptionPassword); err != nil {
|
if err := s.Encrypt(&item, config.GlobalCfg.EncryptionPassword); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := r.credentialRepository.UpdateById(&item, item.ID); err != nil {
|
if err := repository.CredentialRepository.UpdateById(context.TODO(), &item, item.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s credentialService) Encrypt(item *model.Credential, password []byte) error {
|
||||||
|
if item.Password != "-" {
|
||||||
|
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Password), password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.Password = base64.StdEncoding.EncodeToString(encryptedCBC)
|
||||||
|
}
|
||||||
|
if item.PrivateKey != "-" {
|
||||||
|
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.PrivateKey), password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.PrivateKey = base64.StdEncoding.EncodeToString(encryptedCBC)
|
||||||
|
}
|
||||||
|
if item.Passphrase != "-" {
|
||||||
|
encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Passphrase), password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.Passphrase = base64.StdEncoding.EncodeToString(encryptedCBC)
|
||||||
|
}
|
||||||
|
item.Encrypted = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s credentialService) Decrypt(item *model.Credential, password []byte) error {
|
||||||
|
if item.Encrypted {
|
||||||
|
if item.Password != "" && item.Password != "-" {
|
||||||
|
origData, err := base64.StdEncoding.DecodeString(item.Password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.Password = string(decryptedCBC)
|
||||||
|
}
|
||||||
|
if item.PrivateKey != "" && item.PrivateKey != "-" {
|
||||||
|
origData, err := base64.StdEncoding.DecodeString(item.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.PrivateKey = string(decryptedCBC)
|
||||||
|
}
|
||||||
|
if item.Passphrase != "" && item.Passphrase != "-" {
|
||||||
|
origData, err := base64.StdEncoding.DecodeString(item.Passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decryptedCBC, err := utils.AesDecryptCBC(origData, password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.Passphrase = string(decryptedCBC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s credentialService) FindByIdAndDecrypt(c context.Context, id string) (o model.Credential, err error) {
|
||||||
|
credential, err := repository.CredentialRepository.FindById(c, id)
|
||||||
|
if err != nil {
|
||||||
|
return o, err
|
||||||
|
}
|
||||||
|
if err := s.Decrypt(&credential, config.GlobalCfg.EncryptionPassword); err != nil {
|
||||||
|
return o, err
|
||||||
|
}
|
||||||
|
return credential, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s credentialService) Create(item *model.Credential) error {
|
||||||
|
// 加密密码之后进行存储
|
||||||
|
if err := s.Encrypt(item, config.GlobalCfg.EncryptionPassword); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return repository.CredentialRepository.Create(context.TODO(), item)
|
||||||
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
var (
|
|
||||||
accessGatewayService *AccessGatewayService
|
|
||||||
)
|
|
70
server/service/gateway.go
Normal file
70
server/service/gateway.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"next-terminal/server/global/gateway"
|
||||||
|
"next-terminal/server/log"
|
||||||
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
type gatewayService struct{}
|
||||||
|
|
||||||
|
func (r gatewayService) GetGatewayAndReconnectById(accessGatewayId string) (g *gateway.Gateway, err error) {
|
||||||
|
g = gateway.GlobalGatewayManager.GetById(accessGatewayId)
|
||||||
|
if g == nil || !g.Connected {
|
||||||
|
accessGateway, err := repository.GatewayRepository.FindById(context.TODO(), accessGatewayId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
g = r.ReConnect(&accessGateway)
|
||||||
|
}
|
||||||
|
return g, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r gatewayService) GetGatewayById(accessGatewayId string) (g *gateway.Gateway, err error) {
|
||||||
|
g = gateway.GlobalGatewayManager.GetById(accessGatewayId)
|
||||||
|
if g == nil {
|
||||||
|
accessGateway, err := repository.GatewayRepository.FindById(context.TODO(), accessGatewayId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
g = r.ReConnect(&accessGateway)
|
||||||
|
}
|
||||||
|
return g, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r gatewayService) ReConnectAll() error {
|
||||||
|
gateways, err := repository.GatewayRepository.FindAll(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(gateways) > 0 {
|
||||||
|
for i := range gateways {
|
||||||
|
r.ReConnect(&gateways[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r gatewayService) ReConnect(m *model.AccessGateway) *gateway.Gateway {
|
||||||
|
log.Debugf("重建接入网关「%v」中...", m.Name)
|
||||||
|
r.DisconnectById(m.ID)
|
||||||
|
sshClient, err := term.NewSshClient(m.IP, m.Port, m.Username, m.Password, m.PrivateKey, m.Passphrase)
|
||||||
|
var g *gateway.Gateway
|
||||||
|
if err != nil {
|
||||||
|
g = gateway.NewGateway(m.ID, false, err.Error(), nil)
|
||||||
|
} else {
|
||||||
|
g = gateway.NewGateway(m.ID, true, "", sshClient)
|
||||||
|
}
|
||||||
|
gateway.GlobalGatewayManager.Add <- g
|
||||||
|
log.Debugf("重建接入网关「%v」完成", m.Name)
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r gatewayService) DisconnectById(accessGatewayId string) {
|
||||||
|
gateway.GlobalGatewayManager.Del <- accessGatewayId
|
||||||
|
}
|
@ -1,42 +1,27 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
"next-terminal/server/global/cron"
|
"next-terminal/server/global/cron"
|
||||||
"next-terminal/server/log"
|
"next-terminal/server/log"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
"next-terminal/server/repository"
|
"next-terminal/server/repository"
|
||||||
"next-terminal/server/term"
|
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type JobService struct {
|
type jobService struct {
|
||||||
jobRepository *repository.JobRepository
|
|
||||||
jobLogRepository *repository.JobLogRepository
|
|
||||||
assetRepository *repository.AssetRepository
|
|
||||||
credentialRepository *repository.CredentialRepository
|
|
||||||
assetService *AssetService
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJobService(jobRepository *repository.JobRepository, jobLogRepository *repository.JobLogRepository, assetRepository *repository.AssetRepository, credentialRepository *repository.CredentialRepository, assetService *AssetService) *JobService {
|
func (r jobService) ChangeStatusById(id, status string) error {
|
||||||
return &JobService{jobRepository: jobRepository, jobLogRepository: jobLogRepository, assetRepository: assetRepository, credentialRepository: credentialRepository, assetService: assetService}
|
job, err := repository.JobRepository.FindById(context.TODO(), id)
|
||||||
}
|
|
||||||
|
|
||||||
func (r JobService) ChangeStatusById(id, status string) error {
|
|
||||||
job, err := r.jobRepository.FindById(id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if status == constant.JobStatusRunning {
|
if status == constant.JobStatusRunning {
|
||||||
j, err := getJob(&job, &r)
|
j, err := getJob(&job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -48,16 +33,16 @@ func (r JobService) ChangeStatusById(id, status string) error {
|
|||||||
|
|
||||||
jobForUpdate := model.Job{ID: id, Status: constant.JobStatusRunning, CronJobId: int(entryID)}
|
jobForUpdate := model.Job{ID: id, Status: constant.JobStatusRunning, CronJobId: int(entryID)}
|
||||||
|
|
||||||
return r.jobRepository.UpdateById(&jobForUpdate)
|
return repository.JobRepository.UpdateById(context.TODO(), &jobForUpdate)
|
||||||
} else {
|
} else {
|
||||||
cron.GlobalCron.Remove(cron.JobId(job.CronJobId))
|
cron.GlobalCron.Remove(cron.JobId(job.CronJobId))
|
||||||
log.Debugf("关闭计划任务「%v」,运行中计划任务数量「%v」", job.Name, len(cron.GlobalCron.Entries()))
|
log.Debugf("关闭计划任务「%v」,运行中计划任务数量「%v」", job.Name, len(cron.GlobalCron.Entries()))
|
||||||
jobForUpdate := model.Job{ID: id, Status: constant.JobStatusNotRunning}
|
jobForUpdate := model.Job{ID: id, Status: constant.JobStatusNotRunning}
|
||||||
return r.jobRepository.UpdateById(&jobForUpdate)
|
return repository.JobRepository.UpdateById(context.TODO(), &jobForUpdate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getJob(j *model.Job, jobService *JobService) (job cron.Job, err error) {
|
func getJob(j *model.Job) (job cron.Job, err error) {
|
||||||
switch j.Func {
|
switch j.Func {
|
||||||
case constant.FuncCheckAssetStatusJob:
|
case constant.FuncCheckAssetStatusJob:
|
||||||
job = CheckAssetStatusJob{
|
job = CheckAssetStatusJob{
|
||||||
@ -65,232 +50,21 @@ func getJob(j *model.Job, jobService *JobService) (job cron.Job, err error) {
|
|||||||
Mode: j.Mode,
|
Mode: j.Mode,
|
||||||
ResourceIds: j.ResourceIds,
|
ResourceIds: j.ResourceIds,
|
||||||
Metadata: j.Metadata,
|
Metadata: j.Metadata,
|
||||||
jobService: jobService,
|
|
||||||
assetService: jobService.assetService,
|
|
||||||
}
|
}
|
||||||
case constant.FuncShellJob:
|
case constant.FuncShellJob:
|
||||||
job = ShellJob{ID: j.ID, Mode: j.Mode, ResourceIds: j.ResourceIds, Metadata: j.Metadata, jobService: jobService}
|
job = ShellJob{ID: j.ID, Mode: j.Mode, ResourceIds: j.ResourceIds, Metadata: j.Metadata}
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("未识别的任务")
|
return nil, errors.New("未识别的任务")
|
||||||
}
|
}
|
||||||
return job, err
|
return job, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckAssetStatusJob struct {
|
func (r jobService) ExecJobById(id string) (err error) {
|
||||||
ID string
|
job, err := repository.JobRepository.FindById(context.TODO(), id)
|
||||||
Mode string
|
|
||||||
ResourceIds string
|
|
||||||
Metadata string
|
|
||||||
jobService *JobService
|
|
||||||
assetService *AssetService
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r CheckAssetStatusJob) Run() {
|
|
||||||
if r.ID == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var assets []model.Asset
|
|
||||||
if r.Mode == constant.JobModeAll {
|
|
||||||
assets, _ = r.jobService.assetRepository.FindAll()
|
|
||||||
} else {
|
|
||||||
assets, _ = r.jobService.assetRepository.FindByIds(strings.Split(r.ResourceIds, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(assets) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
msgChan := make(chan string)
|
|
||||||
for i := range assets {
|
|
||||||
asset := assets[i]
|
|
||||||
go func() {
|
|
||||||
t1 := time.Now()
|
|
||||||
var (
|
|
||||||
msg string
|
|
||||||
ip = asset.IP
|
|
||||||
port = asset.Port
|
|
||||||
)
|
|
||||||
active, err := r.assetService.CheckStatus(asset.AccessGatewayId, ip, port)
|
|
||||||
|
|
||||||
elapsed := time.Since(t1)
|
|
||||||
if err == nil {
|
|
||||||
msg = fmt.Sprintf("资产「%v」存活状态检测完成,存活「%v」,耗时「%v」", asset.Name, active, elapsed)
|
|
||||||
} else {
|
|
||||||
msg = fmt.Sprintf("资产「%v」存活状态检测完成,存活「%v」,耗时「%v」,原因: %v", asset.Name, active, elapsed, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = r.jobService.assetRepository.UpdateActiveById(active, asset.ID)
|
|
||||||
log.Infof(msg)
|
|
||||||
msgChan <- msg
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
var message = ""
|
|
||||||
for i := 0; i < len(assets); i++ {
|
|
||||||
message += <-msgChan + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = r.jobService.jobRepository.UpdateLastUpdatedById(r.ID)
|
|
||||||
jobLog := model.JobLog{
|
|
||||||
ID: utils.UUID(),
|
|
||||||
JobId: r.ID,
|
|
||||||
Timestamp: utils.NowJsonTime(),
|
|
||||||
Message: message,
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = r.jobService.jobLogRepository.Create(&jobLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ShellJob struct {
|
|
||||||
ID string
|
|
||||||
Mode string
|
|
||||||
ResourceIds string
|
|
||||||
Metadata string
|
|
||||||
jobService *JobService
|
|
||||||
}
|
|
||||||
|
|
||||||
type MetadataShell struct {
|
|
||||||
Shell string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ShellJob) Run() {
|
|
||||||
if r.ID == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var assets []model.Asset
|
|
||||||
if r.Mode == constant.JobModeAll {
|
|
||||||
assets, _ = r.jobService.assetRepository.FindByProtocol("ssh")
|
|
||||||
} else {
|
|
||||||
assets, _ = r.jobService.assetRepository.FindByProtocolAndIds("ssh", strings.Split(r.ResourceIds, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(assets) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var metadataShell MetadataShell
|
|
||||||
err := json.Unmarshal([]byte(r.Metadata), &metadataShell)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("JSON数据解析失败 %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
msgChan := make(chan string)
|
|
||||||
for i := range assets {
|
|
||||||
asset, err := r.jobService.assetRepository.FindByIdAndDecrypt(assets[i].ID)
|
|
||||||
if err != nil {
|
|
||||||
msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询数据异常「%v」", assets[i].Name, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
username = asset.Username
|
|
||||||
password = asset.Password
|
|
||||||
privateKey = asset.PrivateKey
|
|
||||||
passphrase = asset.Passphrase
|
|
||||||
ip = asset.IP
|
|
||||||
port = asset.Port
|
|
||||||
)
|
|
||||||
|
|
||||||
if asset.AccountType == "credential" {
|
|
||||||
credential, err := r.jobService.credentialRepository.FindByIdAndDecrypt(asset.CredentialId)
|
|
||||||
if err != nil {
|
|
||||||
msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询授权凭证数据异常「%v」", assets[i].Name, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if credential.Type == constant.Custom {
|
|
||||||
username = credential.Username
|
|
||||||
password = credential.Password
|
|
||||||
} else {
|
|
||||||
username = credential.Username
|
|
||||||
privateKey = credential.PrivateKey
|
|
||||||
passphrase = credential.Passphrase
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
t1 := time.Now()
|
|
||||||
result, err := exec(metadataShell.Shell, asset.AccessGatewayId, ip, port, username, password, privateKey, passphrase)
|
|
||||||
elapsed := time.Since(t1)
|
|
||||||
var msg string
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(gorm.ErrRecordNotFound, err) {
|
|
||||||
msg = fmt.Sprintf("资产「%v」Shell执行失败,请检查资产所关联接入网关是否存在,耗时「%v」", asset.Name, elapsed)
|
|
||||||
} else {
|
|
||||||
msg = fmt.Sprintf("资产「%v」Shell执行失败,错误内容为:「%v」,耗时「%v」", asset.Name, err.Error(), elapsed)
|
|
||||||
}
|
|
||||||
log.Infof(msg)
|
|
||||||
} else {
|
|
||||||
msg = fmt.Sprintf("资产「%v」Shell执行成功,返回值「%v」,耗时「%v」", asset.Name, result, elapsed)
|
|
||||||
log.Infof(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgChan <- msg
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
var message = ""
|
|
||||||
for i := 0; i < len(assets); i++ {
|
|
||||||
message += <-msgChan + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = r.jobService.jobRepository.UpdateLastUpdatedById(r.ID)
|
|
||||||
jobLog := model.JobLog{
|
|
||||||
ID: utils.UUID(),
|
|
||||||
JobId: r.ID,
|
|
||||||
Timestamp: utils.NowJsonTime(),
|
|
||||||
Message: message,
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = r.jobService.jobLogRepository.Create(&jobLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
func exec(shell, accessGatewayId, ip string, port int, username, password, privateKey, passphrase string) (string, error) {
|
|
||||||
if accessGatewayId != "" && accessGatewayId != "-" {
|
|
||||||
g, err := accessGatewayService.GetGatewayAndReconnectById(accessGatewayId)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
uuid := utils.UUID()
|
|
||||||
exposedIP, exposedPort, err := g.OpenSshTunnel(uuid, ip, port)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer g.CloseSshTunnel(uuid)
|
|
||||||
return ExecCommandBySSH(shell, exposedIP, exposedPort, username, password, privateKey, passphrase)
|
|
||||||
} else {
|
|
||||||
return ExecCommandBySSH(shell, ip, port, username, password, privateKey, passphrase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExecCommandBySSH(cmd, ip string, port int, username, password, privateKey, passphrase string) (result string, err error) {
|
|
||||||
sshClient, err := term.NewSshClient(ip, port, username, password, privateKey, passphrase)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
session, err := sshClient.NewSession()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer session.Close()
|
|
||||||
//执行远程命令
|
|
||||||
combo, err := session.CombinedOutput(cmd)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(combo), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r JobService) ExecJobById(id string) (err error) {
|
|
||||||
job, err := r.jobRepository.FindById(id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
j, err := getJob(&job, &r)
|
j, err := getJob(&job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -298,8 +72,8 @@ func (r JobService) ExecJobById(id string) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobService) InitJob() error {
|
func (r jobService) InitJob() error {
|
||||||
jobs, _ := r.jobRepository.FindAll()
|
jobs, _ := repository.JobRepository.FindAll(context.TODO())
|
||||||
if len(jobs) == 0 {
|
if len(jobs) == 0 {
|
||||||
job := model.Job{
|
job := model.Job{
|
||||||
ID: utils.UUID(),
|
ID: utils.UUID(),
|
||||||
@ -311,7 +85,7 @@ func (r JobService) InitJob() error {
|
|||||||
Created: utils.NowJsonTime(),
|
Created: utils.NowJsonTime(),
|
||||||
Updated: utils.NowJsonTime(),
|
Updated: utils.NowJsonTime(),
|
||||||
}
|
}
|
||||||
if err := r.jobRepository.Create(&job); err != nil {
|
if err := repository.JobRepository.Create(context.TODO(), &job); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Debugf("创建计划任务「%v」cron「%v」", job.Name, job.Cron)
|
log.Debugf("创建计划任务「%v」cron「%v」", job.Name, job.Cron)
|
||||||
@ -329,10 +103,10 @@ func (r JobService) InitJob() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobService) Create(o *model.Job) (err error) {
|
func (r jobService) Create(o *model.Job) (err error) {
|
||||||
|
|
||||||
if o.Status == constant.JobStatusRunning {
|
if o.Status == constant.JobStatusRunning {
|
||||||
j, err := getJob(o, &r)
|
j, err := getJob(o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -343,11 +117,11 @@ func (r JobService) Create(o *model.Job) (err error) {
|
|||||||
o.CronJobId = int(jobId)
|
o.CronJobId = int(jobId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.jobRepository.Create(o)
|
return repository.JobRepository.Create(context.TODO(), o)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobService) DeleteJobById(id string) error {
|
func (r jobService) DeleteJobById(id string) error {
|
||||||
job, err := r.jobRepository.FindById(id)
|
job, err := repository.JobRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -356,11 +130,11 @@ func (r JobService) DeleteJobById(id string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return r.jobRepository.DeleteJobById(id)
|
return repository.JobRepository.DeleteJobById(context.TODO(), id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r JobService) UpdateById(m *model.Job) error {
|
func (r jobService) UpdateById(m *model.Job) error {
|
||||||
if err := r.jobRepository.UpdateById(m); err != nil {
|
if err := repository.JobRepository.UpdateById(context.TODO(), m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
78
server/service/job_check_asset_status.go
Normal file
78
server/service/job_check_asset_status.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/log"
|
||||||
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CheckAssetStatusJob struct {
|
||||||
|
ID string
|
||||||
|
Mode string
|
||||||
|
ResourceIds string
|
||||||
|
Metadata string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r CheckAssetStatusJob) Run() {
|
||||||
|
if r.ID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var assets []model.Asset
|
||||||
|
if r.Mode == constant.JobModeAll {
|
||||||
|
assets, _ = repository.AssetRepository.FindAll(context.TODO())
|
||||||
|
} else {
|
||||||
|
assets, _ = repository.AssetRepository.FindByIds(context.TODO(), strings.Split(r.ResourceIds, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(assets) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msgChan := make(chan string)
|
||||||
|
for i := range assets {
|
||||||
|
asset := assets[i]
|
||||||
|
go func() {
|
||||||
|
t1 := time.Now()
|
||||||
|
var (
|
||||||
|
msg string
|
||||||
|
ip = asset.IP
|
||||||
|
port = asset.Port
|
||||||
|
)
|
||||||
|
active, err := AssetService.CheckStatus(asset.AccessGatewayId, ip, port)
|
||||||
|
|
||||||
|
elapsed := time.Since(t1)
|
||||||
|
if err == nil {
|
||||||
|
msg = fmt.Sprintf("资产「%v」存活状态检测完成,存活「%v」,耗时「%v」", asset.Name, active, elapsed)
|
||||||
|
} else {
|
||||||
|
msg = fmt.Sprintf("资产「%v」存活状态检测完成,存活「%v」,耗时「%v」,原因: %v", asset.Name, active, elapsed, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = repository.AssetRepository.UpdateActiveById(context.TODO(), active, asset.ID)
|
||||||
|
log.Infof(msg)
|
||||||
|
msgChan <- msg
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = ""
|
||||||
|
for i := 0; i < len(assets); i++ {
|
||||||
|
message += <-msgChan + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = repository.JobRepository.UpdateLastUpdatedById(context.TODO(), r.ID)
|
||||||
|
jobLog := model.JobLog{
|
||||||
|
ID: utils.UUID(),
|
||||||
|
JobId: r.ID,
|
||||||
|
Timestamp: utils.NowJsonTime(),
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = repository.JobLogRepository.Create(context.TODO(), &jobLog)
|
||||||
|
}
|
163
server/service/job_exec_shell.go
Normal file
163
server/service/job_exec_shell.go
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/log"
|
||||||
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/term"
|
||||||
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShellJob struct {
|
||||||
|
ID string
|
||||||
|
Mode string
|
||||||
|
ResourceIds string
|
||||||
|
Metadata string
|
||||||
|
}
|
||||||
|
|
||||||
|
type MetadataShell struct {
|
||||||
|
Shell string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r ShellJob) Run() {
|
||||||
|
if r.ID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var assets []model.Asset
|
||||||
|
if r.Mode == constant.JobModeAll {
|
||||||
|
assets, _ = repository.AssetRepository.FindByProtocol(context.TODO(), "ssh")
|
||||||
|
} else {
|
||||||
|
assets, _ = repository.AssetRepository.FindByProtocolAndIds(context.TODO(), "ssh", strings.Split(r.ResourceIds, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(assets) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadataShell MetadataShell
|
||||||
|
err := json.Unmarshal([]byte(r.Metadata), &metadataShell)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("JSON数据解析失败 %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msgChan := make(chan string)
|
||||||
|
for i := range assets {
|
||||||
|
asset, err := AssetService.FindByIdAndDecrypt(context.TODO(), assets[i].ID)
|
||||||
|
if err != nil {
|
||||||
|
msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询数据异常「%v」", assets[i].Name, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
username = asset.Username
|
||||||
|
password = asset.Password
|
||||||
|
privateKey = asset.PrivateKey
|
||||||
|
passphrase = asset.Passphrase
|
||||||
|
ip = asset.IP
|
||||||
|
port = asset.Port
|
||||||
|
)
|
||||||
|
|
||||||
|
if asset.AccountType == "credential" {
|
||||||
|
credential, err := CredentialService.FindByIdAndDecrypt(context.TODO(), asset.CredentialId)
|
||||||
|
if err != nil {
|
||||||
|
msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询授权凭证数据异常「%v」", assets[i].Name, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if credential.Type == constant.Custom {
|
||||||
|
username = credential.Username
|
||||||
|
password = credential.Password
|
||||||
|
} else {
|
||||||
|
username = credential.Username
|
||||||
|
privateKey = credential.PrivateKey
|
||||||
|
passphrase = credential.Passphrase
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
t1 := time.Now()
|
||||||
|
result, err := exec(metadataShell.Shell, asset.AccessGatewayId, ip, port, username, password, privateKey, passphrase)
|
||||||
|
elapsed := time.Since(t1)
|
||||||
|
var msg string
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(gorm.ErrRecordNotFound, err) {
|
||||||
|
msg = fmt.Sprintf("资产「%v」Shell执行失败,请检查资产所关联接入网关是否存在,耗时「%v」", asset.Name, elapsed)
|
||||||
|
} else {
|
||||||
|
msg = fmt.Sprintf("资产「%v」Shell执行失败,错误内容为:「%v」,耗时「%v」", asset.Name, err.Error(), elapsed)
|
||||||
|
}
|
||||||
|
log.Infof(msg)
|
||||||
|
} else {
|
||||||
|
msg = fmt.Sprintf("资产「%v」Shell执行成功,返回值「%v」,耗时「%v」", asset.Name, result, elapsed)
|
||||||
|
log.Infof(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgChan <- msg
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = ""
|
||||||
|
for i := 0; i < len(assets); i++ {
|
||||||
|
message += <-msgChan + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = repository.JobRepository.UpdateLastUpdatedById(context.TODO(), r.ID)
|
||||||
|
jobLog := model.JobLog{
|
||||||
|
ID: utils.UUID(),
|
||||||
|
JobId: r.ID,
|
||||||
|
Timestamp: utils.NowJsonTime(),
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = repository.JobLogRepository.Create(context.TODO(), &jobLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exec(shell, accessGatewayId, ip string, port int, username, password, privateKey, passphrase string) (string, error) {
|
||||||
|
if accessGatewayId != "" && accessGatewayId != "-" {
|
||||||
|
g, err := GatewayService.GetGatewayAndReconnectById(accessGatewayId)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
uuid := utils.UUID()
|
||||||
|
exposedIP, exposedPort, err := g.OpenSshTunnel(uuid, ip, port)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer g.CloseSshTunnel(uuid)
|
||||||
|
return ExecCommandBySSH(shell, exposedIP, exposedPort, username, password, privateKey, passphrase)
|
||||||
|
} else {
|
||||||
|
return ExecCommandBySSH(shell, ip, port, username, password, privateKey, passphrase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecCommandBySSH(cmd, ip string, port int, username, password, privateKey, passphrase string) (result string, err error) {
|
||||||
|
sshClient, err := term.NewSshClient(ip, port, username, password, privateKey, passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := sshClient.NewSession()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = session.Close()
|
||||||
|
}()
|
||||||
|
//执行远程命令
|
||||||
|
combo, err := session.CombinedOutput(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(combo), nil
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/smtp"
|
"net/smtp"
|
||||||
|
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
@ -10,16 +11,11 @@ import (
|
|||||||
"github.com/jordan-wright/email"
|
"github.com/jordan-wright/email"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MailService struct {
|
type mailService struct {
|
||||||
propertyRepository *repository.PropertyRepository
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMailService(propertyRepository *repository.PropertyRepository) *MailService {
|
func (r mailService) SendMail(to, subject, text string) {
|
||||||
return &MailService{propertyRepository: propertyRepository}
|
propertiesMap := repository.PropertyRepository.FindAllMap(context.TODO())
|
||||||
}
|
|
||||||
|
|
||||||
func (r MailService) SendMail(to, subject, text string) {
|
|
||||||
propertiesMap := r.propertyRepository.FindAllMap()
|
|
||||||
host := propertiesMap[constant.MailHost]
|
host := propertiesMap[constant.MailHost]
|
||||||
port := propertiesMap[constant.MailPort]
|
port := propertiesMap[constant.MailPort]
|
||||||
username := propertiesMap[constant.MailUsername]
|
username := propertiesMap[constant.MailUsername]
|
||||||
|
@ -1,28 +1,31 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"next-terminal/server/env"
|
||||||
"next-terminal/server/guacd"
|
"next-terminal/server/guacd"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
"next-terminal/server/repository"
|
"next-terminal/server/repository"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PropertyService struct {
|
type propertyService struct {
|
||||||
propertyRepository *repository.PropertyRepository
|
baseService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPropertyService(propertyRepository *repository.PropertyRepository) *PropertyService {
|
func (service propertyService) InitProperties() error {
|
||||||
return &PropertyService{propertyRepository: propertyRepository}
|
propertyMap := repository.PropertyRepository.FindAllMap(context.TODO())
|
||||||
}
|
|
||||||
|
|
||||||
func (r PropertyService) InitProperties() error {
|
|
||||||
propertyMap := r.propertyRepository.FindAllMap()
|
|
||||||
|
|
||||||
if len(propertyMap[guacd.EnableRecording]) == 0 {
|
if len(propertyMap[guacd.EnableRecording]) == 0 {
|
||||||
property := model.Property{
|
property := model.Property{
|
||||||
Name: guacd.EnableRecording,
|
Name: guacd.EnableRecording,
|
||||||
Value: "true",
|
Value: "true",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,7 +35,7 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.CreateRecordingPath,
|
Name: guacd.CreateRecordingPath,
|
||||||
Value: "true",
|
Value: "true",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,7 +45,7 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.FontName,
|
Name: guacd.FontName,
|
||||||
Value: "menlo",
|
Value: "menlo",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,7 +55,7 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.FontSize,
|
Name: guacd.FontSize,
|
||||||
Value: "12",
|
Value: "12",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,7 +65,7 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.ColorScheme,
|
Name: guacd.ColorScheme,
|
||||||
Value: "gray-black",
|
Value: "gray-black",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,7 +75,7 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.EnableWallpaper,
|
Name: guacd.EnableWallpaper,
|
||||||
Value: "false",
|
Value: "false",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,7 +85,7 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.EnableTheming,
|
Name: guacd.EnableTheming,
|
||||||
Value: "false",
|
Value: "false",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,7 +95,7 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.EnableFontSmoothing,
|
Name: guacd.EnableFontSmoothing,
|
||||||
Value: "false",
|
Value: "false",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,7 +105,7 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.EnableFullWindowDrag,
|
Name: guacd.EnableFullWindowDrag,
|
||||||
Value: "false",
|
Value: "false",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +115,7 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.EnableDesktopComposition,
|
Name: guacd.EnableDesktopComposition,
|
||||||
Value: "false",
|
Value: "false",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,7 +125,7 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.EnableMenuAnimations,
|
Name: guacd.EnableMenuAnimations,
|
||||||
Value: "false",
|
Value: "false",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,7 +135,7 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.DisableBitmapCaching,
|
Name: guacd.DisableBitmapCaching,
|
||||||
Value: "false",
|
Value: "false",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,39 +145,71 @@ func (r PropertyService) InitProperties() error {
|
|||||||
Name: guacd.DisableOffscreenCaching,
|
Name: guacd.DisableOffscreenCaching,
|
||||||
Value: "false",
|
Value: "false",
|
||||||
}
|
}
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(propertyMap[guacd.DisableGlyphCaching]) == 0 {
|
if len(propertyMap[guacd.DisableGlyphCaching]) > 0 {
|
||||||
property := model.Property{
|
if err := repository.PropertyRepository.DeleteByName(context.TODO(), guacd.DisableGlyphCaching); err != nil {
|
||||||
Name: guacd.DisableGlyphCaching,
|
|
||||||
Value: "true",
|
|
||||||
}
|
|
||||||
if err := r.propertyRepository.Create(&property); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r PropertyService) DeleteDeprecatedProperty() error {
|
func (service propertyService) DeleteDeprecatedProperty() error {
|
||||||
propertyMap := r.propertyRepository.FindAllMap()
|
propertyMap := repository.PropertyRepository.FindAllMap(context.TODO())
|
||||||
if propertyMap[guacd.EnableDrive] != "" {
|
if propertyMap[guacd.EnableDrive] != "" {
|
||||||
if err := r.propertyRepository.DeleteByName(guacd.DriveName); err != nil {
|
if err := repository.PropertyRepository.DeleteByName(context.TODO(), guacd.DriveName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if propertyMap[guacd.DrivePath] != "" {
|
if propertyMap[guacd.DrivePath] != "" {
|
||||||
if err := r.propertyRepository.DeleteByName(guacd.DrivePath); err != nil {
|
if err := repository.PropertyRepository.DeleteByName(context.TODO(), guacd.DrivePath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if propertyMap[guacd.DriveName] != "" {
|
if propertyMap[guacd.DriveName] != "" {
|
||||||
if err := r.propertyRepository.DeleteByName(guacd.DriveName); err != nil {
|
if err := repository.PropertyRepository.DeleteByName(context.TODO(), guacd.DriveName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service propertyService) Update(item map[string]interface{}) error {
|
||||||
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := service.Context(tx)
|
||||||
|
for key := range item {
|
||||||
|
value := fmt.Sprintf("%v", item[key])
|
||||||
|
if value == "" {
|
||||||
|
value = "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
property := model.Property{
|
||||||
|
Name: key,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == "enable-ldap" && value == "false" {
|
||||||
|
if err := UserService.DeleteALlLdapUser(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := repository.PropertyRepository.FindByName(c, key)
|
||||||
|
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
if err := repository.PropertyRepository.Create(c, &property); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := repository.PropertyRepository.UpdateByName(c, &property, key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
32
server/service/security.go
Normal file
32
server/service/security.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"next-terminal/server/global/security"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type securityService struct{}
|
||||||
|
|
||||||
|
func (service securityService) ReloadAccessSecurity() error {
|
||||||
|
rules, err := repository.SecurityRepository.FindAll(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(rules) > 0 {
|
||||||
|
// 先清空
|
||||||
|
security.GlobalSecurityManager.Clear()
|
||||||
|
// 再添加到全局的安全管理器中
|
||||||
|
for i := 0; i < len(rules); i++ {
|
||||||
|
rule := &security.Security{
|
||||||
|
ID: rules[i].ID,
|
||||||
|
IP: rules[i].IP,
|
||||||
|
Rule: rules[i].Rule,
|
||||||
|
Priority: rules[i].Priority,
|
||||||
|
}
|
||||||
|
security.GlobalSecurityManager.Add <- rule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,45 +1,55 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"next-terminal/server/config"
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/env"
|
||||||
|
"next-terminal/server/global/session"
|
||||||
|
"next-terminal/server/guacd"
|
||||||
|
"next-terminal/server/log"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
"next-terminal/server/repository"
|
"next-terminal/server/repository"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SessionService struct {
|
type sessionService struct {
|
||||||
sessionRepository *repository.SessionRepository
|
baseService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSessionService(sessionRepository *repository.SessionRepository) *SessionService {
|
func (service sessionService) FixSessionState() error {
|
||||||
return &SessionService{sessionRepository: sessionRepository}
|
sessions, err := repository.SessionRepository.FindByStatus(context.TODO(), constant.Connected)
|
||||||
}
|
|
||||||
|
|
||||||
func (r SessionService) FixSessionState() error {
|
|
||||||
sessions, err := r.sessionRepository.FindByStatus(constant.Connected)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sessions) > 0 {
|
if len(sessions) > 0 {
|
||||||
for i := range sessions {
|
for i := range sessions {
|
||||||
session := model.Session{
|
s := model.Session{
|
||||||
Status: constant.Disconnected,
|
Status: constant.Disconnected,
|
||||||
DisconnectedTime: utils.NowJsonTime(),
|
DisconnectedTime: utils.NowJsonTime(),
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = r.sessionRepository.UpdateById(&session, sessions[i].ID)
|
_ = repository.SessionRepository.UpdateById(context.TODO(), &s, sessions[i].ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionService) EmptyPassword() error {
|
func (service sessionService) EmptyPassword() error {
|
||||||
return r.sessionRepository.EmptyPassword()
|
return repository.SessionRepository.EmptyPassword(context.TODO())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionService) ClearOfflineSession() error {
|
func (service sessionService) ClearOfflineSession() error {
|
||||||
sessions, err := r.sessionRepository.FindByStatus(constant.Disconnected)
|
sessions, err := repository.SessionRepository.FindByStatus(context.TODO(), constant.Disconnected)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -47,11 +57,11 @@ func (r SessionService) ClearOfflineSession() error {
|
|||||||
for i := range sessions {
|
for i := range sessions {
|
||||||
sessionIds = append(sessionIds, sessions[i].ID)
|
sessionIds = append(sessionIds, sessions[i].ID)
|
||||||
}
|
}
|
||||||
return r.sessionRepository.DeleteByIds(sessionIds)
|
return repository.SessionRepository.DeleteByIds(context.TODO(), sessionIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SessionService) ReviewedAll() error {
|
func (service sessionService) ReviewedAll() error {
|
||||||
sessions, err := r.sessionRepository.FindAllUnReviewed()
|
sessions, err := repository.SessionRepository.FindAllUnReviewed(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -60,13 +70,13 @@ func (r SessionService) ReviewedAll() error {
|
|||||||
for i := range sessions {
|
for i := range sessions {
|
||||||
sessionIds = append(sessionIds, sessions[i].ID)
|
sessionIds = append(sessionIds, sessions[i].ID)
|
||||||
if i >= 100 && i%100 == 0 {
|
if i >= 100 && i%100 == 0 {
|
||||||
if err := r.sessionRepository.UpdateReadByIds(true, sessionIds); err != nil {
|
if err := repository.SessionRepository.UpdateReadByIds(context.TODO(), true, sessionIds); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sessionIds = nil
|
sessionIds = nil
|
||||||
} else {
|
} else {
|
||||||
if i == total-1 {
|
if i == total-1 {
|
||||||
if err := r.sessionRepository.UpdateReadByIds(true, sessionIds); err != nil {
|
if err := repository.SessionRepository.UpdateReadByIds(context.TODO(), true, sessionIds); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,3 +85,272 @@ func (r SessionService) ReviewedAll() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mutex sync.Mutex
|
||||||
|
|
||||||
|
func (service sessionService) CloseSessionById(sessionId string, code int, reason string) {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||||
|
if nextSession != nil {
|
||||||
|
log.Debugf("[%v] 会话关闭,原因:%v", sessionId, reason)
|
||||||
|
service.WriteCloseMessage(nextSession.WebSocket, nextSession.Mode, code, reason)
|
||||||
|
|
||||||
|
if nextSession.Observer != nil {
|
||||||
|
obs := nextSession.Observer.All()
|
||||||
|
for _, ob := range obs {
|
||||||
|
service.WriteCloseMessage(ob.WebSocket, ob.Mode, code, reason)
|
||||||
|
log.Debugf("[%v] 强制踢出会话的观察者: %v", sessionId, ob.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session.GlobalSessionManager.Del <- sessionId
|
||||||
|
|
||||||
|
service.DisDBSess(sessionId, code, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service sessionService) WriteCloseMessage(ws *websocket.Conn, mode string, code int, reason string) {
|
||||||
|
switch mode {
|
||||||
|
case constant.Guacd:
|
||||||
|
if ws != nil {
|
||||||
|
err := guacd.NewInstruction("error", "", strconv.Itoa(code))
|
||||||
|
_ = ws.WriteMessage(websocket.TextMessage, []byte(err.String()))
|
||||||
|
disconnect := guacd.NewInstruction("disconnect")
|
||||||
|
_ = ws.WriteMessage(websocket.TextMessage, []byte(disconnect.String()))
|
||||||
|
}
|
||||||
|
case constant.Naive:
|
||||||
|
if ws != nil {
|
||||||
|
msg := `0` + reason
|
||||||
|
_ = ws.WriteMessage(websocket.TextMessage, []byte(msg))
|
||||||
|
}
|
||||||
|
case constant.Terminal:
|
||||||
|
// 这里是关闭观察者的ssh会话
|
||||||
|
if ws != nil {
|
||||||
|
msg := `0` + reason
|
||||||
|
_ = ws.WriteMessage(websocket.TextMessage, []byte(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service sessionService) DisDBSess(sessionId string, code int, reason string) {
|
||||||
|
_ = env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := service.Context(tx)
|
||||||
|
s, err := repository.SessionRepository.FindById(c, sessionId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Status == constant.Disconnected {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Status == constant.Connecting {
|
||||||
|
// 会话还未建立成功,无需保留数据
|
||||||
|
if err := repository.SessionRepository.DeleteById(c, sessionId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ss := model.Session{}
|
||||||
|
ss.ID = sessionId
|
||||||
|
ss.Status = constant.Disconnected
|
||||||
|
ss.DisconnectedTime = utils.NowJsonTime()
|
||||||
|
ss.Code = code
|
||||||
|
ss.Message = reason
|
||||||
|
ss.Password = "-"
|
||||||
|
ss.PrivateKey = "-"
|
||||||
|
ss.Passphrase = "-"
|
||||||
|
|
||||||
|
if err := repository.SessionRepository.UpdateById(c, &ss, sessionId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service sessionService) FindByIdAndDecrypt(c context.Context, id string) (o model.Session, err error) {
|
||||||
|
sess, err := repository.SessionRepository.FindById(c, id)
|
||||||
|
if err != nil {
|
||||||
|
return o, err
|
||||||
|
}
|
||||||
|
if err := service.Decrypt(&sess); err != nil {
|
||||||
|
return o, err
|
||||||
|
}
|
||||||
|
return sess, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service sessionService) Decrypt(item *model.Session) error {
|
||||||
|
if item.Password != "" && item.Password != "-" {
|
||||||
|
origData, err := base64.StdEncoding.DecodeString(item.Password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decryptedCBC, err := utils.AesDecryptCBC(origData, config.GlobalCfg.EncryptionPassword)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.Password = string(decryptedCBC)
|
||||||
|
}
|
||||||
|
if item.PrivateKey != "" && item.PrivateKey != "-" {
|
||||||
|
origData, err := base64.StdEncoding.DecodeString(item.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decryptedCBC, err := utils.AesDecryptCBC(origData, config.GlobalCfg.EncryptionPassword)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.PrivateKey = string(decryptedCBC)
|
||||||
|
}
|
||||||
|
if item.Passphrase != "" && item.Passphrase != "-" {
|
||||||
|
origData, err := base64.StdEncoding.DecodeString(item.Passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decryptedCBC, err := utils.AesDecryptCBC(origData, config.GlobalCfg.EncryptionPassword)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.Passphrase = string(decryptedCBC)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service sessionService) Create(clientIp, assetId, mode string, user *model.User) (*model.Session, error) {
|
||||||
|
asset, err := repository.AssetRepository.FindById(context.TODO(), assetId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
upload = "1"
|
||||||
|
download = "1"
|
||||||
|
_delete = "1"
|
||||||
|
rename = "1"
|
||||||
|
edit = "1"
|
||||||
|
fileSystem = "1"
|
||||||
|
_copy = "1"
|
||||||
|
paste = "1"
|
||||||
|
)
|
||||||
|
|
||||||
|
if asset.Owner != user.ID && constant.TypeUser == user.Type {
|
||||||
|
// 普通用户访问非自己创建的资产需要校验权限
|
||||||
|
resourceSharers, err := repository.ResourceSharerRepository.FindByResourceIdAndUserId(context.TODO(), assetId, user.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(resourceSharers) == 0 {
|
||||||
|
return nil, errors.New("您没有权限访问此资产")
|
||||||
|
}
|
||||||
|
strategyId := resourceSharers[0].StrategyId
|
||||||
|
if strategyId != "" {
|
||||||
|
strategy, err := repository.StrategyRepository.FindById(context.TODO(), strategyId)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(gorm.ErrRecordNotFound, err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
upload = strategy.Upload
|
||||||
|
download = strategy.Download
|
||||||
|
_delete = strategy.Delete
|
||||||
|
rename = strategy.Rename
|
||||||
|
edit = strategy.Edit
|
||||||
|
_copy = strategy.Copy
|
||||||
|
paste = strategy.Paste
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var storageId = ""
|
||||||
|
if constant.RDP == asset.Protocol {
|
||||||
|
attr, err := repository.AssetRepository.FindAssetAttrMapByAssetId(context.TODO(), assetId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if "true" == attr[guacd.EnableDrive] {
|
||||||
|
fileSystem = "1"
|
||||||
|
storageId = attr[guacd.DrivePath]
|
||||||
|
if storageId == "" {
|
||||||
|
storageId = user.ID
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileSystem = "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fileSystem != "1" {
|
||||||
|
fileSystem = "0"
|
||||||
|
}
|
||||||
|
if upload != "1" {
|
||||||
|
upload = "0"
|
||||||
|
}
|
||||||
|
if download != "1" {
|
||||||
|
download = "0"
|
||||||
|
}
|
||||||
|
if _delete != "1" {
|
||||||
|
_delete = "0"
|
||||||
|
}
|
||||||
|
if rename != "1" {
|
||||||
|
rename = "0"
|
||||||
|
}
|
||||||
|
if edit != "1" {
|
||||||
|
edit = "0"
|
||||||
|
}
|
||||||
|
if _copy != "1" {
|
||||||
|
_copy = "0"
|
||||||
|
}
|
||||||
|
if paste != "1" {
|
||||||
|
paste = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &model.Session{
|
||||||
|
ID: utils.UUID(),
|
||||||
|
AssetId: asset.ID,
|
||||||
|
Username: asset.Username,
|
||||||
|
Password: asset.Password,
|
||||||
|
PrivateKey: asset.PrivateKey,
|
||||||
|
Passphrase: asset.Passphrase,
|
||||||
|
Protocol: asset.Protocol,
|
||||||
|
IP: asset.IP,
|
||||||
|
Port: asset.Port,
|
||||||
|
Status: constant.NoConnect,
|
||||||
|
ClientIP: clientIp,
|
||||||
|
Mode: mode,
|
||||||
|
FileSystem: fileSystem,
|
||||||
|
Upload: upload,
|
||||||
|
Download: download,
|
||||||
|
Delete: _delete,
|
||||||
|
Rename: rename,
|
||||||
|
Edit: edit,
|
||||||
|
Copy: _copy,
|
||||||
|
Paste: paste,
|
||||||
|
StorageId: storageId,
|
||||||
|
AccessGatewayId: asset.AccessGatewayId,
|
||||||
|
Reviewed: false,
|
||||||
|
}
|
||||||
|
if constant.Anonymous != user.Type {
|
||||||
|
s.Creator = user.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.AccountType == "credential" {
|
||||||
|
credential, err := repository.CredentialRepository.FindById(context.TODO(), asset.CredentialId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if credential.Type == constant.Custom {
|
||||||
|
s.Username = credential.Username
|
||||||
|
s.Password = credential.Password
|
||||||
|
} else {
|
||||||
|
s.Username = credential.Username
|
||||||
|
s.PrivateKey = credential.PrivateKey
|
||||||
|
s.Passphrase = credential.Passphrase
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repository.SessionRepository.Create(context.TODO(), s); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"mime/multipart"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"next-terminal/server/config"
|
"next-terminal/server/config"
|
||||||
"next-terminal/server/log"
|
"next-terminal/server/log"
|
||||||
@ -12,37 +17,31 @@ import (
|
|||||||
"next-terminal/server/repository"
|
"next-terminal/server/repository"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StorageService struct {
|
type storageService struct {
|
||||||
storageRepository *repository.StorageRepository
|
|
||||||
userRepository *repository.UserRepository
|
|
||||||
propertyRepository *repository.PropertyRepository
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStorageService(storageRepository *repository.StorageRepository, userRepository *repository.UserRepository, propertyRepository *repository.PropertyRepository) *StorageService {
|
func (service storageService) InitStorages() error {
|
||||||
return &StorageService{storageRepository: storageRepository, userRepository: userRepository, propertyRepository: propertyRepository}
|
users, err := repository.UserRepository.FindAll(context.TODO())
|
||||||
}
|
|
||||||
|
|
||||||
func (r StorageService) InitStorages() error {
|
|
||||||
users, err := r.userRepository.FindAll()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i := range users {
|
for i := range users {
|
||||||
userId := users[i].ID
|
userId := users[i].ID
|
||||||
_, err := r.storageRepository.FindByOwnerIdAndDefault(userId, true)
|
_, err := repository.StorageRepository.FindByOwnerIdAndDefault(context.TODO(), userId, true)
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
err = r.CreateStorageByUser(&users[i])
|
err = service.CreateStorageByUser(&users[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drivePath := r.GetBaseDrivePath()
|
drivePath := service.GetBaseDrivePath()
|
||||||
storages, err := r.storageRepository.FindAll()
|
storages, err := repository.StorageRepository.FindAll(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -59,7 +58,7 @@ func (r StorageService) InitStorages() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !userExist {
|
if !userExist {
|
||||||
if err := r.DeleteStorageById(storage.ID, true); err != nil {
|
if err := service.DeleteStorageById(storage.ID, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,8 +75,8 @@ func (r StorageService) InitStorages() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StorageService) CreateStorageByUser(user *model.User) error {
|
func (service storageService) CreateStorageByUser(user *model.User) error {
|
||||||
drivePath := r.GetBaseDrivePath()
|
drivePath := service.GetBaseDrivePath()
|
||||||
storage := model.Storage{
|
storage := model.Storage{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
Name: user.Nickname + "的默认空间",
|
Name: user.Nickname + "的默认空间",
|
||||||
@ -92,7 +91,7 @@ func (r StorageService) CreateStorageByUser(user *model.User) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Infof("创建storage:「%v」文件夹: %v", storage.Name, storageDir)
|
log.Infof("创建storage:「%v」文件夹: %v", storage.Name, storageDir)
|
||||||
err := r.storageRepository.Create(&storage)
|
err := repository.StorageRepository.Create(context.TODO(), &storage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -109,7 +108,7 @@ type File struct {
|
|||||||
Size int64 `json:"size"`
|
Size int64 `json:"size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StorageService) Ls(drivePath, remoteDir string) ([]File, error) {
|
func (service storageService) Ls(drivePath, remoteDir string) ([]File, error) {
|
||||||
fileInfos, err := ioutil.ReadDir(path.Join(drivePath, remoteDir))
|
fileInfos, err := ioutil.ReadDir(path.Join(drivePath, remoteDir))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -132,13 +131,13 @@ func (r StorageService) Ls(drivePath, remoteDir string) ([]File, error) {
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StorageService) GetBaseDrivePath() string {
|
func (service storageService) GetBaseDrivePath() string {
|
||||||
return config.GlobalCfg.Guacd.Drive
|
return config.GlobalCfg.Guacd.Drive
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r StorageService) DeleteStorageById(id string, force bool) error {
|
func (service storageService) DeleteStorageById(id string, force bool) error {
|
||||||
drivePath := r.GetBaseDrivePath()
|
drivePath := service.GetBaseDrivePath()
|
||||||
storage, err := r.storageRepository.FindById(id)
|
storage, err := repository.StorageRepository.FindById(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil
|
return nil
|
||||||
@ -153,7 +152,136 @@ func (r StorageService) DeleteStorageById(id string, force bool) error {
|
|||||||
if err := os.RemoveAll(path.Join(drivePath, id)); err != nil {
|
if err := os.RemoveAll(path.Join(drivePath, id)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := r.storageRepository.DeleteById(id); err != nil {
|
if err := repository.StorageRepository.DeleteById(context.TODO(), id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service storageService) StorageUpload(c echo.Context, file *multipart.FileHeader, storageId string) error {
|
||||||
|
drivePath := service.GetBaseDrivePath()
|
||||||
|
storage, _ := repository.StorageRepository.FindById(context.TODO(), storageId)
|
||||||
|
if storage.LimitSize > 0 {
|
||||||
|
dirSize, err := utils.DirSize(path.Join(drivePath, storageId))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dirSize+file.Size > storage.LimitSize {
|
||||||
|
return errors.New("可用空间不足")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := file.Filename
|
||||||
|
src, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteDir := c.QueryParam("dir")
|
||||||
|
remoteFile := path.Join(remoteDir, filename)
|
||||||
|
|
||||||
|
if strings.Contains(remoteDir, "../") {
|
||||||
|
return errors.New("非法请求 :(")
|
||||||
|
}
|
||||||
|
if strings.Contains(remoteFile, "../") {
|
||||||
|
return errors.New("非法请求 :(")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断文件夹不存在时自动创建
|
||||||
|
dir := path.Join(path.Join(drivePath, storageId), remoteDir)
|
||||||
|
if !utils.FileExists(dir) {
|
||||||
|
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Destination
|
||||||
|
dst, err := os.Create(path.Join(path.Join(drivePath, storageId), remoteFile))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dst.Close()
|
||||||
|
|
||||||
|
// Copy
|
||||||
|
if _, err = io.Copy(dst, src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service storageService) StorageEdit(file string, fileContent string, storageId string) error {
|
||||||
|
drivePath := service.GetBaseDrivePath()
|
||||||
|
if strings.Contains(file, "../") {
|
||||||
|
return errors.New("非法请求 :(")
|
||||||
|
}
|
||||||
|
realFilePath := path.Join(path.Join(drivePath, storageId), file)
|
||||||
|
dstFile, err := os.OpenFile(realFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dstFile.Close()
|
||||||
|
write := bufio.NewWriter(dstFile)
|
||||||
|
if _, err := write.WriteString(fileContent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := write.Flush(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service storageService) StorageDownload(c echo.Context, remoteFile, storageId string) error {
|
||||||
|
drivePath := service.GetBaseDrivePath()
|
||||||
|
if strings.Contains(remoteFile, "../") {
|
||||||
|
return errors.New("非法请求 :(")
|
||||||
|
}
|
||||||
|
// 获取带后缀的文件名称
|
||||||
|
filenameWithSuffix := path.Base(remoteFile)
|
||||||
|
return c.Attachment(path.Join(path.Join(drivePath, storageId), remoteFile), filenameWithSuffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service storageService) StorageLs(remoteDir, storageId string) (error, []File) {
|
||||||
|
drivePath := service.GetBaseDrivePath()
|
||||||
|
if strings.Contains(remoteDir, "../") {
|
||||||
|
return errors.New("非法请求 :("), nil
|
||||||
|
}
|
||||||
|
files, err := service.Ls(path.Join(drivePath, storageId), remoteDir)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
return nil, files
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service storageService) StorageMkDir(remoteDir, storageId string) error {
|
||||||
|
drivePath := service.GetBaseDrivePath()
|
||||||
|
if strings.Contains(remoteDir, "../") {
|
||||||
|
return errors.New("非法请求 :(")
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(path.Join(path.Join(drivePath, storageId), remoteDir), os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service storageService) StorageRm(file, storageId string) error {
|
||||||
|
drivePath := service.GetBaseDrivePath()
|
||||||
|
if strings.Contains(file, "../") {
|
||||||
|
return errors.New("非法请求 :(")
|
||||||
|
}
|
||||||
|
if err := os.RemoveAll(path.Join(path.Join(drivePath, storageId), file)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service storageService) StorageRename(oldName, newName, storageId string) error {
|
||||||
|
drivePath := service.GetBaseDrivePath()
|
||||||
|
if strings.Contains(oldName, "../") {
|
||||||
|
return errors.New("非法请求 :(")
|
||||||
|
}
|
||||||
|
if strings.Contains(newName, "../") {
|
||||||
|
return errors.New("非法请求 :(")
|
||||||
|
}
|
||||||
|
if err := os.Rename(path.Join(path.Join(drivePath, storageId), oldName), path.Join(path.Join(drivePath, storageId), newName)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -1,28 +1,30 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"next-terminal/server/global/cache"
|
"errors"
|
||||||
"strings"
|
"fmt"
|
||||||
|
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/dto"
|
||||||
|
"next-terminal/server/env"
|
||||||
|
"next-terminal/server/global/cache"
|
||||||
"next-terminal/server/log"
|
"next-terminal/server/log"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
"next-terminal/server/repository"
|
"next-terminal/server/repository"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserService struct {
|
type userService struct {
|
||||||
userRepository *repository.UserRepository
|
baseService
|
||||||
loginLogRepository *repository.LoginLogRepository
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserService(userRepository *repository.UserRepository, loginLogRepository *repository.LoginLogRepository) *UserService {
|
func (service userService) InitUser() (err error) {
|
||||||
return &UserService{userRepository: userRepository, loginLogRepository: loginLogRepository}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r UserService) InitUser() (err error) {
|
users, err := repository.UserRepository.FindAll(context.TODO())
|
||||||
|
|
||||||
users, err := r.userRepository.FindAll()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -43,7 +45,7 @@ func (r UserService) InitUser() (err error) {
|
|||||||
Created: utils.NowJsonTime(),
|
Created: utils.NowJsonTime(),
|
||||||
Status: constant.StatusEnabled,
|
Status: constant.StatusEnabled,
|
||||||
}
|
}
|
||||||
if err := r.userRepository.Create(&user); err != nil {
|
if err := repository.UserRepository.Create(context.TODO(), &user); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +58,7 @@ func (r UserService) InitUser() (err error) {
|
|||||||
Type: constant.TypeAdmin,
|
Type: constant.TypeAdmin,
|
||||||
ID: users[i].ID,
|
ID: users[i].ID,
|
||||||
}
|
}
|
||||||
if err := r.userRepository.Update(&user); err != nil {
|
if err := repository.UserRepository.Update(context.TODO(), &user); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Infof("自动修正用户「%v」ID「%v」类型为管理员", users[i].Nickname, users[i].ID)
|
log.Infof("自动修正用户「%v」ID「%v」类型为管理员", users[i].Nickname, users[i].ID)
|
||||||
@ -66,20 +68,20 @@ func (r UserService) InitUser() (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserService) FixUserOnlineState() error {
|
func (service userService) FixUserOnlineState() error {
|
||||||
// 修正用户登录状态
|
// 修正用户登录状态
|
||||||
onlineUsers, err := r.userRepository.FindOnlineUsers()
|
onlineUsers, err := repository.UserRepository.FindOnlineUsers(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(onlineUsers) > 0 {
|
if len(onlineUsers) > 0 {
|
||||||
for i := range onlineUsers {
|
for i := range onlineUsers {
|
||||||
logs, err := r.loginLogRepository.FindAliveLoginLogsByUsername(onlineUsers[i].Username)
|
logs, err := repository.LoginLogRepository.FindAliveLoginLogsByUsername(context.TODO(), onlineUsers[i].Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(logs) == 0 {
|
if len(logs) == 0 {
|
||||||
if err := r.userRepository.UpdateOnlineByUsername(onlineUsers[i].Username, false); err != nil {
|
if err := repository.UserRepository.UpdateOnlineByUsername(context.TODO(), onlineUsers[i].Username, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,77 +90,75 @@ func (r UserService) FixUserOnlineState() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserService) LogoutByToken(token string) (err error) {
|
func (service userService) LogoutByToken(token string) (err error) {
|
||||||
loginLog, err := r.loginLogRepository.FindById(token)
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := service.Context(tx)
|
||||||
|
loginLog, err := repository.LoginLogRepository.FindById(c, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("登录日志「%v」获取失败", token)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
cacheKey := r.BuildCacheKeyByToken(token)
|
cache.TokenManager.Delete(token)
|
||||||
cache.GlobalCache.Delete(cacheKey)
|
|
||||||
|
|
||||||
loginLogForUpdate := &model.LoginLog{LogoutTime: utils.NowJsonTime(), ID: token}
|
loginLogForUpdate := &model.LoginLog{LogoutTime: utils.NowJsonTime(), ID: token}
|
||||||
err = r.loginLogRepository.Update(loginLogForUpdate)
|
err = repository.LoginLogRepository.Update(c, loginLogForUpdate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
loginLogs, err := r.loginLogRepository.FindAliveLoginLogsByUsername(loginLog.Username)
|
loginLogs, err := repository.LoginLogRepository.FindAliveLoginLogsByUsername(c, loginLog.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(loginLogs) == 0 {
|
if len(loginLogs) == 0 {
|
||||||
err = r.userRepository.UpdateOnlineByUsername(loginLog.Username, false)
|
err = repository.UserRepository.UpdateOnlineByUsername(c, loginLog.Username, false)
|
||||||
}
|
}
|
||||||
return
|
return err
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserService) LogoutById(id string) error {
|
func (service userService) LogoutById(c context.Context, id string) error {
|
||||||
user, err := r.userRepository.FindById(id)
|
user, err := repository.UserRepository.FindById(c, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
username := user.Username
|
username := user.Username
|
||||||
loginLogs, err := r.loginLogRepository.FindAliveLoginLogsByUsername(username)
|
loginLogs, err := repository.LoginLogRepository.FindAliveLoginLogsByUsername(c, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for j := range loginLogs {
|
for j := range loginLogs {
|
||||||
token := loginLogs[j].ID
|
token := loginLogs[j].ID
|
||||||
if err := r.LogoutByToken(token); err != nil {
|
if err := service.LogoutByToken(token); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserService) BuildCacheKeyByToken(token string) string {
|
func (service userService) OnEvicted(token string, value interface{}) {
|
||||||
cacheKey := strings.Join([]string{constant.Token, token}, ":")
|
|
||||||
return cacheKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r UserService) GetTokenFormCacheKey(cacheKey string) string {
|
if strings.HasPrefix(token, "forever") {
|
||||||
token := strings.Split(cacheKey, ":")[1]
|
log.Debugf("re gen forever token")
|
||||||
return token
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
func (r UserService) OnEvicted(key string, value interface{}) {
|
|
||||||
if strings.HasPrefix(key, constant.Token) {
|
|
||||||
token := r.GetTokenFormCacheKey(key)
|
|
||||||
log.Debugf("用户Token「%v」过期", token)
|
log.Debugf("用户Token「%v」过期", token)
|
||||||
err := r.LogoutByToken(token)
|
err := service.LogoutByToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("退出登录失败 %v", err)
|
log.Errorf("退出登录失败 %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserService) UpdateStatusById(id string, status string) error {
|
func (service userService) UpdateStatusById(id string, status string) error {
|
||||||
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := service.Context(tx)
|
||||||
|
if c.Value(constant.DB) == nil {
|
||||||
|
c = context.WithValue(c, constant.DB, env.GetDB())
|
||||||
|
}
|
||||||
if constant.StatusDisabled == status {
|
if constant.StatusDisabled == status {
|
||||||
// 将该用户下线
|
// 将该用户下线
|
||||||
if err := r.LogoutById(id); err != nil {
|
if err := service.LogoutById(c, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,18 +166,144 @@ func (r UserService) UpdateStatusById(id string, status string) error {
|
|||||||
ID: id,
|
ID: id,
|
||||||
Status: status,
|
Status: status,
|
||||||
}
|
}
|
||||||
return r.userRepository.Update(&u)
|
return repository.UserRepository.Update(c, &u)
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r UserService) DeleteLoginLogs(tokens []string) error {
|
func (service userService) ReloadToken() error {
|
||||||
for i := range tokens {
|
loginLogs, err := repository.LoginLogRepository.FindAliveLoginLogs(context.TODO())
|
||||||
token := tokens[i]
|
if err != nil {
|
||||||
if err := r.LogoutByToken(token); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := r.loginLogRepository.DeleteById(token); err != nil {
|
|
||||||
|
for i := range loginLogs {
|
||||||
|
loginLog := loginLogs[i]
|
||||||
|
token := loginLog.ID
|
||||||
|
user, err := repository.UserRepository.FindByUsername(context.TODO(), loginLog.Username)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(gorm.ErrRecordNotFound, err) {
|
||||||
|
_ = repository.LoginLogRepository.DeleteById(context.TODO(), token)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
authorization := dto.Authorization{
|
||||||
|
Token: token,
|
||||||
|
Type: constant.LoginToken,
|
||||||
|
Remember: loginLog.Remember,
|
||||||
|
User: &user,
|
||||||
|
}
|
||||||
|
|
||||||
|
if authorization.Remember {
|
||||||
|
// 记住登录有效期两周
|
||||||
|
cache.TokenManager.Set(token, authorization, cache.RememberMeExpiration)
|
||||||
|
} else {
|
||||||
|
cache.TokenManager.Set(token, authorization, cache.NotRememberExpiration)
|
||||||
|
}
|
||||||
|
log.Debugf("重新加载用户「%v」授权Token「%v」到缓存", user.Nickname, token)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service userService) CreateUser(user model.User) (err error) {
|
||||||
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := service.Context(tx)
|
||||||
|
if repository.UserRepository.ExistByUsername(c, user.Username) {
|
||||||
|
return fmt.Errorf("username %s is already used", user.Username)
|
||||||
|
}
|
||||||
|
password := user.Password
|
||||||
|
|
||||||
|
var pass []byte
|
||||||
|
if pass, err = utils.Encoder.Encode([]byte(password)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
user.Password = string(pass)
|
||||||
|
|
||||||
|
user.ID = utils.UUID()
|
||||||
|
user.Created = utils.NowJsonTime()
|
||||||
|
user.Status = constant.StatusEnabled
|
||||||
|
|
||||||
|
if err := repository.UserRepository.Create(c, &user); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = StorageService.CreateStorageByUser(&user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Mail != "" {
|
||||||
|
go MailService.SendMail(user.Mail, "[Next Terminal] 注册通知", "你好,"+user.Nickname+"。管理员为你注册了账号:"+user.Username+" 密码:"+password)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service userService) DeleteUserById(userId string) error {
|
||||||
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := service.Context(tx)
|
||||||
|
// 下线该用户
|
||||||
|
if err := service.LogoutById(c, userId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除用户
|
||||||
|
if err := repository.UserRepository.DeleteById(c, userId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除用户与用户组的关系
|
||||||
|
if err := repository.UserGroupMemberRepository.DeleteByUserId(c, userId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除用户与资产的关系
|
||||||
|
if err := repository.ResourceSharerRepository.DeleteByUserId(c, userId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除用户的默认磁盘空间
|
||||||
|
if err := StorageService.DeleteStorageById(userId, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service userService) DeleteLoginLogs(tokens []string) error {
|
||||||
|
if len(tokens) > 0 {
|
||||||
|
for _, token := range tokens {
|
||||||
|
if err := service.LogoutByToken(token); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := repository.LoginLogRepository.DeleteById(context.TODO(), token); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service userService) SaveLoginLog(clientIP, clientUserAgent string, username string, success, remember bool, id, reason string) error {
|
||||||
|
loginLog := model.LoginLog{
|
||||||
|
Username: username,
|
||||||
|
ClientIP: clientIP,
|
||||||
|
ClientUserAgent: clientUserAgent,
|
||||||
|
LoginTime: utils.NowJsonTime(),
|
||||||
|
Reason: reason,
|
||||||
|
Remember: remember,
|
||||||
|
}
|
||||||
|
if success {
|
||||||
|
loginLog.State = "1"
|
||||||
|
loginLog.ID = id
|
||||||
|
} else {
|
||||||
|
loginLog.State = "0"
|
||||||
|
loginLog.ID = utils.LongUUID()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repository.LoginLogRepository.Create(context.TODO(), &loginLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service userService) DeleteALlLdapUser(ctx context.Context) error {
|
||||||
|
return repository.UserRepository.DeleteBySource(ctx, constant.SourceLdap)
|
||||||
|
}
|
||||||
|
115
server/service/user_group.go
Normal file
115
server/service/user_group.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/env"
|
||||||
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type userGroupService struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service userGroupService) DeleteById(userGroupId string) error {
|
||||||
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := context.WithValue(context.TODO(), constant.DB, tx)
|
||||||
|
// 删除用户组
|
||||||
|
if err := repository.UserGroupRepository.DeleteById(c, userGroupId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除用户组与用户的关系
|
||||||
|
if err := repository.UserGroupMemberRepository.DeleteByUserGroupId(c, userGroupId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除用户组与资产的关系
|
||||||
|
if err := repository.ResourceSharerRepository.DeleteByUserGroupId(c, userGroupId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service userGroupService) Create(name string, members []string) (model.UserGroup, error) {
|
||||||
|
var err error
|
||||||
|
_, err = repository.UserGroupRepository.FindByName(context.TODO(), name)
|
||||||
|
if err == nil {
|
||||||
|
return model.UserGroup{}, constant.ErrNameAlreadyUsed
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.Is(gorm.ErrRecordNotFound, err) {
|
||||||
|
return model.UserGroup{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userGroupId := utils.UUID()
|
||||||
|
userGroup := model.UserGroup{
|
||||||
|
ID: userGroupId,
|
||||||
|
Created: utils.NowJsonTime(),
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
return userGroup, env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := context.WithValue(context.TODO(), constant.DB, tx)
|
||||||
|
if err := repository.UserGroupRepository.Create(c, &userGroup); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(members) > 0 {
|
||||||
|
for _, member := range members {
|
||||||
|
userGroupMember := model.UserGroupMember{
|
||||||
|
ID: utils.Sign([]string{userGroupId, member}),
|
||||||
|
UserId: member,
|
||||||
|
UserGroupId: userGroupId,
|
||||||
|
}
|
||||||
|
if err := repository.UserGroupMemberRepository.Create(c, &userGroupMember); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service userGroupService) Update(userGroupId string, name string, members []string) (err error) {
|
||||||
|
var userGroup model.UserGroup
|
||||||
|
userGroup, err = repository.UserGroupRepository.FindByName(context.TODO(), name)
|
||||||
|
if err == nil && userGroup.ID != userGroupId {
|
||||||
|
return constant.ErrNameAlreadyUsed
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.Is(gorm.ErrRecordNotFound, err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
c := context.WithValue(context.TODO(), constant.DB, tx)
|
||||||
|
userGroup := model.UserGroup{
|
||||||
|
ID: userGroupId,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
if err := repository.UserGroupRepository.Update(c, &userGroup); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := repository.UserGroupMemberRepository.DeleteByUserGroupId(c, userGroupId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(members) > 0 {
|
||||||
|
for _, member := range members {
|
||||||
|
userGroupMember := model.UserGroupMember{
|
||||||
|
ID: utils.Sign([]string{userGroupId, member}),
|
||||||
|
UserId: member,
|
||||||
|
UserGroupId: userGroupId,
|
||||||
|
}
|
||||||
|
if err := repository.UserGroupMemberRepository.Create(c, &userGroupMember); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
17
server/service/var.go
Normal file
17
server/service/var.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
var (
|
||||||
|
AssetService = new(assetService)
|
||||||
|
BackupService = new(backupService)
|
||||||
|
CredentialService = new(credentialService)
|
||||||
|
GatewayService = new(gatewayService)
|
||||||
|
JobService = new(jobService)
|
||||||
|
MailService = new(mailService)
|
||||||
|
PropertyService = new(propertyService)
|
||||||
|
SecurityService = new(securityService)
|
||||||
|
SessionService = new(sessionService)
|
||||||
|
StorageService = new(storageService)
|
||||||
|
UserService = new(userService)
|
||||||
|
UserGroupService = new(userGroupService)
|
||||||
|
AccessTokenService = new(accessTokenService)
|
||||||
|
)
|
146
server/sshd/sshd.go
Normal file
146
server/sshd/sshd.go
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
package sshd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"next-terminal/server/config"
|
||||||
|
"next-terminal/server/constant"
|
||||||
|
"next-terminal/server/global/security"
|
||||||
|
"next-terminal/server/log"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
|
"next-terminal/server/utils"
|
||||||
|
|
||||||
|
"github.com/gliderlabs/ssh"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Sshd struct {
|
||||||
|
gui *Gui
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
gui := &Gui{}
|
||||||
|
sshd := &Sshd{
|
||||||
|
gui: gui,
|
||||||
|
}
|
||||||
|
go sshd.Serve()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sshd Sshd) passwordAuth(ctx ssh.Context, pass string) bool {
|
||||||
|
username := ctx.User()
|
||||||
|
remoteAddr := strings.Split(ctx.RemoteAddr().String(), ":")[0]
|
||||||
|
user, err := repository.UserRepository.FindByUsername(context.TODO(), username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// 保存登录日志
|
||||||
|
_ = service.UserService.SaveLoginLog(remoteAddr, "terminal", username, false, false, "", "账号或密码不正确")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := utils.Encoder.Match([]byte(user.Password), []byte(pass)); err != nil {
|
||||||
|
// 保存登录日志
|
||||||
|
_ = service.UserService.SaveLoginLog(remoteAddr, "terminal", username, false, false, "", "账号或密码不正确")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sshd Sshd) connCallback(ctx ssh.Context, conn net.Conn) net.Conn {
|
||||||
|
securities := security.GlobalSecurityManager.Values()
|
||||||
|
if len(securities) == 0 {
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := strings.Split(conn.RemoteAddr().String(), ":")[0]
|
||||||
|
|
||||||
|
for _, s := range securities {
|
||||||
|
if strings.Contains(s.IP, "/") {
|
||||||
|
// CIDR
|
||||||
|
_, ipNet, err := net.ParseCIDR(s.IP)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !ipNet.Contains(net.ParseIP(ip)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if strings.Contains(s.IP, "-") {
|
||||||
|
// 范围段
|
||||||
|
split := strings.Split(s.IP, "-")
|
||||||
|
if len(split) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
start := split[0]
|
||||||
|
end := split[1]
|
||||||
|
intReqIP := utils.IpToInt(ip)
|
||||||
|
if intReqIP < utils.IpToInt(start) || intReqIP > utils.IpToInt(end) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// IP
|
||||||
|
if s.IP != ip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Rule == constant.AccessRuleAllow {
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
if s.Rule == constant.AccessRuleReject {
|
||||||
|
_, _ = conn.Write([]byte("your access request was denied :(\n"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sshd Sshd) sessionHandler(sess *ssh.Session) {
|
||||||
|
defer func() {
|
||||||
|
_ = (*sess).Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
username := (*sess).User()
|
||||||
|
remoteAddr := strings.Split((*sess).RemoteAddr().String(), ":")[0]
|
||||||
|
|
||||||
|
user, err := repository.UserRepository.FindByUsername(context.TODO(), username)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
_, _ = io.WriteString(*sess, "您输入的账户或密码不正确.\n")
|
||||||
|
} else {
|
||||||
|
_, _ = io.WriteString(*sess, err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否需要进行双因素认证
|
||||||
|
if user.TOTPSecret != "" && user.TOTPSecret != "-" {
|
||||||
|
sshd.gui.totpUI(sess, user, remoteAddr, username)
|
||||||
|
} else {
|
||||||
|
// 保存登录日志
|
||||||
|
_ = service.UserService.SaveLoginLog(remoteAddr, "terminal", username, true, false, utils.LongUUID(), "")
|
||||||
|
sshd.gui.MainUI(sess, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sshd Sshd) Serve() {
|
||||||
|
ssh.Handle(func(s ssh.Session) {
|
||||||
|
_, _ = io.WriteString(s, fmt.Sprintf(constant.Banner, constant.Version))
|
||||||
|
sshd.sessionHandler(&s)
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Printf("⇨ sshd server started on %v\n", config.GlobalCfg.Sshd.Addr)
|
||||||
|
err := ssh.ListenAndServe(
|
||||||
|
config.GlobalCfg.Sshd.Addr,
|
||||||
|
nil,
|
||||||
|
ssh.PasswordAuth(sshd.passwordAuth),
|
||||||
|
ssh.HostKeyFile(config.GlobalCfg.Sshd.Key),
|
||||||
|
ssh.WrapConn(sshd.connCallback),
|
||||||
|
)
|
||||||
|
log.Fatal(fmt.Sprintf("启动sshd服务失败: %v", err.Error()))
|
||||||
|
}
|
@ -1,16 +1,11 @@
|
|||||||
package api
|
package sshd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"next-terminal/server/api"
|
||||||
"next-terminal/server/global/security"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"next-terminal/server/config"
|
"next-terminal/server/config"
|
||||||
"next-terminal/server/constant"
|
"next-terminal/server/constant"
|
||||||
"next-terminal/server/global/cache"
|
"next-terminal/server/global/cache"
|
||||||
@ -18,99 +13,22 @@ import (
|
|||||||
"next-terminal/server/guacd"
|
"next-terminal/server/guacd"
|
||||||
"next-terminal/server/log"
|
"next-terminal/server/log"
|
||||||
"next-terminal/server/model"
|
"next-terminal/server/model"
|
||||||
|
"next-terminal/server/repository"
|
||||||
|
"next-terminal/server/service"
|
||||||
"next-terminal/server/term"
|
"next-terminal/server/term"
|
||||||
"next-terminal/server/totp"
|
"next-terminal/server/totp"
|
||||||
"next-terminal/server/utils"
|
"next-terminal/server/utils"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gliderlabs/ssh"
|
"github.com/gliderlabs/ssh"
|
||||||
"github.com/manifoldco/promptui"
|
"github.com/manifoldco/promptui"
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func sessionHandler(sess *ssh.Session) {
|
type Gui struct {
|
||||||
defer func() {
|
|
||||||
_ = (*sess).Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
username := (*sess).User()
|
|
||||||
remoteAddr := strings.Split((*sess).RemoteAddr().String(), ":")[0]
|
|
||||||
|
|
||||||
user, err := userRepository.FindByUsername(username)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
_, _ = io.WriteString(*sess, "您输入的账户或密码不正确.\n")
|
|
||||||
} else {
|
|
||||||
_, _ = io.WriteString(*sess, err.Error())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否需要进行双因素认证
|
func (gui Gui) MainUI(sess *ssh.Session, user model.User) {
|
||||||
if user.TOTPSecret != "" && user.TOTPSecret != "-" {
|
|
||||||
totpUI(sess, user, remoteAddr, username)
|
|
||||||
} else {
|
|
||||||
// 保存登录日志
|
|
||||||
_ = SaveLoginLog(remoteAddr, "terminal", username, true, false, utils.UUID(), "")
|
|
||||||
mainUI(sess, user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func totpUI(sess *ssh.Session, user model.User, remoteAddr string, username string) {
|
|
||||||
|
|
||||||
validate := func(input string) error {
|
|
||||||
if len(input) < 6 {
|
|
||||||
return errors.New("双因素认证授权码必须为6个数字")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt := promptui.Prompt{
|
|
||||||
Label: "请输入双因素认证授权码",
|
|
||||||
Validate: validate,
|
|
||||||
Mask: '*',
|
|
||||||
Stdin: *sess,
|
|
||||||
Stdout: *sess,
|
|
||||||
}
|
|
||||||
|
|
||||||
var success = false
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
result, err := prompt.Run()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Prompt failed %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
loginFailCountKey := remoteAddr + username
|
|
||||||
|
|
||||||
v, ok := cache.GlobalCache.Get(loginFailCountKey)
|
|
||||||
if !ok {
|
|
||||||
v = 1
|
|
||||||
}
|
|
||||||
count := v.(int)
|
|
||||||
if count >= 5 {
|
|
||||||
_, _ = io.WriteString(*sess, "登录失败次数过多,请等待30秒后再试\r\n")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !totp.Validate(result, user.TOTPSecret) {
|
|
||||||
count++
|
|
||||||
println(count)
|
|
||||||
cache.GlobalCache.Set(loginFailCountKey, count, time.Second*time.Duration(30))
|
|
||||||
// 保存登录日志
|
|
||||||
_ = SaveLoginLog(remoteAddr, "terminal", username, false, false, "", "双因素认证授权码不正确")
|
|
||||||
_, _ = io.WriteString(*sess, "您输入的双因素认证授权码不匹配\r\n")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
success = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if success {
|
|
||||||
// 保存登录日志
|
|
||||||
_ = SaveLoginLog(remoteAddr, "terminal", username, true, false, utils.UUID(), "")
|
|
||||||
mainUI(sess, user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mainUI(sess *ssh.Session, user model.User) {
|
|
||||||
prompt := promptui.Select{
|
prompt := promptui.Select{
|
||||||
Label: "欢迎使用 Next Terminal,请选择您要使用的功能",
|
Label: "欢迎使用 Next Terminal,请选择您要使用的功能",
|
||||||
Items: []string{"我的资产", "退出系统"},
|
Items: []string{"我的资产", "退出系统"},
|
||||||
@ -127,15 +45,15 @@ MainLoop:
|
|||||||
}
|
}
|
||||||
switch result {
|
switch result {
|
||||||
case "我的资产":
|
case "我的资产":
|
||||||
AssetUI(sess, user)
|
gui.AssetUI(sess, user)
|
||||||
case "退出系统":
|
case "退出系统":
|
||||||
break MainLoop
|
break MainLoop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssetUI(sess *ssh.Session, user model.User) {
|
func (gui Gui) AssetUI(sess *ssh.Session, user model.User) {
|
||||||
assets, err := assetRepository.FindByProtocolAndUser(constant.SSH, user)
|
assets, err := repository.AssetRepository.FindByProtocolAndUser(context.TODO(), constant.SSH, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -190,17 +108,16 @@ AssetUILoop:
|
|||||||
case "quit":
|
case "quit":
|
||||||
break AssetUILoop
|
break AssetUILoop
|
||||||
default:
|
default:
|
||||||
if err := createSession(sess, assets[i].ID, user.ID); err != nil {
|
if err := gui.createSession(sess, chooseAssetId, user.ID); err != nil {
|
||||||
_, _ = io.WriteString(*sess, err.Error()+"\r\n")
|
_, _ = io.WriteString(*sess, err.Error()+"\r\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSession(sess *ssh.Session, assetId, creator string) (err error) {
|
func (gui Gui) createSession(sess *ssh.Session, assetId, creator string) (err error) {
|
||||||
asset, err := assetRepository.FindById(assetId)
|
asset, err := repository.AssetRepository.FindById(context.TODO(), assetId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -230,7 +147,7 @@ func createSession(sess *ssh.Session, assetId, creator string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if asset.AccountType == "credential" {
|
if asset.AccountType == "credential" {
|
||||||
credential, err := credentialRepository.FindById(asset.CredentialId)
|
credential, err := repository.CredentialRepository.FindById(context.TODO(), asset.CredentialId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -245,15 +162,15 @@ func createSession(sess *ssh.Session, assetId, creator string) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := sessionRepository.Create(s); err != nil {
|
if err := repository.SessionRepository.Create(context.TODO(), s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleAccessAsset(sess, s.ID)
|
return gui.handleAccessAsset(sess, s.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleAccessAsset(sess *ssh.Session, sessionId string) (err error) {
|
func (gui Gui) handleAccessAsset(sess *ssh.Session, sessionId string) (err error) {
|
||||||
s, err := sessionRepository.FindByIdAndDecrypt(sessionId)
|
s, err := service.SessionService.FindByIdAndDecrypt(context.TODO(), sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -268,7 +185,7 @@ func handleAccessAsset(sess *ssh.Session, sessionId string) (err error) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if s.AccessGatewayId != "" && s.AccessGatewayId != "-" {
|
if s.AccessGatewayId != "" && s.AccessGatewayId != "-" {
|
||||||
g, err := accessGatewayService.GetGatewayAndReconnectById(s.AccessGatewayId)
|
g, err := service.GatewayService.GetGatewayAndReconnectById(s.AccessGatewayId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("获取接入网关失败:" + err.Error())
|
return errors.New("获取接入网关失败:" + err.Error())
|
||||||
}
|
}
|
||||||
@ -290,7 +207,7 @@ func handleAccessAsset(sess *ssh.Session, sessionId string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
recording := ""
|
recording := ""
|
||||||
property, err := propertyRepository.FindByName(guacd.EnableRecording)
|
property, err := repository.PropertyRepository.FindByName(context.TODO(), guacd.EnableRecording)
|
||||||
if err == nil && property.Value == "true" {
|
if err == nil && property.Value == "true" {
|
||||||
recording = path.Join(config.GlobalCfg.Guacd.Recording, sessionId, "recording.cast")
|
recording = path.Join(config.GlobalCfg.Guacd.Recording, sessionId, "recording.cast")
|
||||||
}
|
}
|
||||||
@ -322,7 +239,7 @@ func handleAccessAsset(sess *ssh.Session, sessionId string) (err error) {
|
|||||||
}
|
}
|
||||||
log.Debugf("退出窗口大小监控")
|
log.Debugf("退出窗口大小监控")
|
||||||
// ==== 修改数据库中的会话状态为已断开,修复用户直接关闭窗口时会话状态不正确的问题 ====
|
// ==== 修改数据库中的会话状态为已断开,修复用户直接关闭窗口时会话状态不正确的问题 ====
|
||||||
CloseSessionById(sessionId, Normal, "用户正常退出")
|
service.SessionService.CloseSessionById(sessionId, api.Normal, "用户正常退出")
|
||||||
// ==== 修改数据库中的会话状态为已断开,修复用户直接关闭窗口时会话状态不正确的问题 ====
|
// ==== 修改数据库中的会话状态为已断开,修复用户直接关闭窗口时会话状态不正确的问题 ====
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -338,7 +255,7 @@ func handleAccessAsset(sess *ssh.Session, sessionId string) (err error) {
|
|||||||
sessionForUpdate.Reviewed = true
|
sessionForUpdate.Reviewed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := sessionRepository.UpdateById(&sessionForUpdate, sessionId); err != nil {
|
if err := repository.SessionRepository.UpdateById(context.TODO(), &sessionForUpdate, sessionId); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// ==== 修改数据库中的会话状态为已连接 ====
|
// ==== 修改数据库中的会话状态为已连接 ====
|
||||||
@ -358,168 +275,63 @@ func handleAccessAsset(sess *ssh.Session, sessionId string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==== 修改数据库中的会话状态为已断开 ====
|
// ==== 修改数据库中的会话状态为已断开 ====
|
||||||
CloseSessionById(sessionId, Normal, "用户正常退出")
|
service.SessionService.CloseSessionById(sessionId, api.Normal, "用户正常退出")
|
||||||
// ==== 修改数据库中的会话状态为已断开 ====
|
// ==== 修改数据库中的会话状态为已断开 ====
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func passwordAuth(ctx ssh.Context, pass string) bool {
|
func (gui Gui) totpUI(sess *ssh.Session, user model.User, remoteAddr string, username string) {
|
||||||
username := ctx.User()
|
|
||||||
remoteAddr := strings.Split(ctx.RemoteAddr().String(), ":")[0]
|
|
||||||
user, err := userRepository.FindByUsername(username)
|
|
||||||
|
|
||||||
if err != nil {
|
validate := func(input string) error {
|
||||||
// 保存登录日志
|
if len(input) < 6 {
|
||||||
_ = SaveLoginLog(remoteAddr, "terminal", username, false, false, "", "账号或密码不正确")
|
return errors.New("双因素认证授权码必须为6个数字")
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utils.Encoder.Match([]byte(user.Password), []byte(pass)); err != nil {
|
|
||||||
// 保存登录日志
|
|
||||||
_ = SaveLoginLog(remoteAddr, "terminal", username, false, false, "", "账号或密码不正确")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func connCallback(ctx ssh.Context, conn net.Conn) net.Conn {
|
|
||||||
securities := security.GlobalSecurityManager.Values()
|
|
||||||
if len(securities) == 0 {
|
|
||||||
return conn
|
|
||||||
}
|
|
||||||
|
|
||||||
ip := strings.Split(conn.RemoteAddr().String(), ":")[0]
|
|
||||||
|
|
||||||
for _, s := range securities {
|
|
||||||
if strings.Contains(s.IP, "/") {
|
|
||||||
// CIDR
|
|
||||||
_, ipNet, err := net.ParseCIDR(s.IP)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !ipNet.Contains(net.ParseIP(ip)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else if strings.Contains(s.IP, "-") {
|
|
||||||
// 范围段
|
|
||||||
split := strings.Split(s.IP, "-")
|
|
||||||
if len(split) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
start := split[0]
|
|
||||||
end := split[1]
|
|
||||||
intReqIP := utils.IpToInt(ip)
|
|
||||||
if intReqIP < utils.IpToInt(start) || intReqIP > utils.IpToInt(end) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// IP
|
|
||||||
if s.IP != ip {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Rule == constant.AccessRuleAllow {
|
|
||||||
return conn
|
|
||||||
}
|
|
||||||
if s.Rule == constant.AccessRuleReject {
|
|
||||||
_, _ = conn.Write([]byte("your access request was denied :(\n"))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prompt := promptui.Prompt{
|
||||||
|
Label: "请输入双因素认证授权码",
|
||||||
|
Validate: validate,
|
||||||
|
Mask: '*',
|
||||||
|
Stdin: *sess,
|
||||||
|
Stdout: *sess,
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn
|
var success = false
|
||||||
}
|
for i := 0; i < 5; i++ {
|
||||||
|
result, err := prompt.Run()
|
||||||
func Setup() {
|
|
||||||
ssh.Handle(func(s ssh.Session) {
|
|
||||||
_, _ = io.WriteString(s, fmt.Sprintf(constant.Banner, constant.Version))
|
|
||||||
defer func() {
|
|
||||||
if e, ok := recover().(error); ok {
|
|
||||||
log.Fatal(e)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
sessionHandler(&s)
|
|
||||||
})
|
|
||||||
|
|
||||||
fmt.Printf("⇨ sshd server started on %v\n", config.GlobalCfg.Sshd.Addr)
|
|
||||||
err := ssh.ListenAndServe(
|
|
||||||
config.GlobalCfg.Sshd.Addr,
|
|
||||||
nil,
|
|
||||||
ssh.PasswordAuth(passwordAuth),
|
|
||||||
ssh.HostKeyFile(config.GlobalCfg.Sshd.Key),
|
|
||||||
ssh.WrapConn(connCallback),
|
|
||||||
)
|
|
||||||
log.Fatal(fmt.Sprintf("启动sshd服务失败: %v", err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if config.GlobalCfg.Sshd.Enable {
|
|
||||||
go Setup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Writer struct {
|
|
||||||
sessionId string
|
|
||||||
sess *ssh.Session
|
|
||||||
recorder *term.Recorder
|
|
||||||
rz bool
|
|
||||||
sz bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWriter(sessionId string, sess *ssh.Session, recorder *term.Recorder) *Writer {
|
|
||||||
return &Writer{sessionId: sessionId, sess: sess, recorder: recorder}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Writer) Write(p []byte) (n int, err error) {
|
|
||||||
if w.recorder != nil {
|
|
||||||
s := string(p)
|
|
||||||
if !w.sz && !w.rz {
|
|
||||||
// rz的开头字符
|
|
||||||
hexData := hex.EncodeToString(p)
|
|
||||||
if strings.Contains(hexData, "727a0d2a2a184230303030303030303030303030300d8a11") {
|
|
||||||
w.sz = true
|
|
||||||
} else if strings.Contains(hexData, "727a2077616974696e6720746f20726563656976652e2a2a184230313030303030303233626535300d8a11") {
|
|
||||||
w.rz = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.sz {
|
|
||||||
// sz 会以 OO 结尾
|
|
||||||
if "OO" == s {
|
|
||||||
w.sz = false
|
|
||||||
}
|
|
||||||
} else if w.rz {
|
|
||||||
// rz 最后会显示 Received /home/xxx
|
|
||||||
if strings.Contains(s, "Received") {
|
|
||||||
w.rz = false
|
|
||||||
// 把上传的文件名称也显示一下
|
|
||||||
err := w.recorder.WriteData(s)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
fmt.Printf("Prompt failed %v\n", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
sendObData(w.sessionId, s)
|
loginFailCountKey := remoteAddr + username
|
||||||
|
|
||||||
|
v, ok := cache.LoginFailedKeyManager.Get(loginFailCountKey)
|
||||||
|
if !ok {
|
||||||
|
v = 1
|
||||||
}
|
}
|
||||||
} else {
|
count := v.(int)
|
||||||
err := w.recorder.WriteData(s)
|
if count >= 5 {
|
||||||
if err != nil {
|
_, _ = io.WriteString(*sess, "登录失败次数过多,请等待5分钟后再试\r\n")
|
||||||
return 0, err
|
continue
|
||||||
}
|
}
|
||||||
sendObData(w.sessionId, s)
|
if !totp.Validate(result, user.TOTPSecret) {
|
||||||
|
count++
|
||||||
|
println(count)
|
||||||
|
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||||
|
// 保存登录日志
|
||||||
|
_ = service.UserService.SaveLoginLog(remoteAddr, "terminal", username, false, false, "", "双因素认证授权码不正确")
|
||||||
|
_, _ = io.WriteString(*sess, "您输入的双因素认证授权码不匹配\r\n")
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
success = true
|
||||||
return (*w.sess).Write(p)
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendObData(sessionId, s string) {
|
if success {
|
||||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
// 保存登录日志
|
||||||
if nextSession != nil {
|
_ = service.UserService.SaveLoginLog(remoteAddr, "terminal", username, true, false, utils.UUID(), "")
|
||||||
if nextSession.Observer != nil {
|
gui.MainUI(sess, user)
|
||||||
obs := nextSession.Observer.All()
|
|
||||||
for _, ob := range obs {
|
|
||||||
_ = WriteMessage(ob.WebSocket, NewMessage(Data, s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
77
server/sshd/writer.go
Normal file
77
server/sshd/writer.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package sshd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"next-terminal/server/api"
|
||||||
|
"next-terminal/server/dto"
|
||||||
|
"next-terminal/server/global/session"
|
||||||
|
"next-terminal/server/term"
|
||||||
|
|
||||||
|
"github.com/gliderlabs/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Writer struct {
|
||||||
|
sessionId string
|
||||||
|
sess *ssh.Session
|
||||||
|
recorder *term.Recorder
|
||||||
|
rz bool
|
||||||
|
sz bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriter(sessionId string, sess *ssh.Session, recorder *term.Recorder) *Writer {
|
||||||
|
return &Writer{sessionId: sessionId, sess: sess, recorder: recorder}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Write(p []byte) (n int, err error) {
|
||||||
|
if w.recorder != nil {
|
||||||
|
s := string(p)
|
||||||
|
if !w.sz && !w.rz {
|
||||||
|
// rz的开头字符
|
||||||
|
hexData := hex.EncodeToString(p)
|
||||||
|
if strings.Contains(hexData, "727a0d2a2a184230303030303030303030303030300d8a11") {
|
||||||
|
w.sz = true
|
||||||
|
} else if strings.Contains(hexData, "727a2077616974696e6720746f20726563656976652e2a2a184230313030303030303233626535300d8a11") {
|
||||||
|
w.rz = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.sz {
|
||||||
|
// sz 会以 OO 结尾
|
||||||
|
if "OO" == s {
|
||||||
|
w.sz = false
|
||||||
|
}
|
||||||
|
} else if w.rz {
|
||||||
|
// rz 最后会显示 Received /home/xxx
|
||||||
|
if strings.Contains(s, "Received") {
|
||||||
|
w.rz = false
|
||||||
|
// 把上传的文件名称也显示一下
|
||||||
|
err := w.recorder.WriteData(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
sendObData(w.sessionId, s)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := w.recorder.WriteData(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
sendObData(w.sessionId, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (*w.sess).Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendObData(sessionId, s string) {
|
||||||
|
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||||
|
if nextSession != nil {
|
||||||
|
if nextSession.Observer != nil {
|
||||||
|
obs := nextSession.Observer.All()
|
||||||
|
for _, ob := range obs {
|
||||||
|
_ = api.WriteMessage(ob.WebSocket, dto.NewMessage(api.Data, s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package task
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -10,14 +11,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Ticker struct {
|
type Ticker struct {
|
||||||
sessionRepository *repository.SessionRepository
|
|
||||||
propertyRepository *repository.PropertyRepository
|
|
||||||
loginLogRepository *repository.LoginLogRepository
|
|
||||||
jobLogRepository *repository.JobLogRepository
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTicker(sessionRepository *repository.SessionRepository, propertyRepository *repository.PropertyRepository, loginLogRepository *repository.LoginLogRepository, jobLogRepository *repository.JobLogRepository) *Ticker {
|
func NewTicker() *Ticker {
|
||||||
return &Ticker{sessionRepository: sessionRepository, propertyRepository: propertyRepository, loginLogRepository: loginLogRepository, jobLogRepository: jobLogRepository}
|
return &Ticker{}
|
||||||
|
}
|
||||||
|
func init() {
|
||||||
|
ticker := NewTicker()
|
||||||
|
ticker.SetupTicker()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Ticker) SetupTicker() {
|
func (t *Ticker) SetupTicker() {
|
||||||
@ -26,17 +27,7 @@ func (t *Ticker) SetupTicker() {
|
|||||||
unUsedSessionTicker := time.NewTicker(time.Minute * 60)
|
unUsedSessionTicker := time.NewTicker(time.Minute * 60)
|
||||||
go func() {
|
go func() {
|
||||||
for range unUsedSessionTicker.C {
|
for range unUsedSessionTicker.C {
|
||||||
sessions, _ := t.sessionRepository.FindByStatusIn([]string{constant.NoConnect, constant.Connecting})
|
t.deleteUnUsedSession()
|
||||||
if len(sessions) > 0 {
|
|
||||||
now := time.Now()
|
|
||||||
for i := range sessions {
|
|
||||||
if now.Sub(sessions[i].ConnectedTime.Time) > time.Hour*1 {
|
|
||||||
_ = t.sessionRepository.DeleteById(sessions[i].ID)
|
|
||||||
s := sessions[i].Username + "@" + sessions[i].IP + ":" + strconv.Itoa(sessions[i].Port)
|
|
||||||
log.Infof("会话「%v」ID「%v」超过1小时未打开,已删除。", s, sessions[i].ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -44,15 +35,37 @@ func (t *Ticker) SetupTicker() {
|
|||||||
timeoutSessionTicker := time.NewTicker(time.Hour * 6)
|
timeoutSessionTicker := time.NewTicker(time.Hour * 6)
|
||||||
go func() {
|
go func() {
|
||||||
for range timeoutSessionTicker.C {
|
for range timeoutSessionTicker.C {
|
||||||
deleteOutTimeSession(t)
|
deleteOutTimeSession()
|
||||||
deleteOutTimeLoginLog(t)
|
deleteOutTimeLoginLog()
|
||||||
deleteOutTimeJobLog(t)
|
deleteOutTimeJobLog()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteOutTimeSession(t *Ticker) {
|
func (t *Ticker) deleteUnUsedSession() {
|
||||||
property, err := t.propertyRepository.FindByName("session-saved-limit")
|
sessions, err := repository.SessionRepository.FindByStatusIn(context.TODO(), []string{constant.NoConnect, constant.Connecting})
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("查询会话列表失败: %v", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(sessions) > 0 {
|
||||||
|
now := time.Now()
|
||||||
|
for i := range sessions {
|
||||||
|
if now.Sub(sessions[i].ConnectedTime.Time) > time.Hour*1 {
|
||||||
|
err := repository.SessionRepository.DeleteById(context.TODO(), sessions[i].ID)
|
||||||
|
s := sessions[i].Username + "@" + sessions[i].IP + ":" + strconv.Itoa(sessions[i].Port)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("会话「%v」ID「%v」超过1小时未打开,删除失败: %v", s, sessions[i].ID, err.Error())
|
||||||
|
} else {
|
||||||
|
log.Infof("会话「%v」ID「%v」超过1小时未打开,已删除。", s, sessions[i].ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteOutTimeSession() {
|
||||||
|
property, err := repository.PropertyRepository.FindByName(context.TODO(), "session-saved-limit")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -63,7 +76,7 @@ func deleteOutTimeSession(t *Ticker) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sessions, err := t.sessionRepository.FindOutTimeSessions(limit)
|
sessions, err := repository.SessionRepository.FindOutTimeSessions(context.TODO(), limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -73,15 +86,15 @@ func deleteOutTimeSession(t *Ticker) {
|
|||||||
for i := range sessions {
|
for i := range sessions {
|
||||||
ids = append(ids, sessions[i].ID)
|
ids = append(ids, sessions[i].ID)
|
||||||
}
|
}
|
||||||
err := t.sessionRepository.DeleteByIds(ids)
|
err := repository.SessionRepository.DeleteByIds(context.TODO(), ids)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("删除离线会话失败 %v", err)
|
log.Errorf("删除离线会话失败 %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteOutTimeLoginLog(t *Ticker) {
|
func deleteOutTimeLoginLog() {
|
||||||
property, err := t.propertyRepository.FindByName("login-log-saved-limit")
|
property, err := repository.PropertyRepository.FindByName(context.TODO(), "login-log-saved-limit")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -94,7 +107,7 @@ func deleteOutTimeLoginLog(t *Ticker) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
loginLogs, err := t.loginLogRepository.FindOutTimeLog(limit)
|
loginLogs, err := repository.LoginLogRepository.FindOutTimeLog(context.TODO(), limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("获取登录日志失败 %v", err)
|
log.Errorf("获取登录日志失败 %v", err)
|
||||||
return
|
return
|
||||||
@ -102,7 +115,7 @@ func deleteOutTimeLoginLog(t *Ticker) {
|
|||||||
|
|
||||||
if len(loginLogs) > 0 {
|
if len(loginLogs) > 0 {
|
||||||
for i := range loginLogs {
|
for i := range loginLogs {
|
||||||
err := t.loginLogRepository.DeleteById(loginLogs[i].ID)
|
err := repository.LoginLogRepository.DeleteById(context.TODO(), loginLogs[i].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("删除登录日志失败 %v", err)
|
log.Errorf("删除登录日志失败 %v", err)
|
||||||
}
|
}
|
||||||
@ -110,8 +123,8 @@ func deleteOutTimeLoginLog(t *Ticker) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteOutTimeJobLog(t *Ticker) {
|
func deleteOutTimeJobLog() {
|
||||||
property, err := t.propertyRepository.FindByName("cron-log-saved-limit")
|
property, err := repository.PropertyRepository.FindByName(context.TODO(), "cron-log-saved-limit")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -123,14 +136,14 @@ func deleteOutTimeJobLog(t *Ticker) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
jobLogs, err := t.jobLogRepository.FindOutTimeLog(limit)
|
jobLogs, err := repository.JobLogRepository.FindOutTimeLog(context.TODO(), limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(jobLogs) > 0 {
|
if len(jobLogs) > 0 {
|
||||||
for i := range jobLogs {
|
for i := range jobLogs {
|
||||||
err := t.jobLogRepository.DeleteById(jobLogs[i].ID)
|
err := repository.JobLogRepository.DeleteById(context.TODO(), jobLogs[i].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("删除计划日志失败 %v", err)
|
log.Errorf("删除计划日志失败 %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,11 @@ package term
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/net/proxy"
|
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewSshClient(ip string, port int, username, password, privateKey, passphrase string) (*ssh.Client, error) {
|
func NewSshClient(ip string, port int, username, password, privateKey, passphrase string) (*ssh.Client, error) {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user