chrly/internal/http/signer.go
2024-03-13 01:29:26 +01:00

97 lines
2.5 KiB
Go

package http
import (
"encoding/base64"
"fmt"
"io"
"net/http"
"github.com/gorilla/mux"
"go.opentelemetry.io/otel/metric"
"go.uber.org/multierr"
"ely.by/chrly/internal/otel"
)
type Signer interface {
Sign(data io.Reader) ([]byte, error)
GetPublicKey(format string) ([]byte, error)
}
func NewSignerApi(signer Signer) (*SignerApi, error) {
metrics, err := newSignerApiMetrics(otel.GetMeter())
if err != nil {
return nil, err
}
return &SignerApi{
Signer: signer,
metrics: metrics,
}, nil
}
type SignerApi struct {
Signer
metrics *signerApiMetrics
}
func (s *SignerApi) Handler() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/", s.signHandler).Methods(http.MethodPost)
router.HandleFunc("/public-key.{format:(?:pem|der)}", s.getPublicKeyHandler).Methods(http.MethodGet)
return router
}
func (s *SignerApi) signHandler(resp http.ResponseWriter, req *http.Request) {
signature, err := s.Signer.Sign(req.Body)
if err != nil {
apiServerError(resp, req, fmt.Errorf("unable to sign the value: %w", err))
return
}
resp.Header().Set("Content-Type", "application/octet-stream+base64")
buf := make([]byte, base64.StdEncoding.EncodedLen(len(signature)))
base64.StdEncoding.Encode(buf, signature)
_, _ = resp.Write(buf)
}
func (s *SignerApi) getPublicKeyHandler(resp http.ResponseWriter, req *http.Request) {
format := mux.Vars(req)["format"]
publicKey, err := s.Signer.GetPublicKey(format)
if err != nil {
apiServerError(resp, req, fmt.Errorf("unable to retrieve public key: %w", err))
return
}
if format == "pem" {
resp.Header().Set("Content-Type", "application/x-pem-file")
resp.Header().Set("Content-Disposition", `attachment; filename="yggdrasil_session_pubkey.pem"`)
} else {
resp.Header().Set("Content-Type", "application/octet-stream")
resp.Header().Set("Content-Disposition", `attachment; filename="yggdrasil_session_pubkey.der"`)
}
_, _ = resp.Write(publicKey)
}
func newSignerApiMetrics(meter metric.Meter) (*signerApiMetrics, error) {
m := &signerApiMetrics{}
var errors, err error
m.SignRequest, err = meter.Int64Counter("chrly.app.signer.sign.request", metric.WithUnit("{request}"))
errors = multierr.Append(errors, err)
m.GetPublicKeyRequest, err = meter.Int64Counter("chrly.app.signer.get_public_key.request", metric.WithUnit("{request}"))
errors = multierr.Append(errors, err)
return m, errors
}
type signerApiMetrics struct {
SignRequest metric.Int64Counter
GetPublicKeyRequest metric.Int64Counter
}