func slaveHandler(s *websocket.Conn, sessionID int, dbStore *Store, redisAddr string) { xlog.Debugf("entering SlaveHandler") c, err := redis.Dial("tcp", redisAddr) if err != nil { xlog.Errorf("redis.Dial failed: %v", err) return } defer c.Close() psc := redis.PubSubConn{Conn: c} topic := fmt.Sprintf("session.%d", sessionID) psc.Subscribe(topic) defer psc.Unsubscribe(topic) for { switch v := psc.Receive().(type) { case redis.Message: StatCount("command for slave", 1) var cmd Command if err := json.Unmarshal(v.Data, &cmd); err != nil { break } if err := websocket.JSON.Send(s, cmd); err != nil { xlog.Errorf("slaveHandler: JSON.Send failed: %v", err) return } if cmd.Cmd == "close" { return } case redis.Subscription: xlog.Debugf("mkay... redis.Subscription received: %#v", v) } } }
func ConvertFileToPDF(src, target string) error { cmd := exec.Command("unoconv", "-f", "pdf", "--stdout", src) // cmd := exec.Command("cat", src) f, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE, 0644) if err != nil { return err } defer f.Close() stdout, _ := cmd.StdoutPipe() err = cmd.Start() if err != nil { xlog.Errorf("running unoconv failed: %v", err) return err } io.Copy(f, stdout) err = cmd.Wait() if err != nil { xlog.Errorf("cmd.Wait returned error: %v", err) return err } fi, _ := f.Stat() if fi.Size() == 0 { os.Remove(target) xlog.Error("file resulting from conversion is empty") return errors.New("empty file") } return nil }
func (h *StartSessionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if !VerifyXSRFToken(w, r, h.SessionStore, h.SecureCookie) { return } session, err := h.SessionStore.Get(r, SESSIONNAME) if err != nil { xlog.Debugf("Getting session failed: %v", err) StatCount("getting session failed", 1) http.Error(w, err.Error(), http.StatusForbidden) return } if session.Values["userID"] == nil { http.Error(w, "authentication required", http.StatusForbidden) return } StatCount("start session", 1) data := struct { UploadID string `json:"upload_id"` }{} if err := json.NewDecoder(r.Body).Decode(&data); err != nil { xlog.Errorf("Decoding POST body failed: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } uploadEntry, err := h.DBStore.GetUploadByPublicID(data.UploadID, session.Values["userID"].(int)) if err != nil { xlog.Errorf("Querying upload %s failed: %v", data.UploadID, err) http.Error(w, err.Error(), http.StatusNotFound) return } id := generateID() if err := h.DBStore.InsertSession(&Session{ UploadID: uploadEntry.ID, PublicID: id, Started: time.Now().UTC(), }); err != nil { xlog.Errorf("Insert failed: %v", err) http.Error(w, "insert failed", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{"id": id}) }
func Connect(w http.ResponseWriter, r *http.Request, u auth.User, sessionStore sessions.Store, secureCookie *securecookie.SecureCookie, dbStore *Store) { StatCount("connect call", 1) session, err := sessionStore.Get(r, SESSIONNAME) if err != nil { xlog.Errorf("Error fetching session: %v", err) session, _ = sessionStore.New(r, SESSIONNAME) } if userID, ok := session.Values["userID"].(int); ok { xlog.Debugf("Connect: already logged in (userID = %d), connecting account", userID) // we have a valid session -> connect account to user username := u.Provider() + ":" + u.Id() err := dbStore.AddUser(username, userID) if err != nil { xlog.Errorf("Error adding user: %v", err) http.Error(w, err.Error(), http.StatusForbidden) return } w.Header().Set("Location", "/settings") } else { xlog.Debugf("Connect: not logged in, actually log in user.") // no valid session -> actually login user username := u.Provider() + ":" + u.Id() xlog.Debugf("Connect: username = %s", username) userID, err := dbStore.CreateUser(username) if err != nil { xlog.Errorf("Error creating user: %v", err) http.Error(w, err.Error(), http.StatusForbidden) return } xlog.Debugf("Connect: userID = %d", userID) // set session values session.Values["userID"] = userID session.Values["username"] = username session.Values["email"] = u.Email() session.Values["name"] = u.Name() session.Save(r, w) // set XSRF-TOKEN for AngularJS xsrftoken, _ := secureCookie.Encode(XSRFTOKEN, username) http.SetCookie(w, &http.Cookie{Name: XSRFTOKEN, Value: xsrftoken, Path: "/"}) w.Header().Set("Location", "/") } w.WriteHeader(http.StatusFound) }
func (h *GetUploadsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { session, err := h.SessionStore.Get(r, SESSIONNAME) if err != nil { xlog.Debugf("Getting session failed: %v", err) StatCount("getting session failed", 1) http.Error(w, err.Error(), http.StatusForbidden) return } userID, ok := session.Values["userID"].(int) if !ok { http.Error(w, "no userID in session", http.StatusForbidden) return } StatCount("get uploads", 1) result, err := h.DBStore.GetUploadsForUser(userID) if err != nil { xlog.Errorf("Couldn't query uploads: %v", err) http.Error(w, "query failed", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(result) }
func (h *DisconnectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { StatCount("disconnect call", 1) if !VerifyXSRFToken(w, r, h.SessionStore, h.SecureCookie) { return } // Only disconnect a connected user session, err := h.SessionStore.Get(r, SESSIONNAME) if err != nil { xlog.Errorf("Error fetching session: %v", err) http.Error(w, "Error fetching session", 500) return } token := session.Values["username"] if token == nil { http.Error(w, "Current user not connected", 401) return } // Reset the user's session session.Values["userID"] = nil session.Values["username"] = nil session.Values["name"] = nil session.Values["email"] = nil session.Save(r, w) w.WriteHeader(http.StatusNoContent) }
func main() { xlog.SetOutput(os.Stdout) options := struct { Topic string `goptions:"--topic, description='Topic', obligatory"` Channel string `goptions:"--channel, description='Channel', obligatory"` Lookupd string `goptions:"--lookupd, description='lookupd address', obligatory"` DSN string `goptions:"--dsn, description='MySQL DSN string', obligatory"` }{} goptions.ParseAndFail(&options) sqldb, err := sql.Open("mysql", options.DSN) if err != nil { xlog.Fatalf("sql.Open failed: %v", err) } r, err := nsq.NewReader(options.Topic, options.Channel) if err != nil { xlog.Fatalf("Opening reader for %s/%s failed: %v", options.Topic, options.Channel, err) } r.AddHandler(&Converter{DB: sqldb}) if err := r.ConnectToLookupd(options.Lookupd); err != nil { xlog.Errorf("Connecting to %s failed: %v", options.Lookupd, err) } select {} }
func (h *GetSessionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { session, err := h.SessionStore.Get(r, SESSIONNAME) if err != nil { xlog.Debugf("Getting session failed: %v", err) StatCount("getting session failed", 1) http.Error(w, err.Error(), http.StatusForbidden) return } if session.Values["username"] == nil { http.Error(w, "authentication required", http.StatusForbidden) return } StatCount("get sessions", 1) result, err := h.DBStore.GetSessions(session.Values["userID"].(int)) if err != nil { xlog.Errorf("Querying sessions failed: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(result) }
func (h *GetSessionInfoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { StatCount("session info", 1) session, err := h.SessionStore.Get(r, SESSIONNAME) if err != nil { xlog.Debugf("Getting session failed: %v", err) StatCount("getting session failed", 1) http.Error(w, err.Error(), http.StatusForbidden) return } userID := 0 if session.Values["userID"] != nil { userID = session.Values["userID"].(int) } publicID := r.URL.Query().Get(":id") result, err := h.DBStore.GetSessionInfoByPublicID(publicID, userID) if err != nil { xlog.Errorf("Loading session information failed: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(result) }
func (h *StopSessionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if !VerifyXSRFToken(w, r, h.SessionStore, h.SecureCookie) { return } session, err := h.SessionStore.Get(r, SESSIONNAME) if err != nil { xlog.Debugf("Getting session failed: %v", err) StatCount("getting session failed", 1) http.Error(w, err.Error(), http.StatusForbidden) return } if session.Values["userID"] == nil { http.Error(w, "authentication required", http.StatusForbidden) return } StatCount("stop session", 1) requestData := struct { PublicID string `json:"session_id"` }{} if err := json.NewDecoder(r.Body).Decode(&requestData); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } ownerID, sessionID, err := h.DBStore.GetOwnerForSession(requestData.PublicID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if ownerID != session.Values["userID"].(int) { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } h.DBStore.StopSession(requestData.PublicID) w.WriteHeader(http.StatusNoContent) c, err := redis.Dial("tcp", h.RedisAddr) if err != nil { xlog.Errorf("redis.Dial failed: %v", err) return } defer c.Close() cmdJSON, _ := json.Marshal(&Command{ Cmd: "close", SessionID: sessionID, Timestamp: time.Now(), }) c.Send("PUBLISH", fmt.Sprintf("session.%d", sessionID), string(cmdJSON)) c.Flush() }
// ConvertFileToPDF attempts to convert a file to PDF. func (store *FileUploadStore) ConvertFileToPDF(id, src, target string) error { msg, _ := json.Marshal(map[string]string{"src_file": src, "target_file": target, "upload_id": id}) if _, _, err := store.NSQ.Publish(store.Topic, msg); err != nil { xlog.Errorf("Queuing message to NSQ %s failed: %v", store.NSQ.Addr, err) return err } return nil }
// AddUser adds a new account (identified by username) to a user, identified by its // userID. func (s *Store) AddUser(username string, userID int) error { userData := []*struct { UserID int `meddler:"user_id"` }{} err := meddler.QueryAll(s.sqlDB, &userData, "SELECT user_id FROM accounts WHERE username = ? LIMIT 1", username) if err != nil { xlog.Errorf("AddUser: SELECT for username %s failed: %v", username, err) return err } if len(userData) > 0 { // account already logged in previously, migrate data to this user. if userData[0].UserID == userID { xlog.Debugf("userID is the same, not doing anything.") return nil } xlog.Debugf("AddUser: user exists, migrating data to this user. userID %d -> %d", userData[0].UserID, userID) // first, set account entries to current user. _, err := s.sqlDB.Exec("UPDATE accounts SET user_id = ? WHERE user_id = ?", userID, userData[0].UserID) if err != nil { xlog.Errorf("AddUser: migrating accounts for username %s to userID %d failed: %v", username, userID, err) return err } // then migrate uploads to current user. _, err = s.sqlDB.Exec("UPDATE uploads SET user_id = ? WHERE user_id = ?", userID, userData[0].UserID) if err != nil { xlog.Errorf("AddUser: migrating uploads for username %s to userID %d failed: %v", username, userID, err) return err } // finally, delete old user. ON DELETE CASCADE should clean up any old cruft. _, err = s.sqlDB.Exec("DELETE FROM users WHERE id = ?", userData[0].UserID) } else { // account is unknown, simply add new entry to accounts table. _, err := s.sqlDB.Exec("INSERT INTO accounts (username, user_id) VALUES (?, ?)", username, userID) if err != nil { xlog.Errorf("AddUser: INSERT failed: %v", err) return err } } return nil }
func executeCommand(cmd Command, dbStore *Store) { StatCount("command from master", 1) switch cmd.Cmd { case "clearSlide": if err := dbStore.ClearSlide(cmd.SessionID, cmd.Page); err != nil { xlog.Errorf("clearSlide for %d page %d failed: %v", cmd.SessionID, cmd.Page, err) } } }
func VerifyXSRFToken(w http.ResponseWriter, r *http.Request, sessionStore sessions.Store, secureCookie *securecookie.SecureCookie) bool { xsrftoken := r.Header.Get(XSRFTOKENHEADER) userID := "" err := secureCookie.Decode(XSRFTOKEN, xsrftoken, &userID) if err == nil { session, _ := sessionStore.Get(r, SESSIONNAME) if userID != "" && userID == session.Values["username"].(string) { xlog.Infof("XSRF verification success for user %s", session.Values["username"].(string)) return true } xlog.Errorf("XSRF issue: userID = %s session = %s", userID, session.Values["username"].(string)) } xlog.Errorf("XSRF verification failed: %v (Request: %#v", err, *r) http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) StatCount("XSRF verification failed", 1) return false }
// WebsocketHandler handles an incoming WebSocket and dispatches to the correct // handler based on whether the user is authenticated and whether the session // he's viewing belongs to him. func WebsocketHandler(s *websocket.Conn, dbStore *Store, sessionStore sessions.Store, redisAddr string) { StatCount("websocket", 1) xlog.Infof("WebsocketHandler: opened connection") r := s.Request() session, err := sessionStore.Get(r, SESSIONNAME) if err != nil { xlog.Debugf("Getting session failed: %v", err) StatCount("getting session failed", 1) return } sessionData := struct { SessionID string `json:"session_id"` }{} if err := websocket.JSON.Receive(s, &sessionData); err != nil { xlog.Errorf("WebsocketHandler: JSON.Receive failed: %v", err) return } owner, sessionID, err := dbStore.GetOwnerForSession(sessionData.SessionID) if err != nil { xlog.Errorf("GetOwnerForSession failed: %v", err) return } if session.Values["userID"] == nil { xlog.Errorf("WebsocketHandler is not authenticated -> slave handler") slaveHandler(s, sessionID, dbStore, redisAddr) } else if owner == session.Values["userID"].(int) { xlog.Infof("WebSocketHandler owner matches -> master handler") masterHandler(s, sessionID, dbStore, redisAddr) } else { xlog.Infof("WebSocketHandler owner doesn't match -> slave handler") slaveHandler(s, sessionID, dbStore, redisAddr) } }
func (c *Converter) HandleMessage(message *nsq.Message) error { xlog.Debugf("Processing Message %s: %s", message.Id, string(message.Body)) msg, err := simplejson.NewJson(message.Body) if err != nil { xlog.Errorf("HandleMessage: parsing message %s failed: %v", message.Id, err) return err } srcFile := msg.Get("src_file").MustString() targetFile := msg.Get("target_file").MustString() publicId := msg.Get("upload_id").MustString() if _, err := os.Stat(targetFile); err == nil { xlog.Debugf("target file %s already exists.", targetFile) return nil } if err := ConvertFileToPDF(srcFile, targetFile); err != nil { xlog.Errorf("Converting %s to %s failed: %v", srcFile, targetFile, err) _, err = c.DB.Exec("UPDATE uploads SET conversion = 'error' WHERE public_id = ?", publicId) if err != nil { xlog.Errorf("Updating conversion status for %s failed: %v", publicId, err) } os.Remove(srcFile) os.Remove(targetFile) return nil } else { _, err = c.DB.Exec("UPDATE uploads SET conversion = 'success' WHERE public_id = ?", publicId) if err != nil { xlog.Errorf("Updating conversion status for %s failed: %v", publicId, err) } } xlog.Debugf("Conversion of upload %s finished.", publicId) return nil }
func masterHandler(s *websocket.Conn, sessionID int, dbStore *Store, redisAddr string) { xlog.Debugf("entering MasterHandler") c, err := redis.Dial("tcp", redisAddr) if err != nil { xlog.Errorf("redis.Dial failed: %v", err) return } defer c.Close() for { var cmd Command if err := websocket.JSON.Receive(s, &cmd); err != nil { xlog.Errorf("masterHandler: JSON.Receive failed: %v", err) break } xlog.Debugf("masterHandler: received command: %#v", cmd) cmd.SessionID = sessionID cmd.Timestamp = time.Now() if cmd.Cmd != "clearSlide" { if err := dbStore.InsertCommand(&cmd); err != nil { xlog.Errorf("Inserting command failed: %v", err) break } } executeCommand(cmd, dbStore) cmdJSON, _ := json.Marshal(cmd) c.Send("PUBLISH", fmt.Sprintf("session.%d", sessionID), string(cmdJSON)) c.Flush() } xlog.Debugf("masterHandler: closing connection") }
func (h *LoggedInHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { StatCount("loggedin call", 1) jsonEncoder := json.NewEncoder(w) session, err := h.SessionStore.Get(r, SESSIONNAME) if err != nil { w.Header().Set("Content-Type", "application/json") jsonEncoder.Encode(map[string]bool{"logged_in": false}) xlog.Errorf("Error fetching session: %v", err) return } loggedIn := false username, ok := session.Values["username"].(string) if ok { loggedIn = true } w.Header().Set("Content-Type", "application/json") jsonEncoder.Encode(map[string]interface{}{"logged_in": loggedIn, "username": username}) }
func (h *ConnectedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { session, err := h.SessionStore.Get(r, SESSIONNAME) if err != nil { xlog.Errorf("Error fetching session: %v", err) http.Error(w, err.Error(), http.StatusForbidden) return } userID := session.Values["userID"].(int) systems := h.DBStore.GetConnectedSystemsForUser(userID) jsonData := make(map[string]bool) for _, s := range systems { jsonData[s] = true } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(jsonData) }
// Store stores a new file with a specified id in the filesystem. If the // file isn't a PDF file, it also attempts a conversion to a PDF file. func (store *FileUploadStore) Store(id string, uploadedFile io.Reader, origFileName string) (isPDF bool, err error) { filename := path.Join(store.UploadDir, id+".pdf") tmpFile := path.Join(store.TmpDir, id+"_"+origFileName) tmpf, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE, 0644) if err != nil { return false, err } io.Copy(tmpf, uploadedFile) tmpf.Close() f, err := os.Open(tmpFile) if err != nil { return false, err } defer f.Close() buf := make([]byte, 4) if _, err = f.Read(buf); err != nil { return false, err } if bytes.Equal(buf, []byte("%PDF")) { xlog.Debugf("%s is a PDF file, renaming to %s", tmpFile, filename) os.Rename(tmpFile, filename) return true, nil } if err = store.ConvertFileToPDF(id, tmpFile, filename); err != nil { xlog.Errorf("conversion to PDF of %s failed: %v", tmpFile, err) os.Remove(tmpFile) os.Remove(filename) return false, err } return false, nil }
// GetConnectedSystemsForUser returns a slice of auth service identifiers // for which accounts exist that are associated with the specified userID. func (s *Store) GetConnectedSystemsForUser(userID int) []string { systemMappings := map[string]string{ "google.com": "gplus", "twitter.com": "twitter", "persona": "persona", } connectedAccounts := []*struct { Username string `meddler:"username"` }{} if err := meddler.QueryAll(s.sqlDB, &connectedAccounts, "SELECT username FROM accounts WHERE user_id = ?", userID); err != nil { xlog.Errorf("Querying usernames for userID %d failed: %v", userID, err) return []string{} } systems := make([]string, 0, len(connectedAccounts)) for _, acc := range connectedAccounts { systems = append(systems, systemMappings[strings.Split(acc.Username, ":")[0]]) } return systems }
func (h *PersonaAuthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { session, err := h.SessionStore.Get(r, SESSIONNAME) if err != nil { xlog.Errorf("Error fetching session: %v", err) session, _ = h.SessionStore.New(r, SESSIONNAME) } assertionData := struct { Assertion string `json:"assertion"` }{} if err := json.NewDecoder(r.Body).Decode(&assertionData); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } form := url.Values{"assertion": []string{assertionData.Assertion}, "audience": []string{h.Audience}} xlog.Debugf("Verifying Persona assertion...") resp, err := http.PostForm("https://verifier.login.persona.org/verify", form) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } verifierResponse := struct { Status string `json:"status"` Email string `json:"email"` Audience string `json:"audience"` Expires uint64 `json:"expires"` Issuer string `json:"issuer"` }{} if err := json.NewDecoder(resp.Body).Decode(&verifierResponse); err != nil { http.Error(w, err.Error(), http.StatusForbidden) return } xlog.Debugf("Verifier response: %#v", verifierResponse) if verifierResponse.Status != "okay" { http.Error(w, "Not authenticated", http.StatusForbidden) return } if userID, ok := session.Values["userID"].(int); ok { xlog.Debugf("Persona: already logged in (userID = %d), connecting account", userID) // we have a valid session -> connect account to user username := "******" + verifierResponse.Email err := h.DBStore.AddUser(username, userID) if err != nil { xlog.Errorf("Persona: error adding user: %v", err) http.Error(w, err.Error(), http.StatusForbidden) return } w.WriteHeader(http.StatusOK) // TODO: maybe deliver some additional information? } else { username := "******" + verifierResponse.Email xlog.Debugf("Persona: username = %s", username) userID, err := h.DBStore.CreateUser(username) if err != nil { xlog.Errorf("Error creating user: %v", err) http.Error(w, err.Error(), http.StatusForbidden) } xlog.Debugf("Persona: userID = %d", userID) session.Values["userID"] = userID session.Values["username"] = username session.Values["email"] = verifierResponse.Email session.Save(r, w) xsrftoken, _ := h.SecureCookie.Encode(XSRFTOKEN, username) http.SetCookie(w, &http.Cookie{Name: XSRFTOKEN, Value: xsrftoken, Path: "/"}) w.WriteHeader(http.StatusOK) } }
func (h *UploadHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { /* // re-enable as soon as ng-upload can do that. if !VerifyXSRFToken(w, r, h.SessionStore, h.SecureCookie) { return } */ session, err := h.SessionStore.Get(r, SESSIONNAME) if err != nil { xlog.Debugf("Getting session failed: %v", err) StatCount("getting session failed", 1) http.Error(w, err.Error(), http.StatusForbidden) return } if session.Values["userID"] == nil { http.Error(w, "authentication required", http.StatusForbidden) return } if err := r.ParseMultipartForm(10 * 1024 * 1024); err != nil { http.Error(w, "couldn't parse form", http.StatusInternalServerError) return } StatCount("upload presentation", 1) title := r.FormValue("title") if title == "" { http.Error(w, "empty title", http.StatusNotAcceptable) return } file, fhdr, err := r.FormFile("file") if err != nil { http.Error(w, "couldn't read form", http.StatusInternalServerError) return } id := generateID() conversion := "success" if isPDF, err := h.UploadStore.Store(id, file, fhdr.Filename); err != nil { xlog.Errorf("Storing file for upload %s failed: %v", id, err) http.Error(w, err.Error(), http.StatusInternalServerError) } else if !isPDF { conversion = "progress" } if err := h.DBStore.InsertUpload(&Upload{ PublicID: id, UserID: session.Values["userID"].(int), Title: title, Uploaded: time.Now(), Conversion: conversion, }); err != nil { xlog.Errorf("Insert failed: %v", err) http.Error(w, "insert failed", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{"id": id}) }