package httpserver import ( "net/http" "net/url" "strings" "git.yuntuo.hk/k2safe/orbit/internal/consoleauth" "git.yuntuo.hk/k2safe/orbit/internal/fault" core "git.yuntuo.hk/k2safe/orbit/model" "git.yuntuo.hk/k2safe/orbit/platform/auth" ) // RegisterConsoleApplications 只接受登录 Cookie;业务 Bearer 不能访问跨应用管理。 func RegisterConsoleApplications(server *http.Server, consoleAuth consoleauth.Store, store consoleauth.Applications) { if server == nil || consoleAuth == nil || store == nil { return } mux, ok := server.Handler.(*http.ServeMux) if !ok { return } check := func(w http.ResponseWriter, r *http.Request) (auth.Token, bool) { w.Header().Set("Cache-Control", "no-store") w.Header().Set("X-Content-Type-Options", "nosniff") token, ok := consoleCookieToken(r) if !ok || consoleAuth.ValidateSession(r.Context(), token) != nil { writeError(w, "", fault.Unauthorized) return auth.Token{}, false } // 自定义请求头触发跨域预检;不开放 CORS,另校验浏览器 Origin 和 Fetch Metadata。 if r.Method != "GET" { if r.Header.Get("X-Orbit-Console") != "1" || r.Header.Get("Sec-Fetch-Site") == "cross-site" { writeError(w, "", fault.Forbidden) return auth.Token{}, false } if origin := r.Header.Get("Origin"); origin != "" { u, err := url.Parse(origin) if err != nil || (u.Scheme != "http" && u.Scheme != "https") || !strings.EqualFold(u.Host, r.Host) || u.User != nil { writeError(w, "", fault.Forbidden) return auth.Token{}, false } } } return token, true } if auditor, ok := store.(consoleauth.LoginAuditor); ok { mux.HandleFunc("GET /console/auth/audit", func(w http.ResponseWriter, r *http.Request) { token, ok := check(w, r) if !ok { return } out, err := auditor.ListLoginAudit(r.Context(), token, r.URL.Query().Get("before")) if err != nil { writeError(w, "", err) return } writeConsoleJSON(w, 200, map[string]any{"data": out}) }) } mux.HandleFunc("GET /console/applications", func(w http.ResponseWriter, r *http.Request) { token, ok := check(w, r) if !ok { return } out, err := store.ListApplications(r.Context(), token, core.ID(r.URL.Query().Get("after"))) if err != nil { writeError(w, "", err) return } writeConsoleJSON(w, 200, map[string]any{"data": out}) }) mux.HandleFunc("GET /console/applications/{namespace}", func(w http.ResponseWriter, r *http.Request) { token, ok := check(w, r) if !ok { return } out, err := store.GetApplication(r.Context(), token, core.ID(r.PathValue("namespace"))) if err != nil { writeError(w, "", err) return } writeConsoleJSON(w, 200, map[string]any{"data": out}) }) mux.HandleFunc("POST /console/applications/prepare", func(w http.ResponseWriter, r *http.Request) { token, ok := check(w, r) if !ok { return } var in consoleauth.ApplicationInput if err := decodeBody(r, &in); err != nil { writeError(w, "", err) return } out, err := store.PrepareApplication(r.Context(), token, in) if err != nil { writeError(w, "", err) return } writeConsoleJSON(w, 200, map[string]any{"data": out}) }) mux.HandleFunc("POST /console/applications/apply", func(w http.ResponseWriter, r *http.Request) { token, ok := check(w, r) if !ok { return } var in consoleauth.ApplicationOperation if err := decodeBody(r, &in); err != nil { writeError(w, "", err) return } out, err := store.ApplyApplication(r.Context(), token, in) if err != nil { writeError(w, "", err) return } writeConsoleJSON(w, 200, map[string]any{"data": out}) }) if secrets, ok := store.(consoleauth.CredentialTokens); ok { for _, action := range []string{"reveal", "import"} { mux.HandleFunc("POST /console/applications/{namespace}/credentials/{credential}/token/"+action, func(w http.ResponseWriter, r *http.Request) { token, ok := check(w, r) if !ok { return } var in consoleauth.CredentialTokenInput if err := decodeBody(r, &in); err != nil { writeError(w, "", err) return } out, err := secrets.AccessCredentialToken(r.Context(), token, core.ID(r.PathValue("namespace")), core.ID(r.PathValue("credential")), in, action == "import") if err != nil { writeError(w, "", err) return } writeConsoleJSON(w, 200, map[string]any{"data": out}) }) } } }