diff --git a/api/user/otp.go b/api/user/otp.go index 1818c5d4..7ba75a43 100644 --- a/api/user/otp.go +++ b/api/user/otp.go @@ -1,238 +1,238 @@ package user import ( - "bytes" - "crypto/sha1" - "encoding/base64" - "encoding/hex" - "fmt" - "github.com/0xJacky/Nginx-UI/api" - "github.com/0xJacky/Nginx-UI/internal/crypto" - "github.com/0xJacky/Nginx-UI/internal/user" - "github.com/0xJacky/Nginx-UI/query" - "github.com/0xJacky/Nginx-UI/settings" - "github.com/gin-gonic/gin" - "github.com/pquerna/otp" - "github.com/pquerna/otp/totp" - "image/jpeg" - "net/http" - "strings" + "bytes" + "crypto/sha1" + "encoding/base64" + "encoding/hex" + "fmt" + "github.com/0xJacky/Nginx-UI/api" + "github.com/0xJacky/Nginx-UI/internal/crypto" + "github.com/0xJacky/Nginx-UI/internal/user" + "github.com/0xJacky/Nginx-UI/query" + "github.com/0xJacky/Nginx-UI/settings" + "github.com/gin-gonic/gin" + "github.com/pquerna/otp" + "github.com/pquerna/otp/totp" + "image/jpeg" + "net/http" + "strings" ) func GenerateTOTP(c *gin.Context) { - u := api.CurrentUser(c) + u := api.CurrentUser(c) - issuer := fmt.Sprintf("Nginx UI %s", settings.ServerSettings.Name) - issuer = strings.TrimSpace(issuer) + issuer := fmt.Sprintf("Nginx UI %s", settings.ServerSettings.Name) + issuer = strings.TrimSpace(issuer) - otpOpts := totp.GenerateOpts{ - Issuer: issuer, - AccountName: u.Name, - Period: 30, // seconds - Digits: otp.DigitsSix, - Algorithm: otp.AlgorithmSHA1, - } - otpKey, err := totp.Generate(otpOpts) - if err != nil { - api.ErrHandler(c, err) - return - } - ciphertext, err := crypto.AesEncrypt([]byte(otpKey.Secret())) - if err != nil { - api.ErrHandler(c, err) - return - } + otpOpts := totp.GenerateOpts{ + Issuer: issuer, + AccountName: u.Name, + Period: 30, // seconds + Digits: otp.DigitsSix, + Algorithm: otp.AlgorithmSHA1, + } + otpKey, err := totp.Generate(otpOpts) + if err != nil { + api.ErrHandler(c, err) + return + } + ciphertext, err := crypto.AesEncrypt([]byte(otpKey.Secret())) + if err != nil { + api.ErrHandler(c, err) + return + } - qrCode, err := otpKey.Image(512, 512) - if err != nil { - api.ErrHandler(c, err) - return - } + qrCode, err := otpKey.Image(512, 512) + if err != nil { + api.ErrHandler(c, err) + return + } - // Encode the image to a buffer - var buf []byte - buffer := bytes.NewBuffer(buf) - err = jpeg.Encode(buffer, qrCode, nil) - if err != nil { - fmt.Println("Error encoding image:", err) - return - } + // Encode the image to a buffer + var buf []byte + buffer := bytes.NewBuffer(buf) + err = jpeg.Encode(buffer, qrCode, nil) + if err != nil { + fmt.Println("Error encoding image:", err) + return + } - // Convert the buffer to a base64 string - base64Str := "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(buffer.Bytes()) + // Convert the buffer to a base64 string + base64Str := "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(buffer.Bytes()) - c.JSON(http.StatusOK, gin.H{ - "secret": base64.StdEncoding.EncodeToString(ciphertext), - "qr_code": base64Str, - }) + c.JSON(http.StatusOK, gin.H{ + "secret": base64.StdEncoding.EncodeToString(ciphertext), + "qr_code": base64Str, + }) } func EnrollTOTP(c *gin.Context) { - cUser := api.CurrentUser(c) - if cUser.EnabledOTP() { - c.JSON(http.StatusBadRequest, gin.H{ - "message": "User already enrolled", - }) - return - } + cUser := api.CurrentUser(c) + if cUser.EnabledOTP() { + c.JSON(http.StatusBadRequest, gin.H{ + "message": "User already enrolled", + }) + return + } - if settings.ServerSettings.Demo { - c.JSON(http.StatusBadRequest, gin.H{ - "message": "This feature is disabled in demo mode", - }) - return - } + if settings.ServerSettings.Demo { + c.JSON(http.StatusBadRequest, gin.H{ + "message": "This feature is disabled in demo mode", + }) + return + } - var json struct { - Secret string `json:"secret" binding:"required"` - Passcode string `json:"passcode" binding:"required"` - } - if !api.BindAndValid(c, &json) { - return - } + var json struct { + Secret string `json:"secret" binding:"required"` + Passcode string `json:"passcode" binding:"required"` + } + if !api.BindAndValid(c, &json) { + return + } - secret, err := base64.StdEncoding.DecodeString(json.Secret) - if err != nil { - api.ErrHandler(c, err) - return - } + secret, err := base64.StdEncoding.DecodeString(json.Secret) + if err != nil { + api.ErrHandler(c, err) + return + } - decrypted, err := crypto.AesDecrypt(secret) - if err != nil { - api.ErrHandler(c, err) - return - } + decrypted, err := crypto.AesDecrypt(secret) + if err != nil { + api.ErrHandler(c, err) + return + } - if ok := totp.Validate(json.Passcode, string(decrypted)); !ok { - c.JSON(http.StatusNotAcceptable, gin.H{ - "message": "Invalid passcode", - }) - return - } + if ok := totp.Validate(json.Passcode, string(decrypted)); !ok { + c.JSON(http.StatusNotAcceptable, gin.H{ + "message": "Invalid passcode", + }) + return + } - ciphertext, err := crypto.AesEncrypt(decrypted) - if err != nil { - api.ErrHandler(c, err) - return - } + ciphertext, err := crypto.AesEncrypt(decrypted) + if err != nil { + api.ErrHandler(c, err) + return + } - u := query.Auth - _, err = u.Where(u.ID.Eq(cUser.ID)).Update(u.OTPSecret, ciphertext) - if err != nil { - api.ErrHandler(c, err) - return - } + u := query.Auth + _, err = u.Where(u.ID.Eq(cUser.ID)).Update(u.OTPSecret, ciphertext) + if err != nil { + api.ErrHandler(c, err) + return + } - recoveryCode := sha1.Sum(ciphertext) + recoveryCode := sha1.Sum(ciphertext) - c.JSON(http.StatusOK, gin.H{ - "message": "ok", - "recovery_code": hex.EncodeToString(recoveryCode[:]), - }) + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + "recovery_code": hex.EncodeToString(recoveryCode[:]), + }) } func ResetOTP(c *gin.Context) { - var json struct { - RecoveryCode string `json:"recovery_code"` - } - if !api.BindAndValid(c, &json) { - return - } - recoverCode, err := hex.DecodeString(json.RecoveryCode) - if err != nil { - api.ErrHandler(c, err) - return - } - cUser := api.CurrentUser(c) - k := sha1.Sum(cUser.OTPSecret) - if !bytes.Equal(k[:], recoverCode) { - c.JSON(http.StatusBadRequest, gin.H{ - "message": "Invalid recovery code", - }) - return - } + var json struct { + RecoveryCode string `json:"recovery_code"` + } + if !api.BindAndValid(c, &json) { + return + } + recoverCode, err := hex.DecodeString(json.RecoveryCode) + if err != nil { + api.ErrHandler(c, err) + return + } + cUser := api.CurrentUser(c) + k := sha1.Sum(cUser.OTPSecret) + if !bytes.Equal(k[:], recoverCode) { + c.JSON(http.StatusBadRequest, gin.H{ + "message": "Invalid recovery code", + }) + return + } - u := query.Auth - _, err = u.Where(u.ID.Eq(cUser.ID)).UpdateSimple(u.OTPSecret.Null()) - if err != nil { - api.ErrHandler(c, err) - return - } + u := query.Auth + _, err = u.Where(u.ID.Eq(cUser.ID)).UpdateSimple(u.OTPSecret.Null()) + if err != nil { + api.ErrHandler(c, err) + return + } - c.JSON(http.StatusOK, gin.H{ - "message": "ok", - }) + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) } func OTPStatus(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "status": len(api.CurrentUser(c).OTPSecret) > 0, - }) + c.JSON(http.StatusOK, gin.H{ + "status": len(api.CurrentUser(c).OTPSecret) > 0, + }) } func SecureSessionStatus(c *gin.Context) { - cUser := api.CurrentUser(c) - if !cUser.EnabledOTP() { - c.JSON(http.StatusOK, gin.H{ - "status": false, - }) - return - } - ssid := c.GetHeader("X-Secure-Session-ID") - if ssid == "" { - ssid = c.Query("X-Secure-Session-ID") - } - if ssid == "" { - c.JSON(http.StatusOK, gin.H{ - "status": false, - }) - return - } + cUser := api.CurrentUser(c) + if !cUser.EnabledOTP() { + c.JSON(http.StatusOK, gin.H{ + "status": false, + }) + return + } + ssid := c.GetHeader("X-Secure-Session-ID") + if ssid == "" { + ssid = c.Query("X-Secure-Session-ID") + } + if ssid == "" { + c.JSON(http.StatusOK, gin.H{ + "status": false, + }) + return + } - if user.VerifySecureSessionID(ssid, cUser.ID) { - c.JSON(http.StatusOK, gin.H{ - "status": true, - }) - return - } - c.JSON(http.StatusOK, gin.H{ - "status": false, - }) + if user.VerifySecureSessionID(ssid, cUser.ID) { + c.JSON(http.StatusOK, gin.H{ + "status": true, + }) + return + } + c.JSON(http.StatusOK, gin.H{ + "status": false, + }) } func StartSecure2FASession(c *gin.Context) { - var json struct { - OTP string `json:"otp"` - RecoveryCode string `json:"recovery_code"` - } - if !api.BindAndValid(c, &json) { - return - } - u := api.CurrentUser(c) - if !u.EnabledOTP() { - c.JSON(http.StatusBadRequest, gin.H{ - "message": "User not configured with 2FA", - }) - return - } + var json struct { + OTP string `json:"otp"` + RecoveryCode string `json:"recovery_code"` + } + if !api.BindAndValid(c, &json) { + return + } + u := api.CurrentUser(c) + if !u.EnabledOTP() { + c.JSON(http.StatusBadRequest, gin.H{ + "message": "User not configured with 2FA", + }) + return + } - if json.OTP == "" && json.RecoveryCode == "" { - c.JSON(http.StatusBadRequest, LoginResponse{ - Message: "The user has enabled 2FA", - }) - return - } + if json.OTP == "" && json.RecoveryCode == "" { + c.JSON(http.StatusBadRequest, LoginResponse{ + Message: "The user has enabled 2FA", + }) + return + } - if err := user.VerifyOTP(u, json.OTP, json.RecoveryCode); err != nil { - c.JSON(http.StatusBadRequest, LoginResponse{ - Message: "Invalid 2FA or recovery code", - }) - return - } + if err := user.VerifyOTP(u, json.OTP, json.RecoveryCode); err != nil { + c.JSON(http.StatusBadRequest, LoginResponse{ + Message: "Invalid 2FA or recovery code", + }) + return + } - sessionId := user.SetSecureSessionID(u.ID) + sessionId := user.SetSecureSessionID(u.ID) - c.JSON(http.StatusOK, gin.H{ - "session_id": sessionId, - }) + c.JSON(http.StatusOK, gin.H{ + "session_id": sessionId, + }) } diff --git a/app/package.json b/app/package.json index c3944d32..07b970d2 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "nginx-ui-app-next", - "version": "2.0.0-beta.28", + "version": "2.0.0-beta.29", "type": "module", "scripts": { "dev": "vite --host", diff --git a/app/src/version.json b/app/src/version.json index 06f31606..4c53fbdc 100644 --- a/app/src/version.json +++ b/app/src/version.json @@ -1 +1 @@ -{"version":"2.0.0-beta.28","build_id":149,"total_build":353} \ No newline at end of file +{"version":"2.0.0-beta.29","build_id":150,"total_build":354} \ No newline at end of file diff --git a/app/version.json b/app/version.json index 06f31606..4c53fbdc 100644 --- a/app/version.json +++ b/app/version.json @@ -1 +1 @@ -{"version":"2.0.0-beta.28","build_id":149,"total_build":353} \ No newline at end of file +{"version":"2.0.0-beta.29","build_id":150,"total_build":354} \ No newline at end of file