2018-01-28 14:16:52 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
|
|
|
"testing"
|
2023-07-28 14:57:34 +00:00
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
2018-01-28 14:16:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
aclTestUser = "test"
|
|
|
|
aclTestGroups = []string{"group_a", "group_b"}
|
2023-07-28 14:57:34 +00:00
|
|
|
ptrBoolTrue = func(v bool) *bool { return &v }(true)
|
2018-01-28 14:16:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func aclTestRequest(headers map[string]string) *http.Request {
|
|
|
|
req, _ := http.NewRequest("GET", "http://localhost/auth", nil)
|
|
|
|
for k, v := range headers {
|
|
|
|
req.Header.Set(k, v)
|
|
|
|
}
|
|
|
|
return req
|
|
|
|
}
|
|
|
|
|
|
|
|
func aclTestString(in string) *string { return &in }
|
|
|
|
func aclTestBool(in bool) *bool { return &in }
|
|
|
|
|
|
|
|
func TestEmptyACL(t *testing.T) {
|
|
|
|
a := acl{}
|
|
|
|
|
|
|
|
if a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(map[string]string{})) {
|
|
|
|
t.Fatal("Empty ACL (= default action) was ALLOW instead of DENY")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRuleSetMatcher(t *testing.T) {
|
|
|
|
r := aclRuleSet{
|
|
|
|
Rules: []aclRule{
|
|
|
|
{
|
|
|
|
Field: "field_a",
|
|
|
|
MatchString: aclTestString("expected"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Field: "field_c",
|
|
|
|
MatchString: aclTestString("expected"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Allow: []string{aclTestUser},
|
|
|
|
}
|
|
|
|
fields := map[string]string{
|
|
|
|
"field_a": "expected",
|
|
|
|
"field_b": "unchecked",
|
|
|
|
"field_c": "expected",
|
|
|
|
}
|
|
|
|
|
2023-07-28 14:57:34 +00:00
|
|
|
assert.True(t, r.AppliesToRequest(aclTestRequest(fields)))
|
2018-01-28 14:16:52 +00:00
|
|
|
|
|
|
|
delete(fields, "field_c")
|
2023-07-28 14:57:34 +00:00
|
|
|
assert.False(t, r.AppliesToRequest(aclTestRequest(fields)))
|
2018-01-28 14:16:52 +00:00
|
|
|
}
|
|
|
|
|
2019-04-22 17:54:27 +00:00
|
|
|
func TestGroupAuthenticated(t *testing.T) {
|
2023-07-28 14:57:34 +00:00
|
|
|
a := acl{RuleSets: []aclRuleSet{{
|
2019-04-22 17:54:27 +00:00
|
|
|
Rules: []aclRule{
|
|
|
|
{
|
|
|
|
Field: "field_a",
|
|
|
|
MatchString: aclTestString("expected"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Allow: []string{"@_authenticated"},
|
2023-07-28 14:57:34 +00:00
|
|
|
}}}
|
2019-04-22 17:54:27 +00:00
|
|
|
fields := map[string]string{
|
|
|
|
"field_a": "expected",
|
|
|
|
}
|
|
|
|
|
2023-07-28 14:57:34 +00:00
|
|
|
assert.True(t, a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(fields)))
|
|
|
|
assert.False(t, a.HasAccess("\x00", nil, aclTestRequest(fields)), "access to anon user")
|
|
|
|
assert.False(t, a.HasAccess("", nil, aclTestRequest(fields)), "access to empty user")
|
2019-04-22 17:54:27 +00:00
|
|
|
|
2023-07-28 14:57:34 +00:00
|
|
|
a.RuleSets[0].Allow = []string{"testgroup"}
|
|
|
|
assert.False(t, a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(fields)))
|
2019-04-22 17:54:27 +00:00
|
|
|
}
|
|
|
|
|
2019-12-28 13:12:18 +00:00
|
|
|
func TestAnonymousAccess(t *testing.T) {
|
2023-07-28 14:57:34 +00:00
|
|
|
a := acl{RuleSets: []aclRuleSet{{
|
2019-12-28 13:12:18 +00:00
|
|
|
Rules: []aclRule{
|
|
|
|
{
|
|
|
|
Field: "field_a",
|
|
|
|
MatchString: aclTestString("expected"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Allow: []string{groupAnonymous},
|
2023-07-28 14:57:34 +00:00
|
|
|
}}}
|
2019-12-28 13:12:18 +00:00
|
|
|
fields := map[string]string{
|
|
|
|
"field_a": "expected",
|
|
|
|
}
|
|
|
|
|
2023-07-28 14:57:34 +00:00
|
|
|
assert.True(t, a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(fields)))
|
|
|
|
assert.True(t, a.HasAccess("", nil, aclTestRequest(fields)), "access to empty user")
|
|
|
|
assert.True(t, a.HasAccess("\x00", nil, aclTestRequest(fields)), "access to anon user")
|
|
|
|
}
|
2019-12-28 13:12:18 +00:00
|
|
|
|
2023-07-28 14:57:34 +00:00
|
|
|
func TestAnonymousAccessExplicitDeny(t *testing.T) {
|
|
|
|
a := acl{
|
|
|
|
RuleSets: []aclRuleSet{
|
|
|
|
{
|
|
|
|
Rules: []aclRule{{Field: "field_a", IsPresent: ptrBoolTrue}},
|
|
|
|
Allow: []string{groupAnonymous},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Rules: []aclRule{{Field: "field_b", IsPresent: ptrBoolTrue}},
|
|
|
|
Allow: []string{"somerandomuser"},
|
|
|
|
Deny: []string{groupAnonymous},
|
|
|
|
},
|
|
|
|
},
|
2019-12-28 13:12:18 +00:00
|
|
|
}
|
|
|
|
|
2023-07-28 14:57:34 +00:00
|
|
|
assert.True(t,
|
|
|
|
a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(map[string]string{"field_a": ""})),
|
|
|
|
"anon access with only allowed field should be possible")
|
|
|
|
assert.False(t,
|
|
|
|
a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(map[string]string{"field_b": ""})),
|
|
|
|
"anon access with only denied field should not be possible")
|
|
|
|
assert.False(t,
|
|
|
|
a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(map[string]string{"field_a": "", "field_b": ""})),
|
|
|
|
"anon access with one allowed and one denied field should not be possible")
|
2019-12-28 13:12:18 +00:00
|
|
|
}
|
|
|
|
|
2018-01-28 14:16:52 +00:00
|
|
|
func TestInvertedRegexMatcher(t *testing.T) {
|
|
|
|
fields := map[string]string{
|
|
|
|
"field_a": "expected",
|
|
|
|
"field_b": "unchecked",
|
|
|
|
}
|
|
|
|
|
|
|
|
ar := aclRule{
|
|
|
|
Field: "field_a",
|
|
|
|
Invert: true,
|
|
|
|
MatchRegex: aclTestString("^expected$"),
|
|
|
|
}
|
|
|
|
|
|
|
|
if ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v matches fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
|
|
|
|
fields["field_a"] = "unexpected"
|
|
|
|
|
|
|
|
if !ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v does not match fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRegexMatcher(t *testing.T) {
|
|
|
|
fields := map[string]string{
|
|
|
|
"field_a": "expected",
|
|
|
|
"field_b": "unchecked",
|
|
|
|
}
|
|
|
|
|
|
|
|
ar := aclRule{
|
|
|
|
Field: "field_a",
|
|
|
|
MatchRegex: aclTestString("^expected$"),
|
|
|
|
}
|
|
|
|
|
|
|
|
if !ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v does not match fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
|
|
|
|
fields["field_a"] = "unexpected"
|
|
|
|
|
|
|
|
if ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v matches fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInvertedEqualsMatcher(t *testing.T) {
|
|
|
|
fields := map[string]string{
|
|
|
|
"field_a": "expected",
|
|
|
|
"field_b": "unchecked",
|
|
|
|
}
|
|
|
|
|
|
|
|
ar := aclRule{
|
|
|
|
Field: "field_a",
|
|
|
|
Invert: true,
|
|
|
|
MatchString: aclTestString("expected"),
|
|
|
|
}
|
|
|
|
|
|
|
|
if ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v matches fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
|
|
|
|
fields["field_a"] = "unexpected"
|
|
|
|
|
|
|
|
if !ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v does not match fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestEqualsMatcher(t *testing.T) {
|
|
|
|
fields := map[string]string{
|
|
|
|
"field_a": "expected",
|
|
|
|
"field_b": "unchecked",
|
|
|
|
}
|
|
|
|
|
|
|
|
ar := aclRule{
|
|
|
|
Field: "field_a",
|
|
|
|
MatchString: aclTestString("expected"),
|
|
|
|
}
|
|
|
|
|
|
|
|
if !ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v does not match fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
|
|
|
|
fields["field_a"] = "unexpected"
|
|
|
|
|
|
|
|
if ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v matches fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInvertedIsPresentMatcher(t *testing.T) {
|
|
|
|
fields := map[string]string{
|
|
|
|
"field_a": "expected",
|
|
|
|
"field_b": "unchecked",
|
|
|
|
}
|
|
|
|
|
|
|
|
ar := aclRule{
|
|
|
|
Field: "field_a",
|
|
|
|
Invert: true,
|
|
|
|
IsPresent: aclTestBool(true),
|
|
|
|
}
|
|
|
|
|
|
|
|
if ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v matches fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
|
|
|
|
ar.IsPresent = aclTestBool(false)
|
|
|
|
|
|
|
|
if !ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v does not match fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
|
|
|
|
ar.IsPresent = aclTestBool(true)
|
|
|
|
delete(fields, "field_a")
|
|
|
|
|
|
|
|
if !ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v does not match fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
|
|
|
|
ar.IsPresent = aclTestBool(false)
|
|
|
|
if ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v matches fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestIsPresentMatcher(t *testing.T) {
|
|
|
|
fields := map[string]string{
|
|
|
|
"field_a": "expected",
|
|
|
|
"field_b": "unchecked",
|
|
|
|
}
|
|
|
|
|
|
|
|
ar := aclRule{
|
|
|
|
Field: "field_a",
|
|
|
|
IsPresent: aclTestBool(true),
|
|
|
|
}
|
|
|
|
|
|
|
|
if !ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v does not match fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
|
|
|
|
ar.IsPresent = aclTestBool(false)
|
|
|
|
|
|
|
|
if ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v matches fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
|
|
|
|
ar.IsPresent = aclTestBool(true)
|
|
|
|
delete(fields, "field_a")
|
|
|
|
|
|
|
|
if ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v matches fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
|
|
|
|
ar.IsPresent = aclTestBool(false)
|
|
|
|
if !ar.AppliesToFields(fields) {
|
|
|
|
t.Errorf("Rule %#v does not match fields %#v", ar, fields)
|
|
|
|
}
|
|
|
|
}
|