github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/info/handler.go (about)

     1  package info
     2  
     3  import (
     4  	"context"
     5  	"crypto/x509"
     6  	"net/http"
     7  	"strings"
     8  
     9  	"github.com/kyma-incubator/compass/components/director/pkg/certloader"
    10  	"github.com/kyma-incubator/compass/components/director/pkg/log"
    11  	"github.com/pkg/errors"
    12  
    13  	ord "github.com/kyma-incubator/compass/components/director/internal/open_resource_discovery"
    14  
    15  	"github.com/kyma-incubator/compass/components/director/pkg/httputils"
    16  )
    17  
    18  const (
    19  	plusDelimiter  = "+"
    20  	commaDelimiter = ","
    21  )
    22  
    23  // Config contains the data that should be exported on the info endpoint
    24  type Config struct {
    25  	APIEndpoint                  string `envconfig:"APP_INFO_API_ENDPOINT,default=/v1/info" json:"-"`
    26  	RootCA                       string `envconfig:"APP_INFO_ROOT_CA"`
    27  	ExternalClientCertSecretName string `envconfig:"APP_EXTERNAL_CLIENT_CERT_SECRET_NAME"`
    28  }
    29  
    30  type responseData struct {
    31  	Issuer     string `json:"certIssuer"`
    32  	Subject    string `json:"certSubject"`
    33  	RootCA     string `json:"rootCA"`
    34  	OrdVersion string `json:"ordAggregatorVersion"`
    35  }
    36  
    37  // NewInfoHandler returns handler which gives information about the CMP client certificate.
    38  // The issuer and the subject are dynamically loaded from the certificate itself (reading the cert from the cert cache every time the endpoint is hit) rather than using hardcoded env values.
    39  func NewInfoHandler(ctx context.Context, c Config, certCache certloader.Cache) func(writer http.ResponseWriter, request *http.Request) {
    40  	return func(w http.ResponseWriter, r *http.Request) {
    41  		responseData, err := prepareResponseData(c, certCache)
    42  		if err != nil {
    43  			log.C(ctx).Errorf("Error while processing client certificate from cache: %v", err)
    44  			w.WriteHeader(http.StatusInternalServerError)
    45  			return
    46  		}
    47  
    48  		if r.Method != http.MethodGet {
    49  			w.WriteHeader(http.StatusMethodNotAllowed)
    50  			return
    51  		}
    52  		httputils.RespondWithBody(ctx, w, http.StatusOK, responseData)
    53  	}
    54  }
    55  
    56  func prepareResponseData(c Config, certCache certloader.Cache) (responseData, error) {
    57  	clientCert := certCache.Get()[c.ExternalClientCertSecretName]
    58  	if clientCert == nil || len(clientCert.Certificate) == 0 {
    59  		return responseData{}, errors.New("did not find client certificate in the cache")
    60  	}
    61  
    62  	parsedClientCert, err := x509.ParseCertificate(clientCert.Certificate[0])
    63  	if err != nil {
    64  		return responseData{}, errors.New("error while parsing client certificate")
    65  	}
    66  
    67  	certIssuer := replaceDelimiter(parsedClientCert.Issuer.String())
    68  	certSubject := replaceDelimiter(parsedClientCert.Subject.String())
    69  
    70  	return responseData{
    71  		Issuer:     certIssuer,
    72  		Subject:    certSubject,
    73  		RootCA:     c.RootCA,
    74  		OrdVersion: ord.SpecVersion,
    75  	}, nil
    76  }
    77  
    78  func replaceDelimiter(input string) string {
    79  	return strings.ReplaceAll(input, plusDelimiter, commaDelimiter)
    80  }