pomerium/config/policy_test.go
Caleb Doxsey 8bc86fe06f
config: add route name, description and logo (#5424)
* config: add route name, description and logo

* remove name generation
2025-01-14 14:55:14 -07:00

622 lines
32 KiB
Go

package config
import (
"encoding/json"
"fmt"
mathrand "math/rand/v2"
"net/url"
"strings"
"testing"
envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/pkg/cryptutil"
)
func Test_PolicyValidate(t *testing.T) {
t.Parallel()
tests := []struct {
name string
policy Policy
wantErr bool
}{
{"good", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld")}, false},
{"empty to host", Policy{From: "https://httpbin.corp.example", To: []WeightedURL{{URL: url.URL{Scheme: "https", Path: "/"}}}}, true},
{"empty from host", Policy{From: "https://", To: mustParseWeightedURLs(t, "https://httpbin.corp.example")}, true},
{"empty from scheme", Policy{From: "httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.example")}, true},
{"empty to scheme", Policy{From: "https://httpbin.corp.example", To: []WeightedURL{{URL: url.URL{Host: "httpbin.corp.example"}}}}, true},
{"path in from", Policy{From: "https://httpbin.corp.example/some/path", To: mustParseWeightedURLs(t, "https://httpbin.corp.example")}, true},
{"cors policy", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), CORSAllowPreflight: true}, false},
{"public policy", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), AllowPublicUnauthenticatedAccess: true}, false},
{"public and whitelist", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), AllowPublicUnauthenticatedAccess: true, AllowedUsers: []string{"test@domain.example"}}, true},
{"route must have", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), AllowPublicUnauthenticatedAccess: true, AllowedUsers: []string{"test@domain.example"}}, true},
{"any authenticated user policy", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), AllowAnyAuthenticatedUser: true}, false},
{"any authenticated user and whitelist", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), AllowAnyAuthenticatedUser: true, AllowedUsers: []string{"test@domain.example"}}, true},
{"good client cert", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), TLSClientKey: "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcGdJQkFBS0NBUUVBNjdLanFtUVlHcTBNVnRBQ1ZwZUNtWG1pbmxRYkRQR0xtc1pBVUV3dWVIUW5ydDNXCnR2cERPbTZBbGFKTVVuVytIdTU1ampva2FsS2VWalRLbWdZR2JxVXpWRG9NYlBEYUhla2x0ZEJUTUdsT1VGc1AKNFVKU0RyTzR6ZE4rem80MjhUWDJQbkcyRkNkVktHeTRQRThpbEhiV0xjcjg3MVlqVjUxZnc4Q0xEWDlQWkpOdQo4NjFDRjdWOWlFSm02c1NmUWxtbmhOOGozK1d6VmJQUU55MVdzUjdpOWU5ajYzRXFLdDIyUTlPWEwrV0FjS3NrCm9JU21DTlZSVUFqVThZUlZjZ1FKQit6UTM0QVFQbHowT3A1Ty9RTi9NZWRqYUY4d0xTK2l2L3p2aVM4Y3FQYngKbzZzTHE2Rk5UbHRrL1FreGVDZUtLVFFlLzNrUFl2UUFkbmw2NVFJREFRQUJBb0lCQVFEQVQ0eXN2V2pSY3pxcgpKcU9SeGFPQTJEY3dXazJML1JXOFhtQWhaRmRTWHV2MkNQbGxhTU1yelBmTG41WUlmaHQzSDNzODZnSEdZc3pnClo4aWJiYWtYNUdFQ0t5N3lRSDZuZ3hFS3pRVGpiampBNWR3S0h0UFhQUnJmamQ1Y2FMczVpcDcxaWxCWEYxU3IKWERIaXUycnFtaC9kVTArWGRMLzNmK2VnVDl6bFQ5YzRyUm84dnZueWNYejFyMnVhRVZ2VExsWHVsb2NpeEVrcgoySjlTMmxveWFUb2tFTnNlMDNpSVdaWnpNNElZcVowOGJOeG9IWCszQXVlWExIUStzRkRKMlhaVVdLSkZHMHUyClp3R2w3YlZpRTFQNXdiQUdtZzJDeDVCN1MrdGQyUEpSV3Frb2VxY3F2RVdCc3RFL1FEcDFpVThCOHpiQXd0Y3IKZHc5TXZ6Q2hBb0dCQVBObzRWMjF6MGp6MWdEb2tlTVN5d3JnL2E4RkJSM2R2Y0xZbWV5VXkybmd3eHVucnFsdwo2U2IrOWdrOGovcXEvc3VQSDhVdzNqSHNKYXdGSnNvTkVqNCt2b1ZSM3UrbE5sTEw5b21rMXBoU0dNdVp0b3huCm5nbUxVbkJUMGI1M3BURkJ5WGsveE5CbElreWdBNlg5T2MreW5na3RqNlRyVnMxUERTdnVJY0s1QW9HQkFQZmoKcEUzR2F6cVFSemx6TjRvTHZmQWJBdktCZ1lPaFNnemxsK0ZLZkhzYWJGNkdudFd1dWVhY1FIWFpYZTA1c2tLcApXN2xYQ3dqQU1iUXI3QmdlazcrOSszZElwL1RnYmZCYnN3Syt6Vng3Z2doeWMrdytXRWExaHByWTZ6YXdxdkFaCkhRU2lMUEd1UGp5WXBQa1E2ZFdEczNmWHJGZ1dlTmd4SkhTZkdaT05Bb0dCQUt5WTF3MUM2U3Y2c3VuTC8vNTcKQ2Z5NTAwaXlqNUZBOWRqZkRDNWt4K1JZMnlDV0ExVGsybjZyVmJ6dzg4czBTeDMrYS9IQW1CM2dMRXBSRU5NKwo5NHVwcENFWEQ3VHdlcGUxUnlrTStKbmp4TzlDSE41c2J2U25sUnBQWlMvZzJRTVhlZ3grK2trbkhXNG1ITkFyCndqMlRrMXBBczFXbkJ0TG9WaGVyY01jSkFvR0JBSTYwSGdJb0Y5SysvRUcyY21LbUg5SDV1dGlnZFU2eHEwK0IKWE0zMWMzUHE0amdJaDZlN3pvbFRxa2d0dWtTMjBraE45dC9ibkI2TmhnK1N1WGVwSXFWZldVUnlMejVwZE9ESgo2V1BMTTYzcDdCR3cwY3RPbU1NYi9VRm5Yd0U4OHlzRlNnOUF6VjdVVUQvU0lDYkI5ZHRVMWh4SHJJK0pZRWdWCkFrZWd6N2lCQW9HQkFJRncrQVFJZUIwM01UL0lCbGswNENQTDJEak0rNDhoVGRRdjgwMDBIQU9mUWJrMEVZUDEKQ2FLR3RDbTg2MXpBZjBzcS81REtZQ0l6OS9HUzNYRk00Qm1rRk9nY1NXVENPNmZmTGdLM3FmQzN4WDJudlpIOQpYZGNKTDQrZndhY0x4c2JJKzhhUWNOVHRtb3pkUjEzQnNmUmIrSGpUL2o3dkdrYlFnSkhCT0syegotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=", TLSClientCert: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVJVENDQWdtZ0F3SUJBZ0lSQVBqTEJxS1lwcWU0ekhQc0dWdFR6T0F3RFFZSktvWklodmNOQVFFTEJRQXcKRWpFUU1BNEdBMVVFQXhNSFoyOXZaQzFqWVRBZUZ3MHhPVEE0TVRBeE9EUTVOREJhRncweU1UQXlNVEF4TnpRdwpNREZhTUJNeEVUQVBCZ05WQkFNVENIQnZiV1Z5YVhWdE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBCk1JSUJDZ0tDQVFFQTY3S2pxbVFZR3EwTVZ0QUNWcGVDbVhtaW5sUWJEUEdMbXNaQVVFd3VlSFFucnQzV3R2cEQKT202QWxhSk1VblcrSHU1NWpqb2thbEtlVmpUS21nWUdicVV6VkRvTWJQRGFIZWtsdGRCVE1HbE9VRnNQNFVKUwpEck80emROK3pvNDI4VFgyUG5HMkZDZFZLR3k0UEU4aWxIYldMY3I4NzFZalY1MWZ3OENMRFg5UFpKTnU4NjFDCkY3VjlpRUptNnNTZlFsbW5oTjhqMytXelZiUFFOeTFXc1I3aTllOWo2M0VxS3QyMlE5T1hMK1dBY0tza29JU20KQ05WUlVBalU4WVJWY2dRSkIrelEzNEFRUGx6ME9wNU8vUU4vTWVkamFGOHdMUytpdi96dmlTOGNxUGJ4bzZzTApxNkZOVGx0ay9Ra3hlQ2VLS1RRZS8za1BZdlFBZG5sNjVRSURBUUFCbzNFd2J6QU9CZ05WSFE4QkFmOEVCQU1DCkE3Z3dIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUIwR0ExVWREZ1FXQkJRQ1FYbWIKc0hpcS9UQlZUZVhoQ0dpNjhrVy9DakFmQmdOVkhTTUVHREFXZ0JSNTRKQ3pMRlg0T0RTQ1J0dWNBUGZOdVhWegpuREFOQmdrcWhraUc5dzBCQVFzRkFBT0NBZ0VBcm9XL2trMllleFN5NEhaQXFLNDVZaGQ5ay9QVTFiaDlFK1BRCk5jZFgzTUdEY2NDRUFkc1k4dll3NVE1cnhuMGFzcSt3VGFCcGxoYS9rMi9VVW9IQ1RqUVp1Mk94dEF3UTdPaWIKVE1tMEorU3NWT3d4YnFQTW9rK1RqVE16NFdXaFFUTzVwRmNoZDZXZXNCVHlJNzJ0aG1jcDd1c2NLU2h3YktIegpQY2h1QTQ4SzhPdi96WkxmZnduQVNZb3VCczJjd1ZiRDI3ZXZOMzdoMGFzR1BrR1VXdm1PSDduTHNVeTh3TTdqCkNGL3NwMmJmTC9OYVdNclJnTHZBMGZMS2pwWTQrVEpPbkVxQmxPcCsrbHlJTEZMcC9qMHNybjRNUnlKK0t6UTEKR1RPakVtQ1QvVEFtOS9XSThSL0FlYjcwTjEzTytYNEtaOUJHaDAxTzN3T1Vqd3BZZ3lxSnNoRnNRUG50VmMrSQpKQmF4M2VQU3NicUcwTFkzcHdHUkpRNmMrd1lxdGk2Y0tNTjliYlRkMDhCNUk1N1RRTHhNcUoycTFnWmw1R1VUCmVFZGNWRXltMnZmd0NPd0lrbGNBbThxTm5kZGZKV1FabE5VaHNOVWFBMkVINnlDeXdaZm9aak9hSDEwTXowV20KeTNpZ2NSZFQ3Mi9NR2VkZk93MlV0MVVvRFZmdEcxcysrditUQ1lpNmpUQU05dkZPckJ4UGlOeGFkUENHR2NZZAowakZIc2FWOGFPV1dQQjZBQ1JteHdDVDdRTnRTczM2MlpIOUlFWWR4Q00yMDUrZmluVHhkOUcwSmVRRTd2Kyt6CldoeWo2ZmJBWUIxM2wvN1hkRnpNSW5BOGxpekdrVHB2RHMxeTBCUzlwV3ppYmhqbVFoZGZIejdCZGpGTHVvc2wKZzlNZE5sND0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="}, false},
{"bad base64 client cert", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), TLSClientKey: "!=", TLSClientCert: "!="}, true},
{"bad one client cert empty", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), TLSClientKey: "", TLSClientCert: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVJVENDQWdtZ0F3SUJBZ0lSQVBqTEJxS1lwcWU0ekhQc0dWdFR6T0F3RFFZSktvWklodmNOQVFFTEJRQXcKRWpFUU1BNEdBMVVFQXhNSFoyOXZaQzFqWVRBZUZ3MHhPVEE0TVRBeE9EUTVOREJhRncweU1UQXlNVEF4TnpRdwpNREZhTUJNeEVUQVBCZ05WQkFNVENIQnZiV1Z5YVhWdE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBCk1JSUJDZ0tDQVFFQTY3S2pxbVFZR3EwTVZ0QUNWcGVDbVhtaW5sUWJEUEdMbXNaQVVFd3VlSFFucnQzV3R2cEQKT202QWxhSk1VblcrSHU1NWpqb2thbEtlVmpUS21nWUdicVV6VkRvTWJQRGFIZWtsdGRCVE1HbE9VRnNQNFVKUwpEck80emROK3pvNDI4VFgyUG5HMkZDZFZLR3k0UEU4aWxIYldMY3I4NzFZalY1MWZ3OENMRFg5UFpKTnU4NjFDCkY3VjlpRUptNnNTZlFsbW5oTjhqMytXelZiUFFOeTFXc1I3aTllOWo2M0VxS3QyMlE5T1hMK1dBY0tza29JU20KQ05WUlVBalU4WVJWY2dRSkIrelEzNEFRUGx6ME9wNU8vUU4vTWVkamFGOHdMUytpdi96dmlTOGNxUGJ4bzZzTApxNkZOVGx0ay9Ra3hlQ2VLS1RRZS8za1BZdlFBZG5sNjVRSURBUUFCbzNFd2J6QU9CZ05WSFE4QkFmOEVCQU1DCkE3Z3dIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUIwR0ExVWREZ1FXQkJRQ1FYbWIKc0hpcS9UQlZUZVhoQ0dpNjhrVy9DakFmQmdOVkhTTUVHREFXZ0JSNTRKQ3pMRlg0T0RTQ1J0dWNBUGZOdVhWegpuREFOQmdrcWhraUc5dzBCQVFzRkFBT0NBZ0VBcm9XL2trMllleFN5NEhaQXFLNDVZaGQ5ay9QVTFiaDlFK1BRCk5jZFgzTUdEY2NDRUFkc1k4dll3NVE1cnhuMGFzcSt3VGFCcGxoYS9rMi9VVW9IQ1RqUVp1Mk94dEF3UTdPaWIKVE1tMEorU3NWT3d4YnFQTW9rK1RqVE16NFdXaFFUTzVwRmNoZDZXZXNCVHlJNzJ0aG1jcDd1c2NLU2h3YktIegpQY2h1QTQ4SzhPdi96WkxmZnduQVNZb3VCczJjd1ZiRDI3ZXZOMzdoMGFzR1BrR1VXdm1PSDduTHNVeTh3TTdqCkNGL3NwMmJmTC9OYVdNclJnTHZBMGZMS2pwWTQrVEpPbkVxQmxPcCsrbHlJTEZMcC9qMHNybjRNUnlKK0t6UTEKR1RPakVtQ1QvVEFtOS9XSThSL0FlYjcwTjEzTytYNEtaOUJHaDAxTzN3T1Vqd3BZZ3lxSnNoRnNRUG50VmMrSQpKQmF4M2VQU3NicUcwTFkzcHdHUkpRNmMrd1lxdGk2Y0tNTjliYlRkMDhCNUk1N1RRTHhNcUoycTFnWmw1R1VUCmVFZGNWRXltMnZmd0NPd0lrbGNBbThxTm5kZGZKV1FabE5VaHNOVWFBMkVINnlDeXdaZm9aak9hSDEwTXowV20KeTNpZ2NSZFQ3Mi9NR2VkZk93MlV0MVVvRFZmdEcxcysrditUQ1lpNmpUQU05dkZPckJ4UGlOeGFkUENHR2NZZAowakZIc2FWOGFPV1dQQjZBQ1JteHdDVDdRTnRTczM2MlpIOUlFWWR4Q00yMDUrZmluVHhkOUcwSmVRRTd2Kyt6CldoeWo2ZmJBWUIxM2wvN1hkRnpNSW5BOGxpekdrVHB2RHMxeTBCUzlwV3ppYmhqbVFoZGZIejdCZGpGTHVvc2wKZzlNZE5sND0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="}, true},
{"bad th other client cert empty", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), TLSClientKey: "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcGdJQkFBS0NBUUVBNjdLanFtUVlHcTBNVnRBQ1ZwZUNtWG1pbmxRYkRQR0xtc1pBVUV3dWVIUW5ydDNXCnR2cERPbTZBbGFKTVVuVytIdTU1ampva2FsS2VWalRLbWdZR2JxVXpWRG9NYlBEYUhla2x0ZEJUTUdsT1VGc1AKNFVKU0RyTzR6ZE4rem80MjhUWDJQbkcyRkNkVktHeTRQRThpbEhiV0xjcjg3MVlqVjUxZnc4Q0xEWDlQWkpOdQo4NjFDRjdWOWlFSm02c1NmUWxtbmhOOGozK1d6VmJQUU55MVdzUjdpOWU5ajYzRXFLdDIyUTlPWEwrV0FjS3NrCm9JU21DTlZSVUFqVThZUlZjZ1FKQit6UTM0QVFQbHowT3A1Ty9RTi9NZWRqYUY4d0xTK2l2L3p2aVM4Y3FQYngKbzZzTHE2Rk5UbHRrL1FreGVDZUtLVFFlLzNrUFl2UUFkbmw2NVFJREFRQUJBb0lCQVFEQVQ0eXN2V2pSY3pxcgpKcU9SeGFPQTJEY3dXazJML1JXOFhtQWhaRmRTWHV2MkNQbGxhTU1yelBmTG41WUlmaHQzSDNzODZnSEdZc3pnClo4aWJiYWtYNUdFQ0t5N3lRSDZuZ3hFS3pRVGpiampBNWR3S0h0UFhQUnJmamQ1Y2FMczVpcDcxaWxCWEYxU3IKWERIaXUycnFtaC9kVTArWGRMLzNmK2VnVDl6bFQ5YzRyUm84dnZueWNYejFyMnVhRVZ2VExsWHVsb2NpeEVrcgoySjlTMmxveWFUb2tFTnNlMDNpSVdaWnpNNElZcVowOGJOeG9IWCszQXVlWExIUStzRkRKMlhaVVdLSkZHMHUyClp3R2w3YlZpRTFQNXdiQUdtZzJDeDVCN1MrdGQyUEpSV3Frb2VxY3F2RVdCc3RFL1FEcDFpVThCOHpiQXd0Y3IKZHc5TXZ6Q2hBb0dCQVBObzRWMjF6MGp6MWdEb2tlTVN5d3JnL2E4RkJSM2R2Y0xZbWV5VXkybmd3eHVucnFsdwo2U2IrOWdrOGovcXEvc3VQSDhVdzNqSHNKYXdGSnNvTkVqNCt2b1ZSM3UrbE5sTEw5b21rMXBoU0dNdVp0b3huCm5nbUxVbkJUMGI1M3BURkJ5WGsveE5CbElreWdBNlg5T2MreW5na3RqNlRyVnMxUERTdnVJY0s1QW9HQkFQZmoKcEUzR2F6cVFSemx6TjRvTHZmQWJBdktCZ1lPaFNnemxsK0ZLZkhzYWJGNkdudFd1dWVhY1FIWFpYZTA1c2tLcApXN2xYQ3dqQU1iUXI3QmdlazcrOSszZElwL1RnYmZCYnN3Syt6Vng3Z2doeWMrdytXRWExaHByWTZ6YXdxdkFaCkhRU2lMUEd1UGp5WXBQa1E2ZFdEczNmWHJGZ1dlTmd4SkhTZkdaT05Bb0dCQUt5WTF3MUM2U3Y2c3VuTC8vNTcKQ2Z5NTAwaXlqNUZBOWRqZkRDNWt4K1JZMnlDV0ExVGsybjZyVmJ6dzg4czBTeDMrYS9IQW1CM2dMRXBSRU5NKwo5NHVwcENFWEQ3VHdlcGUxUnlrTStKbmp4TzlDSE41c2J2U25sUnBQWlMvZzJRTVhlZ3grK2trbkhXNG1ITkFyCndqMlRrMXBBczFXbkJ0TG9WaGVyY01jSkFvR0JBSTYwSGdJb0Y5SysvRUcyY21LbUg5SDV1dGlnZFU2eHEwK0IKWE0zMWMzUHE0amdJaDZlN3pvbFRxa2d0dWtTMjBraE45dC9ibkI2TmhnK1N1WGVwSXFWZldVUnlMejVwZE9ESgo2V1BMTTYzcDdCR3cwY3RPbU1NYi9VRm5Yd0U4OHlzRlNnOUF6VjdVVUQvU0lDYkI5ZHRVMWh4SHJJK0pZRWdWCkFrZWd6N2lCQW9HQkFJRncrQVFJZUIwM01UL0lCbGswNENQTDJEak0rNDhoVGRRdjgwMDBIQU9mUWJrMEVZUDEKQ2FLR3RDbTg2MXpBZjBzcS81REtZQ0l6OS9HUzNYRk00Qm1rRk9nY1NXVENPNmZmTGdLM3FmQzN4WDJudlpIOQpYZGNKTDQrZndhY0x4c2JJKzhhUWNOVHRtb3pkUjEzQnNmUmIrSGpUL2o3dkdrYlFnSkhCT0syegotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=", TLSClientCert: ""}, true},
{"good root ca pool", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), TLSCustomCA: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUU1RENDQXN5Z0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREFTTVJBd0RnWURWUVFERXdkbmIyOWsKTFdOaE1CNFhEVEU1TURneE1ERTNOREF3TWxvWERUSXhNREl4TURFM05EQXdNbG93RWpFUU1BNEdBMVVFQXhNSApaMjl2WkMxallUQ0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQUw3b2VldEovNmNFCkdicTcvanNtcU9FM2VyVE1aRHR0eFM4STVGV1c0TkRXbWNpOE5IdWRMZDhlM1JtOEh6Y09jSjRQL0ErcDVsYmsKTjhySzY4OUlsQzhqM28yaEhSdEk2T21saFY3NEoxaUlIOGtkSXU2V2xPMWtOdUx5dGRrbjhRaytJOUNEWjlGSAorZzhRbnVka0tMUWJkZFdDVXJzUjR4cEcyK0VkNWdua0JJNG4zbmNLMFgvWEZocWhDTEU1eFBaQk5OWktGbHJxCm1lYUl4dHoyc2ZvWVY1NmcwMnNGS1QxSUlMNTVFMG14djRUa2JtSWw5Rk9qZEtCdkhFZnJHeXl5OFRGTHErUzMKTXo2em9xNDhuOEhGMUc5cHBLVk9OMUp0Mks1UWEvV2hpbjVrcWNhYTNwNE0vN2tiNmtxU0tMWG1iN0gyN3kvVQpEYjZDUG01d2lodjA2c1FobXN2MHhuS2hqMm8vQzhlcWxzNzZZWDF1Y2NqMzlmSTRlQ1E4cENFbTlVcDh5ZkkvCkxlYVpXbGE0NEZneWw3N1lyc2MvM0U5dk1hS0ZVeGRjR3VtMXQrNUZZYWpkY0EvTlFreTJBeTJqcHRwVXV1SFUKNnhYSzdEcXY5Z01jQS8zM1VYOFpHZklPRk0rY3FlOTQxaTVPT1hGSHJoRDlqeTRQR2M4Z2kxSTRyK1VXd0tCYgoxSGg1clQ3ckJZK1NLTTBzZmtpQlZ1RU9pbnk2dDF1Z2tEdjY4dXNFWFlIWlZXaWl6b1hmcDVHbjZmckUvd1IxCkRkak13TGEvT2tQTnVEVVQ4eU1GS2hWRnFHcXdHQzY2bys1cjQyMlVwa0s4SHJ5K2tsQ3pUTys3U0RodTJiWk4KUVFGT0NLSVVldnR3bGdabVBNck1BNTZ3dzVSSnNhVnhBZ01CQUFHalJUQkRNQTRHQTFVZER3RUIvd1FFQXdJQgpCakFTQmdOVkhSTUJBZjhFQ0RBR0FRSC9BZ0VBTUIwR0ExVWREZ1FXQkJSNTRKQ3pMRlg0T0RTQ1J0dWNBUGZOCnVYVnpuREFOQmdrcWhraUc5dzBCQVFzRkFBT0NBZ0VBZituUmpBVnZuT0pSckpBQWpKWVY3aVF3bHExUXZYRGcKbHZhY0JoVFJyWFh4OW5GaVRZUzV4MkFMbXZ5WHhubTdIS2VDSUZEclJwOE5MVFkyYjJXR01BcTFxc3JBT0QvegpTNmNSSW1OQ21QNmd0UHNUNDlabzBYajNrZjZyTXBPeHBiSUlnSmZMY056UGZpL25jeC9oRDNBOHl6Zk4wQTZZCnFFd2QvSkZPajdEa3RaQmdlSXZETlJXS0pveEpJRlZ4anJqLzFiVmkxZTRWVjVvWmhOako4SzlyV1FRK1EvK3QKZ3lGK0sycGxDQ1RiRWR6eU9heDY1djh5UDJ5RCs2WkFIRk9sRjI2TnZpUkw4OWJ1VHIwaEpZa0N5VXZ3MmJZaQo4Q3MyWDZkd0NDdXVhZUdVR2VRemszMGxQeUdWSmVKL3ZJMGJRSzlpZ2I5dFozY3d0WHBQdjN6a1B1TDE3d01WCitCMXo2RW1HZVVLNXlTQ0xFWjc2aVliNU0vY3ZjTUVOMWdoeFNIN0FmaDhMS0c0eWszT21SQ253akVqdTFhaWoKZGs3cjJuc0xmYU9KWFBRNU1wMzRYU1ltdTlpTVl0VytMbWZiSDJxMW9vS3dKZDhHNVhhRWRmQmpHUEQ5Q3FkWAphSlh0MDA0cVdsalJOS3p1MFNFRmJ6UldGNHRoeXlUTzE4QVI4eTNHV0Vwak95amdKSzlFeU1sQm9Qa3RYQVVVCjZzTFhqT3ZZU0ovd202NUhxVVZBTTVsRy96WVN3TGdCTDAwc1pJKzVGa0QwblU0Rkx6QWRLV05LWkRXZFVNbUwKVi9lV0ZGNGwwVFBvNTVhM0pUL1BGc2J0RFBLVWxvWVFXeTFybmFqR3J1L0Y5bGRCcHB1bUVUa2FOS2ZWT05Jcgp4cERnc1FhVkVXOD0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="}, false},
{"bad root ca pool", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), TLSCustomCA: "!"}, true},
{"good custom ca file", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), TLSCustomCAFile: "testdata/ca.pem"}, false},
{"bad custom ca file", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), TLSCustomCAFile: "testdata/404.pem"}, true},
{"good client certificate files", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), TLSClientCertFile: "testdata/example-cert.pem", TLSClientKeyFile: "testdata/example-key.pem"}, false},
{"bad certificate file", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), TLSClientCertFile: "testdata/example-cert-404.pem", TLSClientKeyFile: "testdata/example-key.pem"}, true},
{"bad key file", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://httpbin.corp.notatld"), TLSClientCertFile: "testdata/example-cert.pem", TLSClientKeyFile: "testdata/example-key-404.pem"}, true},
{"good tls server name", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://internal-host-name"), TLSServerName: "httpbin.corp.notatld"}, false},
{"good kube service account token file", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://internal-host-name"), KubernetesServiceAccountTokenFile: "testdata/kubeserviceaccount.token"}, false},
{"good kube service account token", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://internal-host-name"), KubernetesServiceAccountToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1OTY1MDk4MjIsImV4cCI6MTYyODA0NTgyMiwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.H0I6ccQrL6sKobsKQj9dqNcLw_INhU9_xJsVyCkgkiY"}, false},
{"bad kube service account token and file", Policy{From: "https://httpbin.corp.example", To: mustParseWeightedURLs(t, "https://internal-host-name"), KubernetesServiceAccountToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1OTY1MDk4MjIsImV4cCI6MTYyODA0NTgyMiwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.H0I6ccQrL6sKobsKQj9dqNcLw_INhU9_xJsVyCkgkiY", KubernetesServiceAccountTokenFile: "testdata/kubeserviceaccount.token"}, true},
{"TCP To URLs", Policy{From: "tcp+https://httpbin.corp.example:4000", To: mustParseWeightedURLs(t, "tcp://one.example.com:5000", "tcp://two.example.com:5000")}, false},
{"mix of TCP and non-TCP To URLs", Policy{From: "tcp+https://httpbin.corp.example:4000", To: mustParseWeightedURLs(t, "https://example.com", "tcp://example.com:5000")}, true},
{"UDP To URLs", Policy{From: "udp+https://httpbin.corp.example:4000", To: mustParseWeightedURLs(t, "udp://one.example.com:5000", "udp://two.example.com:5000")}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.policy.Validate()
if (err != nil) != tt.wantErr {
t.Errorf("Validate() error = %v, want %v", err, tt.wantErr)
}
})
}
}
func Test_PolicyValidate_RedirectResponseCode(t *testing.T) {
t.Parallel()
var r PolicyRedirect
p := Policy{
From: "http://example.com",
Redirect: &r,
}
cases := []struct {
Code *int32
ExpectedError string
}{
{nil, ""},
{proto.Int32(0), "unsupported redirect response code 0"},
{proto.Int32(100), "unsupported redirect response code 100"},
{proto.Int32(200), "unsupported redirect response code 200"},
{proto.Int32(300), "unsupported redirect response code 300"},
{proto.Int32(301), ""},
{proto.Int32(302), ""},
{proto.Int32(303), ""},
{proto.Int32(304), "unsupported redirect response code 304"},
{proto.Int32(305), "unsupported redirect response code 305"},
{proto.Int32(306), "unsupported redirect response code 306"},
{proto.Int32(307), ""},
{proto.Int32(308), ""},
{proto.Int32(309), "unsupported redirect response code 309"},
{proto.Int32(400), "unsupported redirect response code 400"},
{proto.Int32(500), "unsupported redirect response code 500"},
{proto.Int32(600), "unsupported redirect response code 600"},
}
for i := range cases {
c := &cases[i]
t.Run(fmt.Sprint(c.Code), func(t *testing.T) {
r.ResponseCode = c.Code
err := p.Validate()
if c.ExpectedError != "" {
assert.ErrorContains(t, err, c.ExpectedError)
} else {
assert.NoError(t, err)
}
})
}
}
func TestPolicy_String(t *testing.T) {
t.Parallel()
tests := []struct {
name string
From string
To []WeightedURL
want string
wantFrom string
}{
{"good", "https://pomerium.io", []WeightedURL{{URL: url.URL{Scheme: "https", Host: "localhost"}}}, "https://pomerium.io → https://localhost", `"https://pomerium.io"`},
{"invalid", "https://pomerium.io", nil, "https://pomerium.io → ?", `"https://pomerium.io"`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &Policy{
From: tt.From,
To: tt.To,
}
p.Validate()
if got := p.String(); got != tt.want {
t.Errorf("Policy.String() = %v, want %v", got, tt.want)
}
out, err := json.Marshal(p.From)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(string(out), tt.wantFrom); diff != "" {
t.Errorf("json diff() = %s", diff)
}
})
}
}
func Test_PolicyRouteID(t *testing.T) {
t.Parallel()
tests := []struct {
name string
basePolicy *Policy
comparePolicy *Policy
wantSame bool
}{
{
"same",
&Policy{From: "https://pomerium.io", To: mustParseWeightedURLs(t, "http://localhost"), AllowedUsers: []string{"foo@bar.com"}},
&Policy{From: "https://pomerium.io", To: mustParseWeightedURLs(t, "http://localhost")},
true,
},
{
"different from",
&Policy{From: "https://pomerium.io", To: mustParseWeightedURLs(t, "http://localhost")},
&Policy{From: "https://notpomerium.io", To: mustParseWeightedURLs(t, "http://localhost")},
false,
},
{
"different path",
&Policy{From: "https://pomerium.io", To: mustParseWeightedURLs(t, "http://localhost")},
&Policy{From: "https://pomerium.io", To: mustParseWeightedURLs(t, "http://localhost"), Path: "/foo"},
false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
assert.NoError(t, tt.basePolicy.Validate())
assert.NoError(t, tt.comparePolicy.Validate())
id1, err := tt.basePolicy.RouteID()
assert.NoError(t, err)
id2, err := tt.comparePolicy.RouteID()
assert.NoError(t, err)
assert.Equal(t, tt.wantSame, id1 == id2)
})
}
}
func TestPolicy_Checksum(t *testing.T) {
t.Parallel()
p := &Policy{From: "https://pomerium.io", To: mustParseWeightedURLs(t, "http://localhost"), AllowedUsers: []string{"foo@bar.com"}}
oldChecksum := p.Checksum()
p.AllowedUsers = []string{"foo@pomerium.io"}
newChecksum := p.Checksum()
if newChecksum == oldChecksum {
t.Errorf("Checksum() failed to update old = %d, new = %d", oldChecksum, newChecksum)
}
if newChecksum == 0 || oldChecksum == 0 {
t.Error("Checksum() not returning data")
}
if p.Checksum() != newChecksum {
t.Error("Checksum() inconsistent")
}
}
func TestPolicy_FromToPb(t *testing.T) {
t.Parallel()
t.Run("normal", func(t *testing.T) {
p := &Policy{
Name: "ROUTE_NAME",
Description: "DESCRIPTION",
LogoURL: "LOGO_URL",
From: "https://pomerium.io",
To: mustParseWeightedURLs(t, "http://localhost"),
AllowedUsers: []string{"foo@bar.com"},
SubPolicies: []SubPolicy{
{
ID: "sub_policy_id",
Name: "sub_policy",
Rego: []string{"deny = true"},
},
},
EnableGoogleCloudServerlessAuthentication: true,
}
pbPolicy, err := p.ToProto()
require.NoError(t, err)
policyFromPb, err := NewPolicyFromProto(pbPolicy)
assert.NoError(t, err)
assert.Equal(t, p.Name, policyFromPb.Name)
assert.Equal(t, p.Description, policyFromPb.Description)
assert.Equal(t, p.LogoURL, policyFromPb.LogoURL)
assert.Equal(t, p.From, policyFromPb.From)
assert.Equal(t, p.To, policyFromPb.To)
assert.Equal(t, p.AllowedUsers, policyFromPb.AllowedUsers)
})
t.Run("envoy cluster name", func(t *testing.T) {
p := &Policy{
From: "https://pomerium.io",
To: mustParseWeightedURLs(t, "http://localhost"),
AllowedUsers: []string{"foo@bar.com"},
}
pbPolicy, err := p.ToProto()
require.NoError(t, err)
cases := []struct {
pbPolicyName string
pbEnvoyOpts *envoy_config_cluster_v3.Cluster
expectedPolicyName string
}{
{"", nil, ""},
{"pb-name", nil, "pb-name"},
{"", &envoy_config_cluster_v3.Cluster{Name: "pb-envoy-name"}, "pb-envoy-name"},
{"pb-name", &envoy_config_cluster_v3.Cluster{Name: "pb-envoy-name"}, "pb-envoy-name"},
}
for _, tc := range cases {
pbPolicy.Name = tc.pbPolicyName
pbPolicy.EnvoyOpts = tc.pbEnvoyOpts
policyFromPb, err := NewPolicyFromProto(pbPolicy)
assert.NoError(t, err)
assert.Equal(t, tc.expectedPolicyName, policyFromPb.EnvoyOpts.Name)
}
})
t.Run("redirect route", func(t *testing.T) {
p := &Policy{
From: "https://pomerium.io",
Redirect: &PolicyRedirect{
HTTPSRedirect: proto.Bool(true),
},
}
pbPolicy, err := p.ToProto()
require.NoError(t, err)
policyFromProto, err := NewPolicyFromProto(pbPolicy)
assert.NoError(t, err)
assert.Equal(t, p.Redirect.HTTPSRedirect, policyFromProto.Redirect.HTTPSRedirect)
})
}
func TestPolicy_Matches(t *testing.T) {
t.Run("full", func(t *testing.T) {
p := &Policy{
From: "https://www.example.com",
To: mustParseWeightedURLs(t, "https://localhost"),
Regex: `/foo`,
}
assert.NoError(t, p.Validate())
assert.False(t, p.Matches(urlutil.MustParseAndValidateURL(`https://www.example.com/foo/bar`), true),
"regex should only match full string")
})
t.Run("issue2952", func(t *testing.T) {
p := &Policy{
From: "https://www.example.com",
To: mustParseWeightedURLs(t, "https://localhost"),
Regex: `^\/foo\/bar\/[0-9a-f]\/{0,1}$`,
}
assert.NoError(t, p.Validate())
assert.True(t, p.Matches(urlutil.MustParseAndValidateURL(`https://www.example.com/foo/bar/0`), true))
})
t.Run("issue2592-test2", func(t *testing.T) {
p := &Policy{
From: "https://www.example.com",
To: mustParseWeightedURLs(t, "https://localhost"),
Regex: `/admin/.*`,
}
assert.NoError(t, p.Validate())
assert.True(t, p.Matches(urlutil.MustParseAndValidateURL(`https://www.example.com/admin/foo`), true))
assert.True(t, p.Matches(urlutil.MustParseAndValidateURL(`https://www.example.com/admin/bar`), true))
})
t.Run("tcp", func(t *testing.T) {
p := &Policy{
From: "tcp+https://proxy.example.com/tcp.example.com:6379",
To: mustParseWeightedURLs(t, "tcp://localhost:6379"),
}
assert.NoError(t, p.Validate())
assert.True(t, p.Matches(urlutil.MustParseAndValidateURL(`https://tcp.example.com:6379`), true))
})
}
func TestPolicy_SortOrder(t *testing.T) {
ptr := func(i int64) *int64 {
return &i
}
testCases := []struct {
name string
policies []Policy
wantIDs []string
}{
{
name: "regexPriorityOrder DESC NULLS LAST",
policies: []Policy{
{From: "a", Path: "/a", RegexPriorityOrder: nil, ID: "3"},
{From: "a", Path: "/a", RegexPriorityOrder: ptr(2), ID: "2"},
{From: "a", Path: "/a", RegexPriorityOrder: ptr(1), ID: "1"},
},
wantIDs: []string{"2", "1", "3"},
},
{
name: "from ASC",
policies: []Policy{
{From: "", Path: "", RegexPriorityOrder: nil, ID: "B"},
{From: "", Path: "", RegexPriorityOrder: ptr(0), ID: "C"},
{From: "source", Path: "/a", RegexPriorityOrder: ptr(1), ID: "A"},
},
wantIDs: []string{"C", "B", "A"},
},
{
name: "id ASC",
policies: []Policy{
{From: "source", Path: "/a", RegexPriorityOrder: ptr(1), Regex: "regex", Prefix: "prefix", ID: "2"},
{From: "source", Path: "/a", RegexPriorityOrder: ptr(1), Regex: "regex", Prefix: "prefix", ID: "1"},
},
wantIDs: []string{"1", "2"},
},
{
name: "path DESC",
policies: []Policy{
{From: "source", Path: "/b", RegexPriorityOrder: ptr(1), ID: "3"},
{From: "source", Path: "/a", RegexPriorityOrder: nil, ID: "2"},
{From: "source", Path: "/a", RegexPriorityOrder: ptr(2), ID: "1"},
},
wantIDs: []string{"3", "1", "2"},
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
SortPolicies(tt.policies)
gotIDs := make([]string, 0, len(tt.policies))
for _, entity := range tt.policies {
gotIDs = append(gotIDs, entity.ID)
}
assert.Equal(t, tt.wantIDs, gotIDs)
})
}
}
func TestPolicy_IsTCP(t *testing.T) {
p1 := Policy{From: "https://example.com"}
assert.False(t, p1.IsTCP())
p2 := Policy{From: "tcp+https://example.com"}
assert.True(t, p2.IsTCP())
}
func TestPolicy_IsTCPUpstream(t *testing.T) {
p1 := Policy{
From: "tcp+https://example.com:1234",
To: mustParseWeightedURLs(t, "https://one.example.com", "https://two.example.com"),
}
assert.False(t, p1.IsTCPUpstream())
p2 := Policy{
From: "tcp+https://example.com:1234",
To: mustParseWeightedURLs(t, "tcp://one.example.com:4000", "tcp://two.example.com:4000"),
}
assert.True(t, p2.IsTCPUpstream())
p3 := Policy{
From: "tcp+https://example.com:1234",
}
assert.False(t, p3.IsTCPUpstream())
}
func mustParseWeightedURLs(t testing.TB, urls ...string) []WeightedURL {
wu, err := ParseWeightedUrls(urls...)
require.NoError(t, err)
return wu
}
func TestRouteID(t *testing.T) {
randomString := func() string {
return strings.TrimSuffix(cryptutil.NewRandomStringN(mathrand.IntN(31)+1), "=")
}
randomBool := func() bool {
return mathrand.N(2) == 0
}
randomURL := func() *url.URL {
u, err := url.Parse(fmt.Sprintf("https://%s.example.com/%s?foo=%s#%s",
randomString(), randomString(), randomString(), randomString()))
require.NoError(t, err)
return u
}
baseFieldMutators := []func(p *Policy){
func(p *Policy) { p.From = randomString() },
func(p *Policy) { p.Prefix = randomString() },
func(p *Policy) { p.Path = randomString() },
func(p *Policy) { p.Regex = randomString() },
}
toMutators := func(p *Policy) {
p.To = make(WeightedURLs, mathrand.N(9)+1)
for i := 0; i < len(p.To); i++ {
p.To[i] = WeightedURL{URL: *randomURL(), LbWeight: mathrand.Uint32()}
}
}
redirectMutators := []func(p *PolicyRedirect){
func(p *PolicyRedirect) { p.HTTPSRedirect = randomPtr(10, randomBool()) },
func(p *PolicyRedirect) { p.SchemeRedirect = randomPtr(10, randomString()) },
func(p *PolicyRedirect) { p.HostRedirect = randomPtr(10, randomString()) },
func(p *PolicyRedirect) { p.PortRedirect = randomPtr(10, mathrand.Uint32()) },
func(p *PolicyRedirect) { p.PathRedirect = randomPtr(10, randomString()) },
func(p *PolicyRedirect) { p.PrefixRewrite = randomPtr(10, randomString()) },
func(p *PolicyRedirect) { p.ResponseCode = randomPtr(10, mathrand.Int32()) },
func(p *PolicyRedirect) { p.StripQuery = randomPtr(10, randomBool()) },
}
responseMutators := []func(p *DirectResponse){
func(p *DirectResponse) { p.Status = mathrand.Int() },
func(p *DirectResponse) { p.Body = randomString() },
}
t.Run("random policies", func(t *testing.T) {
hashes := make(map[uint64]struct{}, 10000)
for i := 0; i < 10000; i++ {
p := Policy{}
for _, m := range baseFieldMutators {
m(&p)
}
switch mathrand.IntN(3) {
case 0:
toMutators(&p)
case 1:
p.Redirect = &PolicyRedirect{}
for _, m := range redirectMutators {
m(p.Redirect)
}
case 2:
p.Response = &DirectResponse{}
for _, m := range responseMutators {
m(p.Response)
}
}
routeID, err := p.RouteID()
require.NoError(t, err)
hashes[routeID] = struct{}{} // odds of a collision should be pretty low here
// check that computing the route id again results in the same value
routeID2, err := p.RouteID()
require.NoError(t, err)
assert.Equal(t, routeID, routeID2)
}
assert.Len(t, hashes, 10000)
})
t.Run("incremental policy", func(t *testing.T) {
hashes := make(map[uint64]Policy, 5000)
p := Policy{}
checkAdd := func(p *Policy) {
routeID, err := p.RouteID()
require.NoError(t, err)
if existing, ok := hashes[routeID]; ok {
require.Equal(t, existing, *p)
} else {
hashes[routeID] = *p
}
// check that computing the route id again results in the same value
routeID2, err := p.RouteID()
require.NoError(t, err)
assert.Equal(t, routeID, routeID2)
}
// to
toMutators(&p)
checkAdd(&p)
// set base fields
for _, m := range baseFieldMutators {
m(&p)
checkAdd(&p)
}
// redirect
p.To = nil
p.Redirect = &PolicyRedirect{}
for range 1000 {
for _, m := range redirectMutators {
m(p.Redirect)
checkAdd(&p)
}
}
// update base fields
for _, m := range baseFieldMutators {
m(&p)
checkAdd(&p)
}
// direct response
p.Redirect = nil
p.Response = &DirectResponse{}
for range 1000 {
for _, m := range responseMutators {
m(p.Response)
checkAdd(&p)
}
}
// update base fields
for _, m := range baseFieldMutators {
m(&p)
checkAdd(&p)
}
// sanity check
assert.Greater(t, len(hashes), 2000)
})
t.Run("field separation", func(t *testing.T) {
cases := []struct {
a, b *Policy
}{
{
&Policy{From: "foo", Prefix: "bar"},
&Policy{From: "f", Prefix: "oobar"},
},
{
&Policy{From: "foo", Prefix: "bar"},
&Policy{From: "foobar", Prefix: ""},
},
{
&Policy{From: "foobar", Prefix: ""},
&Policy{From: "", Prefix: "foobar"},
},
{
&Policy{From: "foo", Prefix: "", Path: "bar"},
&Policy{From: "foo", Prefix: "bar", Path: ""},
},
{
&Policy{From: "", Prefix: "foo", Path: "bar"},
&Policy{From: "foo", Prefix: "bar", Path: ""},
},
{
&Policy{From: "", Prefix: "foo", Path: "bar"},
&Policy{From: "foo", Prefix: "", Path: "bar"},
},
}
for _, c := range cases {
c.a.To = mustParseWeightedURLs(t, "https://foo")
c.b.To = mustParseWeightedURLs(t, "https://foo")
}
for _, c := range cases {
a, err := c.a.RouteID()
require.NoError(t, err)
b, err := c.b.RouteID()
require.NoError(t, err)
assert.NotEqual(t, a, b)
}
})
}
func randomPtr[T any](nilChance int, t T) *T {
if mathrand.N(nilChance) == 0 {
return nil
}
return &t
}