feat: migrate backend from rust to go
This commit replaces the Axum/Rust backend with a Gin/Go implementation. The original Rust code has been archived in the 'rust' branch.
This commit is contained in:
151
internal/server/sessions.go
Normal file
151
internal/server/sessions.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
Username string `json:"username"`
|
||||
Role string `json:"role"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
SessionID string `json:"session_id"`
|
||||
}
|
||||
|
||||
type SessionManager struct {
|
||||
sessions map[string]Session
|
||||
mu sync.RWMutex
|
||||
secret []byte
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
type sessionPayload struct {
|
||||
SessionID string `json:"session_id"`
|
||||
Username string `json:"username"`
|
||||
Role string `json:"role"`
|
||||
Exp int64 `json:"exp"`
|
||||
}
|
||||
|
||||
func NewSessionManager(secret []byte, ttl time.Duration) *SessionManager {
|
||||
return &SessionManager{
|
||||
sessions: make(map[string]Session),
|
||||
secret: secret,
|
||||
ttl: ttl,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SessionManager) CreateSession(username, role string) (string, error) {
|
||||
sessionID := uuid.New().String()
|
||||
now := time.Now()
|
||||
expiresAt := now.Add(m.ttl)
|
||||
|
||||
m.mu.Lock()
|
||||
m.sessions[sessionID] = Session{
|
||||
Username: username,
|
||||
Role: role,
|
||||
CreatedAt: now,
|
||||
ExpiresAt: expiresAt,
|
||||
SessionID: sessionID,
|
||||
}
|
||||
m.mu.Unlock()
|
||||
|
||||
return m.createSignedToken(sessionID, username, role, expiresAt.Unix())
|
||||
}
|
||||
|
||||
func (m *SessionManager) createSignedToken(sessionID, username, role string, exp int64) (string, error) {
|
||||
payload := sessionPayload{
|
||||
SessionID: sessionID,
|
||||
Username: username,
|
||||
Role: role,
|
||||
Exp: exp,
|
||||
}
|
||||
|
||||
payloadJSON, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
payloadB64 := base64.RawURLEncoding.EncodeToString(payloadJSON)
|
||||
|
||||
h := hmac.New(sha256.New, m.secret)
|
||||
h.Write(payloadJSON)
|
||||
signature := h.Sum(nil)
|
||||
signatureB64 := base64.RawURLEncoding.EncodeToString(signature)
|
||||
|
||||
return fmt.Sprintf("%s.%s", payloadB64, signatureB64), nil
|
||||
}
|
||||
|
||||
func (m *SessionManager) ValidateSession(token string) (*Session, string, error) {
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 2 {
|
||||
return nil, "", fmt.Errorf("invalid token format")
|
||||
}
|
||||
|
||||
payloadB64 := parts[0]
|
||||
signatureB64 := parts[1]
|
||||
|
||||
payloadJSON, err := base64.RawURLEncoding.DecodeString(payloadB64)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
signature, err := base64.RawURLEncoding.DecodeString(signatureB64)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
h := hmac.New(sha256.New, m.secret)
|
||||
h.Write(payloadJSON)
|
||||
if !hmac.Equal(signature, h.Sum(nil)) {
|
||||
return nil, "", fmt.Errorf("invalid signature")
|
||||
}
|
||||
|
||||
var payload sessionPayload
|
||||
if err := json.Unmarshal(payloadJSON, &payload); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if time.Now().Unix() > payload.Exp {
|
||||
return nil, "", fmt.Errorf("token expired")
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
session, ok := m.sessions[payload.SessionID]
|
||||
m.mu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return nil, "", fmt.Errorf("session not found")
|
||||
}
|
||||
|
||||
return &session, "", nil
|
||||
}
|
||||
|
||||
func (m *SessionManager) RevokeSession(token string) {
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 2 {
|
||||
return
|
||||
}
|
||||
|
||||
payloadJSON, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var payload sessionPayload
|
||||
if err := json.Unmarshal(payloadJSON, &payload); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
delete(m.sessions, payload.SessionID)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
Reference in New Issue
Block a user