From 0fe96ad7ed3b0c3a68c9a5889b34b764782f9266 Mon Sep 17 00:00:00 2001 From: Chen-I Lim Date: Tue, 9 Feb 2021 12:27:34 -0800 Subject: [PATCH] Check for single user token --- mac/Focalboard/AppDelegate.swift | 5 ++++- mac/Focalboard/ViewController.swift | 13 ++++++++++++- server/api/api.go | 11 +++++++---- server/api/auth.go | 16 +++++++++++----- server/main/main.go | 10 +++++----- server/server/server.go | 8 ++++---- server/ws/websockets.go | 26 +++++++++++--------------- 7 files changed, 54 insertions(+), 35 deletions(-) diff --git a/mac/Focalboard/AppDelegate.swift b/mac/Focalboard/AppDelegate.swift index e9fe0074e..10774ae14 100644 --- a/mac/Focalboard/AppDelegate.swift +++ b/mac/Focalboard/AppDelegate.swift @@ -9,6 +9,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { private var serverProcess: Process? var serverPort = 8088 + var sessionToken: String = "" func applicationDidFinishLaunching(_ aNotification: Notification) { copyResources() @@ -69,6 +70,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { } private func startServer() { + sessionToken = UUID().uuidString + let cwdUrl = webFolder() let executablePath = Bundle.main.path(forResource: "resources/bin/focalboard-server", ofType: "") @@ -76,7 +79,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { NSLog("pid: \(pid)") let serverProcess = Process() serverProcess.currentDirectoryPath = cwdUrl.path - serverProcess.arguments = ["-monitorpid", "\(pid)", "-port", "\(serverPort)", "--single-user"] + serverProcess.arguments = ["-monitorpid", "\(pid)", "-port", "\(serverPort)", "-single-user", sessionToken] serverProcess.launchPath = executablePath serverProcess.launch() self.serverProcess = serverProcess diff --git a/mac/Focalboard/ViewController.swift b/mac/Focalboard/ViewController.swift index cf3f85bf4..0912eea30 100644 --- a/mac/Focalboard/ViewController.swift +++ b/mac/Focalboard/ViewController.swift @@ -19,7 +19,6 @@ class ViewController: webView.uiDelegate = self clearWebViewCache() - loadHomepage() // Do any additional setup after loading the view. NotificationCenter.default.addObserver(self, selector: #selector(onServerStarted), name: AppDelegate.serverStartedNotification, object: nil) @@ -40,10 +39,22 @@ class ViewController: @objc func onServerStarted() { NSLog("onServerStarted") DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.updateSessionToken() self.loadHomepage() } } + private func updateSessionToken() { + let appDelegate = NSApplication.shared.delegate as! AppDelegate + let script = WKUserScript( + source: "localStorage.setItem('sessionId', '\(appDelegate.sessionToken)');", + injectionTime: .atDocumentStart, + forMainFrameOnly: true + ) + webView.configuration.userContentController.removeAllUserScripts() + webView.configuration.userContentController.addUserScript(script) + } + private func loadHomepage() { let appDelegate = NSApplication.shared.delegate as! AppDelegate let port = appDelegate.serverPort diff --git a/server/api/api.go b/server/api/api.go index 017487ca9..76d113b27 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -27,12 +27,15 @@ const ( // REST APIs type API struct { - appBuilder func() *app.App - singleUser bool + appBuilder func() *app.App + singleUserToken string } -func NewAPI(appBuilder func() *app.App, singleUser bool) *API { - return &API{appBuilder: appBuilder, singleUser: singleUser} +func NewAPI(appBuilder func() *app.App, singleUserToken string) *API { + return &API{ + appBuilder: appBuilder, + singleUserToken: singleUserToken, + } } func (a *API) app() *app.App { diff --git a/server/api/auth.go b/server/api/auth.go index 1270a0a53..802f3358c 100644 --- a/server/api/auth.go +++ b/server/api/auth.go @@ -198,12 +198,19 @@ func (a *API) sessionRequired(handler func(w http.ResponseWriter, r *http.Reques func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request), required bool) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - log.Printf(`Single User: %v`, a.singleUser) - if a.singleUser { + token, _ := auth.ParseAuthTokenFromRequest(r) + + log.Printf(`Single User: %v`, len(a.singleUserToken) > 0) + if len(a.singleUserToken) > 0 { + if required && (token != a.singleUserToken) { + errorResponse(w, http.StatusUnauthorized, nil, nil) + return + } + now := time.Now().Unix() session := &model.Session{ ID: "single-user", - Token: "single-user", + Token: token, UserID: "single-user", CreateAt: now, UpdateAt: now, @@ -213,11 +220,10 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request) return } - token, _ := auth.ParseAuthTokenFromRequest(r) session, err := a.app().GetSession(token) if err != nil { if required { - errorResponse(w, http.StatusUnauthorized, map[string]string{"error": err.Error()}, err) + errorResponse(w, http.StatusUnauthorized, nil, err) return } diff --git a/server/main/main.go b/server/main/main.go index b2bdd09a2..f4b279d79 100644 --- a/server/main/main.go +++ b/server/main/main.go @@ -63,12 +63,12 @@ func main() { // Command line args pMonitorPid := flag.Int("monitorpid", -1, "a process ID") pPort := flag.Int("port", config.Port, "the port number") - pSingleUser := flag.Bool("single-user", false, "single user mode") + pSingleUserToken := flag.String("single-user", "", "single user token") flag.Parse() - singleUser := false - if pSingleUser != nil { - singleUser = *pSingleUser + singleUserToken := "" + if pSingleUserToken != nil { + singleUserToken = *pSingleUserToken } if pMonitorPid != nil && *pMonitorPid > 0 { @@ -81,7 +81,7 @@ func main() { config.Port = *pPort } - server, err := server.New(config, singleUser) + server, err := server.New(config, singleUserToken) if err != nil { log.Fatal("server.New ERROR: ", err) } diff --git a/server/server/server.go b/server/server/server.go index 4a683a76b..fe063f91e 100644 --- a/server/server/server.go +++ b/server/server/server.go @@ -49,7 +49,7 @@ type Server struct { localModeServer *http.Server } -func New(cfg *config.Configuration, singleUser bool) (*Server, error) { +func New(cfg *config.Configuration, singleUserToken string) (*Server, error) { logger, err := zap.NewProduction() if err != nil { return nil, err @@ -63,7 +63,7 @@ func New(cfg *config.Configuration, singleUser bool) (*Server, error) { auth := auth.New(cfg, store) - wsServer := ws.NewServer(auth, singleUser) + wsServer := ws.NewServer(auth, singleUserToken) filesBackendSettings := model.FileSettings{} filesBackendSettings.SetDefaults(false) @@ -78,7 +78,7 @@ func New(cfg *config.Configuration, singleUser bool) (*Server, error) { webhookClient := webhook.NewClient(cfg) appBuilder := func() *app.App { return app.New(cfg, store, auth, wsServer, filesBackend, webhookClient) } - api := api.NewAPI(appBuilder, singleUser) + api := api.NewAPI(appBuilder, singleUserToken) // Local router for admin APIs localRouter := mux.NewRouter() @@ -157,7 +157,7 @@ func New(cfg *config.Configuration, singleUser bool) (*Server, error) { "port": cfg.Port == config.DefaultPort, "useSSL": cfg.UseSSL, "dbType": cfg.DBType, - "single_user": singleUser, + "single_user": len(singleUserToken) > 0, } }) telemetryService.RegisterTracker("activity", func() map[string]interface{} { diff --git a/server/ws/websockets.go b/server/ws/websockets.go index b00044c49..f485322d9 100644 --- a/server/ws/websockets.go +++ b/server/ws/websockets.go @@ -18,11 +18,11 @@ type IsValidSessionToken func(token string) bool // Server is a WebSocket server. type Server struct { - upgrader websocket.Upgrader - listeners map[string][]*websocket.Conn - mu sync.RWMutex - auth *auth.Auth - singleUser bool + upgrader websocket.Upgrader + listeners map[string][]*websocket.Conn + mu sync.RWMutex + auth *auth.Auth + singleUserToken string } // UpdateMsg is sent on block updates @@ -50,7 +50,7 @@ type websocketSession struct { } // NewServer creates a new Server. -func NewServer(auth *auth.Auth, singleUser bool) *Server { +func NewServer(auth *auth.Auth, singleUserToken string) *Server { return &Server{ listeners: make(map[string][]*websocket.Conn), upgrader: websocket.Upgrader{ @@ -58,8 +58,8 @@ func NewServer(auth *auth.Auth, singleUser bool) *Server { return true }, }, - auth: auth, - singleUser: singleUser, + auth: auth, + singleUserToken: singleUserToken, } } @@ -91,7 +91,7 @@ func (ws *Server) handleWebSocketOnChange(w http.ResponseWriter, r *http.Request wsSession := websocketSession{ client: client, - isAuthenticated: ws.singleUser, + isAuthenticated: false, } // Simple message handling loop @@ -134,8 +134,8 @@ func (ws *Server) handleWebSocketOnChange(w http.ResponseWriter, r *http.Request } func (ws *Server) isValidSessionToken(token string) bool { - if ws.singleUser { - return true + if len(ws.singleUserToken) > 0 { + return token == ws.singleUserToken } session, err := ws.auth.GetSession(token) @@ -160,10 +160,6 @@ func (ws *Server) authenticateListener(wsSession *websocketSession, token string } func (ws *Server) checkAuthentication(wsSession *websocketSession, command *WebsocketCommand) bool { - if ws.singleUser { - return true - } - if wsSession.isAuthenticated { return true }