mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed? Added `context.Context` as the first parameter to the `callback.Validator` interface and threaded it through all call sites (`WorkflowHandler`, `activity.FrontendHandler`, and their internal validation/preparation methods). The base validator ignores the context. No behavioral change. ## Why? Deployments that decorate the validator (via `fx.Decorate`) need access to the gRPC context to check caller identity — e.g. to allow internal callers (scheduler) to attach `temporal://internal` callbacks while rejecting them from external users. ## How did you test it? - [X] built - [X] run locally and tested manually - [X] covered by existing tests - [ ] added new unit test(s) - [ ] added new functional test(s)
154 lines
4.7 KiB
Go
154 lines
4.7 KiB
Go
package callback
|
|
|
|
import (
|
|
"context"
|
|
"regexp"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
commonpb "go.temporal.io/api/common/v1"
|
|
"go.temporal.io/api/serviceerror"
|
|
)
|
|
|
|
func TestValidateCallbacks(t *testing.T) {
|
|
allowAll := AddressMatchRules{
|
|
Rules: []AddressMatchRule{
|
|
{Regexp: regexp.MustCompile(`.*`), AllowInsecure: true},
|
|
},
|
|
}
|
|
v := NewValidator(
|
|
func(string) int { return 10 },
|
|
func(string) int { return 1000 },
|
|
func(string) int { return 4096 },
|
|
func(string) AddressMatchRules { return allowAll },
|
|
)
|
|
|
|
t.Run("ValidNexusCallback", func(t *testing.T) {
|
|
cbs := []*commonpb.Callback{
|
|
{Variant: &commonpb.Callback_Nexus_{
|
|
Nexus: &commonpb.Callback_Nexus{
|
|
Url: "http://localhost:8080/callback",
|
|
Header: map[string]string{"Content-Type": "application/json"},
|
|
},
|
|
}},
|
|
}
|
|
err := v.Validate(context.Background(), "ns", cbs)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("TooManyCallbacks", func(t *testing.T) {
|
|
v := NewValidator(
|
|
func(string) int { return 1 },
|
|
func(string) int { return 1000 },
|
|
func(string) int { return 4096 },
|
|
func(string) AddressMatchRules { return allowAll },
|
|
)
|
|
cbs := []*commonpb.Callback{
|
|
{Variant: &commonpb.Callback_Nexus_{Nexus: &commonpb.Callback_Nexus{Url: "http://localhost/cb1"}}},
|
|
{Variant: &commonpb.Callback_Nexus_{Nexus: &commonpb.Callback_Nexus{Url: "http://localhost/cb2"}}},
|
|
}
|
|
err := v.Validate(context.Background(), "ns", cbs)
|
|
var invalidArgErr *serviceerror.InvalidArgument
|
|
require.ErrorAs(t, err, &invalidArgErr)
|
|
require.Contains(t, err.Error(), "cannot attach more than 1 callbacks")
|
|
})
|
|
|
|
t.Run("URLTooLong", func(t *testing.T) {
|
|
v := NewValidator(
|
|
func(string) int { return 10 },
|
|
func(string) int { return 50 },
|
|
func(string) int { return 4096 },
|
|
func(string) AddressMatchRules { return allowAll },
|
|
)
|
|
cbs := []*commonpb.Callback{
|
|
{Variant: &commonpb.Callback_Nexus_{
|
|
Nexus: &commonpb.Callback_Nexus{
|
|
Url: "http://localhost/" + string(make([]byte, 51)),
|
|
},
|
|
}},
|
|
}
|
|
err := v.Validate(context.Background(), "ns", cbs)
|
|
var invalidArgErr *serviceerror.InvalidArgument
|
|
require.ErrorAs(t, err, &invalidArgErr)
|
|
require.Contains(t, err.Error(), "url length longer than max length allowed")
|
|
})
|
|
|
|
t.Run("HeaderTooLarge", func(t *testing.T) {
|
|
cbs := []*commonpb.Callback{
|
|
{Variant: &commonpb.Callback_Nexus_{
|
|
Nexus: &commonpb.Callback_Nexus{
|
|
Url: "http://localhost:8080/callback",
|
|
Header: map[string]string{"X-Large": string(make([]byte, 5000))},
|
|
},
|
|
}},
|
|
}
|
|
err := v.Validate(context.Background(), "ns", cbs)
|
|
var invalidArgErr *serviceerror.InvalidArgument
|
|
require.ErrorAs(t, err, &invalidArgErr)
|
|
require.Contains(t, err.Error(), "header size longer than max allowed size")
|
|
})
|
|
|
|
t.Run("HeaderKeysNormalizedToLowercase", func(t *testing.T) {
|
|
cbs := []*commonpb.Callback{
|
|
{Variant: &commonpb.Callback_Nexus_{
|
|
Nexus: &commonpb.Callback_Nexus{
|
|
Url: "http://localhost:8080/callback",
|
|
Header: map[string]string{"Content-Type": "application/json", "X-Custom": "value"},
|
|
},
|
|
}},
|
|
}
|
|
err := v.Validate(context.Background(), "ns", cbs)
|
|
require.NoError(t, err)
|
|
nexus := cbs[0].GetNexus()
|
|
require.Equal(t, "application/json", nexus.Header["content-type"])
|
|
require.Equal(t, "value", nexus.Header["x-custom"])
|
|
_, hasMixed := nexus.Header["Content-Type"]
|
|
require.False(t, hasMixed)
|
|
})
|
|
|
|
t.Run("URLNotInAllowlist", func(t *testing.T) {
|
|
v := NewValidator(
|
|
func(string) int { return 10 },
|
|
func(string) int { return 1000 },
|
|
func(string) int { return 4096 },
|
|
func(string) AddressMatchRules { return AddressMatchRules{} },
|
|
)
|
|
cbs := []*commonpb.Callback{
|
|
{Variant: &commonpb.Callback_Nexus_{
|
|
Nexus: &commonpb.Callback_Nexus{
|
|
Url: "http://localhost:8080/callback",
|
|
},
|
|
}},
|
|
}
|
|
err := v.Validate(context.Background(), "ns", cbs)
|
|
var invalidArgErr *serviceerror.InvalidArgument
|
|
require.ErrorAs(t, err, &invalidArgErr)
|
|
require.Contains(t, err.Error(), "does not match any configured callback address")
|
|
})
|
|
|
|
t.Run("UnsupportedVariant", func(t *testing.T) {
|
|
cbs := []*commonpb.Callback{
|
|
{Variant: nil},
|
|
}
|
|
err := v.Validate(context.Background(), "ns", cbs)
|
|
var unimplementedErr *serviceerror.Unimplemented
|
|
require.ErrorAs(t, err, &unimplementedErr)
|
|
require.Contains(t, err.Error(), "unknown callback variant")
|
|
})
|
|
|
|
t.Run("EmptyCallbacksNoError", func(t *testing.T) {
|
|
err := v.Validate(context.Background(), "ns", nil)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("InternalCallbackSkipped", func(t *testing.T) {
|
|
cbs := []*commonpb.Callback{
|
|
{Variant: &commonpb.Callback_Internal_{
|
|
Internal: &commonpb.Callback_Internal{},
|
|
}},
|
|
}
|
|
err := v.Validate(context.Background(), "ns", cbs)
|
|
require.NoError(t, err)
|
|
})
|
|
}
|