mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-05-22 20:20:32 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f446d5897 |
@@ -2,7 +2,6 @@ package bootstrap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
@@ -126,7 +125,8 @@ func (app *BootstrapApp) setupPolicyEngine() error {
|
|||||||
Config: app.config,
|
Config: app.config,
|
||||||
})
|
})
|
||||||
policyEngine.RegisterRule(service.RuleIPBypassed, &service.IPBypassedRule{
|
policyEngine.RegisterRule(service.RuleIPBypassed, &service.IPBypassedRule{
|
||||||
Log: app.log,
|
Log: app.log,
|
||||||
|
Config: app.config,
|
||||||
})
|
})
|
||||||
|
|
||||||
app.services.policyEngine = policyEngine
|
app.services.policyEngine = policyEngine
|
||||||
|
|||||||
@@ -154,8 +154,9 @@ type AddressClaim struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type IPConfig struct {
|
type IPConfig struct {
|
||||||
Allow []string `description:"List of allowed IPs or CIDR ranges." yaml:"allow"`
|
Allow []string `description:"List of allowed IPs or CIDR ranges." yaml:"allow"`
|
||||||
Block []string `description:"List of blocked IPs or CIDR ranges." yaml:"block"`
|
Block []string `description:"List of blocked IPs or CIDR ranges." yaml:"block"`
|
||||||
|
Bypass []string `description:"List of IPs or CIDR ranges that bypass authentication entirely." yaml:"bypass"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OAuthConfig struct {
|
type OAuthConfig struct {
|
||||||
|
|||||||
@@ -224,15 +224,18 @@ func (rule *IPAllowedRule) Evaluate(ctx *ACLContext) Effect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type IPBypassedRule struct {
|
type IPBypassedRule struct {
|
||||||
Log *logger.Logger
|
Log *logger.Logger
|
||||||
|
Config model.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *IPBypassedRule) Evaluate(ctx *ACLContext) Effect {
|
func (rule *IPBypassedRule) Evaluate(ctx *ACLContext) Effect {
|
||||||
if ctx.ACLs == nil {
|
// merge global and per-app bypass lists
|
||||||
return EffectDeny
|
bypassList := append([]string{}, rule.Config.Auth.IP.Bypass...)
|
||||||
|
if ctx.ACLs != nil {
|
||||||
|
bypassList = append(bypassList, ctx.ACLs.IP.Bypass...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bypassed := range ctx.ACLs.IP.Bypass {
|
for _, bypassed := range bypassList {
|
||||||
match, err := utils.CheckIPFilter(bypassed, ctx.IP.String())
|
match, err := utils.CheckIPFilter(bypassed, ctx.IP.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rule.Log.App.Warn().Err(err).Str("item", bypassed).Msg("Invalid IP/CIDR in bypass list")
|
rule.Log.App.Warn().Err(err).Str("item", bypassed).Msg("Invalid IP/CIDR in bypass list")
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
)
|
)
|
||||||
@@ -669,23 +670,70 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
log := logger.NewLogger().WithTestConfig()
|
log := logger.NewLogger().WithTestConfig()
|
||||||
log.Init()
|
log.Init()
|
||||||
|
|
||||||
rule := &IPBypassedRule{Log: log}
|
defaultIPBR := &IPBypassedRule{Log: log}
|
||||||
|
globBypassIPBR := &IPBypassedRule{
|
||||||
|
Log: log,
|
||||||
|
Config: model.Config{Auth: model.AuthConfig{IP: model.IPConfig{Bypass: []string{"10.0.0.0/24"}}}},
|
||||||
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
rule *IPBypassedRule
|
||||||
ctx *ACLContext
|
ctx *ACLContext
|
||||||
expected Effect
|
expected Effect
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "deny when ACLs are nil",
|
name: "deny when ACLs are nil and no global bypass",
|
||||||
|
rule: defaultIPBR,
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: nil,
|
ACLs: nil,
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
},
|
},
|
||||||
expected: EffectDeny,
|
expected: EffectDeny,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "allows when ACLs are nil but IP matches global bypass",
|
||||||
|
rule: globBypassIPBR,
|
||||||
|
ctx: &ACLContext{
|
||||||
|
ACLs: nil,
|
||||||
|
IP: net.ParseIP("10.0.0.5"),
|
||||||
|
},
|
||||||
|
expected: EffectAllow,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "denies when ACLs are nil and IP does not match global bypass",
|
||||||
|
rule: globBypassIPBR,
|
||||||
|
ctx: &ACLContext{
|
||||||
|
ACLs: nil,
|
||||||
|
IP: net.ParseIP("192.168.1.1"),
|
||||||
|
},
|
||||||
|
expected: EffectDeny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "allows when IP matches per-app bypass but not global bypass",
|
||||||
|
rule: defaultIPBR,
|
||||||
|
ctx: &ACLContext{
|
||||||
|
ACLs: &model.App{
|
||||||
|
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
||||||
|
},
|
||||||
|
IP: net.ParseIP("10.0.0.5"),
|
||||||
|
},
|
||||||
|
expected: EffectAllow,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "allows when IP matches global bypass but not per-app bypass",
|
||||||
|
rule: globBypassIPBR,
|
||||||
|
ctx: &ACLContext{
|
||||||
|
ACLs: &model.App{
|
||||||
|
IP: model.AppIP{Bypass: []string{"172.16.0.0/24"}},
|
||||||
|
},
|
||||||
|
IP: net.ParseIP("10.0.0.5"),
|
||||||
|
},
|
||||||
|
expected: EffectAllow,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "allows when IP matches bypass list",
|
name: "allows when IP matches bypass list",
|
||||||
|
rule: defaultIPBR,
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
||||||
@@ -696,6 +744,7 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "denies when IP does not match bypass list",
|
name: "denies when IP does not match bypass list",
|
||||||
|
rule: defaultIPBR,
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
||||||
@@ -706,6 +755,7 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "denies when bypass list is empty",
|
name: "denies when bypass list is empty",
|
||||||
|
rule: defaultIPBR,
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{},
|
ACLs: &model.App{},
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
@@ -714,6 +764,7 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "skips invalid bypass entries and allows on later match",
|
name: "skips invalid bypass entries and allows on later match",
|
||||||
|
rule: defaultIPBR,
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Bypass: []string{"not-an-ip", "10.0.0.1"}},
|
IP: model.AppIP{Bypass: []string{"not-an-ip", "10.0.0.1"}},
|
||||||
@@ -726,7 +777,7 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
assert.Equal(t, tt.expected, rule.Evaluate(tt.ctx))
|
assert.Equal(t, tt.expected, tt.rule.Evaluate(tt.ctx))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user