feat: implement core functionality for super-frpc service
This commit introduces the core functionality for the super-frpc service including: - Configuration management - User authentication and authorization - Database integration - FRPC instance management - API endpoints for user operations - Token-based authentication system - Password hashing and validation
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TokenInfo struct {
|
||||
Token string
|
||||
CreatedAt time.Time
|
||||
UserID int
|
||||
}
|
||||
|
||||
var (
|
||||
tokenMap = make(map[int]*TokenInfo)
|
||||
tokenMux sync.RWMutex
|
||||
tokenTTL = time.Hour
|
||||
)
|
||||
|
||||
func GenerateToken(userID int) (string, error) {
|
||||
randomBytes := make([]byte, 32)
|
||||
_, err := rand.Read(randomBytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate random bytes: %w", err)
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(append(randomBytes, []byte(fmt.Sprintf("%d", userID))...))
|
||||
token := base64.URLEncoding.EncodeToString(hash[:])
|
||||
|
||||
tokenMux.Lock()
|
||||
defer tokenMux.Unlock()
|
||||
|
||||
tokenMap[userID] = &TokenInfo{
|
||||
Token: token,
|
||||
CreatedAt: time.Now(),
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func ValidateToken(userID int, token string) error {
|
||||
tokenMux.RLock()
|
||||
defer tokenMux.RUnlock()
|
||||
|
||||
tokenInfo, exists := tokenMap[userID]
|
||||
if !exists {
|
||||
return errors.New("token not found")
|
||||
}
|
||||
|
||||
if tokenInfo.Token != token {
|
||||
return errors.New("invalid token")
|
||||
}
|
||||
|
||||
if time.Since(tokenInfo.CreatedAt) > tokenTTL {
|
||||
return errors.New("token expired")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func RefreshToken(userID int) (string, error) {
|
||||
tokenMux.Lock()
|
||||
defer tokenMux.Unlock()
|
||||
|
||||
randomBytes := make([]byte, 32)
|
||||
_, err := rand.Read(randomBytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate random bytes: %w", err)
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(append(randomBytes, []byte(fmt.Sprintf("%d", userID))...))
|
||||
token := base64.URLEncoding.EncodeToString(hash[:])
|
||||
|
||||
tokenMap[userID] = &TokenInfo{
|
||||
Token: token,
|
||||
CreatedAt: time.Now(),
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func RemoveToken(userID int) {
|
||||
tokenMux.Lock()
|
||||
defer tokenMux.Unlock()
|
||||
delete(tokenMap, userID)
|
||||
}
|
||||
|
||||
func GetTokenInfo(userID int) (*TokenInfo, error) {
|
||||
tokenMux.RLock()
|
||||
defer tokenMux.RUnlock()
|
||||
|
||||
tokenInfo, exists := tokenMap[userID]
|
||||
if !exists {
|
||||
return nil, errors.New("token not found")
|
||||
}
|
||||
|
||||
return tokenInfo, nil
|
||||
}
|
||||
|
||||
func extractUserIDFromToken(token string) (int, error) {
|
||||
tokenMux.RLock()
|
||||
defer tokenMux.RUnlock()
|
||||
for userID, tokenInfo := range tokenMap {
|
||||
if tokenInfo.Token == token {
|
||||
return userID, nil
|
||||
}
|
||||
}
|
||||
return 0, errors.New("invalid token")
|
||||
}
|
||||
|
||||
func CleanupExpiredTokens() {
|
||||
tokenMux.Lock()
|
||||
defer tokenMux.Unlock()
|
||||
|
||||
for userID, tokenInfo := range tokenMap {
|
||||
if time.Since(tokenInfo.CreatedAt) > tokenTTL {
|
||||
delete(tokenMap, userID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func hashPassword(password string) (string, error) {
|
||||
hash := sha256.Sum256([]byte(password))
|
||||
return hex.EncodeToString(hash[:]), nil
|
||||
}
|
||||
|
||||
func verifyPassword(password, hashedPassword string) bool {
|
||||
hash, err := hashPassword(password)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return hash == hashedPassword
|
||||
}
|
||||
|
||||
func isValidPassword(password string) bool {
|
||||
if len(password) < 8 {
|
||||
return false
|
||||
}
|
||||
|
||||
hasUpper := false
|
||||
hasLower := false
|
||||
hasDigit := false
|
||||
hasSpecial := false
|
||||
|
||||
specialChars := "!@#$%^&*()_+-=[]{}|;:,.<>?"
|
||||
|
||||
for _, char := range password {
|
||||
switch {
|
||||
case char >= 'A' && char <= 'Z':
|
||||
hasUpper = true
|
||||
case char >= 'a' && char <= 'z':
|
||||
hasLower = true
|
||||
case char >= '0' && char <= '9':
|
||||
hasDigit = true
|
||||
case strings.ContainsRune(specialChars, char):
|
||||
hasSpecial = true
|
||||
}
|
||||
}
|
||||
|
||||
return hasUpper && hasLower && hasDigit && hasSpecial
|
||||
}
|
||||
Reference in New Issue
Block a user