mi-v1/cmd/mi/middleware.go

127 lines
2.6 KiB
Go

package main
import (
"context"
"encoding/hex"
"flag"
"net/http"
"time"
"github.com/google/uuid"
"github.com/o1egl/paseto/v2"
"golang.org/x/crypto/ed25519"
"golang.org/x/net/trace"
)
var (
// Paseto
pasetoPublicKey = flag.String("paseto-public-key", "", "Paseto public key (see tools/paseto-key-gen.go)")
pasetoPrivateKey = flag.String("paseto-private-key", "", "Paseto private key")
)
type GoTraceMiddleware struct{ next http.Handler }
func (gtm GoTraceMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
tr := trace.New("http", r.URL.Path)
ctx = trace.NewContext(ctx, tr)
r = r.WithContext(ctx)
gtm.next.ServeHTTP(w, r)
tr.Finish()
}
type PasetoMiddleware struct {
next http.Handler
pubKey ed25519.PublicKey
privKey ed25519.PrivateKey
v2 paseto.V2
}
func MakeMiddleware(pub, priv string, next http.Handler) (*PasetoMiddleware, error) {
var result PasetoMiddleware
result.next = next
publicKey, err := hex.DecodeString(pub)
if err != nil {
return nil, err
}
result.pubKey = ed25519.PublicKey(publicKey)
privateKey, err := hex.DecodeString(priv)
if err != nil {
return nil, err
}
result.privKey = ed25519.PrivateKey(privateKey)
return &result, nil
}
func (pm PasetoMiddleware) CreateToken(data [][2]string, expiration time.Time) (string, error) {
claims := paseto.JSONToken{
Expiration: expiration,
Jti: uuid.New().String(),
Subject: "Within",
IssuedAt: time.Now(),
}
for _, datum := range data {
claims.Set(datum[0], datum[1])
}
tok, err := pm.v2.Sign(pm.privKey, claims, nil)
if err != nil {
return "", err
}
return tok, nil
}
var publicRoutes map[string]bool
func init() {
publicRoutes = map[string]bool{
"/webhooks/": true,
"/static/": true,
"/static/main.js": true,
"/static/gruvbox.css": true,
"/static/favicon.ico": true,
"/static/favicon.png": true,
"/debug/requests": true,
"/debug/events": true,
}
}
type ctxKey int
const (
tokenKey ctxKey = iota
)
func (pm PasetoMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tok := r.Header.Get("Authorization")
var newJsonToken paseto.JSONToken
var newFooter string
var err error
if r.URL.EscapedPath() == "/.within/botinfo" || r.URL.EscapedPath() == "/" {
goto ok
}
for k := range publicRoutes {
if r.URL.EscapedPath() == k {
goto ok
}
}
err = pm.v2.Verify(tok, pm.pubKey, &newJsonToken, &newFooter)
if err != nil {
http.Error(w, "Not allowed", http.StatusForbidden)
return
}
r = r.WithContext(context.WithValue(r.Context(), tokenKey, newJsonToken))
ok:
pm.next.ServeHTTP(w, r)
}