コード例 #1
0
ファイル: record.go プロジェクト: elos/gaia
// Authenticate checks a request's basic authentication, and associates a *models.User with the context
// if so. Otherwise it handles responding to and closing the request.
//
//		contextWithUser, authWasSuccessful := routes.Authenticate(ctx, w, r, logger, db)
//
// Use for any requests which expect to act on behalf of a user (which is most)
func Authenticate(ctx context.Context, w http.ResponseWriter, r *http.Request, l services.Logger, db services.DB) (context.Context, bool) {
	l = l.WithPrefix("routes.Authenticate: ")
	var (
		c   *models.Credential
		u   *models.User
		err error
	)

	public, private, ok := r.BasicAuth()
	if !ok {
		l.Printf("authentication reverting from basic auth to session")
		// assume std lib didn't make a mistake, and the BasicAuth simply wasn't given
		// fall back to cookie

		if sesh, err := session(r, db); err != nil {
			switch err {
			case http.ErrNoCookie:
				l.Printf("no session cookie")
			case data.ErrNotFound:
				l.Printf("session token not found")
			default:
				l.Printf("session(r, db) error: %s", err)
			}

			l.Printf("authentication reverting from cookie to form values")
			public, private = r.FormValue(publicParam), r.FormValue(privateParam)
		} else if sesh.Valid() {
			if u, err := sesh.Owner(db); err != nil {
				l.Printf("sesh.Owner(db) error: %s", err)
				public, private = r.FormValue(publicParam), r.FormValue(privateParam)
			} else {
				return user.NewContext(ctx, u), true
			}
		} else {
			l.Printf("session no longer valid")
			public, private = r.FormValue(publicParam), r.FormValue(privateParam)
		}
	}

	if c, err = access.Authenticate(db, public, private); err != nil {
		l.Printf("authentication of (%s, %s) failed: couldn't find credential: %s", public, private, err)
		goto unauthorized // this error is on us, but it manifests as a failure to authenticate
	}

	if u, err = c.Owner(db); err != nil {
		l.Printf("authentication failed: couldn't load user's owner: %s", err)
		goto unauthorized // this error is also on us, but also manifests as a failure to authenticate
	}

	// successful authentications
	return user.NewContext(ctx, u), true

	// rejection
unauthorized:
	http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
	return nil, false
}
コード例 #2
0
ファイル: delete_test.go プロジェクト: elos/gaia
func TestDeletePOST(t *testing.T) {
	db := mem.NewDB()

	u, _, err := user.Create(db, "username", "password")
	if err != nil {
		t.Fatalf("user.Create error: %v", err)
	}

	e := &models.Event{
		Id:      db.NewID().String(),
		OwnerId: u.Id,
		Name:    "event name",
		Data: map[string]interface{}{
			"sensor": 45.3,
		},
	}

	if err := db.Save(e); err != nil {
		t.Fatalf("db.Save error: %v", err)
	}

	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.Contains(r.RequestURI, "/records/delete/") {
			records.DeletePOST(user.NewContext(context.Background(), u), w, r, db, services.NewTestLogger(t))
		}

		records.QueryGET(user.NewContext(context.Background(), u), w, r, db, services.NewTestLogger(t))
	}))

	p := s.URL + "/records/delete/?" + url.Values{
		"kind": []string{"event"},
		"id":   []string{e.Id},
	}.Encode()

	resp, err := http.Post(p, "", new(bytes.Buffer))
	if err != nil {
		t.Fatalf("http.Post error: %v", err)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("ioutil.ReadAll error: %v", err)
	}
	t.Logf("resp.Body:\n%s", body)

	if got, want := db.PopulateByID(e), data.ErrNotFound; got != want {
		t.Fatalf("db.PopulateByID: got %v, want %v", got, want)
	}
}
コード例 #3
0
ファイル: command.go プロジェクト: elos/gaia
func ContextualizeCommandWebGET(ctx context.Context, db data.DB, logger services.Logger) websocket.Handler {
	return func(c *websocket.Conn) {

		if err := c.Request().ParseForm(); err != nil {
			logger.Print("Failure parsing form")
			return
		}

		public := c.Request().Form.Get("public")
		private := c.Request().Form.Get("private")

		if public == "" || private == "" {
			logger.Print("failed to retrieve credentials")
			return
		}

		cred, err := access.Authenticate(db, public, private)
		if err != nil {
			logger.Print("failed to auth")
			return
		}

		u, _ := cred.Owner(db)
		CommandWebGET(user.NewContext(ctx, u), c, logger, db)
	}
}
コード例 #4
0
ファイル: record_test.go プロジェクト: elos/gaia
func BenchmarkRecordPostEvent(b *testing.B) {
	db := mem.NewDB()
	u, _, err := user.Create(db, "username", "password")
	if err != nil {
		b.Fatalf("user.Create error: %v", err)
	}

	ctx := user.NewContext(context.Background(), u)

	logger := services.NewTestLogger(b)

	for i := 0; i < b.N; i++ {
		rec := httptest.NewRecorder()
		req, err := http.NewRequest(
			"POST",
			"http://www.elos.pw/record/?"+url.Values{
				"kind": []string{models.EventKind.String()},
			}.Encode(),
			bytes.NewBuffer(
				[]byte(`{
				"name": "event name",
				"data": {
					"sensor1": 34,
					"sensor2": 4.3
				},
				"owner_id": "`+u.ID().String()+`"
			}`),
			))
		if err != nil {
			b.Fatalf("http.NewRequest error: %v", err)
		}
		routes.RecordPOST(ctx, rec, req, logger, db)
	}
}
コード例 #5
0
ファイル: record.go プロジェクト: elos/gaia
func ContextualizeRecordChangesGET(ctx context.Context, db data.DB, logger services.Logger) websocket.Handler {
	return func(ws *websocket.Conn) {
		defer ws.Close()

		l := logger.WithPrefix("ContextualizeRecordChangesGET: ")

		if err := ws.Request().ParseForm(); err != nil {
			l.Printf("error parsing form: %s", err)
			return
		}

		public := ws.Request().Form.Get(publicParam)
		private := ws.Request().Form.Get(privateParam)

		if public == "" || private == "" {
			l.Print("failed to retrieve credentials")
			return
		}

		cred, err := access.Authenticate(db, public, private)
		if err != nil {
			l.Print("failed to authenticate")
			return
		}

		if u, err := cred.Owner(db); err != nil {
			l.Print("error retrieving user: %s", err)
		} else {
			RecordChangesGET(user.NewContext(ctx, u), ws, db, logger)
		}
	}
}
コード例 #6
0
ファイル: view_test.go プロジェクト: elos/gaia
func TestViewGET(t *testing.T) {
	db := mem.WithData(map[data.Kind][]data.Record{
		models.UserKind: {
			&models.User{
				Id:             "1",
				CredentialsIds: []string{"2"},
			},
		},
		models.CredentialKind: {
			&models.Credential{
				Id:      "2",
				OwnerId: "1",
				Spec:    "password",
				Public:  "public",
				Private: "private",
			},
		},
	})

	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		records.ViewGET(user.NewContext(context.Background(), &models.User{Id: "1"}), w, r, db, services.NewTestLogger(t))
	}))

	p := s.URL + "?" + url.Values{
		"kind": []string{"credential"},
		"id":   []string{"2"},
	}.Encode()

	resp, err := http.Get(p)
	if err != nil {
		t.Fatalf("http.Get(%q) error: %v", err)
	}

	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("ioutil.ReadAll error: %v", err)
	}
	body := string(b)
	t.Logf("resp.Body:\n%s", body)

	contents := map[string]bool{
		"credential": true,
		"public":     true,
		"password":   true,
		"owner":      true,
		"/records/edit/?kind=credential&id=2": true,

		"user": false,
	}

	for content, want := range contents {
		if got := strings.Contains(body, content); got != want {
			t.Fatalf("strings.Contains(body, %q): got %t, want %t", content, got, want)
		}
	}
}
コード例 #7
0
ファイル: edit_test.go プロジェクト: elos/gaia
func TestEditGET(t *testing.T) {
	db := mem.NewDB()
	u, _, err := user.Create(db, "username", "password")
	if err != nil {
		t.Fatalf("user.Create error: %v", err)
	}

	e := &models.Event{
		Id:      db.NewID().String(),
		OwnerId: u.Id,
		Name:    "old name",
		Data: map[string]interface{}{
			"sensor": 4,
		},
	}

	if err := db.Save(e); err != nil {
		t.Fatalf("db.Save error: %v", err)
	}

	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		records.EditGET(user.NewContext(context.Background(), u), w, r, db, services.NewTestLogger(t))
	}))

	p := s.URL + "?" + url.Values{
		"kind": []string{"event"},
		"id":   []string{e.Id},
	}.Encode()

	resp, err := http.Get(p)
	if err != nil {
		t.Fatalf("http.Get(%q) error: %v", p, err)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("ioutil.ReadAll error: %v", err)
	}
	t.Logf("resp.Body:\n%s", body)

	if got, want := bytes.Contains(body, []byte(`event`)), true; got != want {
		t.Fatalf("bytes.Contains(body, %q): got %t, want %t", "event", got, want)
	}

	if got, want := bytes.Contains(body, []byte(`old name`)), true; got != want {
		t.Fatalf("bytes.Contains(body, %q): got %t, want %t", "old name", got, want)
	}

	if got, want := bytes.Contains(body, []byte(`/records/view/?kind=event&id=3`)), true; got != want {
		t.Fatalf("bytes.Contains(body, %q): got %t, want %t", `/records/view/?kind=event&id=3`, got, want)
	}
}
コード例 #8
0
ファイル: edit_test.go プロジェクト: elos/gaia
func TestEditPOST(t *testing.T) {
	db := mem.NewDB()
	u, _, err := user.Create(db, "username", "password")
	if err != nil {
		t.Fatalf("user.Create error: %v", err)
	}

	e := &models.Event{
		Id:      db.NewID().String(),
		OwnerId: u.Id,
		Name:    "old name",
		Data: map[string]interface{}{
			"sensor": 4,
		},
		Time: time.Now(),
	}

	if err := db.Save(e); err != nil {
		t.Fatalf("db.Save error: %v", err)
	}

	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.Contains(r.RequestURI, "/records/edit/") {
			records.EditPOST(user.NewContext(context.Background(), u), w, r, db, services.NewTestLogger(t))
			return
		}

		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`default test handler`))
	}))

	p := s.URL + "/records/edit/?" + url.Values{
		"kind":       []string{"event"},
		"id":         []string{e.Id},
		"event/Name": []string{"new eman"},
		"event/Data": []string{`{"sensor": 4, "sensor2": 9}`},
	}.Encode()

	resp, err := http.Post(p, "", new(bytes.Buffer))
	if err != nil {
		t.Fatalf("http.Post error: %v", err)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("iotuil.ReadAll error: %v", err)
	}
	t.Logf("resp.Body:\n%s", body)

	ue := &models.Event{Id: e.Id}
	if err := db.PopulateByID(ue); err != nil {
		t.Fatalf("db.PopulateByID error: %v", err)
	}

	if got, want := ue.Name, "new eman"; got != want {
		t.Errorf("ue.Name: got %q, want %q", got, want)
	}

	if got, want := ue.Data, map[string]interface{}{"sensor": 4.0, "sensor2": 9.0}; !reflect.DeepEqual(got, want) {
		t.Errorf("ue.Data: got %v, want %v", got, want)
	}

	if got, want := ue.Time, e.Time; got != want {
		t.Errorf("ue.Time: got %v, want %v", got, want)
	}

	if got, want := ue.OwnerId, e.OwnerId; got != want {
		t.Errorf("ue.OwnerId: got %q, want %q", got, want)
	}
}
コード例 #9
0
ファイル: create_test.go プロジェクト: elos/gaia
func TestCreatePOST(t *testing.T) {
	adb, dbc, ac, closers, err := records_test.ClientsFromState(data.State{
		models.Kind_USER: []*data.Record{
			*data.Record{
				Kind: models.Kind_USER,
				User: &models.User{
					Id: "1",
				},
			},
		},
		models.Kind_CREDENTIAL: []*data.Record{
			&data.Record{
				Kind: models.Kind_CREDENTIAL,
				Credential: &models.Credential{
					Id:      "2",
					OwnerId: "1",
					Type:    models.Credential_PASSWORD,
					Public:  "username",
					Private: "password",
				},
			},
		},
	})
	defer func() {
		records_test.CloseAll(closers)
	}()
	wui := records.NewWebUI(adb, ac)

	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		records.CreatePOST(user.NewContext(context.Background(), u), w, r, db, services.NewTestLogger(t), webuiclient)
	}))

	p := s.URL + "?" + url.Values{
		"kind":             []string{"EVENT"},
		"EVENT/OwnerId":    []string{"1"},
		"EVENT/Name":       []string{"event name"},
		"EVENT/Quantities": []string{`[{"name": "sensor", "value": 45}]`},
	}.Encode()

	resp, err := http.Post(p, "", new(bytes.Buffer))
	if err != nil {
		t.Fatalf("http.Post error: %v", err)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("ioutil.ReadAll error: %v", err)
	}
	t.Logf("resp.Body:\n%s", body)

	if err := data.CompareState(dbc, data.State{
		models.Kind_USER: []*data.Record{
			*data.Record{
				Kind: models.Kind_USER,
				User: &models.User{
					Id: "1",
				},
			},
		},
		models.Kind_CREDENTIAL: []*data.Record{
			&data.Record{
				Kind: models.Kind_CREDENTIAL,
				Credential: &models.Credential{
					Id:      "2",
					OwnerId: "1",
					Type:    models.Credential_PASSWORD,
					Public:  "username",
					Private: "password",
				},
			},
		},
		models.Kind_EVENT: []*data.Record{
			&data.Record{
				Kind: models.Kind_EVENT,
				Event: &models.Event{
					Id:      "3",
					OwnerId: "1",
					Name:    "event name",
					Quantities: []*Quantity{
						Name:  "sensor",
						Value: 45,
					},
				},
			},
		},
	}); err != nil {
		t.Errorf("data.CompareState error: %v", err)
	}
}
コード例 #10
0
ファイル: query_test.go プロジェクト: elos/gaia
func TestQueryGET(t *testing.T) {
	db := mem.WithData(map[data.Kind][]data.Record{
		models.UserKind: []data.Record{
			&models.User{
				Id:             "1",
				CredentialsIds: []string{"2"},
			},
			&models.User{
				Id: "2",
			},
		},
		models.CredentialKind: []data.Record{
			&models.Credential{
				Id:        "3",
				CreatedAt: time.Now(),
				Spec:      "password",
				Public:    "username",
				Private:   "password",
			},
		},
		models.EventKind: []data.Record{
			&models.Event{
				Id:      "4",
				OwnerId: "1",
				Name:    "event 1",
			},
			&models.Event{
				Id:      "5",
				OwnerId: "1",
				Name:    "event 2",
			},
			&models.Event{
				Id:      "6",
				OwnerId: "1",
				Name:    "event 3",
			},
			&models.Event{
				Id:      "7",
				OwnerId: "1",
				Name:    "generic event",
			},
			&models.Event{
				Id:      "8",
				OwnerId: "1",
				Name:    "generic event",
			},

			// Not owned by User 1.
			&models.Event{
				Id:      "9",
				OwnerId: "2",
				Name:    "not owned",
			},
		},
	})

	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		records.QueryGET(user.NewContext(context.Background(), &models.User{Id: "1"}), w, r, db, services.NewTestLogger(t))
	}))

	p := s.URL + "?" + url.Values{
		"query/Kind": []string{"event"},
	}.Encode()

	resp, err := http.Get(p)
	if err != nil {
		t.Fatalf("http.Get error: %v", err)
	}

	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	body := string(b)
	if err != nil {
		t.Fatalf("ioutil.ReadAll error: %v", err)
	}
	t.Logf("resp.Body:\n%s", body)

	contents := map[string]bool{
		"Query":         true,
		"5 results.":    true,
		"event 1":       true,
		"event 2":       true,
		"event 3":       true,
		"generic event": true,
		"Edit":          true,
		"/records/edit/?kind=event&id=8": true,
		"/records/edit/?kind=event&id=9": false,
		"not owned":                      false,
	}

	for content, want := range contents {
		if got := strings.Contains(body, content); got != want {
			t.Errorf("bytes.Contains(body, %q): got %t, want %t", content, got, want)
		}
	}
}