Add RBAC support with roles, permissions, and session management. Implement middleware for authentication and CSRF protection. Enhance audit logging with additional fields. Update HTTP handlers and routes for new features.
This commit is contained in:
108
internal/auth/session.go
Normal file
108
internal/auth/session.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
SessionCookieName = "session_token"
|
||||
SessionDuration = 24 * time.Hour
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
ID string
|
||||
UserID string
|
||||
Token string
|
||||
ExpiresAt time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type SessionStore struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
func NewSessionStore(db *sql.DB) *SessionStore {
|
||||
return &SessionStore{DB: db}
|
||||
}
|
||||
|
||||
// GenerateToken generates a secure random token
|
||||
func GenerateToken() (string, error) {
|
||||
b := make([]byte, 32)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
// CreateSession creates a new session for a user
|
||||
func (s *SessionStore) CreateSession(ctx context.Context, userID string) (*Session, error) {
|
||||
token, err := GenerateToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sessionID := uuid.New().String()
|
||||
expiresAt := time.Now().Add(SessionDuration)
|
||||
|
||||
_, err = s.DB.ExecContext(ctx,
|
||||
`INSERT INTO sessions (id, user_id, token, expires_at) VALUES (?, ?, ?, ?)`,
|
||||
sessionID, userID, token, expiresAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Session{
|
||||
ID: sessionID,
|
||||
UserID: userID,
|
||||
Token: token,
|
||||
ExpiresAt: expiresAt,
|
||||
CreatedAt: time.Now(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetSession retrieves a session by token
|
||||
func (s *SessionStore) GetSession(ctx context.Context, token string) (*Session, error) {
|
||||
var session Session
|
||||
var expiresAtStr string
|
||||
err := s.DB.QueryRowContext(ctx,
|
||||
`SELECT id, user_id, token, expires_at, created_at FROM sessions WHERE token = ? AND expires_at > ?`,
|
||||
token, time.Now()).Scan(&session.ID, &session.UserID, &session.Token, &expiresAtStr, &session.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session.ExpiresAt, err = time.Parse("2006-01-02 15:04:05", expiresAtStr)
|
||||
if err != nil {
|
||||
// Try with timezone
|
||||
session.ExpiresAt, err = time.Parse(time.RFC3339, expiresAtStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &session, nil
|
||||
}
|
||||
|
||||
// DeleteSession deletes a session by token
|
||||
func (s *SessionStore) DeleteSession(ctx context.Context, token string) error {
|
||||
_, err := s.DB.ExecContext(ctx, `DELETE FROM sessions WHERE token = ?`, token)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteUserSessions deletes all sessions for a user
|
||||
func (s *SessionStore) DeleteUserSessions(ctx context.Context, userID string) error {
|
||||
_, err := s.DB.ExecContext(ctx, `DELETE FROM sessions WHERE user_id = ?`, userID)
|
||||
return err
|
||||
}
|
||||
|
||||
// CleanupExpiredSessions removes expired sessions
|
||||
func (s *SessionStore) CleanupExpiredSessions(ctx context.Context) error {
|
||||
_, err := s.DB.ExecContext(ctx, `DELETE FROM sessions WHERE expires_at < ?`, time.Now())
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user