refactor(auth): move authentication params to headers and simplify validation
- Move token and timestamp validation to HTTP headers - Simplify ValidateTimeStamp to return boolean - Update AddUser to use default "visitor" type - Remove redundant timestamp and token fields from request structs - Update API documentation to reflect header-based authentication
This commit is contained in:
@@ -69,7 +69,8 @@ All API responses are returned in JSON format:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Important Notes:**
|
**Important Notes:**
|
||||||
- For **POST** requests: All data is sent in the request body as JSON
|
- For all requests: Authentication token and timestamp are sent in HTTP headers (prefixed with `X-`)
|
||||||
|
- For **POST** requests: Other data is sent in the request body as JSON
|
||||||
- For **GET** requests: All data is sent in HTTP headers (prefixed with `X-`)
|
- For **GET** requests: All data is sent in HTTP headers (prefixed with `X-`)
|
||||||
- All requests require authentication (except `/register` and `/login`)
|
- All requests require authentication (except `/register` and `/login`)
|
||||||
- Timestamps are in milliseconds (Unix timestamp)
|
- Timestamps are in milliseconds (Unix timestamp)
|
||||||
@@ -82,21 +83,28 @@ All API responses are returned in JSON format:
|
|||||||
**Method:** POST
|
**Method:** POST
|
||||||
**Content-Type:** application/json
|
**Content-Type:** application/json
|
||||||
|
|
||||||
**Request:**
|
**Request Headers:**
|
||||||
|
```
|
||||||
|
X-Timestamp: 1704067200000
|
||||||
|
```
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"username": "your_username",
|
"username": "your_username",
|
||||||
"passwd": "YourPass123!",
|
"passwd": "YourPass123!",
|
||||||
"timeStamp": 1704067200000,
|
|
||||||
"type": "admin"
|
"type": "admin"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
| Header | Type | Required | Description |
|
||||||
|
|--------|------|----------|-------------|
|
||||||
|
| X-Timestamp | int64 | Yes | Client timestamp in milliseconds |
|
||||||
|
|
||||||
| Field | Type | Required | Description |
|
| Field | Type | Required | Description |
|
||||||
|-------|------|----------|-------------|
|
|-------|------|----------|-------------|
|
||||||
| username | string | Yes | Username (no special characters) |
|
| username | string | Yes | Username (no special characters) |
|
||||||
| passwd | string | Yes | Password (must contain uppercase, lowercase, digit, and special character, min 8 chars) |
|
| passwd | string | Yes | Password (must contain uppercase, lowercase, digit, and special character, min 8 chars) |
|
||||||
| timeStamp | int64 | Yes | Client timestamp in milliseconds |
|
|
||||||
| type | string | No | User type: superuser, admin, visitor (default: visitor) |
|
| type | string | No | User type: superuser, admin, visitor (default: visitor) |
|
||||||
|
|
||||||
**Response:**
|
**Response:**
|
||||||
@@ -120,20 +128,27 @@ All API responses are returned in JSON format:
|
|||||||
**Method:** POST
|
**Method:** POST
|
||||||
**Content-Type:** application/json
|
**Content-Type:** application/json
|
||||||
|
|
||||||
**Request:**
|
**Request Headers:**
|
||||||
|
```
|
||||||
|
X-Timestamp: 1704067200000
|
||||||
|
```
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"username": "your_username",
|
"username": "your_username",
|
||||||
"passwd": "YourPass123!",
|
"passwd": "YourPass123!"
|
||||||
"timeStamp": 1704067200000
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
| Header | Type | Required | Description |
|
||||||
|
|--------|------|----------|-------------|
|
||||||
|
| X-Timestamp | int64 | Yes | Client timestamp in milliseconds |
|
||||||
|
|
||||||
| Field | Type | Required | Description |
|
| Field | Type | Required | Description |
|
||||||
|-------|------|----------|-------------|
|
|-------|------|----------|-------------|
|
||||||
| username | string | Yes | Username |
|
| username | string | Yes | Username |
|
||||||
| passwd | string | Yes | Password |
|
| passwd | string | Yes | Password |
|
||||||
| timeStamp | int64 | Yes | Client timestamp in milliseconds |
|
|
||||||
|
|
||||||
**Response:**
|
**Response:**
|
||||||
```json
|
```json
|
||||||
@@ -158,11 +173,15 @@ All API responses are returned in JSON format:
|
|||||||
**Content-Type:** application/json
|
**Content-Type:** application/json
|
||||||
**Auth Required:** Yes (token)
|
**Auth Required:** Yes (token)
|
||||||
|
|
||||||
**Request:**
|
**Request Headers:**
|
||||||
|
```
|
||||||
|
X-Token: your_token
|
||||||
|
X-Timestamp: 1704067200000
|
||||||
|
```
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"token": "your_token",
|
|
||||||
"timeStamp": 1704067200000,
|
|
||||||
"instanceInfo": {
|
"instanceInfo": {
|
||||||
"name": "my_frpc",
|
"name": "my_frpc",
|
||||||
"serverAddr": "127.0.0.1",
|
"serverAddr": "127.0.0.1",
|
||||||
@@ -177,10 +196,13 @@ All API responses are returned in JSON format:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
| Header | Type | Required | Description |
|
||||||
|
|--------|------|----------|-------------|
|
||||||
|
| X-Token | string | Yes | Authentication token |
|
||||||
|
| X-Timestamp | int64 | Yes | Client timestamp in milliseconds |
|
||||||
|
|
||||||
| Field | Type | Required | Description |
|
| Field | Type | Required | Description |
|
||||||
|-------|------|----------|-------------|
|
|-------|------|----------|-------------|
|
||||||
| token | string | Yes | Authentication token |
|
|
||||||
| timeStamp | int64 | Yes | Client timestamp in milliseconds |
|
|
||||||
| instanceInfo.name | string | Yes | Instance name |
|
| instanceInfo.name | string | Yes | Instance name |
|
||||||
| instanceInfo.serverAddr | string | Yes | frps server address |
|
| instanceInfo.serverAddr | string | Yes | frps server address |
|
||||||
| instanceInfo.serverPort | string | Yes | frps server port |
|
| instanceInfo.serverPort | string | Yes | frps server port |
|
||||||
@@ -211,12 +233,16 @@ All API responses are returned in JSON format:
|
|||||||
**Content-Type:** application/json
|
**Content-Type:** application/json
|
||||||
**Auth Required:** Yes (token)
|
**Auth Required:** Yes (token)
|
||||||
|
|
||||||
**Request:**
|
**Request Headers:**
|
||||||
|
```
|
||||||
|
X-Token: your_token
|
||||||
|
X-Timestamp: 1704067200000
|
||||||
|
```
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"instanceName": "my_frpc",
|
"instanceName": "my_frpc"
|
||||||
"token": "your_token",
|
|
||||||
"timeStamp": 1704067200000
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -242,12 +268,16 @@ All API responses are returned in JSON format:
|
|||||||
|
|
||||||
You can modify multiple fields at once:
|
You can modify multiple fields at once:
|
||||||
|
|
||||||
**Request:**
|
**Request Headers:**
|
||||||
|
```
|
||||||
|
X-Token: your_token
|
||||||
|
X-Timestamp: 1704067200000
|
||||||
|
```
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"instanceName": "my_frpc",
|
"instanceName": "my_frpc",
|
||||||
"token": "your_token",
|
|
||||||
"timeStamp": 1704067200000,
|
|
||||||
"name": "new_name",
|
"name": "new_name",
|
||||||
"serverAddr": "192.168.1.1",
|
"serverAddr": "192.168.1.1",
|
||||||
"serverPort": "7000",
|
"serverPort": "7000",
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"super-frpc/postLog"
|
"super-frpc/postLog"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -175,13 +177,24 @@ func isValidPassword(password string) bool { // Validate password complexity and
|
|||||||
return hasUpper && hasLower && hasDigit && hasSpecial
|
return hasUpper && hasLower && hasDigit && hasSpecial
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateTimeStamp(timeStamp int64) error {
|
func ValidateTimeStamp(header http.Header) bool {
|
||||||
if globalConfig.Debug {
|
timeStampStr := header.Get("X-Timestamp")
|
||||||
return nil
|
if timeStampStr == "" {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timeStamp, err := strconv.ParseInt(timeStampStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if globalConfig.Debug {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
currentTime := time.Now().UnixMilli()
|
currentTime := time.Now().UnixMilli()
|
||||||
if currentTime-timeStamp > 3000 || timeStamp-currentTime > 3000 {
|
if currentTime-timeStamp > 3000 || timeStamp-currentTime > 3000 {
|
||||||
return fmt.Errorf("Timestamp out of valid range: %d", timeStamp)
|
return false
|
||||||
}
|
}
|
||||||
return nil
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -42,7 +42,7 @@ func isValidInput(input string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddUser(username, passwd, userType string) (int, error) {
|
func AddUser(username, passwd string) (int, error) { // New user registration with default type "visitor"
|
||||||
if !isValidInput(username) || !isValidInput(passwd) {
|
if !isValidInput(username) || !isValidInput(passwd) {
|
||||||
return 0, errors.New("invalid input: contains illegal characters")
|
return 0, errors.New("invalid input: contains illegal characters")
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ func AddUser(username, passwd, userType string) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result, err := db.Exec("INSERT INTO userLogin (username, passwd, type) VALUES (?, ?, ?)",
|
result, err := db.Exec("INSERT INTO userLogin (username, passwd, type) VALUES (?, ?, ?)",
|
||||||
username, hashedPasswd, userType)
|
username, hashedPasswd, "visitor")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "UNIQUE constraint failed") {
|
if strings.Contains(err.Error(), "UNIQUE constraint failed") {
|
||||||
return 0, errors.New("username already exists")
|
return 0, errors.New("username already exists")
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"super-frpc/postLog"
|
"super-frpc/postLog"
|
||||||
"time"
|
"time"
|
||||||
@@ -27,8 +26,6 @@ type InstanceInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CreateInstanceRequest struct {
|
type CreateInstanceRequest struct {
|
||||||
Token string `json:"token"`
|
|
||||||
TimeStamp int64 `json:"timeStamp"`
|
|
||||||
InstanceInfo InstanceInfo `json:"instanceInfo"`
|
InstanceInfo InstanceInfo `json:"instanceInfo"`
|
||||||
BootAtStart bool `json:"bootAtStart"`
|
BootAtStart bool `json:"bootAtStart"`
|
||||||
RunUser string `json:"runUser"`
|
RunUser string `json:"runUser"`
|
||||||
@@ -80,21 +77,6 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理timeStamp字段
|
|
||||||
timeStamp := int64(0)
|
|
||||||
if ts, ok := reqMap["timeStamp"]; ok {
|
|
||||||
switch v := ts.(type) {
|
|
||||||
case float64:
|
|
||||||
timeStamp = int64(v)
|
|
||||||
case string:
|
|
||||||
if v != "" {
|
|
||||||
if parsed, err := strconv.ParseInt(v, 10, 64); err == nil {
|
|
||||||
timeStamp = parsed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理bootAtStart字段
|
// 处理bootAtStart字段
|
||||||
bootAtStart := false
|
bootAtStart := false
|
||||||
if bas, ok := reqMap["bootAtStart"]; ok {
|
if bas, ok := reqMap["bootAtStart"]; ok {
|
||||||
@@ -128,31 +110,22 @@ func CreateInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
instanceInfo.Additional = additional
|
instanceInfo.Additional = additional
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从Header中验证token和timeStamp
|
||||||
|
userID, _, err := ValidateRequestWithHeader(w, r)
|
||||||
|
if err != nil {
|
||||||
|
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to validate request header: %v", err))
|
||||||
|
SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 构建请求结构体
|
// 构建请求结构体
|
||||||
req := CreateInstanceRequest{
|
req := CreateInstanceRequest{
|
||||||
Token: getStringFromMap(reqMap, "token"),
|
|
||||||
TimeStamp: timeStamp,
|
|
||||||
InstanceInfo: instanceInfo,
|
InstanceInfo: instanceInfo,
|
||||||
BootAtStart: bootAtStart,
|
BootAtStart: bootAtStart,
|
||||||
RunUser: instanceInfo.RunUser,
|
RunUser: instanceInfo.RunUser,
|
||||||
Additional: instanceInfo.Additional,
|
Additional: instanceInfo.Additional,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重新序列化为JSON,用于ValidateRequestWithBody
|
|
||||||
reqBody, err := json.Marshal(req)
|
|
||||||
if err != nil {
|
|
||||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to marshal request body: %v", err))
|
|
||||||
SendErrorResponse(w, http.StatusBadRequest, "Invalid request format")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userID, _, err := ValidateRequestWithBody(w, r, reqBody)
|
|
||||||
if err != nil {
|
|
||||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to validate request body: %v", err))
|
|
||||||
SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := CheckPermission(userID, "superuser", "admin"); err != nil {
|
if err := CheckPermission(userID, "superuser", "admin"); err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to check permission: %v", err))
|
postLog.Error(fmt.Sprintf("[CreateInstanceHandler] Failed to check permission: %v", err))
|
||||||
SendErrorResponse(w, http.StatusForbidden, err.Error())
|
SendErrorResponse(w, http.StatusForbidden, err.Error())
|
||||||
@@ -251,9 +224,10 @@ func DeleteInstanceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userID, _, err := ValidateRequestWithBody(w, r, body)
|
// 从Header中验证token和timeStamp
|
||||||
|
userID, _, err := ValidateRequestWithHeader(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to validate request body: %v", err))
|
postLog.Error(fmt.Sprintf("[DeleteInstanceHandler] Failed to validate request header: %v", err))
|
||||||
SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -345,9 +319,10 @@ func ModifyInstanceHandler(w http.ResponseWriter, r *http.Request, field string)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userID, _, err := ValidateRequestWithBody(w, r, body)
|
// 从Header中验证token和timeStamp
|
||||||
|
userID, _, err := ValidateRequestWithHeader(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to validate request body: %v", err))
|
postLog.Error(fmt.Sprintf("[ModifyInstanceHandler] Failed to validate request header: %v", err))
|
||||||
SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
SendErrorResponse(w, http.StatusUnauthorized, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-58
@@ -13,14 +13,11 @@ import (
|
|||||||
type RegisterRequest struct {
|
type RegisterRequest struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Passwd string `json:"passwd"`
|
Passwd string `json:"passwd"`
|
||||||
TimeStamp int64 `json:"timeStamp"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginRequest struct {
|
type LoginRequest struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Passwd string `json:"passwd"`
|
Passwd string `json:"passwd"`
|
||||||
TimeStamp int64 `json:"timeStamp"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
@@ -36,6 +33,11 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !ValidateTimeStamp(r.Header) {
|
||||||
|
SendErrorResponse(w, http.StatusBadRequest, "Invalid or missing X-Timestamp in header")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
body, err := io.ReadAll(r.Body)
|
body, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||||
@@ -57,11 +59,6 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ValidateTimeStamp(req.TimeStamp); err != nil {
|
|
||||||
SendErrorResponse(w, http.StatusBadRequest, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isValidInput(req.Username) || !isValidInput(req.Passwd) {
|
if !isValidInput(req.Username) || !isValidInput(req.Passwd) {
|
||||||
SendErrorResponse(w, http.StatusBadRequest, "Invalid input: contains illegal characters")
|
SendErrorResponse(w, http.StatusBadRequest, "Invalid input: contains illegal characters")
|
||||||
postLog.Debug(fmt.Sprintf("[RegisterHandler] New user registration failed: username or password contains illegal characters \"%s\":\"%s\"", req.Username, req.Passwd))
|
postLog.Debug(fmt.Sprintf("[RegisterHandler] New user registration failed: username or password contains illegal characters \"%s\":\"%s\"", req.Username, req.Passwd))
|
||||||
@@ -74,23 +71,7 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userType := req.Type
|
userID, err := AddUser(req.Username, req.Passwd)
|
||||||
if userType == "" {
|
|
||||||
userType = "visitor"
|
|
||||||
}
|
|
||||||
|
|
||||||
validTypes := map[string]bool{
|
|
||||||
"superuser": true,
|
|
||||||
"admin": true,
|
|
||||||
"visitor": true,
|
|
||||||
}
|
|
||||||
if !validTypes[userType] {
|
|
||||||
SendErrorResponse(w, http.StatusBadRequest, "Invalid user type")
|
|
||||||
postLog.Warning(fmt.Sprintf("[RegisterHandler] New user registration failed: invalid user type \"%s\"", userType))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userID, err := AddUser(req.Username, req.Passwd, userType)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SendErrorResponse(w, http.StatusInternalServerError, err.Error())
|
SendErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||||
postLog.Error(fmt.Sprintf("[RegisterHandler] Failed to register user \"%s\": %v", req.Username, err))
|
postLog.Error(fmt.Sprintf("[RegisterHandler] Failed to register user \"%s\": %v", req.Username, err))
|
||||||
@@ -118,6 +99,11 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !ValidateTimeStamp(r.Header) {
|
||||||
|
SendErrorResponse(w, http.StatusBadRequest, "Invalid or missing X-Timestamp in header")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
body, err := io.ReadAll(r.Body)
|
body, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
SendErrorResponse(w, http.StatusBadRequest, "Failed to read request body")
|
||||||
@@ -139,12 +125,6 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ValidateTimeStamp(req.TimeStamp); err != nil {
|
|
||||||
SendErrorResponse(w, http.StatusBadRequest, err.Error())
|
|
||||||
postLog.Warning(fmt.Sprintf("[LoginHandler] User \"%s\" Login failed: invalid timestamp \"%d\"", req.Username, req.TimeStamp))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isValidInput(req.Username) || !isValidInput(req.Passwd) {
|
if !isValidInput(req.Username) || !isValidInput(req.Passwd) {
|
||||||
SendErrorResponse(w, http.StatusBadRequest, "Invalid input: contains illegal characters")
|
SendErrorResponse(w, http.StatusBadRequest, "Invalid input: contains illegal characters")
|
||||||
postLog.Debug(fmt.Sprintf("[LoginHandler] Login failed: username or password contains illegal characters \"%s\":\"%s\"", req.Username, req.Passwd))
|
postLog.Debug(fmt.Sprintf("[LoginHandler] Login failed: username or password contains illegal characters \"%s\":\"%s\"", req.Username, req.Passwd))
|
||||||
@@ -218,7 +198,7 @@ func SendSuccessResponse(w http.ResponseWriter, message string, data interface{}
|
|||||||
w.Write(jsonResp)
|
w.Write(jsonResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateRequest(w http.ResponseWriter, r *http.Request, requiredFields ...string) (int, string, error) {
|
func ValidateRequest(w http.ResponseWriter, r *http.Request, requiredFields ...string) (int, string, error) { // ValidateRequest validates the request body and header
|
||||||
body, err := io.ReadAll(r.Body)
|
body, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, "", fmt.Errorf("Failed to read request body: %w", err)
|
return 0, "", fmt.Errorf("Failed to read request body: %w", err)
|
||||||
@@ -234,20 +214,13 @@ func ValidateRequestWithBody(w http.ResponseWriter, r *http.Request, body []byte
|
|||||||
return 0, "", fmt.Errorf("Invalid request format: %w", err)
|
return 0, "", fmt.Errorf("Invalid request format: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
token, ok := reqMap["token"].(string)
|
token := r.Header.Get("X-Token")
|
||||||
if !ok || token == "" {
|
if token == "" {
|
||||||
return 0, "", fmt.Errorf("Token is required: %s", token)
|
return 0, "", fmt.Errorf("Token is required in header: %s", token)
|
||||||
}
|
}
|
||||||
|
|
||||||
timeStamp := int64(0)
|
if !ValidateTimeStamp(r.Header) {
|
||||||
if ts, ok := reqMap["timeStamp"].(float64); ok {
|
return 0, "", fmt.Errorf("Invalid or missing X-Timestamp in header")
|
||||||
timeStamp = int64(ts)
|
|
||||||
} else if !globalConfig.Debug {
|
|
||||||
return 0, "", fmt.Errorf("Timestamp is required: %d", timeStamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ValidateTimeStamp(timeStamp); err != nil {
|
|
||||||
return 0, "", fmt.Errorf("Invalid timestamp: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userID, err := extractUserIDFromToken(token)
|
userID, err := extractUserIDFromToken(token)
|
||||||
@@ -274,20 +247,8 @@ func ValidateRequestWithHeader(w http.ResponseWriter, r *http.Request, requiredF
|
|||||||
return 0, "", fmt.Errorf("Token is required in header: %s", token)
|
return 0, "", fmt.Errorf("Token is required in header: %s", token)
|
||||||
}
|
}
|
||||||
|
|
||||||
timeStampStr := r.Header.Get("X-Timestamp")
|
if !ValidateTimeStamp(r.Header) {
|
||||||
timeStamp := int64(0)
|
return 0, "", fmt.Errorf("Invalid or missing X-Timestamp in header")
|
||||||
if timeStampStr != "" {
|
|
||||||
var err error
|
|
||||||
timeStamp, err = strconv.ParseInt(timeStampStr, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0, "", fmt.Errorf("Invalid timestamp format in header: %w", err)
|
|
||||||
}
|
|
||||||
} else if !globalConfig.Debug {
|
|
||||||
return 0, "", fmt.Errorf("Timestamp is required in header: %s", timeStampStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ValidateTimeStamp(timeStamp); err != nil {
|
|
||||||
return 0, "", err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userID, err := extractUserIDFromToken(token)
|
userID, err := extractUserIDFromToken(token)
|
||||||
|
|||||||
@@ -16,27 +16,27 @@ func setupRoutes() {
|
|||||||
|
|
||||||
http.HandleFunc("/frpcAct/instanceMgr/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/frpcAct/instanceMgr/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
path := r.URL.Path
|
path := r.URL.Path
|
||||||
if len(path) < len("/frpcAct/instanceMgr/") {
|
if len(path) < len("/frpcAct/instanceMgr/") { // Check if path is at least as long as the base path
|
||||||
SendErrorResponse(w, http.StatusNotFound, "invalid path")
|
SendErrorResponse(w, http.StatusNotFound, "invalid path")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
remainingPath := path[len("/frpcAct/instanceMgr/"):]
|
remainingPath := path[len("/frpcAct/instanceMgr/"):] // Get the remaining path after the base path
|
||||||
|
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet { // Handle `/list` and `/list/<instanceName>` by GET request
|
||||||
if remainingPath == "list" {
|
if remainingPath == "list" {
|
||||||
ListInstancesHandler(w, r)
|
ListInstancesHandler(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceName := strings.Trim(remainingPath, "/")
|
instanceName := strings.Trim(remainingPath, "/") // Get the instance name from the remaining path
|
||||||
if instanceName != "" {
|
if instanceName != "" {
|
||||||
ListInstancesHandler(w, r)
|
ListInstancesHandler(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost { // Handle `/create`, `/delete`, and `/modify/<field>` by POST request
|
||||||
if remainingPath == "create" {
|
if remainingPath == "create" {
|
||||||
CreateInstanceHandler(w, r)
|
CreateInstanceHandler(w, r)
|
||||||
return
|
return
|
||||||
@@ -57,6 +57,6 @@ func setupRoutes() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SendErrorResponse(w, http.StatusNotFound, "endpoint not found")
|
SendErrorResponse(w, http.StatusNotFound, "endpoint not found") // Send error response if no endpoint is found
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user