github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/formationmapping/auth_middleware_test.go (about) 1 package formationmapping_test 2 3 import ( 4 "context" 5 "net/http" 6 "net/http/httptest" 7 "testing" 8 9 "github.com/google/uuid" 10 "github.com/gorilla/mux" 11 fm "github.com/kyma-incubator/compass/components/director/internal/formationmapping" 12 "github.com/kyma-incubator/compass/components/director/internal/formationmapping/automock" 13 "github.com/kyma-incubator/compass/components/director/internal/model" 14 "github.com/kyma-incubator/compass/components/director/pkg/consumer" 15 persistenceautomock "github.com/kyma-incubator/compass/components/director/pkg/persistence/automock" 16 "github.com/stretchr/testify/mock" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestAuthenticator_FormationAssignmentHandler(t *testing.T) { 21 consumerUUID := uuid.New().String() 22 appTemplateID := "testAppTemplateID" 23 intSystemID := "intSystemID" 24 runtimeID := "testRuntimeID" 25 26 urlVars := map[string]string{ 27 fm.FormationIDParam: testFormationID, 28 fm.FormationAssignmentIDParam: testFormationAssignmentID, 29 } 30 31 faWithSourceAppAndTargetRuntime := fixFormationAssignmentModel(testFormationID, internalTntID, faSourceID, faTargetID, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeRuntime) 32 faWithSourceRuntimeAndTargetApp := fixFormationAssignmentModel(testFormationID, internalTntID, faSourceID, faTargetID, model.FormationAssignmentTypeRuntime, model.FormationAssignmentTypeApplication) 33 faWithSourceAppAndTargetRuntimeContext := fixFormationAssignmentModel(testFormationID, internalTntID, faSourceID, faTargetID, model.FormationAssignmentTypeApplication, model.FormationAssignmentTypeRuntimeContext) 34 35 intSysApp := &model.Application{ 36 IntegrationSystemID: &intSystemID, 37 } 38 39 appWithAppTemplate := &model.Application{ 40 ApplicationTemplateID: &appTemplateID, 41 } 42 43 consumerSubaccountLabelKey := "consumerSubaccountLabelKey" 44 45 appTemplateLbls := map[string]*model.Label{ 46 consumerSubaccountLabelKey: {Key: consumerSubaccountLabelKey, Value: externalTntID}, 47 } 48 49 appTemplateLblsWithInvalidConsumerSubaccount := map[string]*model.Label{ 50 consumerSubaccountLabelKey: {Key: consumerSubaccountLabelKey, Value: "invalidConsumerSubaccountID"}, 51 } 52 53 appTemplateLblsWithIncorrectType := map[string]*model.Label{ 54 consumerSubaccountLabelKey: {Key: consumerSubaccountLabelKey, Value: model.FormationAssignmentTypeRuntime}, 55 } 56 57 rtmContext := &model.RuntimeContext{ 58 RuntimeID: runtimeID, 59 } 60 61 testCases := []struct { 62 name string 63 transactFn func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner) 64 faServiceFn func() *automock.FormationAssignmentService 65 runtimeRepoFn func() *automock.RuntimeRepository 66 runtimeContextRepoFn func() *automock.RuntimeContextRepository 67 appRepoFn func() *automock.ApplicationRepository 68 appTemplateRepoFn func() *automock.ApplicationTemplateRepository 69 labelRepoFn func() *automock.LabelRepository 70 tenantRepoFn func() *automock.TenantRepository 71 consumerSubaccountLabelKey string 72 hasURLVars bool 73 contextFn func() context.Context 74 httpMethod string 75 expectedStatusCode int 76 expectedErrOutput string 77 }{ 78 // Common authorization checks 79 { 80 name: "Error when the http request method is not PATCH", 81 transactFn: fixUnusedTransactioner, 82 contextFn: func() context.Context { 83 return emptyCtx 84 }, 85 hasURLVars: true, 86 httpMethod: http.MethodGet, 87 expectedStatusCode: http.StatusMethodNotAllowed, 88 expectedErrOutput: "", 89 }, 90 { 91 name: "Error when required parameters are missing", 92 contextFn: func() context.Context { 93 return emptyCtx 94 }, 95 expectedStatusCode: http.StatusBadRequest, 96 expectedErrOutput: fixBuildExpectedErrResponse(t, "Not all of the required parameters are provided"), 97 }, 98 { 99 name: "Unauthorized error when authorization check is unsuccessful but there is no error", 100 transactFn: txGen.ThatSucceeds, 101 faServiceFn: func() *automock.FormationAssignmentService { 102 faSvc := &automock.FormationAssignmentService{} 103 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(fixFormationAssignmentModel(testFormationID, internalTntID, faSourceID, faTargetID, "invalid", "invalid"), nil).Once() 104 return faSvc 105 }, 106 contextFn: func() context.Context { 107 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 108 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 109 }, 110 hasURLVars: true, 111 expectedStatusCode: http.StatusUnauthorized, 112 expectedErrOutput: "", 113 }, 114 { 115 name: "Authorization fail: error when consumer info is missing in the context", 116 contextFn: func() context.Context { 117 return emptyCtx 118 }, 119 hasURLVars: true, 120 expectedStatusCode: http.StatusInternalServerError, 121 expectedErrOutput: "An unexpected error occurred while processing the request", 122 }, 123 { 124 name: "Authorization fail: error when transaction begin fails", 125 transactFn: txGen.ThatFailsOnBegin, 126 contextFn: func() context.Context { 127 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 128 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 129 }, 130 hasURLVars: true, 131 expectedStatusCode: http.StatusInternalServerError, 132 expectedErrOutput: "An unexpected error occurred while processing the request", 133 }, 134 { 135 name: "Authorization fail: error when getting formation assignment globally fails", 136 transactFn: txGen.ThatDoesntExpectCommit, 137 faServiceFn: func() *automock.FormationAssignmentService { 138 faSvc := &automock.FormationAssignmentService{} 139 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(nil, testErr) 140 return faSvc 141 }, 142 contextFn: func() context.Context { 143 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 144 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 145 }, 146 hasURLVars: true, 147 expectedStatusCode: http.StatusInternalServerError, 148 expectedErrOutput: "An unexpected error occurred while processing the request", 149 }, 150 { 151 name: "Authorization fail: error when tenant loading from context fails", 152 transactFn: txGen.ThatDoesntExpectCommit, 153 faServiceFn: func() *automock.FormationAssignmentService { 154 faSvc := &automock.FormationAssignmentService{} 155 faSvc.On("GetGlobalByIDAndFormationID", mock.Anything, testFormationAssignmentID, testFormationID).Return(faWithSourceAppAndTargetRuntime, nil) 156 return faSvc 157 }, 158 contextFn: func() context.Context { 159 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 160 ctxOnlyWithConsumer := consumer.SaveToContext(emptyCtx, c) 161 return ctxOnlyWithConsumer 162 }, 163 hasURLVars: true, 164 expectedStatusCode: http.StatusInternalServerError, 165 expectedErrOutput: "An unexpected error occurred while processing the request", 166 }, 167 { 168 name: "Authorization fail: error when committing transaction", 169 transactFn: txGen.ThatFailsOnCommit, 170 faServiceFn: func() *automock.FormationAssignmentService { 171 faSvc := &automock.FormationAssignmentService{} 172 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(fixFormationAssignmentModel(testFormationID, internalTntID, faSourceID, faTargetID, "invalid", "invalid"), nil) 173 return faSvc 174 }, 175 contextFn: func() context.Context { 176 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 177 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 178 }, 179 hasURLVars: true, 180 expectedStatusCode: http.StatusInternalServerError, 181 expectedErrOutput: "An unexpected error occurred while processing the request", 182 }, 183 // Application/ApplicationTemplate authorization checks 184 { 185 name: "Authorization fail: error when getting tenant", 186 transactFn: txGen.ThatDoesntExpectCommit, 187 faServiceFn: func() *automock.FormationAssignmentService { 188 faSvc := &automock.FormationAssignmentService{} 189 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 190 return faSvc 191 }, 192 tenantRepoFn: func() *automock.TenantRepository { 193 tenantRepo := &automock.TenantRepository{} 194 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(nil, testErr) 195 return tenantRepo 196 }, 197 contextFn: func() context.Context { 198 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 199 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 200 }, 201 hasURLVars: true, 202 expectedStatusCode: http.StatusInternalServerError, 203 expectedErrOutput: "An unexpected error occurred while processing the request", 204 }, 205 { 206 name: "Authorization fail: error when getting application", 207 transactFn: txGen.ThatDoesntExpectCommit, 208 faServiceFn: func() *automock.FormationAssignmentService { 209 faSvc := &automock.FormationAssignmentService{} 210 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 211 return faSvc 212 }, 213 tenantRepoFn: func() *automock.TenantRepository { 214 tenantRepo := &automock.TenantRepository{} 215 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 216 return tenantRepo 217 }, 218 appRepoFn: func() *automock.ApplicationRepository { 219 appRepo := &automock.ApplicationRepository{} 220 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(nil, testErr) 221 return appRepo 222 }, 223 contextFn: func() context.Context { 224 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 225 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 226 }, 227 hasURLVars: true, 228 expectedStatusCode: http.StatusInternalServerError, 229 expectedErrOutput: "An unexpected error occurred while processing the request", 230 }, 231 { 232 name: "Authorization fail: error when application owner existence check fail", 233 transactFn: txGen.ThatDoesntExpectCommit, 234 faServiceFn: func() *automock.FormationAssignmentService { 235 faSvc := &automock.FormationAssignmentService{} 236 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 237 return faSvc 238 }, 239 tenantRepoFn: func() *automock.TenantRepository { 240 tenantRepo := &automock.TenantRepository{} 241 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 242 return tenantRepo 243 }, 244 appRepoFn: func() *automock.ApplicationRepository { 245 appRepo := &automock.ApplicationRepository{} 246 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(intSysApp, nil) 247 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, testErr) 248 return appRepo 249 }, 250 contextFn: func() context.Context { 251 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 252 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 253 }, 254 hasURLVars: true, 255 expectedStatusCode: http.StatusInternalServerError, 256 expectedErrOutput: "An unexpected error occurred while processing the request", 257 }, 258 { 259 name: "Authorization fail: error when application template is nil or empty", 260 transactFn: txGen.ThatDoesntExpectCommit, 261 faServiceFn: func() *automock.FormationAssignmentService { 262 faSvc := &automock.FormationAssignmentService{} 263 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 264 return faSvc 265 }, 266 tenantRepoFn: func() *automock.TenantRepository { 267 tenantRepo := &automock.TenantRepository{} 268 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 269 return tenantRepo 270 }, 271 appRepoFn: func() *automock.ApplicationRepository { 272 appRepo := &automock.ApplicationRepository{} 273 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(intSysApp, nil) 274 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, nil) 275 return appRepo 276 }, 277 contextFn: func() context.Context { 278 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 279 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 280 }, 281 hasURLVars: true, 282 expectedStatusCode: http.StatusUnauthorized, 283 expectedErrOutput: "", 284 }, 285 { 286 name: "Authorization fail: error when application template existence check fails", 287 transactFn: txGen.ThatDoesntExpectCommit, 288 faServiceFn: func() *automock.FormationAssignmentService { 289 faSvc := &automock.FormationAssignmentService{} 290 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 291 return faSvc 292 }, 293 tenantRepoFn: func() *automock.TenantRepository { 294 tenantRepo := &automock.TenantRepository{} 295 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 296 return tenantRepo 297 }, 298 appRepoFn: func() *automock.ApplicationRepository { 299 appRepo := &automock.ApplicationRepository{} 300 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(appWithAppTemplate, nil) 301 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, nil) 302 return appRepo 303 }, 304 appTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 305 appTemplateRepo := &automock.ApplicationTemplateRepository{} 306 appTemplateRepo.On("Exists", contextThatHasTenant(internalTntID), appTemplateID).Return(false, testErr) 307 return appTemplateRepo 308 }, 309 contextFn: func() context.Context { 310 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 311 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 312 }, 313 hasURLVars: true, 314 expectedStatusCode: http.StatusUnauthorized, 315 expectedErrOutput: "An unexpected error occurred while processing the request", 316 }, 317 { 318 name: "Authorization fail: error when application template does not exists", 319 transactFn: txGen.ThatDoesntExpectCommit, 320 faServiceFn: func() *automock.FormationAssignmentService { 321 faSvc := &automock.FormationAssignmentService{} 322 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 323 return faSvc 324 }, 325 tenantRepoFn: func() *automock.TenantRepository { 326 tenantRepo := &automock.TenantRepository{} 327 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 328 return tenantRepo 329 }, 330 appRepoFn: func() *automock.ApplicationRepository { 331 appRepo := &automock.ApplicationRepository{} 332 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(appWithAppTemplate, nil) 333 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, nil) 334 return appRepo 335 }, 336 appTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 337 appTemplateRepo := &automock.ApplicationTemplateRepository{} 338 appTemplateRepo.On("Exists", contextThatHasTenant(internalTntID), appTemplateID).Return(false, nil) 339 return appTemplateRepo 340 }, 341 contextFn: func() context.Context { 342 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 343 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 344 }, 345 hasURLVars: true, 346 expectedStatusCode: http.StatusUnauthorized, 347 expectedErrOutput: "", 348 }, 349 { 350 name: "Authorization fail: error when listing application template labels", 351 transactFn: txGen.ThatDoesntExpectCommit, 352 faServiceFn: func() *automock.FormationAssignmentService { 353 faSvc := &automock.FormationAssignmentService{} 354 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 355 return faSvc 356 }, 357 tenantRepoFn: func() *automock.TenantRepository { 358 tenantRepo := &automock.TenantRepository{} 359 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 360 return tenantRepo 361 }, 362 appRepoFn: func() *automock.ApplicationRepository { 363 appRepo := &automock.ApplicationRepository{} 364 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(appWithAppTemplate, nil) 365 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, nil) 366 return appRepo 367 }, 368 appTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 369 appTemplateRepo := &automock.ApplicationTemplateRepository{} 370 appTemplateRepo.On("Exists", contextThatHasTenant(internalTntID), appTemplateID).Return(true, nil) 371 return appTemplateRepo 372 }, 373 labelRepoFn: func() *automock.LabelRepository { 374 lblRepo := &automock.LabelRepository{} 375 lblRepo.On("ListForGlobalObject", contextThatHasTenant(internalTntID), model.AppTemplateLabelableObject, appTemplateID).Return(nil, testErr) 376 return lblRepo 377 }, 378 contextFn: func() context.Context { 379 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 380 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 381 }, 382 hasURLVars: true, 383 expectedStatusCode: http.StatusInternalServerError, 384 expectedErrOutput: "An unexpected error occurred while processing the request", 385 }, 386 { 387 name: "Authorization fail: error when listing application template labels doesn't include subaccount label", 388 transactFn: txGen.ThatDoesntExpectCommit, 389 faServiceFn: func() *automock.FormationAssignmentService { 390 faSvc := &automock.FormationAssignmentService{} 391 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 392 return faSvc 393 }, 394 tenantRepoFn: func() *automock.TenantRepository { 395 tenantRepo := &automock.TenantRepository{} 396 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 397 return tenantRepo 398 }, 399 appRepoFn: func() *automock.ApplicationRepository { 400 appRepo := &automock.ApplicationRepository{} 401 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(appWithAppTemplate, nil) 402 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, nil) 403 return appRepo 404 }, 405 appTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 406 appTemplateRepo := &automock.ApplicationTemplateRepository{} 407 appTemplateRepo.On("Exists", contextThatHasTenant(internalTntID), appTemplateID).Return(true, nil) 408 return appTemplateRepo 409 }, 410 labelRepoFn: func() *automock.LabelRepository { 411 lblRepo := &automock.LabelRepository{} 412 lblRepo.On("ListForGlobalObject", contextThatHasTenant(internalTntID), model.AppTemplateLabelableObject, appTemplateID).Return(map[string]*model.Label{}, nil) 413 return lblRepo 414 }, 415 contextFn: func() context.Context { 416 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 417 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 418 }, 419 hasURLVars: true, 420 expectedStatusCode: http.StatusUnauthorized, 421 expectedErrOutput: "An unexpected error occurred while processing the request", 422 }, 423 { 424 name: "Authorization fail: error when consumer subaccount label is not of type string", 425 transactFn: txGen.ThatDoesntExpectCommit, 426 faServiceFn: func() *automock.FormationAssignmentService { 427 faSvc := &automock.FormationAssignmentService{} 428 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 429 return faSvc 430 }, 431 tenantRepoFn: func() *automock.TenantRepository { 432 tenantRepo := &automock.TenantRepository{} 433 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 434 return tenantRepo 435 }, 436 appRepoFn: func() *automock.ApplicationRepository { 437 appRepo := &automock.ApplicationRepository{} 438 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(appWithAppTemplate, nil) 439 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, nil) 440 return appRepo 441 }, 442 appTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 443 appTemplateRepo := &automock.ApplicationTemplateRepository{} 444 appTemplateRepo.On("Exists", contextThatHasTenant(internalTntID), appTemplateID).Return(true, nil) 445 return appTemplateRepo 446 }, 447 labelRepoFn: func() *automock.LabelRepository { 448 lblRepo := &automock.LabelRepository{} 449 lblRepo.On("ListForGlobalObject", contextThatHasTenant(internalTntID), model.AppTemplateLabelableObject, appTemplateID).Return(appTemplateLblsWithIncorrectType, nil) 450 return lblRepo 451 }, 452 consumerSubaccountLabelKey: consumerSubaccountLabelKey, 453 contextFn: func() context.Context { 454 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 455 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 456 }, 457 hasURLVars: true, 458 expectedStatusCode: http.StatusUnauthorized, 459 expectedErrOutput: "An unexpected error occurred while processing the request", 460 }, 461 { 462 name: "Authorization fail: when caller has NOT owner access to the target FA with type app that is made through subscription", 463 transactFn: txGen.ThatDoesntExpectCommit, 464 faServiceFn: func() *automock.FormationAssignmentService { 465 faSvc := &automock.FormationAssignmentService{} 466 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 467 return faSvc 468 }, 469 tenantRepoFn: func() *automock.TenantRepository { 470 tenantRepo := &automock.TenantRepository{} 471 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 472 return tenantRepo 473 }, 474 appRepoFn: func() *automock.ApplicationRepository { 475 appRepo := &automock.ApplicationRepository{} 476 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(appWithAppTemplate, nil) 477 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, nil) 478 return appRepo 479 }, 480 appTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 481 appTemplateRepo := &automock.ApplicationTemplateRepository{} 482 appTemplateRepo.On("Exists", contextThatHasTenant(internalTntID), appTemplateID).Return(true, nil) 483 return appTemplateRepo 484 }, 485 labelRepoFn: func() *automock.LabelRepository { 486 lblRepo := &automock.LabelRepository{} 487 lblRepo.On("ListForGlobalObject", contextThatHasTenant(internalTntID), model.AppTemplateLabelableObject, appTemplateID).Return(appTemplateLblsWithInvalidConsumerSubaccount, nil) 488 return lblRepo 489 }, 490 consumerSubaccountLabelKey: consumerSubaccountLabelKey, 491 contextFn: func() context.Context { 492 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 493 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 494 }, 495 hasURLVars: true, 496 expectedStatusCode: http.StatusUnauthorized, 497 expectedErrOutput: "", 498 }, 499 { 500 name: "Authorization success: when the int system caller has owner access to the target formation assignment with type application", 501 transactFn: txGen.ThatSucceeds, 502 faServiceFn: func() *automock.FormationAssignmentService { 503 faSvc := &automock.FormationAssignmentService{} 504 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasConsumer(intSystemID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 505 return faSvc 506 }, 507 tenantRepoFn: func() *automock.TenantRepository { 508 tenantRepo := &automock.TenantRepository{} 509 tenantRepo.On("Get", contextThatHasConsumer(intSystemID), internalTntID).Return(fixBusinessTenantMapping(), nil) 510 return tenantRepo 511 }, 512 appRepoFn: func() *automock.ApplicationRepository { 513 appRepo := &automock.ApplicationRepository{} 514 appRepo.On("GetByID", contextThatHasConsumer(intSystemID), internalTntID, faTargetID).Return(intSysApp, nil) 515 return appRepo 516 }, 517 contextFn: func() context.Context { 518 c := fixGetConsumer(intSystemID, consumer.IntegrationSystem) 519 return fixContextWithConsumer(c) 520 }, 521 hasURLVars: true, 522 expectedStatusCode: http.StatusOK, 523 expectedErrOutput: "", 524 }, 525 { 526 name: "Authorization fail: when the int system caller manages the target FA with type application but the transaction fail", 527 transactFn: txGen.ThatFailsOnCommit, 528 faServiceFn: func() *automock.FormationAssignmentService { 529 faSvc := &automock.FormationAssignmentService{} 530 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasConsumer(intSystemID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 531 return faSvc 532 }, 533 tenantRepoFn: func() *automock.TenantRepository { 534 tenantRepo := &automock.TenantRepository{} 535 tenantRepo.On("Get", contextThatHasConsumer(intSystemID), internalTntID).Return(fixBusinessTenantMapping(), nil) 536 return tenantRepo 537 }, 538 appRepoFn: func() *automock.ApplicationRepository { 539 appRepo := &automock.ApplicationRepository{} 540 appRepo.On("GetByID", contextThatHasConsumer(intSystemID), internalTntID, faTargetID).Return(intSysApp, nil) 541 return appRepo 542 }, 543 contextFn: func() context.Context { 544 c := fixGetConsumer(intSystemID, consumer.IntegrationSystem) 545 return fixContextWithConsumer(c) 546 }, 547 hasURLVars: true, 548 expectedStatusCode: http.StatusInternalServerError, 549 expectedErrOutput: "An unexpected error occurred while processing the request", 550 }, 551 { 552 name: "Authorization success: when caller is business integration and the formation is in a tenant of type resource group", 553 transactFn: txGen.ThatSucceeds, 554 faServiceFn: func() *automock.FormationAssignmentService { 555 faSvc := &automock.FormationAssignmentService{} 556 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasConsumer(consumerUUID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 557 return faSvc 558 }, 559 tenantRepoFn: func() *automock.TenantRepository { 560 tenantRepo := &automock.TenantRepository{} 561 tenantRepo.On("Get", contextThatHasConsumer(consumerUUID), internalTntID).Return(fixResourceGroupBusinessTenantMapping(), nil) 562 return tenantRepo 563 }, 564 contextFn: func() context.Context { 565 c := fixGetConsumer(consumerUUID, consumer.BusinessIntegration) 566 return fixContextWithConsumer(c) 567 }, 568 hasURLVars: true, 569 expectedStatusCode: http.StatusOK, 570 expectedErrOutput: "", 571 }, 572 { 573 name: "Authorization fail: when caller is business integration and the formation is in a tenant of type resource group but the transaction fail", 574 transactFn: txGen.ThatFailsOnCommit, 575 faServiceFn: func() *automock.FormationAssignmentService { 576 faSvc := &automock.FormationAssignmentService{} 577 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasConsumer(consumerUUID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 578 return faSvc 579 }, 580 tenantRepoFn: func() *automock.TenantRepository { 581 tenantRepo := &automock.TenantRepository{} 582 tenantRepo.On("Get", contextThatHasConsumer(consumerUUID), internalTntID).Return(fixResourceGroupBusinessTenantMapping(), nil) 583 return tenantRepo 584 }, 585 contextFn: func() context.Context { 586 c := fixGetConsumer(consumerUUID, consumer.BusinessIntegration) 587 return fixContextWithConsumer(c) 588 }, 589 hasURLVars: true, 590 expectedStatusCode: http.StatusInternalServerError, 591 expectedErrOutput: "An unexpected error occurred while processing the request", 592 }, 593 { 594 name: "Authorization fail: when the caller is the parent of the formation assignment target with type application but the transaction fail", 595 transactFn: txGen.ThatFailsOnCommit, 596 faServiceFn: func() *automock.FormationAssignmentService { 597 faSvc := &automock.FormationAssignmentService{} 598 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasConsumer(appTemplateID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 599 return faSvc 600 }, 601 tenantRepoFn: func() *automock.TenantRepository { 602 tenantRepo := &automock.TenantRepository{} 603 tenantRepo.On("Get", contextThatHasConsumer(appTemplateID), internalTntID).Return(fixBusinessTenantMapping(), nil) 604 return tenantRepo 605 }, 606 appRepoFn: func() *automock.ApplicationRepository { 607 appRepo := &automock.ApplicationRepository{} 608 appRepo.On("GetByID", contextThatHasConsumer(appTemplateID), internalTntID, faTargetID).Return(appWithAppTemplate, nil) 609 return appRepo 610 }, 611 contextFn: func() context.Context { 612 c := fixGetConsumer(appTemplateID, consumer.ExternalCertificate) 613 return fixContextWithConsumer(c) 614 }, 615 hasURLVars: true, 616 expectedStatusCode: http.StatusInternalServerError, 617 expectedErrOutput: "An unexpected error occurred while processing the request", 618 }, 619 { 620 name: "Authorization success: when the caller is the parent of the formation assignment target with type application", 621 transactFn: txGen.ThatSucceeds, 622 faServiceFn: func() *automock.FormationAssignmentService { 623 faSvc := &automock.FormationAssignmentService{} 624 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasConsumer(appTemplateID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 625 return faSvc 626 }, 627 tenantRepoFn: func() *automock.TenantRepository { 628 tenantRepo := &automock.TenantRepository{} 629 tenantRepo.On("Get", contextThatHasConsumer(appTemplateID), internalTntID).Return(fixBusinessTenantMapping(), nil) 630 return tenantRepo 631 }, 632 appRepoFn: func() *automock.ApplicationRepository { 633 appRepo := &automock.ApplicationRepository{} 634 appRepo.On("GetByID", contextThatHasConsumer(appTemplateID), internalTntID, faTargetID).Return(appWithAppTemplate, nil) 635 return appRepo 636 }, 637 contextFn: func() context.Context { 638 c := fixGetConsumer(appTemplateID, consumer.ExternalCertificate) 639 return fixContextWithConsumer(c) 640 }, 641 hasURLVars: true, 642 expectedStatusCode: http.StatusOK, 643 expectedErrOutput: "", 644 }, 645 { 646 name: "Authorization fail: error when consumer info is missing in the context for formation assignment with target type application", 647 transactFn: txGen.ThatDoesntExpectCommit, 648 faServiceFn: func() *automock.FormationAssignmentService { 649 faSvc := &automock.FormationAssignmentService{} 650 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasConsumer(consumerUUID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 651 return faSvc 652 }, 653 tenantRepoFn: func() *automock.TenantRepository { 654 tenantRepo := &automock.TenantRepository{} 655 tenantRepo.On("Get", contextThatHasConsumer(consumerUUID), internalTntID).Return(fixBusinessTenantMapping(), nil) 656 return tenantRepo 657 }, 658 appRepoFn: func() *automock.ApplicationRepository { 659 appRepo := &automock.ApplicationRepository{} 660 appRepo.On("GetByID", contextThatHasConsumer(consumerUUID), internalTntID, faTargetID).Return(appWithAppTemplate, nil) 661 return appRepo 662 }, 663 contextFn: func() context.Context { 664 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 665 return fixContextWithConsumer(c) 666 }, 667 hasURLVars: true, 668 expectedStatusCode: http.StatusInternalServerError, 669 expectedErrOutput: "An unexpected error occurred while processing the request", 670 }, 671 { 672 name: "Authorization success: when the caller has owner access to the target of the FA with type application", 673 transactFn: txGen.ThatSucceeds, 674 faServiceFn: func() *automock.FormationAssignmentService { 675 faSvc := &automock.FormationAssignmentService{} 676 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 677 return faSvc 678 }, 679 tenantRepoFn: func() *automock.TenantRepository { 680 tenantRepo := &automock.TenantRepository{} 681 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 682 return tenantRepo 683 }, 684 appRepoFn: func() *automock.ApplicationRepository { 685 appRepo := &automock.ApplicationRepository{} 686 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(intSysApp, nil) 687 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(true, nil) 688 return appRepo 689 }, 690 contextFn: func() context.Context { 691 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 692 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 693 }, 694 hasURLVars: true, 695 expectedStatusCode: http.StatusOK, 696 expectedErrOutput: "", 697 }, 698 { 699 name: "Authorization fail: when the caller has owner access to the target of the FA with type application but the transaction fail", 700 transactFn: txGen.ThatFailsOnCommit, 701 faServiceFn: func() *automock.FormationAssignmentService { 702 faSvc := &automock.FormationAssignmentService{} 703 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 704 return faSvc 705 }, 706 tenantRepoFn: func() *automock.TenantRepository { 707 tenantRepo := &automock.TenantRepository{} 708 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 709 return tenantRepo 710 }, 711 appRepoFn: func() *automock.ApplicationRepository { 712 appRepo := &automock.ApplicationRepository{} 713 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(intSysApp, nil) 714 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(true, nil) 715 return appRepo 716 }, 717 contextFn: func() context.Context { 718 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 719 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 720 }, 721 hasURLVars: true, 722 expectedStatusCode: http.StatusInternalServerError, 723 expectedErrOutput: "An unexpected error occurred while processing the request", 724 }, 725 { 726 name: "Authorization success: when the caller has owner access to the FA target's parent for type app that is made through subscription", 727 transactFn: txGen.ThatSucceeds, 728 faServiceFn: func() *automock.FormationAssignmentService { 729 faSvc := &automock.FormationAssignmentService{} 730 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 731 return faSvc 732 }, 733 tenantRepoFn: func() *automock.TenantRepository { 734 tenantRepo := &automock.TenantRepository{} 735 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 736 return tenantRepo 737 }, 738 appRepoFn: func() *automock.ApplicationRepository { 739 appRepo := &automock.ApplicationRepository{} 740 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(appWithAppTemplate, nil) 741 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, nil) 742 return appRepo 743 }, 744 appTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 745 appTemplateRepo := &automock.ApplicationTemplateRepository{} 746 appTemplateRepo.On("Exists", contextThatHasTenant(internalTntID), appTemplateID).Return(true, nil) 747 return appTemplateRepo 748 }, 749 labelRepoFn: func() *automock.LabelRepository { 750 lblRepo := &automock.LabelRepository{} 751 lblRepo.On("ListForGlobalObject", contextThatHasTenant(internalTntID), model.AppTemplateLabelableObject, appTemplateID).Return(appTemplateLbls, nil) 752 return lblRepo 753 }, 754 consumerSubaccountLabelKey: consumerSubaccountLabelKey, 755 contextFn: func() context.Context { 756 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 757 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 758 }, 759 hasURLVars: true, 760 expectedStatusCode: http.StatusOK, 761 expectedErrOutput: "", 762 }, 763 { 764 name: "Authz fail: when caller has owner access to FA target's parent for type app that is made through subscription but transact fail", 765 transactFn: txGen.ThatFailsOnCommit, 766 faServiceFn: func() *automock.FormationAssignmentService { 767 faSvc := &automock.FormationAssignmentService{} 768 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceRuntimeAndTargetApp, nil) 769 return faSvc 770 }, 771 tenantRepoFn: func() *automock.TenantRepository { 772 tenantRepo := &automock.TenantRepository{} 773 tenantRepo.On("Get", contextThatHasTenant(internalTntID), internalTntID).Return(fixBusinessTenantMapping(), nil) 774 return tenantRepo 775 }, 776 appRepoFn: func() *automock.ApplicationRepository { 777 appRepo := &automock.ApplicationRepository{} 778 appRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(appWithAppTemplate, nil) 779 appRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, nil) 780 return appRepo 781 }, 782 appTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 783 appTemplateRepo := &automock.ApplicationTemplateRepository{} 784 appTemplateRepo.On("Exists", contextThatHasTenant(internalTntID), appTemplateID).Return(true, nil) 785 return appTemplateRepo 786 }, 787 labelRepoFn: func() *automock.LabelRepository { 788 lblRepo := &automock.LabelRepository{} 789 lblRepo.On("ListForGlobalObject", contextThatHasTenant(internalTntID), model.AppTemplateLabelableObject, appTemplateID).Return(appTemplateLbls, nil) 790 return lblRepo 791 }, 792 consumerSubaccountLabelKey: consumerSubaccountLabelKey, 793 contextFn: func() context.Context { 794 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 795 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 796 }, 797 hasURLVars: true, 798 expectedStatusCode: http.StatusInternalServerError, 799 expectedErrOutput: "An unexpected error occurred while processing the request", 800 }, 801 // Runtime authorization checks 802 { 803 name: "Authorization fail: error when runtime owner existence check fail", 804 transactFn: txGen.ThatDoesntExpectCommit, 805 faServiceFn: func() *automock.FormationAssignmentService { 806 faSvc := &automock.FormationAssignmentService{} 807 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceAppAndTargetRuntime, nil) 808 return faSvc 809 }, 810 runtimeRepoFn: func() *automock.RuntimeRepository { 811 rtmRepo := &automock.RuntimeRepository{} 812 rtmRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, testErr) 813 return rtmRepo 814 }, 815 contextFn: func() context.Context { 816 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 817 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 818 }, 819 hasURLVars: true, 820 expectedStatusCode: http.StatusUnauthorized, 821 expectedErrOutput: "An unexpected error occurred while processing the request", 822 }, 823 { 824 name: "Authorization fail: error when the caller has NOT owner access to the formation assignment with target type runtime", 825 transactFn: txGen.ThatDoesntExpectCommit, 826 faServiceFn: func() *automock.FormationAssignmentService { 827 faSvc := &automock.FormationAssignmentService{} 828 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceAppAndTargetRuntime, nil) 829 return faSvc 830 }, 831 runtimeRepoFn: func() *automock.RuntimeRepository { 832 rtmRepo := &automock.RuntimeRepository{} 833 rtmRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(false, nil) 834 return rtmRepo 835 }, 836 contextFn: func() context.Context { 837 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 838 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 839 }, 840 hasURLVars: true, 841 expectedStatusCode: http.StatusUnauthorized, 842 expectedErrOutput: "", 843 }, 844 { 845 name: "Authorization success: when the caller has owner access to the target of the formation assignment with type runtime", 846 transactFn: txGen.ThatSucceeds, 847 faServiceFn: func() *automock.FormationAssignmentService { 848 faSvc := &automock.FormationAssignmentService{} 849 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceAppAndTargetRuntime, nil) 850 return faSvc 851 }, 852 runtimeRepoFn: func() *automock.RuntimeRepository { 853 rtmRepo := &automock.RuntimeRepository{} 854 rtmRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(true, nil) 855 return rtmRepo 856 }, 857 contextFn: func() context.Context { 858 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 859 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 860 }, 861 hasURLVars: true, 862 expectedStatusCode: http.StatusOK, 863 expectedErrOutput: "OK", 864 }, 865 { 866 name: "Authorization fail: when the caller has owner access to the target of the FA with type runtime but transaction fail", 867 transactFn: txGen.ThatFailsOnCommit, 868 faServiceFn: func() *automock.FormationAssignmentService { 869 faSvc := &automock.FormationAssignmentService{} 870 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceAppAndTargetRuntime, nil) 871 return faSvc 872 }, 873 runtimeRepoFn: func() *automock.RuntimeRepository { 874 rtmRepo := &automock.RuntimeRepository{} 875 rtmRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(true, nil) 876 return rtmRepo 877 }, 878 contextFn: func() context.Context { 879 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 880 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 881 }, 882 hasURLVars: true, 883 expectedStatusCode: http.StatusInternalServerError, 884 expectedErrOutput: "An unexpected error occurred while processing the request", 885 }, 886 // Runtime context authorization checks 887 { 888 name: "Authorization fail: error when getting runtime context globally", 889 transactFn: txGen.ThatDoesntExpectCommit, 890 faServiceFn: func() *automock.FormationAssignmentService { 891 faSvc := &automock.FormationAssignmentService{} 892 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceAppAndTargetRuntimeContext, nil) 893 return faSvc 894 }, 895 runtimeContextRepoFn: func() *automock.RuntimeContextRepository { 896 rtmCtxRepo := &automock.RuntimeContextRepository{} 897 rtmCtxRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(nil, testErr) 898 return rtmCtxRepo 899 }, 900 contextFn: func() context.Context { 901 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 902 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 903 }, 904 hasURLVars: true, 905 expectedStatusCode: http.StatusInternalServerError, 906 expectedErrOutput: "An unexpected error occurred while processing the request", 907 }, 908 { 909 name: "Authorization fail: error when runtime context owner check for runtime fails", 910 transactFn: txGen.ThatDoesntExpectCommit, 911 faServiceFn: func() *automock.FormationAssignmentService { 912 faSvc := &automock.FormationAssignmentService{} 913 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceAppAndTargetRuntimeContext, nil).Once() 914 return faSvc 915 }, 916 runtimeRepoFn: func() *automock.RuntimeRepository { 917 runtimeRepo := &automock.RuntimeRepository{} 918 runtimeRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, runtimeID).Return(false, testErr).Once() 919 return runtimeRepo 920 }, 921 runtimeContextRepoFn: func() *automock.RuntimeContextRepository { 922 rtmCtxRepo := &automock.RuntimeContextRepository{} 923 rtmCtxRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(rtmContext, nil).Once() 924 return rtmCtxRepo 925 }, 926 contextFn: func() context.Context { 927 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 928 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 929 }, 930 hasURLVars: true, 931 expectedStatusCode: http.StatusUnauthorized, 932 expectedErrOutput: "An unexpected error occurred while processing the request", 933 }, 934 { 935 name: "Authorization fail: when caller has NOT owner access to FA with target type rtm context made through subscription", 936 transactFn: txGen.ThatDoesntExpectCommit, 937 faServiceFn: func() *automock.FormationAssignmentService { 938 faSvc := &automock.FormationAssignmentService{} 939 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceAppAndTargetRuntimeContext, nil) 940 return faSvc 941 }, 942 runtimeRepoFn: func() *automock.RuntimeRepository { 943 rtmRepo := &automock.RuntimeRepository{} 944 rtmRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, runtimeID).Return(false, nil) 945 return rtmRepo 946 }, 947 runtimeContextRepoFn: func() *automock.RuntimeContextRepository { 948 rtmCtxRepo := &automock.RuntimeContextRepository{} 949 rtmCtxRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(rtmContext, nil) 950 return rtmCtxRepo 951 }, 952 contextFn: func() context.Context { 953 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 954 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 955 }, 956 hasURLVars: true, 957 expectedStatusCode: http.StatusUnauthorized, 958 expectedErrOutput: "", 959 }, 960 { 961 name: "Authorization success: when caller has owner access to the target of the formation assignment with type rtm context", 962 transactFn: txGen.ThatSucceeds, 963 faServiceFn: func() *automock.FormationAssignmentService { 964 faSvc := &automock.FormationAssignmentService{} 965 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceAppAndTargetRuntimeContext, nil) 966 return faSvc 967 }, 968 runtimeRepoFn: func() *automock.RuntimeRepository { 969 rtmRepo := &automock.RuntimeRepository{} 970 rtmRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, runtimeID).Return(true, nil) 971 return rtmRepo 972 }, 973 runtimeContextRepoFn: func() *automock.RuntimeContextRepository { 974 rtmCtxRepo := &automock.RuntimeContextRepository{} 975 rtmCtxRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(rtmContext, nil) 976 return rtmCtxRepo 977 }, 978 contextFn: func() context.Context { 979 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 980 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 981 }, 982 hasURLVars: true, 983 expectedStatusCode: http.StatusOK, 984 expectedErrOutput: "", 985 }, 986 { 987 name: "Authorization fail: when caller has owner access to the target of the FA with type rtm context but transaction fail", 988 transactFn: txGen.ThatFailsOnCommit, 989 faServiceFn: func() *automock.FormationAssignmentService { 990 faSvc := &automock.FormationAssignmentService{} 991 faSvc.On("GetGlobalByIDAndFormationID", contextThatHasTenant(internalTntID), testFormationAssignmentID, testFormationID).Return(faWithSourceAppAndTargetRuntimeContext, nil) 992 return faSvc 993 }, 994 runtimeRepoFn: func() *automock.RuntimeRepository { 995 rtmRepo := &automock.RuntimeRepository{} 996 rtmRepo.On("OwnerExists", contextThatHasTenant(internalTntID), internalTntID, runtimeID).Return(true, nil) 997 return rtmRepo 998 }, 999 runtimeContextRepoFn: func() *automock.RuntimeContextRepository { 1000 rtmCtxRepo := &automock.RuntimeContextRepository{} 1001 rtmCtxRepo.On("GetByID", contextThatHasTenant(internalTntID), internalTntID, faTargetID).Return(rtmContext, nil) 1002 return rtmCtxRepo 1003 }, 1004 contextFn: func() context.Context { 1005 c := fixGetConsumer(consumerUUID, consumer.ExternalCertificate) 1006 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 1007 }, 1008 hasURLVars: true, 1009 expectedStatusCode: http.StatusInternalServerError, 1010 expectedErrOutput: "An unexpected error occurred while processing the request", 1011 }, 1012 } 1013 1014 for _, tCase := range testCases { 1015 t.Run(tCase.name, func(t *testing.T) { 1016 persist, transact := fixUnusedTransactioner() 1017 if tCase.transactFn != nil { 1018 persist, transact = tCase.transactFn() 1019 } 1020 1021 faSvc := fixUnusedFormationAssignmentSvc() 1022 if tCase.faServiceFn != nil { 1023 faSvc = tCase.faServiceFn() 1024 } 1025 1026 rtmRepo := fixUnusedRuntimeRepo() 1027 if tCase.runtimeRepoFn != nil { 1028 rtmRepo = tCase.runtimeRepoFn() 1029 } 1030 1031 rtmCtxRepo := fixUnusedRuntimeContextRepo() 1032 if tCase.runtimeContextRepoFn != nil { 1033 rtmCtxRepo = tCase.runtimeContextRepoFn() 1034 } 1035 1036 appRepo := fixUnusedAppRepo() 1037 if tCase.appRepoFn != nil { 1038 appRepo = tCase.appRepoFn() 1039 } 1040 1041 appTemplateRepo := fixUnusedAppTemplateRepo() 1042 if tCase.appTemplateRepoFn != nil { 1043 appTemplateRepo = tCase.appTemplateRepoFn() 1044 } 1045 1046 labelRepo := fixUnusedLabelRepo() 1047 if tCase.labelRepoFn != nil { 1048 labelRepo = tCase.labelRepoFn() 1049 } 1050 1051 tenantRepo := fixUnusedTenantRepo() 1052 if tCase.tenantRepoFn != nil { 1053 tenantRepo = tCase.tenantRepoFn() 1054 } 1055 1056 defer mock.AssertExpectationsForObjects(t, persist, transact, faSvc, rtmRepo, rtmCtxRepo, appRepo, appTemplateRepo, labelRepo) 1057 1058 // GIVEN 1059 fmAuthenticator := fm.NewFormationMappingAuthenticator(transact, faSvc, rtmRepo, rtmCtxRepo, appRepo, appTemplateRepo, labelRepo, nil, nil, tenantRepo, tCase.consumerSubaccountLabelKey) 1060 fmAuthMiddleware := fmAuthenticator.FormationAssignmentHandler() 1061 rw := httptest.NewRecorder() 1062 1063 httpMethod := http.MethodPatch 1064 if tCase.httpMethod != "" { 1065 httpMethod = tCase.httpMethod 1066 } 1067 1068 httpReq := fixRequestWithContext(t, tCase.contextFn(), httpMethod) 1069 1070 if tCase.hasURLVars { 1071 httpReq = mux.SetURLVars(httpReq, urlVars) 1072 } 1073 1074 // WHEN 1075 fmAuthMiddleware(fixTestHandler(t)).ServeHTTP(rw, httpReq) 1076 1077 // THEN 1078 require.Equal(t, tCase.expectedStatusCode, rw.Code) 1079 require.Contains(t, rw.Body.String(), tCase.expectedErrOutput) 1080 }) 1081 } 1082 } 1083 1084 func TestAuthenticator_FormationHandler(t *testing.T) { 1085 urlVars := map[string]string{ 1086 fm.FormationIDParam: testFormationID, 1087 } 1088 1089 consumerID := "2c755564-97ef-4499-8c88-7b8518edc171" 1090 leadingProductID := consumerID 1091 leadingProductID2 := "leading-product-id-2" 1092 leadingProductIDs := []string{leadingProductID, leadingProductID2} 1093 1094 formation := &model.Formation{ 1095 ID: testFormationID, 1096 TenantID: internalTntID, 1097 FormationTemplateID: testFormationTemplateID, 1098 Name: testFormationName, 1099 } 1100 1101 formationTemplate := &model.FormationTemplate{ 1102 ID: testFormationTemplateID, 1103 Name: "formationTemplateName", 1104 TenantID: &internalTntID, 1105 LeadingProductIDs: leadingProductIDs, 1106 } 1107 1108 formationTemplateWithNonMatchingProductIDs := &model.FormationTemplate{ 1109 ID: testFormationTemplateID, 1110 Name: "formationTemplateName", 1111 TenantID: &internalTntID, 1112 LeadingProductIDs: []string{leadingProductID2}, 1113 } 1114 1115 testCases := []struct { 1116 name string 1117 transactFn func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner) 1118 formationRepoFn func() *automock.FormationRepository 1119 formationTemplateRepoFn func() *automock.FormationTemplateRepository 1120 consumerSubaccountLabelKey string 1121 hasURLVars bool 1122 contextFn func() context.Context 1123 httpMethod string 1124 expectedStatusCode int 1125 expectedErrOutput string 1126 }{ 1127 // Common authorization checks 1128 { 1129 name: "Error when the http request method is not POST or DELETE", 1130 transactFn: fixUnusedTransactioner, 1131 contextFn: func() context.Context { 1132 return emptyCtx 1133 }, 1134 hasURLVars: true, 1135 httpMethod: http.MethodGet, 1136 expectedStatusCode: http.StatusMethodNotAllowed, 1137 expectedErrOutput: "", 1138 }, 1139 { 1140 name: "Error when the required parameter is missing", 1141 contextFn: func() context.Context { 1142 return emptyCtx 1143 }, 1144 expectedStatusCode: http.StatusBadRequest, 1145 expectedErrOutput: fixBuildExpectedErrResponse(t, "Not all of the required parameters are provided"), 1146 }, 1147 { 1148 name: "Authorization success: when the consumer ID is one of the formation templates product IDs", 1149 transactFn: txGen.ThatSucceeds, 1150 formationRepoFn: func() *automock.FormationRepository { 1151 formationRepo := &automock.FormationRepository{} 1152 formationRepo.On("GetGlobalByID", contextThatHasTenant(internalTntID), testFormationID).Return(formation, nil).Once() 1153 return formationRepo 1154 }, 1155 formationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1156 ftRepo := &automock.FormationTemplateRepository{} 1157 ftRepo.On("Get", contextThatHasTenant(internalTntID), testFormationTemplateID).Return(formationTemplate, nil).Once() 1158 return ftRepo 1159 }, 1160 contextFn: func() context.Context { 1161 c := fixGetConsumer(consumerID, consumer.ExternalCertificate) 1162 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 1163 }, 1164 hasURLVars: true, 1165 expectedStatusCode: http.StatusOK, 1166 }, 1167 { 1168 name: "Unauthorized error when authorization check is unsuccessful but there is no error", 1169 transactFn: txGen.ThatSucceeds, 1170 formationRepoFn: func() *automock.FormationRepository { 1171 formationRepo := &automock.FormationRepository{} 1172 formationRepo.On("GetGlobalByID", contextThatHasTenant(internalTntID), testFormationID).Return(formation, nil).Once() 1173 return formationRepo 1174 }, 1175 formationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1176 ftRepo := &automock.FormationTemplateRepository{} 1177 ftRepo.On("Get", contextThatHasTenant(internalTntID), testFormationTemplateID).Return(formationTemplateWithNonMatchingProductIDs, nil).Once() 1178 return ftRepo 1179 }, 1180 contextFn: func() context.Context { 1181 c := fixGetConsumer(consumerID, consumer.ExternalCertificate) 1182 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 1183 }, 1184 hasURLVars: true, 1185 expectedStatusCode: http.StatusUnauthorized, 1186 }, 1187 { 1188 name: "Authorization fail: error when consumer info is missing in the context", 1189 contextFn: func() context.Context { 1190 return emptyCtx 1191 }, 1192 hasURLVars: true, 1193 expectedStatusCode: http.StatusInternalServerError, 1194 expectedErrOutput: "An unexpected error occurred while processing the request", 1195 }, 1196 { 1197 name: "Authorization fail: error when transaction begin fails", 1198 transactFn: txGen.ThatFailsOnBegin, 1199 contextFn: func() context.Context { 1200 c := fixGetConsumer(consumerID, consumer.ExternalCertificate) 1201 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 1202 }, 1203 hasURLVars: true, 1204 expectedStatusCode: http.StatusInternalServerError, 1205 expectedErrOutput: "An unexpected error occurred while processing the request", 1206 }, 1207 { 1208 name: "Authorization fail: error when getting formation fails", 1209 transactFn: txGen.ThatDoesntExpectCommit, 1210 formationRepoFn: func() *automock.FormationRepository { 1211 formationRepo := &automock.FormationRepository{} 1212 formationRepo.On("GetGlobalByID", contextThatHasTenant(internalTntID), testFormationID).Return(nil, testErr).Once() 1213 return formationRepo 1214 }, 1215 contextFn: func() context.Context { 1216 c := fixGetConsumer(consumerID, consumer.ExternalCertificate) 1217 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 1218 }, 1219 hasURLVars: true, 1220 expectedStatusCode: http.StatusInternalServerError, 1221 expectedErrOutput: "An unexpected error occurred while processing the request", 1222 }, 1223 { 1224 name: "Authorization fail: error when getting formation template fails", 1225 transactFn: txGen.ThatDoesntExpectCommit, 1226 formationRepoFn: func() *automock.FormationRepository { 1227 formationRepo := &automock.FormationRepository{} 1228 formationRepo.On("GetGlobalByID", contextThatHasTenant(internalTntID), testFormationID).Return(formation, nil).Once() 1229 return formationRepo 1230 }, 1231 formationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1232 ftRepo := &automock.FormationTemplateRepository{} 1233 ftRepo.On("Get", contextThatHasTenant(internalTntID), testFormationTemplateID).Return(nil, testErr).Once() 1234 return ftRepo 1235 }, 1236 contextFn: func() context.Context { 1237 c := fixGetConsumer(consumerID, consumer.ExternalCertificate) 1238 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 1239 }, 1240 hasURLVars: true, 1241 expectedStatusCode: http.StatusInternalServerError, 1242 expectedErrOutput: "An unexpected error occurred while processing the request", 1243 }, 1244 { 1245 name: "Authorization fail: error when committing transaction", 1246 transactFn: txGen.ThatFailsOnCommit, 1247 formationRepoFn: func() *automock.FormationRepository { 1248 formationRepo := &automock.FormationRepository{} 1249 formationRepo.On("GetGlobalByID", contextThatHasTenant(internalTntID), testFormationID).Return(formation, nil).Once() 1250 return formationRepo 1251 }, 1252 formationTemplateRepoFn: func() *automock.FormationTemplateRepository { 1253 ftRepo := &automock.FormationTemplateRepository{} 1254 ftRepo.On("Get", contextThatHasTenant(internalTntID), testFormationTemplateID).Return(formationTemplateWithNonMatchingProductIDs, nil).Once() 1255 return ftRepo 1256 }, 1257 contextFn: func() context.Context { 1258 c := fixGetConsumer(consumerID, consumer.ExternalCertificate) 1259 return fixContextWithTenantAndConsumer(c, internalTntID, externalTntID) 1260 }, 1261 hasURLVars: true, 1262 expectedStatusCode: http.StatusInternalServerError, 1263 expectedErrOutput: "An unexpected error occurred while processing the request", 1264 }, 1265 } 1266 1267 for _, tCase := range testCases { 1268 t.Run(tCase.name, func(t *testing.T) { 1269 persist, transact := fixUnusedTransactioner() 1270 if tCase.transactFn != nil { 1271 persist, transact = tCase.transactFn() 1272 } 1273 1274 formationRepo := fixUnusedFormationRepo() 1275 if tCase.formationRepoFn != nil { 1276 formationRepo = tCase.formationRepoFn() 1277 } 1278 1279 formationTemplateRepo := fixUnusedFormationTemplateRepo() 1280 if tCase.formationTemplateRepoFn != nil { 1281 formationTemplateRepo = tCase.formationTemplateRepoFn() 1282 } 1283 1284 defer mock.AssertExpectationsForObjects(t, persist, transact, formationRepo, formationTemplateRepo) 1285 1286 // GIVEN 1287 fmAuthenticator := fm.NewFormationMappingAuthenticator(transact, nil, nil, nil, nil, nil, nil, formationRepo, formationTemplateRepo, nil, tCase.consumerSubaccountLabelKey) 1288 formationAuthMiddleware := fmAuthenticator.FormationHandler() 1289 rw := httptest.NewRecorder() 1290 1291 httpMethod := http.MethodPatch 1292 if tCase.httpMethod != "" { 1293 httpMethod = tCase.httpMethod 1294 } 1295 1296 httpReq := fixRequestWithContext(t, tCase.contextFn(), httpMethod) 1297 1298 if tCase.hasURLVars { 1299 httpReq = mux.SetURLVars(httpReq, urlVars) 1300 } 1301 1302 // WHEN 1303 formationAuthMiddleware(fixTestHandler(t)).ServeHTTP(rw, httpReq) 1304 1305 // THEN 1306 require.Equal(t, tCase.expectedStatusCode, rw.Code) 1307 require.Contains(t, rw.Body.String(), tCase.expectedErrOutput) 1308 }) 1309 } 1310 }