Skip to content

Commit

Permalink
Interceptor middleware for HTTP servers
Browse files Browse the repository at this point in the history
Interceptor reads the request context object from the HTTP header
and adds the client's UUID and x509.Certificate to the Go http.request
context.
  • Loading branch information
ananthb committed May 4, 2023
1 parent 08d34ac commit 9f4ffbd
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 2 deletions.
3 changes: 1 addition & 2 deletions pkg/club/bouncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

// Package club implements a middleware that simulates an environment similar to
// a popular API gateway product from a certain cloud provider.
// Package club provides middleware for use in HTTP API servers and gateways.
package club

import (
Expand Down
64 changes: 64 additions & 0 deletions pkg/club/interceptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

package club

import (
"context"
"crypto/x509"
"encoding/json"
"encoding/pem"
"net/http"

"github.com/RealImage/bifrost"
"github.com/google/uuid"
"golang.org/x/exp/slog"
)

type Key string

const (
keyUUID Key = "uuid"
keyCert Key = "cert"
)

// CtxClientID returns the client's UUID and Certificate from the request context.
// The context must be from a request that has passed through Interceptor.
func CtxClientID(ctx context.Context) (uuid.UUID, *x509.Certificate) {
return ctx.Value(keyUUID).(uuid.UUID), ctx.Value(keyCert).(*x509.Certificate)
}

// Interceptor returns a HTTP Handler middleware function that reads The
// x-amzn-request-context header and adds the client's UUID and certificate to
// the request context.
func Interceptor(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rctxHeader := r.Header.Get(RequestContextHeader)
if rctxHeader != "" {
ctx := r.Context()
var rctx RequestContext
if err := json.Unmarshal([]byte(rctxHeader), &rctx); err != nil {
slog.ErrorCtx(ctx, "error unmarshaling request context", "err", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
block, _ := pem.Decode(rctx.Authentication.ClientCert.ClientCertPEM)
if block == nil {
slog.ErrorCtx(ctx, "error decoding client certificate PEM")
w.WriteHeader(http.StatusInternalServerError)
return
}
uuid, cert, err := bifrost.ParseCertificate(block.Bytes)
if err != nil {
slog.ErrorCtx(ctx, "error parsing client certificate", "err", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
ctx = context.WithValue(ctx, keyUUID, uuid)
ctx = context.WithValue(ctx, keyCert, cert)
r = r.WithContext(ctx)
}
next.ServeHTTP(w, r)
})
}

0 comments on commit 9f4ffbd

Please sign in to comment.