2 // ========================LICENSE_START=================================
5 // Copyright (C) 2022: Nordix Foundation
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 // ========================LICENSE_END===================================
31 "oransc.org/nonrtric/capifcore/internal/common29122"
32 "oransc.org/nonrtric/capifcore/internal/keycloak"
33 "oransc.org/nonrtric/capifcore/internal/publishserviceapi"
34 "oransc.org/nonrtric/capifcore/internal/securityapi"
36 "oransc.org/nonrtric/capifcore/internal/invokermanagement"
37 "oransc.org/nonrtric/capifcore/internal/providermanagement"
38 "oransc.org/nonrtric/capifcore/internal/publishservice"
40 "github.com/labstack/echo/v4"
42 invokermocks "oransc.org/nonrtric/capifcore/internal/invokermanagement/mocks"
43 keycloackmocks "oransc.org/nonrtric/capifcore/internal/keycloak/mocks"
44 servicemocks "oransc.org/nonrtric/capifcore/internal/providermanagement/mocks"
45 publishmocks "oransc.org/nonrtric/capifcore/internal/publishservice/mocks"
47 "github.com/deepmap/oapi-codegen/pkg/middleware"
48 "github.com/deepmap/oapi-codegen/pkg/testutil"
49 echomiddleware "github.com/labstack/echo/v4/middleware"
50 "github.com/stretchr/testify/assert"
51 "github.com/stretchr/testify/mock"
54 func TestPostSecurityIdTokenInvokerRegistered(t *testing.T) {
55 invokerRegisterMock := invokermocks.InvokerRegister{}
56 invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
57 invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
58 serviceRegisterMock := servicemocks.ServiceRegister{}
59 serviceRegisterMock.On("IsFunctionRegistered", mock.AnythingOfType("string")).Return(true)
60 publishRegisterMock := publishmocks.PublishRegister{}
61 publishRegisterMock.On("IsAPIPublished", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
63 jwt := keycloak.Jwttoken{
64 AccessToken: "eyJhbGNIn0.e3YTQ0xLjEifQ.FcqCwCy7iJiOmw",
66 Scope: "3gpp#aefIdpath",
68 accessMgmMock := keycloackmocks.AccessManagement{}
69 accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(jwt, nil)
71 requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock)
75 clientSecret := "secret"
78 data.Set("client_id", clientId)
79 data.Set("client_secret", clientSecret)
80 data.Set("grant_type", "client_credentials")
81 data.Set("scope", "3gpp#"+aefId+":"+path)
83 encodedData := data.Encode()
85 result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
87 assert.Equal(t, http.StatusCreated, result.Code())
88 var resultResponse securityapi.AccessTokenRsp
89 err := result.UnmarshalBodyToObject(&resultResponse)
90 assert.NoError(t, err, "error unmarshaling response")
91 assert.NotEmpty(t, resultResponse.AccessToken)
92 assert.Equal(t, securityapi.AccessTokenRspTokenTypeBearer, resultResponse.TokenType)
93 invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", clientId)
94 invokerRegisterMock.AssertCalled(t, "VerifyInvokerSecret", clientId, clientSecret)
95 serviceRegisterMock.AssertCalled(t, "IsFunctionRegistered", aefId)
96 publishRegisterMock.AssertCalled(t, "IsAPIPublished", aefId, path)
97 accessMgmMock.AssertCalled(t, "GetToken", clientId, clientSecret, "3gpp#"+aefId+":"+path, "invokerrealm")
100 func TestPostSecurityIdTokenInvokerNotRegistered(t *testing.T) {
101 invokerRegisterMock := invokermocks.InvokerRegister{}
102 invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(false)
104 requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil)
107 data.Set("client_id", "id")
108 data.Add("client_secret", "secret")
109 data.Add("grant_type", "client_credentials")
110 data.Add("scope", "3gpp#aefId:path")
111 encodedData := data.Encode()
113 result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
115 assert.Equal(t, http.StatusBadRequest, result.Code())
116 var errDetails securityapi.AccessTokenErr
117 err := result.UnmarshalBodyToObject(&errDetails)
118 assert.NoError(t, err, "error unmarshaling response")
119 assert.Equal(t, securityapi.AccessTokenErrErrorInvalidClient, errDetails.Error)
120 errMsg := "Invoker not registered"
121 assert.Equal(t, &errMsg, errDetails.ErrorDescription)
124 func TestPostSecurityIdTokenInvokerSecretNotValid(t *testing.T) {
125 invokerRegisterMock := invokermocks.InvokerRegister{}
126 invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
127 invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(false)
129 requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil)
132 data.Set("client_id", "id")
133 data.Add("client_secret", "secret")
134 data.Add("grant_type", "client_credentials")
135 data.Add("scope", "3gpp#aefId:path")
136 encodedData := data.Encode()
138 result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
140 assert.Equal(t, http.StatusBadRequest, result.Code())
141 var errDetails securityapi.AccessTokenErr
142 err := result.UnmarshalBodyToObject(&errDetails)
143 assert.NoError(t, err, "error unmarshaling response")
144 assert.Equal(t, securityapi.AccessTokenErrErrorUnauthorizedClient, errDetails.Error)
145 errMsg := "Invoker secret not valid"
146 assert.Equal(t, &errMsg, errDetails.ErrorDescription)
149 func TestPostSecurityIdTokenFunctionNotRegistered(t *testing.T) {
150 invokerRegisterMock := invokermocks.InvokerRegister{}
151 invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
152 invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
153 serviceRegisterMock := servicemocks.ServiceRegister{}
154 serviceRegisterMock.On("IsFunctionRegistered", mock.AnythingOfType("string")).Return(false)
156 requestHandler := getEcho(&serviceRegisterMock, nil, &invokerRegisterMock, nil)
159 data.Set("client_id", "id")
160 data.Add("client_secret", "secret")
161 data.Add("grant_type", "client_credentials")
162 data.Add("scope", "3gpp#aefId:path")
163 encodedData := data.Encode()
165 result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
167 assert.Equal(t, http.StatusBadRequest, result.Code())
168 var errDetails securityapi.AccessTokenErr
169 err := result.UnmarshalBodyToObject(&errDetails)
170 assert.NoError(t, err, "error unmarshaling response")
171 assert.Equal(t, securityapi.AccessTokenErrErrorInvalidScope, errDetails.Error)
172 errMsg := "AEF Function not registered"
173 assert.Equal(t, &errMsg, errDetails.ErrorDescription)
176 func TestPostSecurityIdTokenAPINotPublished(t *testing.T) {
177 invokerRegisterMock := invokermocks.InvokerRegister{}
178 invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
179 invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
180 serviceRegisterMock := servicemocks.ServiceRegister{}
181 serviceRegisterMock.On("IsFunctionRegistered", mock.AnythingOfType("string")).Return(true)
182 publishRegisterMock := publishmocks.PublishRegister{}
183 publishRegisterMock.On("IsAPIPublished", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(false)
185 requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, nil)
188 data.Set("client_id", "id")
189 data.Add("client_secret", "secret")
190 data.Add("grant_type", "client_credentials")
191 data.Add("scope", "3gpp#aefId:path")
192 encodedData := data.Encode()
194 result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
196 assert.Equal(t, http.StatusBadRequest, result.Code())
197 var errDetails securityapi.AccessTokenErr
198 err := result.UnmarshalBodyToObject(&errDetails)
199 assert.NoError(t, err, "error unmarshaling response")
200 assert.Equal(t, securityapi.AccessTokenErrErrorInvalidScope, errDetails.Error)
201 errMsg := "API not published"
202 assert.Equal(t, &errMsg, errDetails.ErrorDescription)
205 func TestPostSecurityIdTokenInvokerInvalidCredentials(t *testing.T) {
206 invokerRegisterMock := invokermocks.InvokerRegister{}
207 invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
208 invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
209 serviceRegisterMock := servicemocks.ServiceRegister{}
210 serviceRegisterMock.On("IsFunctionRegistered", mock.AnythingOfType("string")).Return(true)
211 publishRegisterMock := publishmocks.PublishRegister{}
212 publishRegisterMock.On("IsAPIPublished", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
214 jwt := keycloak.Jwttoken{}
215 accessMgmMock := keycloackmocks.AccessManagement{}
216 accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(jwt, errors.New("invalid_credentials"))
218 requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock)
222 clientSecret := "secret"
225 data.Set("client_id", clientId)
226 data.Set("client_secret", clientSecret)
227 data.Set("grant_type", "client_credentials")
228 data.Set("scope", "3gpp#"+aefId+":"+path)
230 encodedData := data.Encode()
232 result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
234 assert.Equal(t, http.StatusBadRequest, result.Code())
235 var resultResponse securityapi.AccessTokenErr
236 err := result.UnmarshalBodyToObject(&resultResponse)
237 assert.NoError(t, err, "error unmarshaling response")
238 invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", clientId)
239 invokerRegisterMock.AssertCalled(t, "VerifyInvokerSecret", clientId, clientSecret)
240 serviceRegisterMock.AssertCalled(t, "IsFunctionRegistered", aefId)
241 publishRegisterMock.AssertCalled(t, "IsAPIPublished", aefId, path)
242 accessMgmMock.AssertCalled(t, "GetToken", clientId, clientSecret, "3gpp#"+aefId+":"+path, "invokerrealm")
245 func TestPutTrustedInvokerSuccessfully(t *testing.T) {
246 invokerRegisterMock := invokermocks.InvokerRegister{}
247 invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
249 aefProfile := getAefProfile(aefId)
250 aefProfile.SecurityMethods = &[]publishserviceapi.SecurityMethod{
251 publishserviceapi.SecurityMethodPKI,
253 aefProfiles := []publishserviceapi.AefProfile{
257 publishedServices := []publishserviceapi.ServiceAPIDescription{
260 AefProfiles: &aefProfiles,
263 publishRegisterMock := publishmocks.PublishRegister{}
264 publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices)
266 requestHandler := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil)
268 invokerId := "invokerId"
269 serviceSecurityUnderTest := getServiceSecurity(aefId, apiId)
270 serviceSecurityUnderTest.SecurityInfo[0].ApiId = &apiId
272 result := testutil.NewRequest().Put("/trustedInvokers/"+invokerId).WithJsonBody(serviceSecurityUnderTest).Go(t, requestHandler)
274 assert.Equal(t, http.StatusCreated, result.Code())
275 var resultResponse securityapi.ServiceSecurity
276 err := result.UnmarshalBodyToObject(&resultResponse)
277 assert.NoError(t, err, "error unmarshaling response")
278 assert.NotEmpty(t, resultResponse.NotificationDestination)
280 for _, security := range resultResponse.SecurityInfo {
281 assert.Equal(t, *security.ApiId, apiId)
282 assert.Equal(t, *security.SelSecurityMethod, publishserviceapi.SecurityMethodPKI)
284 invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId)
288 func TestPutTrustedInkoverNotRegistered(t *testing.T) {
289 invokerRegisterMock := invokermocks.InvokerRegister{}
290 invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(false)
292 requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil)
294 invokerId := "invokerId"
295 serviceSecurityUnderTest := getServiceSecurity("aefId", "apiId")
297 result := testutil.NewRequest().Put("/trustedInvokers/"+invokerId).WithJsonBody(serviceSecurityUnderTest).Go(t, requestHandler)
299 badRequest := http.StatusBadRequest
300 assert.Equal(t, badRequest, result.Code())
301 var problemDetails common29122.ProblemDetails
302 err := result.UnmarshalBodyToObject(&problemDetails)
303 assert.NoError(t, err, "error unmarshaling response")
304 assert.Equal(t, &badRequest, problemDetails.Status)
305 assert.Contains(t, *problemDetails.Cause, "Invoker not registered")
306 invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId)
309 func TestPutTrustedInkoverInvalidInputServiceSecurity(t *testing.T) {
310 invokerRegisterMock := invokermocks.InvokerRegister{}
311 invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
313 requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil)
315 invokerId := "invokerId"
316 notificationUrl := "url"
317 serviceSecurityUnderTest := getServiceSecurity("aefId", "apiId")
318 serviceSecurityUnderTest.NotificationDestination = common29122.Uri(notificationUrl)
320 result := testutil.NewRequest().Put("/trustedInvokers/"+invokerId).WithJsonBody(serviceSecurityUnderTest).Go(t, requestHandler)
322 badRequest := http.StatusBadRequest
323 assert.Equal(t, badRequest, result.Code())
324 var problemDetails common29122.ProblemDetails
325 err := result.UnmarshalBodyToObject(&problemDetails)
326 assert.NoError(t, err, "error unmarshaling response")
327 assert.Equal(t, &badRequest, problemDetails.Status)
328 assert.Contains(t, *problemDetails.Cause, "ServiceSecurity has invalid notificationDestination")
329 invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId)
332 func TestPutTrustedInvokerInterfaceDetailsNotNil(t *testing.T) {
333 invokerRegisterMock := invokermocks.InvokerRegister{}
334 invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
336 aefProfile := getAefProfile(aefId)
337 aefProfile.SecurityMethods = &[]publishserviceapi.SecurityMethod{
338 publishserviceapi.SecurityMethodPKI,
340 aefProfiles := []publishserviceapi.AefProfile{
344 publishedServices := []publishserviceapi.ServiceAPIDescription{
347 AefProfiles: &aefProfiles,
350 publishRegisterMock := publishmocks.PublishRegister{}
351 publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices)
353 requestHandler := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil)
355 invokerId := "invokerId"
356 serviceSecurityUnderTest := getServiceSecurity(aefId, apiId)
357 serviceSecurityUnderTest.SecurityInfo[0] = securityapi.SecurityInformation{
359 PrefSecurityMethods: []publishserviceapi.SecurityMethod{
360 publishserviceapi.SecurityMethodOAUTH,
362 InterfaceDetails: &publishserviceapi.InterfaceDescription{
363 SecurityMethods: &[]publishserviceapi.SecurityMethod{
364 publishserviceapi.SecurityMethodPSK,
369 result := testutil.NewRequest().Put("/trustedInvokers/"+invokerId).WithJsonBody(serviceSecurityUnderTest).Go(t, requestHandler)
371 assert.Equal(t, http.StatusCreated, result.Code())
372 var resultResponse securityapi.ServiceSecurity
373 err := result.UnmarshalBodyToObject(&resultResponse)
374 assert.NoError(t, err, "error unmarshaling response")
375 assert.NotEmpty(t, resultResponse.NotificationDestination)
377 for _, security := range resultResponse.SecurityInfo {
378 assert.Equal(t, apiId, *security.ApiId)
379 assert.Equal(t, publishserviceapi.SecurityMethodPSK, *security.SelSecurityMethod)
381 invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId)
385 func TestPutTrustedInvokerNotFoundSecurityMethod(t *testing.T) {
386 invokerRegisterMock := invokermocks.InvokerRegister{}
387 invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
389 aefProfiles := []publishserviceapi.AefProfile{
390 getAefProfile("aefId"),
393 publishedServices := []publishserviceapi.ServiceAPIDescription{
396 AefProfiles: &aefProfiles,
399 publishRegisterMock := publishmocks.PublishRegister{}
400 publishRegisterMock.On("GetAllPublishedServices").Return(publishedServices)
402 requestHandler := getEcho(nil, &publishRegisterMock, &invokerRegisterMock, nil)
404 invokerId := "invokerId"
405 serviceSecurityUnderTest := getServiceSecurity("aefId", "apiId")
407 result := testutil.NewRequest().Put("/trustedInvokers/"+invokerId).WithJsonBody(serviceSecurityUnderTest).Go(t, requestHandler)
409 badRequest := http.StatusBadRequest
410 assert.Equal(t, badRequest, result.Code())
411 var problemDetails common29122.ProblemDetails
412 err := result.UnmarshalBodyToObject(&problemDetails)
413 assert.NoError(t, err, "error unmarshaling response")
414 assert.Equal(t, &badRequest, problemDetails.Status)
415 assert.Contains(t, *problemDetails.Cause, "not found")
416 assert.Contains(t, *problemDetails.Cause, "security method")
417 invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", invokerId)
420 func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister, keycloakMgm keycloak.AccessManagement) *echo.Echo {
421 swagger, err := securityapi.GetSwagger()
423 fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
427 swagger.Servers = nil
429 s := NewSecurity(serviceRegister, publishRegister, invokerRegister, keycloakMgm)
432 e.Use(echomiddleware.Logger())
433 e.Use(middleware.OapiRequestValidator(swagger))
435 securityapi.RegisterHandlers(e, s)
439 func getServiceSecurity(aefId string, apiId string) securityapi.ServiceSecurity {
440 return securityapi.ServiceSecurity{
441 NotificationDestination: common29122.Uri("http://golang.cafe/"),
442 SecurityInfo: []securityapi.SecurityInformation{
446 PrefSecurityMethods: []publishserviceapi.SecurityMethod{
447 publishserviceapi.SecurityMethodOAUTH,
454 func getAefProfile(aefId string) publishserviceapi.AefProfile {
455 return publishserviceapi.AefProfile{
457 Versions: []publishserviceapi.Version{
459 Resources: &[]publishserviceapi.Resource{
461 CommType: "REQUEST_RESPONSE",