summaryrefslogtreecommitdiffstats
path: root/internal/auth.go
diff options
context:
space:
mode:
authorAhmed AbdelHalim <[email protected]>2025-09-07 17:50:36 +0200
committerAhmed AbdelHalim <[email protected]>2025-09-07 17:50:36 +0200
commitff63742a2fa138c230fb2e080708d03ed888c248 (patch)
tree10a5061a93cd6a91217d837a0a3a22a0bb17c54e /internal/auth.go
parent17168a489392847794de0aed15fe395230a38261 (diff)
Add basic login and clean up config
Diffstat (limited to 'internal/auth.go')
-rw-r--r--internal/auth.go107
1 files changed, 107 insertions, 0 deletions
diff --git a/internal/auth.go b/internal/auth.go
new file mode 100644
index 0000000..6caa785
--- /dev/null
+++ b/internal/auth.go
@@ -0,0 +1,107 @@
+package internal
+
+import (
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "sync"
+ "time"
+)
+
+type Session struct {
+ Expires time.Time
+}
+
+type SessionManager struct {
+ sessions map[string]*Session
+ mutex sync.RWMutex
+}
+
+func NewSessionManager() *SessionManager {
+ sm := &SessionManager{
+ sessions: make(map[string]*Session),
+ }
+ // Start cleanup goroutine
+ go sm.cleanupExpiredSessions()
+ return sm
+}
+
+// Using the same logic that powers the pi-hole authentication
+// GeneratePasswordHash creates a double SHA256 hash from the password param to
+// validate against config.PasswordHash
+func GeneratePasswordHash(password string) string {
+ first := sha256.Sum256([]byte(password))
+ firstHex := hex.EncodeToString(first[:])
+ second := sha256.Sum256([]byte(firstHex))
+ return hex.EncodeToString(second[:])
+}
+
+func ValidatePassword(password, hash string) bool {
+ return GeneratePasswordHash(password) == hash
+}
+
+func (sm *SessionManager) CreateSession() (string, time.Time, error) {
+ sm.mutex.Lock()
+ defer sm.mutex.Unlock()
+
+ sessionID, err := generateSecureToken()
+ if err != nil {
+ return "", time.Time{}, fmt.Errorf("failed to generate session ID: %w", err)
+ }
+
+ expires := time.Now().Add(1 * time.Hour)
+ sm.sessions[sessionID] = &Session{
+ Expires: expires,
+ }
+
+ return sessionID, expires, nil
+}
+
+func (sm *SessionManager) ValidateSession(sessionID string) (*Session, bool) {
+ sm.mutex.RLock()
+ defer sm.mutex.RUnlock()
+
+ session, exists := sm.sessions[sessionID]
+ if !exists {
+ return nil, false
+ }
+
+ if time.Now().After(session.Expires) {
+ return nil, false
+ }
+
+ return session, true
+}
+
+func (sm *SessionManager) DeleteSession(sessionID string) {
+ sm.mutex.Lock()
+ defer sm.mutex.Unlock()
+ delete(sm.sessions, sessionID)
+}
+
+func generateSecureToken() (string, error) {
+ bytes := make([]byte, 32)
+ _, err := rand.Read(bytes)
+ if err != nil {
+ return "", err
+ }
+ return hex.EncodeToString(bytes), nil
+}
+
+// cleanupExpiredSessions periodically removes expired sessions every 1 hour
+func (sm *SessionManager) cleanupExpiredSessions() {
+ ticker := time.NewTicker(1 * time.Hour)
+ defer ticker.Stop()
+
+ for range ticker.C {
+ sm.mutex.Lock()
+ now := time.Now()
+ for sessionID, session := range sm.sessions {
+ if now.After(session.Expires) {
+ delete(sm.sessions, sessionID)
+ }
+ }
+ sm.mutex.Unlock()
+ }
+}