2026-04-06 00:20:51 -05:00

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()
}
}