Moving add client in keycloak from security to invoker api 89/11089/2
authorychacon <yennifer.chacon@est.tech>
Fri, 12 May 2023 09:14:17 +0000 (11:14 +0200)
committerychacon <yennifer.chacon@est.tech>
Fri, 12 May 2023 09:38:43 +0000 (11:38 +0200)
Issue-ID: NONRTRIC-861
Signed-off-by: ychacon <yennifer.chacon@est.tech>
Change-Id: I0d9ceedc2f23f2d5fa5233f6075f3ecd56d3fc08

19 files changed:
capifcore/internal/invokermanagement/invokermanagement.go
capifcore/internal/invokermanagement/invokermanagement_test.go
capifcore/internal/invokermanagementapi/typeupdate.go
capifcore/internal/invokermanagementapi/typeupdate_test.go
capifcore/internal/keycloak/keycloak.go
capifcore/internal/keycloak/mocks/AccessManagement.go
capifcore/internal/providermanagement/mocks/ServiceRegister.go
capifcore/internal/providermanagement/providermanagement.go
capifcore/internal/providermanagementapi/typeaccess.go
capifcore/internal/publishservice/publishservice.go
capifcore/internal/publishservice/publishservice_test.go
capifcore/internal/publishserviceapi/typevalidation.go
capifcore/internal/restclient/HTTPClient.go
capifcore/internal/restclient/HTTPClient_test.go
capifcore/internal/restclient/mocks/HTTPClient.go
capifcore/internal/securityapi/typeupdate.go
capifcore/internal/securityservice/security.go
capifcore/internal/securityservice/security_test.go
capifcore/main.go

