package server_test import ( "bytes" "encoding/json" "os" "strconv" "strings" "testing" "github.com/keys-pub/keys" "github.com/keys-pub/keys-ext/firestore" "github.com/keys-pub/keys-ext/http/api" "github.com/keys-pub/keys/dstore" "github.com/keys-pub/keys/http" "github.com/keys-pub/keys/tsutil" "github.com/stretchr/testify/require" ) func TestVault(t *testing.T) { env := newEnv(t) // env.logLevel = server.DebugLevel // keys.SetLogger(keys.NewLogger(keys.DebugLevel)) alice := keys.NewEdX25519KeyFromSeed(keys.Bytes32(bytes.Repeat([]byte{0x01}, 32))) testVault(t, env, alice) } func TestVaultFirestore(t *testing.T) { if os.Getenv("TEST_FIRESTORE") != "1" { t.Skip() } // firestore.SetContextLogger(firestore.NewContextLogger(firestore.DebugLevel)) env := newEnvWithFire(t, testFirestore(t), tsutil.NewTestClock()) // env.logLevel = server.DebugLevel key := keys.GenerateEdX25519Key() testVault(t, env, key) } func testVault(t *testing.T, env *env, alice *keys.EdX25519Key) { srv := newTestServer(t, env) clock := env.clock rand := keys.GenerateEdX25519Key() // GET /vault/:kid (not found) req, err := http.NewAuthRequest("GET", dstore.Path("vault", alice.ID()), nil, "", clock.Now(), alice) require.NoError(t, err) code, _, body := srv.Serve(req) require.Equal(t, http.StatusNotFound, code) require.Equal(t, `{"error":{"code":404,"message":"vault not found"}}`, body) // HEAD /vault/:kid (not found) req, err = http.NewAuthRequest("HEAD", dstore.Path("vault", alice.ID()), nil, "", clock.Now(), alice) require.NoError(t, err) code, _, _ = srv.Serve(req) require.Equal(t, http.StatusNotFound, code) // POST /vault/:kid vault := []*api.Data{ &api.Data{Data: []byte("test1")}, } data, err := json.Marshal(vault) require.NoError(t, err) contentHash := http.ContentHash(data) req, err = http.NewAuthRequest("POST", dstore.Path("vault", alice.ID()), bytes.NewReader(data), contentHash, clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusOK, code) require.Equal(t, `{}`, body) // GET /vault/:kid req, err = http.NewAuthRequest("GET", dstore.Path("vault", alice.ID()), nil, "", clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusOK, code) var resp api.VaultResponse err = json.Unmarshal([]byte(body), &resp) require.NoError(t, err) require.Equal(t, int64(1), resp.Index) require.Equal(t, 1, len(resp.Vault)) require.Equal(t, []byte("test1"), resp.Vault[0].Data) // HEAD /vault/:kid req, err = http.NewAuthRequest("HEAD", dstore.Path("vault", alice.ID()), nil, "", clock.Now(), alice) require.NoError(t, err) code, _, _ = srv.Serve(req) require.Equal(t, http.StatusOK, code) // GET /vault/:kid?idx=next req, err = http.NewAuthRequest("GET", dstore.Path("vault", alice.ID())+"?idx="+strconv.Itoa(int(resp.Index)), nil, "", clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusOK, code) var resp2 api.VaultResponse err = json.Unmarshal([]byte(body), &resp2) require.NoError(t, err) require.Equal(t, 0, len(resp2.Vault)) require.Equal(t, resp.Index, resp2.Index) // POST /vault/:kid vault2 := []*api.Data{ &api.Data{Data: []byte("test2")}, &api.Data{Data: []byte("test3")}, } data2, err := json.Marshal(vault2) require.NoError(t, err) contentHash2 := http.ContentHash(data2) req, err = http.NewAuthRequest("POST", dstore.Path("vault", alice.ID()), bytes.NewReader(data2), contentHash2, clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusOK, code) require.Equal(t, `{}`, body) // GET /vault/:kid?idx=next req, err = http.NewAuthRequest("GET", dstore.Path("vault", alice.ID())+"?idx="+strconv.Itoa(int(resp.Index)), nil, "", clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusOK, code) var resp3 api.VaultResponse err = json.Unmarshal([]byte(body), &resp3) require.NoError(t, err) require.Equal(t, 2, len(resp3.Vault)) require.Equal(t, []byte("test2"), resp3.Vault[0].Data) require.Equal(t, []byte("test3"), resp3.Vault[1].Data) // POST /vault/:kid vault3 := []*api.Data{ &api.Data{Data: []byte("test4")}, &api.Data{Data: []byte("test5")}, &api.Data{Data: []byte("test6")}, &api.Data{Data: []byte("test7")}, &api.Data{Data: []byte("test8")}, &api.Data{Data: []byte("test9")}, } data3, err := json.Marshal(vault3) require.NoError(t, err) contentHash3 := http.ContentHash(data3) req, err = http.NewAuthRequest("POST", dstore.Path("vault", alice.ID()), bytes.NewReader(data3), contentHash3, clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusOK, code) require.Equal(t, `{}`, body) // GET /vault/:kid?idx=next req, err = http.NewAuthRequest("GET", dstore.Path("vault", alice.ID())+"?idx="+strconv.Itoa(int(resp3.Index)), nil, "", clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusOK, code) var resp4 api.VaultResponse err = json.Unmarshal([]byte(body), &resp4) require.NoError(t, err) require.Equal(t, 6, len(resp4.Vault)) require.Equal(t, []byte("test4"), resp4.Vault[0].Data) require.Equal(t, []byte("test5"), resp4.Vault[1].Data) require.Equal(t, []byte("test6"), resp4.Vault[2].Data) require.Equal(t, []byte("test7"), resp4.Vault[3].Data) require.Equal(t, []byte("test8"), resp4.Vault[4].Data) require.Equal(t, []byte("test9"), resp4.Vault[5].Data) // DEL (invalid auth) req, err = http.NewAuthRequest("DELETE", dstore.Path("vault", alice.ID()), nil, "", env.clock.Now(), rand) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, `{"error":{"code":403,"message":"auth failed"}}`, body) require.Equal(t, http.StatusForbidden, code) // DEL /vault/:kid req, err = http.NewAuthRequest("DELETE", dstore.Path("vault", alice.ID()), nil, "", env.clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusOK, code) require.Equal(t, `{}`, body) // DEL /vault/:kid (again) req, err = http.NewAuthRequest("DELETE", dstore.Path("vault", alice.ID()), nil, "", env.clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusNotFound, code) require.Equal(t, `{"error":{"code":404,"message":"vault was deleted"}}`, body) // GET /vault/:kid req, err = http.NewAuthRequest("GET", dstore.Path("vault", alice.ID()), nil, "", clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusNotFound, code) require.Equal(t, `{"error":{"code":404,"message":"vault was deleted"}}`, body) // HEAD /vault/:kid req, err = http.NewAuthRequest("HEAD", dstore.Path("vault", alice.ID()), nil, "", clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusNotFound, code) require.Equal(t, `{"error":{"code":404,"message":"vault was deleted"}}`, body) // POST /vault/:kid (deleted) vault = []*api.Data{ &api.Data{Data: []byte("testdeleted")}, } data, err = json.Marshal(vault) require.NoError(t, err) contentHash = http.ContentHash(data) req, err = http.NewAuthRequest("POST", dstore.Path("vault", alice.ID()), bytes.NewReader(data), contentHash, clock.Now(), alice) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusNotFound, code) require.Equal(t, `{"error":{"code":404,"message":"vault was deleted"}}`, body) } func TestVaultAuthFirestore(t *testing.T) { if os.Getenv("TEST_FIRESTORE") != "1" { t.Skip() } firestore.SetContextLogger(firestore.NewContextLogger(firestore.DebugLevel)) fs := testFirestore(t) clock := tsutil.NewTestClock() env := newEnvWithFire(t, fs, clock) // env.logLevel = server.DebugLevel alice := keys.GenerateEdX25519Key() testVaultAuth(t, env, alice) } func TestVaultAuth(t *testing.T) { env := newEnv(t) // env.logLevel = server.DebugLevel // keys.SetLogger(keys.NewLogger(keys.DebugLevel)) key := keys.NewEdX25519KeyFromSeed(testSeed(0x01)) testVaultAuth(t, env, key) } func testVaultAuth(t *testing.T, env *env, key *keys.EdX25519Key) { srv := newTestServer(t, env) clock := env.clock randKey := keys.GenerateEdX25519Key() // GET /vault/:kid (no auth) req, err := http.NewRequest("GET", dstore.Path("vault", key.ID()), nil) require.NoError(t, err) code, _, body := srv.Serve(req) require.Equal(t, http.StatusForbidden, code) require.Equal(t, `{"error":{"code":403,"message":"auth failed"}}`, body) // GET /vault/:kid (invalid key) req, err = http.NewAuthRequest("GET", dstore.Path("vault", key.ID()), nil, "", clock.Now(), randKey) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusForbidden, code) require.Equal(t, `{"error":{"code":403,"message":"auth failed"}}`, body) // POST /vault/:kid (invalid key) content := []byte(`[{"data":"dGVzdGluZzE="},{"data":"dGVzdGluZzI="}]`) contentHash := http.ContentHash(content) req, err = http.NewAuthRequest("POST", dstore.Path("vault", key.ID()), bytes.NewReader(content), contentHash, clock.Now(), randKey) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusForbidden, code) require.Equal(t, `{"error":{"code":403,"message":"auth failed"}}`, body) // GET /vault/:kid req, err = http.NewAuthRequest("GET", dstore.Path("vault", key.ID()), nil, "", clock.Now(), key) require.NoError(t, err) code, _, body = srv.Serve(req) require.Equal(t, http.StatusNotFound, code) require.Equal(t, `{"error":{"code":404,"message":"vault not found"}}`, body) // Replay last request reqReplay, err := http.NewRequest("GET", req.URL.String(), nil) reqReplay.Header.Set("Authorization", req.Header.Get("Authorization")) require.NoError(t, err) code, _, body = srv.Serve(reqReplay) require.Equal(t, http.StatusForbidden, code) require.Equal(t, `{"error":{"code":403,"message":"auth failed"}}`, body) // GET /vault/:kid (invalid authorization) authHeader := req.Header.Get("Authorization") sig := strings.Split(authHeader, ":")[1] req, err = http.NewAuthRequest("GET", dstore.Path("vault", key.ID()), nil, "", clock.Now(), randKey) require.NoError(t, err) req.Header.Set("Authorization", randKey.ID().String()+":"+sig) code, _, body = srv.Serve(req) require.Equal(t, http.StatusForbidden, code) require.Equal(t, `{"error":{"code":403,"message":"auth failed"}}`, body) }