243 lines
6.0 KiB
Go
243 lines
6.0 KiB
Go
package connector
|
|
|
|
import (
|
|
"AdaptixServer/core/utils/krypt"
|
|
"AdaptixServer/core/utils/logs"
|
|
"AdaptixServer/core/utils/token"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
const (
|
|
ClientTypeUI uint8 = 1
|
|
ClientTypeWEB uint8 = 2
|
|
ClientTypeCLI uint8 = 3
|
|
)
|
|
|
|
type Credentials struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
Version string `json:"version"`
|
|
}
|
|
|
|
type AccessJWT struct {
|
|
AccessToken string `json:"access_token"`
|
|
}
|
|
|
|
type SubscribeRequest struct {
|
|
Categories []string `json:"categories"`
|
|
ConsoleTeamMode *bool `json:"console_team_mode,omitempty"`
|
|
}
|
|
|
|
func (tc *TsConnector) tcLogin(ctx *gin.Context) {
|
|
var (
|
|
creds Credentials
|
|
err error
|
|
)
|
|
|
|
err = ctx.ShouldBindJSON(&creds)
|
|
if err != nil {
|
|
_ = ctx.Error(errors.New("invalid credentials"))
|
|
return
|
|
}
|
|
|
|
recvHash := krypt.SHA256([]byte(creds.Password))
|
|
|
|
if tc.OnlyHash {
|
|
if recvHash != tc.Hash {
|
|
_ = ctx.Error(errors.New("incorrect password"))
|
|
return
|
|
}
|
|
} else {
|
|
if recvHash != tc.Operators[creds.Username] {
|
|
_ = ctx.Error(errors.New("incorrect password"))
|
|
return
|
|
}
|
|
}
|
|
|
|
accessToken, err := token.GenerateAccessToken(creds.Username, creds.Version)
|
|
if err != nil {
|
|
_ = ctx.Error(errors.New("could not generate access token"))
|
|
return
|
|
}
|
|
|
|
refreshToken, err := token.GenerateRefreshToken(creds.Username, creds.Version)
|
|
if err != nil {
|
|
_ = ctx.Error(errors.New("could not generate refresh token"))
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, gin.H{"access_token": accessToken, "refresh_token": refreshToken})
|
|
}
|
|
|
|
func (tc *TsConnector) tcWebsocketConnect(username string, version string, wsConn *websocket.Conn, clientType uint8, consoleTeamMode bool, subscriptions []string) {
|
|
tc.teamserver.TsClientConnect(username, version, wsConn, clientType, consoleTeamMode, subscriptions)
|
|
for {
|
|
_, _, err := wsConn.ReadMessage()
|
|
if err == nil {
|
|
continue
|
|
}
|
|
|
|
tc.teamserver.TsClientDisconnect(username)
|
|
break
|
|
}
|
|
}
|
|
|
|
func (tc *TsConnector) tcSync(ctx *gin.Context) {
|
|
value, exists := ctx.Get("username")
|
|
if !exists {
|
|
ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false})
|
|
return
|
|
}
|
|
username, ok := value.(string)
|
|
if !ok {
|
|
ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false})
|
|
return
|
|
}
|
|
|
|
go tc.teamserver.TsClientSync(username)
|
|
|
|
ctx.JSON(http.StatusOK, gin.H{"message": "sync started", "ok": true})
|
|
}
|
|
|
|
func (tc *TsConnector) tcSubscribe(ctx *gin.Context) {
|
|
value, exists := ctx.Get("username")
|
|
if !exists {
|
|
ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false})
|
|
return
|
|
}
|
|
username, ok := value.(string)
|
|
if !ok {
|
|
ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false})
|
|
return
|
|
}
|
|
|
|
var req SubscribeRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"message": "Invalid subscribe request: " + err.Error(), "ok": false})
|
|
return
|
|
}
|
|
|
|
if len(req.Categories) == 0 {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"message": "No categories specified", "ok": false})
|
|
return
|
|
}
|
|
|
|
go tc.teamserver.TsClientSubscribe(username, req.Categories, req.ConsoleTeamMode)
|
|
|
|
ctx.JSON(http.StatusOK, gin.H{"message": "subscribe started", "ok": true})
|
|
}
|
|
|
|
/// OTP
|
|
|
|
func (tc *TsConnector) tcConnectOTP(ctx *gin.Context) {
|
|
otpType, _ := ctx.Get("otpType")
|
|
if otpType != "connect" {
|
|
_ = ctx.Error(errors.New("invalid OTP type"))
|
|
return
|
|
}
|
|
|
|
data, _ := ctx.Get("otpData")
|
|
wsData, ok := data.(ConnectOTPData)
|
|
if !ok {
|
|
_ = ctx.Error(errors.New("invalid OTP data"))
|
|
return
|
|
}
|
|
|
|
exists := tc.teamserver.TsClientExists(wsData.Username)
|
|
if exists {
|
|
ctx.JSON(http.StatusNetworkAuthenticationRequired, gin.H{"message": "Client already connected", "ok": false})
|
|
return
|
|
}
|
|
|
|
clientType := wsData.ClientType
|
|
if clientType == 0 {
|
|
clientType = ClientTypeUI
|
|
}
|
|
|
|
var wsUpgrader websocket.Upgrader
|
|
wsUpgrader.CheckOrigin = func(r *http.Request) bool {
|
|
return true
|
|
}
|
|
wsConn, err := wsUpgrader.Upgrade(ctx.Writer, ctx.Request, nil)
|
|
if err != nil {
|
|
logs.Error("", "WebSocket upgrade error: "+err.Error())
|
|
return
|
|
}
|
|
|
|
if wsConn == nil {
|
|
logs.Error("", "WebSocket is nil")
|
|
return
|
|
}
|
|
|
|
go tc.tcWebsocketConnect(wsData.Username, wsData.Version, wsConn, clientType, wsData.ConsoleTeamMode, wsData.Subscriptions)
|
|
}
|
|
|
|
func (tc *TsConnector) tcChannelOTP(ctx *gin.Context) {
|
|
otpTypeVal, _ := ctx.Get("otpType")
|
|
otpType, _ := otpTypeVal.(string)
|
|
if !strings.HasPrefix(otpType, "channel_") {
|
|
_ = ctx.Error(errors.New("invalid OTP type"))
|
|
return
|
|
}
|
|
|
|
data, _ := ctx.Get("otpData")
|
|
wsData, ok := data.(*ChannelOTPData)
|
|
if !ok || wsData == nil {
|
|
_ = ctx.Error(errors.New("invalid OTP data"))
|
|
return
|
|
}
|
|
|
|
exists := tc.teamserver.TsClientExists(wsData.Username)
|
|
if !exists {
|
|
ctx.JSON(http.StatusNetworkAuthenticationRequired, gin.H{"message": "Server error: client not connected", "ok": false})
|
|
return
|
|
}
|
|
|
|
var wsUpgrader websocket.Upgrader
|
|
wsUpgrader.CheckOrigin = func(r *http.Request) bool {
|
|
return true
|
|
}
|
|
wsConn, err := wsUpgrader.Upgrade(ctx.Writer, ctx.Request, nil)
|
|
if err != nil {
|
|
logs.Error("", "WebSocket upgrade error: "+err.Error())
|
|
return
|
|
}
|
|
|
|
if wsConn == nil {
|
|
logs.Error("", "WebSocket is nil")
|
|
return
|
|
}
|
|
|
|
channelDataStr := string(wsData.ChannelData)
|
|
|
|
switch otpType {
|
|
case "channel_tunnel":
|
|
if err := tc.teamserver.TsTunnelClientNewChannel(channelDataStr, wsConn); err != nil {
|
|
logs.Error("", "Tunnel channel error: "+err.Error())
|
|
wsConn.Close()
|
|
}
|
|
|
|
case "channel_terminal":
|
|
if err := tc.teamserver.TsAgentTerminalCreateChannel(channelDataStr, wsConn); err != nil {
|
|
logs.Error("", "Terminal channel error: "+err.Error())
|
|
wsConn.Close()
|
|
}
|
|
|
|
case "channel_agent_build":
|
|
go func() {
|
|
if err := tc.teamserver.TsAgentBuildCreateChannel(channelDataStr, wsConn); err != nil {
|
|
logs.Error("", "Agent build channel error: "+err.Error())
|
|
}
|
|
}()
|
|
|
|
default:
|
|
logs.Error("", "Unknown channel type: "+otpType)
|
|
wsConn.Close()
|
|
}
|
|
}
|