pomerium/config/policy_ppl_test.go

623 lines
15 KiB
Go

package config
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/pomerium/pomerium/pkg/policy"
"github.com/pomerium/pomerium/pkg/policy/parser"
)
func TestPolicy_ToPPL(t *testing.T) {
str, err := policy.GenerateRegoFromPolicy((&Policy{
AllowPublicUnauthenticatedAccess: true,
CORSAllowPreflight: true,
AllowAnyAuthenticatedUser: true,
AllowedDomains: []string{"a.example.com", "b.example.com"},
AllowedUsers: []string{"user1", "user2"},
AllowedIDPClaims: map[string][]any{
"family_name": {"Smith", "Jones"},
},
SubPolicies: []SubPolicy{
{
AllowedDomains: []string{"c.example.com", "d.example.com"},
AllowedUsers: []string{"user3", "user4"},
AllowedIDPClaims: map[string][]any{
"given_name": {"John"},
},
},
{
AllowedDomains: []string{"e.example.com"},
AllowedUsers: []string{"user5"},
AllowedIDPClaims: map[string][]any{
"timezone": {"EST"},
},
},
},
Policy: &PPLPolicy{
Policy: &parser.Policy{
Rules: []parser.Rule{{
Action: parser.ActionAllow,
Or: []parser.Criterion{{
Name: "user",
Data: parser.Object{
"is": parser.String("user6"),
},
}},
}},
},
},
}).ToPPL())
require.NoError(t, err)
assert.Equal(t, `package pomerium.policy
import rego.v1
default allow := [false, set()]
default deny := [false, set()]
accept_0 := [true, {"accept"}]
cors_preflight_0 := [true, {"cors-request"}] if {
input.http.method == "OPTIONS"
count(object.get(input.http.headers, "Access-Control-Request-Method", [])) > 0
count(object.get(input.http.headers, "Origin", [])) > 0
}
else := [false, {"non-cors-request"}]
authenticated_user_0 := [true, {"user-ok"}] if {
session := get_session(input.session.id)
session.user_id != null
session.user_id != ""
}
else := [false, {"user-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
domain_0 := [true, {"domain-ok"}] if {
session := get_session(input.session.id)
user := get_user(session)
directory_user := get_directory_user(session)
domain := split(get_user_email(session, user, directory_user), "@")[1]
domain == "a.example.com"
}
else := [false, {"domain-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
domain_1 := [true, {"domain-ok"}] if {
session := get_session(input.session.id)
user := get_user(session)
directory_user := get_directory_user(session)
domain := split(get_user_email(session, user, directory_user), "@")[1]
domain == "b.example.com"
}
else := [false, {"domain-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
domain_2 := [true, {"domain-ok"}] if {
session := get_session(input.session.id)
user := get_user(session)
directory_user := get_directory_user(session)
domain := split(get_user_email(session, user, directory_user), "@")[1]
domain == "c.example.com"
}
else := [false, {"domain-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
domain_3 := [true, {"domain-ok"}] if {
session := get_session(input.session.id)
user := get_user(session)
directory_user := get_directory_user(session)
domain := split(get_user_email(session, user, directory_user), "@")[1]
domain == "d.example.com"
}
else := [false, {"domain-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
domain_4 := [true, {"domain-ok"}] if {
session := get_session(input.session.id)
user := get_user(session)
directory_user := get_directory_user(session)
domain := split(get_user_email(session, user, directory_user), "@")[1]
domain == "e.example.com"
}
else := [false, {"domain-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
claim_0 := [true, {"claim-ok"}] if {
rule_path := "family_name"
session := get_session(input.session.id)
session_claims := object.get(session, "claims", {})
user := get_user(session)
user_claims := object.get(user, "claims", {})
all_claims := object.union(session_claims, user_claims)
values := object_get(all_claims, rule_path, [])
count([true | some v; v = values[_0]; v == "Smith"]) > 0
}
else := [false, {"claim-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
claim_1 := [true, {"claim-ok"}] if {
rule_path := "family_name"
session := get_session(input.session.id)
session_claims := object.get(session, "claims", {})
user := get_user(session)
user_claims := object.get(user, "claims", {})
all_claims := object.union(session_claims, user_claims)
values := object_get(all_claims, rule_path, [])
count([true | some v; v = values[_0]; v == "Jones"]) > 0
}
else := [false, {"claim-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
claim_2 := [true, {"claim-ok"}] if {
rule_path := "given_name"
session := get_session(input.session.id)
session_claims := object.get(session, "claims", {})
user := get_user(session)
user_claims := object.get(user, "claims", {})
all_claims := object.union(session_claims, user_claims)
values := object_get(all_claims, rule_path, [])
count([true | some v; v = values[_0]; v == "John"]) > 0
}
else := [false, {"claim-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
claim_3 := [true, {"claim-ok"}] if {
rule_path := "timezone"
session := get_session(input.session.id)
session_claims := object.get(session, "claims", {})
user := get_user(session)
user_claims := object.get(user, "claims", {})
all_claims := object.union(session_claims, user_claims)
values := object_get(all_claims, rule_path, [])
count([true | some v; v = values[_0]; v == "EST"]) > 0
}
else := [false, {"claim-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
user_0 := [true, {"user-ok"}] if {
session := get_session(input.session.id)
user_id := session.user_id
user_id == "user1"
}
else := [false, {"user-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
email_0 := [true, {"email-ok"}] if {
session := get_session(input.session.id)
user := get_user(session)
directory_user := get_directory_user(session)
email := get_user_email(session, user, directory_user)
email == "user1"
}
else := [false, {"email-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
user_1 := [true, {"user-ok"}] if {
session := get_session(input.session.id)
user_id := session.user_id
user_id == "user2"
}
else := [false, {"user-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
email_1 := [true, {"email-ok"}] if {
session := get_session(input.session.id)
user := get_user(session)
directory_user := get_directory_user(session)
email := get_user_email(session, user, directory_user)
email == "user2"
}
else := [false, {"email-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
user_2 := [true, {"user-ok"}] if {
session := get_session(input.session.id)
user_id := session.user_id
user_id == "user3"
}
else := [false, {"user-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
email_2 := [true, {"email-ok"}] if {
session := get_session(input.session.id)
user := get_user(session)
directory_user := get_directory_user(session)
email := get_user_email(session, user, directory_user)
email == "user3"
}
else := [false, {"email-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
user_3 := [true, {"user-ok"}] if {
session := get_session(input.session.id)
user_id := session.user_id
user_id == "user4"
}
else := [false, {"user-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
email_3 := [true, {"email-ok"}] if {
session := get_session(input.session.id)
user := get_user(session)
directory_user := get_directory_user(session)
email := get_user_email(session, user, directory_user)
email == "user4"
}
else := [false, {"email-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
user_4 := [true, {"user-ok"}] if {
session := get_session(input.session.id)
user_id := session.user_id
user_id == "user5"
}
else := [false, {"user-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
email_4 := [true, {"email-ok"}] if {
session := get_session(input.session.id)
user := get_user(session)
directory_user := get_directory_user(session)
email := get_user_email(session, user, directory_user)
email == "user5"
}
else := [false, {"email-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
or_0 := v if {
results := [accept_0, cors_preflight_0, authenticated_user_0, domain_0, domain_1, domain_2, domain_3, domain_4, claim_0, claim_1, claim_2, claim_3, user_0, email_0, user_1, email_1, user_2, email_2, user_3, email_3, user_4, email_4]
normalized := [normalize_criterion_result(x) | x := results[i]]
v := merge_with_or(normalized)
}
user_5 := [true, {"user-ok"}] if {
session := get_session(input.session.id)
user_id := session.user_id
user_id == "user6"
}
else := [false, {"user-unauthorized"}] if {
session := get_session(input.session.id)
session.id != ""
}
else := [false, {"user-unauthenticated"}]
or_1 := v if {
results := [user_5]
normalized := [normalize_criterion_result(x) | x := results[i]]
v := merge_with_or(normalized)
}
allow := v if {
results := [or_0, or_1]
normalized := [normalize_criterion_result(x) | x := results[i]]
v := merge_with_or(normalized)
}
invert_criterion_result(v) := out if {
v[0]
out = array.concat([false], array.slice(v, 1, count(v)))
}
else := out if {
not v[0]
out = array.concat([true], array.slice(v, 1, count(v)))
}
normalize_criterion_result(result) := v if {
is_boolean(result)
v = [result, set()]
}
else := v if {
is_array(result)
v = result
}
else := v if {
v = [false, set()]
}
object_union(xs) := merged if {
merged = {k: v |
some k
xs[_0][k]
vs := [xv | xv := xs[_][k]]
v := vs[count(vs) - 1]
}
}
merge_with_and(results) := [true, reasons, additional_data] if {
true_results := [x | x := results[i]; x[0]]
count(true_results) == count(results)
reasons := union({x | x := true_results[i][1]})
additional_data := object_union({x | x := true_results[i][2]})
}
else := [false, reasons, additional_data] if {
false_results := [x | x := results[i]; not x[0]]
reasons := union({x | x := false_results[i][1]})
additional_data := object_union({x | x := false_results[i][2]})
}
merge_with_or(results) := [true, reasons, additional_data] if {
true_results := [x | x := results[i]; x[0]]
count(true_results) > 0
reasons := union({x | x := true_results[i][1]})
additional_data := object_union({x | x := true_results[i][2]})
}
else := [false, reasons, additional_data] if {
false_results := [x | x := results[i]; not x[0]]
reasons := union({x | x := false_results[i][1]})
additional_data := object_union({x | x := false_results[i][2]})
}
get_session(id) := v if {
v = get_databroker_record("type.googleapis.com/user.ServiceAccount", id)
v != null
}
else := iv if {
v = get_databroker_record("type.googleapis.com/session.Session", id)
v != null
object.get(v, "impersonate_session_id", "") != ""
iv = get_databroker_record("type.googleapis.com/session.Session", v.impersonate_session_id)
iv != null
}
else := v if {
v = get_databroker_record("type.googleapis.com/session.Session", id)
v != null
object.get(v, "impersonate_session_id", "") == ""
}
else := {}
get_user(session) := v if {
v = get_databroker_record("type.googleapis.com/user.User", session.user_id)
v != null
}
else := {}
get_directory_user(session) := v if {
v = get_databroker_record("pomerium.io/DirectoryUser", session.user_id)
v != null
}
else := {}
get_user_email(session, user, directory_user) := v if {
v = object.get(directory_user, "email", "")
v != ""
}
else := v if {
v = user.email
}
else := ""
object_get(obj, key, def) := value if {
undefined := "10a0fd35-0f1a-4e5b-97ce-631e89e1bafa"
value = object.get(obj, key, undefined)
value != undefined
}
else := value if {
segments := split(replace(key, ".", "/"), "/")
count(segments) == 2
o1 := object.get(obj, segments[0], {})
value = object.get(o1, segments[1], def)
}
else := value if {
segments := split(replace(key, ".", "/"), "/")
count(segments) == 3
o1 := object.get(obj, segments[0], {})
o2 := object.get(o1, segments[1], {})
value = object.get(o2, segments[2], def)
}
else := value if {
segments := split(replace(key, ".", "/"), "/")
count(segments) == 4
o1 := object.get(obj, segments[0], {})
o2 := object.get(o1, segments[1], {})
o3 := object.get(o2, segments[2], {})
value = object.get(o3, segments[3], def)
}
else := value if {
segments := split(replace(key, ".", "/"), "/")
count(segments) == 5
o1 := object.get(obj, segments[0], {})
o2 := object.get(o1, segments[1], {})
o3 := object.get(o2, segments[2], {})
o4 := object.get(o3, segments[3], {})
value = object.get(o4, segments[4], def)
}
else := value if {
value = object.get(obj, key, def)
}
`, str)
}
func TestPolicy_ToPPL_Embedded(t *testing.T) {
policy := Policy{
Policy: &PPLPolicy{
Policy: &parser.Policy{
Rules: []parser.Rule{
{
Action: parser.ActionAllow,
Or: []parser.Criterion{
{
Name: "foo",
Data: parser.Number("5"),
},
},
},
},
},
},
}
assert.Equal(t, policy.Policy.Policy, policy.ToPPL())
policy2 := Policy{
AllowedUsers: []string{"test"},
Policy: &PPLPolicy{
Policy: &parser.Policy{
Rules: []parser.Rule{
{
Action: parser.ActionAllow,
Or: []parser.Criterion{
{
Name: "foo",
Data: parser.Number("5"),
},
},
},
},
},
},
}
assert.Equal(t, &parser.Policy{
Rules: []parser.Rule{
{
Action: parser.ActionAllow,
Or: []parser.Criterion{
{
Name: "user",
Data: parser.Object{
"is": parser.String("test"),
},
},
{
Name: "email",
Data: parser.Object{
"is": parser.String("test"),
},
},
},
},
{
Action: parser.ActionAllow,
Or: []parser.Criterion{
{
Name: "foo",
Data: parser.Number("5"),
},
},
},
},
}, policy2.ToPPL())
}