github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/onetimetoken/service_test.go (about) 1 package onetimetoken_test 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/base64" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "io" 11 "net/http" 12 "reflect" 13 "testing" 14 "time" 15 16 "github.com/kyma-incubator/compass/components/director/internal/domain/scenariogroups" 17 18 tenantpkg "github.com/kyma-incubator/compass/components/director/pkg/tenant" 19 20 pkgadapters "github.com/kyma-incubator/compass/components/director/pkg/adapters" 21 22 pkgmodel "github.com/kyma-incubator/compass/components/director/pkg/model" 23 24 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 25 26 "github.com/kyma-incubator/compass/components/director/internal/domain/onetimetoken" 27 "github.com/kyma-incubator/compass/components/director/internal/domain/onetimetoken/automock" 28 "github.com/kyma-incubator/compass/components/director/internal/model" 29 "github.com/kyma-incubator/compass/components/director/internal/tokens" 30 "github.com/kyma-incubator/compass/components/director/pkg/graphql" 31 "github.com/kyma-incubator/compass/components/director/pkg/header" 32 "github.com/kyma-incubator/compass/components/director/pkg/pairing" 33 "github.com/kyma-incubator/compass/components/director/pkg/str" 34 directorTime "github.com/kyma-incubator/compass/components/director/pkg/time" 35 timeMocks "github.com/kyma-incubator/compass/components/director/pkg/time/automock" 36 "github.com/stretchr/testify/assert" 37 "github.com/stretchr/testify/mock" 38 ) 39 40 var runtimeID = "runtimeID" 41 42 var nowTime = time.Now() 43 44 func TestGenerateOneTimeToken(t *testing.T) { 45 const ( 46 tokenValue = "abc" 47 connectorURL = "connector.url" 48 legacyTokenURL = connectorURL + "?token=" + tokenValue 49 appID = "4c86b315-c027-467f-a6fc-b184ca0a80f1" 50 appName = "test-app-name" 51 runtimeID = "31a607c7-695f-4a31-b2d1-777939f84aac" 52 integrationSystemID = "123607c7-695f-4a31-b2d1-777939f84123" 53 54 suggestedTokenHeaderKey = "suggest_token" 55 ctxScenarioGroupKey scenariogroups.Key = "scenarioGroups" 56 ) 57 58 fakeToken := &model.OneTimeToken{ 59 Used: false, 60 UsedAt: time.Time{}, 61 Token: tokenValue, 62 ConnectorURL: connectorURL, 63 } 64 65 headers := http.Header{} 66 headers.Add(suggestedTokenHeaderKey, "true") 67 68 ctxScenarioGroups := "test_scenario_group" 69 ctxScenarioGroupsValue := []string{"test_scenario_group"} 70 71 subaccountExternalID := "sub-external-tenant" 72 subaccountInternalID := "sub-test-tenant" 73 contextSubaccountWithEnabledSuggestion := context.WithValue(context.TODO(), header.ContextKey, headers) 74 contextSubaccountWithEnabledSuggestion = tenant.SaveToContext(contextSubaccountWithEnabledSuggestion, subaccountInternalID, subaccountExternalID) 75 contextSubaccountWithEnabledSuggestionAndScenarioGroups := context.WithValue(contextSubaccountWithEnabledSuggestion, ctxScenarioGroupKey, ctxScenarioGroupsValue) 76 77 ctxWithSubaccount := tenant.SaveToContext(context.TODO(), subaccountInternalID, subaccountExternalID) 78 ctxWithSubaccountAndScenarioGroups := context.WithValue(ctxWithSubaccount, ctxScenarioGroupKey, ctxScenarioGroupsValue) 79 80 gaExternalID := "ga-external-tenant" 81 gaInternalID := "ga-test-tenant" 82 ctxWithGlobalAccount := tenant.SaveToContext(context.TODO(), gaInternalID, gaExternalID) 83 ctxWithGlobalAccountAndScenarioGroups := context.WithValue(ctxWithGlobalAccount, ctxScenarioGroupKey, ctxScenarioGroupsValue) 84 ctxGlobalAccountWithoutExternalTenant := tenant.SaveToContext(context.TODO(), gaInternalID, "") 85 ctxGlobalAccountWithoutExternalTenantAndScenarioGroups := context.WithValue(ctxGlobalAccountWithoutExternalTenant, ctxScenarioGroupKey, ctxScenarioGroupsValue) 86 87 contextGlobalAccountWithEnabledSuggestion := context.WithValue(context.TODO(), header.ContextKey, headers) 88 contextGlobalAccountWithEnabledSuggestion = tenant.SaveToContext(contextGlobalAccountWithEnabledSuggestion, gaInternalID, gaExternalID) 89 contextGlobalAccountWithEnabledSuggestionAndScenarioGroups := context.WithValue(contextGlobalAccountWithEnabledSuggestion, ctxScenarioGroupKey, ctxScenarioGroupsValue) 90 91 subaccountMapping := &model.BusinessTenantMapping{ 92 ID: subaccountInternalID, 93 Name: "subaccount", 94 ExternalTenant: subaccountExternalID, 95 Parent: gaInternalID, 96 Type: tenantpkg.Subaccount, 97 } 98 99 gaMapping := &model.BusinessTenantMapping{ 100 ID: gaInternalID, 101 Name: "ga", 102 ExternalTenant: gaExternalID, 103 Parent: "", 104 Type: tenantpkg.Account, 105 } 106 107 ottConfig := onetimetoken.Config{ 108 ConnectorURL: connectorURL, 109 LegacyConnectorURL: connectorURL, 110 SuggestTokenHeaderKey: suggestedTokenHeaderKey, 111 } 112 113 pairingAdaptersWithMapping := pkgadapters.Adapters{ 114 Mapping: map[string]string{integrationSystemID: "https://my-integration-service.url"}, 115 } 116 117 pairingAdaptersWithEmptyMapping := pkgadapters.Adapters{ 118 Mapping: map[string]string{}, 119 } 120 121 pairingAdaptersWithNilMapping := pkgadapters.Adapters{ 122 Mapping: nil, 123 } 124 125 testCases := []struct { 126 description string 127 objectID string 128 ctx context.Context 129 connectorURL string 130 shouldHaveError bool 131 errorMsg string 132 tokenType pkgmodel.SystemAuthReferenceObjectType 133 expectedToken interface{} 134 pairingAdapters *pkgadapters.Adapters 135 systemAuthSvc func() onetimetoken.SystemAuthService 136 appSvc func() onetimetoken.ApplicationService 137 appConverter func() onetimetoken.ApplicationConverter 138 tenantSvc func() onetimetoken.ExternalTenantsService 139 httpClient func() onetimetoken.HTTPDoer 140 tokenGenerator func() onetimetoken.TokenGenerator 141 timeService directorTime.Service 142 }{ 143 { 144 description: "Generate Application token, no int system, should succeed", 145 ctx: ctxWithSubaccountAndScenarioGroups, 146 systemAuthSvc: func() onetimetoken.SystemAuthService { 147 systemAuthSvc := &automock.SystemAuthService{} 148 systemAuthSvc.On("Create", ctxWithSubaccountAndScenarioGroups, pkgmodel.ApplicationReference, appID, mock.MatchedBy(func(authInput *model.AuthInput) bool { 149 return authInput.OneTimeToken.Token == tokenValue 150 })).Return("", nil) 151 return systemAuthSvc 152 }, 153 appSvc: func() onetimetoken.ApplicationService { 154 appSvc := &automock.ApplicationService{} 155 appSvc.On("Get", ctxWithSubaccountAndScenarioGroups, appID).Return(&model.Application{BaseEntity: &model.BaseEntity{ID: appID}}, nil) 156 return appSvc 157 }, 158 appConverter: func() onetimetoken.ApplicationConverter { 159 return &automock.ApplicationConverter{} 160 }, 161 tenantSvc: func() onetimetoken.ExternalTenantsService { 162 return &automock.ExternalTenantsService{} 163 }, 164 httpClient: func() onetimetoken.HTTPDoer { 165 return &automock.HTTPDoer{} 166 }, 167 tokenGenerator: func() onetimetoken.TokenGenerator { 168 tokenGenerator := &automock.TokenGenerator{} 169 tokenGenerator.On("NewToken").Return(tokenValue, nil) 170 return tokenGenerator 171 }, 172 shouldHaveError: false, 173 objectID: appID, 174 tokenType: pkgmodel.ApplicationReference, 175 connectorURL: connectorURL, 176 pairingAdapters: nil, 177 timeService: directorTime.NewService(), 178 expectedToken: tokenValue, 179 }, 180 { 181 description: "Generate Application token, no int system, with suggestion enabled, should succeed", 182 ctx: contextSubaccountWithEnabledSuggestionAndScenarioGroups, 183 systemAuthSvc: func() onetimetoken.SystemAuthService { 184 systemAuthSvc := &automock.SystemAuthService{} 185 systemAuthSvc.On("Create", contextSubaccountWithEnabledSuggestionAndScenarioGroups, pkgmodel.ApplicationReference, appID, mock.MatchedBy(func(authInput *model.AuthInput) bool { 186 return authInput.OneTimeToken.Token == tokenValue 187 })).Return("", nil) 188 return systemAuthSvc 189 }, 190 appSvc: func() onetimetoken.ApplicationService { 191 appSvc := &automock.ApplicationService{} 192 appSvc.On("Get", contextSubaccountWithEnabledSuggestionAndScenarioGroups, appID).Return(&model.Application{BaseEntity: &model.BaseEntity{ID: appID}}, nil) 193 appSvc.On("ListLabels", contextSubaccountWithEnabledSuggestionAndScenarioGroups, appID).Return(map[string]*model.Label{}, nil) 194 return appSvc 195 }, 196 appConverter: func() onetimetoken.ApplicationConverter { 197 return &automock.ApplicationConverter{} 198 }, 199 tenantSvc: func() onetimetoken.ExternalTenantsService { 200 return &automock.ExternalTenantsService{} 201 }, 202 httpClient: func() onetimetoken.HTTPDoer { 203 return &automock.HTTPDoer{} 204 }, 205 tokenGenerator: func() onetimetoken.TokenGenerator { 206 tokenGenerator := &automock.TokenGenerator{} 207 tokenGenerator.On("NewToken").Return(tokenValue, nil) 208 return tokenGenerator 209 }, 210 shouldHaveError: false, 211 objectID: appID, 212 tokenType: pkgmodel.ApplicationReference, 213 connectorURL: connectorURL, 214 pairingAdapters: nil, 215 timeService: &Timer{}, 216 expectedToken: func() string { 217 nowT := nowTime.Add(ottConfig.ApplicationExpiration) 218 converted, err := nowT.MarshalJSON() 219 assert.NoError(t, err) 220 221 nowStr, err := nowTime.MarshalJSON() 222 assert.NoError(t, err) 223 224 defaultTimeStr, err := time.Time{}.MarshalJSON() 225 assert.NoError(t, err) 226 return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`{"token":"abc","connectorURL":"connector.url","used":false,"expiresAt":%s,"createdAt":%s,"usedAt":%s,"type":"%s","scenario_groups":["%s"]}`, string(converted), string(nowStr), string(defaultTimeStr), tokens.ApplicationToken, ctxScenarioGroups))) 227 }, 228 }, 229 { 230 description: "Generate Application token for legacy application, with suggestion enabled, should succeed", 231 ctx: contextSubaccountWithEnabledSuggestionAndScenarioGroups, 232 systemAuthSvc: func() onetimetoken.SystemAuthService { 233 systemAuthSvc := &automock.SystemAuthService{} 234 systemAuthSvc.On("Create", contextSubaccountWithEnabledSuggestionAndScenarioGroups, pkgmodel.ApplicationReference, appID, mock.MatchedBy(func(authInput *model.AuthInput) bool { 235 return authInput.OneTimeToken.Token == tokenValue 236 })).Return("", nil) 237 return systemAuthSvc 238 }, 239 appSvc: func() onetimetoken.ApplicationService { 240 appSvc := &automock.ApplicationService{} 241 appSvc.On("Get", contextSubaccountWithEnabledSuggestionAndScenarioGroups, appID).Return(&model.Application{BaseEntity: &model.BaseEntity{ID: appID}}, nil) 242 appSvc.On("ListLabels", contextSubaccountWithEnabledSuggestionAndScenarioGroups, appID).Return(map[string]*model.Label{"legacy": {Value: true}}, nil) 243 return appSvc 244 }, 245 appConverter: func() onetimetoken.ApplicationConverter { 246 return &automock.ApplicationConverter{} 247 }, 248 tenantSvc: func() onetimetoken.ExternalTenantsService { 249 return &automock.ExternalTenantsService{} 250 }, 251 httpClient: func() onetimetoken.HTTPDoer { 252 return &automock.HTTPDoer{} 253 }, 254 tokenGenerator: func() onetimetoken.TokenGenerator { 255 tokenGenerator := &automock.TokenGenerator{} 256 tokenGenerator.On("NewToken").Return(tokenValue, nil) 257 return tokenGenerator 258 }, 259 shouldHaveError: false, 260 objectID: appID, 261 tokenType: pkgmodel.ApplicationReference, 262 connectorURL: connectorURL, 263 pairingAdapters: nil, 264 timeService: directorTime.NewService(), 265 expectedToken: legacyTokenURL, 266 }, 267 { 268 description: "Generate Application token should fail when no such app found", 269 ctx: ctxWithSubaccount, 270 systemAuthSvc: func() onetimetoken.SystemAuthService { 271 return &automock.SystemAuthService{} 272 }, 273 appSvc: func() onetimetoken.ApplicationService { 274 appSvc := &automock.ApplicationService{} 275 appSvc.On("Get", ctxWithSubaccount, appID).Return(nil, errors.New("not found")) 276 return appSvc 277 }, 278 appConverter: func() onetimetoken.ApplicationConverter { 279 return &automock.ApplicationConverter{} 280 }, 281 tenantSvc: func() onetimetoken.ExternalTenantsService { 282 return &automock.ExternalTenantsService{} 283 }, 284 httpClient: func() onetimetoken.HTTPDoer { 285 return &automock.HTTPDoer{} 286 }, 287 tokenGenerator: func() onetimetoken.TokenGenerator { 288 return &automock.TokenGenerator{} 289 }, 290 shouldHaveError: true, 291 objectID: appID, 292 tokenType: pkgmodel.ApplicationReference, 293 errorMsg: "not found", 294 connectorURL: connectorURL, 295 pairingAdapters: nil, 296 }, 297 { 298 description: "Generate Application token should fail when pairing adapter mapping is nil", 299 ctx: ctxWithSubaccount, 300 systemAuthSvc: func() onetimetoken.SystemAuthService { 301 return &automock.SystemAuthService{} 302 }, 303 appSvc: func() onetimetoken.ApplicationService { 304 app := &model.Application{} 305 app.IntegrationSystemID = str.Ptr(integrationSystemID) 306 app.BaseEntity = &model.BaseEntity{ 307 ID: appID, 308 } 309 appSvc := &automock.ApplicationService{} 310 appSvc.On("Get", ctxWithSubaccount, appID).Return(app, nil) 311 return appSvc 312 }, 313 appConverter: func() onetimetoken.ApplicationConverter { 314 return &automock.ApplicationConverter{} 315 }, 316 tenantSvc: func() onetimetoken.ExternalTenantsService { 317 return &automock.ExternalTenantsService{} 318 }, 319 httpClient: func() onetimetoken.HTTPDoer { 320 return &automock.HTTPDoer{} 321 }, 322 tokenGenerator: func() onetimetoken.TokenGenerator { 323 return &automock.TokenGenerator{} 324 }, 325 shouldHaveError: true, 326 objectID: appID, 327 tokenType: pkgmodel.ApplicationReference, 328 errorMsg: "pairing adapter configuration mapping cannot be nil", 329 connectorURL: connectorURL, 330 pairingAdapters: &pairingAdaptersWithNilMapping, 331 }, 332 { 333 description: "Generate Application token should fail on db error", 334 ctx: ctxWithSubaccountAndScenarioGroups, 335 systemAuthSvc: func() onetimetoken.SystemAuthService { 336 systemAuthSvc := &automock.SystemAuthService{} 337 systemAuthSvc.On("Create", ctxWithSubaccountAndScenarioGroups, pkgmodel.ApplicationReference, appID, mock.MatchedBy(func(authInput *model.AuthInput) bool { 338 return authInput.OneTimeToken.Token == tokenValue 339 })).Return("", errors.New("db error")) 340 return systemAuthSvc 341 }, 342 appSvc: func() onetimetoken.ApplicationService { 343 appSvc := &automock.ApplicationService{} 344 appSvc.On("Get", ctxWithSubaccountAndScenarioGroups, appID).Return(&model.Application{}, nil) 345 return appSvc 346 }, 347 appConverter: func() onetimetoken.ApplicationConverter { 348 return &automock.ApplicationConverter{} 349 }, 350 tenantSvc: func() onetimetoken.ExternalTenantsService { 351 return &automock.ExternalTenantsService{} 352 }, 353 httpClient: func() onetimetoken.HTTPDoer { 354 return &automock.HTTPDoer{} 355 }, 356 tokenGenerator: func() onetimetoken.TokenGenerator { 357 tokenGenerator := &automock.TokenGenerator{} 358 tokenGenerator.On("NewToken").Return(tokenValue, nil) 359 return tokenGenerator 360 }, 361 shouldHaveError: true, 362 objectID: appID, 363 tokenType: pkgmodel.ApplicationReference, 364 errorMsg: "db error", 365 connectorURL: connectorURL, 366 pairingAdapters: nil, 367 timeService: directorTime.NewService(), 368 }, 369 { 370 description: "Generate Application token, with int system, should succeed when global account external tenant is in the context", 371 ctx: ctxWithGlobalAccountAndScenarioGroups, 372 systemAuthSvc: func() onetimetoken.SystemAuthService { 373 systemAuthSvc := &automock.SystemAuthService{} 374 systemAuthSvc.On("Create", ctxWithGlobalAccountAndScenarioGroups, pkgmodel.ApplicationReference, appID, mock.MatchedBy(func(authInput *model.AuthInput) bool { 375 return authInput.OneTimeToken.Token == tokenValue 376 })).Return("", nil) 377 return systemAuthSvc 378 }, 379 appSvc: func() onetimetoken.ApplicationService { 380 app := &model.Application{BaseEntity: &model.BaseEntity{ID: appID}} 381 app.Name = appName 382 app.IntegrationSystemID = str.Ptr(integrationSystemID) 383 appSvc := &automock.ApplicationService{} 384 appSvc.On("Get", ctxWithGlobalAccountAndScenarioGroups, appID).Return(app, nil) 385 return appSvc 386 }, 387 appConverter: func() onetimetoken.ApplicationConverter { 388 mockAppConverter := &automock.ApplicationConverter{} 389 givenGraphQLApp := graphql.Application{ 390 IntegrationSystemID: str.Ptr(integrationSystemID), 391 BaseEntity: &graphql.BaseEntity{ 392 ID: appID, 393 }, 394 } 395 mockAppConverter.On("ToGraphQL", mock.Anything).Return(&givenGraphQLApp) 396 return mockAppConverter 397 }, 398 tenantSvc: func() onetimetoken.ExternalTenantsService { 399 tenantSvc := &automock.ExternalTenantsService{} 400 tenantSvc.On("GetTenantByID", ctxWithGlobalAccountAndScenarioGroups, gaInternalID).Return(gaMapping, nil) 401 return tenantSvc 402 }, 403 httpClient: func() onetimetoken.HTTPDoer { 404 respBody := new(bytes.Buffer) 405 respBody.WriteString(fmt.Sprintf(`{"token":"%s"}`, tokenValue)) 406 mockHTTPClient := &automock.HTTPDoer{} 407 response := &http.Response{ 408 StatusCode: http.StatusOK, 409 Body: io.NopCloser(respBody), 410 } 411 mockHTTPClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 412 b, err := req.GetBody() 413 if err != nil { 414 return false 415 } 416 appData := pairing.RequestData{} 417 err = json.NewDecoder(b).Decode(&appData) 418 if err != nil { 419 return false 420 } 421 tenantMatches := appData.Tenant == gaExternalID 422 clientUserMatches := appData.ClientUser == "" 423 appIDMatches := appData.Application.ID == appID 424 urlMatches := req.URL.String() == "https://my-integration-service.url" 425 426 return urlMatches && appIDMatches && tenantMatches && clientUserMatches 427 })).Return(response, nil) 428 return mockHTTPClient 429 }, 430 tokenGenerator: func() onetimetoken.TokenGenerator { 431 return &automock.TokenGenerator{} 432 }, 433 shouldHaveError: false, 434 objectID: appID, 435 tokenType: pkgmodel.ApplicationReference, 436 expectedToken: tokenValue, 437 connectorURL: connectorURL, 438 pairingAdapters: &pairingAdaptersWithMapping, 439 timeService: directorTime.NewService(), 440 }, 441 { 442 description: "Generate Application token, with int system, should succeed when subaccount external tenant is in the context", 443 ctx: ctxWithSubaccountAndScenarioGroups, 444 systemAuthSvc: func() onetimetoken.SystemAuthService { 445 systemAuthSvc := &automock.SystemAuthService{} 446 systemAuthSvc.On("Create", ctxWithSubaccountAndScenarioGroups, pkgmodel.ApplicationReference, appID, mock.MatchedBy(func(authInput *model.AuthInput) bool { 447 return authInput.OneTimeToken.Token == tokenValue 448 })).Return("", nil) 449 return systemAuthSvc 450 }, 451 appSvc: func() onetimetoken.ApplicationService { 452 app := &model.Application{BaseEntity: &model.BaseEntity{ID: appID}} 453 app.Name = appName 454 app.IntegrationSystemID = str.Ptr(integrationSystemID) 455 appSvc := &automock.ApplicationService{} 456 appSvc.On("Get", ctxWithSubaccountAndScenarioGroups, appID).Return(app, nil) 457 return appSvc 458 }, 459 appConverter: func() onetimetoken.ApplicationConverter { 460 mockAppConverter := &automock.ApplicationConverter{} 461 givenGraphQLApp := graphql.Application{ 462 IntegrationSystemID: str.Ptr(integrationSystemID), 463 BaseEntity: &graphql.BaseEntity{ 464 ID: appID, 465 }, 466 } 467 mockAppConverter.On("ToGraphQL", mock.Anything).Return(&givenGraphQLApp) 468 return mockAppConverter 469 }, 470 tenantSvc: func() onetimetoken.ExternalTenantsService { 471 tenantSvc := &automock.ExternalTenantsService{} 472 tenantSvc.On("GetTenantByID", ctxWithSubaccountAndScenarioGroups, subaccountInternalID).Return(subaccountMapping, nil) 473 tenantSvc.On("GetTenantByID", ctxWithSubaccountAndScenarioGroups, gaInternalID).Return(gaMapping, nil) 474 return tenantSvc 475 }, 476 httpClient: func() onetimetoken.HTTPDoer { 477 respBody := new(bytes.Buffer) 478 respBody.WriteString(fmt.Sprintf(`{"token":"%s"}`, tokenValue)) 479 mockHTTPClient := &automock.HTTPDoer{} 480 response := &http.Response{ 481 StatusCode: http.StatusOK, 482 Body: io.NopCloser(respBody), 483 } 484 mockHTTPClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 485 b, err := req.GetBody() 486 if err != nil { 487 return false 488 } 489 appData := pairing.RequestData{} 490 err = json.NewDecoder(b).Decode(&appData) 491 if err != nil { 492 return false 493 } 494 tenantMatches := appData.Tenant == gaExternalID 495 clientUserMatches := appData.ClientUser == "" 496 appIDMatches := appData.Application.ID == appID 497 urlMatches := req.URL.String() == "https://my-integration-service.url" 498 499 return urlMatches && appIDMatches && tenantMatches && clientUserMatches 500 })).Return(response, nil) 501 return mockHTTPClient 502 }, 503 tokenGenerator: func() onetimetoken.TokenGenerator { 504 return &automock.TokenGenerator{} 505 }, 506 shouldHaveError: false, 507 objectID: appID, 508 tokenType: pkgmodel.ApplicationReference, 509 expectedToken: tokenValue, 510 connectorURL: connectorURL, 511 pairingAdapters: &pairingAdaptersWithMapping, 512 timeService: directorTime.NewService(), 513 }, 514 { 515 description: "Generate Application token, with int system, should succeed when global account external tenant is not in the context", 516 ctx: ctxGlobalAccountWithoutExternalTenantAndScenarioGroups, 517 systemAuthSvc: func() onetimetoken.SystemAuthService { 518 systemAuthSvc := &automock.SystemAuthService{} 519 systemAuthSvc.On("Create", ctxGlobalAccountWithoutExternalTenantAndScenarioGroups, pkgmodel.ApplicationReference, appID, mock.MatchedBy(func(authInput *model.AuthInput) bool { 520 return authInput.OneTimeToken.Token == tokenValue 521 })).Return("", nil) 522 return systemAuthSvc 523 }, 524 appSvc: func() onetimetoken.ApplicationService { 525 app := &model.Application{BaseEntity: &model.BaseEntity{ID: appID}} 526 app.Name = appName 527 app.IntegrationSystemID = str.Ptr(integrationSystemID) 528 appSvc := &automock.ApplicationService{} 529 appSvc.On("Get", ctxGlobalAccountWithoutExternalTenantAndScenarioGroups, appID).Return(app, nil) 530 return appSvc 531 }, 532 appConverter: func() onetimetoken.ApplicationConverter { 533 mockAppConverter := &automock.ApplicationConverter{} 534 givenGraphQLApp := graphql.Application{ 535 IntegrationSystemID: str.Ptr(integrationSystemID), 536 BaseEntity: &graphql.BaseEntity{ 537 ID: appID, 538 }, 539 } 540 mockAppConverter.On("ToGraphQL", mock.Anything).Return(&givenGraphQLApp) 541 return mockAppConverter 542 }, 543 tenantSvc: func() onetimetoken.ExternalTenantsService { 544 tenantSvc := &automock.ExternalTenantsService{} 545 tenantSvc.On("GetTenantByID", ctxGlobalAccountWithoutExternalTenantAndScenarioGroups, gaInternalID).Return(gaMapping, nil) 546 return tenantSvc 547 }, 548 httpClient: func() onetimetoken.HTTPDoer { 549 respBody := new(bytes.Buffer) 550 respBody.WriteString(fmt.Sprintf(`{"token":"%s"}`, tokenValue)) 551 mockHTTPClient := &automock.HTTPDoer{} 552 response := &http.Response{ 553 StatusCode: http.StatusOK, 554 Body: io.NopCloser(respBody), 555 } 556 mockHTTPClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 557 b, err := req.GetBody() 558 if err != nil { 559 return false 560 } 561 appData := pairing.RequestData{} 562 err = json.NewDecoder(b).Decode(&appData) 563 if err != nil { 564 return false 565 } 566 tenantMatches := appData.Tenant == gaExternalID 567 clientUserMatches := appData.ClientUser == "" 568 appIDMatches := appData.Application.ID == appID 569 urlMatches := req.URL.String() == "https://my-integration-service.url" 570 571 return urlMatches && appIDMatches && tenantMatches && clientUserMatches 572 })).Return(response, nil) 573 return mockHTTPClient 574 }, 575 tokenGenerator: func() onetimetoken.TokenGenerator { 576 return &automock.TokenGenerator{} 577 }, 578 shouldHaveError: false, 579 objectID: appID, 580 tokenType: pkgmodel.ApplicationReference, 581 expectedToken: tokenValue, 582 connectorURL: connectorURL, 583 pairingAdapters: &pairingAdaptersWithMapping, 584 timeService: directorTime.NewService(), 585 }, 586 { 587 description: "Generate Application token, with int system, and token suggestion enabled, should succeed", 588 ctx: contextGlobalAccountWithEnabledSuggestionAndScenarioGroups, 589 systemAuthSvc: func() onetimetoken.SystemAuthService { 590 systemAuthSvc := &automock.SystemAuthService{} 591 systemAuthSvc.On("Create", contextGlobalAccountWithEnabledSuggestionAndScenarioGroups, pkgmodel.ApplicationReference, appID, mock.MatchedBy(func(authInput *model.AuthInput) bool { 592 return authInput.OneTimeToken.Token == tokenValue 593 })).Return("", nil) 594 return systemAuthSvc 595 }, 596 appSvc: func() onetimetoken.ApplicationService { 597 app := &model.Application{BaseEntity: &model.BaseEntity{ID: appID}} 598 app.IntegrationSystemID = str.Ptr(integrationSystemID) 599 appSvc := &automock.ApplicationService{} 600 appSvc.On("Get", contextGlobalAccountWithEnabledSuggestionAndScenarioGroups, appID).Return(app, nil) 601 return appSvc 602 }, 603 appConverter: func() onetimetoken.ApplicationConverter { 604 mockAppConverter := &automock.ApplicationConverter{} 605 givenGraphQLApp := graphql.Application{ 606 IntegrationSystemID: str.Ptr(integrationSystemID), 607 BaseEntity: &graphql.BaseEntity{ 608 ID: appID, 609 }, 610 } 611 mockAppConverter.On("ToGraphQL", mock.Anything).Return(&givenGraphQLApp) 612 return mockAppConverter 613 }, 614 tenantSvc: func() onetimetoken.ExternalTenantsService { 615 tenantSvc := &automock.ExternalTenantsService{} 616 tenantSvc.On("GetTenantByID", contextGlobalAccountWithEnabledSuggestionAndScenarioGroups, gaInternalID).Return(gaMapping, nil) 617 return tenantSvc 618 }, 619 httpClient: func() onetimetoken.HTTPDoer { 620 respBody := new(bytes.Buffer) 621 respBody.WriteString(fmt.Sprintf(`{"token":"%s"}`, tokenValue)) 622 mockHTTPClient := &automock.HTTPDoer{} 623 response := &http.Response{ 624 StatusCode: http.StatusOK, 625 Body: io.NopCloser(respBody), 626 } 627 mockHTTPClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 628 b, err := req.GetBody() 629 if err != nil { 630 return false 631 } 632 appData := pairing.RequestData{} 633 err = json.NewDecoder(b).Decode(&appData) 634 if err != nil { 635 return false 636 } 637 tenantMatches := appData.Tenant == gaExternalID 638 clientUserMatches := appData.ClientUser == "" 639 appIDMatches := appData.Application.ID == appID 640 urlMatches := req.URL.String() == "https://my-integration-service.url" 641 642 return urlMatches && appIDMatches && tenantMatches && clientUserMatches 643 })).Return(response, nil) 644 return mockHTTPClient 645 }, 646 tokenGenerator: func() onetimetoken.TokenGenerator { 647 return &automock.TokenGenerator{} 648 }, 649 shouldHaveError: false, 650 objectID: appID, 651 tokenType: pkgmodel.ApplicationReference, 652 expectedToken: tokenValue, 653 connectorURL: connectorURL, 654 pairingAdapters: &pairingAdaptersWithMapping, 655 timeService: directorTime.NewService(), 656 }, 657 { 658 description: "Generate Application token, with int system, but no adapters defined should succeed", 659 ctx: ctxWithSubaccountAndScenarioGroups, 660 systemAuthSvc: func() onetimetoken.SystemAuthService { 661 systemAuthSvc := &automock.SystemAuthService{} 662 systemAuthSvc.On("Create", ctxWithSubaccountAndScenarioGroups, pkgmodel.ApplicationReference, appID, mock.MatchedBy(func(authInput *model.AuthInput) bool { 663 return authInput.OneTimeToken.Token == tokenValue 664 })).Return("", nil) 665 return systemAuthSvc 666 }, 667 appSvc: func() onetimetoken.ApplicationService { 668 app := &model.Application{} 669 app.IntegrationSystemID = str.Ptr(integrationSystemID) 670 appSvc := &automock.ApplicationService{} 671 appSvc.On("Get", ctxWithSubaccountAndScenarioGroups, appID).Return(app, nil) 672 return appSvc 673 }, 674 appConverter: func() onetimetoken.ApplicationConverter { 675 return &automock.ApplicationConverter{} 676 }, 677 tenantSvc: func() onetimetoken.ExternalTenantsService { 678 return &automock.ExternalTenantsService{} 679 }, 680 httpClient: func() onetimetoken.HTTPDoer { 681 return &automock.HTTPDoer{} 682 }, 683 tokenGenerator: func() onetimetoken.TokenGenerator { 684 tokenGenerator := &automock.TokenGenerator{} 685 tokenGenerator.On("NewToken").Return(tokenValue, nil) 686 return tokenGenerator 687 }, 688 shouldHaveError: false, 689 objectID: appID, 690 tokenType: pkgmodel.ApplicationReference, 691 expectedToken: tokenValue, 692 connectorURL: connectorURL, 693 pairingAdapters: &pairingAdaptersWithEmptyMapping, 694 timeService: directorTime.NewService(), 695 }, 696 { 697 description: "Generate Application token, with int system, should fail when int system fails", 698 ctx: ctxWithSubaccount, 699 systemAuthSvc: func() onetimetoken.SystemAuthService { 700 return &automock.SystemAuthService{} 701 }, 702 appSvc: func() onetimetoken.ApplicationService { 703 app := &model.Application{} 704 app.IntegrationSystemID = str.Ptr(integrationSystemID) 705 app.BaseEntity = &model.BaseEntity{ 706 ID: appID, 707 } 708 appSvc := &automock.ApplicationService{} 709 appSvc.On("Get", ctxWithSubaccount, appID).Return(app, nil) 710 return appSvc 711 }, 712 appConverter: func() onetimetoken.ApplicationConverter { 713 mockAppConverter := &automock.ApplicationConverter{} 714 givenGraphQLApp := graphql.Application{ 715 IntegrationSystemID: str.Ptr(integrationSystemID), 716 BaseEntity: &graphql.BaseEntity{ 717 ID: appID, 718 }, 719 } 720 mockAppConverter.On("ToGraphQL", mock.Anything).Return(&givenGraphQLApp) 721 return mockAppConverter 722 }, 723 tenantSvc: func() onetimetoken.ExternalTenantsService { 724 tenantSvc := &automock.ExternalTenantsService{} 725 tenantSvc.On("GetTenantByID", ctxWithSubaccount, subaccountInternalID).Return(subaccountMapping, nil) 726 tenantSvc.On("GetTenantByID", ctxWithSubaccount, gaInternalID).Return(gaMapping, nil) 727 return tenantSvc 728 }, 729 httpClient: func() onetimetoken.HTTPDoer { 730 mockHTTPClient := &automock.HTTPDoer{} 731 response := &http.Response{ 732 StatusCode: http.StatusInternalServerError, 733 Body: io.NopCloser(&bytes.Buffer{}), 734 } 735 mockHTTPClient.On("Do", mock.MatchedBy(func(req *http.Request) bool { 736 b, err := req.GetBody() 737 if err != nil { 738 return false 739 } 740 appData := pairing.RequestData{} 741 err = json.NewDecoder(b).Decode(&appData) 742 if err != nil { 743 return false 744 } 745 tenantMatches := appData.Tenant == gaExternalID 746 clientUserMatches := appData.ClientUser == "" 747 appIDMatches := appData.Application.ID == appID 748 urlMatches := req.URL.String() == "https://my-integration-service.url" 749 750 return urlMatches && appIDMatches && tenantMatches && clientUserMatches 751 })).Return(response, nil).Times(3) 752 return mockHTTPClient 753 }, 754 tokenGenerator: func() onetimetoken.TokenGenerator { 755 return &automock.TokenGenerator{} 756 }, 757 shouldHaveError: true, 758 objectID: appID, 759 tokenType: pkgmodel.ApplicationReference, 760 errorMsg: "wrong status code", 761 connectorURL: connectorURL, 762 pairingAdapters: &pairingAdaptersWithMapping, 763 }, 764 { 765 description: "Generate Application token, with int system, should fail when no tenant in the context", 766 ctx: context.TODO(), 767 systemAuthSvc: func() onetimetoken.SystemAuthService { 768 return &automock.SystemAuthService{} 769 }, 770 appSvc: func() onetimetoken.ApplicationService { 771 app := &model.Application{} 772 app.IntegrationSystemID = str.Ptr(integrationSystemID) 773 app.BaseEntity = &model.BaseEntity{ 774 ID: appID, 775 } 776 appSvc := &automock.ApplicationService{} 777 appSvc.On("Get", context.TODO(), appID).Return(app, nil) 778 return appSvc 779 }, 780 appConverter: func() onetimetoken.ApplicationConverter { 781 return &automock.ApplicationConverter{} 782 }, 783 tenantSvc: func() onetimetoken.ExternalTenantsService { 784 tenantSvc := &automock.ExternalTenantsService{} 785 return tenantSvc 786 }, 787 httpClient: func() onetimetoken.HTTPDoer { 788 return &automock.HTTPDoer{} 789 }, 790 tokenGenerator: func() onetimetoken.TokenGenerator { 791 return &automock.TokenGenerator{} 792 }, 793 shouldHaveError: true, 794 objectID: appID, 795 tokenType: pkgmodel.ApplicationReference, 796 errorMsg: "cannot read tenant from context", 797 connectorURL: connectorURL, 798 pairingAdapters: &pairingAdaptersWithMapping, 799 }, 800 { 801 description: "Generate Application token, with int system, should fail when can't get global account tenant by internal ID", 802 ctx: ctxGlobalAccountWithoutExternalTenant, 803 systemAuthSvc: func() onetimetoken.SystemAuthService { 804 return &automock.SystemAuthService{} 805 }, 806 appSvc: func() onetimetoken.ApplicationService { 807 app := &model.Application{} 808 app.IntegrationSystemID = str.Ptr(integrationSystemID) 809 app.BaseEntity = &model.BaseEntity{ 810 ID: appID, 811 } 812 appSvc := &automock.ApplicationService{} 813 appSvc.On("Get", ctxGlobalAccountWithoutExternalTenant, appID).Return(app, nil) 814 return appSvc 815 }, 816 appConverter: func() onetimetoken.ApplicationConverter { 817 return &automock.ApplicationConverter{} 818 }, 819 tenantSvc: func() onetimetoken.ExternalTenantsService { 820 tenantSvc := &automock.ExternalTenantsService{} 821 tenantSvc.On("GetTenantByID", ctxGlobalAccountWithoutExternalTenant, gaInternalID).Return(nil, errors.New("some-error")) 822 return tenantSvc 823 }, 824 httpClient: func() onetimetoken.HTTPDoer { 825 return &automock.HTTPDoer{} 826 }, 827 tokenGenerator: func() onetimetoken.TokenGenerator { 828 return &automock.TokenGenerator{} 829 }, 830 shouldHaveError: true, 831 objectID: appID, 832 tokenType: pkgmodel.ApplicationReference, 833 errorMsg: "some-error", 834 connectorURL: connectorURL, 835 pairingAdapters: &pairingAdaptersWithMapping, 836 }, 837 { 838 description: "Generate Application token, with int system, should fail when can't get parent tenant", 839 ctx: ctxWithSubaccount, 840 systemAuthSvc: func() onetimetoken.SystemAuthService { 841 return &automock.SystemAuthService{} 842 }, 843 appSvc: func() onetimetoken.ApplicationService { 844 app := &model.Application{} 845 app.IntegrationSystemID = str.Ptr(integrationSystemID) 846 app.BaseEntity = &model.BaseEntity{ 847 ID: appID, 848 } 849 appSvc := &automock.ApplicationService{} 850 appSvc.On("Get", ctxWithSubaccount, appID).Return(app, nil) 851 return appSvc 852 }, 853 appConverter: func() onetimetoken.ApplicationConverter { 854 return &automock.ApplicationConverter{} 855 }, 856 tenantSvc: func() onetimetoken.ExternalTenantsService { 857 tenantSvc := &automock.ExternalTenantsService{} 858 tenantSvc.On("GetTenantByID", ctxWithSubaccount, subaccountInternalID).Return(subaccountMapping, nil) 859 tenantSvc.On("GetTenantByID", ctxWithSubaccount, gaInternalID).Return(&model.BusinessTenantMapping{}, errors.New("some-error")) 860 return tenantSvc 861 }, 862 httpClient: func() onetimetoken.HTTPDoer { 863 return &automock.HTTPDoer{} 864 }, 865 tokenGenerator: func() onetimetoken.TokenGenerator { 866 return &automock.TokenGenerator{} 867 }, 868 shouldHaveError: true, 869 objectID: appID, 870 tokenType: pkgmodel.ApplicationReference, 871 errorMsg: "some-error", 872 connectorURL: connectorURL, 873 pairingAdapters: &pairingAdaptersWithMapping, 874 }, 875 { 876 description: "Generate Application token, should fail on token generating error", 877 ctx: ctxWithSubaccount, 878 systemAuthSvc: func() onetimetoken.SystemAuthService { 879 return &automock.SystemAuthService{} 880 }, 881 appSvc: func() onetimetoken.ApplicationService { 882 appSvc := &automock.ApplicationService{} 883 appSvc.On("Get", ctxWithSubaccount, appID).Return(&model.Application{}, nil) 884 return appSvc 885 }, 886 appConverter: func() onetimetoken.ApplicationConverter { 887 return &automock.ApplicationConverter{} 888 }, 889 tenantSvc: func() onetimetoken.ExternalTenantsService { 890 return &automock.ExternalTenantsService{} 891 }, 892 httpClient: func() onetimetoken.HTTPDoer { 893 return &automock.HTTPDoer{} 894 }, 895 tokenGenerator: func() onetimetoken.TokenGenerator { 896 tokenGenerator := &automock.TokenGenerator{} 897 tokenGenerator.On("NewToken").Return("", errors.New("error generating token")) 898 return tokenGenerator 899 }, 900 shouldHaveError: true, 901 objectID: appID, 902 tokenType: pkgmodel.ApplicationReference, 903 errorMsg: "error generating token", 904 connectorURL: connectorURL, 905 pairingAdapters: nil, 906 }, 907 { 908 description: "Generate Runtime token should succeed", 909 ctx: ctxWithSubaccountAndScenarioGroups, 910 systemAuthSvc: func() onetimetoken.SystemAuthService { 911 systemAuthSvc := &automock.SystemAuthService{} 912 systemAuthSvc.On("Create", ctxWithSubaccountAndScenarioGroups, pkgmodel.RuntimeReference, runtimeID, mock.MatchedBy(func(authInput *model.AuthInput) bool { 913 return authInput.OneTimeToken.Token == tokenValue 914 })).Return("", nil) 915 return systemAuthSvc 916 }, 917 appSvc: func() onetimetoken.ApplicationService { 918 return &automock.ApplicationService{} 919 }, 920 appConverter: func() onetimetoken.ApplicationConverter { 921 return &automock.ApplicationConverter{} 922 }, 923 tenantSvc: func() onetimetoken.ExternalTenantsService { 924 return &automock.ExternalTenantsService{} 925 }, 926 httpClient: func() onetimetoken.HTTPDoer { 927 return &automock.HTTPDoer{} 928 }, 929 tokenGenerator: func() onetimetoken.TokenGenerator { 930 tokenGenerator := &automock.TokenGenerator{} 931 tokenGenerator.On("NewToken").Return(tokenValue, nil) 932 return tokenGenerator 933 }, 934 shouldHaveError: false, 935 objectID: runtimeID, 936 tokenType: pkgmodel.RuntimeReference, 937 expectedToken: tokenValue, 938 connectorURL: connectorURL, 939 pairingAdapters: nil, 940 timeService: directorTime.NewService(), 941 }, 942 { 943 description: "Generate Runtime token should fail on token generating error", 944 ctx: ctxWithSubaccount, 945 systemAuthSvc: func() onetimetoken.SystemAuthService { 946 return &automock.SystemAuthService{} 947 }, 948 appSvc: func() onetimetoken.ApplicationService { 949 return &automock.ApplicationService{} 950 }, 951 appConverter: func() onetimetoken.ApplicationConverter { 952 return &automock.ApplicationConverter{} 953 }, 954 tenantSvc: func() onetimetoken.ExternalTenantsService { 955 return &automock.ExternalTenantsService{} 956 }, 957 httpClient: func() onetimetoken.HTTPDoer { 958 return &automock.HTTPDoer{} 959 }, 960 tokenGenerator: func() onetimetoken.TokenGenerator { 961 tokenGenerator := &automock.TokenGenerator{} 962 tokenGenerator.On("NewToken").Return("", errors.New("error generating token")) 963 return tokenGenerator 964 }, 965 shouldHaveError: true, 966 objectID: runtimeID, 967 tokenType: pkgmodel.RuntimeReference, 968 errorMsg: "error generating token", 969 connectorURL: connectorURL, 970 pairingAdapters: nil, 971 }, 972 { 973 description: "Generate Runtime token should fail on db error", 974 ctx: ctxWithSubaccountAndScenarioGroups, 975 systemAuthSvc: func() onetimetoken.SystemAuthService { 976 systemAuthSvc := &automock.SystemAuthService{} 977 systemAuthSvc.On("Create", ctxWithSubaccountAndScenarioGroups, pkgmodel.RuntimeReference, runtimeID, mock.MatchedBy(func(authInput *model.AuthInput) bool { 978 return authInput.OneTimeToken.Token == tokenValue 979 })).Return("", errors.New("db error")) 980 return systemAuthSvc 981 }, 982 appSvc: func() onetimetoken.ApplicationService { 983 return &automock.ApplicationService{} 984 }, 985 appConverter: func() onetimetoken.ApplicationConverter { 986 return &automock.ApplicationConverter{} 987 }, 988 tenantSvc: func() onetimetoken.ExternalTenantsService { 989 return &automock.ExternalTenantsService{} 990 }, 991 httpClient: func() onetimetoken.HTTPDoer { 992 return &automock.HTTPDoer{} 993 }, 994 tokenGenerator: func() onetimetoken.TokenGenerator { 995 tokenGenerator := &automock.TokenGenerator{} 996 tokenGenerator.On("NewToken").Return(tokenValue, nil) 997 return tokenGenerator 998 }, 999 shouldHaveError: true, 1000 objectID: runtimeID, 1001 tokenType: pkgmodel.RuntimeReference, 1002 errorMsg: "db error", 1003 connectorURL: connectorURL, 1004 pairingAdapters: nil, 1005 timeService: directorTime.NewService(), 1006 }, 1007 } 1008 1009 for _, test := range testCases { 1010 t.Run(test.description, func(t *testing.T) { 1011 // GIVEN 1012 systemAuthSvc := test.systemAuthSvc() 1013 appSvc := test.appSvc() 1014 appConverter := test.appConverter() 1015 tenantSvc := test.tenantSvc() 1016 httpClient := test.httpClient() 1017 tokenGenerator := test.tokenGenerator() 1018 timeService := test.timeService 1019 1020 tokenSvc := onetimetoken.NewTokenService(systemAuthSvc, appSvc, appConverter, tenantSvc, httpClient, tokenGenerator, ottConfig, test.pairingAdapters, timeService) 1021 1022 // WHEN 1023 token, err := tokenSvc.GenerateOneTimeToken(test.ctx, test.objectID, test.tokenType) 1024 // THEN 1025 if test.shouldHaveError { 1026 assert.Error(t, err) 1027 assert.Contains(t, err.Error(), test.errorMsg) 1028 assert.Empty(t, token) 1029 } else { 1030 assert.NoError(t, err) 1031 if test.tokenType == pkgmodel.ApplicationReference { 1032 assert.Equal(t, tokens.ApplicationToken, token.Type) 1033 } else { 1034 assert.Equal(t, tokens.RuntimeToken, token.Type) 1035 } 1036 var expectedToken string 1037 if reflect.TypeOf(test.expectedToken).Kind() == reflect.Func { 1038 f, ok := test.expectedToken.(func() string) 1039 assert.True(t, ok) 1040 expectedToken = f() 1041 } else { 1042 var ok bool 1043 expectedToken, ok = test.expectedToken.(string) 1044 assert.True(t, ok) 1045 } 1046 assert.Equal(t, expectedToken, token.Token) 1047 assert.Equal(t, fakeToken.UsedAt, token.UsedAt) 1048 assert.Equal(t, fakeToken.Used, token.Used) 1049 if test.pairingAdapters == nil { 1050 assert.Equal(t, fakeToken.ConnectorURL, token.ConnectorURL) 1051 } 1052 } 1053 mock.AssertExpectationsForObjects(t, systemAuthSvc, appSvc, appConverter, tenantSvc, httpClient, tokenGenerator) 1054 }) 1055 } 1056 } 1057 1058 func TestRegenerateOneTimeToken(t *testing.T) { 1059 const ( 1060 systemAuthID = "123" 1061 connectorURL = "http://connector.url" 1062 legacyConnectorURL = "http://connector.url" 1063 token = "YWJj" 1064 1065 ctxScenarioGroupsValue = "test_scenario_group" 1066 ) 1067 const ctxScenarioGroupKey scenariogroups.Key = "scenarioGroups" 1068 scenarioGroups := []string{ctxScenarioGroupsValue} 1069 1070 ottConfig := onetimetoken.Config{ 1071 ConnectorURL: connectorURL, 1072 LegacyConnectorURL: connectorURL, 1073 } 1074 1075 ctxBackgroundWithScenarioGroups := context.WithValue(context.Background(), ctxScenarioGroupKey, scenarioGroups) 1076 1077 t.Run("fails when systemAuth cannot be fetched", func(t *testing.T) { 1078 // GIVEN 1079 sysAuthSvc := &automock.SystemAuthService{} 1080 tokenGenerator := &automock.TokenGenerator{} 1081 timeService := &timeMocks.Service{} 1082 pairingAdapters := &pkgadapters.Adapters{} 1083 1084 sysAuthSvc.On("GetGlobal", context.Background(), systemAuthID).Return(nil, errors.New("error while fetching")) 1085 defer sysAuthSvc.AssertExpectations(t) 1086 1087 tokenService := onetimetoken.NewTokenService(sysAuthSvc, &automock.ApplicationService{}, &automock.ApplicationConverter{}, &automock.ExternalTenantsService{}, 1088 &automock.HTTPDoer{}, tokenGenerator, ottConfig, pairingAdapters, timeService) 1089 1090 // WHEN 1091 token, err := tokenService.RegenerateOneTimeToken(context.Background(), systemAuthID) 1092 1093 // THEN 1094 assert.Nil(t, token) 1095 assert.Error(t, err) 1096 assert.Contains(t, err.Error(), "error while fetching") 1097 }) 1098 1099 t.Run("fails when new token cannot be generated", func(t *testing.T) { 1100 // GIVEN 1101 sysAuthSvc := &automock.SystemAuthService{} 1102 tokenGenerator := &automock.TokenGenerator{} 1103 timeService := &timeMocks.Service{} 1104 pairingAdapters := &pkgadapters.Adapters{} 1105 sysAuthSvc.On("GetGlobal", context.Background(), systemAuthID).Return(&pkgmodel.SystemAuth{RuntimeID: &runtimeID, Value: &model.Auth{}}, nil) 1106 defer sysAuthSvc.AssertExpectations(t) 1107 tokenGenerator.On("NewToken").Return("", errors.New("error while token generating")) 1108 defer tokenGenerator.AssertExpectations(t) 1109 1110 tokenService := onetimetoken.NewTokenService(sysAuthSvc, &automock.ApplicationService{}, &automock.ApplicationConverter{}, &automock.ExternalTenantsService{}, 1111 &automock.HTTPDoer{}, tokenGenerator, ottConfig, pairingAdapters, timeService) 1112 1113 // WHEN 1114 token, err := tokenService.RegenerateOneTimeToken(context.Background(), systemAuthID) 1115 1116 // THEN 1117 assert.Nil(t, token) 1118 assert.Error(t, err) 1119 assert.Contains(t, err.Error(), "while generating onetime token for Runtime: error while token generating") 1120 }) 1121 1122 t.Run("fails when systemAuth cannot be updated", func(t *testing.T) { 1123 // GIVEN 1124 updateErrMsg := "error while updating" 1125 sysAuthSvc := &automock.SystemAuthService{} 1126 tokenGenerator := &automock.TokenGenerator{} 1127 timeService := &timeMocks.Service{} 1128 pairingAdapters := &pkgadapters.Adapters{} 1129 1130 timeService.On("Now").Return(time.Now()) 1131 sysAuthSvc.On("GetGlobal", ctxBackgroundWithScenarioGroups, systemAuthID).Return(&pkgmodel.SystemAuth{RuntimeID: &runtimeID, Value: &model.Auth{}}, nil) 1132 sysAuthSvc.On("Update", ctxBackgroundWithScenarioGroups, mock.Anything).Return(errors.New(updateErrMsg)) 1133 defer sysAuthSvc.AssertExpectations(t) 1134 1135 tokenGenerator.On("NewToken").Return(token, nil) 1136 defer tokenGenerator.AssertExpectations(t) 1137 1138 tokenService := onetimetoken.NewTokenService(sysAuthSvc, &automock.ApplicationService{}, &automock.ApplicationConverter{}, &automock.ExternalTenantsService{}, 1139 &automock.HTTPDoer{}, tokenGenerator, ottConfig, pairingAdapters, timeService) 1140 1141 // WHEN 1142 token, err := tokenService.RegenerateOneTimeToken(ctxBackgroundWithScenarioGroups, systemAuthID) 1143 1144 // THEN 1145 assert.Nil(t, token) 1146 assert.Error(t, err) 1147 assert.Contains(t, err.Error(), updateErrMsg) 1148 }) 1149 1150 t.Run("succeeds when systemAuth has missing 'value' value", func(t *testing.T) { 1151 // GIVEN 1152 sysAuthSvc := &automock.SystemAuthService{} 1153 tokenGenerator := &automock.TokenGenerator{} 1154 timeService := &timeMocks.Service{} 1155 pairingAdapters := &pkgadapters.Adapters{} 1156 now := time.Now() 1157 timeService.On("Now").Return(now) 1158 1159 sysAuthSvc.On("GetGlobal", ctxBackgroundWithScenarioGroups, systemAuthID).Return(&pkgmodel.SystemAuth{RuntimeID: &runtimeID}, nil) 1160 sysAuthSvc.On("Update", ctxBackgroundWithScenarioGroups, mock.Anything).Return(nil) 1161 defer sysAuthSvc.AssertExpectations(t) 1162 1163 tokenGenerator.On("NewToken").Return(token, nil) 1164 defer tokenGenerator.AssertExpectations(t) 1165 1166 tokenService := onetimetoken.NewTokenService(sysAuthSvc, &automock.ApplicationService{}, &automock.ApplicationConverter{}, &automock.ExternalTenantsService{}, 1167 &automock.HTTPDoer{}, tokenGenerator, ottConfig, pairingAdapters, timeService) 1168 expectedToken := &model.OneTimeToken{ 1169 Token: token, 1170 ConnectorURL: connectorURL, 1171 Type: tokens.RuntimeToken, 1172 CreatedAt: now, 1173 Used: false, 1174 ExpiresAt: now.Add(ottConfig.ApplicationExpiration), 1175 UsedAt: time.Time{}, 1176 ScenarioGroups: scenarioGroups, 1177 } 1178 1179 // WHEN 1180 token, err := tokenService.RegenerateOneTimeToken(ctxBackgroundWithScenarioGroups, systemAuthID) 1181 1182 // THEN 1183 assert.Equal(t, expectedToken, token) 1184 assert.NoError(t, err) 1185 }) 1186 1187 t.Run("succeeds when no errors are thrown", func(t *testing.T) { 1188 // GIVEN 1189 sysAuthSvc := &automock.SystemAuthService{} 1190 tokenGenerator := &automock.TokenGenerator{} 1191 timeService := &timeMocks.Service{} 1192 pairingAdapters := &pkgadapters.Adapters{} 1193 now := time.Now() 1194 timeService.On("Now").Return(now) 1195 1196 sysAuthSvc.On("GetGlobal", ctxBackgroundWithScenarioGroups, systemAuthID).Return(&pkgmodel.SystemAuth{RuntimeID: &runtimeID, Value: &model.Auth{}}, nil) 1197 sysAuthSvc.On("Update", ctxBackgroundWithScenarioGroups, mock.Anything).Return(nil) 1198 defer sysAuthSvc.AssertExpectations(t) 1199 1200 tokenGenerator.On("NewToken").Return(token, nil) 1201 defer tokenGenerator.AssertExpectations(t) 1202 expectedToken := &model.OneTimeToken{ 1203 Token: token, 1204 ConnectorURL: connectorURL, 1205 Type: tokens.RuntimeToken, 1206 CreatedAt: now, 1207 Used: false, 1208 ExpiresAt: now.Add(ottConfig.ApplicationExpiration), 1209 UsedAt: time.Time{}, 1210 ScenarioGroups: scenarioGroups, 1211 } 1212 1213 tokenService := onetimetoken.NewTokenService(sysAuthSvc, &automock.ApplicationService{}, &automock.ApplicationConverter{}, &automock.ExternalTenantsService{}, 1214 &automock.HTTPDoer{}, tokenGenerator, ottConfig, pairingAdapters, timeService) 1215 1216 // WHEN 1217 token, err := tokenService.RegenerateOneTimeToken(ctxBackgroundWithScenarioGroups, systemAuthID) 1218 1219 // THEN 1220 assert.Equal(t, expectedToken, token) 1221 assert.NoError(t, err) 1222 }) 1223 } 1224 1225 func TestIsTokenValid(t *testing.T) { 1226 const ( 1227 csrTokenExpiration = time.Minute * 5 1228 appTokenExpiration = time.Minute * 5 1229 runtimeTokenExpiration = time.Minute * 5 1230 connectorURL = "connector.url" 1231 1232 suggestedTokenHeaderKey = "suggest_token" 1233 ) 1234 1235 ottConfig := onetimetoken.Config{ 1236 ConnectorURL: connectorURL, 1237 LegacyConnectorURL: connectorURL, 1238 SuggestTokenHeaderKey: suggestedTokenHeaderKey, 1239 CSRExpiration: csrTokenExpiration, 1240 ApplicationExpiration: appTokenExpiration, 1241 RuntimeExpiration: runtimeTokenExpiration, 1242 } 1243 1244 timeService := directorTime.NewService() 1245 pairingAdapters := &pkgadapters.Adapters{} 1246 appSvc := &automock.ApplicationService{} 1247 appConverter := &automock.ApplicationConverter{} 1248 tenantSvc := &automock.ExternalTenantsService{} 1249 httpClient := &automock.HTTPDoer{} 1250 tokenGenerator := &automock.TokenGenerator{} 1251 systemAuthSvc := &automock.SystemAuthService{} 1252 1253 validOTTSystemAuth := &pkgmodel.SystemAuth{ 1254 ID: "id", 1255 TenantID: nil, 1256 AppID: nil, 1257 RuntimeID: nil, 1258 IntegrationSystemID: nil, 1259 Value: &model.Auth{ 1260 Credential: model.CredentialData{}, 1261 AdditionalHeaders: nil, 1262 AdditionalQueryParams: nil, 1263 RequestAuth: nil, 1264 OneTimeToken: &model.OneTimeToken{ 1265 Token: "token", 1266 ConnectorURL: "url", 1267 Type: tokens.ApplicationToken, 1268 CreatedAt: time.Now(), 1269 Used: false, 1270 UsedAt: time.Time{}, 1271 }, 1272 }, 1273 } 1274 1275 testCases := []struct { 1276 description string 1277 systemAuth *pkgmodel.SystemAuth 1278 1279 shouldHaveError bool 1280 errorMsg string 1281 }{ 1282 { 1283 description: "Should return true when system auth token is valid", 1284 systemAuth: validOTTSystemAuth, 1285 shouldHaveError: false, 1286 }, 1287 { 1288 description: "Should return false with error when the system auth value is nil", 1289 systemAuth: &pkgmodel.SystemAuth{ 1290 ID: "123", 1291 TenantID: nil, 1292 AppID: nil, 1293 RuntimeID: nil, 1294 IntegrationSystemID: nil, 1295 Value: nil, 1296 }, 1297 shouldHaveError: true, 1298 errorMsg: "System Auth value for auth id 123 is missing", 1299 }, 1300 { 1301 description: "Should return false with error when the system auth value is nil", 1302 systemAuth: &pkgmodel.SystemAuth{ 1303 ID: "123", 1304 }, 1305 shouldHaveError: true, 1306 errorMsg: "System Auth value for auth id 123 is missing", 1307 }, 1308 { 1309 description: "Should return false with error when the system auth OTT is nil", 1310 systemAuth: &pkgmodel.SystemAuth{ 1311 ID: "123", 1312 Value: &model.Auth{}, 1313 }, 1314 shouldHaveError: true, 1315 errorMsg: "One Time Token for system auth id 123 is missing", 1316 }, 1317 { 1318 description: "Should return false when the system auth OTT is used", 1319 systemAuth: &pkgmodel.SystemAuth{ 1320 ID: "234", 1321 Value: &model.Auth{ 1322 OneTimeToken: &model.OneTimeToken{ 1323 CreatedAt: time.Time{}, 1324 Used: true, 1325 }, 1326 }, 1327 }, 1328 shouldHaveError: true, 1329 errorMsg: "One Time Token for system auth id 234 has been used", 1330 }, 1331 { 1332 description: "Should return false when the system auth OTT is expired", 1333 systemAuth: &pkgmodel.SystemAuth{ 1334 ID: "234", 1335 Value: &model.Auth{ 1336 OneTimeToken: &model.OneTimeToken{ 1337 Type: tokens.ApplicationToken, 1338 CreatedAt: time.Now().Add(-10 * time.Minute), 1339 Used: false, 1340 }, 1341 }, 1342 }, 1343 shouldHaveError: true, 1344 errorMsg: "One Time Token with validity 5m0s for system auth with ID 234 has expired", 1345 }, 1346 { 1347 description: "Should return false when the system auth OTT has no OTT type", 1348 systemAuth: &pkgmodel.SystemAuth{ 1349 ID: "234", 1350 Value: &model.Auth{ 1351 OneTimeToken: &model.OneTimeToken{ 1352 Used: false, 1353 }, 1354 }, 1355 }, 1356 shouldHaveError: true, 1357 errorMsg: "one-time token for system auth id 234 has no valid expiration type", 1358 }, 1359 } 1360 1361 for _, test := range testCases { 1362 t.Run(test.description, func(t *testing.T) { 1363 // GIVEN 1364 tokenSvc := onetimetoken.NewTokenService(systemAuthSvc, appSvc, appConverter, tenantSvc, httpClient, tokenGenerator, ottConfig, pairingAdapters, timeService) 1365 1366 // WHEN 1367 isValid, err := tokenSvc.IsTokenValid(test.systemAuth) 1368 1369 // THEN 1370 if test.shouldHaveError { 1371 assert.Error(t, err) 1372 assert.Contains(t, err.Error(), test.errorMsg) 1373 assert.False(t, isValid) 1374 } else { 1375 assert.NoError(t, err) 1376 assert.True(t, isValid) 1377 } 1378 mock.AssertExpectationsForObjects(t, systemAuthSvc, appSvc, appConverter, tenantSvc, httpClient, tokenGenerator) 1379 }) 1380 } 1381 } 1382 1383 type Timer struct{} 1384 1385 func (t *Timer) Now() time.Time { 1386 return nowTime 1387 }