completely save config with toml format

This commit is contained in:
杨黄林
2023-09-17 01:35:42 +08:00
parent 2f79a5d093
commit 7c30c6d150
13 changed files with 398 additions and 207 deletions

View File

@@ -12,7 +12,7 @@ import (
func (c *HandleController) BasicAuth() gin.HandlerFunc {
return func(context *gin.Context) {
if strings.TrimSpace(c.CommonInfo.AdminUser) == "" || strings.TrimSpace(c.CommonInfo.AdminPwd) == "" {
if trimString(c.CommonInfo.AdminUser) == "" || trimString(c.CommonInfo.AdminPwd) == "" {
if context.Request.RequestURI == LoginUrl {
context.Redirect(http.StatusTemporaryRedirect, LoginSuccessUrl)
}
@@ -54,7 +54,7 @@ func (c *HandleController) BasicAuth() gin.HandlerFunc {
}
func (c *HandleController) LoginAuth(username, password string, context *gin.Context) bool {
if strings.TrimSpace(c.CommonInfo.AdminUser) == "" || strings.TrimSpace(c.CommonInfo.AdminPwd) == "" {
if trimString(c.CommonInfo.AdminUser) == "" || trimString(c.CommonInfo.AdminPwd) == "" {
return true
}

View File

@@ -5,14 +5,12 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/BurntSushi/toml"
plugin "github.com/fatedier/frp/pkg/plugin/server"
ginI18n "github.com/gin-contrib/i18n"
"github.com/gin-gonic/gin"
"io"
"log"
"net/http"
"os"
"sort"
"strconv"
"strings"
@@ -123,7 +121,7 @@ func (c *HandleController) MakeIndexFunc() func(context *gin.Context) {
return func(context *gin.Context) {
context.HTML(http.StatusOK, "index.html", gin.H{
"version": c.Version,
"showExit": strings.TrimSpace(c.CommonInfo.AdminUser) != "" && strings.TrimSpace(c.CommonInfo.AdminPwd) != "",
"showExit": trimString(c.CommonInfo.AdminUser) != "" && trimString(c.CommonInfo.AdminPwd) != "",
"FrpsPanel": ginI18n.MustGetMessage(context, "Frps Panel"),
"User": ginI18n.MustGetMessage(context, "User"),
"Token": ginI18n.MustGetMessage(context, "Token"),
@@ -206,6 +204,7 @@ func (c *HandleController) MakeLangFunc() func(context *gin.Context) {
"OperateError": ginI18n.MustGetMessage(context, "Operate error"),
"OperateFailed": ginI18n.MustGetMessage(context, "Operate failed"),
"UserExist": ginI18n.MustGetMessage(context, "User exist"),
"UserNotExist": ginI18n.MustGetMessage(context, "User not exist"),
"UserFormatError": ginI18n.MustGetMessage(context, "User format error"),
"TokenFormatError": ginI18n.MustGetMessage(context, "Token format error"),
"ShouldCheckUser": ginI18n.MustGetMessage(context, "Please check at least one user"),
@@ -220,6 +219,7 @@ func (c *HandleController) MakeLangFunc() func(context *gin.Context) {
"SubdomainsInvalid": ginI18n.MustGetMessage(context, "Subdomains is invalid"),
"CommentInvalid": ginI18n.MustGetMessage(context, "Comment is invalid"),
"ParamError": ginI18n.MustGetMessage(context, "Param error"),
"OtherError": ginI18n.MustGetMessage(context, "Other error"),
"Name": ginI18n.MustGetMessage(context, "Name"),
"Port": ginI18n.MustGetMessage(context, "Port"),
"Connections": ginI18n.MustGetMessage(context, "Connections"),
@@ -287,52 +287,6 @@ func (c *HandleController) MakeQueryTokensFunc() func(context *gin.Context) {
}
}
func filter(main TokenInfo, sub TokenInfo) bool {
replaceSpaceUser := TrimAllSpaceReg.ReplaceAllString(sub.User, "")
if len(replaceSpaceUser) != 0 {
if !strings.Contains(main.User, replaceSpaceUser) {
return false
}
}
replaceSpaceToken := TrimAllSpaceReg.ReplaceAllString(sub.Token, "")
if len(replaceSpaceToken) != 0 {
if !strings.Contains(main.Token, replaceSpaceToken) {
return false
}
}
replaceSpaceComment := TrimAllSpaceReg.ReplaceAllString(sub.Comment, "")
if len(replaceSpaceComment) != 0 {
if !strings.Contains(main.Comment, replaceSpaceComment) {
return false
}
}
return true
}
func TokensList(tokens map[string]TokenInfo) Tokens {
return Tokens{
tokens,
}
}
func (c *HandleController) SaveToken() error {
tokenFile, err := os.Create(c.TokensFile)
if err != nil {
log.Printf("error to crate file %v: %v", c.TokensFile, err)
}
if err = toml.NewEncoder(tokenFile).Encode(TokensList(c.Tokens)); err != nil {
log.Printf("error to encode tokens: %v", err)
}
if err = tokenFile.Close(); err != nil {
log.Printf("error to close file %v: %v", c.TokensFile, err)
}
return err
}
func (c *HandleController) MakeAddTokenFunc() func(context *gin.Context) {
return func(context *gin.Context) {
info := TokenInfo{
@@ -345,46 +299,34 @@ func (c *HandleController) MakeAddTokenFunc() func(context *gin.Context) {
}
err := context.BindJSON(&info)
if err != nil {
log.Printf("user add failed, param error : %v", err)
response.Success = false
response.Code = ParamError
response.Message = "user add failed, param error "
response.Message = fmt.Sprintf("user add failed, param error : %v", err)
log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
if !UserFormatReg.MatchString(info.User) {
log.Printf("user add failed, user format error")
response.Success = false
response.Code = UserFormatError
response.Message = fmt.Sprintf("user add failed, user format error")
context.JSON(http.StatusOK, &response)
return
}
if _, exist := c.Tokens[info.User]; exist {
log.Printf("user add failed, user [%v] exist", info.User)
response.Success = false
response.Code = UserExist
response.Message = fmt.Sprintf("user add failed, user [%s] exist ", info.User)
context.JSON(http.StatusOK, &response)
return
}
if !TokenFormatReg.MatchString(info.Token) {
log.Printf("user add failed, token format error")
response.Success = false
response.Code = TokenFormatError
response.Message = fmt.Sprintf("user add failed, token format error")
context.JSON(http.StatusOK, &response)
result := c.verifyToken(info, TOKEN_ADD)
if !result.Success {
context.JSON(http.StatusOK, &result)
return
}
info.Comment = cleanString(info.Comment)
info.Ports = cleanStrings(info.Ports)
info.Domains = cleanStrings(info.Domains)
info.Subdomains = cleanStrings(info.Subdomains)
c.Tokens[info.User] = info
err = c.SaveToken()
err = c.saveToken()
if err != nil {
log.Printf("add failed, error : %v", err)
response.Success = false
response.Code = SaveError
response.Message = "user add failed"
response.Message = fmt.Sprintf("add failed, error : %v", err)
log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
@@ -403,10 +345,10 @@ func (c *HandleController) MakeUpdateTokensFunc() func(context *gin.Context) {
update := TokenUpdate{}
err := context.BindJSON(&update)
if err != nil {
log.Printf("update failed, param error : %v", err)
response.Success = false
response.Code = ParamError
response.Message = "user update failed, param error"
response.Message = fmt.Sprintf("update failed, param error : %v", err)
log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
@@ -414,32 +356,35 @@ func (c *HandleController) MakeUpdateTokensFunc() func(context *gin.Context) {
before := update.Before
after := update.After
if after.User != before.User {
log.Printf("update failed, user not match")
if before.User != after.User {
response.Success = false
response.Code = ParamError
response.Message = "update failed, user not match"
response.Message = fmt.Sprintf("update failed, user should be same : before -> %v, after -> %v", before.User, after.User)
log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
if !TokenFormatReg.MatchString(after.Token) {
log.Printf("update failed, token format error")
response.Success = false
response.Code = TokenFormatError
response.Message = "user update failed, token format error"
context.JSON(http.StatusOK, &response)
result := c.verifyToken(after, TOKEN_UPDATE)
if !result.Success {
context.JSON(http.StatusOK, &result)
return
}
after.Comment = cleanString(after.Comment)
after.Ports = cleanStrings(after.Ports)
after.Domains = cleanStrings(after.Domains)
after.Subdomains = cleanStrings(after.Subdomains)
c.Tokens[after.User] = after
err = c.SaveToken()
err = c.saveToken()
if err != nil {
log.Printf("user update failed, error : %v", err)
response.Success = false
response.Code = SaveError
response.Message = "user update failed"
response.Message = fmt.Sprintf("user update failed, error : %v", err)
log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
@@ -458,24 +403,33 @@ func (c *HandleController) MakeRemoveTokensFunc() func(context *gin.Context) {
remove := TokenRemove{}
err := context.BindJSON(&remove)
if err != nil {
log.Printf("user remove failed, param error : %v", err)
response.Success = false
response.Code = ParamError
response.Message = "user remove failed, param error "
response.Message = fmt.Sprintf("user remove failed, param error : %v", err)
log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
for _, user := range remove.Users {
result := c.verifyToken(user, TOKEN_REMOVE)
if !result.Success {
context.JSON(http.StatusOK, &result)
return
}
}
for _, user := range remove.Users {
delete(c.Tokens, user.User)
}
err = c.SaveToken()
err = c.saveToken()
if err != nil {
log.Printf("user update failed, error : %v", err)
response.Success = false
response.Code = SaveError
response.Message = "user update failed"
response.Message = fmt.Sprintf("user update failed, error : %v", err)
log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
@@ -494,27 +448,36 @@ func (c *HandleController) MakeDisableTokensFunc() func(context *gin.Context) {
disable := TokenDisable{}
err := context.BindJSON(&disable)
if err != nil {
log.Printf("disable failed, param error : %v", err)
response.Success = false
response.Code = ParamError
response.Message = "disable failed, param error "
response.Message = fmt.Sprintf("disable failed, param error : %v", err)
log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
for _, user := range disable.Users {
result := c.verifyToken(user, TOKEN_DISABLE)
if !result.Success {
context.JSON(http.StatusOK, &result)
return
}
}
for _, user := range disable.Users {
token := c.Tokens[user.User]
token.Status = false
c.Tokens[user.User] = token
}
err = c.SaveToken()
err = c.saveToken()
if err != nil {
log.Printf("disable failed, error : %v", err)
response.Success = false
response.Code = SaveError
response.Message = "disable failed"
response.Message = fmt.Sprintf("disable failed, error : %v", err)
log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
@@ -533,21 +496,30 @@ func (c *HandleController) MakeEnableTokensFunc() func(context *gin.Context) {
enable := TokenEnable{}
err := context.BindJSON(&enable)
if err != nil {
log.Printf("enable failed, param error : %v", err)
response.Success = false
response.Code = ParamError
response.Message = "enable failed, param error "
response.Message = fmt.Sprintf("enable failed, param error : %v", err)
log.Printf(response.Message)
context.JSON(http.StatusOK, &response)
return
}
for _, user := range enable.Users {
result := c.verifyToken(user, TOKEN_ENABLE)
if !result.Success {
context.JSON(http.StatusOK, &result)
return
}
}
for _, user := range enable.Users {
token := c.Tokens[user.User]
token.Status = true
c.Tokens[user.User] = token
}
err = c.SaveToken()
err = c.saveToken()
if err != nil {
log.Printf("enable failed, error : %v", err)
@@ -588,7 +560,7 @@ func (c *HandleController) MakeProxyFunc() func(context *gin.Context) {
request, _ := http.NewRequest("GET", requestUrl, nil)
username := c.CommonInfo.DashboardUser
password := c.CommonInfo.DashboardPwd
if len(strings.TrimSpace(username)) != 0 && len(strings.TrimSpace(password)) != 0 {
if trimString(username) != "" && trimString(password) != "" {
request.SetBasicAuth(username, password)
log.Printf("Proxy to %s", requestUrl)
}

View File

@@ -78,7 +78,7 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
}
proxyType := content.ProxyType
if StringIndexOf(proxyType, supportProxyTypes) == -1 {
if stringContains(proxyType, supportProxyTypes) {
log.Printf("proxy type [%v] not support, plugin do nothing", proxyType)
res.Unchange = true
return res
@@ -100,12 +100,12 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
portErr = fmt.Errorf("user [%v] port range [%v] format error", user, port)
break
}
start, err := strconv.Atoi(strings.TrimSpace(allowedRanges[0]))
start, err := strconv.Atoi(trimString(allowedRanges[0]))
if err != nil {
portErr = fmt.Errorf("user [%v] port rang [%v] start port [%v] is not a number", user, port, allowedRanges[0])
break
}
end, err := strconv.Atoi(strings.TrimSpace(allowedRanges[1]))
end, err := strconv.Atoi(trimString(allowedRanges[1]))
if err != nil {
portErr = fmt.Errorf("user [%v] port rang [%v] end port [%v] is not a number", user, port, allowedRanges[0])
break
@@ -141,7 +141,7 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
if portAllowed {
if token, exist := c.Tokens[user]; exist {
for _, userDomain := range userDomains {
if StringIndexOf(userDomain, token.Domains) == -1 {
if stringContains(userDomain, token.Domains) {
domainAllowed = false
break
}
@@ -183,12 +183,3 @@ func (c *HandleController) JudgePort(content *plugin.NewProxyContent) plugin.Res
}
return res
}
func StringIndexOf(element string, data []string) int {
for k, v := range data {
if element == v {
return k
}
}
return -1
}

View File

@@ -0,0 +1,205 @@
package controller
import (
"fmt"
"github.com/BurntSushi/toml"
"log"
"os"
"strings"
)
func filter(main TokenInfo, sub TokenInfo) bool {
replaceSpaceUser := trimAllSpace.ReplaceAllString(sub.User, "")
if len(replaceSpaceUser) != 0 {
if !strings.Contains(main.User, replaceSpaceUser) {
return false
}
}
replaceSpaceToken := trimAllSpace.ReplaceAllString(sub.Token, "")
if len(replaceSpaceToken) != 0 {
if !strings.Contains(main.Token, replaceSpaceToken) {
return false
}
}
replaceSpaceComment := trimAllSpace.ReplaceAllString(sub.Comment, "")
if len(replaceSpaceComment) != 0 {
if !strings.Contains(main.Comment, replaceSpaceComment) {
return false
}
}
return true
}
func trimString(str string) string {
return strings.TrimSpace(str)
}
func (c *HandleController) verifyToken(token TokenInfo, operate int) OperationResponse {
response := OperationResponse{
Success: true,
Code: Success,
Message: "operate success",
}
var (
validateExist = false
validateNotExist = false
validateUser = false
validateToken = false
validateComment = false
validatePorts = false
validateDomains = false
validateSubdomains = false
)
if operate == TOKEN_ADD {
validateExist = true
validateUser = true
validateToken = true
validateComment = true
validatePorts = true
validateDomains = true
validateSubdomains = true
} else if operate == TOKEN_UPDATE {
validateNotExist = true
validateUser = true
validateToken = true
validateComment = true
validatePorts = true
validateDomains = true
validateSubdomains = true
} else if operate == TOKEN_ENABLE || operate == TOKEN_DISABLE || operate == TOKEN_REMOVE {
validateNotExist = true
}
if validateUser && !userFormat.MatchString(token.User) {
response.Success = false
response.Code = UserFormatError
response.Message = fmt.Sprintf("operate failed, user [%s] format error", token.User)
log.Printf(response.Message)
return response
}
if validateExist {
if _, exist := c.Tokens[token.User]; exist {
response.Success = false
response.Code = UserExist
response.Message = fmt.Sprintf("operate failed, user [%s] exist ", token.User)
log.Printf(response.Message)
return response
}
}
if validateNotExist {
if _, exist := c.Tokens[token.User]; !exist {
response.Success = false
response.Code = UserNotExist
response.Message = fmt.Sprintf("operate failed, user [%s] not exist ", token.User)
log.Printf(response.Message)
return response
}
}
if validateToken && !tokenFormat.MatchString(token.Token) {
response.Success = false
response.Code = TokenFormatError
response.Message = fmt.Sprintf("operate failed, token [%s] format error", token.Token)
log.Printf(response.Message)
return response
}
trimmedComment := trimString(token.Comment)
if validateComment && trimmedComment != "" && commentFormat.MatchString(trimmedComment) {
response.Success = false
response.Code = CommentFormatError
response.Message = fmt.Sprintf("operate failed, comment [%s] format error", token.Comment)
log.Printf(response.Message)
return response
}
if validatePorts {
for _, port := range token.Ports {
trimmedPort := trimString(port)
if trimmedPort != "" && !portsFormatSingle.MatchString(trimmedPort) && !portsFormatRange.MatchString(trimmedPort) {
response.Success = false
response.Code = PortsFormatError
response.Message = fmt.Sprintf("operate failed, ports [%v] format error", token.Ports)
log.Printf(response.Message)
return response
}
}
}
if validateDomains {
for _, domain := range token.Domains {
trimmedDomain := trimString(domain)
if trimmedDomain != "" && !domainFormat.MatchString(trimmedDomain) {
response.Success = false
response.Code = DomainsFormatError
response.Message = fmt.Sprintf("operate failed, domains [%v] format error", token.Domains)
log.Printf(response.Message)
return response
}
}
}
if validateSubdomains {
for _, subdomain := range token.Subdomains {
trimmedSubdomain := trimString(subdomain)
if trimmedSubdomain != "" && !subdomainFormat.MatchString(trimmedSubdomain) {
response.Success = false
response.Code = SubdomainsFormatError
response.Message = fmt.Sprintf("operate failed, subdomains [%v] format error", token.Subdomains)
log.Printf(response.Message)
return response
}
}
}
return response
}
func cleanStrings(originalStrings []string) []string {
cleanedStrings := make([]string, len(originalStrings))
for i, str := range originalStrings {
cleanedStrings[i] = cleanString(str)
}
return cleanedStrings
}
func cleanString(originalString string) string {
return trimAllSpace.ReplaceAllString(originalString, "")
}
func stringContains(element string, data []string) bool {
for _, v := range data {
if element == v {
return true
}
}
return false
}
func tokensList(tokens map[string]TokenInfo) Tokens {
return Tokens{
tokens,
}
}
func (c *HandleController) saveToken() error {
tokenFile, err := os.Create(c.TokensFile)
if err != nil {
log.Printf("error to crate file %v: %v", c.TokensFile, err)
}
if err = toml.NewEncoder(tokenFile).Encode(tokensList(c.Tokens)); err != nil {
log.Printf("error to encode tokens: %v", err)
}
if err = tokenFile.Close(); err != nil {
log.Printf("error to close file %v: %v", c.TokensFile, err)
}
return err
}

View File

@@ -5,14 +5,29 @@ import (
)
const (
Success = 0
ParamError = 1
UserExist = 2
SaveError = 3
UserFormatError = 4
TokenFormatError = 5
FrpServerError = 6
Success int = iota
ParamError
UserExist
UserNotExist
SaveError
UserFormatError
TokenFormatError
CommentFormatError
PortsFormatError
DomainsFormatError
SubdomainsFormatError
FrpServerError
)
const (
TOKEN_ADD int = iota
TOKEN_UPDATE
TOKEN_REMOVE
TOKEN_ENABLE
TOKEN_DISABLE
)
const (
SessionName = "GOSESSION"
AuthName = "_PANEL_AUTH"
LoginUrl = "/login"
@@ -22,9 +37,14 @@ const (
)
var (
UserFormatReg = regexp.MustCompile("^\\w+$")
TokenFormatReg = regexp.MustCompile("^[\\w!@#$%^&*()]+$")
TrimAllSpaceReg = regexp.MustCompile("[\\n\\t\\r\\s]")
userFormat = regexp.MustCompile("^\\w+$")
tokenFormat = regexp.MustCompile("^[\\w!@#$%^&*()]+$")
commentFormat = regexp.MustCompile("[\\n\\t\\r]")
portsFormatSingle = regexp.MustCompile("^\\s*\\d{1,5}\\s*$")
portsFormatRange = regexp.MustCompile("^\\s*\\d{1,5}\\s*-\\s*\\d{1,5}\\s*$")
domainFormat = regexp.MustCompile("^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}$")
subdomainFormat = regexp.MustCompile("^[a-zA-z0-9-]{1,20}$")
trimAllSpace = regexp.MustCompile("[\\n\\t\\r\\s]")
)
type Response struct {
@@ -36,9 +56,8 @@ type HTTPError struct {
Err error
}
type Config struct {
type Common struct {
Common CommonInfo
Tokens
}
type CommonInfo struct {