Foundation: config, schema/migrations, auth, stable IDs, metadata probing
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Middleware resolves the current user for each request, from a session
|
||||
// cookie first and falling back to trusted external auth, then makes it
|
||||
// available via UserFromContext.
|
||||
type Middleware struct {
|
||||
Sessions *Sessions
|
||||
ExtAuth *ExtAuth
|
||||
}
|
||||
|
||||
// Authenticate resolves the current user (session cookie, then trusted
|
||||
// external auth) and stores it in the request context, without requiring
|
||||
// one to be present. Handlers that need to know whether someone is
|
||||
// signed in (e.g. the login page, the home page) should use this.
|
||||
func (m *Middleware) Authenticate(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user := m.Sessions.CurrentUser(r)
|
||||
if user == nil && m.ExtAuth != nil {
|
||||
if username := m.ExtAuth.IdentityFromRequest(r); username != "" {
|
||||
if u, err := m.ExtAuth.ResolveUser(username); err == nil && !u.Disabled {
|
||||
user = u
|
||||
_ = m.Sessions.Issue(w, r, u.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
if user != nil {
|
||||
r = r.WithContext(WithUser(r.Context(), user))
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// RequireUser redirects unauthenticated requests to /login, preserving
|
||||
// the original destination.
|
||||
func (m *Middleware) RequireUser(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if UserFromContext(r.Context()) == nil {
|
||||
http.Redirect(w, r, "/login?next="+url.QueryEscape(r.URL.RequestURI()), http.StatusFound)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// RequireAdmin is like RequireUser but also rejects non-admin users with
|
||||
// 403. Authorization is enforced here, on the server, not merely by
|
||||
// hiding admin links in the UI.
|
||||
func (m *Middleware) RequireAdmin(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user := UserFromContext(r.Context())
|
||||
if user == nil {
|
||||
http.Redirect(w, r, "/login?next="+url.QueryEscape(r.URL.RequestURI()), http.StatusFound)
|
||||
return
|
||||
}
|
||||
if !user.IsAdmin {
|
||||
http.Error(w, "403 Forbidden: administrator access required", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user