mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-19 01:28:51 +02:00
authorize: add UserPrincipalName SAN match
Add support for UserPrincipalName SAN matching to the policy evaluator. Add unit tests. Unfortunately, adding a new test cert requires regenerating the existing ones as well, because the CA key isn't saved.
This commit is contained in:
parent
ee365532ac
commit
d495a1531e
4 changed files with 279 additions and 73 deletions
|
@ -3,6 +3,7 @@ package evaluator
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -11,6 +12,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
lru "github.com/hashicorp/golang-lru/v2"
|
lru "github.com/hashicorp/golang-lru/v2"
|
||||||
|
"golang.org/x/crypto/cryptobyte"
|
||||||
|
cb_asn1 "golang.org/x/crypto/cryptobyte/asn1"
|
||||||
|
|
||||||
"github.com/pomerium/pomerium/config"
|
"github.com/pomerium/pomerium/config"
|
||||||
"github.com/pomerium/pomerium/internal/log"
|
"github.com/pomerium/pomerium/internal/log"
|
||||||
|
@ -242,6 +245,17 @@ func validateClientCertificateSANs(chain []*x509.Certificate, matchers SANMatche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if r := matchers[config.SANTypeUserPrincipalName]; r != nil {
|
||||||
|
names, err := getUserPrincipalNamesFromCert(cert)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
if r.MatchString(name) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return errNoSANMatch
|
return errNoSANMatch
|
||||||
}
|
}
|
||||||
|
@ -256,3 +270,58 @@ func parseCertificate(pemStr string) (*x509.Certificate, error) {
|
||||||
}
|
}
|
||||||
return x509.ParseCertificate(block.Bytes)
|
return x509.ParseCertificate(block.Bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
oidSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17}
|
||||||
|
oidUserPrincipalName = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 20, 2, 3}
|
||||||
|
otherNameTag = cb_asn1.Tag(0).Constructed().ContextSpecific()
|
||||||
|
upnValueTag = cb_asn1.Tag(0).Constructed().ContextSpecific()
|
||||||
|
)
|
||||||
|
|
||||||
|
func getUserPrincipalNamesFromSAN(raw []byte) ([]string, error) {
|
||||||
|
san := cryptobyte.String(raw)
|
||||||
|
var generalNames cryptobyte.String
|
||||||
|
if !san.ReadASN1(&generalNames, cb_asn1.SEQUENCE) {
|
||||||
|
return nil, errors.New("error reading GeneralNames sequence")
|
||||||
|
}
|
||||||
|
var upns []string
|
||||||
|
for !generalNames.Empty() {
|
||||||
|
var name cryptobyte.String
|
||||||
|
var tag cb_asn1.Tag
|
||||||
|
if !generalNames.ReadAnyASN1(&name, &tag) {
|
||||||
|
return nil, errors.New("error reading GeneralName")
|
||||||
|
} else if tag != otherNameTag {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var oid asn1.ObjectIdentifier
|
||||||
|
if !name.ReadASN1ObjectIdentifier(&oid) {
|
||||||
|
return nil, errors.New("error reading OtherName type ID")
|
||||||
|
} else if !oid.Equal(oidUserPrincipalName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var value cryptobyte.String
|
||||||
|
if !name.ReadAnyASN1(&value, &tag) {
|
||||||
|
return nil, errors.New("error reading UserPrincipalName value")
|
||||||
|
} else if tag != upnValueTag {
|
||||||
|
return nil, fmt.Errorf("unexpected UserPrincipalName data tag 0x%x", tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
var utf8string cryptobyte.String
|
||||||
|
if !value.ReadASN1(&utf8string, cb_asn1.UTF8String) {
|
||||||
|
return nil, errors.New("error reading UserPrincipalName: expected UTF8String")
|
||||||
|
}
|
||||||
|
upns = append(upns, string(utf8string))
|
||||||
|
}
|
||||||
|
return upns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserPrincipalNamesFromCert(cert *x509.Certificate) ([]string, error) {
|
||||||
|
for _, ext := range cert.Extensions {
|
||||||
|
if ext.Id.Equal(oidSubjectAltName) {
|
||||||
|
return getUserPrincipalNamesFromSAN(ext.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package evaluator
|
package evaluator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -18,134 +19,148 @@ import (
|
||||||
const (
|
const (
|
||||||
testCA = `
|
testCA = `
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIBaDCCAQ6gAwIBAgICEAAwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
MIIBZzCCAQ6gAwIBAgICEAAwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
||||||
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAaMRgw
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIxMjEwOTAxWjAaMRgw
|
||||||
FgYDVQQDEw9UcnVzdGVkIFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
|
FgYDVQQDEw9UcnVzdGVkIFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
|
||||||
AAR2/RkzmSK6paoeTKFx1Bd52ZCg29ulJlMxFdSZT8FlmmaK9mN6KWwO+NHYObiW
|
AATMLdnk1HSlNQCNimWztrBbBU0xghli5LWSk7P1/1CW3JPvujT0xC2tEpooAxWi
|
||||||
y3AQuoSTrZXlrlRW5ANvMI+io0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
|
XECL22GH9E2pH/dQoDj2FP41o0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
|
||||||
BAUwAwEB/zAdBgNVHQ4EFgQUJGFVU2UOvOVgaY9YcCUiunGpiCQwCgYIKoZIzj0E
|
BAUwAwEB/zAdBgNVHQ4EFgQUoLhiwb9DEY80NUm/XfiLszTnetcwCgYIKoZIzj0E
|
||||||
AwIDSAAwRQIhAMU5/NjpitOSbUobtjeOriPH8JRo9qy1iFyeVNAcdVvgAiAewq2A
|
AwIDRwAwRAIgWWAReqEjmvzpUKupOlcmEA4WBoPpsWWTDzFFApWE/+0CIG6FEkwQ
|
||||||
PhgzWTw5F9PJg++9i+xGQTqHs3ZirG27cCjvhQ==
|
rJ4htyRqNMapRJmIHqgVxhyV/Pb7t4tsp6d7
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
testValidCert = `
|
testValidCert = `
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIBYjCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
MIIBYjCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
||||||
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAeMRww
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIxMjEwOTAxWjAeMRww
|
||||||
GgYDVQQDExN0cnVzdGVkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
|
GgYDVQQDExN0cnVzdGVkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
|
||||||
AQcDQgAEcWa1Bz6mpsLnM1VD8gtzELjzjEp9Dopp/xWScFO9qtay5SBOeX+Ftr0O
|
AQcDQgAEUk42ibrv64GOkeoZdw+AUiohrtmg2g89jq60E7+HPBrDHWI+jZ22EtBe
|
||||||
8+/RkoKHzGgZ80gr6xQyUJL3MCwVZKM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw
|
N9pphc6hktrlGM3oHn95NOemJ/mlAaM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw
|
||||||
HwYDVR0jBBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwCgYIKoZIzj0EAwIDSAAw
|
HwYDVR0jBBgwFoAUoLhiwb9DEY80NUm/XfiLszTnetcwCgYIKoZIzj0EAwIDSAAw
|
||||||
RQIgXM1ogmy0vcz4lYzji5X3In1n2GLOFNTgucFPkM0GtqgCIQCsXPs/0OjSFyDR
|
RQIgGVB3Q4gFtZNgzjUUe1TuhJLy4NY1vRMrtVk1ywEC4hoCIQCg7XSIirpS4Mxc
|
||||||
FBqAm1NqDJcxq685fS9t3VfHwapcVA==
|
kB9Ugn4FUX8SDD5+md2IReM27uKBjw==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
testUntrustedCert = `
|
testUntrustedCert = `
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIBZjCCAQygAwIBAgICEAEwCgYIKoZIzj0EAwIwHDEaMBgGA1UEAxMRVW50cnVz
|
MIIBZTCCAQygAwIBAgICEAEwCgYIKoZIzj0EAwIwHDEaMBgGA1UEAxMRVW50cnVz
|
||||||
dGVkIFJvb3QgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDcxODAzMjFaMCAx
|
dGVkIFJvb3QgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zNDA3MjEyMTA5MDFaMCAx
|
||||||
HjAcBgNVBAMTFXVudHJ1c3RlZCBjbGllbnQgY2VydDBZMBMGByqGSM49AgEGCCqG
|
HjAcBgNVBAMTFXVudHJ1c3RlZCBjbGllbnQgY2VydDBZMBMGByqGSM49AgEGCCqG
|
||||||
SM49AwEHA0IABJxEIKqLhhMEm5XZXkT+p+hlC2TFyaW0HIZqoE9navJrAcUB8L2M
|
SM49AwEHA0IABMD1+g0Dx5xcezQWJsWJX9AoNlPLsaqspsVS/rJoMoa01gE3fh8y
|
||||||
mVQ+/wLaCznJHLeSLn46uGH5p1hoGFqOrdajODA2MBMGA1UdJQQMMAoGCCsGAQUF
|
KaVH3N/SQH4E54TUywJA06wSIGN3SsNgpnajODA2MBMGA1UdJQQMMAoGCCsGAQUF
|
||||||
BwMCMB8GA1UdIwQYMBaAFIp2rlIiSnr33ea3cGyLsX4LEYwWMAoGCCqGSM49BAMC
|
BwMCMB8GA1UdIwQYMBaAFHqcIcMoPGoBDLYSIol5EqKgXAA+MAoGCCqGSM49BAMC
|
||||||
A0gAMEUCIDtJIZJDcqIYaDXhZFs0nd0nHER8IGP9n4BBFMWewAb2AiEAlQyavOxw
|
A0cAMEQCIDCSM5mZkwApd+Ne0fYK3o0dP9CKeEd31lzF9PlMAzU2AiBL8EBVN5T6
|
||||||
iTQQxt0rXB4Ox5zWpU9q68+F9BGBkQKTsBs=
|
M74llANbsQV6/9O3WSDYsvxvAGK/aN2pBg==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
testRevokedCert = `
|
testRevokedCert = `
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIBYjCCAQigAwIBAgICEAIwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
MIIBYjCCAQigAwIBAgICEAIwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
||||||
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAeMRww
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIxMjEwOTAxWjAeMRww
|
||||||
GgYDVQQDExNyZXZva2VkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
|
GgYDVQQDExNyZXZva2VkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
|
||||||
AQcDQgAEcnoO4EM72C7xL31RE9e6m9YJYyF6E4JloASECd8mdiXPlMXIjq8MZHB5
|
AQcDQgAEfOs33d2sg3TQg19dh1bA5gv+wUljqfkykl/rj5vh3PwMoNgf38M09Lbs
|
||||||
28mFAVQNE7erAtBftID1SbuY4IpXxqM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw
|
fT6kz7wpKEA0G9/h/f2c4y59/d34ZqM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw
|
||||||
HwYDVR0jBBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwCgYIKoZIzj0EAwIDSAAw
|
HwYDVR0jBBgwFoAUoLhiwb9DEY80NUm/XfiLszTnetcwCgYIKoZIzj0EAwIDSAAw
|
||||||
RQIgUUETSO064YIu+VKnyRb0yBnNTjXLy3TvGuYgZI8VX0YCIQDd0gyNEC5YLvRN
|
RQIhAKNJwVxMvKIjkMfk9JhePG24CGawlIzCMJLRExQnIxzoAiAbMOnxje3tr/Mg
|
||||||
njxfnLoimp+TzTVzvsCokUbNSNRKJA==
|
kl9linML1Km4f5aqOp2+TF9O/EHhKg==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
testCRL = `
|
testCRL = `
|
||||||
-----BEGIN X509 CRL-----
|
-----BEGIN X509 CRL-----
|
||||||
MIHeMIGFAgEBMAoGCCqGSM49BAMCMBoxGDAWBgNVBAMTD1RydXN0ZWQgUm9vdCBD
|
MIHdMIGFAgEBMAoGCCqGSM49BAMCMBoxGDAWBgNVBAMTD1RydXN0ZWQgUm9vdCBD
|
||||||
QRgPMDAwMTAxMDEwMDAwMDBaMBUwEwICEAIXDTIzMDgxMDE4MDMyMVqgMDAuMB8G
|
QRgPMDAwMTAxMDEwMDAwMDBaMBUwEwICEAIXDTI0MDcyMzIxMDkwMVqgMDAuMB8G
|
||||||
A1UdIwQYMBaAFCRhVVNlDrzlYGmPWHAlIrpxqYgkMAsGA1UdFAQEAgIgADAKBggq
|
A1UdIwQYMBaAFKC4YsG/QxGPNDVJv134i7M053rXMAsGA1UdFAQEAgIgADAKBggq
|
||||||
hkjOPQQDAgNIADBFAiEAumtTtjiQt1VsbsEnyr+xbpK0KmzKvkpxIVgE1M9CND0C
|
hkjOPQQDAgNHADBEAiA6zjKyqK0IE8bO/LtnQT2CSRolOcdwHPMjtTkoO7ht8gIg
|
||||||
IA8zx5clcaGIT5xRnBLZW7RwA37IOmB+7zjAuJQpmKKp
|
Qo8S83mFqwG14NiwaudxpYyYfGcCJZ6aaxqLc03ixiA=
|
||||||
-----END X509 CRL-----
|
-----END X509 CRL-----
|
||||||
`
|
`
|
||||||
testIntermediateCA = `
|
testIntermediateCA = `
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIBkTCCATegAwIBAgICEAMwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
MIIBkTCCATegAwIBAgICEAMwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
||||||
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAiMSAw
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIxMjEwOTAxWjAiMSAw
|
||||||
HgYDVQQDExdUcnVzdGVkIEludGVybWVkaWF0ZSBDQTBZMBMGByqGSM49AgEGCCqG
|
HgYDVQQDExdUcnVzdGVkIEludGVybWVkaWF0ZSBDQTBZMBMGByqGSM49AgEGCCqG
|
||||||
SM49AwEHA0IABMY+zxL/2dNORuha3uVVOXZYIkTpa9V8N9UVrM15HOHkrdLlz1qk
|
SM49AwEHA0IABNPOBn1f6EJYlOBs9E4ZllV3rVvzGbh7Z3f3klIfKv0nmYZqTYsE
|
||||||
4wbePkkoGtNRzoayb0iZqeA4YjOxqyPG8emjYzBhMA4GA1UdDwEB/wQEAwIBBjAP
|
06GbMlc+Rdw//wQ40j9+Gnvk/yTVPUydtSSjYzBhMA4GA1UdDwEB/wQEAwIBBjAP
|
||||||
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQpGNmcLLM3vHiOADYGPDQL8AhkyDAf
|
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQtnNz7iWzU1Fp427ksQsEf31WvgTAf
|
||||||
BgNVHSMEGDAWgBQkYVVTZQ685WBpj1hwJSK6camIJDAKBggqhkjOPQQDAgNIADBF
|
BgNVHSMEGDAWgBSguGLBv0MRjzQ1Sb9d+IuzNOd61zAKBggqhkjOPQQDAgNIADBF
|
||||||
AiEAnR6xrk7OCk91ymtzU+duZXDqDq35w0oO+MM8nqpac4YCIED+6c9dJKvRCc/C
|
AiEAzOI48p3sxhFnpp2s0LFxjzQXwOUqsKtlkWh16hEzG4MCIGspIDiBlxE594+M
|
||||||
nP8PMxRaUsbQet1woE7Fckn5tK4N
|
iiciSIzesgppOJb1Hk/JS9r6qYhz
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
testValidIntermediateCert = `
|
testValidIntermediateCert = `
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIBdTCCARqgAwIBAgICEAAwCgYIKoZIzj0EAwIwIjEgMB4GA1UEAxMXVHJ1c3Rl
|
MIIBdTCCARqgAwIBAgICEAAwCgYIKoZIzj0EAwIwIjEgMB4GA1UEAxMXVHJ1c3Rl
|
||||||
ZCBJbnRlcm1lZGlhdGUgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDcxODAz
|
ZCBJbnRlcm1lZGlhdGUgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zNDA3MjEyMTA5
|
||||||
MjFaMCgxJjAkBgNVBAMTHWNsaWVudCBjZXJ0IGZyb20gaW50ZXJtZWRpYXRlMFkw
|
MDFaMCgxJjAkBgNVBAMTHWNsaWVudCBjZXJ0IGZyb20gaW50ZXJtZWRpYXRlMFkw
|
||||||
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5SVyYjNRuuFXGjEmCcuVtMq7e2bmndPK
|
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi9mkxtKlhiiCHteqDHZP+YH4awImLtEx
|
||||||
bRJ7lJ5cc0kZSoNJes5wXOtGRFbx3+admRHq+w1XEBXOe+yRUB8kdKM4MDYwEwYD
|
i7s/Nt1AWp3VMQ/cxRsZcmRHceGIpPoinYpmGfVppxy9fddZ4IBjw6M4MDYwEwYD
|
||||||
VR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUKRjZnCyzN7x4jgA2Bjw0C/AI
|
VR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0jBBgwFoAULZzc+4ls1NRaeNu5LELBH99V
|
||||||
ZMgwCgYIKoZIzj0EAwIDSQAwRgIhAMj0O2wDRLoxGIPUDOmUfYxmxglOecQhSkWO
|
r4EwCgYIKoZIzj0EAwIDSQAwRgIhAJGUfGvzsTwMMqLzhSNdmKUWyajbN+wRF2gr
|
||||||
NBtItSxmAiEAy0XCzvpL6XOZU3zxyCjTdJQa2RiC6YnypMaCaETzCaU=
|
3CzI77OZAiEAh40Bew0mQQUs3WWilY0BauxtDtdcjX/IerxKTzEXKbg=
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
testValidCertWithDNSSANs = `
|
testValidCertWithDNSSANs = `
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIBlTCCATugAwIBAgICEAQwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
MIIBlDCCATugAwIBAgICEAQwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
||||||
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIxMjEwOTAxWjAYMRYw
|
||||||
FAYDVQQDEw1jbGllbnQgY2VydCAzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
FAYDVQQDEw1jbGllbnQgY2VydCAzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
||||||
LFai39b7TYauNjg4M58f0qY6jTC7xEOhE84wTTcevZvH/t2Y7U0BBNGkvpb14yxh
|
Dw9uoVvSDyVpqRN79Dtd3pf8pbTTKV8ZFDMa+pft3cWCSbzsxkHjx5yfiYmy94m0
|
||||||
60vrRKZA9t9G6ZvWKcY/BKNxMG8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j
|
IAUEkpsivA7w5zqhqZfONaNxMG8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j
|
||||||
BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwNwYDVR0RBDAwLoIVYS5jbGllbnQz
|
BBgwFoAUoLhiwb9DEY80NUm/XfiLszTnetcwNwYDVR0RBDAwLoIVYS5jbGllbnQz
|
||||||
LmV4YW1wbGUuY29tghViLmNsaWVudDMuZXhhbXBsZS5jb20wCgYIKoZIzj0EAwID
|
LmV4YW1wbGUuY29tghViLmNsaWVudDMuZXhhbXBsZS5jb20wCgYIKoZIzj0EAwID
|
||||||
SAAwRQIgBSw8MsKWPPcpGtuVpNJonTEthIOjIGXswxiYG49y2BECIQC5D1DCX/lY
|
RwAwRAIgTgkgYXb/6sErId2S/1X8TkdQETmMeQ85e0zQLC2WGn0CIAPF5nrlf5EL
|
||||||
KSwF4aapPx4906VujTL+Ehj8L5ImUYcPbA==
|
VLK0wso+qCFmqrPNnv4X8nYOEc3L7kPA
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
testValidCertWithEmailSAN = `
|
testValidCertWithEmailSAN = `
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIBezCCASKgAwIBAgICEAUwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
MIIBfTCCASKgAwIBAgICEAUwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
||||||
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIxMjEwOTAxWjAYMRYw
|
||||||
FAYDVQQDEw1jbGllbnQgY2VydCA0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
FAYDVQQDEw1jbGllbnQgY2VydCA0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
||||||
WvX8BnCrzUSpLrYka8ed+bz6/HoXUvq5nRqysKe0nGYSsXKRjxLdCG8AKsoGIQIv
|
zMPGmaQ/Hhss/Pil1ku5QgFmVkaZdMHllql508HddNQ96t24vddILPS1YZwRv1SQ
|
||||||
KOQScf/4TJUNIUY4XOsFI6NYMFYwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j
|
D47sdwRcrgLcYvXAYhnfxKNYMFYwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j
|
||||||
BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwHgYDVR0RBBcwFYETY2xpZW50NEBl
|
BBgwFoAUoLhiwb9DEY80NUm/XfiLszTnetcwHgYDVR0RBBcwFYETY2xpZW50NEBl
|
||||||
eGFtcGxlLmNvbTAKBggqhkjOPQQDAgNHADBEAiAMYGTjUBqgnai8UL3B/iQkCkMb
|
eGFtcGxlLmNvbTAKBggqhkjOPQQDAgNJADBGAiEAiKp/7Cv8tUJBUr+Z+T6ztghs
|
||||||
xgCC1ZYdZaJ1RBwFfgIgIhjQZ2s6dTaah/LzYJ9ZwMvSA86XQvzTVSuT6s+RJw0=
|
hhSH/LZcLzRHJ8jtACoCIQD78Ut4V6/8eSOS3DHIDwqXwOZ/L06DwugzYP5/jlZA
|
||||||
|
cQ==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
testValidCertWithIPSAN = `
|
testValidCertWithIPSAN = `
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIBbTCCAROgAwIBAgICEAYwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
MIIBbTCCAROgAwIBAgICEAYwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
||||||
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIxMjEwOTAxWjAYMRYw
|
||||||
FAYDVQQDEw1jbGllbnQgY2VydCA1MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
FAYDVQQDEw1jbGllbnQgY2VydCA1MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
||||||
6+9BE+aomrR2Mdnx4iFY63t0hsVD6rYaHBW1b9roFQX6Cor4YeUfkEEF4LrGeAyb
|
cRD582BfNyLzyuvtxR/Z19mwWqRF6Kxx5ZSovtPKIeJ75Okiv1q20TOwGrEYLmHr
|
||||||
wcqb6G1ExgNyjEh10Ai1M6NJMEcwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j
|
YCBrMd3YM56KKcEhSUi/nqNJMEcwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j
|
||||||
BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwDwYDVR0RBAgwBocEwKgKCjAKBggq
|
BBgwFoAUoLhiwb9DEY80NUm/XfiLszTnetcwDwYDVR0RBAgwBocEwKgKCjAKBggq
|
||||||
hkjOPQQDAgNIADBFAiEA/mq32YZZAacOH/P/wjvfD1n74DD/GkhW4kfS72Z0oGQC
|
hkjOPQQDAgNIADBFAiAv+MGYGG9SaQFekl1e7zIG0rxMkbS8UPmkklR/RuXQ+gIh
|
||||||
IAQ+L8E78JOLaPWXiL7WFpVrb0hOHkV2m9Qw4GB41mUN
|
ALvBj9aAXW9wN8nYLH9L2bPbMT//a42VuD6qvi6B0i6P
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
testValidCertWithURISAN = `
|
testValidCertWithURISAN = `
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIBhTCCASugAwIBAgICEAcwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
MIIBhTCCASugAwIBAgICEAcwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
||||||
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIxMjEwOTAxWjAYMRYw
|
||||||
FAYDVQQDEw1jbGllbnQgY2VydCA2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
FAYDVQQDEw1jbGllbnQgY2VydCA2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
||||||
7mHladapSY5PlVYToL1dHr0tJPGe5XVP8DSZJz+WWyS9tWsuEsK6P5yeZrbWASOX
|
B7Znrqifp50mCqJfdoCvEu0UP+W1qUZm1dckfMq02f9kgbn2JGgvb1nGE8gKA0n1
|
||||||
foH7iVIdx3DMyukGsvMX+KNhMF8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j
|
V6/QtuIUBnyosfX3IuF9qqNhMF8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j
|
||||||
BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwJwYDVR0RBCAwHoYcc3BpZmZlOi8v
|
BBgwFoAUoLhiwb9DEY80NUm/XfiLszTnetcwJwYDVR0RBCAwHoYcc3BpZmZlOi8v
|
||||||
ZXhhbXBsZS5jb20vZm9vL2JhcjAKBggqhkjOPQQDAgNIADBFAiAhzElKeGJzp2zP
|
ZXhhbXBsZS5jb20vZm9vL2JhcjAKBggqhkjOPQQDAgNIADBFAiAwp1maaT976oWE
|
||||||
GOTUEy0f6b2tvMYGDLQxCcp4bc4QuQIhAPwX4Y3Cr7uazQlbwL6D51y9NCcDyj3D
|
f8RnxAPGX1Ecc77A6uhOM0ZCVrYDCQIhAJf5MW7BddAtbRYHyUEhwaVFA9Ci56m9
|
||||||
Z18vZNxm9ZR1
|
TEgriIGNirO9
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
`
|
||||||
|
testValidCertWithUPNSAN = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBhDCCASqgAwIBAgICEAcwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
||||||
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIxMjEwOTAxWjAYMRYw
|
||||||
|
FAYDVQQDEw1jbGllbnQgY2VydCA3MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
||||||
|
Pbsk3ZsS7dPwVh+G+hG/nv9PcHfFpOrCiAP0/orxhTRiYD2QbxCB9KBC2YZKrMGF
|
||||||
|
pvhqM/fX6r1hB6o1Gz/J3KNgMF4wEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j
|
||||||
|
BBgwFoAUoLhiwb9DEY80NUm/XfiLszTnetcwJgYDVR0RBB8wHaAbBgorBgEEAYI3
|
||||||
|
FAIDoA0MC3Rlc3RfZGV2aWNlMAoGCCqGSM49BAMCA0gAMEUCIQDG82BiX0pzkvW+
|
||||||
|
UgGq54X7ItNLu2uBFBTgR8NCJpBCMAIgaBzSkAyTSbbLU/gRrhD6HiyGoxDn38xh
|
||||||
|
HcsXWWvNgTQ=
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
@ -366,6 +381,25 @@ func Test_isValidClientCertificate(t *testing.T) {
|
||||||
assert.NoError(t, err, "should not return an error")
|
assert.NoError(t, err, "should not return an error")
|
||||||
assert.False(t, valid, "should return false")
|
assert.False(t, valid, "should return false")
|
||||||
})
|
})
|
||||||
|
t.Run("UserPrincipalName SAN", func(t *testing.T) {
|
||||||
|
valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{
|
||||||
|
Presented: true,
|
||||||
|
Leaf: testValidCertWithUPNSAN,
|
||||||
|
}, ClientCertConstraints{SANMatchers: SANMatchers{
|
||||||
|
config.SANTypeUserPrincipalName: regexp.MustCompile(`^test_device$`),
|
||||||
|
}})
|
||||||
|
assert.NoError(t, err, "should not return an error")
|
||||||
|
assert.True(t, valid, "should return true")
|
||||||
|
|
||||||
|
valid, err = isValidClientCertificate(testCA, "", ClientCertificateInfo{
|
||||||
|
Presented: true,
|
||||||
|
Leaf: testValidCertWithURISAN,
|
||||||
|
}, ClientCertConstraints{SANMatchers: SANMatchers{
|
||||||
|
config.SANTypeDNS: regexp.MustCompile(`^test-device$`), // mismatched type
|
||||||
|
}})
|
||||||
|
assert.NoError(t, err, "should not return an error")
|
||||||
|
assert.False(t, valid, "should return false")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClientCertConstraintsFromConfig(t *testing.T) {
|
func TestClientCertConstraintsFromConfig(t *testing.T) {
|
||||||
|
@ -452,3 +486,64 @@ func TestClientCertConstraintsFromConfig(t *testing.T) {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetUserPrincipalNamesFromSAN(t *testing.T) {
|
||||||
|
b64 := func(raw string) []byte {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(raw)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// One UserPrincipalName.
|
||||||
|
names, err := getUserPrincipalNamesFromSAN(b64(`
|
||||||
|
MBegFQYKKwYBBAGCNxQCA6AHDAVoZWxsbw==`))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []string{"hello"}, names)
|
||||||
|
|
||||||
|
// Multiple UserPrincipalNames.
|
||||||
|
names, err = getUserPrincipalNamesFromSAN(b64(`
|
||||||
|
MD+gEwYKKwYBBAGCNxQCA6AFDANmb2+gEwYKKwYBBAGCNxQCA6AFDANiYXKgEwYKKwYBBAGCNxQCA6AF
|
||||||
|
DANiYXo=`))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []string{"foo", "bar", "baz"}, names)
|
||||||
|
|
||||||
|
// Several OtherNames, only one UserPrincipalName.
|
||||||
|
names, err = getUserPrincipalNamesFromSAN(b64(`
|
||||||
|
MFigFgYDKQEBoA8bDUdlbmVyYWxTdHJpbmegIQYKKwYBBAGCNxQCA6ATDBFVc2VyUHJpbmNpcGFsTmFt
|
||||||
|
ZaAbBgMpAQKgFB4SAEIATQBQAFMAdAByAGkAbgBn`))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []string{"UserPrincipalName"}, names)
|
||||||
|
|
||||||
|
// Two DNS names, no OtherNames.
|
||||||
|
names, err = getUserPrincipalNamesFromSAN(b64(`
|
||||||
|
MC6CFWEuY2xpZW50My5leGFtcGxlLmNvbYIVYi5jbGllbnQzLmV4YW1wbGUuY29t`))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, names)
|
||||||
|
|
||||||
|
// A UserPrincipalName with the wrong data type (GeneralString).
|
||||||
|
names, err = getUserPrincipalNamesFromSAN(b64(`
|
||||||
|
MB+gHQYKKwYBBAGCNxQCA6APGw1HZW5lcmFsU3RyaW5n`))
|
||||||
|
assert.ErrorContains(t, err, "expected UTF8String")
|
||||||
|
assert.Empty(t, names)
|
||||||
|
|
||||||
|
// Other malformed inputs.
|
||||||
|
names, err = getUserPrincipalNamesFromSAN(nil)
|
||||||
|
assert.ErrorContains(t, err, "error reading GeneralNames sequence")
|
||||||
|
assert.Empty(t, names)
|
||||||
|
|
||||||
|
names, err = getUserPrincipalNamesFromSAN(b64("MA+gDwYCRgGgBwwFaGVsbG8="))
|
||||||
|
assert.ErrorContains(t, err, "error reading GeneralName")
|
||||||
|
assert.Empty(t, names)
|
||||||
|
|
||||||
|
names, err = getUserPrincipalNamesFromSAN(b64("MA+gDQICRgGgBwwFaGVsbG8="))
|
||||||
|
assert.ErrorContains(t, err, "error reading OtherName type ID")
|
||||||
|
assert.Empty(t, names)
|
||||||
|
|
||||||
|
names, err = getUserPrincipalNamesFromSAN(b64("MBegFQYKKwYBBAGCNxQCA6AIDAVoZWxsbw=="))
|
||||||
|
assert.ErrorContains(t, err, "error reading UserPrincipalName value")
|
||||||
|
assert.Empty(t, names)
|
||||||
|
|
||||||
|
names, err = getUserPrincipalNamesFromSAN(b64("MBegFQYKKwYBBAGCNxQCA6EHDAVoZWxsbw=="))
|
||||||
|
assert.ErrorContains(t, err, "unexpected UserPrincipalName data tag")
|
||||||
|
assert.Empty(t, names)
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
@ -68,6 +69,33 @@ func newCRL(
|
||||||
return string(pem.EncodeToMemory(&pem.Block{Type: "X509 CRL", Bytes: der}))
|
return string(pem.EncodeToMemory(&pem.Block{Type: "X509 CRL", Bytes: der}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a raw SubjectAltName extension with a single UserPrincipalName.
|
||||||
|
func newSANUserPrincipalName(upnValue string) []byte {
|
||||||
|
type UPN struct {
|
||||||
|
Utf8String string `asn1:"utf8"`
|
||||||
|
}
|
||||||
|
type OtherName struct {
|
||||||
|
OID asn1.ObjectIdentifier
|
||||||
|
Value UPN `asn1:"tag:0"`
|
||||||
|
}
|
||||||
|
type GeneralNames struct {
|
||||||
|
OtherName OtherName `asn1:"tag:0"`
|
||||||
|
}
|
||||||
|
san, err := asn1.Marshal(GeneralNames{
|
||||||
|
OtherName: OtherName{
|
||||||
|
OID: asn1.ObjectIdentifier{
|
||||||
|
1, 3, 6, 1, 4, 1, 311, 20, 2, 3},
|
||||||
|
Value: UPN{
|
||||||
|
Utf8String: upnValue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
return san
|
||||||
|
}
|
||||||
|
|
||||||
// Generates new test certificates and CRLs.
|
// Generates new test certificates and CRLs.
|
||||||
func main() {
|
func main() {
|
||||||
notAfter := time.Now().Add(3650 * 24 * time.Hour)
|
notAfter := time.Now().Add(3650 * 24 * time.Hour)
|
||||||
|
@ -190,6 +218,19 @@ func main() {
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||||
}, rootCA, rootKey)
|
}, rootCA, rootKey)
|
||||||
|
|
||||||
|
trustedClientCert7PEM, _, _ := newCertificate(&x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(0x1007),
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "client cert 7",
|
||||||
|
},
|
||||||
|
NotAfter: notAfter,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||||
|
ExtraExtensions: []pkix.Extension{{
|
||||||
|
Id: asn1.ObjectIdentifier{2, 5, 29, 17},
|
||||||
|
Value: newSANUserPrincipalName("test_device"),
|
||||||
|
}},
|
||||||
|
}, rootCA, rootKey)
|
||||||
|
|
||||||
fmt.Println(`
|
fmt.Println(`
|
||||||
const (
|
const (
|
||||||
testCA = ` + "`\n" + rootPEM + "`" + `
|
testCA = ` + "`\n" + rootPEM + "`" + `
|
||||||
|
@ -203,6 +244,7 @@ const (
|
||||||
testValidCertWithEmailSAN = ` + "`\n" + trustedClientCert4PEM + "`" + `
|
testValidCertWithEmailSAN = ` + "`\n" + trustedClientCert4PEM + "`" + `
|
||||||
testValidCertWithIPSAN = ` + "`\n" + trustedClientCert5PEM + "`" + `
|
testValidCertWithIPSAN = ` + "`\n" + trustedClientCert5PEM + "`" + `
|
||||||
testValidCertWithURISAN = ` + "`\n" + trustedClientCert6PEM + "`" + `
|
testValidCertWithURISAN = ` + "`\n" + trustedClientCert6PEM + "`" + `
|
||||||
|
testValidCertWithUPNSAN = ` + "`\n" + trustedClientCert7PEM + "`" + `
|
||||||
)
|
)
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,7 +171,7 @@ func TestHeadersEvaluator(t *testing.T) {
|
||||||
assert.Equal(t, "CUSTOM_VALUE", output.Headers.Get("X-Custom-Header"))
|
assert.Equal(t, "CUSTOM_VALUE", output.Headers.Get("X-Custom-Header"))
|
||||||
assert.Equal(t, "ID_TOKEN", output.Headers.Get("X-ID-Token"))
|
assert.Equal(t, "ID_TOKEN", output.Headers.Get("X-ID-Token"))
|
||||||
assert.Equal(t, "ACCESS_TOKEN", output.Headers.Get("X-Access-Token"))
|
assert.Equal(t, "ACCESS_TOKEN", output.Headers.Get("X-Access-Token"))
|
||||||
assert.Equal(t, "ebf421e323e31c3900a7985a16e72c59f45f5a2c15283297567e226b3b17d1a1",
|
assert.Equal(t, "d850ddd39279f34f01b13548f364dd529b23531aaffe8f592c03bb79f76bf031",
|
||||||
output.Headers.Get("Client-Cert-Fingerprint"))
|
output.Headers.Get("Client-Cert-Fingerprint"))
|
||||||
assert.Equal(t, "escaped $dollar sign", output.Headers.Get("Foo"))
|
assert.Equal(t, "escaped $dollar sign", output.Headers.Get("Foo"))
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue