205 lines
4.3 KiB
Go
205 lines
4.3 KiB
Go
package auth
|
|
|
|
import (
|
|
"dk/internal/password"
|
|
"dk/internal/users"
|
|
)
|
|
|
|
// Manager is the global singleton instance
|
|
var Manager *AuthManager
|
|
|
|
// AuthManager is a wrapper for the session store to add
|
|
// authentication tools over the store itself
|
|
type AuthManager struct {
|
|
store *SessionStore
|
|
}
|
|
|
|
// Init initializes the global auth manager (auth.Manager)
|
|
func Init(sessionsFilePath string) {
|
|
Manager = &AuthManager{
|
|
store: NewSessionStore(sessionsFilePath),
|
|
}
|
|
}
|
|
|
|
// Authenticate checks for the usernaname or email, then verifies the plain password
|
|
// against the stored hash.
|
|
func (am *AuthManager) Authenticate(usernameOrEmail, plainPassword string) (*users.User, error) {
|
|
var user *users.User
|
|
var err error
|
|
|
|
// Try to find user by username first
|
|
user, err = users.GetByUsername(usernameOrEmail)
|
|
if err != nil {
|
|
user, err = users.GetByEmail(usernameOrEmail)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
isValid, err := password.Verify(plainPassword, user.Password)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !isValid {
|
|
return nil, ErrInvalidCredentials
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
func (am *AuthManager) CreateSession(user *users.User) *Session {
|
|
return am.store.Create(user.ID, user.Username, user.Email)
|
|
}
|
|
|
|
func (am *AuthManager) GetSession(sessionID string) (*Session, bool) {
|
|
return am.store.Get(sessionID)
|
|
}
|
|
|
|
func (am *AuthManager) UpdateSession(sessionID string) bool {
|
|
return am.store.Update(sessionID)
|
|
}
|
|
|
|
func (am *AuthManager) DeleteSession(sessionID string) {
|
|
am.store.Delete(sessionID)
|
|
}
|
|
|
|
func (am *AuthManager) SessionStats() (total, active int) {
|
|
return am.store.Stats()
|
|
}
|
|
|
|
func (am *AuthManager) Close() error {
|
|
return am.store.Close()
|
|
}
|
|
|
|
// SetFlash stores a flash message in the session that will be removed after retrieval
|
|
func (am *AuthManager) SetFlash(sessionID, key string, value any) bool {
|
|
session, exists := am.store.Get(sessionID)
|
|
if !exists {
|
|
return false
|
|
}
|
|
|
|
am.store.mu.Lock()
|
|
defer am.store.mu.Unlock()
|
|
|
|
if session.Data == nil {
|
|
session.Data = make(map[string]any)
|
|
}
|
|
|
|
// Store flash messages under a special key
|
|
flashData, ok := session.Data["_flash"].(map[string]any)
|
|
if !ok {
|
|
flashData = make(map[string]any)
|
|
}
|
|
flashData[key] = value
|
|
session.Data["_flash"] = flashData
|
|
|
|
return true
|
|
}
|
|
|
|
// GetFlash retrieves and removes a flash message from the session
|
|
func (am *AuthManager) GetFlash(sessionID, key string) (any, bool) {
|
|
session, exists := am.store.Get(sessionID)
|
|
if !exists {
|
|
return nil, false
|
|
}
|
|
|
|
am.store.mu.Lock()
|
|
defer am.store.mu.Unlock()
|
|
|
|
if session.Data == nil {
|
|
return nil, false
|
|
}
|
|
|
|
flashData, ok := session.Data["_flash"].(map[string]any)
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
value, exists := flashData[key]
|
|
if exists {
|
|
delete(flashData, key)
|
|
if len(flashData) == 0 {
|
|
delete(session.Data, "_flash")
|
|
} else {
|
|
session.Data["_flash"] = flashData
|
|
}
|
|
}
|
|
|
|
return value, exists
|
|
}
|
|
|
|
// GetAllFlash retrieves and removes all flash messages from the session
|
|
func (am *AuthManager) GetAllFlash(sessionID string) map[string]any {
|
|
session, exists := am.store.Get(sessionID)
|
|
if !exists {
|
|
return nil
|
|
}
|
|
|
|
am.store.mu.Lock()
|
|
defer am.store.mu.Unlock()
|
|
|
|
if session.Data == nil {
|
|
return nil
|
|
}
|
|
|
|
flashData, ok := session.Data["_flash"].(map[string]any)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
// Remove flash data from session
|
|
delete(session.Data, "_flash")
|
|
|
|
return flashData
|
|
}
|
|
|
|
// SetSessionData stores arbitrary data in the session
|
|
func (am *AuthManager) SetSessionData(sessionID, key string, value any) bool {
|
|
session, exists := am.store.Get(sessionID)
|
|
if !exists {
|
|
return false
|
|
}
|
|
|
|
am.store.mu.Lock()
|
|
defer am.store.mu.Unlock()
|
|
|
|
if session.Data == nil {
|
|
session.Data = make(map[string]any)
|
|
}
|
|
|
|
session.Data[key] = value
|
|
return true
|
|
}
|
|
|
|
// GetSessionData retrieves data from the session
|
|
func (am *AuthManager) GetSessionData(sessionID, key string) (any, bool) {
|
|
session, exists := am.store.Get(sessionID)
|
|
if !exists {
|
|
return nil, false
|
|
}
|
|
|
|
am.store.mu.RLock()
|
|
defer am.store.mu.RUnlock()
|
|
|
|
if session.Data == nil {
|
|
return nil, false
|
|
}
|
|
|
|
value, exists := session.Data[key]
|
|
return value, exists
|
|
}
|
|
|
|
var (
|
|
ErrInvalidCredentials = &AuthError{"invalid username/email or password"}
|
|
ErrSessionNotFound = &AuthError{"session not found"}
|
|
ErrSessionExpired = &AuthError{"session expired"}
|
|
)
|
|
|
|
type AuthError struct {
|
|
Message string
|
|
}
|
|
|
|
func (e *AuthError) Error() string {
|
|
return e.Message
|
|
}
|