go.temporal.io/server@v1.23.0/common/rpc/interceptor/namespace_validator_test.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package interceptor 26 27 import ( 28 "context" 29 "fmt" 30 "testing" 31 32 "github.com/golang/mock/gomock" 33 "github.com/google/uuid" 34 "github.com/stretchr/testify/require" 35 "github.com/stretchr/testify/suite" 36 enumspb "go.temporal.io/api/enums/v1" 37 "go.temporal.io/api/operatorservice/v1" 38 "go.temporal.io/api/serviceerror" 39 "go.temporal.io/api/workflowservice/v1" 40 "google.golang.org/grpc" 41 42 "go.temporal.io/server/api/adminservice/v1" 43 persistencespb "go.temporal.io/server/api/persistence/v1" 44 tokenspb "go.temporal.io/server/api/token/v1" 45 "go.temporal.io/server/common" 46 "go.temporal.io/server/common/dynamicconfig" 47 "go.temporal.io/server/common/namespace" 48 "go.temporal.io/server/common/persistence" 49 ) 50 51 type ( 52 namespaceValidatorSuite struct { 53 suite.Suite 54 *require.Assertions 55 56 controller *gomock.Controller 57 mockRegistry *namespace.MockRegistry 58 } 59 ) 60 61 func TestNamespaceValidatorSuite(t *testing.T) { 62 suite.Run(t, &namespaceValidatorSuite{}) 63 } 64 65 func (s *namespaceValidatorSuite) SetupSuite() { 66 } 67 68 func (s *namespaceValidatorSuite) TearDownSuite() { 69 } 70 71 func (s *namespaceValidatorSuite) SetupTest() { 72 s.Assertions = require.New(s.T()) 73 74 s.controller = gomock.NewController(s.T()) 75 s.mockRegistry = namespace.NewMockRegistry(s.controller) 76 } 77 78 func (s *namespaceValidatorSuite) TearDownTest() { 79 s.controller.Finish() 80 } 81 82 func (s *namespaceValidatorSuite) Test_StateValidationIntercept_NamespaceNotSet() { 83 taskToken, _ := common.NewProtoTaskTokenSerializer().Serialize(&tokenspb.Task{ 84 NamespaceId: "", 85 WorkflowId: "wid", 86 }) 87 88 nvi := NewNamespaceValidatorInterceptor( 89 s.mockRegistry, 90 dynamicconfig.GetBoolPropertyFn(false), 91 dynamicconfig.GetIntPropertyFn(100)) 92 serverInfo := &grpc.UnaryServerInfo{ 93 FullMethod: "/temporal/random", 94 } 95 96 testCases := []struct { 97 expectedErr error 98 req interface{} 99 }{ 100 { 101 req: &workflowservice.StartWorkflowExecutionRequest{}, 102 expectedErr: &serviceerror.InvalidArgument{}, 103 }, 104 { 105 req: &workflowservice.RespondWorkflowTaskCompletedRequest{ 106 Namespace: "test-namespace", // Ignored, must be set on token. 107 TaskToken: nil, 108 }, 109 expectedErr: &serviceerror.InvalidArgument{}, 110 }, 111 { 112 req: &workflowservice.RespondWorkflowTaskCompletedRequest{ 113 Namespace: "test-namespace", // Ignored, must be set on token. 114 TaskToken: taskToken, 115 }, 116 expectedErr: &serviceerror.InvalidArgument{}, 117 }, 118 } 119 120 for _, testCase := range testCases { 121 handlerCalled := false 122 _, err := nvi.StateValidationIntercept(context.Background(), testCase.req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 123 handlerCalled = true 124 return &workflowservice.StartWorkflowExecutionResponse{}, nil 125 }) 126 127 if testCase.expectedErr != nil { 128 s.IsType(testCase.expectedErr, err) 129 s.False(handlerCalled) 130 } else { 131 s.NoError(err) 132 s.True(handlerCalled) 133 } 134 } 135 } 136 137 func (s *namespaceValidatorSuite) Test_StateValidationIntercept_NamespaceNotFound() { 138 139 nvi := NewNamespaceValidatorInterceptor( 140 s.mockRegistry, 141 dynamicconfig.GetBoolPropertyFn(false), 142 dynamicconfig.GetIntPropertyFn(100)) 143 serverInfo := &grpc.UnaryServerInfo{ 144 FullMethod: "/temporal/random", 145 } 146 147 s.mockRegistry.EXPECT().GetNamespace(namespace.Name("not-found-namespace")).Return(nil, serviceerror.NewNamespaceNotFound("missing-namespace")) 148 req := &workflowservice.StartWorkflowExecutionRequest{Namespace: "not-found-namespace"} 149 handlerCalled := false 150 _, err := nvi.StateValidationIntercept(context.Background(), req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 151 handlerCalled = true 152 return &workflowservice.StartWorkflowExecutionResponse{}, nil 153 }) 154 155 s.IsType(&serviceerror.NamespaceNotFound{}, err) 156 s.False(handlerCalled) 157 158 s.mockRegistry.EXPECT().GetNamespaceByID(namespace.ID("not-found-namespace-id")).Return(nil, serviceerror.NewNamespaceNotFound("missing-namespace")) 159 taskToken, _ := common.NewProtoTaskTokenSerializer().Serialize(&tokenspb.Task{ 160 NamespaceId: "not-found-namespace-id", 161 }) 162 tokenReq := &workflowservice.RespondWorkflowTaskCompletedRequest{ 163 Namespace: "test-namespace", 164 TaskToken: taskToken, 165 } 166 handlerCalled = false 167 _, err = nvi.StateValidationIntercept(context.Background(), tokenReq, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 168 handlerCalled = true 169 return &workflowservice.RespondWorkflowTaskCompletedResponse{}, nil 170 }) 171 172 s.IsType(&serviceerror.NamespaceNotFound{}, err) 173 s.False(handlerCalled) 174 } 175 176 func (s *namespaceValidatorSuite) Test_StateValidationIntercept_StatusFromNamespace() { 177 testCases := []struct { 178 state enumspb.NamespaceState 179 replicationState enumspb.ReplicationState 180 expectedErr error 181 method string 182 req interface{} 183 }{ 184 // StartWorkflowExecution 185 { 186 state: enumspb.NAMESPACE_STATE_REGISTERED, 187 expectedErr: nil, 188 method: "/temporal/StartWorkflowExecution", 189 req: &workflowservice.StartWorkflowExecutionRequest{Namespace: "test-namespace"}, 190 }, 191 { 192 state: enumspb.NAMESPACE_STATE_DEPRECATED, 193 expectedErr: &serviceerror.NamespaceInvalidState{}, 194 method: "/temporal/StartWorkflowExecution", 195 req: &workflowservice.StartWorkflowExecutionRequest{Namespace: "test-namespace"}, 196 }, 197 { 198 state: enumspb.NAMESPACE_STATE_DELETED, 199 expectedErr: &serviceerror.NamespaceInvalidState{}, 200 method: "/temporal/StartWorkflowExecution", 201 req: &workflowservice.StartWorkflowExecutionRequest{Namespace: "test-namespace"}, 202 }, 203 { 204 state: enumspb.NAMESPACE_STATE_REGISTERED, 205 replicationState: enumspb.REPLICATION_STATE_HANDOVER, 206 expectedErr: common.ErrNamespaceHandover, 207 method: "/temporal/StartWorkflowExecution", 208 req: &workflowservice.StartWorkflowExecutionRequest{Namespace: "test-namespace"}, 209 }, 210 // DescribeNamespace 211 { 212 state: enumspb.NAMESPACE_STATE_UNSPECIFIED, 213 expectedErr: errNamespaceNotSet, 214 method: "/temporal/DescribeNamespace", 215 req: &workflowservice.DescribeNamespaceRequest{}, 216 }, 217 { 218 state: enumspb.NAMESPACE_STATE_UNSPECIFIED, 219 expectedErr: nil, 220 method: "/temporal/DescribeNamespace", 221 req: &workflowservice.DescribeNamespaceRequest{Id: "test-namespace-id"}, 222 }, 223 { 224 state: enumspb.NAMESPACE_STATE_UNSPECIFIED, 225 expectedErr: nil, 226 method: "/temporal/DescribeNamespace", 227 req: &workflowservice.DescribeNamespaceRequest{Namespace: "test-namespace"}, 228 }, 229 // RegisterNamespace 230 { 231 state: enumspb.NAMESPACE_STATE_UNSPECIFIED, 232 expectedErr: nil, 233 method: "/temporal/RegisterNamespace", 234 req: &workflowservice.RegisterNamespaceRequest{Namespace: "test-namespace"}, 235 }, 236 { 237 state: enumspb.NAMESPACE_STATE_UNSPECIFIED, 238 expectedErr: errNamespaceNotSet, 239 method: "/temporal/RegisterNamespace", 240 req: &workflowservice.RegisterNamespaceRequest{}, 241 }, 242 // PollWorkflowTaskQueue (default) 243 { 244 state: enumspb.NAMESPACE_STATE_REGISTERED, 245 expectedErr: nil, 246 method: "/temporal/PollWorkflowTaskQueue", 247 req: &workflowservice.PollWorkflowTaskQueueRequest{Namespace: "test-namespace"}, 248 }, 249 { 250 state: enumspb.NAMESPACE_STATE_DEPRECATED, 251 expectedErr: nil, 252 method: "/temporal/PollWorkflowTaskQueue", 253 req: &workflowservice.PollWorkflowTaskQueueRequest{Namespace: "test-namespace"}, 254 }, 255 { 256 state: enumspb.NAMESPACE_STATE_DELETED, 257 expectedErr: &serviceerror.NamespaceInvalidState{}, 258 method: "/temporal/PollWorkflowTaskQueue", 259 req: &workflowservice.PollWorkflowTaskQueueRequest{Namespace: "test-namespace"}, 260 }, 261 // UpdateNamespace 262 { 263 state: enumspb.NAMESPACE_STATE_REGISTERED, 264 expectedErr: nil, 265 method: "/temporal/UpdateNamespace", 266 req: &workflowservice.UpdateNamespaceRequest{Namespace: "test-namespace"}, 267 }, 268 { 269 state: enumspb.NAMESPACE_STATE_DEPRECATED, 270 expectedErr: nil, 271 method: "/temporal/UpdateNamespace", 272 req: &workflowservice.UpdateNamespaceRequest{Namespace: "test-namespace"}, 273 }, 274 { 275 state: enumspb.NAMESPACE_STATE_REGISTERED, 276 replicationState: enumspb.REPLICATION_STATE_HANDOVER, 277 expectedErr: nil, 278 method: "/temporal/UpdateNamespace", 279 req: &workflowservice.UpdateNamespaceRequest{Namespace: "test-namespace"}, 280 }, 281 { 282 state: enumspb.NAMESPACE_STATE_DELETED, 283 expectedErr: &serviceerror.NamespaceInvalidState{}, 284 method: "/temporal/UpdateNamespace", 285 req: &workflowservice.UpdateNamespaceRequest{Namespace: "test-namespace"}, 286 }, 287 } 288 289 for i, testCase := range testCases { 290 s.T().Run(fmt.Sprintf("test-case-%v", i), func(t *testing.T) { 291 _, isDescribeNamespace := testCase.req.(*workflowservice.DescribeNamespaceRequest) 292 _, isRegisterNamespace := testCase.req.(*workflowservice.RegisterNamespaceRequest) 293 if !isDescribeNamespace && !isRegisterNamespace { 294 s.mockRegistry.EXPECT().GetNamespace(namespace.Name("test-namespace")).Return(namespace.FromPersistentState( 295 &persistence.GetNamespaceResponse{ 296 Namespace: &persistencespb.NamespaceDetail{ 297 Config: &persistencespb.NamespaceConfig{}, 298 ReplicationConfig: &persistencespb.NamespaceReplicationConfig{ 299 State: testCase.replicationState, 300 }, 301 Info: &persistencespb.NamespaceInfo{ 302 State: testCase.state, 303 }, 304 }, 305 }), nil) 306 } 307 308 nvi := NewNamespaceValidatorInterceptor( 309 s.mockRegistry, 310 dynamicconfig.GetBoolPropertyFn(false), 311 dynamicconfig.GetIntPropertyFn(100)) 312 serverInfo := &grpc.UnaryServerInfo{ 313 FullMethod: testCase.method, 314 } 315 316 handlerCalled := false 317 _, err := nvi.StateValidationIntercept(context.Background(), testCase.req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 318 handlerCalled = true 319 return &workflowservice.StartWorkflowExecutionResponse{}, nil 320 }) 321 322 if testCase.expectedErr != nil { 323 s.IsType(testCase.expectedErr, err) 324 s.False(handlerCalled) 325 } else { 326 s.NoError(err) 327 s.True(handlerCalled) 328 } 329 }) 330 } 331 } 332 333 func (s *namespaceValidatorSuite) Test_StateValidationIntercept_StatusFromToken() { 334 taskToken, _ := common.NewProtoTaskTokenSerializer().Serialize(&tokenspb.Task{ 335 NamespaceId: "test-namespace-id", 336 }) 337 338 testCases := []struct { 339 state enumspb.NamespaceState 340 expectedErr error 341 method string 342 req interface{} 343 }{ 344 // RespondWorkflowTaskCompleted 345 { 346 state: enumspb.NAMESPACE_STATE_REGISTERED, 347 expectedErr: nil, 348 method: "/temporal/RespondWorkflowTaskCompleted", 349 req: &workflowservice.RespondWorkflowTaskCompletedRequest{ 350 TaskToken: taskToken, 351 }, 352 }, 353 { 354 state: enumspb.NAMESPACE_STATE_DEPRECATED, 355 expectedErr: nil, 356 method: "/temporal/RespondWorkflowTaskCompleted", 357 req: &workflowservice.RespondWorkflowTaskCompletedRequest{ 358 TaskToken: taskToken, 359 }, 360 }, 361 { 362 state: enumspb.NAMESPACE_STATE_DELETED, 363 expectedErr: &serviceerror.NamespaceInvalidState{}, 364 method: "/temporal/RespondWorkflowTaskCompleted", 365 req: &workflowservice.RespondWorkflowTaskCompletedRequest{ 366 TaskToken: taskToken, 367 }, 368 }, 369 } 370 371 for _, testCase := range testCases { 372 s.mockRegistry.EXPECT().GetNamespaceByID(namespace.ID("test-namespace-id")).Return(namespace.FromPersistentState( 373 &persistence.GetNamespaceResponse{ 374 Namespace: &persistencespb.NamespaceDetail{ 375 Config: &persistencespb.NamespaceConfig{}, 376 ReplicationConfig: &persistencespb.NamespaceReplicationConfig{}, 377 Info: &persistencespb.NamespaceInfo{ 378 State: testCase.state, 379 }, 380 }, 381 }), nil) 382 383 nvi := NewNamespaceValidatorInterceptor( 384 s.mockRegistry, 385 dynamicconfig.GetBoolPropertyFn(false), 386 dynamicconfig.GetIntPropertyFn(100)) 387 serverInfo := &grpc.UnaryServerInfo{ 388 FullMethod: testCase.method, 389 } 390 391 handlerCalled := false 392 _, err := nvi.StateValidationIntercept(context.Background(), testCase.req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 393 handlerCalled = true 394 return &workflowservice.RespondWorkflowTaskCompletedResponse{}, nil 395 }) 396 397 if testCase.expectedErr != nil { 398 s.IsType(testCase.expectedErr, err) 399 s.False(handlerCalled) 400 } else { 401 s.NoError(err) 402 s.True(handlerCalled) 403 } 404 } 405 } 406 407 func (s *namespaceValidatorSuite) Test_StateValidationIntercept_DescribeNamespace_Id() { 408 nvi := NewNamespaceValidatorInterceptor( 409 s.mockRegistry, 410 dynamicconfig.GetBoolPropertyFn(false), 411 dynamicconfig.GetIntPropertyFn(100)) 412 serverInfo := &grpc.UnaryServerInfo{ 413 FullMethod: "/temporal/random", 414 } 415 416 req := &workflowservice.DescribeNamespaceRequest{Id: "test-namespace-id"} 417 handlerCalled := false 418 _, err := nvi.StateValidationIntercept(context.Background(), req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 419 handlerCalled = true 420 return &workflowservice.DescribeNamespaceResponse{}, nil 421 }) 422 423 s.NoError(err) 424 s.True(handlerCalled) 425 426 req = &workflowservice.DescribeNamespaceRequest{} 427 handlerCalled = false 428 _, err = nvi.StateValidationIntercept(context.Background(), req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 429 handlerCalled = true 430 return &workflowservice.DescribeNamespaceResponse{}, nil 431 }) 432 433 s.IsType(&serviceerror.InvalidArgument{}, err) 434 s.False(handlerCalled) 435 } 436 437 func (s *namespaceValidatorSuite) Test_StateValidationIntercept_GetClusterInfo() { 438 nvi := NewNamespaceValidatorInterceptor( 439 s.mockRegistry, 440 dynamicconfig.GetBoolPropertyFn(false), 441 dynamicconfig.GetIntPropertyFn(100)) 442 serverInfo := &grpc.UnaryServerInfo{ 443 FullMethod: "/temporal/random", 444 } 445 446 // Example of API which doesn't have namespace field. 447 req := &workflowservice.GetClusterInfoRequest{} 448 handlerCalled := false 449 _, err := nvi.StateValidationIntercept(context.Background(), req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 450 handlerCalled = true 451 return &workflowservice.GetClusterInfoResponse{}, nil 452 }) 453 454 s.NoError(err) 455 s.True(handlerCalled) 456 } 457 458 func (s *namespaceValidatorSuite) Test_Intercept_RegisterNamespace() { 459 nvi := NewNamespaceValidatorInterceptor( 460 s.mockRegistry, 461 dynamicconfig.GetBoolPropertyFn(false), 462 dynamicconfig.GetIntPropertyFn(100)) 463 serverInfo := &grpc.UnaryServerInfo{ 464 FullMethod: "/temporal/random", 465 } 466 467 req := &workflowservice.RegisterNamespaceRequest{Namespace: "new-namespace"} 468 handlerCalled := false 469 _, err := nvi.StateValidationIntercept(context.Background(), req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 470 handlerCalled = true 471 return &workflowservice.RegisterNamespaceResponse{}, nil 472 }) 473 474 s.NoError(err) 475 s.True(handlerCalled) 476 477 req = &workflowservice.RegisterNamespaceRequest{} 478 handlerCalled = false 479 _, err = nvi.StateValidationIntercept(context.Background(), req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 480 handlerCalled = true 481 return &workflowservice.RegisterNamespaceResponse{}, nil 482 }) 483 484 s.IsType(&serviceerror.InvalidArgument{}, err) 485 s.False(handlerCalled) 486 } 487 488 func (s *namespaceValidatorSuite) Test_StateValidationIntercept_TokenNamespaceEnforcement() { 489 testCases := []struct { 490 tokenNamespaceID namespace.ID 491 tokenNamespaceName namespace.Name 492 requestNamespaceID namespace.ID 493 requestNamespaceName namespace.Name 494 enableTokenNamespaceEnforcement bool 495 expectedErr error 496 }{ 497 { 498 tokenNamespaceID: "valid-id", 499 tokenNamespaceName: "valid-name", 500 requestNamespaceID: "valid-id", 501 requestNamespaceName: "valid-name", 502 enableTokenNamespaceEnforcement: true, 503 expectedErr: nil, 504 }, 505 { 506 tokenNamespaceID: "valid-id", 507 tokenNamespaceName: "valid-name", 508 requestNamespaceID: "valid-id", 509 requestNamespaceName: "valid-name", 510 enableTokenNamespaceEnforcement: false, 511 expectedErr: nil, 512 }, 513 { 514 tokenNamespaceID: "valid-id", 515 tokenNamespaceName: "valid-name", 516 requestNamespaceID: "invalid-id", 517 requestNamespaceName: "invalid-name", 518 enableTokenNamespaceEnforcement: true, 519 expectedErr: &serviceerror.InvalidArgument{}, 520 }, 521 { 522 tokenNamespaceID: "valid-id", 523 tokenNamespaceName: "valid-name", 524 requestNamespaceID: "invalid-id", 525 requestNamespaceName: "invalid-name", 526 enableTokenNamespaceEnforcement: false, 527 expectedErr: nil, 528 }, 529 } 530 531 for _, testCase := range testCases { 532 taskToken, _ := common.NewProtoTaskTokenSerializer().Serialize(&tokenspb.Task{ 533 NamespaceId: testCase.tokenNamespaceID.String(), 534 }) 535 tokenNamespace := namespace.FromPersistentState( 536 &persistence.GetNamespaceResponse{ 537 Namespace: &persistencespb.NamespaceDetail{ 538 Config: &persistencespb.NamespaceConfig{}, 539 ReplicationConfig: &persistencespb.NamespaceReplicationConfig{}, 540 Info: &persistencespb.NamespaceInfo{ 541 Id: testCase.tokenNamespaceID.String(), 542 Name: testCase.tokenNamespaceName.String(), 543 State: enumspb.NAMESPACE_STATE_REGISTERED, 544 }, 545 }, 546 }) 547 548 req := &workflowservice.RespondWorkflowTaskCompletedRequest{ 549 Namespace: testCase.requestNamespaceName.String(), 550 TaskToken: taskToken, 551 } 552 queryReq := &workflowservice.RespondQueryTaskCompletedRequest{ 553 Namespace: testCase.requestNamespaceName.String(), 554 TaskToken: taskToken, 555 } 556 requestNamespace := namespace.FromPersistentState( 557 &persistence.GetNamespaceResponse{ 558 Namespace: &persistencespb.NamespaceDetail{ 559 Config: &persistencespb.NamespaceConfig{}, 560 ReplicationConfig: &persistencespb.NamespaceReplicationConfig{}, 561 Info: &persistencespb.NamespaceInfo{ 562 Id: testCase.requestNamespaceID.String(), 563 Name: testCase.requestNamespaceName.String(), 564 State: enumspb.NAMESPACE_STATE_REGISTERED, 565 }, 566 }, 567 }) 568 569 // 2 times because of RespondQueryTaskCompleted. 570 s.mockRegistry.EXPECT().GetNamespace(testCase.requestNamespaceName).Return(requestNamespace, nil).Times(2) 571 s.mockRegistry.EXPECT().GetNamespaceByID(testCase.tokenNamespaceID).Return(tokenNamespace, nil).Times(2) 572 573 nvi := NewNamespaceValidatorInterceptor( 574 s.mockRegistry, 575 dynamicconfig.GetBoolPropertyFn(testCase.enableTokenNamespaceEnforcement), 576 dynamicconfig.GetIntPropertyFn(100)) 577 serverInfo := &grpc.UnaryServerInfo{ 578 FullMethod: "/temporal/RandomMethod", 579 } 580 581 handlerCalled := false 582 _, err := nvi.StateValidationIntercept(context.Background(), req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 583 handlerCalled = true 584 return &workflowservice.RespondWorkflowTaskCompletedResponse{}, nil 585 }) 586 _, queryErr := nvi.StateValidationIntercept(context.Background(), queryReq, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 587 handlerCalled = true 588 return &workflowservice.RespondQueryTaskCompletedResponse{}, nil 589 }) 590 591 if testCase.expectedErr != nil { 592 s.IsType(testCase.expectedErr, err) 593 s.IsType(testCase.expectedErr, queryErr) 594 s.False(handlerCalled) 595 } else { 596 s.NoError(err) 597 s.NoError(queryErr) 598 s.True(handlerCalled) 599 } 600 } 601 } 602 603 func (s *namespaceValidatorSuite) Test_Intercept_SearchAttributeRequests() { 604 // it's just a list of requests 605 testCases := []struct { 606 req any 607 hasNamespace bool 608 }{ 609 { 610 req: &adminservice.AddSearchAttributesRequest{}, 611 hasNamespace: false, 612 }, 613 { 614 req: &adminservice.RemoveSearchAttributesRequest{}, 615 hasNamespace: false, 616 }, 617 { 618 req: &adminservice.GetSearchAttributesRequest{}, 619 hasNamespace: false, 620 }, 621 { 622 req: &operatorservice.AddSearchAttributesRequest{}, 623 hasNamespace: false, 624 }, 625 { 626 req: &operatorservice.RemoveSearchAttributesRequest{}, 627 hasNamespace: false, 628 }, 629 { 630 req: &operatorservice.ListSearchAttributesRequest{}, 631 hasNamespace: false, 632 }, 633 { 634 req: &adminservice.AddSearchAttributesRequest{Namespace: "test-namespace"}, 635 hasNamespace: true, 636 }, 637 { 638 req: &adminservice.RemoveSearchAttributesRequest{Namespace: "test-namespace"}, 639 hasNamespace: true, 640 }, 641 { 642 req: &adminservice.GetSearchAttributesRequest{Namespace: "test-namespace"}, 643 hasNamespace: true, 644 }, 645 { 646 req: &operatorservice.AddSearchAttributesRequest{Namespace: "test-namespace"}, 647 hasNamespace: true, 648 }, 649 { 650 req: &operatorservice.RemoveSearchAttributesRequest{Namespace: "test-namespace"}, 651 hasNamespace: true, 652 }, 653 { 654 req: &operatorservice.ListSearchAttributesRequest{Namespace: "test-namespace"}, 655 hasNamespace: true, 656 }, 657 } 658 659 for _, testCase := range testCases { 660 if testCase.hasNamespace { 661 s.mockRegistry.EXPECT().GetNamespace(namespace.Name("test-namespace")).Return(nil, nil) 662 } 663 664 nvi := NewNamespaceValidatorInterceptor( 665 s.mockRegistry, 666 dynamicconfig.GetBoolPropertyFn(false), 667 dynamicconfig.GetIntPropertyFn(100), 668 ) 669 serverInfo := &grpc.UnaryServerInfo{ 670 FullMethod: "/temporal/random", 671 } 672 673 handlerCalled := false 674 _, err := nvi.StateValidationIntercept( 675 context.Background(), 676 testCase.req, 677 serverInfo, 678 func(ctx context.Context, req any) (any, error) { 679 handlerCalled = true 680 return nil, nil 681 }, 682 ) 683 s.NoError(err) 684 s.True(handlerCalled) 685 } 686 } 687 688 func (s *namespaceValidatorSuite) Test_NamespaceValidateIntercept() { 689 nvi := NewNamespaceValidatorInterceptor( 690 s.mockRegistry, 691 dynamicconfig.GetBoolPropertyFn(false), 692 dynamicconfig.GetIntPropertyFn(10)) 693 serverInfo := &grpc.UnaryServerInfo{ 694 FullMethod: "/temporal/random", 695 } 696 requestNamespace := namespace.FromPersistentState( 697 &persistence.GetNamespaceResponse{ 698 Namespace: &persistencespb.NamespaceDetail{ 699 Config: &persistencespb.NamespaceConfig{}, 700 ReplicationConfig: &persistencespb.NamespaceReplicationConfig{}, 701 Info: &persistencespb.NamespaceInfo{ 702 Id: uuid.New().String(), 703 Name: "namespace", 704 State: enumspb.NAMESPACE_STATE_REGISTERED, 705 }, 706 }, 707 }) 708 requestNamespaceTooLong := namespace.FromPersistentState( 709 &persistence.GetNamespaceResponse{ 710 Namespace: &persistencespb.NamespaceDetail{ 711 Config: &persistencespb.NamespaceConfig{}, 712 ReplicationConfig: &persistencespb.NamespaceReplicationConfig{}, 713 Info: &persistencespb.NamespaceInfo{ 714 Id: uuid.New().String(), 715 Name: "namespaceTooLong", 716 State: enumspb.NAMESPACE_STATE_REGISTERED, 717 }, 718 }, 719 }) 720 s.mockRegistry.EXPECT().GetNamespace(namespace.Name("namespace")).Return(requestNamespace, nil).AnyTimes() 721 s.mockRegistry.EXPECT().GetNamespace(namespace.Name("namespaceTooLong")).Return(requestNamespaceTooLong, nil).AnyTimes() 722 723 req := &workflowservice.StartWorkflowExecutionRequest{Namespace: "namespace"} 724 handlerCalled := false 725 _, err := nvi.NamespaceValidateIntercept(context.Background(), req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 726 handlerCalled = true 727 return &workflowservice.StartWorkflowExecutionResponse{}, nil 728 }) 729 s.True(handlerCalled) 730 s.NoError(err) 731 732 req = &workflowservice.StartWorkflowExecutionRequest{Namespace: "namespaceTooLong"} 733 handlerCalled = false 734 _, err = nvi.NamespaceValidateIntercept(context.Background(), req, serverInfo, func(ctx context.Context, req interface{}) (interface{}, error) { 735 handlerCalled = true 736 return &workflowservice.StartWorkflowExecutionResponse{}, nil 737 }) 738 s.False(handlerCalled) 739 s.Error(err) 740 } 741 742 func (s *namespaceValidatorSuite) TestSetNamespace() { 743 namespaceRequestName := uuid.New().String() 744 namespaceEntryName := uuid.New().String() 745 namespaceEntry := namespace.FromPersistentState( 746 &persistence.GetNamespaceResponse{ 747 Namespace: &persistencespb.NamespaceDetail{ 748 Config: &persistencespb.NamespaceConfig{}, 749 ReplicationConfig: &persistencespb.NamespaceReplicationConfig{}, 750 Info: &persistencespb.NamespaceInfo{ 751 Id: uuid.New().String(), 752 Name: namespaceEntryName, 753 State: enumspb.NAMESPACE_STATE_REGISTERED, 754 }, 755 }, 756 }) 757 758 nvi := NewNamespaceValidatorInterceptor( 759 s.mockRegistry, 760 dynamicconfig.GetBoolPropertyFn(false), 761 dynamicconfig.GetIntPropertyFn(10), 762 ) 763 764 queryReq := &workflowservice.RespondQueryTaskCompletedRequest{} 765 nvi.setNamespace(namespaceEntry, queryReq) 766 s.Equal(namespaceEntryName, queryReq.Namespace) 767 queryReq.Namespace = namespaceRequestName 768 nvi.setNamespace(namespaceEntry, queryReq) 769 s.Equal(namespaceRequestName, queryReq.Namespace) 770 771 completeWorkflowTaskReq := &workflowservice.RespondWorkflowTaskCompletedRequest{} 772 nvi.setNamespace(namespaceEntry, completeWorkflowTaskReq) 773 s.Equal(namespaceEntryName, completeWorkflowTaskReq.Namespace) 774 completeWorkflowTaskReq.Namespace = namespaceRequestName 775 nvi.setNamespace(namespaceEntry, completeWorkflowTaskReq) 776 s.Equal(namespaceRequestName, completeWorkflowTaskReq.Namespace) 777 778 failWorkflowTaskReq := &workflowservice.RespondWorkflowTaskFailedRequest{} 779 nvi.setNamespace(namespaceEntry, failWorkflowTaskReq) 780 s.Equal(namespaceEntryName, failWorkflowTaskReq.Namespace) 781 failWorkflowTaskReq.Namespace = namespaceRequestName 782 nvi.setNamespace(namespaceEntry, failWorkflowTaskReq) 783 s.Equal(namespaceRequestName, failWorkflowTaskReq.Namespace) 784 785 heartbeatActivityTaskReq := &workflowservice.RecordActivityTaskHeartbeatRequest{} 786 nvi.setNamespace(namespaceEntry, heartbeatActivityTaskReq) 787 s.Equal(namespaceEntryName, heartbeatActivityTaskReq.Namespace) 788 heartbeatActivityTaskReq.Namespace = namespaceRequestName 789 nvi.setNamespace(namespaceEntry, heartbeatActivityTaskReq) 790 s.Equal(namespaceRequestName, heartbeatActivityTaskReq.Namespace) 791 792 cancelActivityTaskReq := &workflowservice.RespondActivityTaskCanceledRequest{} 793 nvi.setNamespace(namespaceEntry, cancelActivityTaskReq) 794 s.Equal(namespaceEntryName, cancelActivityTaskReq.Namespace) 795 cancelActivityTaskReq.Namespace = namespaceRequestName 796 nvi.setNamespace(namespaceEntry, cancelActivityTaskReq) 797 s.Equal(namespaceRequestName, cancelActivityTaskReq.Namespace) 798 799 completeActivityTaskReq := &workflowservice.RespondActivityTaskCompletedRequest{} 800 nvi.setNamespace(namespaceEntry, completeActivityTaskReq) 801 s.Equal(namespaceEntryName, completeActivityTaskReq.Namespace) 802 completeActivityTaskReq.Namespace = namespaceRequestName 803 nvi.setNamespace(namespaceEntry, completeActivityTaskReq) 804 s.Equal(namespaceRequestName, completeActivityTaskReq.Namespace) 805 806 failActivityTaskReq := &workflowservice.RespondActivityTaskFailedRequest{} 807 nvi.setNamespace(namespaceEntry, failActivityTaskReq) 808 s.Equal(namespaceEntryName, failActivityTaskReq.Namespace) 809 failActivityTaskReq.Namespace = namespaceRequestName 810 nvi.setNamespace(namespaceEntry, failActivityTaskReq) 811 s.Equal(namespaceRequestName, failActivityTaskReq.Namespace) 812 }