diff --git a/pkg/apiserver/server.go b/pkg/apiserver/server.go index aa22e60289..076ebe095d 100644 --- a/pkg/apiserver/server.go +++ b/pkg/apiserver/server.go @@ -200,13 +200,6 @@ func Start(params *APIServerParams) { handlers.RegisterSessionAuthRoutes(r.PathPrefix("").Subrouter(), kotsStore, handler, policyMiddleware) - /********************************************************************** - * Session auth routes - **********************************************************************/ - - licenseAuthRouter := r.PathPrefix("").Subrouter() - handlers.RegisterLicenseIDAuthRoutes(licenseAuthRouter, kotsStore, handler, policyMiddleware) - // Prevent API requests that don't match anything in this router from returning UI content r.PathPrefix("/api").Handler(handlers.StatusNotFoundHandler{}) diff --git a/pkg/handlers/custom_metrics.go b/pkg/handlers/custom_metrics.go index 783b459f13..4f658ae7a2 100644 --- a/pkg/handlers/custom_metrics.go +++ b/pkg/handlers/custom_metrics.go @@ -7,9 +7,10 @@ import ( "github.com/pkg/errors" "github.com/replicatedhq/kots/pkg/kotsadm" + "github.com/replicatedhq/kots/pkg/kotsutil" "github.com/replicatedhq/kots/pkg/logger" "github.com/replicatedhq/kots/pkg/replicatedapp" - "github.com/replicatedhq/kots/pkg/session" + "github.com/replicatedhq/kots/pkg/store" ) type SendCustomApplicationMetricsRequest struct { @@ -18,37 +19,57 @@ type SendCustomApplicationMetricsRequest struct { type ApplicationMetricsData map[string]interface{} -func (h *Handler) SendCustomApplicationMetrics(w http.ResponseWriter, r *http.Request) { - if kotsadm.IsAirgap() { - w.WriteHeader(http.StatusForbidden) - w.Write([]byte("This request cannot be satisfied in airgap mode")) - return - } +func (h *Handler) GetSendCustomApplicationMetricsHandler(kotsStore store.Store) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if kotsadm.IsAirgap() { + w.WriteHeader(http.StatusForbidden) + w.Write([]byte("This request cannot be satisfied in airgap mode")) + return + } - license := session.ContextGetLicense(r) - app := session.ContextGetApp(r) + apps, err := kotsStore.ListInstalledApps() + if err != nil { + logger.Error(errors.Wrap(err, "list installed apps")) + w.WriteHeader(http.StatusInternalServerError) + return + } - request := SendCustomApplicationMetricsRequest{} - if err := json.NewDecoder(r.Body).Decode(&request); err != nil { - logger.Error(errors.Wrap(err, "decode request")) - w.WriteHeader(http.StatusBadRequest) - return - } + if len(apps) != 1 { + logger.Errorf("custom application metrics can be sent only if one app is installed") + w.WriteHeader(http.StatusInternalServerError) + return + } - if err := validateCustomMetricsData(request.Data); err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } + app := apps[0] + license, err := kotsutil.LoadLicenseFromBytes([]byte(app.License)) + if err != nil { + logger.Error(errors.Wrap(err, "load license from bytes")) + w.WriteHeader(http.StatusInternalServerError) + return + } - err := replicatedapp.SendApplicationMetricsData(license, app, request.Data) - if err != nil { - logger.Error(errors.Wrap(err, "set application data")) - w.WriteHeader(http.StatusBadRequest) - return - } + request := SendCustomApplicationMetricsRequest{} + if err := json.NewDecoder(r.Body).Decode(&request); err != nil { + logger.Error(errors.Wrap(err, "decode request")) + w.WriteHeader(http.StatusBadRequest) + return + } + + if err := validateCustomMetricsData(request.Data); err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } - JSON(w, http.StatusOK, "") + err = replicatedapp.SendApplicationMetricsData(license, app, request.Data) + if err != nil { + logger.Error(errors.Wrap(err, "set application data")) + w.WriteHeader(http.StatusBadRequest) + return + } + + JSON(w, http.StatusOK, "") + } } func validateCustomMetricsData(data ApplicationMetricsData) error { diff --git a/pkg/handlers/custom_metrics_test.go b/pkg/handlers/custom_metrics_test.go index a8791b3819..8e0b3bda1b 100644 --- a/pkg/handlers/custom_metrics_test.go +++ b/pkg/handlers/custom_metrics_test.go @@ -2,6 +2,7 @@ package handlers import ( "bytes" + "fmt" "io" "net/http" "net/http/httptest" @@ -11,8 +12,7 @@ import ( gomock "github.com/golang/mock/gomock" "github.com/gorilla/mux" apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/session" - "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + mock_store "github.com/replicatedhq/kots/pkg/store/mock" "github.com/stretchr/testify/require" ) @@ -68,9 +68,6 @@ func Test_validateCustomMetricsData(t *testing.T) { } func Test_SendCustomApplicationMetrics(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - req := require.New(t) customMetricsData := []byte(`{"data":{"key1_string":"val1","key2_int":5,"key3_float":1.5,"key4_numeric_string":"1.6"}}`) appID := "app-id-123" @@ -94,24 +91,30 @@ func Test_SendCustomApplicationMetrics(t *testing.T) { os.Setenv("USE_MOCK_REPORTING", "1") defer os.Unsetenv("USE_MOCK_REPORTING") + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + app := apptypes.App{ + ID: appID, + License: fmt.Sprintf(`apiVersion: kots.io/v1beta1 +kind: License +spec: + licenseID: 2ULcK9BJd1dHGetHYZIysK9IADZ + endpoint: %s`, server.URL), + } + + mockStore := mock_store.NewMockStore(ctrl) + mockStore.EXPECT().ListInstalledApps().Times(1).Return([]*apptypes.App{&app}, nil) + handler := Handler{} clientWriter := httptest.NewRecorder() clientRequest := &http.Request{ Body: io.NopCloser(bytes.NewBuffer(customMetricsData)), } - clientRequest = session.ContextSetLicense(clientRequest, &v1beta1.License{ - Spec: v1beta1.LicenseSpec{ - Endpoint: server.URL, - }, - }) - clientRequest = session.ContextSetApp(clientRequest, &apptypes.App{ - ID: appID, - }) - // Validate - handler.SendCustomApplicationMetrics(clientWriter, clientRequest) + handler.GetSendCustomApplicationMetricsHandler(mockStore)(clientWriter, clientRequest) req.Equal(http.StatusOK, clientWriter.Code) } diff --git a/pkg/handlers/handlers.go b/pkg/handlers/handlers.go index 43f2435602..11b2c708c2 100644 --- a/pkg/handlers/handlers.go +++ b/pkg/handlers/handlers.go @@ -339,14 +339,9 @@ func RegisterUnauthenticatedRoutes(handler *Handler, kotsStore store.Store, debu loggingRouter.Path("/api/v1/troubleshoot/supportbundle/{bundleId}/redactions").Methods("PUT").HandlerFunc(handler.SetSupportBundleRedactions) loggingRouter.Path("/api/v1/preflight/app/{appSlug}/sequence/{sequence}").Methods("POST").HandlerFunc(handler.PostPreflightStatus) - // This the handler for license API and should be called by the application only. + // These handlers should be called by the application only. loggingRouter.Path("/license/v1/license").Methods("GET").HandlerFunc(handler.GetPlatformLicenseCompatibility) -} - -func RegisterLicenseIDAuthRoutes(r *mux.Router, kotsStore store.Store, handler KOTSHandler, middleware *policy.Middleware) { - r.Use(LoggingMiddleware, RequireValidLicenseMiddleware(kotsStore)) - r.Name("SendCustomApplicationMetrics").Path("/api/v1/app/custom-metrics").Methods("POST"). - HandlerFunc(middleware.EnforceLicense(handler.SendCustomApplicationMetrics)) + loggingRouter.Path("/api/v1/app/custom-metrics").Methods("POST").HandlerFunc(handler.GetSendCustomApplicationMetricsHandler(kotsStore)) } func StreamJSON(c *websocket.Conn, payload interface{}) { diff --git a/pkg/handlers/handlers_test.go b/pkg/handlers/handlers_test.go index 6eae1213fd..7b15466cc9 100644 --- a/pkg/handlers/handlers_test.go +++ b/pkg/handlers/handlers_test.go @@ -1329,16 +1329,6 @@ var HandlerPolicyTests = map[string][]HandlerPolicyTest{ ExpectStatus: http.StatusOK, }, }, - "SendCustomApplicationMetrics": { - { - Roles: []rbactypes.Role{rbac.ClusterAdminRole}, - SessionRoles: []string{rbac.ClusterAdminRoleID}, - Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) { - handlerRecorder.SendCustomApplicationMetrics(gomock.Any(), gomock.Any()) - }, - ExpectStatus: http.StatusOK, - }, - }, } type HandlerPolicyTest struct { diff --git a/pkg/handlers/interface.go b/pkg/handlers/interface.go index 2a51dfa471..40d7f22bdf 100644 --- a/pkg/handlers/interface.go +++ b/pkg/handlers/interface.go @@ -155,7 +155,4 @@ type KOTSHandler interface { // Helm IsHelmManaged(w http.ResponseWriter, r *http.Request) GetAppValuesFile(w http.ResponseWriter, r *http.Request) - - // API available to applications (except legacy /license/v1/license) - SendCustomApplicationMetrics(w http.ResponseWriter, r *http.Request) } diff --git a/pkg/handlers/middleware.go b/pkg/handlers/middleware.go index 82ca484693..ea328f8a89 100644 --- a/pkg/handlers/middleware.go +++ b/pkg/handlers/middleware.go @@ -107,21 +107,3 @@ func RequireValidSessionQuietMiddleware(kotsStore store.Store) mux.MiddlewareFun }) } } - -func RequireValidLicenseMiddleware(kotsStore store.Store) mux.MiddlewareFunc { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - license, app, err := requireValidLicense(kotsStore, w, r) - if err != nil { - if !kotsStore.IsNotFound(err) { - logger.Error(errors.Wrapf(err, "request %q", r.RequestURI)) - } - return - } - - r = session.ContextSetLicense(r, license) - r = session.ContextSetApp(r, app) - next.ServeHTTP(w, r) - }) - } -} diff --git a/pkg/handlers/mock/mock.go b/pkg/handlers/mock/mock.go index d0959b37a2..45f4d1c242 100644 --- a/pkg/handlers/mock/mock.go +++ b/pkg/handlers/mock/mock.go @@ -1234,18 +1234,6 @@ func (mr *MockKOTSHandlerMockRecorder) SaveSnapshotConfig(w, r interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveSnapshotConfig", reflect.TypeOf((*MockKOTSHandler)(nil).SaveSnapshotConfig), w, r) } -// SendCustomApplicationMetrics mocks base method. -func (m *MockKOTSHandler) SendCustomApplicationMetrics(w http.ResponseWriter, r *http.Request) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SendCustomApplicationMetrics", w, r) -} - -// SendCustomApplicationMetrics indicates an expected call of SendCustomApplicationMetrics. -func (mr *MockKOTSHandlerMockRecorder) SendCustomApplicationMetrics(w, r interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCustomApplicationMetrics", reflect.TypeOf((*MockKOTSHandler)(nil).SendCustomApplicationMetrics), w, r) -} - // SetAppConfigValues mocks base method. func (m *MockKOTSHandler) SetAppConfigValues(w http.ResponseWriter, r *http.Request) { m.ctrl.T.Helper() diff --git a/pkg/handlers/session.go b/pkg/handlers/session.go index d79433997b..2179f996ed 100644 --- a/pkg/handlers/session.go +++ b/pkg/handlers/session.go @@ -8,16 +8,13 @@ import ( "time" "github.com/pkg/errors" - apptypes "github.com/replicatedhq/kots/pkg/app/types" "github.com/replicatedhq/kots/pkg/handlers/types" "github.com/replicatedhq/kots/pkg/k8sutil" - "github.com/replicatedhq/kots/pkg/kotsutil" "github.com/replicatedhq/kots/pkg/logger" "github.com/replicatedhq/kots/pkg/session" sessiontypes "github.com/replicatedhq/kots/pkg/session/types" "github.com/replicatedhq/kots/pkg/store" "github.com/replicatedhq/kots/pkg/util" - "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" kuberneteserrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -132,50 +129,6 @@ func requireValidSession(kotsStore store.Store, w http.ResponseWriter, r *http.R return sess, nil } -func requireValidLicense(kotsStore store.Store, w http.ResponseWriter, r *http.Request) (*v1beta1.License, *apptypes.App, error) { - if r.Method == "OPTIONS" { - return nil, nil, nil - } - - licenseID := r.Header.Get("authorization") - if licenseID == "" { - err := errors.New("missing authorization header") - response := types.ErrorResponse{Error: util.StrPointer(err.Error())} - JSON(w, http.StatusUnauthorized, response) - return nil, nil, err - } - - apps, err := kotsStore.ListInstalledApps() - if err != nil { - return nil, nil, errors.Wrap(err, "get all apps") - } - - var license *v1beta1.License - var app *apptypes.App - - for _, a := range apps { - l, err := kotsutil.LoadLicenseFromBytes([]byte(a.License)) - if err != nil { - return nil, nil, errors.Wrap(err, "load license") - } - - if l.Spec.LicenseID == licenseID { - license = l - app = a - break - } - } - - if license == nil { - err := errors.New("license ID is not valid") - response := types.ErrorResponse{Error: util.StrPointer(err.Error())} - JSON(w, http.StatusUnauthorized, response) - return nil, nil, err - } - - return license, app, nil -} - func requireValidKOTSToken(w http.ResponseWriter, r *http.Request) error { if r.Header.Get("Authorization") == "" { w.WriteHeader(http.StatusUnauthorized)