github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/authenticator/claims/validators_test.go (about)

     1  package claims_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/kyma-incubator/compass/components/director/pkg/idtokenclaims"
     9  
    10  	"github.com/kyma-incubator/compass/components/director/internal/authenticator/claims"
    11  	"github.com/kyma-incubator/compass/components/director/internal/authenticator/claims/automock"
    12  
    13  	"github.com/kyma-incubator/compass/components/director/internal/domain/scenarioassignment"
    14  
    15  	persistenceautomock "github.com/kyma-incubator/compass/components/director/pkg/persistence/automock"
    16  	"github.com/kyma-incubator/compass/components/director/pkg/persistence/txtest"
    17  	"github.com/pkg/errors"
    18  
    19  	"github.com/kyma-incubator/compass/components/hydrator/pkg/tenantmapping"
    20  
    21  	"github.com/form3tech-oss/jwt-go"
    22  	"github.com/kyma-incubator/compass/components/director/internal/domain/tenant"
    23  	"github.com/kyma-incubator/compass/components/director/internal/labelfilter"
    24  	"github.com/kyma-incubator/compass/components/director/internal/model"
    25  	"github.com/stretchr/testify/mock"
    26  	"github.com/stretchr/testify/require"
    27  
    28  	"github.com/kyma-incubator/compass/components/director/pkg/consumer"
    29  	"github.com/stretchr/testify/assert"
    30  )
    31  
    32  const (
    33  	consumerID           = "consumerID"
    34  	tenantID             = "tenantID"
    35  	consumerTenantID     = "consumer-tnt"
    36  	consumerExtTenantID  = "consumr-ext-tnt"
    37  	providerTenantID     = "provider-tnt"
    38  	providerExtTenantID  = "provider-ext-tnt"
    39  	onBehalfOfConsumerID = "onBehalfOfConsumer"
    40  	region               = "region"
    41  	clientID             = "client_id"
    42  	scopes               = "application:read application:write"
    43  	subscriptionLabelKey = "subscription"
    44  
    45  	runtimeID  = "rt-id"
    46  	runtime2ID = "rt-id2"
    47  )
    48  
    49  func TestValidator_Validate(t *testing.T) {
    50  	providerLabelKey := "providerName"
    51  	consumerSubaccountLabelKey := "global_subaccount_id"
    52  	tokenPrefix := "prefix-"
    53  	testErr := errors.New("test")
    54  
    55  	runtime := &model.Runtime{ID: runtimeID, Name: "rt"}
    56  
    57  	expectedRuntimeFilters := []*labelfilter.LabelFilter{
    58  		labelfilter.NewForKeyWithQuery(providerLabelKey, fmt.Sprintf("\"%s\"", clientID)),
    59  		labelfilter.NewForKeyWithQuery(tenant.RegionLabelKey, fmt.Sprintf("\"%s\"", region)),
    60  	}
    61  
    62  	expectedAppTemplateFilters := []*labelfilter.LabelFilter{
    63  		labelfilter.NewForKeyWithQuery(providerLabelKey, fmt.Sprintf("\"%s\"", clientID)),
    64  		labelfilter.NewForKeyWithQuery(tenant.RegionLabelKey, fmt.Sprintf("\"%s\"", region)),
    65  		labelfilter.NewForKeyWithQuery(scenarioassignment.SubaccountIDKey, fmt.Sprintf("\"%s\"", providerExtTenantID)),
    66  	}
    67  
    68  	rtmCtxWithConsumerSubaccountLabel := &model.RuntimeContextPage{
    69  		Data: []*model.RuntimeContext{
    70  			{
    71  				ID:        "rtmCtxID",
    72  				RuntimeID: runtime2ID,
    73  				Key:       subscriptionLabelKey,
    74  				Value:     tenantID,
    75  			},
    76  		},
    77  	}
    78  
    79  	rtmCtxFilter := []*labelfilter.LabelFilter{
    80  		labelfilter.NewForKeyWithQuery(consumerSubaccountLabelKey, fmt.Sprintf("\"%s\"", consumerExtTenantID)),
    81  	}
    82  
    83  	applicationTemplate := &model.ApplicationTemplate{
    84  		ID: "appTemplateID",
    85  	}
    86  
    87  	applications := []*model.Application{
    88  		{
    89  			ApplicationTemplateID: &applicationTemplate.ID,
    90  		},
    91  	}
    92  
    93  	emptyApplications := []*model.Application{}
    94  
    95  	testCases := []struct {
    96  		Name                         string
    97  		RuntimeServiceFn             func() *automock.RuntimeService
    98  		ApplicationTemplateServiceFn func() *automock.ApplicationTemplateService
    99  		ApplicationServiceFn         func() *automock.ApplicationService
   100  		RuntimeCtxSvcFn              func() *automock.RuntimeCtxService
   101  		IntegrationSystemServiceFn   func() *automock.IntegrationSystemService
   102  		Claims                       idtokenclaims.Claims
   103  		PersistenceFn                func() *persistenceautomock.PersistenceTx
   104  		TransactionerFn              func(persistTx *persistenceautomock.PersistenceTx) *persistenceautomock.Transactioner
   105  		ExpectedErr                  string
   106  	}{
   107  		// common
   108  		{
   109  			Name:   "Succeeds when all claims properties are present",
   110  			Claims: getClaims(consumerTenantID, consumerExtTenantID, scopes),
   111  		},
   112  		{
   113  			Name:   "Succeeds when no scopes are present",
   114  			Claims: getClaims(consumerTenantID, consumerExtTenantID, ""),
   115  		},
   116  		{
   117  			Name:   "Succeeds when both internal and external tenant IDs are missing",
   118  			Claims: getClaims("", "", scopes),
   119  		},
   120  		{
   121  			Name:        "Fails when internal tenant ID is missing",
   122  			Claims:      getClaims("", consumerExtTenantID, ""),
   123  			ExpectedErr: "Tenant not found",
   124  		},
   125  		{
   126  			Name: "Fails when inner validation fails",
   127  			Claims: idtokenclaims.Claims{
   128  				Tenant: map[string]string{
   129  					"consumerTenant": consumerTenantID,
   130  					"externalTenant": consumerExtTenantID,
   131  				},
   132  
   133  				Scopes:       scopes,
   134  				ConsumerID:   consumerID,
   135  				ConsumerType: consumer.Runtime,
   136  				StandardClaims: jwt.StandardClaims{
   137  					ExpiresAt: 1,
   138  				},
   139  			},
   140  			ExpectedErr: "while validating claims",
   141  		},
   142  		{
   143  			Name:        "consumer-provider flow: error when consumer type is not supported",
   144  			Claims:      getClaimsForConsumerProviderFlow(consumer.Application, consumerTenantID, consumerExtTenantID, consumerID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   145  			ExpectedErr: fmt.Sprintf("consumer with type %s is not supported", consumer.Application),
   146  		},
   147  		// runtime
   148  		{
   149  			Name:   "consumer-provider flow: error when token clientID missing",
   150  			Claims: getClaimsForRuntimeConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerTenantID, providerExtTenantID, scopes, region, ""),
   151  			TransactionerFn: func(persistTx *persistenceautomock.PersistenceTx) *persistenceautomock.Transactioner {
   152  				transact := txtest.NoopTransactioner(persistTx)
   153  				transact.On("Begin").Return(persistTx, nil).Twice()
   154  				transact.On("RollbackUnlessCommitted", mock.Anything, persistTx).Return(true).Twice()
   155  				return transact
   156  			},
   157  			ExpectedErr: "could not find consumer token client ID",
   158  		},
   159  		{
   160  			Name:   "consumer-provider flow: error when transaction cannot be opened",
   161  			Claims: getClaimsForRuntimeConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   162  			PersistenceFn: func() *persistenceautomock.PersistenceTx {
   163  				return txtest.PersistenceContextThatDoesntExpectCommit()
   164  			},
   165  			TransactionerFn: func(persistTx *persistenceautomock.PersistenceTx) *persistenceautomock.Transactioner {
   166  				transact := txtest.NoopTransactioner(persistTx)
   167  				transact.On("Begin").Return(persistTx, testErr).Twice()
   168  				return transact
   169  			},
   170  			ExpectedErr: "An error has occurred while opening transaction",
   171  		},
   172  		{
   173  			Name:        "consumer-provider flow: error when region missing",
   174  			Claims:      getClaimsForRuntimeConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerTenantID, providerExtTenantID, scopes, "", clientID),
   175  			ExpectedErr: "could not determine token's region",
   176  		},
   177  		{
   178  			Name:   "Success for consumer-provider flow when runtime with labels and runtime context are found",
   179  			Claims: getClaimsForRuntimeConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   180  			RuntimeServiceFn: func() *automock.RuntimeService {
   181  				runtimeSvc := &automock.RuntimeService{}
   182  				runtimeSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedRuntimeFilters).Return(runtime, nil).Once()
   183  				return runtimeSvc
   184  			},
   185  			RuntimeCtxSvcFn: func() *automock.RuntimeCtxService {
   186  				rtmCtxSvc := &automock.RuntimeCtxService{}
   187  				rtmCtxSvc.On("ListByFilter", contextThatHasTenant(consumerTenantID), runtimeID, rtmCtxFilter, 100, "").Return(rtmCtxWithConsumerSubaccountLabel, nil).Once()
   188  				return rtmCtxSvc
   189  			},
   190  		},
   191  		{
   192  			Name:   "Consumer-provider flow: Error when no runtimes nor applications are found",
   193  			Claims: getClaimsForRuntimeConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   194  			RuntimeServiceFn: func() *automock.RuntimeService {
   195  				runtimeSvc := &automock.RuntimeService{}
   196  				runtimeSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedRuntimeFilters).Return(nil, testErr).Once()
   197  				return runtimeSvc
   198  			},
   199  			ApplicationTemplateServiceFn: func() *automock.ApplicationTemplateService {
   200  				appTemplateSvc := &automock.ApplicationTemplateService{}
   201  				appTemplateSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedAppTemplateFilters).Return(applicationTemplate, nil).Once()
   202  				return appTemplateSvc
   203  			},
   204  			ApplicationServiceFn: func() *automock.ApplicationService {
   205  				applicationSvc := &automock.ApplicationService{}
   206  				applicationSvc.On("ListAll", contextThatHasTenant(consumerTenantID)).Return(emptyApplications, nil).Once()
   207  				return applicationSvc
   208  			},
   209  			ExpectedErr: "subscription record not found neither for application",
   210  		},
   211  		{
   212  			Name:   "Consumer-provider flow: Error when listing runtime context with filters",
   213  			Claims: getClaimsForRuntimeConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   214  			RuntimeServiceFn: func() *automock.RuntimeService {
   215  				runtimeSvc := &automock.RuntimeService{}
   216  				runtimeSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedRuntimeFilters).Return(runtime, nil).Once()
   217  				return runtimeSvc
   218  			},
   219  			ApplicationTemplateServiceFn: func() *automock.ApplicationTemplateService {
   220  				appTemplateSvc := &automock.ApplicationTemplateService{}
   221  				appTemplateSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedAppTemplateFilters).Return(applicationTemplate, nil).Once()
   222  				return appTemplateSvc
   223  			},
   224  			ApplicationServiceFn: func() *automock.ApplicationService {
   225  				applicationSvc := &automock.ApplicationService{}
   226  				applicationSvc.On("ListAll", contextThatHasTenant(consumerTenantID)).Return(emptyApplications, nil).Once()
   227  				return applicationSvc
   228  			},
   229  			RuntimeCtxSvcFn: func() *automock.RuntimeCtxService {
   230  				rtmCtxSvc := &automock.RuntimeCtxService{}
   231  				rtmCtxSvc.On("ListByFilter", contextThatHasTenant(consumerTenantID), runtimeID, mock.Anything, 100, "").Return(nil, testErr).Once()
   232  				return rtmCtxSvc
   233  			},
   234  			ExpectedErr: "subscription record not found neither for application",
   235  		},
   236  		{
   237  			Name:   "Consumer-provider flow: Error when listing runtime context with filters",
   238  			Claims: getClaimsForRuntimeConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   239  			RuntimeServiceFn: func() *automock.RuntimeService {
   240  				runtimeSvc := &automock.RuntimeService{}
   241  				runtimeSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedRuntimeFilters).Return(runtime, nil).Once()
   242  				return runtimeSvc
   243  			},
   244  			RuntimeCtxSvcFn: func() *automock.RuntimeCtxService {
   245  				rtmCtxSvc := &automock.RuntimeCtxService{}
   246  				rtmCtxSvc.On("ListByFilter", contextThatHasTenant(consumerTenantID), runtimeID, mock.Anything, 100, "").Return(&model.RuntimeContextPage{}, nil).Once()
   247  				return rtmCtxSvc
   248  			},
   249  			ApplicationTemplateServiceFn: func() *automock.ApplicationTemplateService {
   250  				appTemplateSvc := &automock.ApplicationTemplateService{}
   251  				appTemplateSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedAppTemplateFilters).Return(applicationTemplate, nil).Once()
   252  				return appTemplateSvc
   253  			},
   254  			ApplicationServiceFn: func() *automock.ApplicationService {
   255  				applicationSvc := &automock.ApplicationService{}
   256  				applicationSvc.On("ListAll", contextThatHasTenant(consumerTenantID)).Return(emptyApplications, nil).Once()
   257  				return applicationSvc
   258  			},
   259  			ExpectedErr: "subscription record not found neither for application",
   260  		},
   261  		{
   262  			Name:   "Consumer-provider flow: Error when transaction cannot be committed",
   263  			Claims: getClaimsForRuntimeConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   264  			RuntimeServiceFn: func() *automock.RuntimeService {
   265  				runtimeSvc := &automock.RuntimeService{}
   266  				runtimeSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedRuntimeFilters).Return(runtime, nil).Once()
   267  				return runtimeSvc
   268  			},
   269  			RuntimeCtxSvcFn: func() *automock.RuntimeCtxService {
   270  				rtmCtxSvc := &automock.RuntimeCtxService{}
   271  				rtmCtxSvc.On("ListByFilter", contextThatHasTenant(consumerTenantID), runtimeID, rtmCtxFilter, 100, "").Return(rtmCtxWithConsumerSubaccountLabel, nil).Once()
   272  				return rtmCtxSvc
   273  			},
   274  			PersistenceFn: func() *persistenceautomock.PersistenceTx {
   275  				persistTx := txtest.PersistenceContextThatDoesntExpectCommit()
   276  				persistTx.On("Commit").Return(testErr).Once()
   277  				return persistTx
   278  			},
   279  			TransactionerFn: func(persistTx *persistenceautomock.PersistenceTx) *persistenceautomock.Transactioner {
   280  				return txtest.TransactionerThatDoesARollbackTwice(persistTx)
   281  			},
   282  			ApplicationTemplateServiceFn: func() *automock.ApplicationTemplateService {
   283  				appTemplateSvc := &automock.ApplicationTemplateService{}
   284  				appTemplateSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedAppTemplateFilters).Return(applicationTemplate, nil).Once()
   285  				return appTemplateSvc
   286  			},
   287  			ApplicationServiceFn: func() *automock.ApplicationService {
   288  				applicationSvc := &automock.ApplicationService{}
   289  				applicationSvc.On("ListAll", contextThatHasTenant(consumerTenantID)).Return(emptyApplications, nil).Once()
   290  				return applicationSvc
   291  			},
   292  			ExpectedErr: testErr.Error(),
   293  		},
   294  		// integration system
   295  		{
   296  			Name:   "Success for integration system consumer-provider flow: when subaccount tenant ID is provided instead of integration system ID for consumer ID",
   297  			Claims: getClaimsForIntegrationSystemConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerExtTenantID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   298  		},
   299  		{
   300  			Name: "Success for integration system consumer-provider flow: when integration system with consumer ID exists",
   301  			IntegrationSystemServiceFn: func() *automock.IntegrationSystemService {
   302  				intSysSvc := &automock.IntegrationSystemService{}
   303  				intSysSvc.On("Exists", context.TODO(), consumerID).Return(true, nil)
   304  				return intSysSvc
   305  			},
   306  			Claims: getClaimsForIntegrationSystemConsumerProviderFlow(consumerTenantID, consumerExtTenantID, consumerID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   307  		},
   308  		{
   309  			Name: "integration system consumer-provider flow: error when check for integration system existence fails",
   310  			IntegrationSystemServiceFn: func() *automock.IntegrationSystemService {
   311  				intSysSvc := &automock.IntegrationSystemService{}
   312  				intSysSvc.On("Exists", context.TODO(), consumerID).Return(false, testErr)
   313  				return intSysSvc
   314  			},
   315  			Claims:      getClaimsForIntegrationSystemConsumerProviderFlow(consumerTenantID, consumerExtTenantID, consumerID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   316  			ExpectedErr: testErr.Error(),
   317  		},
   318  		{
   319  			Name: "integration system consumer-provider flow: error when integration system with consumer ID does not exist",
   320  			IntegrationSystemServiceFn: func() *automock.IntegrationSystemService {
   321  				intSysSvc := &automock.IntegrationSystemService{}
   322  				intSysSvc.On("Exists", context.TODO(), consumerID).Return(false, nil)
   323  				return intSysSvc
   324  			},
   325  			Claims:      getClaimsForIntegrationSystemConsumerProviderFlow(consumerTenantID, consumerExtTenantID, consumerID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   326  			ExpectedErr: fmt.Sprintf("integration system with ID %s does not exist", consumerID),
   327  		},
   328  		// application
   329  		{
   330  			Name:   "Consumer-provider flow: Success when no runtime, but there is an application subscribed",
   331  			Claims: getClaimsForRuntimeConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   332  			RuntimeServiceFn: func() *automock.RuntimeService {
   333  				runtimeSvc := &automock.RuntimeService{}
   334  				runtimeSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedRuntimeFilters).Return(nil, testErr).Once()
   335  				return runtimeSvc
   336  			},
   337  			ApplicationTemplateServiceFn: func() *automock.ApplicationTemplateService {
   338  				appTemplateSvc := &automock.ApplicationTemplateService{}
   339  				appTemplateSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedAppTemplateFilters).Return(applicationTemplate, nil).Once()
   340  				return appTemplateSvc
   341  			},
   342  			ApplicationServiceFn: func() *automock.ApplicationService {
   343  				applicationSvc := &automock.ApplicationService{}
   344  				applicationSvc.On("ListAll", contextThatHasTenant(consumerTenantID)).Return(applications, nil).Once()
   345  				return applicationSvc
   346  			},
   347  		},
   348  		{
   349  			Name:   "Consumer-provider flow: Error while getting application template in provider tenant",
   350  			Claims: getClaimsForRuntimeConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   351  			RuntimeServiceFn: func() *automock.RuntimeService {
   352  				runtimeSvc := &automock.RuntimeService{}
   353  				runtimeSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedRuntimeFilters).Return(nil, testErr).Once()
   354  				return runtimeSvc
   355  			},
   356  			ApplicationTemplateServiceFn: func() *automock.ApplicationTemplateService {
   357  				appTemplateSvc := &automock.ApplicationTemplateService{}
   358  				appTemplateSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedAppTemplateFilters).Return(nil, testErr).Once()
   359  				return appTemplateSvc
   360  			},
   361  			ApplicationServiceFn: func() *automock.ApplicationService {
   362  				applicationSvc := &automock.ApplicationService{}
   363  				return applicationSvc
   364  			},
   365  			ExpectedErr: testErr.Error(),
   366  		},
   367  		{
   368  			Name:   "Consumer-provider flow: Error occurred while listing consumer applications",
   369  			Claims: getClaimsForRuntimeConsumerProviderFlow(consumerTenantID, consumerExtTenantID, providerTenantID, providerExtTenantID, scopes, region, clientID),
   370  			RuntimeServiceFn: func() *automock.RuntimeService {
   371  				runtimeSvc := &automock.RuntimeService{}
   372  				runtimeSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedRuntimeFilters).Return(nil, testErr).Once()
   373  				return runtimeSvc
   374  			},
   375  			ApplicationTemplateServiceFn: func() *automock.ApplicationTemplateService {
   376  				appTemplateSvc := &automock.ApplicationTemplateService{}
   377  				appTemplateSvc.On("GetByFilters", contextThatHasTenant(providerTenantID), expectedAppTemplateFilters).Return(applicationTemplate, nil).Once()
   378  				return appTemplateSvc
   379  			},
   380  			ApplicationServiceFn: func() *automock.ApplicationService {
   381  				applicationSvc := &automock.ApplicationService{}
   382  				applicationSvc.On("ListAll", contextThatHasTenant(consumerTenantID)).Return(nil, testErr).Once()
   383  				return applicationSvc
   384  			},
   385  			ExpectedErr: testErr.Error(),
   386  		},
   387  	}
   388  
   389  	for _, testCase := range testCases {
   390  		t.Run(testCase.Name, func(t *testing.T) {
   391  			runtimeSvc := &automock.RuntimeService{}
   392  			if testCase.RuntimeServiceFn != nil {
   393  				runtimeSvc = testCase.RuntimeServiceFn()
   394  			}
   395  			runtimeCtxSvc := &automock.RuntimeCtxService{}
   396  			if testCase.RuntimeCtxSvcFn != nil {
   397  				runtimeCtxSvc = testCase.RuntimeCtxSvcFn()
   398  			}
   399  			appTemplateSvc := &automock.ApplicationTemplateService{}
   400  			if testCase.ApplicationTemplateServiceFn != nil {
   401  				appTemplateSvc = testCase.ApplicationTemplateServiceFn()
   402  			}
   403  			applicationSvc := &automock.ApplicationService{}
   404  			if testCase.ApplicationServiceFn != nil {
   405  				applicationSvc = testCase.ApplicationServiceFn()
   406  			}
   407  			intSysSvc := &automock.IntegrationSystemService{}
   408  			if testCase.IntegrationSystemServiceFn != nil {
   409  				intSysSvc = testCase.IntegrationSystemServiceFn()
   410  			}
   411  			persistTxMock := txtest.PersistenceContextThatExpectsCommit()
   412  			if testCase.PersistenceFn != nil {
   413  				persistTxMock = testCase.PersistenceFn()
   414  			}
   415  			transactionerMock := txtest.TransactionerThatSucceedsTwice(persistTxMock)
   416  			if testCase.TransactionerFn != nil {
   417  				transactionerMock = testCase.TransactionerFn(persistTxMock)
   418  			}
   419  
   420  			validator := claims.NewValidator(transactionerMock, runtimeSvc, runtimeCtxSvc, appTemplateSvc, applicationSvc, intSysSvc, providerLabelKey, consumerSubaccountLabelKey, tokenPrefix)
   421  			err := validator.Validate(context.TODO(), testCase.Claims)
   422  
   423  			if len(testCase.ExpectedErr) > 0 {
   424  				require.Error(t, err)
   425  				assert.Contains(t, err.Error(), testCase.ExpectedErr)
   426  			} else {
   427  				require.NoError(t, err)
   428  			}
   429  
   430  			mock.AssertExpectationsForObjects(t, runtimeSvc, runtimeCtxSvc, intSysSvc)
   431  		})
   432  	}
   433  }
   434  
   435  func TestScopesValidator_Validate(t *testing.T) {
   436  	t.Run("Succeeds when all claims properties are present", func(t *testing.T) {
   437  		v := claims.NewScopesValidator([]string{"application:read"})
   438  		c := getClaims(consumerTenantID, consumerExtTenantID, scopes)
   439  
   440  		err := v.Validate(context.TODO(), c)
   441  		assert.NoError(t, err)
   442  	})
   443  	t.Run("Fails when no scopes are present", func(t *testing.T) {
   444  		requiredScopes := []string{"application:read"}
   445  		v := claims.NewScopesValidator(requiredScopes)
   446  		c := getClaims(consumerTenantID, consumerExtTenantID, "")
   447  
   448  		err := v.Validate(context.TODO(), c)
   449  		assert.Error(t, err)
   450  		assert.Contains(t, err.Error(), fmt.Sprintf("Not all required scopes %q were found in claim with scopes %q", requiredScopes, c.Scopes))
   451  	})
   452  	t.Run("Fails when inner validation fails", func(t *testing.T) {
   453  		requiredScopes := []string{"application:read"}
   454  		v := claims.NewScopesValidator(requiredScopes)
   455  		c := getClaims(consumerTenantID, consumerExtTenantID, scopes)
   456  		c.ExpiresAt = 1
   457  
   458  		err := v.Validate(context.TODO(), c)
   459  		assert.Error(t, err)
   460  		assert.Contains(t, err.Error(), "while validating claims")
   461  	})
   462  }
   463  
   464  func getClaimsForRuntimeConsumerProviderFlow(consumerTenant, consumerExternalTenant, providerTenant, providerExtTenant, scopes, region, clientID string) idtokenclaims.Claims {
   465  	return getClaimsForConsumerProviderFlow(consumer.Runtime, consumerTenant, consumerExternalTenant, consumerID, providerTenant, providerExtTenant, scopes, region, clientID)
   466  }
   467  
   468  func getClaimsForIntegrationSystemConsumerProviderFlow(consumerTenant, consumerExternalTenant, consumerID, providerTenant, providerExtTenant, scopes, region, clientID string) idtokenclaims.Claims {
   469  	return getClaimsForConsumerProviderFlow(consumer.IntegrationSystem, consumerTenant, consumerExternalTenant, consumerID, providerTenant, providerExtTenant, scopes, region, clientID)
   470  }
   471  
   472  func getClaimsForConsumerProviderFlow(consumerType consumer.ConsumerType, consumerTenant, consumerExternalTenant, consumerID, providerTenant, providerExtTenant, scopes, region, clientID string) idtokenclaims.Claims {
   473  	return idtokenclaims.Claims{
   474  		Tenant: map[string]string{
   475  			tenantmapping.ConsumerTenantKey:         consumerTenant,
   476  			tenantmapping.ExternalTenantKey:         consumerExternalTenant,
   477  			tenantmapping.ProviderTenantKey:         providerTenant,
   478  			tenantmapping.ProviderExternalTenantKey: providerExtTenant,
   479  		},
   480  		Scopes:        scopes,
   481  		ConsumerID:    consumerID,
   482  		ConsumerType:  consumerType,
   483  		OnBehalfOf:    onBehalfOfConsumerID,
   484  		Region:        region,
   485  		TokenClientID: clientID,
   486  	}
   487  }
   488  
   489  func getClaims(intTenantID, extTenantID, scopes string) idtokenclaims.Claims {
   490  	return idtokenclaims.Claims{
   491  		Tenant: map[string]string{
   492  			tenantmapping.ConsumerTenantKey: intTenantID,
   493  			tenantmapping.ExternalTenantKey: extTenantID,
   494  		},
   495  
   496  		Scopes:       scopes,
   497  		ConsumerID:   consumerID,
   498  		ConsumerType: consumer.Runtime,
   499  	}
   500  }
   501  
   502  func contextThatHasTenant(expectedTenant string) interface{} {
   503  	return mock.MatchedBy(func(actual context.Context) bool {
   504  		actualTenant, err := tenant.LoadFromContext(actual)
   505  		if err != nil {
   506  			return false
   507  		}
   508  		return actualTenant == expectedTenant
   509  	})
   510  }