From 65f718afb57c1aab02fa48d34582f133ff65d5af Mon Sep 17 00:00:00 2001 From: shikha0203 Date: Tue, 7 Feb 2023 14:54:29 +0000 Subject: [PATCH 01/16] Added typeupdate and typeaccess Issue-ID: NONRTRIC-814 Signed-off-by: shikha0203 Change-Id: I4d74454a88b5d1bb3d98375510211ddb69476e3d --- .../invokermanagement/invokermanagement.go | 12 ----- .../internal/publishservice/publishservice.go | 53 ++-------------------- .../internal/publishservice/publishservice_test.go | 2 - capifcore/internal/publishserviceapi/typeaccess.go | 30 ++++++++++++ capifcore/internal/publishserviceapi/typeupdate.go | 26 +++++++++++ 5 files changed, 59 insertions(+), 64 deletions(-) create mode 100644 capifcore/internal/publishserviceapi/typeaccess.go create mode 100644 capifcore/internal/publishserviceapi/typeupdate.go diff --git a/capifcore/internal/invokermanagement/invokermanagement.go b/capifcore/internal/invokermanagement/invokermanagement.go index c6f2db3..1fbe2f0 100644 --- a/capifcore/internal/invokermanagement/invokermanagement.go +++ b/capifcore/internal/invokermanagement/invokermanagement.go @@ -21,14 +21,12 @@ package invokermanagement import ( - "errors" "fmt" "net/http" "path" "sync" "oransc.org/nonrtric/capifcore/internal/eventsapi" - publishapi "oransc.org/nonrtric/capifcore/internal/publishserviceapi" "oransc.org/nonrtric/capifcore/internal/common29122" invokerapi "oransc.org/nonrtric/capifcore/internal/invokermanagementapi" @@ -214,20 +212,10 @@ func (im *InvokerManager) validateInvoker(invoker invokerapi.APIInvokerEnrolment if err := invoker.Validate(); err != nil { return err } - if !im.areAPIsPublished(invoker.ApiList) { - return errors.New("some APIs needed by invoker are not registered") - } return nil } -func (im *InvokerManager) areAPIsPublished(apis *invokerapi.APIList) bool { - if apis == nil { - return true - } - return im.publishRegister.AreAPIsPublished((*[]publishapi.ServiceAPIDescription)(apis)) -} - func (im *InvokerManager) sendEvent(invokerId string, eventType eventsapi.CAPIFEvent) { invokerIds := []string{invokerId} event := eventsapi.EventNotification{ diff --git a/capifcore/internal/publishservice/publishservice.go b/capifcore/internal/publishservice/publishservice.go index ee3efef..bf79899 100644 --- a/capifcore/internal/publishservice/publishservice.go +++ b/capifcore/internal/publishservice/publishservice.go @@ -42,9 +42,6 @@ import ( //go:generate mockery --name PublishRegister type PublishRegister interface { - // Checks if the provided APIs are published. - // Returns true if all provided APIs have been published, false otherwise. - AreAPIsPublished(serviceDescriptions *[]publishapi.ServiceAPIDescription) bool // Checks if the provided API is published. // Returns true if the provided API has been published, false otherwise. IsAPIPublished(aefId, path string) bool @@ -71,15 +68,6 @@ func NewPublishService(serviceRegister providermanagement.ServiceRegister, hm he } } -func (ps *PublishService) AreAPIsPublished(serviceDescriptions *[]publishapi.ServiceAPIDescription) bool { - - if serviceDescriptions != nil { - registeredApis := ps.getAllAefIds() - return checkNewDescriptions(*serviceDescriptions, registeredApis) - } - return true -} - func (ps *PublishService) getAllAefIds() []string { ps.lock.Lock() defer ps.lock.Unlock() @@ -87,46 +75,12 @@ func (ps *PublishService) getAllAefIds() []string { allIds := []string{} for _, descriptions := range ps.publishedServices { for _, description := range descriptions { - allIds = append(allIds, getIdsFromDescription(description)...) + allIds = append(allIds, description.GetAefIds()...) } } return allIds } -func getIdsFromDescription(description publishapi.ServiceAPIDescription) []string { - allIds := []string{} - if description.AefProfiles != nil { - for _, aefProfile := range *description.AefProfiles { - allIds = append(allIds, aefProfile.AefId) - } - } - return allIds -} - -func checkNewDescriptions(newDescriptions []publishapi.ServiceAPIDescription, registeredAefIds []string) bool { - registered := true - for _, newApi := range newDescriptions { - if !checkProfiles(newApi.AefProfiles, registeredAefIds) { - registered = false - break - } - } - return registered -} - -func checkProfiles(newProfiles *[]publishapi.AefProfile, registeredAefIds []string) bool { - allRegistered := true - if newProfiles != nil { - for _, profile := range *newProfiles { - if !slices.Contains(registeredAefIds, profile.AefId) { - allRegistered = false - break - } - } - } - return allRegistered -} - func (ps *PublishService) IsAPIPublished(aefId, path string) bool { return slices.Contains(ps.getAllAefIds(), aefId) } @@ -181,8 +135,7 @@ func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) e } } - newId := "api_id_" + newServiceAPIDescription.ApiName - newServiceAPIDescription.ApiId = &newId + newServiceAPIDescription.PrepareNewService() shouldReturn, returnValue := ps.installHelmChart(newServiceAPIDescription, ctx) if shouldReturn { @@ -307,11 +260,11 @@ func (ps *PublishService) PutApfIdServiceApisServiceApiId(ctx echo.Context, apfI if err != nil { return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err)) } - ps.updateDescription(pos, apfId, &updatedServiceDescription, &publishedService) err = ps.checkProfilesRegistered(apfId, *updatedServiceDescription.AefProfiles) if err != nil { return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err)) } + ps.updateDescription(pos, apfId, &updatedServiceDescription, &publishedService) publishedService.AefProfiles = updatedServiceDescription.AefProfiles ps.publishedServices[apfId][pos] = publishedService err = ctx.JSON(http.StatusOK, publishedService) diff --git a/capifcore/internal/publishservice/publishservice_test.go b/capifcore/internal/publishservice/publishservice_test.go index 3c19204..b69b956 100644 --- a/capifcore/internal/publishservice/publishservice_test.go +++ b/capifcore/internal/publishservice/publishservice_test.go @@ -80,8 +80,6 @@ func TestPublishUnpublishService(t *testing.T) { assert.Equal(t, newApiId, *resultService.ApiId) assert.Equal(t, "http://example.com/"+apfId+"/service-apis/"+*resultService.ApiId, result.Recorder.Header().Get(echo.HeaderLocation)) newServiceDescription.ApiId = &newApiId - wantedAPILIst := []publishapi.ServiceAPIDescription{newServiceDescription} - assert.True(t, serviceUnderTest.AreAPIsPublished(&wantedAPILIst)) assert.True(t, serviceUnderTest.IsAPIPublished(aefId, apiName)) serviceRegisterMock.AssertCalled(t, "GetAefsForPublisher", apfId) helmManagerMock.AssertCalled(t, "InstallHelmChart", namespace, repoName, chartName, releaseName) diff --git a/capifcore/internal/publishserviceapi/typeaccess.go b/capifcore/internal/publishserviceapi/typeaccess.go new file mode 100644 index 0000000..32c1a7a --- /dev/null +++ b/capifcore/internal/publishserviceapi/typeaccess.go @@ -0,0 +1,30 @@ +// - +// +// ========================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 publishserviceapi + +func (sd ServiceAPIDescription) GetAefIds() []string { + allIds := []string{} + if sd.AefProfiles != nil { + for _, aefProfile := range *sd.AefProfiles { + allIds = append(allIds, aefProfile.AefId) + } + } + return allIds +} diff --git a/capifcore/internal/publishserviceapi/typeupdate.go b/capifcore/internal/publishserviceapi/typeupdate.go new file mode 100644 index 0000000..98e059c --- /dev/null +++ b/capifcore/internal/publishserviceapi/typeupdate.go @@ -0,0 +1,26 @@ +// - +// ========================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 publishserviceapi + +func (sd *ServiceAPIDescription) PrepareNewService() { + apiName := "api_id_" + sd.ApiName + sd.ApiId = &apiName +} -- 2.16.6 From e499f63287a0786ed1fb94f5bf2cce09422c518f Mon Sep 17 00:00:00 2001 From: elinuxhenrik Date: Tue, 7 Feb 2023 16:50:05 +0100 Subject: [PATCH 02/16] Generate new mock for PublishRegister Issue-ID: NONRTRIC-814 Signed-off-by: elinuxhenrik Change-Id: Ib6ed58f3d777e47d968f5c87925a8044a176905e --- capifcore/internal/publishservice/mocks/PublishRegister.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/capifcore/internal/publishservice/mocks/PublishRegister.go b/capifcore/internal/publishservice/mocks/PublishRegister.go index a798a71..df25620 100644 --- a/capifcore/internal/publishservice/mocks/PublishRegister.go +++ b/capifcore/internal/publishservice/mocks/PublishRegister.go @@ -13,20 +13,6 @@ type PublishRegister struct { mock.Mock } -// AreAPIsPublished provides a mock function with given fields: serviceDescriptions -func (_m *PublishRegister) AreAPIsPublished(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool { - ret := _m.Called(serviceDescriptions) - - var r0 bool - if rf, ok := ret.Get(0).(func(*[]publishserviceapi.ServiceAPIDescription) bool); ok { - r0 = rf(serviceDescriptions) - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - // GetAllPublishedServices provides a mock function with given fields: func (_m *PublishRegister) GetAllPublishedServices() []publishserviceapi.ServiceAPIDescription { ret := _m.Called() -- 2.16.6 From bf237808ac109b30461a453c59ff4e9cc9b297f4 Mon Sep 17 00:00:00 2001 From: elinuxhenrik Date: Wed, 8 Feb 2023 14:26:24 +0100 Subject: [PATCH 03/16] Refactor check earlier registration Issue-ID: NONRTRIC-814 Signed-off-by: elinuxhenrik Change-Id: Ic9f6038faa123325a0c8a8be7a7f5cc5f7a586a0 --- capifcore/internal/invokermanagement/invokermanagement.go | 12 ++++++------ capifcore/internal/invokermanagementapi/typevalidation.go | 7 +++++-- .../internal/invokermanagementapi/typevalidation_test.go | 6 +++--- capifcore/internal/providermanagement/providermanagement.go | 12 ++++++------ capifcore/internal/providermanagementapi/typevalidation.go | 7 +++++-- .../internal/providermanagementapi/typevalidation_test.go | 6 +++--- capifcore/internal/publishservice/publishservice.go | 12 ++++++------ capifcore/internal/publishserviceapi/typevalidation.go | 7 +++++-- capifcore/internal/publishserviceapi/typevalidation_test.go | 6 +++--- 9 files changed, 42 insertions(+), 33 deletions(-) diff --git a/capifcore/internal/invokermanagement/invokermanagement.go b/capifcore/internal/invokermanagement/invokermanagement.go index 1fbe2f0..43bdc02 100644 --- a/capifcore/internal/invokermanagement/invokermanagement.go +++ b/capifcore/internal/invokermanagement/invokermanagement.go @@ -106,8 +106,8 @@ func (im *InvokerManager) PostOnboardedInvokers(ctx echo.Context) error { return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, "invalid format for invoker")) } - if im.isInvokerOnboarded(newInvoker) { - return sendCoreError(ctx, http.StatusForbidden, fmt.Sprintf(errMsg, "invoker already onboarded")) + if err := im.isInvokerOnboarded(newInvoker); err != nil { + return sendCoreError(ctx, http.StatusForbidden, fmt.Sprintf(errMsg, err)) } if err := im.validateInvoker(newInvoker, ctx); err != nil { @@ -129,13 +129,13 @@ func (im *InvokerManager) PostOnboardedInvokers(ctx echo.Context) error { return nil } -func (im *InvokerManager) isInvokerOnboarded(newInvoker invokerapi.APIInvokerEnrolmentDetails) bool { +func (im *InvokerManager) isInvokerOnboarded(newInvoker invokerapi.APIInvokerEnrolmentDetails) error { for _, invoker := range im.onboardedInvokers { - if invoker.IsOnboarded(newInvoker) { - return true + if err := invoker.ValidateAlreadyOnboarded(newInvoker); err != nil { + return err } } - return false + return nil } func (im *InvokerManager) prepareNewInvoker(newInvoker *invokerapi.APIInvokerEnrolmentDetails) { diff --git a/capifcore/internal/invokermanagementapi/typevalidation.go b/capifcore/internal/invokermanagementapi/typevalidation.go index 8bfb784..10db338 100644 --- a/capifcore/internal/invokermanagementapi/typevalidation.go +++ b/capifcore/internal/invokermanagementapi/typevalidation.go @@ -42,6 +42,9 @@ func (ied *APIInvokerEnrolmentDetails) Validate() error { return nil } -func (ied *APIInvokerEnrolmentDetails) IsOnboarded(otherInvoker APIInvokerEnrolmentDetails) bool { - return ied.OnboardingInformation.ApiInvokerPublicKey == otherInvoker.OnboardingInformation.ApiInvokerPublicKey +func (ied *APIInvokerEnrolmentDetails) ValidateAlreadyOnboarded(otherInvoker APIInvokerEnrolmentDetails) error { + if ied.OnboardingInformation.ApiInvokerPublicKey == otherInvoker.OnboardingInformation.ApiInvokerPublicKey { + return errors.New("invoker with identical public key already onboarded") + } + return nil } diff --git a/capifcore/internal/invokermanagementapi/typevalidation_test.go b/capifcore/internal/invokermanagementapi/typevalidation_test.go index 499b0a1..b6b491d 100644 --- a/capifcore/internal/invokermanagementapi/typevalidation_test.go +++ b/capifcore/internal/invokermanagementapi/typevalidation_test.go @@ -54,7 +54,7 @@ func TestValidateInvoker(t *testing.T) { assert.Nil(t, err) } -func TestIsOnboarded(t *testing.T) { +func TestValidateAlreadyOnboarded(t *testing.T) { publicKey := "publicKey" invokerUnderTest := APIInvokerEnrolmentDetails{ OnboardingInformation: OnboardingInformation{ @@ -67,8 +67,8 @@ func TestIsOnboarded(t *testing.T) { ApiInvokerPublicKey: "otherPublicKey", }, } - assert.False(t, invokerUnderTest.IsOnboarded(otherInvoker)) + assert.Nil(t, invokerUnderTest.ValidateAlreadyOnboarded(otherInvoker)) otherInvoker.OnboardingInformation.ApiInvokerPublicKey = publicKey - assert.True(t, invokerUnderTest.IsOnboarded(otherInvoker)) + assert.NotNil(t, invokerUnderTest.ValidateAlreadyOnboarded(otherInvoker)) } diff --git a/capifcore/internal/providermanagement/providermanagement.go b/capifcore/internal/providermanagement/providermanagement.go index 5e9211e..d5e7a63 100644 --- a/capifcore/internal/providermanagement/providermanagement.go +++ b/capifcore/internal/providermanagement/providermanagement.go @@ -76,8 +76,8 @@ func (pm *ProviderManager) PostRegistrations(ctx echo.Context) error { return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, "invalid format for provider")) } - if pm.isProviderRegistered(newProvider) { - return sendCoreError(ctx, http.StatusForbidden, fmt.Sprintf(errMsg, "provider already registered")) + if err := pm.isProviderRegistered(newProvider); err != nil { + return sendCoreError(ctx, http.StatusForbidden, fmt.Sprintf(errMsg, err)) } if err := newProvider.Validate(); err != nil { @@ -95,13 +95,13 @@ func (pm *ProviderManager) PostRegistrations(ctx echo.Context) error { return nil } -func (pm *ProviderManager) isProviderRegistered(newProvider provapi.APIProviderEnrolmentDetails) bool { +func (pm *ProviderManager) isProviderRegistered(newProvider provapi.APIProviderEnrolmentDetails) error { for _, prov := range pm.registeredProviders { - if prov.IsRegistered(newProvider) { - return true + if err := prov.ValidateAlreadyRegistered(newProvider); err != nil { + return err } } - return false + return nil } func (pm *ProviderManager) prepareNewProvider(newProvider *provapi.APIProviderEnrolmentDetails) { diff --git a/capifcore/internal/providermanagementapi/typevalidation.go b/capifcore/internal/providermanagementapi/typevalidation.go index 3c514a8..bb32991 100644 --- a/capifcore/internal/providermanagementapi/typevalidation.go +++ b/capifcore/internal/providermanagementapi/typevalidation.go @@ -68,6 +68,9 @@ func (pd APIProviderEnrolmentDetails) validateFunctions() error { return nil } -func (pd APIProviderEnrolmentDetails) IsRegistered(otherProvider APIProviderEnrolmentDetails) bool { - return pd.RegSec == otherProvider.RegSec +func (pd APIProviderEnrolmentDetails) ValidateAlreadyRegistered(otherProvider APIProviderEnrolmentDetails) error { + if pd.RegSec == otherProvider.RegSec { + return errors.New("provider with identical regSec already registered") + } + return nil } diff --git a/capifcore/internal/providermanagementapi/typevalidation_test.go b/capifcore/internal/providermanagementapi/typevalidation_test.go index b79a13b..a5a4e3c 100644 --- a/capifcore/internal/providermanagementapi/typevalidation_test.go +++ b/capifcore/internal/providermanagementapi/typevalidation_test.go @@ -106,7 +106,7 @@ func TestValidateAPIProviderEnrolmentDetails(t *testing.T) { assert.Nil(t, providerDetailsUnderTest.Validate()) } -func TestIsRegistered(t *testing.T) { +func TestValidateAlreadyRegistered(t *testing.T) { regSec := "regSec" providerUnderTest := APIProviderEnrolmentDetails{ RegSec: regSec, @@ -115,10 +115,10 @@ func TestIsRegistered(t *testing.T) { otherProvider := APIProviderEnrolmentDetails{ RegSec: "otherRegSec", } - assert.False(t, providerUnderTest.IsRegistered(otherProvider)) + assert.Nil(t, providerUnderTest.ValidateAlreadyRegistered(otherProvider)) otherProvider.RegSec = regSec - assert.True(t, providerUnderTest.IsRegistered(otherProvider)) + assert.NotNil(t, providerUnderTest.ValidateAlreadyRegistered(otherProvider)) } func getProvider() APIProviderEnrolmentDetails { diff --git a/capifcore/internal/publishservice/publishservice.go b/capifcore/internal/publishservice/publishservice.go index bf79899..7960f12 100644 --- a/capifcore/internal/publishservice/publishservice.go +++ b/capifcore/internal/publishservice/publishservice.go @@ -118,8 +118,8 @@ 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.isServicePublished(newServiceAPIDescription) { - return sendCoreError(ctx, http.StatusForbidden, fmt.Sprintf(errorMsg, "service already published")) + if err := ps.isServicePublished(newServiceAPIDescription); err != nil { + return sendCoreError(ctx, http.StatusForbidden, fmt.Sprintf(errorMsg, err)) } if err := newServiceAPIDescription.Validate(); err != nil { @@ -161,15 +161,15 @@ func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) e return nil } -func (ps *PublishService) isServicePublished(newService publishapi.ServiceAPIDescription) bool { +func (ps *PublishService) isServicePublished(newService publishapi.ServiceAPIDescription) error { for _, services := range ps.publishedServices { for _, service := range services { - if service.IsPublished(newService) { - return true + if err := service.ValidateAlreadyPublished(newService); err != nil { + return err } } } - return false + return nil } func (ps *PublishService) installHelmChart(newServiceAPIDescription publishapi.ServiceAPIDescription, ctx echo.Context) (bool, error) { diff --git a/capifcore/internal/publishserviceapi/typevalidation.go b/capifcore/internal/publishserviceapi/typevalidation.go index ad19324..287a07d 100644 --- a/capifcore/internal/publishserviceapi/typevalidation.go +++ b/capifcore/internal/publishserviceapi/typevalidation.go @@ -32,6 +32,9 @@ func (sd ServiceAPIDescription) Validate() error { return nil } -func (sd ServiceAPIDescription) IsPublished(otherService ServiceAPIDescription) bool { - return sd.ApiName == otherService.ApiName +func (sd ServiceAPIDescription) ValidateAlreadyPublished(otherService ServiceAPIDescription) error { + if sd.ApiName == otherService.ApiName { + return errors.New("service with identical apiName is already published") + } + return nil } diff --git a/capifcore/internal/publishserviceapi/typevalidation_test.go b/capifcore/internal/publishserviceapi/typevalidation_test.go index 39e3619..d7b59d5 100644 --- a/capifcore/internal/publishserviceapi/typevalidation_test.go +++ b/capifcore/internal/publishserviceapi/typevalidation_test.go @@ -39,7 +39,7 @@ func TestValidate(t *testing.T) { } -func TestIsServicePublished(t *testing.T) { +func TestValidateAlreadyPublished(t *testing.T) { apiName := "apiName" serviceUnderTest := ServiceAPIDescription{ ApiName: apiName, @@ -48,8 +48,8 @@ func TestIsServicePublished(t *testing.T) { otherService := ServiceAPIDescription{ ApiName: "otherApiName", } - assert.False(t, serviceUnderTest.IsPublished(otherService)) + assert.Nil(t, serviceUnderTest.ValidateAlreadyPublished(otherService)) otherService.ApiName = apiName - assert.True(t, serviceUnderTest.IsPublished(otherService)) + assert.NotNil(t, serviceUnderTest.ValidateAlreadyPublished(otherService)) } -- 2.16.6 From 8bae1ff85eef426eafced1dabac9fadd733dd29a Mon Sep 17 00:00:00 2001 From: elinuxhenrik Date: Thu, 9 Feb 2023 09:52:33 +0100 Subject: [PATCH 04/16] Remov unnecessary comment Issue-ID: NONRTRIC-814 Signed-off-by: elinuxhenrik Change-Id: I9f034e1aa747f01b60d0bf8028d957206ddb1c5e --- capifcore/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/capifcore/main.go b/capifcore/main.go index 1c1890f..3ee4192 100644 --- a/capifcore/main.go +++ b/capifcore/main.go @@ -83,7 +83,6 @@ func main() { } func getEcho() *echo.Echo { - // This is how you set up a basic Echo router e := echo.New() // Log all requests e.Use(echomiddleware.Logger()) -- 2.16.6 From cfa08775db2ed44e603b0ceccf36a50f59bd679a Mon Sep 17 00:00:00 2001 From: ychacon Date: Wed, 8 Feb 2023 09:41:34 +0100 Subject: [PATCH 05/16] Changes in implementation of Security API - get Token Issue-ID: NONRTRIC-836 Signed-off-by: ychacon Change-Id: I5eb350a63582c6e24651dd89f392dab0a9db1d8b --- capifcore/internal/securityapi/typeaccess.go | 38 +++++++++ capifcore/internal/securityapi/typevalidation.go | 62 ++++++++++++++ .../internal/securityapi/typevalidation_test.go | 97 ++++++++++++++++++++++ capifcore/internal/securityservice/security.go | 79 ++++++++++++------ .../internal/securityservice/security_test.go | 58 ++++++------- 5 files changed, 278 insertions(+), 56 deletions(-) create mode 100644 capifcore/internal/securityapi/typeaccess.go create mode 100644 capifcore/internal/securityapi/typevalidation.go create mode 100644 capifcore/internal/securityapi/typevalidation_test.go diff --git a/capifcore/internal/securityapi/typeaccess.go b/capifcore/internal/securityapi/typeaccess.go new file mode 100644 index 0000000..8b88312 --- /dev/null +++ b/capifcore/internal/securityapi/typeaccess.go @@ -0,0 +1,38 @@ +// - +// ========================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 securityapi + +import ( + "github.com/labstack/echo/v4" +) + +func (tokenReq *AccessTokenReq) GetAccessTokenReq(ctx echo.Context) { + clientId := ctx.FormValue("client_id") + clientSecret := ctx.FormValue("client_secret") + scope := ctx.FormValue("scope") + grantType := ctx.FormValue("grant_type") + + tokenReq.ClientId = clientId + tokenReq.ClientSecret = &clientSecret + tokenReq.Scope = &scope + tokenReq.GrantType = AccessTokenReqGrantType(grantType) + +} diff --git a/capifcore/internal/securityapi/typevalidation.go b/capifcore/internal/securityapi/typevalidation.go new file mode 100644 index 0000000..90dbda3 --- /dev/null +++ b/capifcore/internal/securityapi/typevalidation.go @@ -0,0 +1,62 @@ +// - +// ========================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 securityapi + +import ( + "strings" +) + +func (tokenReq AccessTokenReq) Validate() (bool, AccessTokenErr) { + + if tokenReq.ClientId == "" { + return false, createAccessTokenError(AccessTokenErrErrorInvalidRequest, "Invalid request") + } + + if tokenReq.GrantType != AccessTokenReqGrantTypeClientCredentials { + return false, createAccessTokenError(AccessTokenErrErrorInvalidGrant, "Invalid value for grant_type") + } + + //3gpp#aefId1:apiName1,apiName2,…apiNameX;aefId2:apiName1,apiName2,…apiNameY;…aefIdN:apiName1,apiName2,…apiNameZ + if tokenReq.Scope != nil { + scope := strings.Split(*tokenReq.Scope, "#") + if len(scope) < 2 { + return false, createAccessTokenError(AccessTokenErrErrorInvalidScope, "Malformed scope") + } + if scope[0] != "3gpp" { + return false, createAccessTokenError(AccessTokenErrErrorInvalidScope, "Scope should start with 3gpp") + } + aefList := strings.Split(scope[1], ";") + for _, aef := range aefList { + apiList := strings.Split(aef, ":") + if len(apiList) < 2 { + return false, createAccessTokenError(AccessTokenErrErrorInvalidScope, "Malformed scope") + } + } + } + return true, AccessTokenErr{} +} + +func createAccessTokenError(err AccessTokenErrError, message string) AccessTokenErr { + return AccessTokenErr{ + Error: err, + ErrorDescription: &message, + } +} diff --git a/capifcore/internal/securityapi/typevalidation_test.go b/capifcore/internal/securityapi/typevalidation_test.go new file mode 100644 index 0000000..0515d06 --- /dev/null +++ b/capifcore/internal/securityapi/typevalidation_test.go @@ -0,0 +1,97 @@ +// - +// ========================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 securityapi + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValidateClientIdNotPresent(t *testing.T) { + accessTokenUnderTest := AccessTokenReq{} + valid, err := accessTokenUnderTest.Validate() + + assert.Equal(t, false, valid) + assert.Equal(t, AccessTokenErrErrorInvalidRequest, err.Error) + assert.Equal(t, "Invalid request", *err.ErrorDescription) +} + +func TestValidateGrantType(t *testing.T) { + accessTokenUnderTest := AccessTokenReq{ + ClientId: "clientId", + GrantType: AccessTokenReqGrantType(""), + } + valid, err := accessTokenUnderTest.Validate() + + assert.Equal(t, false, valid) + assert.Equal(t, AccessTokenErrErrorInvalidGrant, err.Error) + assert.Equal(t, "Invalid value for grant_type", *err.ErrorDescription) + + accessTokenUnderTest.GrantType = AccessTokenReqGrantType("client_credentials") + valid, err = accessTokenUnderTest.Validate() + assert.Equal(t, true, valid) +} + +func TestValidateScopeNotValid(t *testing.T) { + scope := "scope#aefId:path" + accessTokenUnderTest := AccessTokenReq{ + ClientId: "clientId", + GrantType: ("client_credentials"), + Scope: &scope, + } + valid, err := accessTokenUnderTest.Validate() + + assert.Equal(t, false, valid) + assert.Equal(t, AccessTokenErrErrorInvalidScope, err.Error) + assert.Equal(t, "Scope should start with 3gpp", *err.ErrorDescription) + + scope = "3gpp#aefId:path" + accessTokenUnderTest.Scope = &scope + valid, err = accessTokenUnderTest.Validate() + assert.Equal(t, true, valid) +} + +func TestValidateScopeMalformed(t *testing.T) { + scope := "3gpp" + accessTokenUnderTest := AccessTokenReq{ + ClientId: "clientId", + GrantType: ("client_credentials"), + Scope: &scope, + } + valid, err := accessTokenUnderTest.Validate() + + assert.Equal(t, false, valid) + assert.Equal(t, AccessTokenErrErrorInvalidScope, err.Error) + assert.Equal(t, "Malformed scope", *err.ErrorDescription) + + scope = "3gpp#aefId" + accessTokenUnderTest.Scope = &scope + valid, err = accessTokenUnderTest.Validate() + assert.Equal(t, false, valid) + assert.Equal(t, AccessTokenErrErrorInvalidScope, err.Error) + assert.Equal(t, "Malformed scope", *err.ErrorDescription) + + scope = "3gpp#aefId:path" + accessTokenUnderTest.Scope = &scope + valid, err = accessTokenUnderTest.Validate() + assert.Equal(t, true, valid) +} diff --git a/capifcore/internal/securityservice/security.go b/capifcore/internal/securityservice/security.go index 3df2918..d71ff0d 100644 --- a/capifcore/internal/securityservice/security.go +++ b/capifcore/internal/securityservice/security.go @@ -23,7 +23,9 @@ package security import ( "net/http" "strings" + "time" + "github.com/golang-jwt/jwt" "github.com/labstack/echo/v4" "oransc.org/nonrtric/capifcore/internal/common29122" @@ -34,6 +36,8 @@ import ( "oransc.org/nonrtric/capifcore/internal/publishservice" ) +var jwtKey = "my-secret-key" + type Security struct { serviceRegister providermanagement.ServiceRegister publishRegister publishservice.PublishRegister @@ -49,34 +53,62 @@ func NewSecurity(serviceRegister providermanagement.ServiceRegister, publishRegi } func (s *Security) PostSecuritiesSecurityIdToken(ctx echo.Context, securityId string) error { - clientId := ctx.FormValue("client_id") - clientSecret := ctx.FormValue("client_secret") - scope := ctx.FormValue("scope") + var accessTokenReq securityapi.AccessTokenReq + accessTokenReq.GetAccessTokenReq(ctx) - if !s.invokerRegister.IsInvokerRegistered(clientId) { - return sendCoreError(ctx, http.StatusBadRequest, "Invoker not registered") + if valid, err := accessTokenReq.Validate(); !valid { + return ctx.JSON(http.StatusBadRequest, err) } - if !s.invokerRegister.VerifyInvokerSecret(clientId, clientSecret) { - return sendCoreError(ctx, http.StatusBadRequest, "Invoker secret not valid") + + if !s.invokerRegister.IsInvokerRegistered(accessTokenReq.ClientId) { + return sendAccessTokenError(ctx, http.StatusBadRequest, securityapi.AccessTokenErrErrorInvalidClient, "Invoker not registered") } - if scope != "" { - scopeData := strings.Split(strings.Split(scope, "#")[1], ":") - if !s.serviceRegister.IsFunctionRegistered(scopeData[0]) { - return sendCoreError(ctx, http.StatusBadRequest, "Function not registered") - } - if !s.publishRegister.IsAPIPublished(scopeData[0], scopeData[1]) { - return sendCoreError(ctx, http.StatusBadRequest, "API not published") + + if !s.invokerRegister.VerifyInvokerSecret(accessTokenReq.ClientId, *accessTokenReq.ClientSecret) { + return sendAccessTokenError(ctx, http.StatusBadRequest, securityapi.AccessTokenErrErrorUnauthorizedClient, "Invoker secret not valid") + } + + if accessTokenReq.Scope != nil { + scope := strings.Split(*accessTokenReq.Scope, "#") + aefList := strings.Split(scope[1], ";") + for _, aef := range aefList { + apiList := strings.Split(aef, ":") + if !s.serviceRegister.IsFunctionRegistered(apiList[0]) { + return sendAccessTokenError(ctx, http.StatusBadRequest, securityapi.AccessTokenErrErrorInvalidScope, "AEF Function not registered") + } + for _, api := range strings.Split(apiList[1], ",") { + if !s.publishRegister.IsAPIPublished(apiList[0], api) { + return sendAccessTokenError(ctx, http.StatusBadRequest, securityapi.AccessTokenErrErrorInvalidScope, "API not published") + } + } } } + expirationTime := time.Now().Add(time.Hour).Unix() + + claims := &jwt.MapClaims{ + "iss": accessTokenReq.ClientId, + "exp": expirationTime, + "data": map[string]interface{}{ + "scope": accessTokenReq.Scope, + }, + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + tokenString, err := token.SignedString([]byte(jwtKey)) + if err != nil { + // If there is an error in creating the JWT return an internal server error + return err + } + accessTokenResp := securityapi.AccessTokenRsp{ - AccessToken: "asdadfsrt dsr t5", - ExpiresIn: 0, - Scope: &scope, + AccessToken: tokenString, + ExpiresIn: common29122.DurationSec(expirationTime), + Scope: accessTokenReq.Scope, TokenType: "Bearer", } - err := ctx.JSON(http.StatusCreated, accessTokenResp) + err = ctx.JSON(http.StatusCreated, accessTokenResp) if err != nil { // Something really bad happened, tell Echo that our handler failed return err @@ -105,11 +137,10 @@ func (s *Security) PostTrustedInvokersApiInvokerIdUpdate(ctx echo.Context, apiIn return ctx.NoContent(http.StatusNotImplemented) } -func sendCoreError(ctx echo.Context, code int, message string) error { - pd := common29122.ProblemDetails{ - Cause: &message, - Status: &code, +func sendAccessTokenError(ctx echo.Context, code int, err securityapi.AccessTokenErrError, message string) error { + accessTokenErr := securityapi.AccessTokenErr{ + Error: err, + ErrorDescription: &message, } - err := ctx.JSON(code, pd) - return err + return ctx.JSON(code, accessTokenErr) } diff --git a/capifcore/internal/securityservice/security_test.go b/capifcore/internal/securityservice/security_test.go index 7043cca..6c62161 100644 --- a/capifcore/internal/securityservice/security_test.go +++ b/capifcore/internal/securityservice/security_test.go @@ -35,8 +35,6 @@ import ( "github.com/labstack/echo/v4" - "oransc.org/nonrtric/capifcore/internal/common29122" - invokermocks "oransc.org/nonrtric/capifcore/internal/invokermanagement/mocks" servicemocks "oransc.org/nonrtric/capifcore/internal/providermanagement/mocks" publishmocks "oransc.org/nonrtric/capifcore/internal/publishservice/mocks" @@ -65,9 +63,10 @@ func TestPostSecurityIdTokenInvokerRegistered(t *testing.T) { aefId := "aefId" path := "path" data.Set("client_id", clientId) - data.Add("client_secret", clientSecret) - data.Add("grant_type", "client_credentials") - data.Add("scope", "scope#"+aefId+":"+path) + data.Set("client_secret", clientSecret) + data.Set("grant_type", "client_credentials") + data.Set("scope", "3gpp#"+aefId+":"+path) + encodedData := data.Encode() result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler) @@ -77,9 +76,8 @@ func TestPostSecurityIdTokenInvokerRegistered(t *testing.T) { err := result.UnmarshalBodyToObject(&resultResponse) assert.NoError(t, err, "error unmarshaling response") assert.NotEmpty(t, resultResponse.AccessToken) - assert.Equal(t, "scope#"+aefId+":"+path, *resultResponse.Scope) + assert.Equal(t, "3gpp#"+aefId+":"+path, *resultResponse.Scope) assert.Equal(t, securityapi.AccessTokenRspTokenTypeBearer, resultResponse.TokenType) - assert.Equal(t, common29122.DurationSec(0), resultResponse.ExpiresIn) invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", clientId) invokerRegisterMock.AssertCalled(t, "VerifyInvokerSecret", clientId, clientSecret) serviceRegisterMock.AssertCalled(t, "IsFunctionRegistered", aefId) @@ -96,19 +94,18 @@ func TestPostSecurityIdTokenInvokerNotRegistered(t *testing.T) { data.Set("client_id", "id") data.Add("client_secret", "secret") data.Add("grant_type", "client_credentials") - data.Add("scope", "scope#aefId:path") + data.Add("scope", "3gpp#aefId:path") encodedData := data.Encode() result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler) assert.Equal(t, http.StatusBadRequest, result.Code()) - var problemDetails common29122.ProblemDetails - err := result.UnmarshalBodyToObject(&problemDetails) + var errDetails securityapi.AccessTokenErr + err := result.UnmarshalBodyToObject(&errDetails) assert.NoError(t, err, "error unmarshaling response") - badRequest := http.StatusBadRequest - assert.Equal(t, &badRequest, problemDetails.Status) + assert.Equal(t, securityapi.AccessTokenErrErrorInvalidClient, errDetails.Error) errMsg := "Invoker not registered" - assert.Equal(t, &errMsg, problemDetails.Cause) + assert.Equal(t, &errMsg, errDetails.ErrorDescription) } func TestPostSecurityIdTokenInvokerSecretNotValid(t *testing.T) { @@ -122,19 +119,18 @@ func TestPostSecurityIdTokenInvokerSecretNotValid(t *testing.T) { data.Set("client_id", "id") data.Add("client_secret", "secret") data.Add("grant_type", "client_credentials") - data.Add("scope", "scope#aefId:path") + data.Add("scope", "3gpp#aefId:path") encodedData := data.Encode() result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler) assert.Equal(t, http.StatusBadRequest, result.Code()) - var problemDetails common29122.ProblemDetails - err := result.UnmarshalBodyToObject(&problemDetails) + var errDetails securityapi.AccessTokenErr + err := result.UnmarshalBodyToObject(&errDetails) assert.NoError(t, err, "error unmarshaling response") - badRequest := http.StatusBadRequest - assert.Equal(t, &badRequest, problemDetails.Status) + assert.Equal(t, securityapi.AccessTokenErrErrorUnauthorizedClient, errDetails.Error) errMsg := "Invoker secret not valid" - assert.Equal(t, &errMsg, problemDetails.Cause) + assert.Equal(t, &errMsg, errDetails.ErrorDescription) } func TestPostSecurityIdTokenFunctionNotRegistered(t *testing.T) { @@ -150,19 +146,18 @@ func TestPostSecurityIdTokenFunctionNotRegistered(t *testing.T) { data.Set("client_id", "id") data.Add("client_secret", "secret") data.Add("grant_type", "client_credentials") - data.Add("scope", "scope#aefId:path") + data.Add("scope", "3gpp#aefId:path") encodedData := data.Encode() result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler) assert.Equal(t, http.StatusBadRequest, result.Code()) - var problemDetails common29122.ProblemDetails - err := result.UnmarshalBodyToObject(&problemDetails) + var errDetails securityapi.AccessTokenErr + err := result.UnmarshalBodyToObject(&errDetails) assert.NoError(t, err, "error unmarshaling response") - badRequest := http.StatusBadRequest - assert.Equal(t, &badRequest, problemDetails.Status) - errMsg := "Function not registered" - assert.Equal(t, &errMsg, problemDetails.Cause) + assert.Equal(t, securityapi.AccessTokenErrErrorInvalidScope, errDetails.Error) + errMsg := "AEF Function not registered" + assert.Equal(t, &errMsg, errDetails.ErrorDescription) } func TestPostSecurityIdTokenAPINotPublished(t *testing.T) { @@ -180,19 +175,18 @@ func TestPostSecurityIdTokenAPINotPublished(t *testing.T) { data.Set("client_id", "id") data.Add("client_secret", "secret") data.Add("grant_type", "client_credentials") - data.Add("scope", "scope#aefId:path") + data.Add("scope", "3gpp#aefId:path") encodedData := data.Encode() result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler) assert.Equal(t, http.StatusBadRequest, result.Code()) - var problemDetails common29122.ProblemDetails - err := result.UnmarshalBodyToObject(&problemDetails) + var errDetails securityapi.AccessTokenErr + err := result.UnmarshalBodyToObject(&errDetails) assert.NoError(t, err, "error unmarshaling response") - badRequest := http.StatusBadRequest - assert.Equal(t, &badRequest, problemDetails.Status) + assert.Equal(t, securityapi.AccessTokenErrErrorInvalidScope, errDetails.Error) errMsg := "API not published" - assert.Equal(t, &errMsg, problemDetails.Cause) + assert.Equal(t, &errMsg, errDetails.ErrorDescription) } func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister) *echo.Echo { -- 2.16.6 From d55825ec39422d2b54da563fb3182f0cde630da3 Mon Sep 17 00:00:00 2001 From: ychacon Date: Thu, 9 Feb 2023 16:48:36 +0100 Subject: [PATCH 06/16] Update Release Notes for G Maintenance Release Issue-ID: NONRTRIC-838 Signed-off-by: ychacon Change-Id: I2ef1670a4d0eaaa58087f7146e783edb861cfcde --- docs/release-notes.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/release-notes.rst b/docs/release-notes.rst index d2f7d56..e17aa2b 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -20,6 +20,9 @@ Version history capifcore | | | | Initial version | | | | | of capifcore | +------------+----------+------------------+-----------------+ +| 2023-02-10 | 1.0.1 | Yennifer Chacon | G Maintenance | +| | | | Release | ++------------+----------+------------------+-----------------+ Release Data ============ @@ -43,3 +46,23 @@ G Release | **Purpose of the delivery** | Introduction of capifcore | | | | +-----------------------------+---------------------------------------------------+ + +G Maintenance +------------- ++-----------------------------+---------------------------------------------------+ +| **Project** | Non-RT RIC CAPIF Core | +| | | ++-----------------------------+---------------------------------------------------+ +| **Repo/commit-ID** | nonrtric/plt/sme/ | +| | Ic9f6038faa123325a0c8a8be7a7f5cc5f7a586a0 | +| | | ++-----------------------------+---------------------------------------------------+ +| **Release designation** | G release | +| | | ++-----------------------------+---------------------------------------------------+ +| **Release date** | 2023-02-10 | +| | | ++-----------------------------+---------------------------------------------------+ +| **Purpose of the delivery** | Refactor and improvements of capifcore | +| | | ++-----------------------------+---------------------------------------------------+ \ No newline at end of file -- 2.16.6 From 604e2992bfd31bde571f29f90864ca74306a39af Mon Sep 17 00:00:00 2001 From: ychacon Date: Mon, 13 Feb 2023 16:34:55 +0100 Subject: [PATCH 07/16] Update commitId in release notes Issue-ID: NONRTRIC-838 Signed-off-by: ychacon Change-Id: I5be89734bbcd0a1c9165b7b797baaecebf1630fa --- docs/release-notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-notes.rst b/docs/release-notes.rst index e17aa2b..7cfe5a3 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -54,7 +54,7 @@ G Maintenance | | | +-----------------------------+---------------------------------------------------+ | **Repo/commit-ID** | nonrtric/plt/sme/ | -| | Ic9f6038faa123325a0c8a8be7a7f5cc5f7a586a0 | +| | b4cee94bd251b0bb92fc435fbe74ea1079d43356 | | | | +-----------------------------+---------------------------------------------------+ | **Release designation** | G release | -- 2.16.6 From 1a36c0f848111d82bf58552662f7bff3b05f79ed Mon Sep 17 00:00:00 2001 From: JohnKeeney Date: Tue, 21 Feb 2023 18:22:13 +0000 Subject: [PATCH 08/16] Update Committers - remove Henrik Issue-ID: NONRTRIC-843 Change-Id: I68a41fb1423285674c13639029b4faed15cd9185 Signed-off-by: JohnKeeney --- INFO.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/INFO.yaml b/INFO.yaml index b6339d9..870c84f 100644 --- a/INFO.yaml +++ b/INFO.yaml @@ -33,10 +33,10 @@ repositories: - 'nonrtric/plt/sme' committers: - <<: *oran_nonrtric_ptl - - name: 'Henrik Andersson' - email: 'henrik.b.andersson@est.tech' + - name: 'Yennifer Chacon' + email: 'yennifer.chacon@est.tech' company: 'Ericsson Software Technology' - id: 'elinuxhenrik' + id: 'ychacon' timezone: 'Sweden/Stockholm' - name: 'Patrik Buhr' email: 'patrik.buhr@est.tech' -- 2.16.6 From 4308df0663b45eb9d95b3babdf519a06ee76c15a Mon Sep 17 00:00:00 2001 From: ychacon Date: Thu, 23 Feb 2023 11:13:14 +0100 Subject: [PATCH 09/16] Generating token using keycloak Issue-ID: NONRTRIC-836 Signed-off-by: ychacon Change-Id: If337576ccac816292709c6d7ab30d239a2d8a77b --- capifcore/configs/keycloak.yaml | 23 ++++++ capifcore/internal/config/config.go | 55 +++++++++++++ capifcore/internal/keycloak/keycloak.go | 90 ++++++++++++++++++++++ .../internal/keycloak/mocks/AccessManagement.go | 52 +++++++++++++ capifcore/internal/securityapi/typevalidation.go | 2 +- capifcore/internal/securityservice/security.go | 32 +++----- .../internal/securityservice/security_test.go | 67 ++++++++++++++-- capifcore/main.go | 11 ++- 8 files changed, 299 insertions(+), 33 deletions(-) create mode 100644 capifcore/configs/keycloak.yaml create mode 100644 capifcore/internal/config/config.go create mode 100644 capifcore/internal/keycloak/keycloak.go create mode 100644 capifcore/internal/keycloak/mocks/AccessManagement.go diff --git a/capifcore/configs/keycloak.yaml b/capifcore/configs/keycloak.yaml new file mode 100644 index 0000000..1ca2ba9 --- /dev/null +++ b/capifcore/configs/keycloak.yaml @@ -0,0 +1,23 @@ +# ============LICENSE_START=============================================== +# Copyright (C) 2023 Nordix Foundation. All rights reserved. +# ======================================================================== +# 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================================================= +# + +# Keycloak configurations +authorizationServer: + host: "localhost" + port: "8080" + realms: + invokerrealm: "invokerrealm" diff --git a/capifcore/internal/config/config.go b/capifcore/internal/config/config.go new file mode 100644 index 0000000..4a53022 --- /dev/null +++ b/capifcore/internal/config/config.go @@ -0,0 +1,55 @@ +// - +// ========================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 config + +import ( + "os" + "path/filepath" + + "gopkg.in/yaml.v2" +) + +type AuthorizationServer struct { + Port string `yaml:"port"` + Host string `yaml:"host"` + Realms map[string]string `yaml:"realms"` +} + +type Config struct { + AuthorizationServer AuthorizationServer `yaml:"authorizationServer"` +} + +func ReadKeycloakConfigFile(configFolder string) (*Config, error) { + + f, err := os.Open(filepath.Join(configFolder, "keycloak.yaml")) + if err != nil { + return nil, err + } + defer f.Close() + + var cfg *Config + decoder := yaml.NewDecoder(f) + err = decoder.Decode(&cfg) + if err != nil { + return nil, err + } + return cfg, nil +} diff --git a/capifcore/internal/keycloak/keycloak.go b/capifcore/internal/keycloak/keycloak.go new file mode 100644 index 0000000..16f65c8 --- /dev/null +++ b/capifcore/internal/keycloak/keycloak.go @@ -0,0 +1,90 @@ +// - +// ========================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 keycloak + +import ( + "encoding/json" + "errors" + "io" + "net/http" + "net/url" + + "oransc.org/nonrtric/capifcore/internal/config" +) + +//go:generate mockery --name AccessManagement +type AccessManagement interface { + // Get JWT token for a client. + // Returns JWT token if client exits and credentials are correct otherwise returns error. + GetToken(clientId, clientPassword, scope string, realm string) (Jwttoken, error) +} + +type KeycloakManager struct { + keycloakServerUrl string + realms map[string]string +} + +func NewKeycloakManager(cfg *config.Config) *KeycloakManager { + + keycloakUrl := "http://" + cfg.AuthorizationServer.Host + ":" + cfg.AuthorizationServer.Port + + return &KeycloakManager{ + keycloakServerUrl: keycloakUrl, + realms: cfg.AuthorizationServer.Realms, + } +} + +type Jwttoken struct { + AccessToken string `json:"access_token"` + IDToken string `json:"id_token"` + ExpiresIn int `json:"expires_in"` + RefreshExpiresIn int `json:"refresh_expires_in"` + RefreshToken string `json:"refresh_token"` + TokenType string `json:"token_type"` + NotBeforePolicy int `json:"not-before-policy"` + SessionState string `json:"session_state"` + Scope string `json:"scope"` +} + +func (km *KeycloakManager) GetToken(clientId, clientPassword, scope string, realm string) (Jwttoken, error) { + var jwt Jwttoken + getTokenUrl := km.keycloakServerUrl + "/realms/" + realm + "/protocol/openid-connect/token" + + resp, err := http.PostForm(getTokenUrl, + url.Values{"grant_type": {"client_credentials"}, "client_id": {clientId}, "client_secret": {clientPassword}}) + + if err != nil { + return jwt, err + } + + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + + if err != nil { + return jwt, err + } + if resp.StatusCode != http.StatusOK { + return jwt, errors.New(string(body)) + } + + json.Unmarshal([]byte(body), &jwt) + return jwt, nil +} diff --git a/capifcore/internal/keycloak/mocks/AccessManagement.go b/capifcore/internal/keycloak/mocks/AccessManagement.go new file mode 100644 index 0000000..9ac2179 --- /dev/null +++ b/capifcore/internal/keycloak/mocks/AccessManagement.go @@ -0,0 +1,52 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + keycloak "oransc.org/nonrtric/capifcore/internal/keycloak" +) + +// AccessManagement is an autogenerated mock type for the AccessManagement type +type AccessManagement struct { + mock.Mock +} + +// GetToken provides a mock function with given fields: clientId, clientPassword, scope, realm +func (_m *AccessManagement) GetToken(clientId string, clientPassword string, scope string, realm string) (keycloak.Jwttoken, error) { + ret := _m.Called(clientId, clientPassword, scope, realm) + + var r0 keycloak.Jwttoken + var r1 error + if rf, ok := ret.Get(0).(func(string, string, string, string) (keycloak.Jwttoken, error)); ok { + return rf(clientId, clientPassword, scope, realm) + } + if rf, ok := ret.Get(0).(func(string, string, string, string) keycloak.Jwttoken); ok { + r0 = rf(clientId, clientPassword, scope, realm) + } else { + r0 = ret.Get(0).(keycloak.Jwttoken) + } + + if rf, ok := ret.Get(1).(func(string, string, string, string) error); ok { + r1 = rf(clientId, clientPassword, scope, realm) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewAccessManagement interface { + mock.TestingT + Cleanup(func()) +} + +// NewAccessManagement creates a new instance of AccessManagement. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewAccessManagement(t mockConstructorTestingTNewAccessManagement) *AccessManagement { + mock := &AccessManagement{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/capifcore/internal/securityapi/typevalidation.go b/capifcore/internal/securityapi/typevalidation.go index 90dbda3..1241f96 100644 --- a/capifcore/internal/securityapi/typevalidation.go +++ b/capifcore/internal/securityapi/typevalidation.go @@ -35,7 +35,7 @@ func (tokenReq AccessTokenReq) Validate() (bool, AccessTokenErr) { } //3gpp#aefId1:apiName1,apiName2,…apiNameX;aefId2:apiName1,apiName2,…apiNameY;…aefIdN:apiName1,apiName2,…apiNameZ - if tokenReq.Scope != nil { + if tokenReq.Scope != nil && *tokenReq.Scope != "" { scope := strings.Split(*tokenReq.Scope, "#") if len(scope) < 2 { return false, createAccessTokenError(AccessTokenErrErrorInvalidScope, "Malformed scope") diff --git a/capifcore/internal/securityservice/security.go b/capifcore/internal/securityservice/security.go index d71ff0d..dcf1dbb 100644 --- a/capifcore/internal/securityservice/security.go +++ b/capifcore/internal/securityservice/security.go @@ -23,32 +23,31 @@ package security import ( "net/http" "strings" - "time" - "github.com/golang-jwt/jwt" "github.com/labstack/echo/v4" "oransc.org/nonrtric/capifcore/internal/common29122" securityapi "oransc.org/nonrtric/capifcore/internal/securityapi" "oransc.org/nonrtric/capifcore/internal/invokermanagement" + "oransc.org/nonrtric/capifcore/internal/keycloak" "oransc.org/nonrtric/capifcore/internal/providermanagement" "oransc.org/nonrtric/capifcore/internal/publishservice" ) -var jwtKey = "my-secret-key" - type Security struct { serviceRegister providermanagement.ServiceRegister publishRegister publishservice.PublishRegister invokerRegister invokermanagement.InvokerRegister + keycloak keycloak.AccessManagement } -func NewSecurity(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister) *Security { +func NewSecurity(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister, km keycloak.AccessManagement) *Security { return &Security{ serviceRegister: serviceRegister, publishRegister: publishRegister, invokerRegister: invokerRegister, + keycloak: km, } } @@ -68,7 +67,7 @@ func (s *Security) PostSecuritiesSecurityIdToken(ctx echo.Context, securityId st return sendAccessTokenError(ctx, http.StatusBadRequest, securityapi.AccessTokenErrErrorUnauthorizedClient, "Invoker secret not valid") } - if accessTokenReq.Scope != nil { + if accessTokenReq.Scope != nil && *accessTokenReq.Scope != "" { scope := strings.Split(*accessTokenReq.Scope, "#") aefList := strings.Split(scope[1], ";") for _, aef := range aefList { @@ -83,27 +82,14 @@ func (s *Security) PostSecuritiesSecurityIdToken(ctx echo.Context, securityId st } } } - - expirationTime := time.Now().Add(time.Hour).Unix() - - claims := &jwt.MapClaims{ - "iss": accessTokenReq.ClientId, - "exp": expirationTime, - "data": map[string]interface{}{ - "scope": accessTokenReq.Scope, - }, - } - - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - tokenString, err := token.SignedString([]byte(jwtKey)) + jwtToken, err := s.keycloak.GetToken(accessTokenReq.ClientId, *accessTokenReq.ClientSecret, *accessTokenReq.Scope, "invokerrealm") if err != nil { - // If there is an error in creating the JWT return an internal server error - return err + return sendAccessTokenError(ctx, http.StatusBadRequest, securityapi.AccessTokenErrErrorUnauthorizedClient, err.Error()) } accessTokenResp := securityapi.AccessTokenRsp{ - AccessToken: tokenString, - ExpiresIn: common29122.DurationSec(expirationTime), + AccessToken: jwtToken.AccessToken, + ExpiresIn: common29122.DurationSec(jwtToken.ExpiresIn), Scope: accessTokenReq.Scope, TokenType: "Bearer", } diff --git a/capifcore/internal/securityservice/security_test.go b/capifcore/internal/securityservice/security_test.go index 6c62161..13af737 100644 --- a/capifcore/internal/securityservice/security_test.go +++ b/capifcore/internal/securityservice/security_test.go @@ -21,12 +21,14 @@ package security import ( + "errors" "fmt" "net/http" "net/url" "os" "testing" + "oransc.org/nonrtric/capifcore/internal/keycloak" "oransc.org/nonrtric/capifcore/internal/securityapi" "oransc.org/nonrtric/capifcore/internal/invokermanagement" @@ -36,6 +38,7 @@ import ( "github.com/labstack/echo/v4" invokermocks "oransc.org/nonrtric/capifcore/internal/invokermanagement/mocks" + keycloackmocks "oransc.org/nonrtric/capifcore/internal/keycloak/mocks" servicemocks "oransc.org/nonrtric/capifcore/internal/providermanagement/mocks" publishmocks "oransc.org/nonrtric/capifcore/internal/publishservice/mocks" @@ -55,7 +58,15 @@ func TestPostSecurityIdTokenInvokerRegistered(t *testing.T) { publishRegisterMock := publishmocks.PublishRegister{} publishRegisterMock.On("IsAPIPublished", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true) - requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock) + jwt := keycloak.Jwttoken{ + AccessToken: "eyJhbGNIn0.e3YTQ0xLjEifQ.FcqCwCy7iJiOmw", + ExpiresIn: 300, + Scope: "3gpp#aefIdpath", + } + accessMgmMock := keycloackmocks.AccessManagement{} + accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(jwt, nil) + + requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock) data := url.Values{} clientId := "id" @@ -76,19 +87,19 @@ func TestPostSecurityIdTokenInvokerRegistered(t *testing.T) { err := result.UnmarshalBodyToObject(&resultResponse) assert.NoError(t, err, "error unmarshaling response") assert.NotEmpty(t, resultResponse.AccessToken) - assert.Equal(t, "3gpp#"+aefId+":"+path, *resultResponse.Scope) assert.Equal(t, securityapi.AccessTokenRspTokenTypeBearer, resultResponse.TokenType) invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", clientId) invokerRegisterMock.AssertCalled(t, "VerifyInvokerSecret", clientId, clientSecret) serviceRegisterMock.AssertCalled(t, "IsFunctionRegistered", aefId) publishRegisterMock.AssertCalled(t, "IsAPIPublished", aefId, path) + accessMgmMock.AssertCalled(t, "GetToken", clientId, clientSecret, "3gpp#"+aefId+":"+path, "invokerrealm") } func TestPostSecurityIdTokenInvokerNotRegistered(t *testing.T) { invokerRegisterMock := invokermocks.InvokerRegister{} invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(false) - requestHandler := getEcho(nil, nil, &invokerRegisterMock) + requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil) data := url.Values{} data.Set("client_id", "id") @@ -113,7 +124,7 @@ func TestPostSecurityIdTokenInvokerSecretNotValid(t *testing.T) { invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true) invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(false) - requestHandler := getEcho(nil, nil, &invokerRegisterMock) + requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil) data := url.Values{} data.Set("client_id", "id") @@ -140,7 +151,7 @@ func TestPostSecurityIdTokenFunctionNotRegistered(t *testing.T) { serviceRegisterMock := servicemocks.ServiceRegister{} serviceRegisterMock.On("IsFunctionRegistered", mock.AnythingOfType("string")).Return(false) - requestHandler := getEcho(&serviceRegisterMock, nil, &invokerRegisterMock) + requestHandler := getEcho(&serviceRegisterMock, nil, &invokerRegisterMock, nil) data := url.Values{} data.Set("client_id", "id") @@ -169,7 +180,7 @@ func TestPostSecurityIdTokenAPINotPublished(t *testing.T) { publishRegisterMock := publishmocks.PublishRegister{} publishRegisterMock.On("IsAPIPublished", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(false) - requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock) + requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, nil) data := url.Values{} data.Set("client_id", "id") @@ -189,7 +200,47 @@ func TestPostSecurityIdTokenAPINotPublished(t *testing.T) { assert.Equal(t, &errMsg, errDetails.ErrorDescription) } -func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister) *echo.Echo { +func TestPostSecurityIdTokenInvokerInvalidCredentials(t *testing.T) { + invokerRegisterMock := invokermocks.InvokerRegister{} + invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true) + invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true) + serviceRegisterMock := servicemocks.ServiceRegister{} + serviceRegisterMock.On("IsFunctionRegistered", mock.AnythingOfType("string")).Return(true) + publishRegisterMock := publishmocks.PublishRegister{} + publishRegisterMock.On("IsAPIPublished", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true) + + jwt := keycloak.Jwttoken{} + accessMgmMock := keycloackmocks.AccessManagement{} + accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(jwt, errors.New("invalid_credentials")) + + requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock) + + data := url.Values{} + clientId := "id" + clientSecret := "secret" + aefId := "aefId" + path := "path" + data.Set("client_id", clientId) + data.Set("client_secret", clientSecret) + data.Set("grant_type", "client_credentials") + data.Set("scope", "3gpp#"+aefId+":"+path) + + encodedData := data.Encode() + + result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler) + + assert.Equal(t, http.StatusBadRequest, result.Code()) + var resultResponse securityapi.AccessTokenErr + err := result.UnmarshalBodyToObject(&resultResponse) + assert.NoError(t, err, "error unmarshaling response") + invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", clientId) + invokerRegisterMock.AssertCalled(t, "VerifyInvokerSecret", clientId, clientSecret) + serviceRegisterMock.AssertCalled(t, "IsFunctionRegistered", aefId) + publishRegisterMock.AssertCalled(t, "IsAPIPublished", aefId, path) + accessMgmMock.AssertCalled(t, "GetToken", clientId, clientSecret, "3gpp#"+aefId+":"+path, "invokerrealm") +} + +func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister, keycloakMgm keycloak.AccessManagement) *echo.Echo { swagger, err := securityapi.GetSwagger() if err != nil { fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err) @@ -198,7 +249,7 @@ func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister swagger.Servers = nil - s := NewSecurity(serviceRegister, publishRegister, invokerRegister) + s := NewSecurity(serviceRegister, publishRegister, invokerRegister, keycloakMgm) e := echo.New() e.Use(echomiddleware.Logger()) diff --git a/capifcore/main.go b/capifcore/main.go index 3ee4192..f8b9daf 100644 --- a/capifcore/main.go +++ b/capifcore/main.go @@ -32,12 +32,14 @@ import ( "oransc.org/nonrtric/capifcore/internal/discoverserviceapi" "oransc.org/nonrtric/capifcore/internal/eventsapi" "oransc.org/nonrtric/capifcore/internal/invokermanagementapi" + "oransc.org/nonrtric/capifcore/internal/keycloak" "oransc.org/nonrtric/capifcore/internal/providermanagementapi" "oransc.org/nonrtric/capifcore/internal/securityapi" "github.com/deepmap/oapi-codegen/pkg/middleware" echomiddleware "github.com/labstack/echo/v4/middleware" log "github.com/sirupsen/logrus" + config "oransc.org/nonrtric/capifcore/internal/config" "oransc.org/nonrtric/capifcore/internal/discoverservice" "oransc.org/nonrtric/capifcore/internal/eventservice" "oransc.org/nonrtric/capifcore/internal/helmmanagement" @@ -87,6 +89,13 @@ func getEcho() *echo.Echo { // Log all requests e.Use(echomiddleware.Logger()) + // Read configuration file + cfg, err := config.ReadKeycloakConfigFile("configs") + if err != nil { + log.Fatalf("Error loading configuration file\n: %s", err) + } + km := keycloak.NewKeycloakManager(cfg) + var group *echo.Group // Register ProviderManagement providerManagerSwagger, err := providermanagementapi.GetSwagger() @@ -150,7 +159,7 @@ func getEcho() *echo.Echo { log.Fatalf("Error loading Security swagger spec\n: %s", err) } securitySwagger.Servers = nil - securityService := security.NewSecurity(providerManager, publishService, invokerManager) + securityService := security.NewSecurity(providerManager, publishService, invokerManager, km) group = e.Group("/capif-security/v1") group.Use(middleware.OapiRequestValidator(securitySwagger)) securityapi.RegisterHandlersWithBaseURL(e, securityService, "/capif-security/v1") -- 2.16.6 From c865c910a6a04fc202c8eb8b6403544c44784d5f Mon Sep 17 00:00:00 2001 From: ychacon Date: Wed, 1 Mar 2023 19:00:59 +0100 Subject: [PATCH 10/16] Implementation for PUT trustedInvokers endpoint Issue-ID: NONRTRIC-848 Signed-off-by: ychacon Change-Id: Iba953bfb8aa11d77c8aeac906d640fb470769bbc --- capifcore/internal/publishserviceapi/typeaccess.go | 11 ++ capifcore/internal/securityapi/typeupdate.go | 84 +++++++++ capifcore/internal/securityapi/typeupdate_test.go | 106 +++++++++++ capifcore/internal/securityapi/typevalidation.go | 29 +++ .../internal/securityapi/typevalidation_test.go | 48 +++++ capifcore/internal/securityservice/security.go | 69 ++++++- .../internal/securityservice/security_test.go | 207 +++++++++++++++++++++ 7 files changed, 553 insertions(+), 1 deletion(-) create mode 100644 capifcore/internal/securityapi/typeupdate.go create mode 100644 capifcore/internal/securityapi/typeupdate_test.go diff --git a/capifcore/internal/publishserviceapi/typeaccess.go b/capifcore/internal/publishserviceapi/typeaccess.go index 32c1a7a..c8a7afc 100644 --- a/capifcore/internal/publishserviceapi/typeaccess.go +++ b/capifcore/internal/publishserviceapi/typeaccess.go @@ -28,3 +28,14 @@ func (sd ServiceAPIDescription) GetAefIds() []string { } return allIds } + +func (sd ServiceAPIDescription) GetAefProfileById(id *string) *AefProfile { + if sd.AefProfiles != nil { + for _, aefProfile := range *sd.AefProfiles { + if aefProfile.AefId == *id { + return &aefProfile + } + } + } + return nil +} diff --git a/capifcore/internal/securityapi/typeupdate.go b/capifcore/internal/securityapi/typeupdate.go new file mode 100644 index 0000000..364c123 --- /dev/null +++ b/capifcore/internal/securityapi/typeupdate.go @@ -0,0 +1,84 @@ +// - +// +// ========================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 securityapi + +import ( + "fmt" + "strings" + + "oransc.org/nonrtric/capifcore/internal/publishserviceapi" +) + +var securityMethods []publishserviceapi.SecurityMethod + +func (newContext *ServiceSecurity) PrepareNewSecurityContext(services []publishserviceapi.ServiceAPIDescription) error { + securityMethods = []publishserviceapi.SecurityMethod{} + for i, securityInfo := range newContext.SecurityInfo { + + if securityInfo.InterfaceDetails != nil { + addSecurityMethodsFromInterfaceDetails(securityInfo.InterfaceDetails.SecurityMethods, &securityInfo.PrefSecurityMethods) + + } else { + checkNil := securityInfo.ApiId != nil && securityInfo.AefId != nil + if checkNil { + service := getServiceByApiId(&services, securityInfo.ApiId) + afpProfile := service.GetAefProfileById(securityInfo.AefId) + + addSecurityMethodsFromAefProfile(afpProfile) + } + } + + if isSecuryMethodsEmpty() { + return fmt.Errorf("not found compatible security method") + } + newContext.SecurityInfo[i].SelSecurityMethod = &securityMethods[0] + } + return nil +} + +func isSecuryMethodsEmpty() bool { + return len(securityMethods) <= 0 +} + +func addSecurityMethodsFromInterfaceDetails(methodsFromInterface *[]publishserviceapi.SecurityMethod, prefMethods *[]publishserviceapi.SecurityMethod) { + + if methodsFromInterface != nil { + securityMethods = append(securityMethods, *methodsFromInterface...) + } + if prefMethods != nil { + securityMethods = append(securityMethods, *prefMethods...) + } +} + +func addSecurityMethodsFromAefProfile(afpProfile *publishserviceapi.AefProfile) { + if afpProfile.SecurityMethods != nil { + securityMethods = append(securityMethods, *afpProfile.SecurityMethods...) + } +} + +func getServiceByApiId(services *[]publishserviceapi.ServiceAPIDescription, apiId *string) *publishserviceapi.ServiceAPIDescription { + + for _, service := range *services { + if apiId != nil && strings.Compare(*service.ApiId, *apiId) == 0 { + return &service + } + } + return nil +} diff --git a/capifcore/internal/securityapi/typeupdate_test.go b/capifcore/internal/securityapi/typeupdate_test.go new file mode 100644 index 0000000..1ad530d --- /dev/null +++ b/capifcore/internal/securityapi/typeupdate_test.go @@ -0,0 +1,106 @@ +// - +// ========================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 securityapi + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "oransc.org/nonrtric/capifcore/internal/common29122" + publishapi "oransc.org/nonrtric/capifcore/internal/publishserviceapi" +) + +func TestPrepareNewSecurityContext(t *testing.T) { + apiId := "app-management" + aefId := "aefId" + description := "Description" + services := []publishapi.ServiceAPIDescription{ + { + AefProfiles: &[]publishapi.AefProfile{ + { + AefId: aefId, + Versions: []publishapi.Version{ + { + Resources: &[]publishapi.Resource{ + { + CommType: "REQUEST_RESPONSE", + }, + }, + }, + }, + SecurityMethods: &[]publishapi.SecurityMethod{ + publishapi.SecurityMethodPKI, + }, + }, + }, + ApiId: &apiId, + Description: &description, + }, + } + + servSecurityUnderTest := ServiceSecurity{ + NotificationDestination: common29122.Uri("http://golang.cafe/"), + SecurityInfo: []SecurityInformation{ + { + PrefSecurityMethods: []publishapi.SecurityMethod{ + publishapi.SecurityMethodOAUTH, + }, + }, + }, + } + + err := servSecurityUnderTest.PrepareNewSecurityContext(services) + + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "not found ") + assert.Contains(t, err.Error(), "security method") + + servSecurityUnderTest.SecurityInfo = []SecurityInformation{ + { + ApiId: &apiId, + AefId: &aefId, + PrefSecurityMethods: []publishapi.SecurityMethod{ + publishapi.SecurityMethodOAUTH, + }, + }, + } + + servSecurityUnderTest.PrepareNewSecurityContext(services) + assert.Equal(t, publishapi.SecurityMethodPKI, *servSecurityUnderTest.SecurityInfo[0].SelSecurityMethod) + + servSecurityUnderTest.SecurityInfo = []SecurityInformation{ + { + ApiId: &apiId, + PrefSecurityMethods: []publishapi.SecurityMethod{ + publishapi.SecurityMethodOAUTH, + }, + InterfaceDetails: &publishapi.InterfaceDescription{ + SecurityMethods: &[]publishapi.SecurityMethod{ + publishapi.SecurityMethodPSK, + }, + }, + }, + } + + servSecurityUnderTest.PrepareNewSecurityContext(services) + assert.Equal(t, publishapi.SecurityMethodPSK, *servSecurityUnderTest.SecurityInfo[0].SelSecurityMethod) + +} diff --git a/capifcore/internal/securityapi/typevalidation.go b/capifcore/internal/securityapi/typevalidation.go index 1241f96..4be8aee 100644 --- a/capifcore/internal/securityapi/typevalidation.go +++ b/capifcore/internal/securityapi/typevalidation.go @@ -21,6 +21,9 @@ package securityapi import ( + "errors" + "fmt" + "net/url" "strings" ) @@ -54,6 +57,32 @@ func (tokenReq AccessTokenReq) Validate() (bool, AccessTokenErr) { return true, AccessTokenErr{} } +func (ss ServiceSecurity) Validate() error { + + if len(strings.TrimSpace(string(ss.NotificationDestination))) == 0 { + return errors.New("ServiceSecurity missing required notificationDestination") + } + + if _, err := url.ParseRequestURI(string(ss.NotificationDestination)); err != nil { + return fmt.Errorf("ServiceSecurity has invalid notificationDestination, err=%s", err) + } + + if len(ss.SecurityInfo) == 0 { + return errors.New("ServiceSecurity missing required SecurityInfo") + } + for _, securityInfo := range ss.SecurityInfo { + securityInfo.Validate() + } + return nil +} + +func (si SecurityInformation) Validate() error { + if len(si.PrefSecurityMethods) == 0 { + return errors.New("SecurityInformation missing required PrefSecurityMethods") + } + return nil +} + func createAccessTokenError(err AccessTokenErrError, message string) AccessTokenErr { return AccessTokenErr{ Error: err, diff --git a/capifcore/internal/securityapi/typevalidation_test.go b/capifcore/internal/securityapi/typevalidation_test.go index 0515d06..f44de2f 100644 --- a/capifcore/internal/securityapi/typevalidation_test.go +++ b/capifcore/internal/securityapi/typevalidation_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "oransc.org/nonrtric/capifcore/internal/publishserviceapi" ) func TestValidateClientIdNotPresent(t *testing.T) { @@ -95,3 +96,50 @@ func TestValidateScopeMalformed(t *testing.T) { valid, err = accessTokenUnderTest.Validate() assert.Equal(t, true, valid) } + +func TestValidateServiceSecurity(t *testing.T) { + serviceSecurityUnderTest := ServiceSecurity{} + + err := serviceSecurityUnderTest.Validate() + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "missing") + assert.Contains(t, err.Error(), "notificationDestination") + + serviceSecurityUnderTest.NotificationDestination = "invalid dest" + err = serviceSecurityUnderTest.Validate() + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "invalid") + assert.Contains(t, err.Error(), "notificationDestination") + } + + serviceSecurityUnderTest.NotificationDestination = "http://golang.cafe/" + err = serviceSecurityUnderTest.Validate() + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "missing") + assert.Contains(t, err.Error(), "SecurityInfo") + + serviceSecurityUnderTest.SecurityInfo = []SecurityInformation{ + { + PrefSecurityMethods: []publishserviceapi.SecurityMethod{ + publishserviceapi.SecurityMethodOAUTH, + }, + }, + } + err = serviceSecurityUnderTest.Validate() + assert.Nil(t, err) +} + +func TestValidatePrefSecurityMethodsNotPresent(t *testing.T) { + securityInfoUnderTest := SecurityInformation{} + err := securityInfoUnderTest.Validate() + + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "missing") + assert.Contains(t, err.Error(), "PrefSecurityMethods") + + securityInfoUnderTest.PrefSecurityMethods = []publishserviceapi.SecurityMethod{ + publishserviceapi.SecurityMethodOAUTH, + } + err = securityInfoUnderTest.Validate() + assert.Nil(t, err) +} diff --git a/capifcore/internal/securityservice/security.go b/capifcore/internal/securityservice/security.go index dcf1dbb..52d28ce 100644 --- a/capifcore/internal/securityservice/security.go +++ b/capifcore/internal/securityservice/security.go @@ -21,8 +21,11 @@ package security import ( + "fmt" "net/http" + "path" "strings" + "sync" "github.com/labstack/echo/v4" @@ -40,6 +43,8 @@ type Security struct { publishRegister publishservice.PublishRegister invokerRegister invokermanagement.InvokerRegister keycloak keycloak.AccessManagement + trustedInvokers map[string]securityapi.ServiceSecurity + lock sync.Mutex } func NewSecurity(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister, km keycloak.AccessManagement) *Security { @@ -48,6 +53,7 @@ func NewSecurity(serviceRegister providermanagement.ServiceRegister, publishRegi publishRegister: publishRegister, invokerRegister: invokerRegister, keycloak: km, + trustedInvokers: make(map[string]securityapi.ServiceSecurity), } } @@ -112,7 +118,57 @@ func (s *Security) GetTrustedInvokersApiInvokerId(ctx echo.Context, apiInvokerId } func (s *Security) PutTrustedInvokersApiInvokerId(ctx echo.Context, apiInvokerId string) error { - return ctx.NoContent(http.StatusNotImplemented) + errMsg := "Unable to update security context due to %s." + + if !s.invokerRegister.IsInvokerRegistered(apiInvokerId) { + return sendCoreError(ctx, http.StatusBadRequest, "Unable to update security context due to Invoker not registered") + } + serviceSecurity, err := getServiceSecurityFromRequest(ctx) + if err != nil { + return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err)) + } + + if err := serviceSecurity.Validate(); err != nil { + return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err)) + } + + err = s.prepareNewSecurityContext(&serviceSecurity, apiInvokerId) + 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)) + + err = ctx.JSON(http.StatusCreated, s.trustedInvokers[apiInvokerId]) + if err != nil { + // Something really bad happened, tell Echo that our handler failed + return err + } + + return nil +} + +func getServiceSecurityFromRequest(ctx echo.Context) (securityapi.ServiceSecurity, error) { + var serviceSecurity securityapi.ServiceSecurity + err := ctx.Bind(&serviceSecurity) + if err != nil { + return securityapi.ServiceSecurity{}, fmt.Errorf("invalid format for service security") + } + return serviceSecurity, nil +} + +func (s *Security) prepareNewSecurityContext(newContext *securityapi.ServiceSecurity, apiInvokerId string) error { + s.lock.Lock() + defer s.lock.Unlock() + + err := newContext.PrepareNewSecurityContext(s.publishRegister.GetAllPublishedServices()) + if err != nil { + return err + } + + s.trustedInvokers[apiInvokerId] = *newContext + return nil } func (s *Security) PostTrustedInvokersApiInvokerIdDelete(ctx echo.Context, apiInvokerId string) error { @@ -130,3 +186,14 @@ func sendAccessTokenError(ctx echo.Context, code int, err securityapi.AccessToke } return ctx.JSON(code, accessTokenErr) } + +// This function wraps sending of an error in the Error format, and +// handling the failure to marshal that. +func sendCoreError(ctx echo.Context, code int, message string) error { + pd := common29122.ProblemDetails{ + Cause: &message, + Status: &code, + } + err := ctx.JSON(code, pd) + return err +} diff --git a/capifcore/internal/securityservice/security_test.go b/capifcore/internal/securityservice/security_test.go index 13af737..d31ff6e 100644 --- a/capifcore/internal/securityservice/security_test.go +++ b/capifcore/internal/securityservice/security_test.go @@ -28,7 +28,9 @@ import ( "os" "testing" + "oransc.org/nonrtric/capifcore/internal/common29122" "oransc.org/nonrtric/capifcore/internal/keycloak" + "oransc.org/nonrtric/capifcore/internal/publishserviceapi" "oransc.org/nonrtric/capifcore/internal/securityapi" "oransc.org/nonrtric/capifcore/internal/invokermanagement" @@ -240,6 +242,181 @@ func TestPostSecurityIdTokenInvokerInvalidCredentials(t *testing.T) { accessMgmMock.AssertCalled(t, "GetToken", clientId, clientSecret, "3gpp#"+aefId+":"+path, "invokerrealm") } +func TestPutTrustedInvokerSuccessfully(t *testing.T) { + invokerRegisterMock := invokermocks.InvokerRegister{} + invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true) + aefId := "aefId" + aefProfile := getAefProfile(aefId) + aefProfile.SecurityMethods = &[]publishserviceapi.SecurityMethod{ + publishserviceapi.SecurityMethodPKI, + } + aefProfiles := []publishserviceapi.AefProfile{ + aefProfile, + } + apiId := "apiId" + publishedServices := []publishserviceapi.ServiceAPIDescription{ + { + ApiId: &apiId, + AefProfiles: &aefProfiles, + }, + } + publishRegisterMock := publishmocks.PublishRegister{} + publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices) + + requestHandler := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) + + invokerId := "invokerId" + serviceSecurityUnderTest := getServiceSecurity(aefId, apiId) + serviceSecurityUnderTest.SecurityInfo[0].ApiId = &apiId + + result := testutil.NewRequest().Put("/trustedInvokers/"+invokerId).WithJsonBody(serviceSecurityUnderTest).Go(t, requestHandler) + + assert.Equal(t, http.StatusCreated, result.Code()) + var resultResponse securityapi.ServiceSecurity + err := result.UnmarshalBodyToObject(&resultResponse) + assert.NoError(t, err, "error unmarshaling response") + assert.NotEmpty(t, resultResponse.NotificationDestination) + + for _, security := range resultResponse.SecurityInfo { + assert.Equal(t, *security.ApiId, apiId) + assert.Equal(t, *security.SelSecurityMethod, publishserviceapi.SecurityMethodPKI) + } + invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId) + +} + +func TestPutTrustedInkoverNotRegistered(t *testing.T) { + invokerRegisterMock := invokermocks.InvokerRegister{} + invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(false) + + requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil) + + invokerId := "invokerId" + serviceSecurityUnderTest := getServiceSecurity("aefId", "apiId") + + result := testutil.NewRequest().Put("/trustedInvokers/"+invokerId).WithJsonBody(serviceSecurityUnderTest).Go(t, requestHandler) + + badRequest := http.StatusBadRequest + assert.Equal(t, badRequest, result.Code()) + var problemDetails common29122.ProblemDetails + err := result.UnmarshalBodyToObject(&problemDetails) + assert.NoError(t, err, "error unmarshaling response") + assert.Equal(t, &badRequest, problemDetails.Status) + assert.Contains(t, *problemDetails.Cause, "Invoker not registered") + invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId) +} + +func TestPutTrustedInkoverInvalidInputServiceSecurity(t *testing.T) { + invokerRegisterMock := invokermocks.InvokerRegister{} + invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true) + + requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil) + + invokerId := "invokerId" + notificationUrl := "url" + serviceSecurityUnderTest := getServiceSecurity("aefId", "apiId") + serviceSecurityUnderTest.NotificationDestination = common29122.Uri(notificationUrl) + + result := testutil.NewRequest().Put("/trustedInvokers/"+invokerId).WithJsonBody(serviceSecurityUnderTest).Go(t, requestHandler) + + badRequest := http.StatusBadRequest + assert.Equal(t, badRequest, result.Code()) + var problemDetails common29122.ProblemDetails + err := result.UnmarshalBodyToObject(&problemDetails) + assert.NoError(t, err, "error unmarshaling response") + assert.Equal(t, &badRequest, problemDetails.Status) + assert.Contains(t, *problemDetails.Cause, "ServiceSecurity has invalid notificationDestination") + invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId) +} + +func TestPutTrustedInvokerInterfaceDetailsNotNil(t *testing.T) { + invokerRegisterMock := invokermocks.InvokerRegister{} + invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true) + aefId := "aefId" + aefProfile := getAefProfile(aefId) + aefProfile.SecurityMethods = &[]publishserviceapi.SecurityMethod{ + publishserviceapi.SecurityMethodPKI, + } + aefProfiles := []publishserviceapi.AefProfile{ + aefProfile, + } + apiId := "apiId" + publishedServices := []publishserviceapi.ServiceAPIDescription{ + { + ApiId: &apiId, + AefProfiles: &aefProfiles, + }, + } + publishRegisterMock := publishmocks.PublishRegister{} + publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices) + + requestHandler := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) + + invokerId := "invokerId" + serviceSecurityUnderTest := getServiceSecurity(aefId, apiId) + serviceSecurityUnderTest.SecurityInfo[0] = securityapi.SecurityInformation{ + ApiId: &apiId, + PrefSecurityMethods: []publishserviceapi.SecurityMethod{ + publishserviceapi.SecurityMethodOAUTH, + }, + InterfaceDetails: &publishserviceapi.InterfaceDescription{ + SecurityMethods: &[]publishserviceapi.SecurityMethod{ + publishserviceapi.SecurityMethodPSK, + }, + }, + } + + result := testutil.NewRequest().Put("/trustedInvokers/"+invokerId).WithJsonBody(serviceSecurityUnderTest).Go(t, requestHandler) + + assert.Equal(t, http.StatusCreated, result.Code()) + var resultResponse securityapi.ServiceSecurity + err := result.UnmarshalBodyToObject(&resultResponse) + assert.NoError(t, err, "error unmarshaling response") + assert.NotEmpty(t, resultResponse.NotificationDestination) + + for _, security := range resultResponse.SecurityInfo { + assert.Equal(t, apiId, *security.ApiId) + assert.Equal(t, publishserviceapi.SecurityMethodPSK, *security.SelSecurityMethod) + } + invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId) + +} + +func TestPutTrustedInvokerNotFoundSecurityMethod(t *testing.T) { + invokerRegisterMock := invokermocks.InvokerRegister{} + invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true) + + aefProfiles := []publishserviceapi.AefProfile{ + getAefProfile("aefId"), + } + apiId := "apiId" + publishedServices := []publishserviceapi.ServiceAPIDescription{ + { + ApiId: &apiId, + AefProfiles: &aefProfiles, + }, + } + publishRegisterMock := publishmocks.PublishRegister{} + publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices) + + requestHandler := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) + + invokerId := "invokerId" + serviceSecurityUnderTest := getServiceSecurity("aefId", "apiId") + + result := testutil.NewRequest().Put("/trustedInvokers/"+invokerId).WithJsonBody(serviceSecurityUnderTest).Go(t, requestHandler) + + badRequest := http.StatusBadRequest + assert.Equal(t, badRequest, result.Code()) + var problemDetails common29122.ProblemDetails + err := result.UnmarshalBodyToObject(&problemDetails) + assert.NoError(t, err, "error unmarshaling response") + assert.Equal(t, &badRequest, problemDetails.Status) + assert.Contains(t, *problemDetails.Cause, "not found") + assert.Contains(t, *problemDetails.Cause, "security method") + invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId) +} + func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister, keycloakMgm keycloak.AccessManagement) *echo.Echo { swagger, err := securityapi.GetSwagger() if err != nil { @@ -258,3 +435,33 @@ func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister securityapi.RegisterHandlers(e, s) return e } + +func getServiceSecurity(aefId string, apiId string) securityapi.ServiceSecurity { + return securityapi.ServiceSecurity{ + NotificationDestination: common29122.Uri("http://golang.cafe/"), + SecurityInfo: []securityapi.SecurityInformation{ + { + AefId: &aefId, + ApiId: &apiId, + PrefSecurityMethods: []publishserviceapi.SecurityMethod{ + publishserviceapi.SecurityMethodOAUTH, + }, + }, + }, + } +} + +func getAefProfile(aefId string) publishserviceapi.AefProfile { + return publishserviceapi.AefProfile{ + AefId: aefId, + Versions: []publishserviceapi.Version{ + { + Resources: &[]publishserviceapi.Resource{ + { + CommType: "REQUEST_RESPONSE", + }, + }, + }, + }, + } +} -- 2.16.6 From 856821490df816d7343d90f76f729240bd4e00d6 Mon Sep 17 00:00:00 2001 From: ychacon Date: Tue, 7 Mar 2023 21:59:08 +0100 Subject: [PATCH 11/16] Implementation for GET/DELETE trustedInvokers endpoint Issue-ID: NONRTRIC-848 Signed-off-by: ychacon Change-Id: I7c3c8c484afa2b56cfabe7d82d47255f91fd726a --- capifcore/internal/securityservice/security.go | 58 ++++++++++++- .../internal/securityservice/security_test.go | 97 +++++++++++++++++++--- capifcore/main_test.go | 2 +- 3 files changed, 140 insertions(+), 17 deletions(-) diff --git a/capifcore/internal/securityservice/security.go b/capifcore/internal/securityservice/security.go index 52d28ce..d3d9026 100644 --- a/capifcore/internal/securityservice/security.go +++ b/capifcore/internal/securityservice/security.go @@ -28,7 +28,7 @@ import ( "sync" "github.com/labstack/echo/v4" - + copystructure "github.com/mitchellh/copystructure" "oransc.org/nonrtric/capifcore/internal/common29122" securityapi "oransc.org/nonrtric/capifcore/internal/securityapi" @@ -110,11 +110,63 @@ func (s *Security) PostSecuritiesSecurityIdToken(ctx echo.Context, securityId st } func (s *Security) DeleteTrustedInvokersApiInvokerId(ctx echo.Context, apiInvokerId string) error { - return ctx.NoContent(http.StatusNotImplemented) + if _, ok := s.trustedInvokers[apiInvokerId]; ok { + s.deleteTrustedInvoker(apiInvokerId) + } + + return ctx.NoContent(http.StatusNoContent) +} + +func (s *Security) deleteTrustedInvoker(apiInvokerId string) { + s.lock.Lock() + defer s.lock.Unlock() + delete(s.trustedInvokers, apiInvokerId) } func (s *Security) GetTrustedInvokersApiInvokerId(ctx echo.Context, apiInvokerId string, params securityapi.GetTrustedInvokersApiInvokerIdParams) error { - return ctx.NoContent(http.StatusNotImplemented) + + if trustedInvoker, ok := s.trustedInvokers[apiInvokerId]; ok { + updatedInvoker := s.checkParams(trustedInvoker, params) + if updatedInvoker != nil { + err := ctx.JSON(http.StatusOK, updatedInvoker) + if err != nil { + return err + } + } + } else { + return sendCoreError(ctx, http.StatusNotFound, fmt.Sprintf("invoker %s not registered as trusted invoker", apiInvokerId)) + } + + return nil +} + +func (s *Security) checkParams(trustedInvoker securityapi.ServiceSecurity, params securityapi.GetTrustedInvokersApiInvokerIdParams) *securityapi.ServiceSecurity { + emptyString := "" + + var sendAuthenticationInfo = (params.AuthenticationInfo != nil) && *params.AuthenticationInfo + var sendAuthorizationInfo = (params.AuthorizationInfo != nil) && *params.AuthorizationInfo + + if sendAuthenticationInfo && sendAuthorizationInfo { + return &trustedInvoker + } + + data, _ := copystructure.Copy(trustedInvoker) + updatedInvoker, ok := data.(securityapi.ServiceSecurity) + if !ok { + return nil + } + + if !sendAuthenticationInfo { + for i := range updatedInvoker.SecurityInfo { + updatedInvoker.SecurityInfo[i].AuthenticationInfo = &emptyString + } + } + if !sendAuthorizationInfo { + for i := range updatedInvoker.SecurityInfo { + updatedInvoker.SecurityInfo[i].AuthorizationInfo = &emptyString + } + } + return &updatedInvoker } func (s *Security) PutTrustedInvokersApiInvokerId(ctx echo.Context, apiInvokerId string) error { diff --git a/capifcore/internal/securityservice/security_test.go b/capifcore/internal/securityservice/security_test.go index d31ff6e..1abb8ae 100644 --- a/capifcore/internal/securityservice/security_test.go +++ b/capifcore/internal/securityservice/security_test.go @@ -68,7 +68,7 @@ func TestPostSecurityIdTokenInvokerRegistered(t *testing.T) { accessMgmMock := keycloackmocks.AccessManagement{} accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(jwt, nil) - requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock) + requestHandler, _ := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock) data := url.Values{} clientId := "id" @@ -101,7 +101,7 @@ func TestPostSecurityIdTokenInvokerNotRegistered(t *testing.T) { invokerRegisterMock := invokermocks.InvokerRegister{} invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(false) - requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil) + requestHandler, _ := getEcho(nil, nil, &invokerRegisterMock, nil) data := url.Values{} data.Set("client_id", "id") @@ -126,7 +126,7 @@ func TestPostSecurityIdTokenInvokerSecretNotValid(t *testing.T) { invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true) invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(false) - requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil) + requestHandler, _ := getEcho(nil, nil, &invokerRegisterMock, nil) data := url.Values{} data.Set("client_id", "id") @@ -153,7 +153,7 @@ func TestPostSecurityIdTokenFunctionNotRegistered(t *testing.T) { serviceRegisterMock := servicemocks.ServiceRegister{} serviceRegisterMock.On("IsFunctionRegistered", mock.AnythingOfType("string")).Return(false) - requestHandler := getEcho(&serviceRegisterMock, nil, &invokerRegisterMock, nil) + requestHandler, _ := getEcho(&serviceRegisterMock, nil, &invokerRegisterMock, nil) data := url.Values{} data.Set("client_id", "id") @@ -182,7 +182,7 @@ func TestPostSecurityIdTokenAPINotPublished(t *testing.T) { publishRegisterMock := publishmocks.PublishRegister{} publishRegisterMock.On("IsAPIPublished", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(false) - requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, nil) + requestHandler, _ := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, nil) data := url.Values{} data.Set("client_id", "id") @@ -215,7 +215,7 @@ func TestPostSecurityIdTokenInvokerInvalidCredentials(t *testing.T) { accessMgmMock := keycloackmocks.AccessManagement{} accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(jwt, errors.New("invalid_credentials")) - requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock) + requestHandler, _ := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock) data := url.Values{} clientId := "id" @@ -263,7 +263,7 @@ func TestPutTrustedInvokerSuccessfully(t *testing.T) { publishRegisterMock := publishmocks.PublishRegister{} publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices) - requestHandler := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) + requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) invokerId := "invokerId" serviceSecurityUnderTest := getServiceSecurity(aefId, apiId) @@ -289,7 +289,7 @@ func TestPutTrustedInkoverNotRegistered(t *testing.T) { invokerRegisterMock := invokermocks.InvokerRegister{} invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(false) - requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil) + requestHandler, _ := getEcho(nil, nil, &invokerRegisterMock, nil) invokerId := "invokerId" serviceSecurityUnderTest := getServiceSecurity("aefId", "apiId") @@ -310,7 +310,7 @@ func TestPutTrustedInkoverInvalidInputServiceSecurity(t *testing.T) { invokerRegisterMock := invokermocks.InvokerRegister{} invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true) - requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil) + requestHandler, _ := getEcho(nil, nil, &invokerRegisterMock, nil) invokerId := "invokerId" notificationUrl := "url" @@ -350,7 +350,7 @@ func TestPutTrustedInvokerInterfaceDetailsNotNil(t *testing.T) { publishRegisterMock := publishmocks.PublishRegister{} publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices) - requestHandler := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) + requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) invokerId := "invokerId" serviceSecurityUnderTest := getServiceSecurity(aefId, apiId) @@ -399,7 +399,7 @@ func TestPutTrustedInvokerNotFoundSecurityMethod(t *testing.T) { publishRegisterMock := publishmocks.PublishRegister{} publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices) - requestHandler := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) + requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) invokerId := "invokerId" serviceSecurityUnderTest := getServiceSecurity("aefId", "apiId") @@ -417,7 +417,78 @@ func TestPutTrustedInvokerNotFoundSecurityMethod(t *testing.T) { invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId) } -func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister, keycloakMgm keycloak.AccessManagement) *echo.Echo { +func TestDeleteSecurityContext(t *testing.T) { + + requestHandler, securityUnderTest := getEcho(nil, nil, nil, nil) + + aefId := "aefId" + apiId := "apiId" + serviceSecurityUnderTest := getServiceSecurity(aefId, apiId) + serviceSecurityUnderTest.SecurityInfo[0].ApiId = &apiId + + invokerId := "invokerId" + securityUnderTest.trustedInvokers[invokerId] = serviceSecurityUnderTest + + // Delete the security context + result := testutil.NewRequest().Delete("/trustedInvokers/"+invokerId).Go(t, requestHandler) + + assert.Equal(t, http.StatusNoContent, result.Code()) + _, ok := securityUnderTest.trustedInvokers[invokerId] + assert.False(t, ok) +} + +func TestGetSecurityContextByInvokerId(t *testing.T) { + + requestHandler, securityUnderTest := getEcho(nil, nil, nil, nil) + + aefId := "aefId" + apiId := "apiId" + authenticationInfo := "authenticationInfo" + authorizationInfo := "authorizationInfo" + serviceSecurityUnderTest := getServiceSecurity(aefId, apiId) + serviceSecurityUnderTest.SecurityInfo[0].AuthenticationInfo = &authenticationInfo + serviceSecurityUnderTest.SecurityInfo[0].AuthorizationInfo = &authorizationInfo + + invokerId := "invokerId" + securityUnderTest.trustedInvokers[invokerId] = serviceSecurityUnderTest + + // Get security context + result := testutil.NewRequest().Get("/trustedInvokers/"+invokerId).Go(t, requestHandler) + + assert.Equal(t, http.StatusOK, result.Code()) + var resultService securityapi.ServiceSecurity + err := result.UnmarshalBodyToObject(&resultService) + assert.NoError(t, err, "error unmarshaling response") + + for _, secInfo := range resultService.SecurityInfo { + assert.Equal(t, apiId, *secInfo.ApiId) + assert.Equal(t, aefId, *secInfo.AefId) + assert.Equal(t, "", *secInfo.AuthenticationInfo) + assert.Equal(t, "", *secInfo.AuthorizationInfo) + } + + result = testutil.NewRequest().Get("/trustedInvokers/"+invokerId+"?authenticationInfo=true&authorizationInfo=false").Go(t, requestHandler) + assert.Equal(t, http.StatusOK, result.Code()) + err = result.UnmarshalBodyToObject(&resultService) + assert.NoError(t, err, "error unmarshaling response") + + for _, secInfo := range resultService.SecurityInfo { + assert.Equal(t, authenticationInfo, *secInfo.AuthenticationInfo) + assert.Equal(t, "", *secInfo.AuthorizationInfo) + } + + result = testutil.NewRequest().Get("/trustedInvokers/"+invokerId+"?authenticationInfo=true&authorizationInfo=true").Go(t, requestHandler) + assert.Equal(t, http.StatusOK, result.Code()) + err = result.UnmarshalBodyToObject(&resultService) + assert.NoError(t, err, "error unmarshaling response") + + for _, secInfo := range resultService.SecurityInfo { + assert.Equal(t, authenticationInfo, *secInfo.AuthenticationInfo) + assert.Equal(t, authorizationInfo, *secInfo.AuthorizationInfo) + } +} + +func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister, keycloakMgm keycloak.AccessManagement) (*echo.Echo, *Security) { swagger, err := securityapi.GetSwagger() if err != nil { fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err) @@ -433,7 +504,7 @@ func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister e.Use(middleware.OapiRequestValidator(swagger)) securityapi.RegisterHandlers(e, s) - return e + return e, s } func getServiceSecurity(aefId string, apiId string) securityapi.ServiceSecurity { diff --git a/capifcore/main_test.go b/capifcore/main_test.go index 7d77b92..571d475 100644 --- a/capifcore/main_test.go +++ b/capifcore/main_test.go @@ -101,7 +101,7 @@ func Test_routing(t *testing.T) { name: "Security path", args: args{ url: "/capif-security/v1/trustedInvokers/apiInvokerId", - returnStatus: http.StatusNotImplemented, + returnStatus: http.StatusNotFound, method: "GET", }, }, -- 2.16.6 From b2a87e363829f6447b4f06c1aa4524608bbeb422 Mon Sep 17 00:00:00 2001 From: ychacon Date: Fri, 10 Mar 2023 17:22:27 +0100 Subject: [PATCH 12/16] Implementation for Update and revoke trustedInvokers endpoint Issue-ID: NONRTRIC-848 Signed-off-by: ychacon Change-Id: Iee3c7fbeb8032bda02f65a0ef513ce65d8359b9a --- capifcore/internal/securityapi/typevalidation.go | 21 +++++ capifcore/internal/securityservice/security.go | 80 +++++++++++++++- .../internal/securityservice/security_test.go | 102 +++++++++++++++++++++ 3 files changed, 201 insertions(+), 2 deletions(-) diff --git a/capifcore/internal/securityapi/typevalidation.go b/capifcore/internal/securityapi/typevalidation.go index 4be8aee..4a9ee28 100644 --- a/capifcore/internal/securityapi/typevalidation.go +++ b/capifcore/internal/securityapi/typevalidation.go @@ -83,6 +83,27 @@ func (si SecurityInformation) Validate() error { return nil } +func (sn SecurityNotification) Validate() error { + + if len(strings.TrimSpace(string(sn.ApiInvokerId))) == 0 { + return errors.New("SecurityNotification missing required ApiInvokerId") + } + + if len(sn.ApiIds) < 1 { + return errors.New("SecurityNotification missing required ApiIds") + } + + if len(strings.TrimSpace(string(sn.Cause))) == 0 { + return errors.New("SecurityNotification missing required Cause") + } + + if sn.Cause != CauseOVERLIMITUSAGE && sn.Cause != CauseUNEXPECTEDREASON { + return errors.New("SecurityNotification unexpected value for Cause") + } + + return nil +} + func createAccessTokenError(err AccessTokenErrError, message string) AccessTokenErr { return AccessTokenErr{ Error: err, diff --git a/capifcore/internal/securityservice/security.go b/capifcore/internal/securityservice/security.go index d3d9026..aee022e 100644 --- a/capifcore/internal/securityservice/security.go +++ b/capifcore/internal/securityservice/security.go @@ -29,6 +29,7 @@ import ( "github.com/labstack/echo/v4" copystructure "github.com/mitchellh/copystructure" + "k8s.io/utils/strings/slices" "oransc.org/nonrtric/capifcore/internal/common29122" securityapi "oransc.org/nonrtric/capifcore/internal/securityapi" @@ -224,11 +225,86 @@ func (s *Security) prepareNewSecurityContext(newContext *securityapi.ServiceSecu } func (s *Security) PostTrustedInvokersApiInvokerIdDelete(ctx echo.Context, apiInvokerId string) error { - return ctx.NoContent(http.StatusNotImplemented) + var notification securityapi.SecurityNotification + + errMsg := "Unable to revoke invoker due to %s" + + if err := ctx.Bind(¬ification); err != nil { + return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, "invalid format for security notification")) + } + + if err := notification.Validate(); err != nil { + return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err)) + } + + if ss, ok := s.trustedInvokers[apiInvokerId]; ok { + securityInfoCopy := s.revokeTrustedInvoker(&ss, notification, apiInvokerId) + + if len(securityInfoCopy) == 0 { + s.deleteTrustedInvoker(apiInvokerId) + } else { + ss.SecurityInfo = securityInfoCopy + s.updateTrustedInvoker(ss, apiInvokerId) + } + + } else { + return sendCoreError(ctx, http.StatusNotFound, "the invoker is not register as a trusted invoker") + } + + return ctx.NoContent(http.StatusNoContent) + +} + +func (s *Security) revokeTrustedInvoker(ss *securityapi.ServiceSecurity, notification securityapi.SecurityNotification, apiInvokerId string) []securityapi.SecurityInformation { + + data, _ := copystructure.Copy(ss.SecurityInfo) + securityInfoCopy, _ := data.([]securityapi.SecurityInformation) + + for i, context := range ss.SecurityInfo { + if notification.AefId == context.AefId || slices.Contains(notification.ApiIds, *context.ApiId) { + securityInfoCopy = append(securityInfoCopy[:i], securityInfoCopy[i+1:]...) + } + } + + return securityInfoCopy + } func (s *Security) PostTrustedInvokersApiInvokerIdUpdate(ctx echo.Context, apiInvokerId string) error { - return ctx.NoContent(http.StatusNotImplemented) + var serviceSecurity securityapi.ServiceSecurity + + errMsg := "Unable to update service security context due to %s" + + if err := ctx.Bind(&serviceSecurity); err != nil { + return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, "invalid format for service security context")) + } + + if err := serviceSecurity.Validate(); err != nil { + return sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf(errMsg, err)) + } + + if _, ok := s.trustedInvokers[apiInvokerId]; ok { + s.updateTrustedInvoker(serviceSecurity, apiInvokerId) + } else { + return sendCoreError(ctx, http.StatusNotFound, "the invoker is not register as a trusted invoker") + } + + uri := ctx.Request().Host + ctx.Request().URL.String() + ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, apiInvokerId)) + + err := ctx.JSON(http.StatusOK, s.trustedInvokers[apiInvokerId]) + if err != nil { + // Something really bad happened, tell Echo that our handler failed + return err + } + + return nil +} + +func (s *Security) updateTrustedInvoker(serviceSecurity securityapi.ServiceSecurity, invokerId string) { + s.lock.Lock() + defer s.lock.Unlock() + s.trustedInvokers[invokerId] = serviceSecurity } func sendAccessTokenError(ctx echo.Context, code int, err securityapi.AccessTokenErrError, message string) error { diff --git a/capifcore/internal/securityservice/security_test.go b/capifcore/internal/securityservice/security_test.go index 1abb8ae..57e9cea 100644 --- a/capifcore/internal/securityservice/security_test.go +++ b/capifcore/internal/securityservice/security_test.go @@ -488,6 +488,108 @@ func TestGetSecurityContextByInvokerId(t *testing.T) { } } +func TestUpdateTrustedInvoker(t *testing.T) { + + requestHandler, securityUnderTest := getEcho(nil, nil, nil, nil) + + aefId := "aefId" + apiId := "apiId" + invokerId := "invokerId" + serviceSecurityTest := getServiceSecurity(aefId, apiId) + serviceSecurityTest.SecurityInfo[0].ApiId = &apiId + securityUnderTest.trustedInvokers[invokerId] = serviceSecurityTest + + // Update the service security with valid invoker, should return 200 with updated service security + newNotifURL := "http://golang.org/" + serviceSecurityTest.NotificationDestination = common29122.Uri(newNotifURL) + result := testutil.NewRequest().Post("/trustedInvokers/"+invokerId+"/update").WithJsonBody(serviceSecurityTest).Go(t, requestHandler) + + var resultResponse securityapi.ServiceSecurity + assert.Equal(t, http.StatusOK, result.Code()) + err := result.UnmarshalBodyToObject(&resultResponse) + assert.NoError(t, err, "error unmarshaling response") + assert.Equal(t, newNotifURL, string(resultResponse.NotificationDestination)) + + // Update with an service security missing required NotificationDestination, should get 400 with problem details + invalidServiceSecurity := securityapi.ServiceSecurity{ + SecurityInfo: []securityapi.SecurityInformation{ + { + AefId: &aefId, + ApiId: &apiId, + PrefSecurityMethods: []publishserviceapi.SecurityMethod{ + publishserviceapi.SecurityMethodOAUTH, + }, + }, + }, + } + + result = testutil.NewRequest().Post("/trustedInvokers/"+invokerId+"/update").WithJsonBody(invalidServiceSecurity).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") + assert.Equal(t, http.StatusBadRequest, *problemDetails.Status) + assert.Contains(t, *problemDetails.Cause, "missing") + assert.Contains(t, *problemDetails.Cause, "notificationDestination") + + // Update a service security that has not been registered, should get 404 with problem details + missingId := "1" + result = testutil.NewRequest().Post("/trustedInvokers/"+missingId+"/update").WithJsonBody(serviceSecurityTest).Go(t, requestHandler) + + assert.Equal(t, http.StatusNotFound, result.Code()) + err = result.UnmarshalBodyToObject(&problemDetails) + assert.NoError(t, err, "error unmarshaling response") + assert.Equal(t, http.StatusNotFound, *problemDetails.Status) + assert.Contains(t, *problemDetails.Cause, "not register") + assert.Contains(t, *problemDetails.Cause, "trusted invoker") +} + +func TestRevokeAuthorizationToInvoker(t *testing.T) { + aefId := "aefId" + apiId := "apiId" + invokerId := "invokerId" + + notification := securityapi.SecurityNotification{ + AefId: &aefId, + ApiInvokerId: invokerId, + ApiIds: []string{apiId}, + Cause: securityapi.CauseUNEXPECTEDREASON, + } + + requestHandler, securityUnderTest := getEcho(nil, nil, nil, nil) + + serviceSecurityTest := getServiceSecurity(aefId, apiId) + serviceSecurityTest.SecurityInfo[0].ApiId = &apiId + + apiIdTwo := "apiIdTwo" + secInfo := securityapi.SecurityInformation{ + AefId: &aefId, + ApiId: &apiIdTwo, + PrefSecurityMethods: []publishserviceapi.SecurityMethod{ + publishserviceapi.SecurityMethodPKI, + }, + } + + serviceSecurityTest.SecurityInfo = append(serviceSecurityTest.SecurityInfo, secInfo) + + securityUnderTest.trustedInvokers[invokerId] = serviceSecurityTest + + // Revoke apiId + result := testutil.NewRequest().Post("/trustedInvokers/"+invokerId+"/delete").WithJsonBody(notification).Go(t, requestHandler) + + assert.Equal(t, http.StatusNoContent, result.Code()) + assert.Equal(t, 1, len(securityUnderTest.trustedInvokers[invokerId].SecurityInfo)) + + notification.ApiIds = []string{apiIdTwo} + // Revoke apiIdTwo + result = testutil.NewRequest().Post("/trustedInvokers/"+invokerId+"/delete").WithJsonBody(notification).Go(t, requestHandler) + + assert.Equal(t, http.StatusNoContent, result.Code()) + _, ok := securityUnderTest.trustedInvokers[invokerId] + assert.False(t, ok) +} + func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister, keycloakMgm keycloak.AccessManagement) (*echo.Echo, *Security) { swagger, err := securityapi.GetSwagger() if err != nil { -- 2.16.6 From 051a4a32068b4718ef9ddb1868e532a976de843e Mon Sep 17 00:00:00 2001 From: ychacon Date: Mon, 3 Apr 2023 10:59:06 +0200 Subject: [PATCH 13/16] Integration with keycloak, add client Issue-ID: NONRTRIC-856 Signed-off-by: ychacon Change-Id: I60bba76bf8e1d7c2e907a3a90a307481b589637a --- capifcore/configs/keycloak.yaml | 3 + capifcore/internal/config/config.go | 12 +++- capifcore/internal/keycloak/keycloak.go | 66 ++++++++++++++++++++-- .../internal/keycloak/mocks/AccessManagement.go | 32 ++++++++--- capifcore/internal/restclient/HTTPClient.go | 19 ++++++- capifcore/internal/restclient/HTTPClient_test.go | 2 +- capifcore/internal/securityapi/typeupdate.go | 7 ++- capifcore/internal/securityservice/security.go | 9 ++- .../internal/securityservice/security_test.go | 28 ++++++--- capifcore/main.go | 2 +- 10 files changed, 144 insertions(+), 36 deletions(-) diff --git a/capifcore/configs/keycloak.yaml b/capifcore/configs/keycloak.yaml index 1ca2ba9..86b3905 100644 --- a/capifcore/configs/keycloak.yaml +++ b/capifcore/configs/keycloak.yaml @@ -19,5 +19,8 @@ authorizationServer: host: "localhost" port: "8080" + admin: + user: "admin" + password: "secret" realms: invokerrealm: "invokerrealm" diff --git a/capifcore/internal/config/config.go b/capifcore/internal/config/config.go index 4a53022..f7e07e6 100644 --- a/capifcore/internal/config/config.go +++ b/capifcore/internal/config/config.go @@ -27,10 +27,16 @@ import ( "gopkg.in/yaml.v2" ) +type AdminUser struct { + User string `yaml:"user"` + Password string `yaml:"password"` +} + type AuthorizationServer struct { - Port string `yaml:"port"` - Host string `yaml:"host"` - Realms map[string]string `yaml:"realms"` + Port string `yaml:"port"` + Host string `yaml:"host"` + AdminUser AdminUser `yaml:"admin"` + Realms map[string]string `yaml:"realms"` } type Config struct { diff --git a/capifcore/internal/keycloak/keycloak.go b/capifcore/internal/keycloak/keycloak.go index 16f65c8..3646516 100644 --- a/capifcore/internal/keycloak/keycloak.go +++ b/capifcore/internal/keycloak/keycloak.go @@ -27,28 +27,44 @@ import ( "net/http" "net/url" + log "github.com/sirupsen/logrus" "oransc.org/nonrtric/capifcore/internal/config" + "oransc.org/nonrtric/capifcore/internal/restclient" ) //go:generate mockery --name AccessManagement type AccessManagement interface { // Get JWT token for a client. // Returns JWT token if client exits and credentials are correct otherwise returns error. - GetToken(clientId, clientPassword, scope string, realm string) (Jwttoken, error) + GetToken(realm string, data map[string][]string) (Jwttoken, error) + // Add new client in keycloak + AddClient(clientId string, realm string) error +} + +type AdminUser struct { + User string + Password string } type KeycloakManager struct { keycloakServerUrl string + admin AdminUser realms map[string]string + client restclient.HTTPClient } -func NewKeycloakManager(cfg *config.Config) *KeycloakManager { +func NewKeycloakManager(cfg *config.Config, c restclient.HTTPClient) *KeycloakManager { keycloakUrl := "http://" + cfg.AuthorizationServer.Host + ":" + cfg.AuthorizationServer.Port return &KeycloakManager{ keycloakServerUrl: keycloakUrl, - realms: cfg.AuthorizationServer.Realms, + client: c, + admin: AdminUser{ + User: cfg.AuthorizationServer.AdminUser.User, + Password: cfg.AuthorizationServer.AdminUser.Password, + }, + realms: cfg.AuthorizationServer.Realms, } } @@ -64,12 +80,11 @@ type Jwttoken struct { Scope string `json:"scope"` } -func (km *KeycloakManager) GetToken(clientId, clientPassword, scope string, realm string) (Jwttoken, error) { +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, - url.Values{"grant_type": {"client_credentials"}, "client_id": {clientId}, "client_secret": {clientPassword}}) + resp, err := http.PostForm(getTokenUrl, data) if err != nil { return jwt, err @@ -88,3 +103,42 @@ func (km *KeycloakManager) GetToken(clientId, clientPassword, scope string, real json.Unmarshal([]byte(body), &jwt) return jwt, nil } + +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"` +} + +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 { + log.Errorf("error wrong credentials or url %v\n", err) + return err + } + + createClientUrl := km.keycloakServerUrl + "/admin/realms/" + realm + "/clients" + newClient := Client{ + ClientID: clientId, + Enabled: true, + ServiceAccountsEnabled: true, + BearerOnly: false, + PublicClient: false, + } + + 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) + return err + } + + log.Info("Created new client") + return nil + +} diff --git a/capifcore/internal/keycloak/mocks/AccessManagement.go b/capifcore/internal/keycloak/mocks/AccessManagement.go index 9ac2179..59b914a 100644 --- a/capifcore/internal/keycloak/mocks/AccessManagement.go +++ b/capifcore/internal/keycloak/mocks/AccessManagement.go @@ -12,23 +12,37 @@ type AccessManagement struct { mock.Mock } -// GetToken provides a mock function with given fields: clientId, clientPassword, scope, realm -func (_m *AccessManagement) GetToken(clientId string, clientPassword string, scope string, realm string) (keycloak.Jwttoken, error) { - ret := _m.Called(clientId, clientPassword, scope, realm) +// AddClient provides a mock function with given fields: clientId, realm +func (_m *AccessManagement) AddClient(clientId string, realm string) error { + ret := _m.Called(clientId, realm) + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(clientId, realm) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// 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) var r0 keycloak.Jwttoken var r1 error - if rf, ok := ret.Get(0).(func(string, string, string, string) (keycloak.Jwttoken, error)); ok { - return rf(clientId, clientPassword, scope, realm) + if rf, ok := ret.Get(0).(func(string, map[string][]string) (keycloak.Jwttoken, error)); ok { + return rf(realm, data) } - if rf, ok := ret.Get(0).(func(string, string, string, string) keycloak.Jwttoken); ok { - r0 = rf(clientId, clientPassword, scope, realm) + if rf, ok := ret.Get(0).(func(string, map[string][]string) keycloak.Jwttoken); ok { + r0 = rf(realm, data) } else { r0 = ret.Get(0).(keycloak.Jwttoken) } - if rf, ok := ret.Get(1).(func(string, string, string, string) error); ok { - r1 = rf(clientId, clientPassword, scope, realm) + if rf, ok := ret.Get(1).(func(string, map[string][]string) error); ok { + r1 = rf(realm, data) } else { r1 = ret.Error(1) } diff --git a/capifcore/internal/restclient/HTTPClient.go b/capifcore/internal/restclient/HTTPClient.go index de0ad1d..c771a54 100644 --- a/capifcore/internal/restclient/HTTPClient.go +++ b/capifcore/internal/restclient/HTTPClient.go @@ -45,12 +45,19 @@ func (pe RequestError) Error() string { } func Put(url string, body []byte, client HTTPClient) error { - return do(http.MethodPut, url, body, ContentTypeJSON, client) + var header = map[string]string{"Content-Type": ContentTypeJSON} + return do(http.MethodPut, url, body, header, client) } -func do(method string, url string, body []byte, contentType string, client HTTPClient) error { +func Post(url string, body []byte, header map[string]string, client HTTPClient) error { + return do(http.MethodPost, url, body, header, client) +} + +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 { - req.Header.Set("Content-Type", contentType) + if len(header) > 0 { + setHeader(req, header) + } if response, respErr := client.Do(req); respErr == nil { if isResponseSuccess(response.StatusCode) { return nil @@ -65,6 +72,12 @@ func do(method string, url string, body []byte, contentType string, client HTTPC } } +func setHeader(req *http.Request, header map[string]string) { + for key, element := range header { + req.Header.Set(key, element) + } +} + func isResponseSuccess(statusCode int) bool { return statusCode >= http.StatusOK && statusCode <= 299 } diff --git a/capifcore/internal/restclient/HTTPClient_test.go b/capifcore/internal/restclient/HTTPClient_test.go index 21186aa..e390686 100644 --- a/capifcore/internal/restclient/HTTPClient_test.go +++ b/capifcore/internal/restclient/HTTPClient_test.go @@ -109,7 +109,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, "", &clientMock) + err := do("PUT", tt.args.url, nil, map[string]string{}, &clientMock) assertions.Equal(tt.wantErr, err, tt.name) }) } diff --git a/capifcore/internal/securityapi/typeupdate.go b/capifcore/internal/securityapi/typeupdate.go index 364c123..3402b8e 100644 --- a/capifcore/internal/securityapi/typeupdate.go +++ b/capifcore/internal/securityapi/typeupdate.go @@ -31,7 +31,6 @@ var securityMethods []publishserviceapi.SecurityMethod func (newContext *ServiceSecurity) PrepareNewSecurityContext(services []publishserviceapi.ServiceAPIDescription) error { securityMethods = []publishserviceapi.SecurityMethod{} for i, securityInfo := range newContext.SecurityInfo { - if securityInfo.InterfaceDetails != nil { addSecurityMethodsFromInterfaceDetails(securityInfo.InterfaceDetails.SecurityMethods, &securityInfo.PrefSecurityMethods) @@ -39,9 +38,11 @@ func (newContext *ServiceSecurity) PrepareNewSecurityContext(services []publishs checkNil := securityInfo.ApiId != nil && securityInfo.AefId != nil if checkNil { service := getServiceByApiId(&services, securityInfo.ApiId) - afpProfile := service.GetAefProfileById(securityInfo.AefId) + if service != nil { + afpProfile := service.GetAefProfileById(securityInfo.AefId) + addSecurityMethodsFromAefProfile(afpProfile) + } - addSecurityMethodsFromAefProfile(afpProfile) } } diff --git a/capifcore/internal/securityservice/security.go b/capifcore/internal/securityservice/security.go index aee022e..e211f67 100644 --- a/capifcore/internal/securityservice/security.go +++ b/capifcore/internal/securityservice/security.go @@ -23,6 +23,7 @@ package security import ( "fmt" "net/http" + "net/url" "path" "strings" "sync" @@ -89,7 +90,8 @@ func (s *Security) PostSecuritiesSecurityIdToken(ctx echo.Context, securityId st } } } - jwtToken, err := s.keycloak.GetToken(accessTokenReq.ClientId, *accessTokenReq.ClientSecret, *accessTokenReq.Scope, "invokerrealm") + data := url.Values{"grant_type": {"client_credentials"}, "client_id": {accessTokenReq.ClientId}, "client_secret": {*accessTokenReq.ClientSecret}} + jwtToken, err := s.keycloak.GetToken("invokerrealm", data) if err != nil { return sendAccessTokenError(ctx, http.StatusBadRequest, securityapi.AccessTokenErrErrorUnauthorizedClient, err.Error()) } @@ -190,6 +192,11 @@ 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)) diff --git a/capifcore/internal/securityservice/security_test.go b/capifcore/internal/securityservice/security_test.go index 57e9cea..1dda127 100644 --- a/capifcore/internal/securityservice/security_test.go +++ b/capifcore/internal/securityservice/security_test.go @@ -66,7 +66,7 @@ func TestPostSecurityIdTokenInvokerRegistered(t *testing.T) { Scope: "3gpp#aefIdpath", } accessMgmMock := keycloackmocks.AccessManagement{} - accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(jwt, nil) + accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("map[string][]string")).Return(jwt, nil) requestHandler, _ := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock) @@ -94,7 +94,7 @@ func TestPostSecurityIdTokenInvokerRegistered(t *testing.T) { invokerRegisterMock.AssertCalled(t, "VerifyInvokerSecret", clientId, clientSecret) serviceRegisterMock.AssertCalled(t, "IsFunctionRegistered", aefId) publishRegisterMock.AssertCalled(t, "IsAPIPublished", aefId, path) - accessMgmMock.AssertCalled(t, "GetToken", clientId, clientSecret, "3gpp#"+aefId+":"+path, "invokerrealm") + accessMgmMock.AssertNumberOfCalls(t, "GetToken", 1) } func TestPostSecurityIdTokenInvokerNotRegistered(t *testing.T) { @@ -213,7 +213,7 @@ func TestPostSecurityIdTokenInvokerInvalidCredentials(t *testing.T) { jwt := keycloak.Jwttoken{} accessMgmMock := keycloackmocks.AccessManagement{} - accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(jwt, errors.New("invalid_credentials")) + accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("map[string][]string")).Return(jwt, errors.New("invalid_credentials")) requestHandler, _ := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock) @@ -239,7 +239,7 @@ func TestPostSecurityIdTokenInvokerInvalidCredentials(t *testing.T) { invokerRegisterMock.AssertCalled(t, "VerifyInvokerSecret", clientId, clientSecret) serviceRegisterMock.AssertCalled(t, "IsFunctionRegistered", aefId) publishRegisterMock.AssertCalled(t, "IsAPIPublished", aefId, path) - accessMgmMock.AssertCalled(t, "GetToken", clientId, clientSecret, "3gpp#"+aefId+":"+path, "invokerrealm") + accessMgmMock.AssertNumberOfCalls(t, "GetToken", 1) } func TestPutTrustedInvokerSuccessfully(t *testing.T) { @@ -263,7 +263,10 @@ func TestPutTrustedInvokerSuccessfully(t *testing.T) { publishRegisterMock := publishmocks.PublishRegister{} publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices) - requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) + accessMgmMock := keycloackmocks.AccessManagement{} + accessMgmMock.On("AddClient", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil) + + requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock) invokerId := "invokerId" serviceSecurityUnderTest := getServiceSecurity(aefId, apiId) @@ -282,6 +285,7 @@ func TestPutTrustedInvokerSuccessfully(t *testing.T) { assert.Equal(t, *security.SelSecurityMethod, publishserviceapi.SecurityMethodPKI) } invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId) + accessMgmMock.AssertCalled(t, "AddClient", invokerId, "invokerrealm") } @@ -350,7 +354,10 @@ func TestPutTrustedInvokerInterfaceDetailsNotNil(t *testing.T) { publishRegisterMock := publishmocks.PublishRegister{} publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices) - requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) + accessMgmMock := keycloackmocks.AccessManagement{} + accessMgmMock.On("AddClient", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil) + + requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock) invokerId := "invokerId" serviceSecurityUnderTest := getServiceSecurity(aefId, apiId) @@ -379,7 +386,7 @@ 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) { @@ -399,7 +406,10 @@ func TestPutTrustedInvokerNotFoundSecurityMethod(t *testing.T) { publishRegisterMock := publishmocks.PublishRegister{} publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices) - requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil) + accessMgmMock := keycloackmocks.AccessManagement{} + accessMgmMock.On("AddClient", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil) + + requestHandler, _ := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock) invokerId := "invokerId" serviceSecurityUnderTest := getServiceSecurity("aefId", "apiId") @@ -510,7 +520,7 @@ func TestUpdateTrustedInvoker(t *testing.T) { assert.NoError(t, err, "error unmarshaling response") assert.Equal(t, newNotifURL, string(resultResponse.NotificationDestination)) - // Update with an service security missing required NotificationDestination, should get 400 with problem details + // Update with a service security missing required NotificationDestination, should get 400 with problem details invalidServiceSecurity := securityapi.ServiceSecurity{ SecurityInfo: []securityapi.SecurityInformation{ { diff --git a/capifcore/main.go b/capifcore/main.go index f8b9daf..5ba3923 100644 --- a/capifcore/main.go +++ b/capifcore/main.go @@ -94,7 +94,7 @@ func getEcho() *echo.Echo { if err != nil { log.Fatalf("Error loading configuration file\n: %s", err) } - km := keycloak.NewKeycloakManager(cfg) + km := keycloak.NewKeycloakManager(cfg, &http.Client{}) var group *echo.Group // Register ProviderManagement -- 2.16.6 From b01ea504c8924bbf86355d6a888b767e6af853c0 Mon Sep 17 00:00:00 2001 From: ychacon Date: Tue, 4 Apr 2023 15:48:26 +0200 Subject: [PATCH 14/16] Adding docker-compose file for keycloak and postgres DB Issue-ID: NONRTRIC-856 Signed-off-by: ychacon Change-Id: I6c59369cc54dc2c67584901755c1a9370b6085dc --- capifcore/docker-compose.yml | 84 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 capifcore/docker-compose.yml diff --git a/capifcore/docker-compose.yml b/capifcore/docker-compose.yml new file mode 100644 index 0000000..da2be5b --- /dev/null +++ b/capifcore/docker-compose.yml @@ -0,0 +1,84 @@ +# Copyright (C) 2023 Nordix Foundation. All rights reserved. +# ======================================================================== +# 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================================================= +# +version: '3.5' + +services: + postgres: + container_name: postgres_container + image: postgres:latest + environment: + POSTGRES_DB: keycloak + POSTGRES_USER: keycloak + POSTGRES_PASSWORD: password + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: "exit 0" + ports: + - "5432:5432" + networks: + - capif + restart: unless-stopped + + pgadmin: + container_name: pgadmin_container + image: dpage/pgadmin4 + environment: + PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org} + PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin} + PGADMIN_CONFIG_SERVER_MODE: 'False' + volumes: + - pgadmin:/var/lib/pgadmin + + ports: + - "${PGADMIN_PORT:-5050}:80" + networks: + - capif + restart: unless-stopped + + keycloak: + container_name: keycloak + image: quay.io/keycloak/keycloak:20.0.3 + environment: + KC_DB: postgres + KC_DB_URL_HOST: postgres_container + KC_DB_URL_DATABASE: keycloak + KC_DB_PASSWORD: password + KC_DB_USERNAME: keycloak + KC_DB_SCHEMA: public + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: secret + ports: + - 8080:8080 + depends_on: + - postgres + healthcheck: + test: curl --fail --silent http://localhost:8180/health/ready 2>&1 || exit 1 + interval: 10s + timeout: 10s + retries: 5 + entrypoint: ["/opt/keycloak/bin/kc.sh", "start-dev"] + networks: + - capif + +networks: + capif: + driver: bridge + +volumes: + postgres_data: + driver: local + pgadmin: \ No newline at end of file -- 2.16.6 From 777987abe351d45394cda0404f863147e83cb53e Mon Sep 17 00:00:00 2001 From: ychacon Date: Tue, 25 Apr 2023 10:02:56 +0200 Subject: [PATCH 15/16] Bug fix Issue-ID: NONRTRIC-861 Signed-off-by: ychacon Change-Id: Id77be8cfb5d857cb722277d11fda33b464c09a64 --- capifcore/internal/providermanagementapi/typeaccess.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capifcore/internal/providermanagementapi/typeaccess.go b/capifcore/internal/providermanagementapi/typeaccess.go index 78b68c0..5257701 100644 --- a/capifcore/internal/providermanagementapi/typeaccess.go +++ b/capifcore/internal/providermanagementapi/typeaccess.go @@ -22,7 +22,7 @@ package providermanagementapi func (ed APIProviderEnrolmentDetails) GetExposingFunctionIdsForPublisher(apfId string) []string { for _, registeredFunc := range *ed.ApiProvFuncs { - if *registeredFunc.ApiProvFuncId == apfId && registeredFunc.isProvidingFunction() { + if *registeredFunc.ApiProvFuncId == apfId { return ed.getExposingFunctionIds() } } -- 2.16.6 From b0843ad24d0653b3f09bcb55c17e2ea27061f2e1 Mon Sep 17 00:00:00 2001 From: ychacon Date: Wed, 26 Apr 2023 09:05:49 +0200 Subject: [PATCH 16/16] Add and update libraries Issue-ID: NONRTRIC-861 Signed-off-by: ychacon Change-Id: I9ba1aeac191e0fb466e6ae62acff34613396af17 --- capifcore/go.mod | 65 ++++++++++------------- capifcore/go.sum | 158 ++++++++++++++++++------------------------------------- 2 files changed, 80 insertions(+), 143 deletions(-) diff --git a/capifcore/go.mod b/capifcore/go.mod index f19186b..fbdc016 100644 --- a/capifcore/go.mod +++ b/capifcore/go.mod @@ -5,16 +5,24 @@ go 1.19 require ( github.com/deepmap/oapi-codegen v1.11.0 github.com/getkin/kin-openapi v0.106.0 - github.com/labstack/echo/v4 v4.9.1 - github.com/pkg/errors v0.9.1 + github.com/labstack/echo/v4 v4.10.2 github.com/sirupsen/logrus v1.9.0 - github.com/stretchr/testify v1.7.1 + github.com/stretchr/testify v1.8.1 + github.com/swaggo/swag v1.8.12 gopkg.in/yaml.v2 v2.4.0 helm.sh/helm/v3 v3.9.0 k8s.io/cli-runtime v0.25.3-rc.0 k8s.io/client-go v0.25.3-rc.0 ) +require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/go-openapi/spec v0.20.8 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/swaggo/files/v2 v2.0.0 // indirect + golang.org/x/tools v0.8.0 // indirect +) + require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/BurntSushi/toml v1.1.0 // indirect @@ -23,17 +31,13 @@ require ( github.com/Masterminds/semver/v3 v3.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/containerd/containerd v1.6.6 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgraph-io/ristretto v0.0.2 // indirect github.com/docker/cli v20.10.17+incompatible // indirect github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/docker v20.10.17+incompatible // indirect @@ -45,13 +49,12 @@ require ( github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-gorp/gorp/v3 v3.0.2 // indirect github.com/go-logr/logr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/swag v0.21.1 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect @@ -61,11 +64,10 @@ require ( github.com/google/go-cmp v0.5.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -79,15 +81,13 @@ require ( github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lib/pq v1.10.6 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/go-wordwrap v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -100,10 +100,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect - github.com/ory/go-acc v0.2.8 // indirect - github.com/ory/viper v1.7.5 // indirect - github.com/pborman/uuid v1.2.0 // indirect - github.com/pelletier/go-toml v1.9.3 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.1 // indirect @@ -113,34 +109,31 @@ require ( github.com/rubenv/sql-migrate v1.1.2 // indirect github.com/russross/blackfriday v1.5.2 // indirect github.com/shopspring/decimal v1.2.0 // indirect - github.com/spf13/afero v1.6.0 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/cobra v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.4.0 // indirect - github.com/subosito/gotenv v1.2.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect + github.com/swaggo/echo-swagger v1.4.0 github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasttemplate v1.2.1 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect - golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect - golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/crypto v0.8.0 // indirect + golang.org/x/net v0.9.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/term v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect google.golang.org/grpc v1.47.0 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.25.3-rc.0 // indirect k8s.io/apiextensions-apiserver v0.25.2 // indirect @@ -150,7 +143,7 @@ require ( k8s.io/klog/v2 v2.70.1 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect k8s.io/kubectl v0.25.2 // indirect - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed oras.land/oras-go v1.1.1 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/kustomize/api v0.12.1 // indirect diff --git a/capifcore/go.sum b/capifcore/go.sum index c43e7bc..c8d757d 100644 --- a/capifcore/go.sum +++ b/capifcore/go.sum @@ -44,6 +44,8 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -59,11 +61,6 @@ github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvd github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/hcsshim v0.9.3 h1:k371PzBuRrz2b+ebGuI2nVgVhgsVX60jMfSw80NECxo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -72,7 +69,6 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -89,8 +85,6 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -110,13 +104,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0= github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -135,12 +124,6 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC github.com/deepmap/oapi-codegen v1.11.0 h1:f/X2NdIkaBKsSdpeuwLnY/vDI0AtPUrmB5LMgc7YD+A= github.com/deepmap/oapi-codegen v1.11.0/go.mod h1:k+ujhoQGxmQYBZBbxhOZNZf4j08qv5mC+OH+fFTnKxM= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= -github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po= -github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 h1:DBZ2sN7CK6dgvHVpQsQj4sRMCbWTmd17l+5SUCjnQSY= github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -178,8 +161,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/getkin/kin-openapi v0.94.0/go.mod h1:LWZfzOd7PRy8GJ1dJ6mCU6tNdSfOwRac1BUPam4aw6Q= github.com/getkin/kin-openapi v0.106.0 h1:hrqfqJPAvWvuO/V0lCr/xyQOq4Gy21mcr28JJOSRcEI= @@ -206,13 +187,19 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= +github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= @@ -237,14 +224,12 @@ github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -317,7 +302,6 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -328,15 +312,11 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -353,7 +333,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -373,7 +352,6 @@ github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -391,7 +369,6 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= @@ -411,8 +388,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= -github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y= -github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo= +github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= +github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= @@ -436,9 +413,6 @@ github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -455,12 +429,15 @@ github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/V github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -477,7 +454,6 @@ github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFW github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= @@ -485,8 +461,6 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -516,7 +490,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= @@ -525,16 +498,7 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/ory/go-acc v0.2.8 h1:rOHHAPQjf0u7eHFGWpiXK+gIu/e0GRSJNr9pDukdNC4= -github.com/ory/go-acc v0.2.8/go.mod h1:iCRZUdGb/7nqvSn8xWZkhfVrtXRZ9Wru2E5rabCjFPI= -github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE= -github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= -github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -551,7 +515,6 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg= github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -563,8 +526,6 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= @@ -572,15 +533,12 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -606,63 +564,59 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/swaggo/echo-swagger v1.4.0 h1:RCxLKySw1SceHLqnmc41pKyiIeE+OiD7NSI7FUOBlLo= +github.com/swaggo/echo-swagger v1.4.0/go.mod h1:Wh3VlwjZGZf/LH0s81tz916JokuPG7y/ZqaqnckYqoQ= +github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw= +github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= +github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w= +github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -675,7 +629,6 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMzt github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= @@ -690,11 +643,8 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -705,7 +655,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -713,8 +662,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -751,25 +700,23 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -797,8 +744,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220513224357-95641704303c/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -825,13 +772,12 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -853,7 +799,6 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -893,14 +838,16 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -909,15 +856,16 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -972,6 +920,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= +golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1053,7 +1003,6 @@ google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxv google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -1100,12 +1049,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -- 2.16.6