@@ -13,16 +13,10 @@ import (
13
13
"github.com/mirkobrombin/goup/internal/plugin"
14
14
)
15
15
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
-
24
16
// AuthPluginConfig represents the configuration for the AuthPlugin.
25
17
type AuthPluginConfig struct {
18
+ // Whather the plugin is enabled.
19
+ Enable bool `json:"enable"`
26
20
// URL paths to protect with authentication.
27
21
ProtectedPaths []string `json:"protected_paths"`
28
22
// username:password pairs for authentication.
@@ -32,6 +26,7 @@ type AuthPluginConfig struct {
32
26
SessionExpiration int `json:"session_expiration"`
33
27
}
34
28
29
+ // session and AuthPluginState remain per domain.
35
30
type session struct {
36
31
Username string
37
32
Expiry time.Time
@@ -43,6 +38,15 @@ type AuthPluginState struct {
43
38
mu sync.RWMutex
44
39
}
45
40
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
+
46
50
func (p * AuthPlugin ) Name () string {
47
51
return "AuthPlugin"
48
52
}
@@ -55,17 +59,32 @@ func (p *AuthPlugin) OnInitForSite(conf config.SiteConfig, domainLogger *logger.
55
59
if err := p .SetupLoggers (conf , p .Name (), domainLogger ); err != nil {
56
60
return err
57
61
}
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
+ }
59
70
60
71
pluginConfigRaw , ok := conf .PluginConfigs [p .Name ()]
61
72
if ! ok {
73
+ // Default to disabled if plugin config is not present
74
+ p .siteConfigs [conf .Domain ] = AuthPluginConfig {Enable : false }
62
75
return nil
63
76
}
64
77
65
- // Parse plugin configuration.
66
- authConfig := AuthPluginConfig {}
78
+ authConfig := AuthPluginConfig {
79
+ Enable : false ,
80
+ }
81
+
67
82
if rawMap , ok := pluginConfigRaw .(map [string ]interface {}); ok {
68
83
// ProtectedPaths
84
+ if en , ok := rawMap ["enable" ].(bool ); ok {
85
+ authConfig .Enable = en
86
+ }
87
+
69
88
if paths , ok := rawMap ["protected_paths" ].([]interface {}); ok {
70
89
for _ , path := range paths {
71
90
if pStr , ok := path .(string ); ok {
@@ -98,28 +117,48 @@ func (p *AuthPlugin) OnInitForSite(conf config.SiteConfig, domainLogger *logger.
98
117
return errors .New ("session_expiration cannot be less than -1" )
99
118
}
100
119
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
+ }
102
130
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 )
106
133
}
107
134
108
135
p .DomainLogger .Infof ("[AuthPlugin] Initialized for domain=%s with session_expiration=%d" ,
109
- conf .Domain , p . conf .SessionExpiration )
136
+ conf .Domain , authConfig .SessionExpiration )
110
137
111
138
return nil
112
139
}
113
140
114
141
func (p * AuthPlugin ) BeforeRequest (r * http.Request ) {}
115
142
116
143
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 {
118
157
return false
119
158
}
120
159
121
160
protected := false
122
- for _ , path := range p . conf .ProtectedPaths {
161
+ for _ , path := range conf .ProtectedPaths {
123
162
if strings .HasPrefix (r .URL .Path , path ) {
124
163
protected = true
125
164
break
@@ -130,9 +169,13 @@ func (p *AuthPlugin) HandleRequest(w http.ResponseWriter, r *http.Request) bool
130
169
return false
131
170
}
132
171
133
- // The path is protected, check session or credentials.
172
+ st , hasState := p .states [host ]
173
+ if ! hasState {
174
+ return false
175
+ }
176
+
134
177
ip := getClientIP (r )
135
- if sess , exists := p . state .getSession (ip ); exists {
178
+ if sess , exists := st .getSession (ip ); exists {
136
179
p .DomainLogger .Infof ("[AuthPlugin] Valid session for IP=%s user=%s" , ip , sess .Username )
137
180
return false
138
181
}
@@ -151,15 +194,13 @@ func (p *AuthPlugin) HandleRequest(w http.ResponseWriter, r *http.Request) bool
151
194
return true
152
195
}
153
196
154
- // Validate credentials
155
- expectedPassword , userExists := p .conf .Credentials [username ]
197
+ expectedPassword , userExists := conf .Credentials [username ]
156
198
if ! userExists || expectedPassword != password {
157
199
unauthorized (w )
158
200
return true
159
201
}
160
202
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 )
163
204
p .PluginLogger .Infof ("[AuthPlugin] Authenticated IP=%s user=%s" , ip , username )
164
205
165
206
return false
0 commit comments