|
@@ -0,0 +1,250 @@
|
|
|
+/*
|
|
|
+Available routes:
|
|
|
+
|
|
|
+/api/users OPTIONS Expose available actions
|
|
|
+/api/users GET Return user list, possibly filtered
|
|
|
+/api/users POST Create user
|
|
|
+/api/users/123 GET Return user 123 -> not listed in book
|
|
|
+/api/users/123 PUT Update user 123
|
|
|
+/api/users/123 DELETE Delete user 123
|
|
|
+
|
|
|
+POST fields:
|
|
|
+ "user" -> name
|
|
|
+ "mail" -> email
|
|
|
+ "first"
|
|
|
+ "last"
|
|
|
+*/
|
|
|
+
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "database/sql"
|
|
|
+ "encoding/json"
|
|
|
+ "fmt"
|
|
|
+ "log"
|
|
|
+ "net/http"
|
|
|
+
|
|
|
+ _ "github.com/go-sql-driver/mysql"
|
|
|
+
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+
|
|
|
+ "github.com/gorilla/mux"
|
|
|
+)
|
|
|
+
|
|
|
+var database *sql.DB
|
|
|
+
|
|
|
+type Users struct {
|
|
|
+ Users []User `json:"users"`
|
|
|
+}
|
|
|
+
|
|
|
+type User struct {
|
|
|
+ ID int `json:"id"`
|
|
|
+ Name string `json:"username"`
|
|
|
+ Email string `json:"mail"`
|
|
|
+ First string `json:"first"`
|
|
|
+ Last string `json:"last"`
|
|
|
+}
|
|
|
+
|
|
|
+type CreateResponse struct {
|
|
|
+ Error string `json:"error"`
|
|
|
+ ErrorCode int `json:"code"`
|
|
|
+}
|
|
|
+
|
|
|
+// MySQL error message look like Error nn: description
|
|
|
+func dbParseEror(raw_error string) (string, int64) {
|
|
|
+ Parts := strings.Split(raw_error, ": ")
|
|
|
+ errorMessage := Parts[1] // After the ":"
|
|
|
+ Code := strings.Split(Parts[0], "Error ")
|
|
|
+ errorCode, _ := strconv.ParseInt(Code[1], 10, 32)
|
|
|
+ return errorMessage, errorCode
|
|
|
+}
|
|
|
+
|
|
|
+type ErrMsg struct {
|
|
|
+ ErrCode int
|
|
|
+ StatusCode int
|
|
|
+ Message string
|
|
|
+}
|
|
|
+
|
|
|
+func ErrorMessages(code int64) ErrMsg {
|
|
|
+ var em ErrMsg = ErrMsg{}
|
|
|
+
|
|
|
+ errorMessage := ""
|
|
|
+ statusCode := 200
|
|
|
+ errorCode := 0
|
|
|
+
|
|
|
+ switch code {
|
|
|
+ case 1062:
|
|
|
+ errorMessage = "Duplicate entry"
|
|
|
+ statusCode = 409
|
|
|
+ errorCode = 10
|
|
|
+ }
|
|
|
+
|
|
|
+ em.ErrCode = errorCode
|
|
|
+ em.Message = errorMessage
|
|
|
+ em.StatusCode = statusCode
|
|
|
+
|
|
|
+ return em
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+UserCreate creates a new user and stores it.
|
|
|
+
|
|
|
+The SQL query is vulnerable to injection, so the route matching needs be safe.
|
|
|
+*/
|
|
|
+func UserCreate(w http.ResponseWriter, r *http.Request) {
|
|
|
+ w.Header().Set("Access-Control-Allow-Origin", "http://localhost:8000")
|
|
|
+
|
|
|
+ NewUser := User{
|
|
|
+ Name: r.FormValue("user"),
|
|
|
+ Email: r.FormValue("mail"),
|
|
|
+ First: r.FormValue("first"),
|
|
|
+ Last: r.FormValue("last"),
|
|
|
+ }
|
|
|
+
|
|
|
+ output, err := json.MarshalIndent(NewUser, "", " ")
|
|
|
+ fmt.Println(string(output))
|
|
|
+ if err != nil {
|
|
|
+ fmt.Println("Something went wrong!")
|
|
|
+ }
|
|
|
+
|
|
|
+ Response := CreateResponse{}
|
|
|
+ sql := "INSERT INTO users SET user_name='" + NewUser.Name +
|
|
|
+ "', user_first='" + NewUser.First +
|
|
|
+ "', user_last='" + NewUser.Last +
|
|
|
+ "', user_email='" + NewUser.Email + "'"
|
|
|
+ result, err := database.Exec(sql)
|
|
|
+ if err != nil {
|
|
|
+ errorMessage, errorCode := dbParseEror(err.Error())
|
|
|
+ em := ErrorMessages(errorCode)
|
|
|
+ fmt.Println(errorMessage)
|
|
|
+
|
|
|
+ Response.Error = em.Message
|
|
|
+ Response.ErrorCode = em.ErrCode
|
|
|
+ w.WriteHeader(em.StatusCode)
|
|
|
+ }
|
|
|
+
|
|
|
+ var id int64
|
|
|
+ var rows int64
|
|
|
+ if result != nil {
|
|
|
+ id, _ = result.LastInsertId()
|
|
|
+ rows, _ = result.RowsAffected()
|
|
|
+ } else {
|
|
|
+ id = 0
|
|
|
+ rows = 0
|
|
|
+ }
|
|
|
+ fmt.Printf("Id: %d, Rows affected: %d\n", id, rows)
|
|
|
+
|
|
|
+ createOutput, err := json.Marshal(Response)
|
|
|
+ fmt.Fprintln(w, string(createOutput))
|
|
|
+}
|
|
|
+
|
|
|
+func UsersRetrieve(w http.ResponseWriter, r *http.Request) {
|
|
|
+ w.Header().Set("Pragma", "no-cache")
|
|
|
+ rows, err := database.Query(`
|
|
|
+SELECT u.user_id, u.user_name, u.user_email, u.user_first, u.user_last
|
|
|
+FROM users u
|
|
|
+LIMIT 10
|
|
|
+ `)
|
|
|
+ if err != nil {
|
|
|
+ fmt.Fprintln(w, "Something went wrong!")
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+ Response := Users{}
|
|
|
+ for rows.Next() {
|
|
|
+ user := User{}
|
|
|
+ rows.Scan(&user.ID, &user.Name, &user.Email, &user.First, &user.Last)
|
|
|
+ Response.Users = append(Response.Users, user)
|
|
|
+ }
|
|
|
+
|
|
|
+ output, _ := json.MarshalIndent(Response, "", " ")
|
|
|
+ fmt.Fprintln(w, string(output))
|
|
|
+}
|
|
|
+
|
|
|
+type API struct {
|
|
|
+ Message string `json:"message"`
|
|
|
+}
|
|
|
+
|
|
|
+func UserMethods(w http.ResponseWriter, r *http.Request) {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func UserReplace(w http.ResponseWriter, r *http.Request) {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func UserDelete(w http.ResponseWriter, r *http.Request) {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func UserRetrieve(w http.ResponseWriter, r *http.Request) {
|
|
|
+ w.Header().Set("Pragma", "no-cache")
|
|
|
+
|
|
|
+ vars := mux.Vars(r)
|
|
|
+ id := vars["id"]
|
|
|
+ user := User{}
|
|
|
+ var result API
|
|
|
+
|
|
|
+ sqlQuery := `
|
|
|
+SELECT u.user_id, u.user_name, u.user_first, u.user_last, u.user_email
|
|
|
+FROM users u
|
|
|
+WHERE u.user_id = ?
|
|
|
+`
|
|
|
+ stmt, err := database.Prepare(sqlQuery)
|
|
|
+ if err != nil {
|
|
|
+ log.Fatal(err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ row := stmt.QueryRow(id)
|
|
|
+ scanErr := row.Scan(&user.ID, &user.Name, &user.First, &user.Last, &user.Email)
|
|
|
+ switch {
|
|
|
+ case scanErr == sql.ErrNoRows:
|
|
|
+ // FIXME XSS
|
|
|
+ result = API{Message: fmt.Sprintf("No such user: %s", id)}
|
|
|
+ json, err := json.MarshalIndent(result, "", " ")
|
|
|
+ if err != nil {
|
|
|
+ log.Fatal(err.Error())
|
|
|
+ }
|
|
|
+ w.Write(json)
|
|
|
+ case err != nil:
|
|
|
+ // FIXME XSS
|
|
|
+ result = API{Message: fmt.Sprintf("Error reading user: %s", id)}
|
|
|
+ json, errIndent := json.MarshalIndent(result, "", " ")
|
|
|
+ if errIndent != nil {
|
|
|
+ log.Fatal(errIndent.Error())
|
|
|
+ }
|
|
|
+ w.Write(json)
|
|
|
+ log.Fatal(err.Error())
|
|
|
+ case err == nil:
|
|
|
+ json, errIndent := json.MarshalIndent(user, "", " ")
|
|
|
+ if errIndent != nil {
|
|
|
+ log.Fatal(errIndent.Error())
|
|
|
+ }
|
|
|
+ w.Write(json)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func main() {
|
|
|
+ var err error
|
|
|
+
|
|
|
+ db, err := sql.Open("mysql", "goroot:gopass@/goweb_social_network")
|
|
|
+ if err != nil {
|
|
|
+ log.Fatal(err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ database = db
|
|
|
+
|
|
|
+ routes := mux.NewRouter()
|
|
|
+ routes.HandleFunc("/api/users", UserCreate).Methods("POST")
|
|
|
+ routes.HandleFunc(`/api/users`, UsersRetrieve).Methods("GET")
|
|
|
+
|
|
|
+ // Not yet implemented.
|
|
|
+ routes.HandleFunc(`/api/users`, UserMethods).Methods("OPTIONS")
|
|
|
+ routes.HandleFunc(`/api/users/{id:[\d]+}`, UserRetrieve).Methods("GET")
|
|
|
+ routes.HandleFunc(`/api/users/{id:[\d]+}`, UserReplace).Methods("PUT")
|
|
|
+ routes.HandleFunc(`/api/users/{id:[\d]+}`, UserDelete).Methods("DELETE")
|
|
|
+ // --------------------
|
|
|
+
|
|
|
+ http.Handle("/", routes)
|
|
|
+ http.ListenAndServe(":8080", nil)
|
|
|
+}
|