From 5971c00a4f8e09235d75bd1d5d3d84c32048cba6 Mon Sep 17 00:00:00 2001 From: Antony Male Date: Fri, 29 Jan 2016 17:16:01 +0000 Subject: [PATCH] Support multiple API keys (command-line and config) (fixes #2747) --- cmd/syncthing/gui.go | 2 +- cmd/syncthing/gui_auth.go | 3 +-- cmd/syncthing/gui_csrf.go | 5 +++-- cmd/syncthing/main.go | 2 +- lib/config/guiconfiguration.go | 16 ++++++++++++---- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 7e7cc4f51..de4150438 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -238,7 +238,7 @@ func (s *apiService) Serve() { // Wrap everything in CSRF protection. The /rest prefix should be // protected, other requests will grant cookies. - handler := csrfMiddleware(s.id.String()[:5], "/rest", guiCfg.APIKey(), mux) + handler := csrfMiddleware(s.id.String()[:5], "/rest", guiCfg, mux) // Add the CORS handling handler = corsMiddleware(handler) diff --git a/cmd/syncthing/gui_auth.go b/cmd/syncthing/gui_auth.go index 1344a43e4..82a634e4e 100644 --- a/cmd/syncthing/gui_auth.go +++ b/cmd/syncthing/gui_auth.go @@ -33,9 +33,8 @@ func emitLoginAttempt(success bool, username string) { } func basicAuthAndSessionMiddleware(cookieName string, cfg config.GUIConfiguration, next http.Handler) http.Handler { - apiKey := cfg.APIKey() return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if apiKey != "" && r.Header.Get("X-API-Key") == apiKey { + if cfg.IsValidAPIKey(r.Header.Get("X-API-Key")) { next.ServeHTTP(w, r) return } diff --git a/cmd/syncthing/gui_csrf.go b/cmd/syncthing/gui_csrf.go index 74e425717..00e2d3e93 100644 --- a/cmd/syncthing/gui_csrf.go +++ b/cmd/syncthing/gui_csrf.go @@ -13,6 +13,7 @@ import ( "os" "strings" + "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/sync" ) @@ -30,11 +31,11 @@ const maxCsrfTokens = 25 // Check for CSRF token on /rest/ URLs. If a correct one is not given, reject // the request with 403. For / and /index.html, set a new CSRF cookie if none // is currently set. -func csrfMiddleware(unique, prefix, apiKey string, next http.Handler) http.Handler { +func csrfMiddleware(unique string, prefix string, cfg config.GUIConfiguration, next http.Handler) http.Handler { loadCsrfTokens() return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Allow requests carrying a valid API key - if apiKey != "" && r.Header.Get("X-API-Key") == apiKey { + if cfg.IsValidAPIKey(r.Header.Get("X-API-Key")) { next.ServeHTTP(w, r) return } diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index 755896863..145b3084b 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -478,7 +478,7 @@ func upgradeViaRest() error { cfg, _ := loadConfig() target := cfg.GUI().URL() r, _ := http.NewRequest("POST", target+"/rest/system/upgrade", nil) - r.Header.Set("X-API-Key", cfg.GUI().APIKey()) + r.Header.Set("X-API-Key", cfg.GUI().RawAPIKey) tr := &http.Transport{ Dial: dialer.Dial, diff --git a/lib/config/guiconfiguration.go b/lib/config/guiconfiguration.go index 3cda0eb34..14b824577 100644 --- a/lib/config/guiconfiguration.go +++ b/lib/config/guiconfiguration.go @@ -76,9 +76,17 @@ func (c GUIConfiguration) URL() string { return u.String() } -func (c GUIConfiguration) APIKey() string { - if override := os.Getenv("STGUIAPIKEY"); override != "" { - return override +// Returns whether the given API key is valid, including both the value in config +// and any overrides +func (c GUIConfiguration) IsValidAPIKey(apiKey string) bool { + switch apiKey { + case "": + return false + + case c.RawAPIKey, os.Getenv("STGUIAPIKEY"): + return true + + default: + return false } - return c.RawAPIKey }