Compare commits
2 Commits
Author | SHA256 | Date | |
---|---|---|---|
e5d07f0ce6
|
|||
df9478a920
|
46
gitea_status_proxy/config.go
Normal file
46
gitea_status_proxy/config.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/tailscale/hujson"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ForgeEndpoint string `json:"forge_url"`
|
||||||
|
Keys []string `json:"keys"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type contextKey string
|
||||||
|
|
||||||
|
const configKey contextKey = "config"
|
||||||
|
|
||||||
|
func ReadConfig(reader io.Reader) (*Config, error) {
|
||||||
|
data, err := io.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading config data: %w", err)
|
||||||
|
}
|
||||||
|
config := Config{}
|
||||||
|
data, err = hujson.Standardize(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse json: %w", err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &config); err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing json to api keys and target url: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadConfigFile(filename string) (*Config, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot open config file for reading. err: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return ReadConfig(file)
|
||||||
|
}
|
15
gitea_status_proxy/handlers.go
Normal file
15
gitea_status_proxy/handlers.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConfigMiddleWare(cfg *Config) func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := context.WithValue(r.Context(), configKey, cfg)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
169
gitea_status_proxy/main.go
Normal file
169
gitea_status_proxy/main.go
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"src.opensuse.org/autogits/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
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() {
|
||||||
|
configFile := flag.String("config", "", "status proxy config file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *configFile == "" {
|
||||||
|
common.LogError("missing required argument config")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := ReadConfigFile(*configFile)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
common.LogError("Failed to read config file", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
mux.Handle("/repos/{owner}/{repo}/statuses/{sha}", ConfigMiddleWare(config)(http.HandlerFunc(StatusProxy)))
|
||||||
|
|
||||||
|
common.LogInfo("server up and listening on :3000")
|
||||||
|
err = http.ListenAndServe(":3000", mux)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
common.LogError("Server failed to start up", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func StatusProxy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == http.MethodPost {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user