ex6api_error.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /*
  2. Available routes:
  3. /api/users OPTIONS Expose available actions
  4. /api/users GET Return user list, possibly filtered
  5. /api/users POST Create user
  6. /api/users/123 GET Return user 123 -> not listed in book
  7. /api/users/123 PUT Update user 123
  8. /api/users/123 DELETE Delete user 123
  9. POST fields:
  10. "user" -> name
  11. "mail" -> email
  12. "first"
  13. "last"
  14. */
  15. package main
  16. import (
  17. "database/sql"
  18. "encoding/json"
  19. "fmt"
  20. "log"
  21. "net/http"
  22. _ "github.com/go-sql-driver/mysql"
  23. "strconv"
  24. "strings"
  25. "github.com/gorilla/mux"
  26. )
  27. var database *sql.DB
  28. type Users struct {
  29. Users []User `json:"users"`
  30. }
  31. type User struct {
  32. ID int `json:"id"`
  33. Name string `json:"username"`
  34. Email string `json:"mail"`
  35. First string `json:"first"`
  36. Last string `json:"last"`
  37. }
  38. type CreateResponse struct {
  39. Error string `json:"error"`
  40. ErrorCode int `json:"code"`
  41. }
  42. // MySQL error message look like Error nn: description
  43. func dbParseEror(raw_error string) (string, int64) {
  44. Parts := strings.Split(raw_error, ": ")
  45. errorMessage := Parts[1] // After the ":"
  46. Code := strings.Split(Parts[0], "Error ")
  47. errorCode, _ := strconv.ParseInt(Code[1], 10, 32)
  48. return errorMessage, errorCode
  49. }
  50. type ErrMsg struct {
  51. ErrCode int
  52. StatusCode int
  53. Message string
  54. }
  55. func ErrorMessages(code int64) ErrMsg {
  56. var em ErrMsg = ErrMsg{}
  57. errorMessage := ""
  58. statusCode := 200
  59. errorCode := 0
  60. switch code {
  61. case 1062:
  62. errorMessage = "Duplicate entry"
  63. statusCode = 409
  64. errorCode = 10
  65. }
  66. em.ErrCode = errorCode
  67. em.Message = errorMessage
  68. em.StatusCode = statusCode
  69. return em
  70. }
  71. /*
  72. UserCreate creates a new user and stores it.
  73. The SQL query is vulnerable to injection, so the route matching needs be safe.
  74. */
  75. func UserCreate(w http.ResponseWriter, r *http.Request) {
  76. w.Header().Set("Access-Control-Allow-Origin", "http://localhost:8000")
  77. NewUser := User{
  78. Name: r.FormValue("user"),
  79. Email: r.FormValue("mail"),
  80. First: r.FormValue("first"),
  81. Last: r.FormValue("last"),
  82. }
  83. output, err := json.MarshalIndent(NewUser, "", " ")
  84. fmt.Println(string(output))
  85. if err != nil {
  86. fmt.Println("Something went wrong!")
  87. }
  88. Response := CreateResponse{}
  89. sql := "INSERT INTO users SET user_name='" + NewUser.Name +
  90. "', user_first='" + NewUser.First +
  91. "', user_last='" + NewUser.Last +
  92. "', user_email='" + NewUser.Email + "'"
  93. result, err := database.Exec(sql)
  94. if err != nil {
  95. errorMessage, errorCode := dbParseEror(err.Error())
  96. em := ErrorMessages(errorCode)
  97. fmt.Println(errorMessage)
  98. Response.Error = em.Message
  99. Response.ErrorCode = em.ErrCode
  100. w.WriteHeader(em.StatusCode)
  101. }
  102. var id int64
  103. var rows int64
  104. if result != nil {
  105. id, _ = result.LastInsertId()
  106. rows, _ = result.RowsAffected()
  107. } else {
  108. id = 0
  109. rows = 0
  110. }
  111. fmt.Printf("Id: %d, Rows affected: %d\n", id, rows)
  112. createOutput, err := json.Marshal(Response)
  113. fmt.Fprintln(w, string(createOutput))
  114. }
  115. func UsersRetrieve(w http.ResponseWriter, r *http.Request) {
  116. w.Header().Set("Pragma", "no-cache")
  117. rows, err := database.Query(`
  118. SELECT u.user_id, u.user_name, u.user_email, u.user_first, u.user_last
  119. FROM users u
  120. LIMIT 10
  121. `)
  122. if err != nil {
  123. fmt.Fprintln(w, "Something went wrong!")
  124. log.Fatal(err)
  125. }
  126. Response := Users{}
  127. for rows.Next() {
  128. user := User{}
  129. rows.Scan(&user.ID, &user.Name, &user.Email, &user.First, &user.Last)
  130. Response.Users = append(Response.Users, user)
  131. }
  132. output, _ := json.MarshalIndent(Response, "", " ")
  133. fmt.Fprintln(w, string(output))
  134. }
  135. type API struct {
  136. Message string `json:"message"`
  137. }
  138. func UserMethods(w http.ResponseWriter, r *http.Request) {
  139. }
  140. func UserReplace(w http.ResponseWriter, r *http.Request) {
  141. }
  142. func UserDelete(w http.ResponseWriter, r *http.Request) {
  143. }
  144. func UserRetrieve(w http.ResponseWriter, r *http.Request) {
  145. w.Header().Set("Pragma", "no-cache")
  146. vars := mux.Vars(r)
  147. id := vars["id"]
  148. user := User{}
  149. var result API
  150. sqlQuery := `
  151. SELECT u.user_id, u.user_name, u.user_first, u.user_last, u.user_email
  152. FROM users u
  153. WHERE u.user_id = ?
  154. `
  155. stmt, err := database.Prepare(sqlQuery)
  156. if err != nil {
  157. log.Fatal(err.Error())
  158. }
  159. row := stmt.QueryRow(id)
  160. scanErr := row.Scan(&user.ID, &user.Name, &user.First, &user.Last, &user.Email)
  161. switch {
  162. case scanErr == sql.ErrNoRows:
  163. // FIXME XSS
  164. result = API{Message: fmt.Sprintf("No such user: %s", id)}
  165. json, err := json.MarshalIndent(result, "", " ")
  166. if err != nil {
  167. log.Fatal(err.Error())
  168. }
  169. w.Write(json)
  170. case err != nil:
  171. // FIXME XSS
  172. result = API{Message: fmt.Sprintf("Error reading user: %s", id)}
  173. json, errIndent := json.MarshalIndent(result, "", " ")
  174. if errIndent != nil {
  175. log.Fatal(errIndent.Error())
  176. }
  177. w.Write(json)
  178. log.Fatal(err.Error())
  179. case err == nil:
  180. json, errIndent := json.MarshalIndent(user, "", " ")
  181. if errIndent != nil {
  182. log.Fatal(errIndent.Error())
  183. }
  184. w.Write(json)
  185. }
  186. }
  187. func main() {
  188. var err error
  189. db, err := sql.Open("mysql", "goroot:gopass@/goweb_social_network")
  190. if err != nil {
  191. log.Fatal(err.Error())
  192. }
  193. database = db
  194. routes := mux.NewRouter()
  195. routes.HandleFunc("/api/users", UserCreate).Methods("POST")
  196. routes.HandleFunc(`/api/users`, UsersRetrieve).Methods("GET")
  197. // Not yet implemented.
  198. routes.HandleFunc(`/api/users`, UserMethods).Methods("OPTIONS")
  199. routes.HandleFunc(`/api/users/{id:[\d]+}`, UserRetrieve).Methods("GET")
  200. routes.HandleFunc(`/api/users/{id:[\d]+}`, UserReplace).Methods("PUT")
  201. routes.HandleFunc(`/api/users/{id:[\d]+}`, UserDelete).Methods("DELETE")
  202. // --------------------
  203. http.Handle("/", routes)
  204. http.ListenAndServe(":8080", nil)
  205. }