grim/containers/oauth-kludge

Initial proof of concept

2020-11-22, Gary Kramlich
dcbfc4fd58e8
Parents
Children cfb3a5d491b7
Initial proof of concept
  • +2 -0
    .hgignore
  • +9 -0
    go.mod
  • +15 -0
    go.sum
  • +128 -0
    main.go
  • --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/.hgignore Sun Nov 22 03:42:38 2020 -0600
    @@ -0,0 +1,2 @@
    +syntax: glob
    +oauth-kludge
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/go.mod Sun Nov 22 03:42:38 2020 -0600
    @@ -0,0 +1,9 @@
    +module keep.imfreedom.org/grim/oauth-kludge
    +
    +go 1.15
    +
    +require (
    + github.com/go-http-utils/logger v0.0.0-20161128092850-f3a42dcdeae6
    + github.com/kelseyhightower/envconfig v1.4.0
    + github.com/stretchr/testify v1.6.1 // indirect
    +)
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/go.sum Sun Nov 22 03:42:38 2020 -0600
    @@ -0,0 +1,15 @@
    +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
    +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
    +github.com/go-http-utils/logger v0.0.0-20161128092850-f3a42dcdeae6 h1:R/ypabUA7vskKTRSlgP6rMUHTU6PBRgIcHVSU9qQ6qM=
    +github.com/go-http-utils/logger v0.0.0-20161128092850-f3a42dcdeae6/go.mod h1:CpBLxS3WrxouNECP/Y1A3i6qDnUYs8BvcXjgOW4Vqcw=
    +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
    +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
    +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
    +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
    +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
    +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
    +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
    +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
    +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
    +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
    +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/main.go Sun Nov 22 03:42:38 2020 -0600
    @@ -0,0 +1,128 @@
    +package main
    +
    +import (
    + "fmt"
    + "io"
    + "net/http"
    + "net/url"
    + "os"
    + "os/signal"
    + "strings"
    + "syscall"
    + "time"
    +
    + "github.com/go-http-utils/logger"
    + "github.com/kelseyhightower/envconfig"
    +)
    +
    +type config struct {
    + ListenAddr string `envconfig:"LISTEN_ADDR" default:":8080"`
    + Scope string `envconfig:"SCOPE" required:"true"`
    + TokenEndpoint *url.URL `envconfig:"TOKEN_ENDPOINT" required:"true"`
    +}
    +
    +var cfg config
    +var client *http.Client = &http.Client{Timeout: 5 * time.Second}
    +
    +func sendError(w http.ResponseWriter, err error, status int) {
    + w.Header().Set("Content-Type", "application/json")
    + w.WriteHeader(status)
    + w.Write([]byte(fmt.Sprintf("{\"error\":\"%v\"}", err)))
    +}
    +
    +func copyHeader(src, dest http.Header, name string) {
    + if value, found := src[name]; found {
    + dest[name] = value
    + }
    +}
    +
    +func kludge(target *url.URL, scope string) http.Handler {
    + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    + // parse the form
    + if err := r.ParseForm(); err != nil {
    + sendError(w, err, http.StatusBadRequest)
    + return
    + }
    +
    + if _, found := r.Form["scope"]; !found {
    + r.Form.Set("scope", scope)
    + }
    +
    + newBody := strings.NewReader(r.Form.Encode())
    + newRequest, err := http.NewRequest(r.Method, target.String(), newBody)
    + if err != nil {
    + sendError(w, err, http.StatusInternalServerError)
    + return
    + }
    +
    + // Remove the User-Agent header
    + newRequest.Header.Set("User-Agent", "")
    +
    + // Copy the Content-Type header
    + newRequest.Header.Set("Content-Type", r.Header.Get("Content-Type"))
    +
    + resp, err := client.Do(newRequest)
    + if err != nil {
    + sendError(w, err, http.StatusInternalServerError)
    + return
    + }
    +
    + src := resp.Header
    + dest := w.Header()
    + copyHeader(src, dest, "Content-Type")
    + copyHeader(src, dest, "Cache-Control")
    + copyHeader(src, dest, "Pragma")
    + w.WriteHeader(resp.StatusCode)
    +
    + io.Copy(w, resp.Body)
    + resp.Body.Close()
    + })
    +}
    +
    +func newServer() *http.Server {
    + handler := logger.Handler(
    + kludge(cfg.TokenEndpoint, cfg.Scope),
    + os.Stdout,
    + logger.CommonLoggerType,
    + )
    +
    + return &http.Server{
    + Addr: cfg.ListenAddr,
    + Handler: handler,
    + ReadTimeout: 5 * time.Second,
    + WriteTimeout: 10 * time.Second,
    + }
    +}
    +
    +func main() {
    + err := envconfig.Process("OAUTH_KLUDGE", &cfg)
    + if err != nil {
    + fmt.Printf("error: %v\n", err)
    + return
    + }
    +
    + // create a signal channel for catching os signals
    + signalChan := make(chan os.Signal, 1)
    + signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
    +
    + // create our error channel
    + errChan := make(chan error, 1)
    +
    + server := newServer()
    + go func() {
    + defer server.Close()
    + fmt.Printf("Listening on %s\n", cfg.ListenAddr)
    + errChan <- server.ListenAndServe()
    + }()
    +
    + for {
    + select {
    + case err := <-errChan:
    + fmt.Printf("error: %v\n", err)
    + return
    + case s := <-signalChan:
    + fmt.Printf("caught %v\n", s)
    + return
    + }
    + }
    +}