2024-01-12 19:19:52 +01:00
|
|
|
package pairing
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
|
|
|
|
"github.com/twofas/2fas-server/internal/common/logging"
|
2024-04-05 11:31:40 +02:00
|
|
|
"github.com/twofas/2fas-server/internal/pass/connection"
|
2024-01-12 19:19:52 +01:00
|
|
|
)
|
|
|
|
|
2024-01-21 10:25:12 +01:00
|
|
|
func ExtensionConfigureHandler(pairingApp *Pairing) gin.HandlerFunc {
|
2024-01-12 19:19:52 +01:00
|
|
|
return func(gCtx *gin.Context) {
|
|
|
|
var req ConfigureBrowserExtensionRequest
|
|
|
|
if err := gCtx.BindJSON(&req); err != nil {
|
|
|
|
gCtx.String(http.StatusBadRequest, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err := uuid.Parse(req.ExtensionID); err != nil {
|
|
|
|
gCtx.String(http.StatusBadRequest, "extension_id is not valid uuid")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := pairingApp.ConfigureBrowserExtension(gCtx, req)
|
|
|
|
if err != nil {
|
|
|
|
logging.Errorf("Failed to configure: %v", err)
|
|
|
|
gCtx.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
gCtx.JSON(http.StatusCreated, resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-21 10:25:12 +01:00
|
|
|
func ExtensionWaitForConnWSHandler(pairingApp *Pairing) gin.HandlerFunc {
|
2024-01-12 19:19:52 +01:00
|
|
|
return func(gCtx *gin.Context) {
|
2024-04-05 11:31:40 +02:00
|
|
|
token, err := connection.TokenFromWSProtocol(gCtx.Request)
|
2024-01-12 19:19:52 +01:00
|
|
|
if err != nil {
|
|
|
|
logging.Errorf("Failed to get token from request: %v", err)
|
2024-04-05 11:31:40 +02:00
|
|
|
gCtx.Status(http.StatusUnauthorized)
|
2024-01-12 19:19:52 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
extensionID, err := pairingApp.VerifyPairingToken(gCtx, token)
|
|
|
|
if err != nil {
|
|
|
|
logging.Errorf("Failed to verify pairing token: %v", err)
|
2024-04-05 11:31:40 +02:00
|
|
|
gCtx.Status(http.StatusUnauthorized)
|
2024-01-12 19:19:52 +01:00
|
|
|
return
|
|
|
|
}
|
2024-01-21 10:25:12 +01:00
|
|
|
|
2024-04-05 11:31:40 +02:00
|
|
|
if err := pairingApp.ServePairingWS(gCtx.Writer, gCtx.Request, extensionID); err != nil {
|
|
|
|
logging.Errorf("Failed serve ws: %v", err)
|
|
|
|
gCtx.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2024-01-12 19:19:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-05 11:31:40 +02:00
|
|
|
func ExtensionProxyWSHandler(pairingApp *Pairing, proxyApp *connection.ProxyServer) gin.HandlerFunc {
|
2024-01-12 19:19:52 +01:00
|
|
|
return func(gCtx *gin.Context) {
|
2024-04-05 11:31:40 +02:00
|
|
|
token, err := connection.TokenFromWSProtocol(gCtx.Request)
|
2024-01-12 19:19:52 +01:00
|
|
|
if err != nil {
|
|
|
|
logging.Errorf("Failed to get token from request: %v", err)
|
|
|
|
gCtx.Status(http.StatusForbidden)
|
|
|
|
return
|
|
|
|
}
|
2024-01-24 20:57:31 +01:00
|
|
|
extensionID, err := pairingApp.VerifyExtProxyToken(gCtx, token)
|
2024-01-12 19:19:52 +01:00
|
|
|
if err != nil {
|
|
|
|
logging.Errorf("Failed to verify proxy token: %v", err)
|
|
|
|
gCtx.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
pairingInfo, err := pairingApp.GetPairingInfo(gCtx, extensionID)
|
|
|
|
if err != nil {
|
|
|
|
logging.Errorf("Failed to get pairing info: %v", err)
|
|
|
|
gCtx.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !pairingInfo.IsPaired() {
|
|
|
|
gCtx.String(http.StatusForbidden, "Pairing is not yet done")
|
|
|
|
return
|
|
|
|
}
|
2024-04-05 11:31:40 +02:00
|
|
|
if err := proxyApp.ServeExtensionProxyToMobileWS(gCtx.Writer, gCtx.Request, pairingInfo.Device.DeviceID); err != nil {
|
|
|
|
logging.Errorf("Failed to serve ws: %v", err)
|
|
|
|
gCtx.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2024-01-12 19:19:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func MobileConfirmHandler(pairingApp *Pairing) gin.HandlerFunc {
|
|
|
|
return func(gCtx *gin.Context) {
|
|
|
|
token, err := tokenFromRequest(gCtx)
|
|
|
|
if err != nil {
|
|
|
|
logging.Errorf("Failed to get token from request: %v", err)
|
|
|
|
gCtx.Status(http.StatusForbidden)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
extensionID, err := pairingApp.VerifyConnectionToken(gCtx, token)
|
|
|
|
if err != nil {
|
|
|
|
logging.Errorf("Failed to verify connection token: %v", err)
|
|
|
|
gCtx.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var req ConfirmPairingRequest
|
|
|
|
if err := gCtx.BindJSON(&req); err != nil {
|
|
|
|
gCtx.String(http.StatusBadRequest, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := uuid.Parse(req.DeviceID); err != nil {
|
|
|
|
gCtx.String(http.StatusBadRequest, "extension_id is not valid uuid")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-01-24 20:57:31 +01:00
|
|
|
resp, err := pairingApp.ConfirmPairing(gCtx, req, extensionID)
|
|
|
|
if err != nil {
|
2024-01-12 19:19:52 +01:00
|
|
|
logging.Errorf("Failed to ConfirmPairing: %v", err)
|
|
|
|
gCtx.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2024-01-24 20:57:31 +01:00
|
|
|
gCtx.JSON(http.StatusOK, resp)
|
2024-01-12 19:19:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-05 11:31:40 +02:00
|
|
|
func MobileProxyWSHandler(pairingApp *Pairing, proxy *connection.ProxyServer) gin.HandlerFunc {
|
2024-01-12 19:19:52 +01:00
|
|
|
return func(gCtx *gin.Context) {
|
2024-04-05 11:31:40 +02:00
|
|
|
token, err := connection.TokenFromWSProtocol(gCtx.Request)
|
2024-01-12 19:19:52 +01:00
|
|
|
if err != nil {
|
|
|
|
logging.Errorf("Failed to get token from request: %v", err)
|
|
|
|
gCtx.Status(http.StatusForbidden)
|
|
|
|
return
|
|
|
|
}
|
2024-01-24 20:57:31 +01:00
|
|
|
extensionID, err := pairingApp.VerifyMobileProxyToken(gCtx, token)
|
2024-01-12 19:19:52 +01:00
|
|
|
if err != nil {
|
|
|
|
logging.Errorf("Failed to verify connection token: %v", err)
|
|
|
|
gCtx.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
log := logging.WithField("extension_id", extensionID)
|
|
|
|
pairingInfo, err := pairingApp.GetPairingInfo(gCtx, extensionID)
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Failed to get pairing info: %v", err)
|
|
|
|
gCtx.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !pairingInfo.IsPaired() {
|
|
|
|
gCtx.String(http.StatusForbidden, "Pairing is not yet done")
|
|
|
|
return
|
|
|
|
}
|
2024-04-05 11:31:40 +02:00
|
|
|
if err := proxy.ServeMobileProxyToExtensionWS(gCtx.Writer, gCtx.Request, pairingInfo.Device.DeviceID); err != nil {
|
|
|
|
log.Errorf("Failed to serve ws: %w", err)
|
|
|
|
gCtx.Status(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2024-01-12 19:19:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func tokenFromRequest(gCtx *gin.Context) (string, error) {
|
|
|
|
tokenHeader := gCtx.GetHeader("Authorization")
|
|
|
|
if tokenHeader == "" {
|
|
|
|
return "", errors.New("missing Authorization header")
|
|
|
|
}
|
|
|
|
splitToken := strings.Split(tokenHeader, "Bearer ")
|
|
|
|
if len(splitToken) != 2 {
|
|
|
|
gCtx.Status(http.StatusForbidden)
|
|
|
|
return "", errors.New("missing 'Bearer: value'")
|
|
|
|
}
|
|
|
|
return splitToken[1], nil
|
|
|
|
}
|