diff --git a/gitea_status_proxy/config.go b/gitea_status_proxy/config.go index 19610db..2ca8cc8 100644 --- a/gitea_status_proxy/config.go +++ b/gitea_status_proxy/config.go @@ -11,7 +11,6 @@ import ( type Config struct { ForgeEndpoint string `json:"forge_url"` - ForgeToken string `json:"token"` Keys []string `json:"keys"` } diff --git a/gitea_status_proxy/handlers.go b/gitea_status_proxy/handlers.go index 9e0a196..35b333c 100644 --- a/gitea_status_proxy/handlers.go +++ b/gitea_status_proxy/handlers.go @@ -3,10 +3,6 @@ package main import ( "context" "net/http" - "slices" - "strings" - - "src.opensuse.org/autogits/common" ) func ConfigMiddleWare(cfg *Config) func(http.Handler) http.Handler { @@ -17,38 +13,3 @@ func ConfigMiddleWare(cfg *Config) func(http.Handler) http.Handler { }) } } - -func ProxyAuthMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - header := r.Header.Get("Authorization") - if header == "" { - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return - } - token_arr := strings.Split(header, " ") - if len(token_arr) != 2 { - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return - } - - if !strings.EqualFold(token_arr[0], "Bearer") { - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return - } - - token := token_arr[1] - config, ok := r.Context().Value(configKey).(*Config) - - if !ok { - common.LogError("Config missing from context") - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - if !slices.Contains(config.Keys, token) { - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return - } - next.ServeHTTP(w, r) - }) -} diff --git a/gitea_status_proxy/main.go b/gitea_status_proxy/main.go index 727943a..3ac6b0b 100644 --- a/gitea_status_proxy/main.go +++ b/gitea_status_proxy/main.go @@ -7,19 +7,22 @@ import ( "fmt" "io" "net/http" - "time" + "os" + "slices" + "strings" "src.opensuse.org/autogits/common" - - "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" ) type Status struct { - Context string `json:"context"` - Description string `json:"description"` - State string `json:"state"` - TargetUrl string `json:"target_url"` + Context string `json:"context"` + State string `json:"state"` + TargetUrl string `json:"target_url"` +} + +type StatusInput struct { + State string `json:"state"` + TargetUrl string `json:"target_url"` } func main() { @@ -38,28 +41,12 @@ func main() { return } - r := chi.NewRouter() + mux := http.NewServeMux() - r.Use(ConfigMiddleWare(config)) - - r.Use(middleware.RequestID) - r.Use(middleware.RealIP) - r.Use(middleware.Logger) - r.Use(middleware.Recoverer) - - r.Use(middleware.Timeout(60 * time.Second)) - - r.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("hi")) - }) - - r.Route("/repos/{owner}/{repo}/statuses/{sha}", func(r chi.Router) { - r.Use(ProxyAuthMiddleware) - r.Post("/", StatusProxy) - }) + mux.Handle("/repos/{owner}/{repo}/statuses/{sha}", ConfigMiddleWare(config)(http.HandlerFunc(StatusProxy))) common.LogInfo("server up and listening on :3000") - err = http.ListenAndServe(":3000", r) + err = http.ListenAndServe(":3000", mux) if err != nil { common.LogError("Server failed to start up", err) @@ -68,67 +55,115 @@ func main() { } func StatusProxy(w http.ResponseWriter, r *http.Request) { - owner := chi.URLParam(r, "owner") - repo := chi.URLParam(r, "repo") - sha := chi.URLParam(r, "sha") + if r.Method == http.MethodPost { + config, ok := r.Context().Value(configKey).(*Config) - config, ok := r.Context().Value(configKey).(*Config) - if !ok { - common.LogError("Failed to get config from context, is it set?") - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - - posturl := fmt.Sprintf("%s/repos/%s/%s/statuses/%s", config.ForgeEndpoint, owner, repo, sha) - decoder := json.NewDecoder(r.Body) - var status Status - err := decoder.Decode(&status) - if err != nil { - http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) - return - } - status_payload, err := json.Marshal(status) - - if err != nil { - http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) - return - } - client := &http.Client{} - req, err := http.NewRequest("POST", posturl, bytes.NewBuffer(status_payload)) - - if err != nil { - http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) - return - } - - req.Header.Add("Content-Type", "Content-Type") - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", config.ForgeToken)) - - resp, err := client.Do(req) - - if err != nil { - common.LogError(fmt.Sprintf("Request to forge endpoint failed: %v", err)) - http.Error(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway) - return - } - defer resp.Body.Close() - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(resp.StatusCode) - - /* - the commented out section sets every key - value from the headers, unsure if this - leaks information from gitea - - for k, v := range resp.Header { - for _, vv := range v { - w.Header().Add(k, vv) - } + if !ok { + common.LogError("Config missing from context") + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return } - */ - _, err = io.Copy(w, resp.Body) - if err != nil { - common.LogError("Error copying response body: %v", err) + header := r.Header.Get("Authorization") + if header == "" { + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + return + } + token_arr := strings.Split(header, " ") + if len(token_arr) != 2 { + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + return + } + + if !strings.EqualFold(token_arr[0], "Bearer") { + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + return + } + + token := token_arr[1] + + if !slices.Contains(config.Keys, token) { + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + return + } + + owner := r.PathValue("owner") + repo := r.PathValue("repo") + sha := r.PathValue("sha") + + if !ok { + common.LogError("Failed to get config from context, is it set?") + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + } + + posturl := fmt.Sprintf("%s/repos/%s/%s/statuses/%s", config.ForgeEndpoint, owner, repo, sha) + decoder := json.NewDecoder(r.Body) + var statusinput StatusInput + err := decoder.Decode(&statusinput) + if err != nil { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + return + } + status := Status{ + Context: "Build in obs", + State: statusinput.State, + TargetUrl: statusinput.TargetUrl, + } + + status_payload, err := json.Marshal(status) + + if err != nil { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + return + } + client := &http.Client{} + req, err := http.NewRequest("POST", posturl, bytes.NewBuffer(status_payload)) + + if err != nil { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + return + } + ForgeToken := os.Getenv("GITEA_TOKEN") + + if ForgeToken == "" { + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + common.LogError("GITEA_TOKEN was not set, all requests will fail") + return + } + + req.Header.Add("Content-Type", "Content-Type") + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", ForgeToken)) + + resp, err := client.Do(req) + + if err != nil { + common.LogError(fmt.Sprintf("Request to forge endpoint failed: %v", err)) + http.Error(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway) + return + } + defer resp.Body.Close() + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(resp.StatusCode) + + /* + the commented out section sets every key + value from the headers, unsure if this + leaks information from gitea + + for k, v := range resp.Header { + for _, vv := range v { + w.Header().Add(k, vv) + } + } + */ + + _, err = io.Copy(w, resp.Body) + if err != nil { + common.LogError("Error copying response body: %v", err) + } + } else { + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return } } diff --git a/go.mod b/go.mod index 1617425..53a9efc 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ require ( require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/go-chi/chi/v5 v5.2.1 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.23.0 // indirect diff --git a/go.sum b/go.sum index 7e8325d..37e0f65 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,6 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= -github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=