// Examine the request for a valid session cookie. If one is found, return the username and user permissions. If the cookie is somehow invalid it will be unset. // // If dbValidate is set to true the username specified in the cookie will be loaded and validated againsts the database. If the user no longer exists the session will be unset. This also means that the returned User will have fully populated fields. func Challenge(w http.ResponseWriter, r *http.Request, dbValidate bool) (*data.User, int) { session, _ := store.Get(r, "rter-credentials") // Will return an empty map if there isn't a valid session cookie user := new(data.User) uval, ok := session.Values["username"] if !ok { deleteSession(session, w, r) return nil, 0 } user.Username, ok = uval.(string) if !ok { deleteSession(session, w, r) return nil, 0 } if dbValidate { err := storage.Select(user) if err != nil { log.Println(err) if err == storage.ErrZeroAffected { log.Println("Valid Cookie for non-existant user: "******"permissions"] if !ok { return user, 0 } permissions, ok := pval.(int) if !ok { return user, 0 } return user, permissions }
// Generic Update handler func Update(w http.ResponseWriter, r *http.Request) { user, permissions := auth.Challenge(w, r, true) if user == nil || permissions < 1 { http.Error(w, "Please Login", http.StatusUnauthorized) return } w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") vars := mux.Vars(r) var ( val interface{} // Generic container for the updated object err error ) // Build a URI like representation of the datatype types := []string{vars["datatype"]} if childtype, ok := vars["childtype"]; ok { types = append(types, childtype) } // Switch based on that URI like representation and instantiate something in the generic container. Also infer the identifier from the vars and perform validation. switch strings.Join(types, "/") { case "items": v := new(data.Item) v.ID, err = strconv.ParseInt(vars["key"], 10, 64) val = v case "items/comments": v := new(data.ItemComment) v.ID, err = strconv.ParseInt(vars["childkey"], 10, 64) val = v case "users": if vars["key"] != user.Username { http.Error(w, "Please don't hack other users", http.StatusUnauthorized) return } v := new(data.User) v.Username = vars["key"] val = v case "users/direction": v := new(data.UserDirection) v.Username = vars["key"] val = v case "roles": v := new(data.Role) v.Title = vars["key"] val = v case "taxonomy": v := new(data.Term) v.Term = vars["key"] val = v case "taxonomy/ranking": v := new(data.TermRanking) v.Term = vars["key"] val = v default: http.NotFound(w, r) return } if err != nil { log.Println(err, vars) http.Error(w, "Malformed key in URI", http.StatusBadRequest) return } err = storage.Select(val) //Load previous values so that update is non distructive of empty fields if err == storage.ErrZeroAffected { http.NotFound(w, r) return } else if err != nil { log.Println(err) http.Error(w, "Select3 Database error, likely due to malformed request.", http.StatusInternalServerError) return } // Decode the JSON into our generic object. The decode will leave unscpecified fields untouched. decoder := json.NewDecoder(r.Body) err = decoder.Decode(&val) if err != nil { log.Println(err) http.Error(w, "Malformed json.", http.StatusBadRequest) return } // Validate JSON, run pre-update hooks, etc... //We must reset fields we set earlier incase they were changed during the JSON decode switch v := val.(type) { case (*data.Item): v.ID, err = strconv.ParseInt(vars["key"], 10, 64) v.Author = user.Username case (*data.ItemComment): v.ID, err = strconv.ParseInt(vars["childkey"], 10, 64) v.Author = user.Username case (*data.User): v.Username = vars["key"] case (*data.UserDirection): v.Username = vars["key"] v.LockUsername = user.Username case (*data.Role): v.Title = vars["key"] case (*data.Term): v.Term = vars["key"] v.Author = user.Username case (*data.TermRanking): v.Term = vars["key"] } if err != nil { log.Println(err, vars) http.Error(w, "Malformed key in URI", http.StatusBadRequest) return } // Run the update err = storage.Update(val) if err == storage.ErrZeroAffected { w.WriteHeader(http.StatusNotModified) return } else if err != nil { log.Println(err) http.Error(w, "Update2 Database error, likely due to malformed request.", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") // Header are important when GZIP is enabled // Return the updated item encoder := json.NewEncoder(w) err = encoder.Encode(val) if err != nil { log.Println(err) } }
// Generic Read handler for reading single objects func Read(w http.ResponseWriter, r *http.Request) { //No Auth for the moment w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") vars := mux.Vars(r) var ( val interface{} // Generic container for the read object err error ) // Build a URI like representation of the datatype types := []string{vars["datatype"]} if childtype, ok := vars["childtype"]; ok { types = append(types, childtype) } // Switch based on that URI like representation and instantiate something in the generic container. Also infer the identifier from the vars and perform validation. switch strings.Join(types, "/") { case "items": item := new(data.Item) item.ID, err = strconv.ParseInt(vars["key"], 10, 64) val = item case "items/comments": comment := new(data.ItemComment) comment.ID, err = strconv.ParseInt(vars["childkey"], 10, 64) val = comment case "users": user := new(data.User) user.Username = vars["key"] val = user case "users/direction": direction := new(data.UserDirection) direction.Username = vars["key"] val = direction case "roles": role := new(data.Role) role.Title = vars["key"] val = role case "taxonomy": term := new(data.Term) term.Term = vars["key"] val = term case "taxonomy/ranking": ranking := new(data.TermRanking) ranking.Term = vars["key"] val = ranking default: http.NotFound(w, r) return } if err != nil { log.Println(err) http.Error(w, "Malformed key in URI", http.StatusBadRequest) return } // Perform the Select err = storage.Select(val) if err == storage.ErrZeroAffected { http.Error(w, "No matches for query", http.StatusNotFound) return } else if err != nil { log.Println(err) http.Error(w, "Select Database error, likely due to malformed request", http.StatusInternalServerError) return } // Important. Let's never send salt/hash out. switch v := val.(type) { case *data.User: v.Salt = "" v.Password = "" } w.Header().Set("Content-Type", "application/json") // Header are important when GZIP is enabled // Return the object we've selected from the database. encoder := json.NewEncoder(w) err = encoder.Encode(val) if err != nil { log.Println(err) } }
// Handle requests containing JSON with user credentials. If the credentials are valid the response will set a session cookie effectively logging in the user. // // Expected JSON format: // {"Username":"******", "Password":"******"} // // Invalid credentials will result in a 401 StatusUnauthorized response. Malformed JSON will result in a 400 StatusBadRequest or possibly 500 StatusInternalServerError. func AuthHandlerFunc(w http.ResponseWriter, r *http.Request) { // Parse the JSON into a user object loginInfo := new(data.User) decoder := json.NewDecoder(r.Body) err := decoder.Decode(&loginInfo) if err != nil { log.Println("JSON problem") log.Println(err) http.Error(w, "Malformed json.", http.StatusBadRequest) return } // Try and load the actual user user := new(data.User) user.Username = loginInfo.Username err = storage.Select(user) if err == storage.ErrZeroAffected { log.Println("No such user: "******"Invalid credentials.", http.StatusUnauthorized) return } else if err != nil { log.Println(err) http.Error(w, "Database error, likely due to malformed request.", http.StatusInternalServerError) return } // Validate if !user.Auth(loginInfo.Password) { log.Println("Wrong Password for: ", user.Username) http.Error(w, "Invalid credentials.", http.StatusUnauthorized) return } // Get role permissions role := new(data.Role) role.Title = user.Role err = storage.Select(role) if err != nil { log.Println("Issues loading role during auth", err) role.Permissions = 0 // Default to no permissions } // Build a cookie session session, _ := store.Get(r, "rter-credentials") session.Values["username"] = user.Username session.Values["role"] = user.Role session.Values["permissions"] = role.Permissions err = session.Save(r, w) if err != nil { log.Println(err) } }
// Handle a POST request with an image, phone_id, lat, lng and heading. Return a target heading from the web UI func MultiUploadHandler(rterDir string, uploadPath string, w http.ResponseWriter, r *http.Request) { imageFile, header, err := r.FormFile("image") if err != nil { return } user := new(data.User) user.Username = r.FormValue("phone_id") err = storage.Select(user) if err == storage.ErrZeroAffected { user.Role = "public" storage.Insert(user) // log.Println("upload failed, phone_id invalid:", user.Username) // http.Error(w, "Invalid credentials.", http.StatusUnauthorized) // return } else if err != nil { log.Println(err) http.Error(w, "Database error, likely due to malformed request.", http.StatusInternalServerError) return } os.Mkdir(filepath.Join(uploadPath, user.Username), os.ModeDir|0775) matchingItems := make([]*data.Item, 0) err = storage.SelectWhere(&matchingItems, "WHERE Type=\"streaming-video-v0\" AND Author=?", user.Username) exists := true if err == storage.ErrZeroAffected { exists = false } else if err != nil { log.Println(err) http.Error(w, "Database error, likely due to malformed request.", http.StatusInternalServerError) return } var item *data.Item if exists { item = matchingItems[0] } else { item = new(data.Item) } item.Author = user.Username item.Type = "streaming-video-v0" item.Live = true item.HasGeo = true item.HasHeading = true item.Lat, err = strconv.ParseFloat(r.FormValue("lat"), 64) if err != nil { item.HasGeo = false } item.Lng, err = strconv.ParseFloat(r.FormValue("lng"), 64) if err != nil { item.HasGeo = false } item.Heading, err = strconv.ParseFloat(r.FormValue("heading"), 64) if err != nil { item.HasHeading = false } item.StartTime = time.Now() item.StopTime = item.StartTime path := uploadPath if strings.HasSuffix(header.Filename, ".png") { path = filepath.Join(path, fmt.Sprintf("%v/%v.png", user.Username, item.StopTime.UnixNano())) } else if strings.HasSuffix(header.Filename, ".jpg") || strings.HasSuffix(header.Filename, "jpeg") { path = filepath.Join(path, fmt.Sprintf("%v/%v.jpg", user.Username, item.StopTime.UnixNano())) } outputFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0664) if err != nil { log.Println(err) http.Error(w, "Error, likely due to malformed request.", http.StatusInternalServerError) } defer outputFile.Close() io.Copy(outputFile, imageFile) path = path[len(rterDir):] item.ContentURI = path item.ThumbnailURI = path if exists { storage.Update(item) } else { storage.Insert(item) } userDirection := new(data.UserDirection) userDirection.Username = user.Username err = storage.Select(userDirection) if err != nil { log.Println(err) http.Error(w, "Database error, likely due to malformed request.", http.StatusInternalServerError) return } w.Write([]byte(strconv.FormatFloat(userDirection.Heading, 'f', 6, 64))) }