func TestAuthenticateQueryParams(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) var userContext context.Context var authed bool s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userContext, authed = routes.Authenticate(ctx, w, r, logger, db) if authed { w.WriteHeader(http.StatusOK) } })) defer s.Close() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create(db, \"username\", \"password\") error: %s", err) } client := new(http.Client) req, err := http.NewRequest("GET", s.URL, new(bytes.Buffer)) if err != nil { t.Fatalf("http.NewRequest error: %s", err) } client.Do(req) if got, want := authed, false; got != want { t.Fatalf("authed: got %t, want %t", got, want) } req, err = http.NewRequest("GET", s.URL+"?public=username&private=password", new(bytes.Buffer)) if err != nil { t.Fatalf("http.NewRequest error: %s", err) } client.Do(req) if got, want := authed, true; got != want { t.Fatalf("authed: got %t, want %t") } authedU, ok := user.FromContext(userContext) if got, want := ok, true; got != want { t.Fatalf("_, ok := user.FromContext: got %t, want %t") } if got, want := data.Equivalent(authedU, u), true; got != want { t.Errorf("data.Equivalent(authedU, u): got %t, want %t", got, want) } }
func TestAuthenticatePostForm(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) var userContext context.Context var authed bool s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userContext, authed = routes.Authenticate(ctx, w, r, logger, db) if authed { w.WriteHeader(http.StatusOK) } })) defer s.Close() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create(db, \"username\", \"password\") error: %s", err) } client := new(http.Client) client.PostForm(s.URL, url.Values{ "public": []string{"username"}, "private": []string{"password"}, }) if got, want := authed, true; got != want { t.Fatalf("authed: got %t, want %t", got, want) } authedU, ok := user.FromContext(userContext) if got, want := ok, true; got != want { t.Fatalf("_, ok := user.FromContext: got %t, want %t", got, want) } if got, want := data.Equivalent(authedU, u), true; got != want { t.Fatal("data.Equivalent(authedU, u): got %t ,want %t", got, want) } }
func TestLoginPOST(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) m := http.NewServeMux() m.Handle("/login/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, ok := routes.Authenticate(ctx, w, r, logger, db) if !ok { t.Fatal("bad authentication") } routes.LoginPOST(ctx, w, r, db, logger) })) m.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusAccepted) })) s := httptest.NewServer(http.HandlerFunc(m.ServeHTTP)) defer s.Close() _, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create(db, \"username\", \"password\") error: %s", err) } req, err := http.NewRequest("GET", s.URL+"/login/", new(bytes.Buffer)) if err != nil { t.Fatalf("http.NewRequest error: %s", err) } req.SetBasicAuth("username", "password") jar, err := cookiejar.New(new(cookiejar.Options)) if err != nil { t.Fatalf("cookiejar.New error: %s", err) } client := &http.Client{ Jar: jar, } resp, err := client.Do(req) if err != nil { t.Fatalf("error posting to LoginPOST: %s", err) } t.Logf("Response:\n\t%v", resp) if got, want := resp.StatusCode, http.StatusAccepted; got != want { t.Errorf("resp.StatusCode: got %d, want %d", got, want) } iter, err := db.Query(models.SessionKind).Execute() if err != nil { t.Fatalf("db.Query(models.SessionKind).Execute(): %s", err) } seshes := mem.Slice(iter, func() data.Record { return new(models.Session) }) if got, want := len(seshes), 1; got != want { t.Fatalf("len(seshes): got %d, want %d", got, want) } uri, err := url.Parse(s.URL) if err != nil { t.Fatalf("url.Prase(s.URL) error: %s", err) } t.Logf("URL: %s", uri) cookies := jar.Cookies(uri) if got, want := len(cookies), 1; got != want { t.Fatalf("len(cookies): got %d, want %d", got, want) } if got, want := cookies[0].Value, seshes[0].(*models.Session).Token; got != want { t.Errorf("cookies[0].Value: got %s, want %s", got, want) } }
func router(ctx context.Context, m *Middleware, s *Services) (http.Handler, context.CancelFunc) { mux := http.NewServeMux() requestBackground, cancelAll := context.WithCancel(ctx) mux.Handle("/app/", http.StripPrefix("/app/", http.FileServer(s.AppFileSystem))) mux.HandleFunc(routes.Index, logRequest(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": w.Write([]byte("Who is John Galt?")) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // /records/query/ mux.HandleFunc(routes.RecordsQuery, logRequest(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": routes.Records.QueryGET(ctx, w, r, s.WebUIClient) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // /records/new/ mux.HandleFunc(routes.RecordsNew, logRequest(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": routes.Records.NewGET(ctx, w, r, s.WebUIClient) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // /records/create/ mux.HandleFunc(routes.RecordsCreate, logRequest(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": routes.Records.CreateGET(ctx, w, r, s.WebUIClient) case "POST": routes.Records.CreatePOST(ctx, w, r, s.WebUIClient) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // /records/edit/ mux.HandleFunc(routes.RecordsEdit, logRequest(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": routes.Records.EditGET(ctx, w, r, s.WebUIClient) case "POST": routes.Records.EditPOST(ctx, w, r, s.WebUIClient) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // /records/view/ mux.HandleFunc(routes.RecordsView, logRequest(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": routes.Records.ViewGET(ctx, w, r, s.WebUIClient) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // /records/delete/ mux.HandleFunc(routes.RecordsDelete, logRequest(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "POST": routes.Records.DeletePOST(ctx, w, r, s.WebUIClient) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // /register/ mux.HandleFunc(routes.Register, logRequest(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "POST": routes.RegisterPOST(requestBackground, w, r, s.WebUIClient) case "GET": routes.RegisterGET(requestBackground, w, r, s.WebUIClient) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // /login/ mux.HandleFunc(routes.Login, logRequest(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "POST": routes.LoginPOST(ctx, w, r, s.WebUIClient) case "GET": routes.LoginGET(requestBackground, w, r, s.WebUIClient) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // /record/ mux.HandleFunc(routes.Record, logRequest(cors(func(w http.ResponseWriter, r *http.Request) { if r.Method == "OPTIONS" { routes.RecordOPTIONS(requestBackground, w, r) return } ctx, ok := routes.Authenticate(requestBackground, w, r, s.Logger, s.DB) if !ok { return } switch r.Method { case "GET": routes.RecordGET(ctx, w, r, s.Logger, s.DB) case "POST": routes.RecordPOST(ctx, w, r, s.Logger, s.DB) case "DELETE": routes.RecordDELETE(ctx, w, r, s.Logger, s.DB) case "OPTIONS": routes.RecordOPTIONS(ctx, w, r) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }), s.Logger)) // /record/query/ mux.HandleFunc(routes.RecordQuery, logRequest(cors(func(w http.ResponseWriter, r *http.Request) { if r.Method == "OPTIONS" { routes.RecordOPTIONS(requestBackground, w, r) return } ctx, ok := routes.Authenticate(requestBackground, w, r, s.Logger, s.DB) if !ok { return } switch r.Method { case "POST": routes.RecordQueryPOST(ctx, w, r, s.Logger, s.DB) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }), s.Logger)) // /event/ mux.HandleFunc(routes.Event, logRequest(cors(func(w http.ResponseWriter, r *http.Request) { ctx, ok := routes.Authenticate(requestBackground, w, r, s.Logger, s.DB) if !ok { return } switch r.Method { case "POST": routes.EventPOST(ctx, w, r, s.DB, s.Logger) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }), s.Logger)) // /record/changes/ mux.HandleFunc(routes.RecordChanges, logRequest(websocket.Handler( routes.ContextualizeRecordChangesGET(requestBackground, s.DB, s.Logger), ).ServeHTTP, s.Logger)) // /command/sms/ mux.HandleFunc(routes.CommandSMS, logRequest(func(w http.ResponseWriter, r *http.Request) { ctx := requestBackground switch r.Method { case "POST": routes.CommandSMSPOST(ctx, w, r, s.Logger, s.SMSCommandSessions) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // /command/web/ mux.HandleFunc(routes.CommandWeb, logRequest(websocket.Handler( routes.ContextualizeCommandWebGET(requestBackground, s.DB, s.Logger), ).ServeHTTP, s.Logger)) // /mobile/location/ mux.HandleFunc(routes.MobileLocation, logRequest(func(w http.ResponseWriter, r *http.Request) { ctx, ok := routes.Authenticate(requestBackground, w, r, s.Logger, s.DB) if !ok { return } switch r.Method { case "POST": routes.MobileLocationPOST(ctx, w, r, s.Logger, s.DB) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // /cal/week/ mux.HandleFunc(routes.CalWeek, logRequest(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": cal.WeekGET(ctx, w, r, s.CalWebUIClient) default: http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } }, s.Logger)) // Handle letsencrypt fs := http.FileServer(http.Dir("/var/www/elos/")) mux.Handle("/.well-known/", logRequest(func(w http.ResponseWriter, r *http.Request) { fs.ServeHTTP(w, r) }, s.Logger)) return mux, cancelAll }
// --- TestAuthenticateSession {{{ func TestAuthenticateSession(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) var userContext context.Context var authed bool s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userContext, authed = routes.Authenticate(ctx, w, r, logger, db) if authed { w.WriteHeader(http.StatusOK) } })) defer s.Close() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create(db, \"username\", \"password\") error: %s", err) } client := new(http.Client) req, err := http.NewRequest("GET", s.URL, new(bytes.Buffer)) if err != nil { t.Fatalf("http.NewRequest error: %s", err) } req.AddCookie(&http.Cookie{ Name: "elos-session-token", Value: "garbage", }) client.Do(req) if got, want := authed, false; got != want { t.Errorf("authed: got %t, want %t", got, want) } sesh := models.NewSessionForUser(u) sesh.SetID(db.NewID()) if err := db.Save(sesh); err != nil { t.Fatalf("db.Save(sesh) error: %s", err) } req, err = http.NewRequest("GET", s.URL, new(bytes.Buffer)) if err != nil { t.Fatalf("http.NewRequest error: %s", err) } req.AddCookie(&http.Cookie{ Name: "elos-session-token", Value: sesh.Token, }) client.Do(req) if got, want := authed, true; got != want { t.Fatalf("authed: got %t, want %t", got, want) } authedU, ok := user.FromContext(userContext) if got, want := ok, true; got != want { t.Fatalf("_, ok := user.FromContext: got %t, want %t", got, want) } if got, want := data.Equivalent(authedU, u), true; got != want { t.Errorf("data.Equivalent(authedU, u): got %t, want %t", got, want) } }
// TestEventPOST tests a POST request to the '/event/' endpoint. // in the happy case (i.e., all parameters present). // We verify: // * The event's name // * The event's data // * The event's tags func TestEventPOST(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, ok := routes.Authenticate(ctx, w, r, logger, db) if !ok { t.Error("routes.Authenticate failed") } routes.EventPOST(ctx, w, r, db, logger) })) defer s.Close() _, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create(db, \"username\", \"password\") error: %s", err) } u := s.URL + "?" + url.Values{ "tags": []string{"tag1", "tag2"}, }.Encode() body := map[string]interface{}{ "name": "event name", "time": time.Now(), "data": map[string]interface{}{ "arbitrary": []string{"data"}, "here": 1.0, "foo": map[string]interface{}{ "bar": "there", }, }, } b, err := json.Marshal(body) if err != nil { t.Fatal("json.Marshal(body) error: %s", err) } req, err := http.NewRequest("POST", u, bytes.NewBuffer(b)) req.SetBasicAuth("username", "password") client := new(http.Client) resp, err := client.Do(req) if err != nil { t.Fatalf("client.Do(req) error: %s", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusCreated; got != want { t.Fatalf("resp.StatusCode: got %d, want %d", got, want) } b, err = ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("ioutil.ReadAll(resp.Body) error: %s", err) } e := new(models.Event) if err := json.Unmarshal(b, e); err != nil { t.Fatalf("json.Unmarshal(b, e) error: %s", err) } t.Logf("Event:\n\t%v", e) if got, want := e.Name, "event name"; got != want { t.Errorf("e.Name: got %q, want %q", got, want) } if got, want := e.Data["foo"].(map[string]interface{})["bar"], "there"; got != want { t.Errorf("e.Data[\"foo\"][\"bar\"]: got %q, want %q", got, want) } if got, want := len(e.TagsIds), 2; got != want { t.Fatalf("len(e.TagsIds): got %d, want %d", got, want) } tags, err := e.Tags(db) if err != nil { t.Fatalf("e.Tags(db) error: %s", err) } if got, want := len(tags), 2; got != want { t.Fatalf("len(tags): got %d, want %d", got, want) } if got, want := tags[0].Name, "tag1"; got != want { t.Errorf("tags[0].Name: got %q, want %q", got, want) } if got, want := tags[1].Name, "tag2"; got != want { t.Errorf("tags[1].Name: got %q, want %q", got, want) } }