mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-29 20:27:16 +00:00
107 lines
2.3 KiB
Go
107 lines
2.3 KiB
Go
package labeler
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"net/http"
|
|
"sync"
|
|
)
|
|
|
|
// Session represents an authenticated admin session.
|
|
type Session struct {
|
|
DID string
|
|
Handle string
|
|
}
|
|
|
|
// Auth manages admin authentication.
|
|
type Auth struct {
|
|
ownerDID string
|
|
sessions map[string]*Session
|
|
sessionsMu sync.RWMutex
|
|
}
|
|
|
|
// NewAuth creates a new Auth manager.
|
|
func NewAuth(ownerDID string) *Auth {
|
|
return &Auth{
|
|
ownerDID: ownerDID,
|
|
sessions: make(map[string]*Session),
|
|
}
|
|
}
|
|
|
|
func (a *Auth) createSession(did, handle string) (string, error) {
|
|
b := make([]byte, 32)
|
|
if _, err := rand.Read(b); err != nil {
|
|
return "", err
|
|
}
|
|
token := base64.URLEncoding.EncodeToString(b)
|
|
|
|
a.sessionsMu.Lock()
|
|
a.sessions[token] = &Session{DID: did, Handle: handle}
|
|
a.sessionsMu.Unlock()
|
|
|
|
return token, nil
|
|
}
|
|
|
|
func (a *Auth) getSession(token string) *Session {
|
|
a.sessionsMu.RLock()
|
|
defer a.sessionsMu.RUnlock()
|
|
return a.sessions[token]
|
|
}
|
|
|
|
func (a *Auth) deleteSession(token string) {
|
|
a.sessionsMu.Lock()
|
|
delete(a.sessions, token)
|
|
a.sessionsMu.Unlock()
|
|
}
|
|
|
|
const sessionCookieName = "labeler_session"
|
|
|
|
func setSessionCookie(w http.ResponseWriter, r *http.Request, token string) {
|
|
secure := r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https"
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: sessionCookieName,
|
|
Value: token,
|
|
Path: "/",
|
|
MaxAge: 86400, // 24 hours
|
|
HttpOnly: true,
|
|
Secure: secure,
|
|
SameSite: http.SameSiteLaxMode,
|
|
})
|
|
}
|
|
|
|
func clearSessionCookie(w http.ResponseWriter) {
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: sessionCookieName,
|
|
Value: "",
|
|
Path: "/",
|
|
MaxAge: -1,
|
|
HttpOnly: true,
|
|
SameSite: http.SameSiteLaxMode,
|
|
})
|
|
}
|
|
|
|
func getSessionCookie(r *http.Request) (string, bool) {
|
|
cookie, err := r.Cookie(sessionCookieName)
|
|
if err != nil {
|
|
return "", false
|
|
}
|
|
return cookie.Value, true
|
|
}
|
|
|
|
// RequireOwner is middleware that checks the session belongs to the owner DID.
|
|
func (a *Auth) RequireOwner(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
token, ok := getSessionCookie(r)
|
|
if !ok {
|
|
http.Redirect(w, r, "/auth/login", http.StatusFound)
|
|
return
|
|
}
|
|
session := a.getSession(token)
|
|
if session == nil || session.DID != a.ownerDID {
|
|
http.Redirect(w, r, "/auth/login", http.StatusFound)
|
|
return
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|