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  }