go.temporal.io/server@v1.23.0/common/rpc/interceptor/namespace_validator.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  
    30  	enumspb "go.temporal.io/api/enums/v1"
    31  	"go.temporal.io/api/operatorservice/v1"
    32  	"go.temporal.io/api/serviceerror"
    33  	"go.temporal.io/api/workflowservice/v1"
    34  	"google.golang.org/grpc"
    35  
    36  	"go.temporal.io/server/api/adminservice/v1"
    37  	"go.temporal.io/server/common"
    38  	"go.temporal.io/server/common/dynamicconfig"
    39  	"go.temporal.io/server/common/namespace"
    40  )
    41  
    42  type (
    43  	TaskTokenGetter interface {
    44  		GetTaskToken() []byte
    45  	}
    46  
    47  	// NamespaceValidatorInterceptor contains NamespaceValidateIntercept and StateValidationIntercept
    48  	NamespaceValidatorInterceptor struct {
    49  		namespaceRegistry               namespace.Registry
    50  		tokenSerializer                 common.TaskTokenSerializer
    51  		enableTokenNamespaceEnforcement dynamicconfig.BoolPropertyFn
    52  		maxNamespaceLength              dynamicconfig.IntPropertyFn
    53  	}
    54  )
    55  
    56  var (
    57  	errNamespaceNotSet            = serviceerror.NewInvalidArgument("Namespace not set on request.")
    58  	errBothNamespaceIDAndNameSet  = serviceerror.NewInvalidArgument("Only one of namespace name or Id should be set on request.")
    59  	errNamespaceTooLong           = serviceerror.NewInvalidArgument("Namespace length exceeds limit.")
    60  	errTaskTokenNotSet            = serviceerror.NewInvalidArgument("Task token not set on request.")
    61  	errTaskTokenNamespaceMismatch = serviceerror.NewInvalidArgument("Operation requested with a token from a different namespace.")
    62  
    63  	allowedNamespaceStates = map[string][]enumspb.NamespaceState{
    64  		"StartWorkflowExecution":           {enumspb.NAMESPACE_STATE_REGISTERED},
    65  		"SignalWithStartWorkflowExecution": {enumspb.NAMESPACE_STATE_REGISTERED},
    66  		"DeleteNamespace":                  {enumspb.NAMESPACE_STATE_REGISTERED, enumspb.NAMESPACE_STATE_DEPRECATED, enumspb.NAMESPACE_STATE_DELETED},
    67  	}
    68  	// If API name is not in the map above, these are allowed states for all APIs that have `namespace` or `task_token` field in the request object.
    69  	defaultAllowedNamespaceStates = []enumspb.NamespaceState{enumspb.NAMESPACE_STATE_REGISTERED, enumspb.NAMESPACE_STATE_DEPRECATED}
    70  
    71  	allowedMethodsDuringHandover = map[string]struct{}{
    72  		"UpdateNamespace":                  {},
    73  		"GetReplicationMessages":           {},
    74  		"ReplicateEventsV2":                {},
    75  		"GetWorkflowExecutionRawHistoryV2": {},
    76  	}
    77  )
    78  
    79  var _ grpc.UnaryServerInterceptor = (*NamespaceValidatorInterceptor)(nil).StateValidationIntercept
    80  var _ grpc.UnaryServerInterceptor = (*NamespaceValidatorInterceptor)(nil).NamespaceValidateIntercept
    81  
    82  func NewNamespaceValidatorInterceptor(
    83  	namespaceRegistry namespace.Registry,
    84  	enableTokenNamespaceEnforcement dynamicconfig.BoolPropertyFn,
    85  	maxNamespaceLength dynamicconfig.IntPropertyFn,
    86  ) *NamespaceValidatorInterceptor {
    87  	return &NamespaceValidatorInterceptor{
    88  		namespaceRegistry:               namespaceRegistry,
    89  		tokenSerializer:                 common.NewProtoTaskTokenSerializer(),
    90  		enableTokenNamespaceEnforcement: enableTokenNamespaceEnforcement,
    91  		maxNamespaceLength:              maxNamespaceLength,
    92  	}
    93  }
    94  
    95  func (ni *NamespaceValidatorInterceptor) NamespaceValidateIntercept(
    96  	ctx context.Context,
    97  	req interface{},
    98  	info *grpc.UnaryServerInfo,
    99  	handler grpc.UnaryHandler,
   100  ) (interface{}, error) {
   101  	err := ni.setNamespaceIfNotPresent(req)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	reqWithNamespace, hasNamespace := req.(NamespaceNameGetter)
   106  	if hasNamespace {
   107  		namespaceName := namespace.Name(reqWithNamespace.GetNamespace())
   108  		if len(namespaceName) > ni.maxNamespaceLength() {
   109  			return nil, errNamespaceTooLong
   110  		}
   111  	}
   112  
   113  	return handler(ctx, req)
   114  }
   115  
   116  func (ni *NamespaceValidatorInterceptor) setNamespaceIfNotPresent(
   117  	req interface{},
   118  ) error {
   119  	switch request := req.(type) {
   120  	case NamespaceNameGetter:
   121  		if request.GetNamespace() == "" {
   122  			namespaceEntry, err := ni.extractNamespaceFromTaskToken(req)
   123  			if err != nil {
   124  				return err
   125  			}
   126  			ni.setNamespace(namespaceEntry, req)
   127  		}
   128  		return nil
   129  	default:
   130  		return nil
   131  	}
   132  }
   133  
   134  func (ni *NamespaceValidatorInterceptor) setNamespace(
   135  	namespaceEntry *namespace.Namespace,
   136  	req interface{},
   137  ) {
   138  	switch request := req.(type) {
   139  	case *workflowservice.RespondQueryTaskCompletedRequest:
   140  		if request.Namespace == "" {
   141  			request.Namespace = namespaceEntry.Name().String()
   142  		}
   143  	case *workflowservice.RespondWorkflowTaskCompletedRequest:
   144  		if request.Namespace == "" {
   145  			request.Namespace = namespaceEntry.Name().String()
   146  		}
   147  	case *workflowservice.RespondWorkflowTaskFailedRequest:
   148  		if request.Namespace == "" {
   149  			request.Namespace = namespaceEntry.Name().String()
   150  		}
   151  	case *workflowservice.RecordActivityTaskHeartbeatRequest:
   152  		if request.Namespace == "" {
   153  			request.Namespace = namespaceEntry.Name().String()
   154  		}
   155  	case *workflowservice.RespondActivityTaskCanceledRequest:
   156  		if request.Namespace == "" {
   157  			request.Namespace = namespaceEntry.Name().String()
   158  		}
   159  	case *workflowservice.RespondActivityTaskCompletedRequest:
   160  		if request.Namespace == "" {
   161  			request.Namespace = namespaceEntry.Name().String()
   162  		}
   163  	case *workflowservice.RespondActivityTaskFailedRequest:
   164  		if request.Namespace == "" {
   165  			request.Namespace = namespaceEntry.Name().String()
   166  		}
   167  	}
   168  }
   169  
   170  // StateValidationIntercept validates:
   171  // 1. Namespace is specified in task token if there is a `task_token` field.
   172  // 2. Namespace is specified in request if there is a `namespace` field and no `task_token` field.
   173  // 3. Namespace exists.
   174  // 4. Namespace from request match namespace from task token, if check is enabled with dynamic config.
   175  // 5. Namespace is in correct state.
   176  func (ni *NamespaceValidatorInterceptor) StateValidationIntercept(
   177  	ctx context.Context,
   178  	req interface{},
   179  	info *grpc.UnaryServerInfo,
   180  	handler grpc.UnaryHandler,
   181  ) (interface{}, error) {
   182  	namespaceEntry, err := ni.extractNamespace(req)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  
   187  	err = ni.checkNamespaceState(namespaceEntry, info.FullMethod)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	err = ni.checkReplicationState(namespaceEntry, info.FullMethod)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	return handler(ctx, req)
   197  }
   198  
   199  func (ni *NamespaceValidatorInterceptor) extractNamespace(req interface{}) (*namespace.Namespace, error) {
   200  	// Token namespace has priority over request namespace. Check it first.
   201  	tokenNamespaceEntry, tokenErr := ni.extractNamespaceFromTaskToken(req)
   202  	if tokenErr != nil {
   203  		return nil, tokenErr
   204  	}
   205  
   206  	requestNamespaceEntry, requestErr := ni.extractNamespaceFromRequest(req)
   207  	// If namespace was extracted from token then it will be used.
   208  	if requestErr != nil && tokenNamespaceEntry == nil {
   209  		return nil, requestErr
   210  	}
   211  
   212  	err := ni.checkNamespaceMatch(requestNamespaceEntry, tokenNamespaceEntry)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	// Use namespace from task token (if specified) and ignore namespace from request.
   218  	if tokenNamespaceEntry != nil {
   219  		return tokenNamespaceEntry, nil
   220  	}
   221  
   222  	return requestNamespaceEntry, nil
   223  }
   224  
   225  func (ni *NamespaceValidatorInterceptor) extractNamespaceFromRequest(req interface{}) (*namespace.Namespace, error) {
   226  	reqWithNamespace, hasNamespace := req.(NamespaceNameGetter)
   227  	if !hasNamespace {
   228  		return nil, nil
   229  	}
   230  	namespaceName := namespace.Name(reqWithNamespace.GetNamespace())
   231  
   232  	switch request := req.(type) {
   233  	case *workflowservice.DescribeNamespaceRequest:
   234  		// Special case for DescribeNamespace API which should read namespace directly from database.
   235  		// Therefore, it must bypass namespace registry and validator.
   236  		if request.GetId() == "" && namespaceName.IsEmpty() {
   237  			return nil, errNamespaceNotSet
   238  		}
   239  		return nil, nil
   240  	case *adminservice.GetNamespaceRequest:
   241  		// special case for Admin.GetNamespace API which accept either Namespace ID or Namespace name as input
   242  		if request.GetId() == "" && namespaceName.IsEmpty() {
   243  			return nil, errNamespaceNotSet
   244  		}
   245  		return nil, nil
   246  	case *workflowservice.RegisterNamespaceRequest:
   247  		// Special case for RegisterNamespace API. `namespaceName` is name of namespace that about to be registered.
   248  		// There is no namespace entry for it, therefore, it must bypass namespace registry and validator.
   249  		if namespaceName.IsEmpty() {
   250  			return nil, errNamespaceNotSet
   251  		}
   252  		return nil, nil
   253  	case *operatorservice.DeleteNamespaceRequest:
   254  		// special case for Operator.DeleteNamespace API which accept either Namespace ID or Namespace name as input
   255  		namespaceID := namespace.ID(request.GetNamespaceId())
   256  		if namespaceID.IsEmpty() && namespaceName.IsEmpty() {
   257  			return nil, errNamespaceNotSet
   258  		}
   259  		if !namespaceID.IsEmpty() && !namespaceName.IsEmpty() {
   260  			return nil, errBothNamespaceIDAndNameSet
   261  		}
   262  		if namespaceID != "" {
   263  			return ni.namespaceRegistry.GetNamespaceByID(namespaceID)
   264  		}
   265  		return ni.namespaceRegistry.GetNamespace(namespaceName)
   266  	case *adminservice.AddSearchAttributesRequest,
   267  		*adminservice.RemoveSearchAttributesRequest,
   268  		*adminservice.GetSearchAttributesRequest,
   269  		*operatorservice.AddSearchAttributesRequest,
   270  		*operatorservice.RemoveSearchAttributesRequest,
   271  		*operatorservice.ListSearchAttributesRequest:
   272  		// Namespace is optional for search attributes operations.
   273  		// It's required when using SQL DB for visibility, but not when using Elasticsearch.
   274  		if !namespaceName.IsEmpty() {
   275  			return ni.namespaceRegistry.GetNamespace(namespaceName)
   276  		}
   277  		return nil, nil
   278  	default:
   279  		// All other APIs.
   280  		if namespaceName.IsEmpty() {
   281  			return nil, errNamespaceNotSet
   282  		}
   283  		return ni.namespaceRegistry.GetNamespace(namespaceName)
   284  	}
   285  }
   286  
   287  func (ni *NamespaceValidatorInterceptor) extractNamespaceFromTaskToken(req interface{}) (*namespace.Namespace, error) {
   288  	reqWithTaskToken, hasTaskToken := req.(TaskTokenGetter)
   289  	if !hasTaskToken {
   290  		return nil, nil
   291  	}
   292  	taskTokenBytes := reqWithTaskToken.GetTaskToken()
   293  	if len(taskTokenBytes) == 0 {
   294  		return nil, errTaskTokenNotSet
   295  	}
   296  	var namespaceID namespace.ID
   297  	// Special case for deprecated RespondQueryTaskCompleted API.
   298  	if _, ok := req.(*workflowservice.RespondQueryTaskCompletedRequest); ok {
   299  		taskToken, err := ni.tokenSerializer.DeserializeQueryTaskToken(taskTokenBytes)
   300  		if err != nil {
   301  			return nil, err
   302  		}
   303  		namespaceID = namespace.ID(taskToken.GetNamespaceId())
   304  	} else {
   305  		taskToken, err := ni.tokenSerializer.Deserialize(taskTokenBytes)
   306  		if err != nil {
   307  			return nil, err
   308  		}
   309  		namespaceID = namespace.ID(taskToken.GetNamespaceId())
   310  	}
   311  
   312  	if namespaceID.IsEmpty() {
   313  		return nil, errNamespaceNotSet
   314  	}
   315  	return ni.namespaceRegistry.GetNamespaceByID(namespaceID)
   316  }
   317  
   318  func (ni *NamespaceValidatorInterceptor) checkNamespaceMatch(requestNamespace *namespace.Namespace, tokenNamespace *namespace.Namespace) error {
   319  	if tokenNamespace == nil || requestNamespace == nil || !ni.enableTokenNamespaceEnforcement() {
   320  		return nil
   321  	}
   322  
   323  	if requestNamespace.ID() != tokenNamespace.ID() {
   324  		return errTaskTokenNamespaceMismatch
   325  	}
   326  	return nil
   327  }
   328  
   329  func (ni *NamespaceValidatorInterceptor) checkNamespaceState(namespaceEntry *namespace.Namespace, fullMethod string) error {
   330  	if namespaceEntry == nil {
   331  		return nil
   332  	}
   333  
   334  	_, methodName := SplitMethodName(fullMethod)
   335  
   336  	allowedStates, allowedStatesDefined := allowedNamespaceStates[methodName]
   337  	if !allowedStatesDefined {
   338  		allowedStates = defaultAllowedNamespaceStates
   339  	}
   340  
   341  	for _, allowedState := range allowedStates {
   342  		if allowedState == namespaceEntry.State() {
   343  			return nil
   344  		}
   345  	}
   346  
   347  	return serviceerror.NewNamespaceInvalidState(namespaceEntry.Name().String(), namespaceEntry.State(), allowedStates)
   348  }
   349  
   350  func (ni *NamespaceValidatorInterceptor) checkReplicationState(namespaceEntry *namespace.Namespace, fullMethod string) error {
   351  	if namespaceEntry == nil {
   352  		return nil
   353  	}
   354  	if namespaceEntry.ReplicationState() != enumspb.REPLICATION_STATE_HANDOVER {
   355  		return nil
   356  	}
   357  
   358  	_, methodName := SplitMethodName(fullMethod)
   359  
   360  	if _, ok := allowedMethodsDuringHandover[methodName]; ok {
   361  		return nil
   362  	}
   363  
   364  	return common.ErrNamespaceHandover
   365  }