Add validation to event service 19/10319/1
authorelinuxhenrik <henrik.b.andersson@est.tech>
Tue, 31 Jan 2023 14:44:29 +0000 (15:44 +0100)
committerelinuxhenrik <henrik.b.andersson@est.tech>
Tue, 31 Jan 2023 14:44:33 +0000 (15:44 +0100)
Issue-ID: NONRTRIC-814
Signed-off-by: elinuxhenrik <henrik.b.andersson@est.tech>
Change-Id: I3b58b4e3896c7e10ecfae216c62d3bc9b5f94cbf

capifcore/internal/eventsapi/typevalidation.go [new file with mode: 0644]
capifcore/internal/eventsapi/typevalidation_test.go [new file with mode: 0644]
capifcore/internal/eventservice/eventservice.go
capifcore/internal/eventservice/eventservice_test.go

diff --git a/capifcore/internal/eventsapi/typevalidation.go b/capifcore/internal/eventsapi/typevalidation.go
new file mode 100644 (file)
index 0000000..5477706
--- /dev/null
@@ -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 (file)
index 0000000..df57897
--- /dev/null
@@ -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)
+}
index fd3cce7..1d63a45 100644 (file)
@@ -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
 }
index a328e57..d0b646b 100644 (file)
@@ -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()
        }
 }