authorize: omit client cert rule when not needed (#4386)

Currently we always add an invalid_client_certificate deny rule to all
PPL policies. Instead, let's add this rule only when a client CA is
configured. This way, if a user is not using client certificates at all,
they won't see any reason strings related to client certificates in the
authorize logs.

Change the "valid-client-certificate-or-none-required" reason string to
just "valid-client-certificate" accordingly.

Pass the main Evaluator config to NewPolicyEvaluator so that we can
determine whether there is a client CA configured or not. Extract the
existing default deny rule to a separate method. Add unit tests
exercising the new behavior.
This commit is contained in:
Kenneth Jenkins 2023-07-24 15:27:57 -07:00 committed by GitHub
parent 219296a875
commit 4698e4661a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 166 additions and 103 deletions

View file

@ -119,6 +119,8 @@ func New(ctx context.Context, store *store.Store, options ...Option) (*Evaluator
return nil, err return nil, err
} }
e.clientCA = cfg.clientCA
e.policyEvaluators = make(map[uint64]*PolicyEvaluator) e.policyEvaluators = make(map[uint64]*PolicyEvaluator)
for i := range cfg.policies { for i := range cfg.policies {
configPolicy := cfg.policies[i] configPolicy := cfg.policies[i]
@ -126,15 +128,14 @@ func New(ctx context.Context, store *store.Store, options ...Option) (*Evaluator
if err != nil { if err != nil {
return nil, fmt.Errorf("authorize: error computing policy route id: %w", err) return nil, fmt.Errorf("authorize: error computing policy route id: %w", err)
} }
policyEvaluator, err := NewPolicyEvaluator(ctx, store, &configPolicy) clientCA, _ := e.getClientCA(&configPolicy)
policyEvaluator, err := NewPolicyEvaluator(ctx, store, &configPolicy, clientCA)
if err != nil { if err != nil {
return nil, err return nil, err
} }
e.policyEvaluators[id] = policyEvaluator e.policyEvaluators[id] = policyEvaluator
} }
e.clientCA = cfg.clientCA
return e, nil return e, nil
} }

View file

