init
This commit is contained in:
commit
b15b95781c
108 changed files with 14802 additions and 0 deletions
111
internal/http/api_key_middleware.go
Normal file
111
internal/http/api_key_middleware.go
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package httpserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"update_server/internal/apikeys"
|
||||
)
|
||||
|
||||
func (h *handler) requireAPIKey(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if h.apiKeys == nil {
|
||||
writeJSON(w, http.StatusServiceUnavailable, map[string]any{"error": "api key auth is unavailable"})
|
||||
return
|
||||
}
|
||||
|
||||
token, ok := bearerToken(r.Header.Get("Authorization"))
|
||||
if !ok {
|
||||
writeAPIKeyAuthError(w, http.StatusUnauthorized, "missing bearer api key")
|
||||
return
|
||||
}
|
||||
|
||||
state, err := h.apiKeys.Authenticate(r.Context(), token)
|
||||
if err != nil {
|
||||
if errors.Is(err, apikeys.ErrUnauthenticated) {
|
||||
writeAPIKeyAuthError(w, http.StatusUnauthorized, "invalid api key")
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]any{"error": "api key lookup failed"})
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(apikeys.NewContext(r.Context(), state)))
|
||||
})
|
||||
}
|
||||
|
||||
func (h *handler) requireAPIKeyPermission(permission apikeys.Permission) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
state, ok := apikeys.FromContext(r.Context())
|
||||
if !ok {
|
||||
writeAPIKeyAuthError(w, http.StatusUnauthorized, "api key authentication required")
|
||||
return
|
||||
}
|
||||
|
||||
if !apikeys.HasPermission(state.APIKey, permission) {
|
||||
writeJSON(w, http.StatusForbidden, map[string]any{"error": "api key permission denied"})
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) requireAPIKeyProjectAccess(routeParam string) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
state, ok := apikeys.FromContext(r.Context())
|
||||
if !ok {
|
||||
writeAPIKeyAuthError(w, http.StatusUnauthorized, "api key authentication required")
|
||||
return
|
||||
}
|
||||
|
||||
projectID, err := routeID(r, routeParam)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid project id"})
|
||||
return
|
||||
}
|
||||
|
||||
allowed, err := h.apiKeys.CanAccessProject(r.Context(), state.APIKey, projectID)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]any{"error": "project access lookup failed"})
|
||||
return
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
writeJSON(w, http.StatusForbidden, map[string]any{"error": "api key cannot access this project"})
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func bearerToken(header string) (string, bool) {
|
||||
header = strings.TrimSpace(header)
|
||||
if header == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
parts := strings.Fields(header)
|
||||
if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") {
|
||||
return "", false
|
||||
}
|
||||
|
||||
token := strings.TrimSpace(parts[1])
|
||||
if token == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return token, true
|
||||
}
|
||||
|
||||
func writeAPIKeyAuthError(w http.ResponseWriter, status int, message string) {
|
||||
w.Header().Set("WWW-Authenticate", `Bearer realm="update-server"`)
|
||||
writeJSON(w, status, map[string]any{"error": message})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue