aboutsummaryrefslogtreecommitdiff
path: root/lib/api/tokenmanager.go
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api/tokenmanager.go')
-rw-r--r--lib/api/tokenmanager.go87
1 files changed, 87 insertions, 0 deletions
diff --git a/lib/api/tokenmanager.go b/lib/api/tokenmanager.go
index 73ba8425e..adccda2e9 100644
--- a/lib/api/tokenmanager.go
+++ b/lib/api/tokenmanager.go
@@ -7,10 +7,14 @@
package api
import (
+ "net/http"
"slices"
+ "strings"
"time"
+ "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
+ "github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/sync"
)
@@ -135,3 +139,86 @@ func (m *tokenManager) scheduledSave() {
bs, _ := m.tokens.Marshal() // can't fail
_ = m.miscDB.PutBytes(m.key, bs) // can fail, but what are we going to do?
}
+
+type tokenCookieManager struct {
+ cookieName string
+ shortID string
+ guiCfg config.GUIConfiguration
+ evLogger events.Logger
+ tokens *tokenManager
+}
+
+func newTokenCookieManager(shortID string, guiCfg config.GUIConfiguration, evLogger events.Logger, miscDB *db.NamespacedKV) *tokenCookieManager {
+ return &tokenCookieManager{
+ cookieName: "sessionid-" + shortID,
+ shortID: shortID,
+ guiCfg: guiCfg,
+ evLogger: evLogger,
+ tokens: newTokenManager("sessions", miscDB, maxSessionLifetime, maxActiveSessions),
+ }
+}
+
+func (m *tokenCookieManager) createSession(username string, persistent bool, w http.ResponseWriter, r *http.Request) {
+ sessionid := m.tokens.New()
+
+ // Best effort detection of whether the connection is HTTPS --
+ // either directly to us, or as used by the client towards a reverse
+ // proxy who sends us headers.
+ connectionIsHTTPS := r.TLS != nil ||
+ strings.ToLower(r.Header.Get("x-forwarded-proto")) == "https" ||
+ strings.Contains(strings.ToLower(r.Header.Get("forwarded")), "proto=https")
+ // If the connection is HTTPS, or *should* be HTTPS, set the Secure
+ // bit in cookies.
+ useSecureCookie := connectionIsHTTPS || m.guiCfg.UseTLS()
+
+ maxAge := 0
+ if persistent {
+ maxAge = int(maxSessionLifetime.Seconds())
+ }
+ http.SetCookie(w, &http.Cookie{
+ Name: m.cookieName,
+ Value: sessionid,
+ // In HTTP spec Max-Age <= 0 means delete immediately,
+ // but in http.Cookie MaxAge = 0 means unspecified (session) and MaxAge < 0 means delete immediately
+ MaxAge: maxAge,
+ Secure: useSecureCookie,
+ Path: "/",
+ })
+
+ emitLoginAttempt(true, username, r.RemoteAddr, m.evLogger)
+}
+
+func (m *tokenCookieManager) hasValidSession(r *http.Request) bool {
+ for _, cookie := range r.Cookies() {
+ // We iterate here since there may, historically, be multiple
+ // cookies with the same name but different path. Any "old" ones
+ // won't match an existing session and will be ignored, then
+ // later removed on logout or when timing out.
+ if cookie.Name == m.cookieName {
+ if m.tokens.Check(cookie.Value) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func (m *tokenCookieManager) destroySession(w http.ResponseWriter, r *http.Request) {
+ for _, cookie := range r.Cookies() {
+ // We iterate here since there may, historically, be multiple
+ // cookies with the same name but different path. We drop them
+ // all.
+ if cookie.Name == m.cookieName {
+ m.tokens.Delete(cookie.Value)
+
+ // Create a cookie deletion command
+ http.SetCookie(w, &http.Cookie{
+ Name: m.cookieName,
+ Value: "",
+ MaxAge: -1,
+ Secure: cookie.Secure,
+ Path: cookie.Path,
+ })
+ }
+ }
+}