Generating token using keycloak
[nonrtric/plt/sme.git] / capifcore / internal / securityservice / security_test.go
1 // -
2 //   ========================LICENSE_START=================================
3 //   O-RAN-SC
4 //   %%
5 //   Copyright (C) 2022: Nordix Foundation
6 //   %%
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
10 //
11 //        http://www.apache.org/licenses/LICENSE-2.0
12 //
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===================================
19 //
20
21 package security
22
23 import (
24         "errors"
25         "fmt"
26         "net/http"
27         "net/url"
28         "os"
29         "testing"
30
31         "oransc.org/nonrtric/capifcore/internal/keycloak"
32         "oransc.org/nonrtric/capifcore/internal/securityapi"
33
34         "oransc.org/nonrtric/capifcore/internal/invokermanagement"
35         "oransc.org/nonrtric/capifcore/internal/providermanagement"
36         "oransc.org/nonrtric/capifcore/internal/publishservice"
37
38         "github.com/labstack/echo/v4"
39
40         invokermocks "oransc.org/nonrtric/capifcore/internal/invokermanagement/mocks"
41         keycloackmocks "oransc.org/nonrtric/capifcore/internal/keycloak/mocks"
42         servicemocks "oransc.org/nonrtric/capifcore/internal/providermanagement/mocks"
43         publishmocks "oransc.org/nonrtric/capifcore/internal/publishservice/mocks"
44
45         "github.com/deepmap/oapi-codegen/pkg/middleware"
46         "github.com/deepmap/oapi-codegen/pkg/testutil"
47         echomiddleware "github.com/labstack/echo/v4/middleware"
48         "github.com/stretchr/testify/assert"
49         "github.com/stretchr/testify/mock"
50 )
51
52 func TestPostSecurityIdTokenInvokerRegistered(t *testing.T) {
53         invokerRegisterMock := invokermocks.InvokerRegister{}
54         invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
55         invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
56         serviceRegisterMock := servicemocks.ServiceRegister{}
57         serviceRegisterMock.On("IsFunctionRegistered", mock.AnythingOfType("string")).Return(true)
58         publishRegisterMock := publishmocks.PublishRegister{}
59         publishRegisterMock.On("IsAPIPublished", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
60
61         jwt := keycloak.Jwttoken{
62                 AccessToken: "eyJhbGNIn0.e3YTQ0xLjEifQ.FcqCwCy7iJiOmw",
63                 ExpiresIn:   300,
64                 Scope:       "3gpp#aefIdpath",
65         }
66         accessMgmMock := keycloackmocks.AccessManagement{}
67         accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(jwt, nil)
68
69         requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock)
70
71         data := url.Values{}
72         clientId := "id"
73         clientSecret := "secret"
74         aefId := "aefId"
75         path := "path"
76         data.Set("client_id", clientId)
77         data.Set("client_secret", clientSecret)
78         data.Set("grant_type", "client_credentials")
79         data.Set("scope", "3gpp#"+aefId+":"+path)
80
81         encodedData := data.Encode()
82
83         result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
84
85         assert.Equal(t, http.StatusCreated, result.Code())
86         var resultResponse securityapi.AccessTokenRsp
87         err := result.UnmarshalBodyToObject(&resultResponse)
88         assert.NoError(t, err, "error unmarshaling response")
89         assert.NotEmpty(t, resultResponse.AccessToken)
90         assert.Equal(t, securityapi.AccessTokenRspTokenTypeBearer, resultResponse.TokenType)
91         invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", clientId)
92         invokerRegisterMock.AssertCalled(t, "VerifyInvokerSecret", clientId, clientSecret)
93         serviceRegisterMock.AssertCalled(t, "IsFunctionRegistered", aefId)
94         publishRegisterMock.AssertCalled(t, "IsAPIPublished", aefId, path)
95         accessMgmMock.AssertCalled(t, "GetToken", clientId, clientSecret, "3gpp#"+aefId+":"+path, "invokerrealm")
96 }
97
98 func TestPostSecurityIdTokenInvokerNotRegistered(t *testing.T) {
99         invokerRegisterMock := invokermocks.InvokerRegister{}
100         invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(false)
101
102         requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil)
103
104         data := url.Values{}
105         data.Set("client_id", "id")
106         data.Add("client_secret", "secret")
107         data.Add("grant_type", "client_credentials")
108         data.Add("scope", "3gpp#aefId:path")
109         encodedData := data.Encode()
110
111         result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
112
113         assert.Equal(t, http.StatusBadRequest, result.Code())
114         var errDetails securityapi.AccessTokenErr
115         err := result.UnmarshalBodyToObject(&errDetails)
116         assert.NoError(t, err, "error unmarshaling response")
117         assert.Equal(t, securityapi.AccessTokenErrErrorInvalidClient, errDetails.Error)
118         errMsg := "Invoker not registered"
119         assert.Equal(t, &errMsg, errDetails.ErrorDescription)
120 }
121
122 func TestPostSecurityIdTokenInvokerSecretNotValid(t *testing.T) {
123         invokerRegisterMock := invokermocks.InvokerRegister{}
124         invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
125         invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(false)
126
127         requestHandler := getEcho(nil, nil, &invokerRegisterMock, nil)
128
129         data := url.Values{}
130         data.Set("client_id", "id")
131         data.Add("client_secret", "secret")
132         data.Add("grant_type", "client_credentials")
133         data.Add("scope", "3gpp#aefId:path")
134         encodedData := data.Encode()
135
136         result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
137
138         assert.Equal(t, http.StatusBadRequest, result.Code())
139         var errDetails securityapi.AccessTokenErr
140         err := result.UnmarshalBodyToObject(&errDetails)
141         assert.NoError(t, err, "error unmarshaling response")
142         assert.Equal(t, securityapi.AccessTokenErrErrorUnauthorizedClient, errDetails.Error)
143         errMsg := "Invoker secret not valid"
144         assert.Equal(t, &errMsg, errDetails.ErrorDescription)
145 }
146
147 func TestPostSecurityIdTokenFunctionNotRegistered(t *testing.T) {
148         invokerRegisterMock := invokermocks.InvokerRegister{}
149         invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
150         invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
151         serviceRegisterMock := servicemocks.ServiceRegister{}
152         serviceRegisterMock.On("IsFunctionRegistered", mock.AnythingOfType("string")).Return(false)
153
154         requestHandler := getEcho(&serviceRegisterMock, nil, &invokerRegisterMock, nil)
155
156         data := url.Values{}
157         data.Set("client_id", "id")
158         data.Add("client_secret", "secret")
159         data.Add("grant_type", "client_credentials")
160         data.Add("scope", "3gpp#aefId:path")
161         encodedData := data.Encode()
162
163         result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
164
165         assert.Equal(t, http.StatusBadRequest, result.Code())
166         var errDetails securityapi.AccessTokenErr
167         err := result.UnmarshalBodyToObject(&errDetails)
168         assert.NoError(t, err, "error unmarshaling response")
169         assert.Equal(t, securityapi.AccessTokenErrErrorInvalidScope, errDetails.Error)
170         errMsg := "AEF Function not registered"
171         assert.Equal(t, &errMsg, errDetails.ErrorDescription)
172 }
173
174 func TestPostSecurityIdTokenAPINotPublished(t *testing.T) {
175         invokerRegisterMock := invokermocks.InvokerRegister{}
176         invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
177         invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
178         serviceRegisterMock := servicemocks.ServiceRegister{}
179         serviceRegisterMock.On("IsFunctionRegistered", mock.AnythingOfType("string")).Return(true)
180         publishRegisterMock := publishmocks.PublishRegister{}
181         publishRegisterMock.On("IsAPIPublished", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(false)
182
183         requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, nil)
184
185         data := url.Values{}
186         data.Set("client_id", "id")
187         data.Add("client_secret", "secret")
188         data.Add("grant_type", "client_credentials")
189         data.Add("scope", "3gpp#aefId:path")
190         encodedData := data.Encode()
191
192         result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
193
194         assert.Equal(t, http.StatusBadRequest, result.Code())
195         var errDetails securityapi.AccessTokenErr
196         err := result.UnmarshalBodyToObject(&errDetails)
197         assert.NoError(t, err, "error unmarshaling response")
198         assert.Equal(t, securityapi.AccessTokenErrErrorInvalidScope, errDetails.Error)
199         errMsg := "API not published"
200         assert.Equal(t, &errMsg, errDetails.ErrorDescription)
201 }
202
203 func TestPostSecurityIdTokenInvokerInvalidCredentials(t *testing.T) {
204         invokerRegisterMock := invokermocks.InvokerRegister{}
205         invokerRegisterMock.On("IsInvokerRegistered", mock.AnythingOfType("string")).Return(true)
206         invokerRegisterMock.On("VerifyInvokerSecret", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
207         serviceRegisterMock := servicemocks.ServiceRegister{}
208         serviceRegisterMock.On("IsFunctionRegistered", mock.AnythingOfType("string")).Return(true)
209         publishRegisterMock := publishmocks.PublishRegister{}
210         publishRegisterMock.On("IsAPIPublished", mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(true)
211
212         jwt := keycloak.Jwttoken{}
213         accessMgmMock := keycloackmocks.AccessManagement{}
214         accessMgmMock.On("GetToken", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(jwt, errors.New("invalid_credentials"))
215
216         requestHandler := getEcho(&serviceRegisterMock, &publishRegisterMock, &invokerRegisterMock, &accessMgmMock)
217
218         data := url.Values{}
219         clientId := "id"
220         clientSecret := "secret"
221         aefId := "aefId"
222         path := "path"
223         data.Set("client_id", clientId)
224         data.Set("client_secret", clientSecret)
225         data.Set("grant_type", "client_credentials")
226         data.Set("scope", "3gpp#"+aefId+":"+path)
227
228         encodedData := data.Encode()
229
230         result := testutil.NewRequest().Post("/securities/invokerId/token").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(encodedData)).Go(t, requestHandler)
231
232         assert.Equal(t, http.StatusBadRequest, result.Code())
233         var resultResponse securityapi.AccessTokenErr
234         err := result.UnmarshalBodyToObject(&resultResponse)
235         assert.NoError(t, err, "error unmarshaling response")
236         invokerRegisterMock.AssertCalled(t, "IsInvokerRegistered", clientId)
237         invokerRegisterMock.AssertCalled(t, "VerifyInvokerSecret", clientId, clientSecret)
238         serviceRegisterMock.AssertCalled(t, "IsFunctionRegistered", aefId)
239         publishRegisterMock.AssertCalled(t, "IsAPIPublished", aefId, path)
240         accessMgmMock.AssertCalled(t, "GetToken", clientId, clientSecret, "3gpp#"+aefId+":"+path, "invokerrealm")
241 }
242
243 func getEcho(serviceRegister providermanagement.ServiceRegister, publishRegister publishservice.PublishRegister, invokerRegister invokermanagement.InvokerRegister, keycloakMgm keycloak.AccessManagement) *echo.Echo {
244         swagger, err := securityapi.GetSwagger()
245         if err != nil {
246                 fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
247                 os.Exit(1)
248         }
249
250         swagger.Servers = nil
251
252         s := NewSecurity(serviceRegister, publishRegister, invokerRegister, keycloakMgm)
253
254         e := echo.New()
255         e.Use(echomiddleware.Logger())
256         e.Use(middleware.OapiRequestValidator(swagger))
257
258         securityapi.RegisterHandlers(e, s)
259         return e
260 }