@ -2,6 +2,7 @@ package evaluator
import ( import (
"context" "context"
"encoding/base64"
"net/http" "net/http"
"net/url" "net/url"
"testing" "testing"
@ -109,10 +110,14 @@ func TestEvaluator(t *testing.T) {
}, },
}, },
}, },
{
To: config.WeightedURLs{{URL: *mustParseURL("https://to11.example.com")}},
AllowedUsers: []string{"a@example.com"},
TLSDownstreamClientCA: base64.StdEncoding.EncodeToString([]byte(testCA)),
},
} }
options := []Option{ options := []Option{
WithAuthenticateURL("https://authn.example.com"), WithAuthenticateURL("https://authn.example.com"),
WithClientCA([]byte(testCA)),
WithPolicies(policies), WithPolicies(policies),
} }
@ -122,7 +127,10 @@ func TestEvaluator(t *testing.T) {
Leaf: testValidCert, Leaf: testValidCert,
} }
t.Run("client certificate", func(t *testing.T) { t.Run("client certificate (default CA)", func(t *testing.T) {
// Clone the existing options and add a default client CA.
options := append([]Option(nil), options...)
options = append(options, WithClientCA([]byte(testCA)))
t.Run("invalid", func(t *testing.T) { t.Run("invalid", func(t *testing.T) {
res, err := eval(t, options, nil, &Request{ res, err := eval(t, options, nil, &Request{
Policy: &policies[0], Policy: &policies[0],
@ -141,6 +149,25 @@ func TestEvaluator(t *testing.T) {
assert.False(t, res.Deny.Value) assert.False(t, res.Deny.Value)
}) })
}) })
t.Run("client certificate (per-policy CA)", func(t *testing.T) {
t.Run("invalid", func(t *testing.T) {
res, err := eval(t, options, nil, &Request{
Policy: &policies[10],
})
require.NoError(t, err)
assert.Equal(t, NewRuleResult(true, criteria.ReasonInvalidClientCertificate), res.Deny)
})
t.Run("valid", func(t *testing.T) {
res, err := eval(t, options, nil, &Request{
Policy: &policies[10],
HTTP: RequestHTTP{
ClientCertificate: validCertInfo,
},
})
require.NoError(t, err)
assert.False(t, res.Deny.Value)
})
})
t.Run("identity_headers", func(t *testing.T) { t.Run("identity_headers", func(t *testing.T) {
t.Run("kubernetes", func(t *testing.T) { t.Run("kubernetes", func(t *testing.T) {
res, err := eval(t, options, []proto.Message{ res, err := eval(t, options, []proto.Message{
@ -158,9 +185,8 @@ func TestEvaluator(t *testing.T) {
ID: "session1", ID: "session1",
}, },
HTTP: RequestHTTP{ HTTP: RequestHTTP{
Method: http.MethodGet, Method: http.MethodGet,
URL: "https://from.example.com", URL: "https://from.example.com",
ClientCertificate: validCertInfo,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
@ -183,9 +209,8 @@ func TestEvaluator(t *testing.T) {
ID: "session1", ID: "session1",
}, },
HTTP: RequestHTTP{ HTTP: RequestHTTP{
Method: http.MethodGet, Method: http.MethodGet,
URL: "https://from.example.com", URL: "https://from.example.com",
ClientCertificate: validCertInfo,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
@ -210,9 +235,8 @@ func TestEvaluator(t *testing.T) {
ID: "session1", ID: "session1",
}, },
HTTP: RequestHTTP{ HTTP: RequestHTTP{
Method: http.MethodGet, Method: http.MethodGet,
URL: "https://from.example.com", URL: "https://from.example.com",
ClientCertificate: validCertInfo,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
@ -234,9 +258,8 @@ func TestEvaluator(t *testing.T) {
ID: "session1", ID: "session1",
}, },
HTTP: RequestHTTP{ HTTP: RequestHTTP{
Method: http.MethodGet, Method: http.MethodGet,
URL: "https://from.example.com", URL: "https://from.example.com",
ClientCertificate: validCertInfo,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
@ -258,9 +281,8 @@ func TestEvaluator(t *testing.T) {
ID: "session1", ID: "session1",
}, },
HTTP: RequestHTTP{ HTTP: RequestHTTP{
Method: http.MethodGet, Method: http.MethodGet,
URL: "https://from.example.com", URL: "https://from.example.com",
ClientCertificate: validCertInfo,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
@ -289,9 +311,8 @@ func TestEvaluator(t *testing.T) {
ID: "session2", ID: "session2",
}, },
HTTP: RequestHTTP{ HTTP: RequestHTTP{
Method: http.MethodGet, Method: http.MethodGet,
URL: "https://from.example.com", URL: "https://from.example.com",
ClientCertificate: validCertInfo,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
@ -314,9 +335,8 @@ func TestEvaluator(t *testing.T) {
ID: "session1", ID: "session1",
}, },
HTTP: RequestHTTP{ HTTP: RequestHTTP{
Method: http.MethodGet, Method: http.MethodGet,
URL: "https://from.example.com", URL: "https://from.example.com",
ClientCertificate: validCertInfo,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
@ -338,9 +358,8 @@ func TestEvaluator(t *testing.T) {
ID: "session1", ID: "session1",
}, },
HTTP: RequestHTTP{ HTTP: RequestHTTP{
Method: http.MethodGet, Method: http.MethodGet,
URL: "https://from.example.com", URL: "https://from.example.com",
ClientCertificate: validCertInfo,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
@ -367,9 +386,8 @@ func TestEvaluator(t *testing.T) {
ID: "session1", ID: "session1",
}, },
HTTP: RequestHTTP{ HTTP: RequestHTTP{
Method: http.MethodGet, Method: http.MethodGet,
URL: "https://from.example.com", URL: "https://from.example.com",
ClientCertificate: validCertInfo,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
@ -390,9 +408,8 @@ func TestEvaluator(t *testing.T) {
ID: "session1", ID: "session1",
}, },
HTTP: RequestHTTP{ HTTP: RequestHTTP{
Method: http.MethodGet, Method: http.MethodGet,
URL: "https://from.example.com", URL: "https://from.example.com",
ClientCertificate: validCertInfo,
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
@ -427,10 +444,9 @@ func TestEvaluator(t *testing.T) {
ID: "session1", ID: "session1",
}, },
HTTP: RequestHTTP{ HTTP: RequestHTTP{
Method: http.MethodGet, Method: http.MethodGet,
URL: "https://from.example.com", URL: "https://from.example.com",
ClientCertificate: validCertInfo, Headers: tc.src,
Headers: tc.src,
}, },
}) })
if assert.NoError(t, err) { if assert.NoError(t, err) {
@ -445,7 +461,7 @@ func TestEvaluator(t *testing.T) {
http.MethodGet, http.MethodGet,
*mustParseURL("https://from.example.com/"), *mustParseURL("https://from.example.com/"),
nil, nil,
validCertInfo, ClientCertificateInfo{},
"", "",
), ),
}) })
@ -459,7 +475,7 @@ func TestEvaluator(t *testing.T) {
"POST", "POST",
*mustParseURL("https://from.example.com/test"), *mustParseURL("https://from.example.com/test"),
nil, nil,
validCertInfo, ClientCertificateInfo{},
"", "",
), ),
}) })

View file

@ -107,11 +107,16 @@ type PolicyEvaluator struct {
} }
// NewPolicyEvaluator creates a new PolicyEvaluator. // NewPolicyEvaluator creates a new PolicyEvaluator.
func NewPolicyEvaluator(ctx context.Context, store *store.Store, configPolicy *config.Policy) (*PolicyEvaluator, error) { func NewPolicyEvaluator(
ctx context.Context, store *store.Store, configPolicy *config.Policy, clientCA string,
) (*PolicyEvaluator, error) {
e := new(PolicyEvaluator) e := new(PolicyEvaluator)
// generate the base rego script for the policy // generate the base rego script for the policy
ppl := configPolicy.ToPPL() ppl := configPolicy.ToPPL()
if clientCA != "" {
ppl.AddDefaultClientCertificateRule()
}
base, err := policy.GenerateRegoFromPolicy(ppl) base, err := policy.GenerateRegoFromPolicy(ppl)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -32,13 +32,15 @@ func TestPolicyEvaluator(t *testing.T) {
privateJWK, err := cryptutil.PrivateJWKFromBytes(encodedSigningKey) privateJWK, err := cryptutil.PrivateJWKFromBytes(encodedSigningKey)
require.NoError(t, err) require.NoError(t, err)
var clientCA string
eval := func(t *testing.T, policy *config.Policy, data []proto.Message, input *PolicyRequest) (*PolicyResponse, error) { eval := func(t *testing.T, policy *config.Policy, data []proto.Message, input *PolicyRequest) (*PolicyResponse, error) {
ctx := context.Background() ctx := context.Background()
ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...)) ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...))
store := store.New() store := store.New()
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY")) store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY"))
store.UpdateSigningKey(privateJWK) store.UpdateSigningKey(privateJWK)
e, err := NewPolicyEvaluator(ctx, store, policy) e, err := NewPolicyEvaluator(ctx, store, policy, clientCA)
require.NoError(t, err) require.NoError(t, err)
return e.Evaluate(ctx, input) return e.Evaluate(ctx, input)
} }
@ -66,6 +68,40 @@ func TestPolicyEvaluator(t *testing.T) {
} }
t.Run("allowed", func(t *testing.T) { t.Run("allowed", func(t *testing.T) {
output, err := eval(t,
p1,
[]proto.Message{s1, u1, s2, u2},
&PolicyRequest{
HTTP: RequestHTTP{Method: http.MethodGet, URL: "https://from.example.com/path"},
Session: RequestSession{ID: "s1"},
})
require.NoError(t, err)
assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(true, criteria.ReasonEmailOK),
Deny: NewRuleResult(false),
Traces: []contextutil.PolicyEvaluationTrace{{Allow: true}},
}, output)
})
t.Run("forbidden", func(t *testing.T) {
output, err := eval(t,
p1,
[]proto.Message{s1, u1, s2, u2},
&PolicyRequest{
HTTP: RequestHTTP{Method: http.MethodGet, URL: "https://from.example.com/path"},
Session: RequestSession{ID: "s2"},
})
require.NoError(t, err)
assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(false, criteria.ReasonEmailUnauthorized, criteria.ReasonUserUnauthorized),
Deny: NewRuleResult(false),
Traces: []contextutil.PolicyEvaluationTrace{{}},
}, output)
})
// Enable client certificate validation.
clientCA = "---FAKE CA CERTIFICATE---"
t.Run("allowed with cert", func(t *testing.T) {
output, err := eval(t, output, err := eval(t,
p1, p1,
[]proto.Message{s1, u1, s2, u2}, []proto.Message{s1, u1, s2, u2},
@ -78,7 +114,7 @@ func TestPolicyEvaluator(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, &PolicyResponse{ assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(true, criteria.ReasonEmailOK), Allow: NewRuleResult(true, criteria.ReasonEmailOK),
Deny: NewRuleResult(false, criteria.ReasonValidClientCertificateOrNoneRequired), Deny: NewRuleResult(false, criteria.ReasonValidClientCertificate),
Traces: []contextutil.PolicyEvaluationTrace{{Allow: true}}, Traces: []contextutil.PolicyEvaluationTrace{{Allow: true}},
}, output) }, output)
}) })
@ -99,7 +135,7 @@ func TestPolicyEvaluator(t *testing.T) {
Traces: []contextutil.PolicyEvaluationTrace{{Allow: true, Deny: true}}, Traces: []contextutil.PolicyEvaluationTrace{{Allow: true, Deny: true}},
}, output) }, output)
}) })
t.Run("forbidden", func(t *testing.T) { t.Run("forbidden with cert", func(t *testing.T) {
output, err := eval(t, output, err := eval(t,
p1, p1,
[]proto.Message{s1, u1, s2, u2}, []proto.Message{s1, u1, s2, u2},
@ -112,10 +148,11 @@ func TestPolicyEvaluator(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, &PolicyResponse{ assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(false, criteria.ReasonEmailUnauthorized, criteria.ReasonUserUnauthorized), Allow: NewRuleResult(false, criteria.ReasonEmailUnauthorized, criteria.ReasonUserUnauthorized),
Deny: NewRuleResult(false, criteria.ReasonValidClientCertificateOrNoneRequired), Deny: NewRuleResult(false, criteria.ReasonValidClientCertificate),
Traces: []contextutil.PolicyEvaluationTrace{{}}, Traces: []contextutil.PolicyEvaluationTrace{{}},
}, output) }, output)
}) })
t.Run("ppl", func(t *testing.T) { t.Run("ppl", func(t *testing.T) {
t.Run("allow", func(t *testing.T) { t.Run("allow", func(t *testing.T) {
rego, err := policy.GenerateRegoFromReader(strings.NewReader(` rego, err := policy.GenerateRegoFromReader(strings.NewReader(`
@ -143,7 +180,7 @@ func TestPolicyEvaluator(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, &PolicyResponse{ assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(true, criteria.ReasonAccept), Allow: NewRuleResult(true, criteria.ReasonAccept),
Deny: NewRuleResult(false, criteria.ReasonValidClientCertificateOrNoneRequired), Deny: NewRuleResult(false, criteria.ReasonValidClientCertificate),
Traces: []contextutil.PolicyEvaluationTrace{{}, {ID: "p1", Allow: true}}, Traces: []contextutil.PolicyEvaluationTrace{{}, {ID: "p1", Allow: true}},
}, output) }, output)
}) })
@ -243,7 +280,7 @@ func TestPolicyEvaluator(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, &PolicyResponse{ assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(true), Allow: NewRuleResult(true),
Deny: NewRuleResult(false, criteria.ReasonValidClientCertificateOrNoneRequired), Deny: NewRuleResult(false, criteria.ReasonValidClientCertificate),
Traces: []contextutil.PolicyEvaluationTrace{{}, {ID: "p1", Allow: true}}, Traces: []contextutil.PolicyEvaluationTrace{{}, {ID: "p1", Allow: true}},
}, output) }, output)
}) })
@ -266,7 +303,7 @@ func TestPolicyEvaluator(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, &PolicyResponse{ assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(true, criteria.ReasonEmailOK), Allow: NewRuleResult(true, criteria.ReasonEmailOK),
Deny: NewRuleResult(false, criteria.ReasonValidClientCertificateOrNoneRequired), Deny: NewRuleResult(false, criteria.ReasonValidClientCertificate),
Traces: []contextutil.PolicyEvaluationTrace{{Allow: true}}, Traces: []contextutil.PolicyEvaluationTrace{{Allow: true}},
}, output) }, output)
}) })
@ -290,7 +327,7 @@ func TestPolicyEvaluator(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, &PolicyResponse{ assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(false, criteria.ReasonUserUnauthenticated), Allow: NewRuleResult(false, criteria.ReasonUserUnauthenticated),
Deny: NewRuleResult(false, criteria.ReasonValidClientCertificateOrNoneRequired), Deny: NewRuleResult(false, criteria.ReasonValidClientCertificate),
Traces: []contextutil.PolicyEvaluationTrace{{Allow: false}}, Traces: []contextutil.PolicyEvaluationTrace{{Allow: false}},
}, output) }, output)
}) })

