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 }