@@ -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.
2517type 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.
3530type 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+
4650func (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
114141func (p * AuthPlugin ) BeforeRequest (r * http.Request ) {}
115142
116143func (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