pomerium/pkg/policy/criteria/client_certificate_test.go
Caleb Doxsey a2fd95aae6
core/ci: update linting (#4844)
* core/ci: update linting

* re-add exportloopref

* re-add gocheckcompilerdirectives

* re-add stylecheck

* re-add usestdlibvars

* upgrade lint

---------

Co-authored-by: Denis Mishin <dmishin@pomerium.com>
2023-12-14 09:07:54 -08:00

220 lines
5.4 KiB
Go

package criteria
import (
"strings"
"testing"
"github.com/open-policy-agent/opa/ast"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/pomerium/pomerium/pkg/policy/parser"
)
const testCert = `
-----BEGIN CERTIFICATE-----
MIIBYTCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwNzMxMTUzMzE5WjAeMRww
GgYDVQQDExN0cnVzdGVkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
AQcDQgAEfAYP3ZwiKJgk9zXpR/CMHYlAxjweJaMJihIS2FTA5gb0xBcTEe5AGpNF
CHWPk4YCB25VeHg9GmY9Q1+qDD1hdqM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw
HwYDVR0jBBgwFoAUXep6D8FTP6+5ZdR/HjP3pYfmxkwwCgYIKoZIzj0EAwIDRwAw
RAIgProROtxpvKS/qjrjonSvacnhdU0JwoXj2DgYvF/qjrUCIAXlHkdEzyXmTLuu
/YxuOibV35vlaIzj21GRj4pYmVR1
-----END CERTIFICATE-----`
func TestClientCertificate(t *testing.T) {
t.Parallel()
cases := []struct {
label string
policy string
cert string
expected A
}{
{
"no certificate",
`allow:
or:
- client_certificate:
fingerprint: 17859273e8a980631d367b2d5a6a6635412b0f22835f69e47b3f65624546a704`,
"",
A{false, A{ReasonClientCertificateUnauthorized}, M{}},
},
{
"no fingerprint match",
`allow:
or:
- client_certificate:
fingerprint: df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a`,
testCert,
A{false, A{ReasonClientCertificateUnauthorized}, M{}},
},
{
"fingerprint match",
`allow:
or:
- client_certificate:
fingerprint: 17859273e8a980631d367b2d5a6a6635412b0f22835f69e47b3f65624546a704`,
testCert,
A{true, A{ReasonClientCertificateOK}, M{}},
},
{
"fingerprint list match",
`allow:
or:
- client_certificate:
fingerprint:
- 17859273e8a980631d367b2d5a6a6635412b0f22835f69e47b3f65624546a704
- df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a`,
testCert,
A{true, A{ReasonClientCertificateOK}, M{}},
},
{
"spki hash match",
`allow:
or:
- client_certificate:
spki_hash: FsDbM0rUYIiL3V339eIKqiz6HPSB+Pz2WeAWhqlqh8U=`,
testCert,
A{true, A{ReasonClientCertificateOK}, M{}},
},
{
"spki hash list match",
`allow:
or:
- client_certificate:
spki_hash:
- FsDbM0rUYIiL3V339eIKqiz6HPSB+Pz2WeAWhqlqh8U=
- NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A=`,
testCert,
A{true, A{ReasonClientCertificateOK}, M{}},
},
}
for i := range cases {
c := cases[i]
t.Run(c.label, func(t *testing.T) {
t.Parallel()
input := Input{
HTTP: InputHTTP{
ClientCertificate: ClientCertificateInfo{
Leaf: c.cert,
},
},
}
res, err := evaluate(t, c.policy, nil, input)
require.NoError(t, err)
assert.Equal(t, c.expected, res["allow"])
})
}
}
func TestCanonicalCertFingerprint(t *testing.T) {
t.Parallel()
cases := []struct {
label string
input string
output string
err string
}{
{
"object",
`{}`, "", "certificate fingerprint must be a string (was {})",
},
{
"empty",
`""`, "", "certificate fingerprint must not be empty",
},
{
"SHA-1 fingerprint",
`"B1:E6:A2:DC:DD:6B:87:A4:9B:C5:7C:3B:7C:7F:1C:74:9A:DB:88:36"`,
"", "unsupported certificate fingerprint format (B1:E6:A2:DC:DD:6B:87:A4:9B:C5:7C:3B:7C:7F:1C:74:9A:DB:88:36)",
},
{
"uppercase short",
`"DF6FF72FE9116521268F6F2DD4966F51DF479883FE7037B39F75916AC3049D1A"`,
"", "unsupported certificate fingerprint format (DF6FF72FE9116521268F6F2DD4966F51DF479883FE7037B39F75916AC3049D1A)",
},
{
"valid short",
`"df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a"`,
"df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a", "",
},
{
"lowercase long",
`"df:6f:f7:2f:e9:11:65:21:26:8f:6f:2d:d4:96:6f:51:df:47:98:83:fe:70:37:b3:9f:75:91:6a:c3:04:9d:1a"`,
"", "unsupported certificate fingerprint format (df:6f:f7:2f:e9:11:65:21:26:8f:6f:2d:d4:96:6f:51:df:47:98:83:fe:70:37:b3:9f:75:91:6a:c3:04:9d:1a)",
},
{
"valid long",
`"DF:6F:F7:2F:E9:11:65:21:26:8F:6F:2D:D4:96:6F:51:DF:47:98:83:FE:70:37:B3:9F:75:91:6A:C3:04:9D:1A"`,
"df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a", "",
},
}
for i := range cases {
c := cases[i]
t.Run(c.label, func(t *testing.T) {
t.Parallel()
value, err := parser.ParseValue(strings.NewReader(c.input))
require.NoError(t, err)
f, err := canonicalCertFingerprint(value)
if c.err == "" {
require.NoError(t, err)
assert.Equal(t, ast.String(c.output), f)
} else {
assert.Equal(t, c.err, err.Error())
}
})
}
}
func TestSPKIHashFormatErrors(t *testing.T) {
t.Parallel()
cases := []struct {
label string
input string
err string
}{
{
"object",
`{}`, "certificate SPKI hash condition expects a string or array of strings",
},
{
"not base64",
`"not%valid%base64%data"`, "certificate SPKI hash must be a base64-encoded SHA-256 hash (was not%valid%base64%data)",
},
{
"SHA-1 hash",
`"VYby3BAoHawLLtsyckwo5Q=="`, "certificate SPKI hash must be a base64-encoded SHA-256 hash (was VYby3BAoHawLLtsyckwo5Q==)",
},
{
"valid",
`"FsDbM0rUYIiL3V339eIKqiz6HPSB+Pz2WeAWhqlqh8U="`, "",
},
}
for i := range cases {
c := cases[i]
t.Run(c.label, func(t *testing.T) {
t.Parallel()
value, err := parser.ParseValue(strings.NewReader(c.input))
require.NoError(t, err)
var body ast.Body
err = addCertSPKIHashCondition(&body, value)
if c.err == "" {
assert.NoError(t, err)
} else {
assert.Equal(t, c.err, err.Error())
}
})
}
}