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

218 lines
4.9 KiB
Go

package connector
import (
"encoding/json"
"errors"
"net/http"
"os"
"github.com/gin-gonic/gin"
)
type AccessOTP struct {
Type string `json:"type"`
Data json.RawMessage `json:"data,omitempty"`
}
type ConnectOTPData struct {
Username string
Version string
ClientType uint8 `json:"client_type,omitempty"`
ConsoleTeamMode bool `json:"console_team_mode,omitempty"`
Subscriptions []string `json:"subscriptions,omitempty"`
}
type ChannelOTPData struct {
Username string
ChannelData json.RawMessage
}
type FileOTPData struct {
Id string `json:"id"`
}
func (tc *TsConnector) validateOTPMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
otp := ctx.Query("otp")
if otp == "" {
_ = ctx.Error(errors.New("authorization token required"))
return
}
otpType, data, ok := tc.teamserver.ValidateOTP(otp)
if !ok {
_ = ctx.Error(errors.New("invalid or expired OTP"))
return
}
ctx.Set("otpType", otpType)
ctx.Set("otpData", data)
ctx.Next()
}
}
func (tc *TsConnector) tcOTP_Generate(ctx *gin.Context) {
var (
accessOTP AccessOTP
data interface{}
)
err := ctx.ShouldBindJSON(&accessOTP)
if err != nil {
_ = ctx.Error(errors.New("invalid request body"))
return
}
if len(accessOTP.Data) == 0 {
ctx.JSON(http.StatusBadRequest, gin.H{"message": "data is required", "ok": false})
return
}
switch accessOTP.Type {
case "connect":
usernameStr, version, ok := tc.extractUserContext(ctx)
if !ok {
return
}
var connectData ConnectOTPData
err := json.Unmarshal(accessOTP.Data, &connectData)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"message": "invalid connect data: " + err.Error(), "ok": false})
return
}
connectData.Username = usernameStr
connectData.Version = version
data = connectData
case "channel_tunnel", "channel_terminal", "channel_agent_build":
usernameStr, _, ok := tc.extractUserContext(ctx)
if !ok {
return
}
data = &ChannelOTPData{
Username: usernameStr,
ChannelData: accessOTP.Data,
}
case "download", "tmp_upload":
var fileData FileOTPData
err := json.Unmarshal(accessOTP.Data, &fileData)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"message": "invalid file data: " + err.Error(), "ok": false})
return
}
data = fileData.Id
default:
ctx.JSON(http.StatusBadRequest, gin.H{"message": "unknown OTP type", "ok": false})
return
}
otp, err := tc.teamserver.CreateOTP(accessOTP.Type, data)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false})
return
}
ctx.JSON(http.StatusOK, gin.H{"message": otp, "ok": true})
}
func (tc *TsConnector) extractUserContext(ctx *gin.Context) (string, string, bool) {
username, exists := ctx.Get("username")
if !exists {
ctx.JSON(http.StatusOK, gin.H{"message": "username not found in context", "ok": false})
return "", "", false
}
usernameStr, ok := username.(string)
if !ok || usernameStr == "" {
ctx.JSON(http.StatusOK, gin.H{"message": "invalid username in context", "ok": false})
return "", "", false
}
versionValue, _ := ctx.Get("version")
version, _ := versionValue.(string)
return usernameStr, version, true
}
func (tc *TsConnector) tcOTP_DownloadSync(ctx *gin.Context) {
otpType, _ := ctx.Get("otpType")
if otpType != "download" {
_ = ctx.Error(errors.New("invalid OTP type"))
return
}
data, _ := ctx.Get("otpData")
fileId, ok := data.(string)
if !ok || fileId == "" {
_ = ctx.Error(errors.New("invalid OTP data"))
return
}
path, err := tc.teamserver.TsDownloadGetFilepath(fileId)
if err != nil {
ctx.String(http.StatusNotFound, err.Error())
return
}
file, err := os.Open(path)
if err != nil {
ctx.String(http.StatusNotFound, "File not found")
return
}
defer func(file *os.File) {
_ = file.Close()
}(file)
fileInfo, err := file.Stat()
if err != nil {
ctx.String(http.StatusInternalServerError, "Cannot get file info")
return
}
ctx.Header("Content-Disposition", "attachment; filename="+fileInfo.Name())
ctx.Header("Content-Type", "application/octet-stream")
ctx.Header("Content-Length", string(rune(fileInfo.Size())))
ctx.File(path)
}
func (tc *TsConnector) tcOTP_UploadTemp(ctx *gin.Context) {
otpType, _ := ctx.Get("otpType")
if otpType != "tmp_upload" {
_ = ctx.Error(errors.New("invalid OTP type"))
return
}
data, _ := ctx.Get("otpData")
fileId, ok := data.(string)
if !ok || fileId == "" {
_ = ctx.Error(errors.New("invalid OTP data"))
return
}
savePath, err := tc.teamserver.TsUploadGetFilepath(fileId)
if err != nil {
ctx.String(http.StatusNotFound, err.Error())
return
}
ctx.Request.Body = http.MaxBytesReader(ctx.Writer, ctx.Request.Body, 2<<30)
file, err := ctx.FormFile("file")
if err != nil {
ctx.String(http.StatusNotFound, err.Error())
return
}
err = ctx.SaveUploadedFile(file, savePath)
if err != nil {
ctx.String(http.StatusNotFound, err.Error())
return
}
ctx.String(http.StatusOK, "")
}