Files
argoyle 9992fb4ef1 feat: migrate auth0mock from Node.js to Go
Refactor the application to a Go-based architecture for improved
performance and maintainability. Replace the Dockerfile to utilize a
multi-stage build process, enhancing image efficiency. Implement
comprehensive session store tests to ensure reliability and create
new OAuth handlers for managing authentication efficiently. Update 
documentation to reflect these structural changes.
2025-12-29 16:30:37 +01:00

129 lines
2.6 KiB
Go

package store
import (
"encoding/json"
"fmt"
"os"
"sync"
)
// User represents a user in the system
type User struct {
Email string `json:"email"`
GivenName string `json:"given_name"`
FamilyName string `json:"family_name"`
UserID string `json:"user_id"`
Picture string `json:"picture,omitempty"`
}
// UserStore provides thread-safe user storage
type UserStore struct {
mu sync.RWMutex
users map[string]*User
}
// NewUserStore creates a new user store
func NewUserStore() *UserStore {
return &UserStore{
users: make(map[string]*User),
}
}
// LoadFromFile loads users from a JSON file
func (s *UserStore) LoadFromFile(path string) error {
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil // File doesn't exist, start with empty store
}
return fmt.Errorf("read users file: %w", err)
}
var rawUsers map[string]json.RawMessage
if err := json.Unmarshal(data, &rawUsers); err != nil {
return fmt.Errorf("parse users file: %w", err)
}
s.mu.Lock()
defer s.mu.Unlock()
for email, raw := range rawUsers {
var user User
if err := json.Unmarshal(raw, &user); err != nil {
return fmt.Errorf("parse user %s: %w", email, err)
}
user.Email = email // Ensure email is set
if user.UserID == "" {
user.UserID = email
}
s.users[email] = &user
}
return nil
}
// GetByEmail retrieves a user by email
func (s *UserStore) GetByEmail(email string) (*User, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
user, ok := s.users[email]
if !ok {
return nil, false
}
// Return a copy to prevent external modification
userCopy := *user
return &userCopy, true
}
// Create adds a new user
func (s *UserStore) Create(email string, user *User) {
s.mu.Lock()
defer s.mu.Unlock()
user.Email = email
if user.UserID == "" {
user.UserID = email
}
s.users[email] = user
}
// Update modifies an existing user
func (s *UserStore) Update(email string, updates *User) (*User, bool) {
s.mu.Lock()
defer s.mu.Unlock()
existing, ok := s.users[email]
if !ok {
return nil, false
}
// Apply updates (only non-empty fields)
if updates.GivenName != "" {
existing.GivenName = updates.GivenName
}
if updates.FamilyName != "" {
existing.FamilyName = updates.FamilyName
}
if updates.Picture != "" {
existing.Picture = updates.Picture
}
// Return a copy
userCopy := *existing
return &userCopy, true
}
// List returns all users
func (s *UserStore) List() []*User {
s.mu.RLock()
defer s.mu.RUnlock()
users := make([]*User, 0, len(s.users))
for _, user := range s.users {
userCopy := *user
users = append(users, &userCopy)
}
return users
}