c129cb99fc
Single Go binary serving the API and an embedded keyboard-first UI for promoting fafflix titles into the cheeztv kids tree via hardlinks. - internal/library: hardlink sync, idempotent re-runs, drift reporting, strict single-path-element name validation as the traversal guard - internal/arr: minimal sonarr/radarr v3 client with a 60s list cache and a key-brokered poster proxy - internal/auth: server-side Authentik group enforcement on every route - internal/server: library JSON API, art proxy, health probes, SPA - ui: two-tile landing page, fuzzy-filtered title list, detail panel - Makefile, Dockerfile, .woodpecker pipelines, pre-commit config
56 lines
1.6 KiB
Go
56 lines
1.6 KiB
Go
// Package auth enforces Authentik group membership from the oauth2-proxy
|
|
// identity header. oauth2-proxy already gates the route, but mediamark mutates
|
|
// the on-disk library, so it re-checks the group server-side rather than
|
|
// trusting the front door alone.
|
|
package auth
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"git.unkin.net/unkin/mediamark/internal/config"
|
|
)
|
|
|
|
// Middleware rejects requests whose group header carries none of the allowed
|
|
// groups. header is the request header to read; allowed must be non-empty.
|
|
type Middleware struct {
|
|
header string
|
|
allowed map[string]bool
|
|
}
|
|
|
|
// New builds a Middleware. An empty allowed set denies everything, which is the
|
|
// correct fail-closed behaviour if config validation is ever bypassed.
|
|
func New(header string, allowed []string) *Middleware {
|
|
m := &Middleware{header: header, allowed: make(map[string]bool, len(allowed))}
|
|
for _, g := range allowed {
|
|
m.allowed[g] = true
|
|
}
|
|
return m
|
|
}
|
|
|
|
// Permit reports whether the request carries an allowed group.
|
|
func (m *Middleware) Permit(r *http.Request) bool {
|
|
if len(m.allowed) == 0 {
|
|
return false
|
|
}
|
|
for _, v := range r.Header.Values(m.header) {
|
|
for _, g := range config.ParseGroups(v) {
|
|
if m.allowed[g] {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Wrap gates next behind Permit, answering 403 with a plain body that never
|
|
// echoes the submitted groups back to the caller.
|
|
func (m *Middleware) Wrap(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if !m.Permit(r) {
|
|
http.Error(w, "forbidden: missing required group", http.StatusForbidden)
|
|
return
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|