Files
auth0mock/store/users.go
T

129 lines
2.6 KiB
Go
Raw Normal View History

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
}