Skip to content

Commit b63615b

Browse files
author
mirkobrombin
committed
fix: AuthPlugin always get triggered, even by sites that does not declare it
1 parent 5b781f0 commit b63615b

File tree

1 file changed

+65
-24
lines changed

1 file changed

+65
-24
lines changed

plugins/auth.go

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,10 @@ import (
1313
"github.com/mirkobrombin/goup/internal/plugin"
1414
)
1515

16-
// AuthPlugin provides HTTP Basic Authentication for protected paths.
17-
type AuthPlugin struct {
18-
plugin.BasePlugin
19-
20-
conf AuthPluginConfig
21-
state *AuthPluginState
22-
}
23-
2416
// AuthPluginConfig represents the configuration for the AuthPlugin.
2517
type AuthPluginConfig struct {
18+
// Whather the plugin is enabled.
19+
Enable bool `json:"enable"`
2620
// URL paths to protect with authentication.
2721
ProtectedPaths []string `json:"protected_paths"`
2822
// username:password pairs for authentication.
@@ -32,6 +26,7 @@ type AuthPluginConfig struct {
3226
SessionExpiration int `json:"session_expiration"`
3327
}
3428

29+
// session and AuthPluginState remain per domain.
3530
type session struct {
3631
Username string
3732
Expiry time.Time
@@ -43,6 +38,15 @@ type AuthPluginState struct {
4338
mu sync.RWMutex
4439
}
4540

41+
// AuthPlugin provides HTTP Basic Authentication for protected paths.
42+
// Instead of storing one global conf/state, we now store a map of domain->config
43+
// and a map of domain->plugin state, so each site has its own settings.
44+
type AuthPlugin struct {
45+
plugin.BasePlugin
46+
siteConfigs map[string]AuthPluginConfig
47+
states map[string]*AuthPluginState
48+
}
49+
4650
func (p *AuthPlugin) Name() string {
4751
return "AuthPlugin"
4852
}
@@ -55,17 +59,32 @@ func (p *AuthPlugin) OnInitForSite(conf config.SiteConfig, domainLogger *logger.
5559
if err := p.SetupLoggers(conf, p.Name(), domainLogger); err != nil {
5660
return err
5761
}
58-
p.state = &AuthPluginState{sessions: make(map[string]session)}
62+
63+
// Initialize maps once
64+
if p.siteConfigs == nil {
65+
p.siteConfigs = make(map[string]AuthPluginConfig)
66+
}
67+
if p.states == nil {
68+
p.states = make(map[string]*AuthPluginState)
69+
}
5970

6071
pluginConfigRaw, ok := conf.PluginConfigs[p.Name()]
6172
if !ok {
73+
// Default to disabled if plugin config is not present
74+
p.siteConfigs[conf.Domain] = AuthPluginConfig{Enable: false}
6275
return nil
6376
}
6477

65-
// Parse plugin configuration.
66-
authConfig := AuthPluginConfig{}
78+
authConfig := AuthPluginConfig{
79+
Enable: false,
80+
}
81+
6782
if rawMap, ok := pluginConfigRaw.(map[string]interface{}); ok {
6883
// ProtectedPaths
84+
if en, ok := rawMap["enable"].(bool); ok {
85+
authConfig.Enable = en
86+
}
87+
6988
if paths, ok := rawMap["protected_paths"].([]interface{}); ok {
7089
for _, path := range paths {
7190
if pStr, ok := path.(string); ok {
@@ -98,28 +117,48 @@ func (p *AuthPlugin) OnInitForSite(conf config.SiteConfig, domainLogger *logger.
98117
return errors.New("session_expiration cannot be less than -1")
99118
}
100119

101-
p.conf = authConfig
120+
p.siteConfigs[conf.Domain] = authConfig
121+
122+
if !authConfig.Enable {
123+
return nil
124+
}
125+
126+
// Initialize a new AuthPluginState for this domain
127+
p.states[conf.Domain] = &AuthPluginState{
128+
sessions: make(map[string]session),
129+
}
102130

103-
// Initialization of the plugin state with optional session cleanup.
104-
if p.conf.SessionExpiration != -1 {
105-
go p.state.cleanupExpiredSessions(time.Minute, p.DomainLogger)
131+
if authConfig.SessionExpiration != -1 {
132+
go p.states[conf.Domain].cleanupExpiredSessions(time.Minute, p.DomainLogger)
106133
}
107134

108135
p.DomainLogger.Infof("[AuthPlugin] Initialized for domain=%s with session_expiration=%d",
109-
conf.Domain, p.conf.SessionExpiration)
136+
conf.Domain, authConfig.SessionExpiration)
110137

111138
return nil
112139
}
113140

114141
func (p *AuthPlugin) BeforeRequest(r *http.Request) {}
115142

116143
func (p *AuthPlugin) HandleRequest(w http.ResponseWriter, r *http.Request) bool {
117-
if p.conf.Credentials == nil {
144+
// Determine the domain (host without port) to select the correct config/state
145+
host := r.Host
146+
if colonIndex := strings.Index(host, ":"); colonIndex != -1 {
147+
host = host[:colonIndex]
148+
}
149+
150+
// If we have no config for this domain, do nothing
151+
conf, ok := p.siteConfigs[host]
152+
if !ok || !conf.Enable {
153+
return false
154+
}
155+
156+
if conf.Credentials == nil {
118157
return false
119158
}
120159

121160
protected := false
122-
for _, path := range p.conf.ProtectedPaths {
161+
for _, path := range conf.ProtectedPaths {
123162
if strings.HasPrefix(r.URL.Path, path) {
124163
protected = true
125164
break
@@ -130,9 +169,13 @@ func (p *AuthPlugin) HandleRequest(w http.ResponseWriter, r *http.Request) bool
130169
return false
131170
}
132171

133-
// The path is protected, check session or credentials.
172+
st, hasState := p.states[host]
173+
if !hasState {
174+
return false
175+
}
176+
134177
ip := getClientIP(r)
135-
if sess, exists := p.state.getSession(ip); exists {
178+
if sess, exists := st.getSession(ip); exists {
136179
p.DomainLogger.Infof("[AuthPlugin] Valid session for IP=%s user=%s", ip, sess.Username)
137180
return false
138181
}
@@ -151,15 +194,13 @@ func (p *AuthPlugin) HandleRequest(w http.ResponseWriter, r *http.Request) bool
151194
return true
152195
}
153196

154-
// Validate credentials
155-
expectedPassword, userExists := p.conf.Credentials[username]
197+
expectedPassword, userExists := conf.Credentials[username]
156198
if !userExists || expectedPassword != password {
157199
unauthorized(w)
158200
return true
159201
}
160202

161-
// Create a new session
162-
p.state.createSession(ip, username, p.conf.SessionExpiration, p.PluginLogger)
203+
st.createSession(ip, username, conf.SessionExpiration, p.PluginLogger)
163204
p.PluginLogger.Infof("[AuthPlugin] Authenticated IP=%s user=%s", ip, username)
164205

165206
return false

0 commit comments

Comments
 (0)