index 43bdc02..ee7030a 100644 (file)
@@ -27,6 +27,7 @@ import (
        "sync"
 
        "oransc.org/nonrtric/capifcore/internal/eventsapi"
+       "oransc.org/nonrtric/capifcore/internal/keycloak"
 
        "oransc.org/nonrtric/capifcore/internal/common29122"
        invokerapi "oransc.org/nonrtric/capifcore/internal/invokermanagementapi"
@@ -53,16 +54,18 @@ type InvokerManager struct {
        onboardedInvokers map[string]invokerapi.APIInvokerEnrolmentDetails
        publishRegister   publishservice.PublishRegister
        nextId            int64
+       keycloak          keycloak.AccessManagement
        eventChannel      chan<- eventsapi.EventNotification
        lock              sync.Mutex
 }
 
 // Creates a manager that implements both the InvokerRegister and the invokermanagementapi.ServerInterface interfaces.
-func NewInvokerManager(publishRegister publishservice.PublishRegister, eventChannel chan<- eventsapi.EventNotification) *InvokerManager {
+func NewInvokerManager(publishRegister publishservice.PublishRegister, km keycloak.AccessManagement, eventChannel chan<- eventsapi.EventNotification) *InvokerManager {
        return &InvokerManager{
                onboardedInvokers: make(map[string]invokerapi.APIInvokerEnrolmentDetails),
                publishRegister:   publishRegister,
                nextId:            1000,
+               keycloak:          km,
                eventChannel:      eventChannel,
        }
 }
@@ -147,9 +150,24 @@ func (im *InvokerManager) prepareNewInvoker(newInvoker *invokerapi.APIInvokerEnr
 
        newInvoker.PrepareNewInvoker()
 
+       im.addClientInKeycloak(newInvoker)
+
        im.onboardedInvokers[*newInvoker.ApiInvokerId] = *newInvoker
 }
 
+func (im *InvokerManager) addClientInKeycloak(newInvoker *invokerapi.APIInvokerEnrolmentDetails) error {
+       if err := im.keycloak.AddClient(*newInvoker.ApiInvokerId, "invokerrealm"); err != nil {
+               return err
+       }
+
+       if body, err := im.keycloak.GetClientRepresentation(*newInvoker.ApiInvokerId, "invokerrealm"); err != nil {
+               return err
+       } else {
+               newInvoker.OnboardingInformation.OnboardingSecret = body.Secret
+       }
+       return nil
+}
+
 // Deletes an individual API Invoker.
 func (im *InvokerManager) DeleteOnboardedInvokersOnboardingId(ctx echo.Context, onboardingId string) error {
        if _, ok := im.onboardedInvokers[onboardingId]; ok {
index 4365eb3..7b92972 100644 (file)
@@ -29,12 +29,14 @@ import (
 
        "oransc.org/nonrtric/capifcore/internal/eventsapi"
        "oransc.org/nonrtric/capifcore/internal/invokermanagementapi"
+       "oransc.org/nonrtric/capifcore/internal/keycloak"
 
        "github.com/labstack/echo/v4"
 
        "oransc.org/nonrtric/capifcore/internal/common29122"
        "oransc.org/nonrtric/capifcore/internal/publishserviceapi"
 
+       keycloackmocks "oransc.org/nonrtric/capifcore/internal/keycloak/mocks"
        "oransc.org/nonrtric/capifcore/internal/publishservice"
        publishmocks "oransc.org/nonrtric/capifcore/internal/publishservice/mocks"
 
@@ -42,6 +44,7 @@ import (
        "github.com/deepmap/oapi-codegen/pkg/testutil"
        echomiddleware "github.com/labstack/echo/v4/middleware"
        "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/mock"
 )
 
 func TestOnboardInvoker(t *testing.T) {
@@ -55,11 +58,20 @@ func TestOnboardInvoker(t *testing.T) {
                        AefProfiles: &aefProfiles,
                },
        }
+
+       invokerInfo := "invoker a"
+       wantedInvokerSecret := "onboarding_secret_" + strings.Replace(invokerInfo, " ", "_", 1)
+       var client keycloak.Client
+       client.Secret = &wantedInvokerSecret
        publishRegisterMock := publishmocks.PublishRegister{}
        publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices)
-       invokerUnderTest, eventChannel, requestHandler := getEcho(&publishRegisterMock)
 
-       invokerInfo := "invoker a"
+       accessMgmMock := keycloackmocks.AccessManagement{}
+       accessMgmMock.On("AddClient", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil)
+       accessMgmMock.On("GetClientRepresentation", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(&client, nil)
+
+       invokerUnderTest, eventChannel, requestHandler := getEcho(&publishRegisterMock, &accessMgmMock)
+
        newInvoker := getInvoker(invokerInfo)
 
        // Onboard a valid invoker
@@ -73,7 +85,7 @@ func TestOnboardInvoker(t *testing.T) {
        assert.Equal(t, wantedInvokerId, *resultInvoker.ApiInvokerId)
        assert.Equal(t, newInvoker.NotificationDestination, resultInvoker.NotificationDestination)
        assert.Equal(t, newInvoker.OnboardingInformation.ApiInvokerPublicKey, resultInvoker.OnboardingInformation.ApiInvokerPublicKey)
-       wantedInvokerSecret := "onboarding_secret_" + strings.Replace(invokerInfo, " ", "_", 1)
+
        assert.Equal(t, wantedInvokerSecret, *resultInvoker.OnboardingInformation.OnboardingSecret)
        assert.Equal(t, "http://example.com/onboardedInvokers/"+*resultInvoker.ApiInvokerId, result.Recorder.Header().Get(echo.HeaderLocation))
        assert.True(t, invokerUnderTest.IsInvokerRegistered(wantedInvokerId))
@@ -128,7 +140,7 @@ func TestOnboardInvoker(t *testing.T) {
 }
 
 func TestDeleteInvoker(t *testing.T) {
-       invokerUnderTest, eventChannel, requestHandler := getEcho(nil)
+       invokerUnderTest, eventChannel, requestHandler := getEcho(nil, nil)
 
        invokerId := "invokerId"
        newInvoker := invokermanagementapi.APIInvokerEnrolmentDetails{
@@ -157,7 +169,7 @@ func TestDeleteInvoker(t *testing.T) {
 func TestUpdateInvoker(t *testing.T) {
        publishRegisterMock := publishmocks.PublishRegister{}
        publishRegisterMock.On("GetAllPublishedServices").Return([]publishserviceapi.ServiceAPIDescription{})
-       serviceUnderTest, _, requestHandler := getEcho(&publishRegisterMock)
+       serviceUnderTest, _, requestHandler := getEcho(&publishRegisterMock, nil)
 
        invokerId := "invokerId"
        invoker := invokermanagementapi.APIInvokerEnrolmentDetails{
@@ -261,7 +273,7 @@ func TestGetInvokerApiList(t *testing.T) {
        })
        publishRegisterMock := publishmocks.PublishRegister{}
        publishRegisterMock.On("GetAllPublishedServices").Return(apiList)
-       invokerUnderTest, _, _ := getEcho(&publishRegisterMock)
+       invokerUnderTest, _, _ := getEcho(&publishRegisterMock, nil)
 
        invokerInfo := "invoker a"
        newInvoker := getInvoker(invokerInfo)
@@ -280,7 +292,7 @@ func TestGetInvokerApiList(t *testing.T) {
        assert.Equal(t, apiId, *(*wantedApiList)[0].ApiId)
 }
 
-func getEcho(publishRegister publishservice.PublishRegister) (*InvokerManager, chan eventsapi.EventNotification, *echo.Echo) {
+func getEcho(publishRegister publishservice.PublishRegister, keycloakMgm keycloak.AccessManagement) (*InvokerManager, chan eventsapi.EventNotification, *echo.Echo) {
        swagger, err := invokermanagementapi.GetSwagger()
        if err != nil {
                fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
@@ -290,7 +302,7 @@ func getEcho(publishRegister publishservice.PublishRegister) (*InvokerManager, c
        swagger.Servers = nil
 
        eventChannel := make(chan eventsapi.EventNotification)
-       im := NewInvokerManager(publishRegister, eventChannel)
+       im := NewInvokerManager(publishRegister, keycloakMgm, eventChannel)
 
        e := echo.New()
        e.Use(echomiddleware.Logger())
index 1de6280..abd399e 100644 (file)
@@ -30,8 +30,6 @@ var uuidFunc = getUUID
 
 func (ied *APIInvokerEnrolmentDetails) PrepareNewInvoker() {
        ied.createId()
-       ied.getOnboardingSecret()
-
 }
 
 func (ied *APIInvokerEnrolmentDetails) createId() {
index 5128d5f..f04a13d 100644 (file)
@@ -34,11 +34,9 @@ func TestPrepareNewInvoker(t *testing.T) {
 
        invokerUnderTest.PrepareNewInvoker()
        assert.Equal(t, "api_invoker_id_1", *invokerUnderTest.ApiInvokerId)
-       assert.Equal(t, "onboarding_secret_api_invoker_id_1", *invokerUnderTest.OnboardingInformation.OnboardingSecret)
 
        invokerInfo := "invoker info"
        invokerUnderTest.ApiInvokerInformation = &invokerInfo
        invokerUnderTest.PrepareNewInvoker()
        assert.Equal(t, "api_invoker_id_invoker_info", *invokerUnderTest.ApiInvokerId)
-       assert.Equal(t, "onboarding_secret_invoker_info", *invokerUnderTest.OnboardingInformation.OnboardingSecret)
 }
index 3646516..200f8d4 100644 (file)
@@ -39,6 +39,8 @@ type AccessManagement interface {
        GetToken(realm string, data map[string][]string) (Jwttoken, error)
        // Add new client in keycloak
        AddClient(clientId string, realm string) error
+       // Returns information about client including secret
+       GetClientRepresentation(clientId string, realm string) (*Client, error)
 }
 
 type AdminUser struct {
@@ -83,7 +85,6 @@ type Jwttoken struct {
 func (km *KeycloakManager) GetToken(realm string, data map[string][]string) (Jwttoken, error) {
        var jwt Jwttoken
        getTokenUrl := km.keycloakServerUrl + "/realms/" + realm + "/protocol/openid-connect/token"
-
        resp, err := http.PostForm(getTokenUrl, data)
 
        if err != nil {
@@ -96,6 +97,7 @@ func (km *KeycloakManager) GetToken(realm string, data map[string][]string) (Jwt
        if err != nil {
                return jwt, err
        }
+
        if resp.StatusCode != http.StatusOK {
                return jwt, errors.New(string(body))
        }
@@ -105,16 +107,20 @@ func (km *KeycloakManager) GetToken(realm string, data map[string][]string) (Jwt
 }
 
 type Client struct {
-       AdminURL               string `json:"adminUrl,omitempty"`
-       BearerOnly             bool   `json:"bearerOnly,omitempty"`
-       ClientID               string `json:"clientId,omitempty"`
-       Enabled                bool   `json:"enabled,omitempty"`
-       PublicClient           bool   `json:"publicClient,omitempty"`
-       RootURL                string `json:"rootUrl,omitempty"`
-       ServiceAccountsEnabled bool   `json:"serviceAccountsEnabled,omitempty"`
+       AdminURL                     string  `json:"adminUrl,omitempty"`
+       AuthorizationServicesEnabled *bool   `json:"authorizationServicesEnabled,omitempty"`
+       BearerOnly                   bool    `json:"bearerOnly,omitempty"`
+       ClientID                     string  `json:"clientId,omitempty"`
+       Enabled                      bool    `json:"enabled,omitempty"`
+       ID                           *string `json:"id,omitempty"`
+       PublicClient                 bool    `json:"publicClient,omitempty"`
+       RootURL                      string  `json:"rootUrl,omitempty"`
+       Secret                       *string `json:"secret,omitempty"`
+       ServiceAccountsEnabled       bool    `json:"serviceAccountsEnabled,omitempty"`
 }
 
 func (km *KeycloakManager) AddClient(clientId string, realm string) error {
+
        data := url.Values{"grant_type": {"password"}, "username": {km.admin.User}, "password": {km.admin.Password}, "client_id": {"admin-cli"}}
        token, err := km.GetToken("master", data)
        if err != nil {
@@ -123,22 +129,56 @@ func (km *KeycloakManager) AddClient(clientId string, realm string) error {
        }
 
        createClientUrl := km.keycloakServerUrl + "/admin/realms/" + realm + "/clients"
-       newClient := Client{
-               ClientID:               clientId,
-               Enabled:                true,
-               ServiceAccountsEnabled: true,
-               BearerOnly:             false,
-               PublicClient:           false,
+       newClient := map[string]interface{}{"clientId": clientId, "serviceAccountsEnabled": true}
+
+       body, err := json.Marshal(newClient)
+       if err != nil {
+               return err
        }
 
-       body, _ := json.Marshal(newClient)
        var headers = map[string]string{"Content-Type": "application/json", "Authorization": "Bearer " + token.AccessToken}
-       if error := restclient.Post(createClientUrl, body, headers, km.client); error != nil {
-               log.Errorf("error with http request: %+v\n", err)
+       if err := restclient.Post(createClientUrl, body, headers, km.client); err != nil {
+               log.Errorf("addClient - error with http request: %+v\n", err)
                return err
        }
 
-       log.Info("Created new client")
+       log.Debug("Created new client")
        return nil
 
 }
+
+func (km *KeycloakManager) GetClientRepresentation(clientId string, realm string) (*Client, error) {
+
+       data := url.Values{"grant_type": {"password"}, "username": {km.admin.User}, "password": {km.admin.Password}, "client_id": {"admin-cli"}}
+       token, err := km.GetToken("master", data)
+       if err != nil {
+               log.Errorf("error wrong credentials or url %v\n", err)
+               return nil, err
+       }
+
+       createClientUrl, _ := url.Parse(km.keycloakServerUrl + "/admin/realms/" + realm + "/clients")
+       q := createClientUrl.Query()
+       q.Add("clientId", clientId)
+       createClientUrl.RawQuery = q.Encode()
+
+       var headers = map[string]string{"Content-Type": "application/json", "Authorization": "Bearer " + token.AccessToken}
+
+       if resp, err := restclient.Get(createClientUrl.String(), headers, km.client); err == nil {
+               var client []Client
+
+               if err = json.Unmarshal(resp, &client); err != nil {
+                       log.Errorf("error unmarshal keycloak client object: %+v\n", err)
+                       return nil, err
+               }
+
+               if len(client) > 0 {
+                       return &client[0], nil
+               }
+               return nil, nil
+
+       } else {
+               log.Errorf("error with http request: %+v\n", err)
+               return nil, err
+       }
+
+}
index 59b914a..35086d4 100644 (file)
@@ -26,6 +26,32 @@ func (_m *AccessManagement) AddClient(clientId string, realm string) error {
        return r0
 }
 
+// GetClientRepresentation provides a mock function with given fields: clientId, realm
+func (_m *AccessManagement) GetClientRepresentation(clientId string, realm string) (*keycloak.Client, error) {
+       ret := _m.Called(clientId, realm)
+
+       var r0 *keycloak.Client
+       var r1 error
+       if rf, ok := ret.Get(0).(func(string, string) (*keycloak.Client, error)); ok {
+               return rf(clientId, realm)
+       }
+       if rf, ok := ret.Get(0).(func(string, string) *keycloak.Client); ok {
+               r0 = rf(clientId, realm)
+       } else {
+               if ret.Get(0) != nil {
+                       r0 = ret.Get(0).(*keycloak.Client)
+               }
+       }
+
+       if rf, ok := ret.Get(1).(func(string, string) error); ok {
+               r1 = rf(clientId, realm)
+       } else {
+               r1 = ret.Error(1)
+       }
+
+       return r0, r1
+}
+
 // GetToken provides a mock function with given fields: realm, data
 func (_m *AccessManagement) GetToken(realm string, data map[string][]string) (keycloak.Jwttoken, error) {
        ret := _m.Called(realm, data)
index c795ed1..a1f9c41 100644 (file)
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.14.0. DO NOT EDIT.
+// Code generated by mockery v2.20.0. DO NOT EDIT.
 
 package mocks
 
@@ -39,6 +39,20 @@ func (_m *ServiceRegister) IsFunctionRegistered(functionId string) bool {
        return r0
 }
 
+// IsPublishingFunctionRegistered provides a mock function with given fields: apiProvFuncId
+func (_m *ServiceRegister) IsPublishingFunctionRegistered(apiProvFuncId string) bool {
+       ret := _m.Called(apiProvFuncId)
+
+       var r0 bool
+       if rf, ok := ret.Get(0).(func(string) bool); ok {
+               r0 = rf(apiProvFuncId)
+       } else {
+               r0 = ret.Get(0).(bool)
+       }
+
+       return r0
+}
+
 type mockConstructorTestingTNewServiceRegister interface {
        mock.TestingT
        Cleanup(func())
index d5e7a63..2d6bd61 100644 (file)
@@ -38,6 +38,7 @@ import (
 type ServiceRegister interface {
        IsFunctionRegistered(functionId string) bool
        GetAefsForPublisher(apfId string) []string
+       IsPublishingFunctionRegistered(apiProvFuncId string) bool
 }
 
 type ProviderManager struct {
@@ -69,6 +70,15 @@ func (pm *ProviderManager) GetAefsForPublisher(apfId string) []string {
        return nil
 }
 
+func (pm *ProviderManager) IsPublishingFunctionRegistered(apiProvFuncId string) bool {
+       for _, provider := range pm.registeredProviders {
+               if provider.IsPublishingFunctionRegistered(apiProvFuncId) {
+                       return true
+               }
+       }
+       return false
+}
+
 func (pm *ProviderManager) PostRegistrations(ctx echo.Context) error {
        var newProvider provapi.APIProviderEnrolmentDetails
        errMsg := "Unable to register provider due to %s"
index 5257701..b1894d4 100644 (file)
@@ -48,7 +48,16 @@ func (ed APIProviderEnrolmentDetails) IsFunctionRegistered(functionId string) bo
        return false
 }
 
-func (fd APIProviderFunctionDetails) isProvidingFunction() bool {
+func (ed APIProviderEnrolmentDetails) IsPublishingFunctionRegistered(functionId string) bool {
+       for _, registeredFunc := range *ed.ApiProvFuncs {
+               if *registeredFunc.ApiProvFuncId == functionId && registeredFunc.isPublishingFunction() {
+                       return true
+               }
+       }
+       return false
+}
+
+func (fd APIProviderFunctionDetails) isPublishingFunction() bool {
        return fd.ApiProvFuncRole == ApiProviderFuncRoleAPF
 }
 
index 7960f12..2fe5b2e 100644 (file)
@@ -118,6 +118,10 @@ func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) e
                return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errorMsg, "invalid format for service "+apfId))
        }
 
+       if !ps.serviceRegister.IsPublishingFunctionRegistered(apfId) {
+               return sendCoreError(ctx, http.StatusForbidden, fmt.Sprintf(errorMsg, "api is only available for publishers "+apfId))
+       }
+
        if err := ps.isServicePublished(newServiceAPIDescription); err != nil {
                return sendCoreError(ctx, http.StatusForbidden, fmt.Sprintf(errorMsg, err))
        }
index b69b956..8de1e9f 100644 (file)
@@ -52,6 +52,7 @@ func TestPublishUnpublishService(t *testing.T) {
        aefId := "aefId"
        serviceRegisterMock := serviceMocks.ServiceRegister{}
        serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId, "otherAefId"})
+       serviceRegisterMock.On("IsPublishingFunctionRegistered", apfId).Return(true)
        helmManagerMock := helmMocks.HelmManager{}
        helmManagerMock.On("InstallHelmChart", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
        serviceUnderTest, eventChannel, requestHandler := getEcho(&serviceRegisterMock, &helmManagerMock)
@@ -71,7 +72,6 @@ func TestPublishUnpublishService(t *testing.T) {
 
        // Publish a service for provider
        result = testutil.NewRequest().Post("/"+apfId+"/service-apis").WithJsonBody(newServiceDescription).Go(t, requestHandler)
-
        assert.Equal(t, http.StatusCreated, result.Code())
        var resultService publishapi.ServiceAPIDescription
        err := result.UnmarshalBodyToObject(&resultService)
@@ -136,6 +136,7 @@ func TestPostUnpublishedServiceWithUnregisteredFunction(t *testing.T) {
        aefId := "aefId"
        serviceRegisterMock := serviceMocks.ServiceRegister{}
        serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{"otherAefId"})
+       serviceRegisterMock.On("IsPublishingFunctionRegistered", apfId).Return(true)
        _, _, requestHandler := getEcho(&serviceRegisterMock, nil)
 
        newServiceDescription := getServiceAPIDescription(aefId, "apiName", "description")
@@ -157,6 +158,7 @@ func TestGetServices(t *testing.T) {
        aefId := "aefId"
        serviceRegisterMock := serviceMocks.ServiceRegister{}
        serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId})
+       serviceRegisterMock.On("IsPublishingFunctionRegistered", apfId).Return(true)
        _, _, requestHandler := getEcho(&serviceRegisterMock, nil)
 
        // Check no services published for provider
@@ -212,6 +214,7 @@ func TestUpdateDescription(t *testing.T) {
 
        serviceRegisterMock := serviceMocks.ServiceRegister{}
        serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId, "otherAefId", "aefIdNew"})
+       serviceRegisterMock.On("IsPublishingFunctionRegistered", apfId).Return(true)
        helmManagerMock := helmMocks.HelmManager{}
        helmManagerMock.On("InstallHelmChart", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
        serviceUnderTest, eventChannel, requestHandler := getEcho(&serviceRegisterMock, &helmManagerMock)
@@ -285,6 +288,7 @@ func TestUpdateValidServiceWithDeletedFunction(t *testing.T) {
        description := "description"
 
        serviceRegisterMock := serviceMocks.ServiceRegister{}
+       serviceRegisterMock.On("IsPublishingFunctionRegistered", apfId).Return(true)
        serviceRegisterMock.On("GetAefsForPublisher", apfId).Return([]string{aefId, "otherAefId", "aefIdNew"})
        helmManagerMock := helmMocks.HelmManager{}
        helmManagerMock.On("InstallHelmChart", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
@@ -367,7 +371,11 @@ func TestUpdateValidServiceWithDeletedFunction(t *testing.T) {
 }
 
 func TestPublishInvalidService(t *testing.T) {
-       _, _, requestHandler := getEcho(nil, nil)
+       apfId := "apfId"
+       serviceRegisterMock := serviceMocks.ServiceRegister{}
+       serviceRegisterMock.On("IsPublishingFunctionRegistered", apfId).Return(true)
+
+       _, _, requestHandler := getEcho(&serviceRegisterMock, nil)
        newServiceDescription := getServiceAPIDescription("aefId", " ", "description")
 
        // Publish a service
index 287a07d..5e8a8fc 100644 (file)
@@ -21,7 +21,6 @@ package publishserviceapi
 
 import (
        "errors"
-       //"fmt"
        "strings"
 )
 
index c771a54..e9d3469 100644 (file)
@@ -33,6 +33,7 @@ const ContentTypePlain = "text/plain"
 //go:generate mockery --name HTTPClient
 type HTTPClient interface {
        Do(*http.Request) (*http.Response, error)
+       Get(url string) (*http.Response, error)
 }
 
 type RequestError struct {
@@ -44,31 +45,49 @@ func (pe RequestError) Error() string {
        return fmt.Sprintf("Request failed due to error response with status: %v and body: %v", pe.StatusCode, string(pe.Body))
 }
 
+func Get(url string, header map[string]string, client HTTPClient) ([]byte, error) {
+       return do(http.MethodGet, url, nil, header, client)
+}
+
 func Put(url string, body []byte, client HTTPClient) error {
        var header = map[string]string{"Content-Type": ContentTypeJSON}
-       return do(http.MethodPut, url, body, header, client)
+       _, err := do(http.MethodPut, url, body, header, client)
+       return err
 }
 
 func Post(url string, body []byte, header map[string]string, client HTTPClient) error {
-       return do(http.MethodPost, url, body, header, client)
+       _, err := do(http.MethodPost, url, body, header, client)
+       return err
 }
 
-func do(method string, url string, body []byte, header map[string]string, client HTTPClient) error {
-       if req, reqErr := http.NewRequest(method, url, bytes.NewBuffer(body)); reqErr == nil {
+func do(method string, url string, body []byte, header map[string]string, client HTTPClient) ([]byte, error) {
+       if req, reqErr := http.NewRequest(method, url, nil); reqErr == nil {
                if len(header) > 0 {
                        setHeader(req, header)
                }
+               if body != nil {
+                       req.Body = io.NopCloser(bytes.NewReader(body))
+               }
+
                if response, respErr := client.Do(req); respErr == nil {
                        if isResponseSuccess(response.StatusCode) {
-                               return nil
+                               fmt.Printf("HTTP client:: response statuscode:: %v body:: %v\n", response.StatusCode, response.Body)
+                               defer response.Body.Close()
+
+                               // Read the response body
+                               respBody, err := io.ReadAll(response.Body)
+                               if err != nil {
+                                       return nil, err
+                               }
+                               return respBody, nil
                        } else {
-                               return getRequestError(response)
+                               return nil, getRequestError(response)
                        }
                } else {
-                       return respErr
+                       return nil, respErr
                }
        } else {
-               return reqErr
+               return nil, reqErr
        }
 }
 
index e390686..47cf8b7 100644 (file)
@@ -47,6 +47,7 @@ func TestPutOk(t *testing.T) {
 
        clientMock.On("Do", mock.Anything).Return(&http.Response{
                StatusCode: http.StatusOK,
+               Body:       io.NopCloser(bytes.NewReader([]byte("body"))),
        }, nil)
 
        if err := Put("http://localhost:9990", []byte("body"), &clientMock); err != nil {
@@ -67,6 +68,36 @@ func TestPutOk(t *testing.T) {
        clientMock.AssertNumberOfCalls(t, "Do", 1)
 }
 
+func TestPostOk(t *testing.T) {
+       assertions := require.New(t)
+       clientMock := mocks.HTTPClient{}
+
+       clientMock.On("Do", mock.Anything).Return(&http.Response{
+               StatusCode: http.StatusOK,
+               Body:       io.NopCloser(bytes.NewReader([]byte("body"))),
+       }, nil)
+
+       headers := map[string]string{
+               "Content-Type": "application/json",
+       }
+       if err := Post("http://localhost:9990", []byte("body"), headers, &clientMock); err != nil {
+               t.Errorf("Post() error = %v, did not want error", err)
+       }
+       var actualRequest *http.Request
+       clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
+               actualRequest = req
+               return true
+       }))
+       assertions.Equal(http.MethodPost, actualRequest.Method)
+       assertions.Equal("http", actualRequest.URL.Scheme)
+       assertions.Equal("localhost:9990", actualRequest.URL.Host)
+       assertions.Equal("application/json", actualRequest.Header.Get("Content-Type"))
+       body, _ := io.ReadAll(actualRequest.Body)
+       expectedBody := []byte("body")
+       assertions.Equal(expectedBody, body)
+       clientMock.AssertNumberOfCalls(t, "Do", 1)
+}
+
 func Test_doErrorCases(t *testing.T) {
        assertions := require.New(t)
        type args struct {
@@ -109,7 +140,7 @@ func Test_doErrorCases(t *testing.T) {
                                StatusCode: tt.args.mockReturnStatus,
                                Body:       io.NopCloser(bytes.NewReader(tt.args.mockReturnBody)),
                        }, tt.args.mockReturnError)
-                       err := do("PUT", tt.args.url, nil, map[string]string{}, &clientMock)
+                       _, err := do("PUT", tt.args.url, nil, map[string]string{}, &clientMock)
                        assertions.Equal(tt.wantErr, err, tt.name)
                })
        }
index 7627c4b..7d0064a 100644 (file)
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.14.0. DO NOT EDIT.
+// Code generated by mockery v2.20.0. DO NOT EDIT.
 
 package mocks
 
@@ -18,6 +18,10 @@ func (_m *HTTPClient) Do(_a0 *http.Request) (*http.Response, error) {
        ret := _m.Called(_a0)
 
        var r0 *http.Response
+       var r1 error
+       if rf, ok := ret.Get(0).(func(*http.Request) (*http.Response, error)); ok {
+               return rf(_a0)
+       }
        if rf, ok := ret.Get(0).(func(*http.Request) *http.Response); ok {
                r0 = rf(_a0)
        } else {
@@ -26,7 +30,6 @@ func (_m *HTTPClient) Do(_a0 *http.Request) (*http.Response, error) {
                }
        }
 
-       var r1 error
        if rf, ok := ret.Get(1).(func(*http.Request) error); ok {
                r1 = rf(_a0)
        } else {
@@ -36,6 +39,32 @@ func (_m *HTTPClient) Do(_a0 *http.Request) (*http.Response, error) {
        return r0, r1
 }
 
+// Get provides a mock function with given fields: url
+func (_m *HTTPClient) Get(url string) (*http.Response, error) {
+       ret := _m.Called(url)
+
+       var r0 *http.Response
+       var r1 error
+       if rf, ok := ret.Get(0).(func(string) (*http.Response, error)); ok {
+               return rf(url)
+       }
+       if rf, ok := ret.Get(0).(func(string) *http.Response); ok {
+               r0 = rf(url)
+       } else {
+               if ret.Get(0) != nil {
+                       r0 = ret.Get(0).(*http.Response)
+               }
+       }
+
+       if rf, ok := ret.Get(1).(func(string) error); ok {
+               r1 = rf(url)
+       } else {
+               r1 = ret.Error(1)
+       }
+
+       return r0, r1
+}
+
 type mockConstructorTestingTNewHTTPClient interface {
        mock.TestingT
        Cleanup(func())
index 3402b8e..3adb836 100644 (file)
@@ -59,7 +59,6 @@ func isSecuryMethodsEmpty() bool {
 }
 
 func addSecurityMethodsFromInterfaceDetails(methodsFromInterface *[]publishserviceapi.SecurityMethod, prefMethods *[]publishserviceapi.SecurityMethod) {
-
        if methodsFromInterface != nil {
                securityMethods = append(securityMethods, *methodsFromInterface...)
        }
@@ -68,9 +67,9 @@ func addSecurityMethodsFromInterfaceDetails(methodsFromInterface *[]publishservi
        }
 }
 
-func addSecurityMethodsFromAefProfile(afpProfile *publishserviceapi.AefProfile) {
-       if afpProfile.SecurityMethods != nil {
-               securityMethods = append(securityMethods, *afpProfile.SecurityMethods...)
+func addSecurityMethodsFromAefProfile(aefProfile *publishserviceapi.AefProfile) {
+       if aefProfile.SecurityMethods != nil {
+               securityMethods = append(securityMethods, *aefProfile.SecurityMethods...)
        }
 }
 
index e211f67..ddedc85 100644 (file)
@@ -192,11 +192,6 @@ func (s *Security) PutTrustedInvokersApiInvokerId(ctx echo.Context, apiInvokerId
                return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err))
        }
 
-       err = s.keycloak.AddClient(apiInvokerId, "invokerrealm")
-       if err != nil {
-               return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err))
-       }
-
        uri := ctx.Request().Host + ctx.Request().URL.String()
        ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, apiInvokerId))
 
index 1dda127..ea0fbe6 100644 (file)
@@ -263,10 +263,7 @@ func TestPutTrustedInvokerSuccessfully(t *testing.T) {
        publishRegisterMock := publishmocks.PublishRegister{}
        publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices)
 
-       accessMgmMock := keycloackmocks.AccessManagement{}
-       accessMgmMock.On("AddClient", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil)
-
-       requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock)
+       requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil)
 
        invokerId := "invokerId"
        serviceSecurityUnderTest := getServiceSecurity(aefId, apiId)
@@ -285,8 +282,6 @@ func TestPutTrustedInvokerSuccessfully(t *testing.T) {
                assert.Equal(t, *security.SelSecurityMethod, publishserviceapi.SecurityMethodPKI)
        }
        invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId)
-       accessMgmMock.AssertCalled(t, "AddClient", invokerId, "invokerrealm")
-
 }
 
 func TestPutTrustedInkoverNotRegistered(t *testing.T) {
@@ -354,10 +349,7 @@ func TestPutTrustedInvokerInterfaceDetailsNotNil(t *testing.T) {
        publishRegisterMock := publishmocks.PublishRegister{}
        publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices)
 
-       accessMgmMock := keycloackmocks.AccessManagement{}
-       accessMgmMock.On("AddClient", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil)
-
-       requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock)
+       requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil)
 
        invokerId := "invokerId"
        serviceSecurityUnderTest := getServiceSecurity(aefId, apiId)
@@ -386,7 +378,6 @@ func TestPutTrustedInvokerInterfaceDetailsNotNil(t *testing.T) {
                assert.Equal(t, publishserviceapi.SecurityMethodPSK, *security.SelSecurityMethod)
        }
        invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId)
-       accessMgmMock.AssertCalled(t, "AddClient", invokerId, "invokerrealm")
 }
 
 func TestPutTrustedInvokerNotFoundSecurityMethod(t *testing.T) {
index 5ba3923..93c0626 100644 (file)
@@ -137,7 +137,7 @@ func getEcho() *echo.Echo {
                log.Fatalf("Error loading InvokerManagement swagger spec\n: %s", err)
        }
        invokerManagerSwagger.Servers = nil
-       invokerManager := invokermanagement.NewInvokerManager(publishService, eventChannel)
+       invokerManager := invokermanagement.NewInvokerManager(publishService, km, eventChannel)
        group = e.Group("/api-invoker-management/v1")
        group.Use(middleware.OapiRequestValidator(invokerManagerSwagger))
        invokermanagementapi.RegisterHandlersWithBaseURL(e, invokerManager, "/api-invoker-management/v1")