From cfe4597782fe0a2bb01d655889b2cca9724f5bc5 Mon Sep 17 00:00:00 2001 From: elinuxhenrik Date: Tue, 31 Jan 2023 15:44:29 +0100 Subject: [PATCH] Add validation to event service Issue-ID: NONRTRIC-814 Signed-off-by: elinuxhenrik Change-Id: I3b58b4e3896c7e10ecfae216c62d3bc9b5f94cbf --- capifcore/internal/eventsapi/typevalidation.go | 70 ++++++++++++++++++++++ .../internal/eventsapi/typevalidation_test.go | 61 +++++++++++++++++++ capifcore/internal/eventservice/eventservice.go | 8 ++- .../internal/eventservice/eventservice_test.go | 29 +++++++-- 4 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 capifcore/internal/eventsapi/typevalidation.go create mode 100644 capifcore/internal/eventsapi/typevalidation_test.go diff --git a/capifcore/internal/eventsapi/typevalidation.go b/capifcore/internal/eventsapi/typevalidation.go new file mode 100644 index 0000000..5477706 --- /dev/null +++ b/capifcore/internal/eventsapi/typevalidation.go @@ -0,0 +1,70 @@ +// - +// ========================LICENSE_START================================= +// O-RAN-SC +// %% +// Copyright (C) 2023: Nordix Foundation +// %% +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ========================LICENSE_END=================================== +// + +package eventsapi + +import ( + "errors" + "fmt" + "net/url" + "strings" +) + +func (es EventSubscription) Validate() error { + if len(es.Events) == 0 { + return errors.New("required attribute EventSubscription:events must contain at least one element") + } + + for _, event := range es.Events { + if err := validateEvent(event); err != nil { + return errors.New("EventSubscription events contains invalid event") + } + } + + if len(strings.TrimSpace(string(es.NotificationDestination))) == 0 { + return errors.New("EventSubscription missing required notificationDestination") + } + if _, err := url.ParseRequestURI(string(es.NotificationDestination)); err != nil { + return fmt.Errorf("APIInvokerEnrolmentDetails has invalid notificationDestination, err=%s", err) + } + + return nil +} + +func validateEvent(event CAPIFEvent) error { + switch event { + case CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE: + case CAPIFEventACCESSCONTROLPOLICYUPDATE: + case CAPIFEventAPIINVOKERAUTHORIZATIONREVOKED: + case CAPIFEventAPIINVOKEROFFBOARDED: + case CAPIFEventAPIINVOKERONBOARDED: + case CAPIFEventAPIINVOKERUPDATED: + case CAPIFEventAPITOPOLOGYHIDINGCREATED: + case CAPIFEventAPITOPOLOGYHIDINGREVOKED: + case CAPIFEventSERVICEAPIAVAILABLE: + case CAPIFEventSERVICEAPIINVOCATIONFAILURE: + case CAPIFEventSERVICEAPIINVOCATIONSUCCESS: + case CAPIFEventSERVICEAPIUNAVAILABLE: + case CAPIFEventSERVICEAPIUPDATE: + default: + return errors.New("wrong event type") + } + return nil +} diff --git a/capifcore/internal/eventsapi/typevalidation_test.go b/capifcore/internal/eventsapi/typevalidation_test.go new file mode 100644 index 0000000..df57897 --- /dev/null +++ b/capifcore/internal/eventsapi/typevalidation_test.go @@ -0,0 +1,61 @@ +// - +// ========================LICENSE_START================================= +// O-RAN-SC +// %% +// Copyright (C) 2023: Nordix Foundation +// %% +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ========================LICENSE_END=================================== +// + +package eventsapi + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValidateEventSubscription(t *testing.T) { + subUnderTest := EventSubscription{} + + err := subUnderTest.Validate() + + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "required") + assert.Contains(t, err.Error(), "events") + + var invalidEventType CAPIFEvent = "invalid" + subUnderTest.Events = []CAPIFEvent{invalidEventType} + err = subUnderTest.Validate() + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "invalid") + assert.Contains(t, err.Error(), "events") + + subUnderTest.Events = []CAPIFEvent{CAPIFEventAPIINVOKERONBOARDED} + err = subUnderTest.Validate() + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "missing") + assert.Contains(t, err.Error(), "notificationDestination") + + subUnderTest.NotificationDestination = "invalid dest" + err = subUnderTest.Validate() + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "invalid") + assert.Contains(t, err.Error(), "notificationDestination") + } + + subUnderTest.NotificationDestination = "http://golang.cafe/" + err = subUnderTest.Validate() + assert.Nil(t, err) +} diff --git a/capifcore/internal/eventservice/eventservice.go b/capifcore/internal/eventservice/eventservice.go index fd3cce7..1d63a45 100644 --- a/capifcore/internal/eventservice/eventservice.go +++ b/capifcore/internal/eventservice/eventservice.go @@ -74,6 +74,11 @@ func (es *EventService) PostSubscriberIdSubscriptions(ctx echo.Context, subscrib if err != nil { return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err)) } + + if err := newSubscription.Validate(); err != nil { + return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err)) + } + uri := ctx.Request().Host + ctx.Request().URL.String() subId := es.getSubscriptionId(subscriberId) es.addSubscription(subId, newSubscription) @@ -166,8 +171,9 @@ func matchesFilters(eventIds *[]string, filters []eventsapi.CAPIFEventFilter, ge filterIds := getIds(filter) if filterIds == nil || len(*filterIds) == 0 { return matchesFilters(eventIds, filters[1:], getIds) + } else { + return slices.Contains(*getIds(filter), id) && matchesFilters(eventIds, filters[1:], getIds) } - return slices.Contains(*getIds(filter), id) && matchesFilters(eventIds, filters[1:], getIds) } return true } diff --git a/capifcore/internal/eventservice/eventservice_test.go b/capifcore/internal/eventservice/eventservice_test.go index a328e57..d0b646b 100644 --- a/capifcore/internal/eventservice/eventservice_test.go +++ b/capifcore/internal/eventservice/eventservice_test.go @@ -24,7 +24,7 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "os" "path" @@ -48,7 +48,7 @@ func TestRegisterSubscriptions(t *testing.T) { Events: []eventsapi.CAPIFEvent{ eventsapi.CAPIFEventSERVICEAPIAVAILABLE, }, - NotificationDestination: common29122.Uri("notificationUrl"), + NotificationDestination: common29122.Uri("http://golang.cafe/"), } serviceUnderTest, requestHandler := getEcho(nil) subscriberId := "subscriberId" @@ -77,6 +77,27 @@ func TestRegisterSubscriptions(t *testing.T) { assert.Equal(t, subscription2, *registeredSub2) } +func TestRegisterInvalidSubscription(t *testing.T) { + subscription1 := eventsapi.EventSubscription{ + Events: []eventsapi.CAPIFEvent{eventsapi.CAPIFEventACCESSCONTROLPOLICYUNAVAILABLE}, + } + serviceUnderTest, requestHandler := getEcho(nil) + subscriberId := "subscriberId" + + result := testutil.NewRequest().Post("/"+subscriberId+"/subscriptions").WithJsonBody(subscription1).Go(t, requestHandler) + assert.Equal(t, http.StatusBadRequest, result.Code()) + var problemDetails common29122.ProblemDetails + err := result.UnmarshalBodyToObject(&problemDetails) + assert.NoError(t, err, "error unmarshaling response") + badRequest := http.StatusBadRequest + assert.Equal(t, &badRequest, problemDetails.Status) + assert.Contains(t, *problemDetails.Cause, "missing") + assert.Contains(t, *problemDetails.Cause, "notificationDestination") + subscriptionId := path.Base(result.Recorder.Header().Get(echo.HeaderLocation)) + registeredSub := serviceUnderTest.getSubscription(subscriptionId) + assert.Nil(t, registeredSub) +} + func TestDeregisterSubscription(t *testing.T) { subscription := eventsapi.EventSubscription{ Events: []eventsapi.CAPIFEvent{ @@ -113,7 +134,7 @@ func TestSendEvent(t *testing.T) { wg.Done() return &http.Response{ StatusCode: 200, - Body: ioutil.NopCloser(bytes.NewBufferString(`OK`)), + Body: io.NopCloser(bytes.NewBufferString(`OK`)), Header: make(http.Header), // Must be set to non-nil value or it panics } } @@ -143,7 +164,7 @@ func TestSendEvent(t *testing.T) { }() if waitTimeout(&wg, 1*time.Second) { - t.Error("Not all calls to server were made") + t.Error("No event notification was sent") t.Fail() } } -- 2.16.6