You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

72 lines
2.3 KiB

package handlers
import (
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"strings"
"time"
)
type loginStartupStatus struct {
ClockSyncToken string `json:"clock_sync_token,omitempty"`
Ready bool `json:"ready"`
State string `json:"state"`
ElapsedMS int64 `json:"elapsed_ms"`
Completed int `json:"completed"`
Total int `json:"total"`
Stages []struct {
ID string `json:"id"`
Label string `json:"label"`
State string `json:"state"`
} `json:"stages"`
CaptchaEnabled bool `json:"captcha_enabled"`
}
func (h *DeviceHandler) handleBootstrapTime(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "no-store")
// Custom header + JSON require a CORS preflight for cross-origin browsers.
if r.Method != http.MethodPost || r.Header.Get("X-Clock-Bootstrap") != "1" || !strings.HasPrefix(r.Header.Get("Content-Type"), "application/json") {
http.Error(w, "invalid clock initialization request", http.StatusForbidden)
return
}
if origin := r.Header.Get("Origin"); origin != "" {
u, err := url.Parse(origin)
if err != nil || u.Host != r.Host || (u.Scheme != "http" && u.Scheme != "https") {
http.Error(w, "invalid origin", http.StatusForbidden)
return
}
}
ctx, cancel := context.WithTimeout(r.Context(), 6*time.Second)
defer cancel()
r.Body = http.MaxBytesReader(w, r.Body, 1024)
payload, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "invalid body", http.StatusBadRequest)
return
}
endpoint := h.apiTarget.ResolveReference(&url.URL{Path: "/api/v1/auth/bootstrap-time"})
request, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), strings.NewReader(string(payload)))
if err != nil {
http.Error(w, "clock service unavailable", 502)
return
}
request.Header.Set("Content-Type", "application/json")
response, err := h.apiClient.Do(request)
if err != nil {
http.Error(w, "clock service unavailable", 502)
return
}
defer response.Body.Close()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(response.StatusCode)
_, _ = io.Copy(w, io.LimitReader(response.Body, 4096))
}
func writeStartupResponse(w http.ResponseWriter, state loginStartupStatus) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_ = json.NewEncoder(w).Encode(state)
}