1
0
Fork 0
mirror of https://github.com/Luzifer/nginx-sso.git synced 2024-10-17 23:24:22 +00:00
nginx-sso/acl_test.go
Knut Ahlers b515730dcf
Rewrite ACL logic
in order to

- allow explicity deny for anon access
- allow multiple rule-sets to apply to the same request

Signed-off-by: Knut Ahlers <knut@ahlers.me>
2023-07-28 20:15:21 +02:00

285 lines
6.5 KiB
Go

package main
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
var (
aclTestUser = "test"
aclTestGroups = []string{"group_a", "group_b"}
ptrBoolTrue = func(v bool) *bool { return &v }(true)
)
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",
}
assert.True(t, r.AppliesToRequest(aclTestRequest(fields)))
delete(fields, "field_c")
assert.False(t, r.AppliesToRequest(aclTestRequest(fields)))
}
func TestGroupAuthenticated(t *testing.T) {
a := acl{RuleSets: []aclRuleSet{{
Rules: []aclRule{
{
Field: "field_a",
MatchString: aclTestString("expected"),
},
},
Allow: []string{"@_authenticated"},
}}}
fields := map[string]string{
"field_a": "expected",
}
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")
a.RuleSets[0].Allow = []string{"testgroup"}
assert.False(t, a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(fields)))
}
func TestAnonymousAccess(t *testing.T) {
a := acl{RuleSets: []aclRuleSet{{
Rules: []aclRule{
{
Field: "field_a",
MatchString: aclTestString("expected"),
},
},
Allow: []string{groupAnonymous},
}}}
fields := map[string]string{
"field_a": "expected",
}
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")
}
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},
},
},
}
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")
}
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)
}
}