View file

@ -79,13 +79,6 @@ func (p *Policy) ToPPL() *parser.Policy {
} }
ppl.Rules = append(ppl.Rules, allowRule) ppl.Rules = append(ppl.Rules, allowRule)
denyRule := parser.Rule{Action: parser.ActionDeny}
denyRule.Or = append(denyRule.Or,
parser.Criterion{
Name: "invalid_client_certificate",
})
ppl.Rules = append(ppl.Rules, denyRule)
// append embedded PPL policy rules // append embedded PPL policy rules
if p.Policy != nil && p.Policy.Policy != nil { if p.Policy != nil && p.Policy.Policy != nil {
ppl.Rules = append(ppl.Rules, p.Policy.Policy.Rules...) ppl.Rules = append(ppl.Rules, p.Policy.Policy.Rules...)

View file

@ -392,25 +392,6 @@ allow = v {
v := merge_with_or(normalized) v := merge_with_or(normalized)
} }
invalid_client_certificate_0 = [true, {"invalid-client-certificate"}] {
is_boolean(input.is_valid_client_certificate)
not input.is_valid_client_certificate
}
else = [false, {"valid-client-certificate-or-none-required"}]
or_2 = v {
results := [invalid_client_certificate_0]
normalized := [normalize_criterion_result(x) | x := results[i]]
v := merge_with_or(normalized)
}
deny = v {
results := [or_2]
normalized := [normalize_criterion_result(x) | x := results[i]]
v := merge_with_or(normalized)
}
invert_criterion_result(in) = out { invert_criterion_result(in) = out {
in[0] in[0]
out = array.concat([false], array.slice(in, 1, count(in))) out = array.concat([false], array.slice(in, 1, count(in)))

View file

@ -26,12 +26,13 @@ func (invalidClientCertificateCriterion) Name() string {
func (c invalidClientCertificateCriterion) GenerateRule(_ string, _ parser.Value) (*ast.Rule, []*ast.Rule, error) { func (c invalidClientCertificateCriterion) GenerateRule(_ string, _ parser.Value) (*ast.Rule, []*ast.Rule, error) {
rule := NewCriterionRule(c.g, c.Name(), rule := NewCriterionRule(c.g, c.Name(),
ReasonInvalidClientCertificate, ReasonValidClientCertificateOrNoneRequired, ReasonInvalidClientCertificate, ReasonValidClientCertificate,
invalidClientCertificateBody) invalidClientCertificateBody)
return rule, nil, nil return rule, nil, nil
} }
// InvalidClientCertificate returns a Criterion which returns true if the client certificate is valid. // InvalidClientCertificate returns a Criterion which returns true if the
// client certificate is invalid.
func InvalidClientCertificate(generator *Generator) Criterion { func InvalidClientCertificate(generator *Generator) Criterion {
return invalidClientCertificateCriterion{g: generator} return invalidClientCertificateCriterion{g: generator}
} }

View file

@ -7,31 +7,31 @@ type Reason string
// Well-known reasons. // Well-known reasons.
const ( const (
ReasonAccept = "accept" ReasonAccept = "accept"
ReasonClaimOK = "claim-ok" ReasonClaimOK = "claim-ok"
ReasonClaimUnauthorized = "claim-unauthorized" ReasonClaimUnauthorized = "claim-unauthorized"
ReasonCORSRequest = "cors-request" ReasonCORSRequest = "cors-request"
ReasonDeviceOK = "device-ok" ReasonDeviceOK = "device-ok"
ReasonDeviceUnauthenticated = "device-unauthenticated" ReasonDeviceUnauthenticated = "device-unauthenticated"
ReasonDeviceUnauthorized = "device-unauthorized" ReasonDeviceUnauthorized = "device-unauthorized"
ReasonDomainOK = "domain-ok" ReasonDomainOK = "domain-ok"
ReasonDomainUnauthorized = "domain-unauthorized" ReasonDomainUnauthorized = "domain-unauthorized"
ReasonEmailOK = "email-ok" ReasonEmailOK = "email-ok"
ReasonEmailUnauthorized = "email-unauthorized" ReasonEmailUnauthorized = "email-unauthorized"
ReasonHTTPMethodOK = "http-method-ok" ReasonHTTPMethodOK = "http-method-ok"
ReasonHTTPMethodUnauthorized = "http-method-unauthorized" ReasonHTTPMethodUnauthorized = "http-method-unauthorized"
ReasonHTTPPathOK = "http-path-ok" ReasonHTTPPathOK = "http-path-ok"
ReasonHTTPPathUnauthorized = "http-path-unauthorized" ReasonHTTPPathUnauthorized = "http-path-unauthorized"
ReasonInvalidClientCertificate = "invalid-client-certificate" ReasonInvalidClientCertificate = "invalid-client-certificate"
ReasonNonCORSRequest = "non-cors-request" ReasonNonCORSRequest = "non-cors-request"
ReasonNonPomeriumRoute = "non-pomerium-route" ReasonNonPomeriumRoute = "non-pomerium-route"
ReasonPomeriumRoute = "pomerium-route" ReasonPomeriumRoute = "pomerium-route"
ReasonReject = "reject" ReasonReject = "reject"
ReasonRouteNotFound = "route-not-found" ReasonRouteNotFound = "route-not-found"
ReasonUserOK = "user-ok" ReasonUserOK = "user-ok"
ReasonUserUnauthenticated = "user-unauthenticated" // user needs to log in ReasonUserUnauthenticated = "user-unauthenticated" // user needs to log in
ReasonUserUnauthorized = "user-unauthorized" // user does not have access ReasonUserUnauthorized = "user-unauthorized" // user does not have access
ReasonValidClientCertificateOrNoneRequired = "valid-client-certificate-or-none-required" ReasonValidClientCertificate = "valid-client-certificate"
) )
// Reasons is a collection of reasons. // Reasons is a collection of reasons.

View file

@ -0,0 +1,9 @@
package parser
// AddDefaultClientCertificateRule adds a deny rule to the policy with the
// criterion invalid_client_certificate.
func (p *Policy) AddDefaultClientCertificateRule() {
denyRule := Rule{Action: ActionDeny}
denyRule.Or = append(denyRule.Or, Criterion{Name: "invalid_client_certificate"})
p.Rules = append(p.Rules, denyRule)
}

View file

@ -0,0 +1,20 @@
package parser
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestAddDefaultClientCertificateRule(t *testing.T) {
var p Policy
p.AddDefaultClientCertificateRule()
assert.Equal(t, Policy{
Rules: []Rule{{
Action: ActionDeny,
Or: []Criterion{
{Name: "invalid_client_certificate"},
},
}},
}, p)
}