github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/auth/token/spec/token_store.go (about) 1 package spec 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 "time" 8 9 "github.com/google/go-cmp/cmp" 10 testkeys "github.com/qri-io/qri/auth/key/test" 11 "github.com/qri-io/qri/auth/token" 12 "github.com/qri-io/qri/profile" 13 ) 14 15 // AssertTokenStoreSpec ensures an token.TokenStore implementation behaves as 16 // expected 17 func AssertTokenStoreSpec(t *testing.T, newTokenStore func(context.Context) token.Store) { 18 prevTs := token.Timestamp 19 token.Timestamp = func() time.Time { return time.Time{} } 20 defer func() { token.Timestamp = prevTs }() 21 22 ctx, cancel := context.WithCancel(context.Background()) 23 defer cancel() 24 25 pk := testkeys.GetKeyData(0).PrivKey 26 tokens, err := token.NewPrivKeySource(pk) 27 if err != nil { 28 t.Fatalf("creating local tokens: %q", err) 29 } 30 store := newTokenStore(ctx) 31 32 results, err := store.ListTokens(ctx, 0, -1) 33 if err != nil { 34 t.Errorf("listing all tokens of an empty store shouldn't error. got: %q ", err) 35 } 36 if len(results) > 0 { 37 t.Errorf("new store should return no results. got: %d", len(results)) 38 } 39 40 _, err = store.RawToken(ctx, "this doesn't exist") 41 if !errors.Is(err, token.ErrTokenNotFound) { 42 t.Errorf("expected store.RawToken(nonexistent key) to return a wrap of token.ErrTokenNotFound. got: %q", err) 43 } 44 err = store.DeleteToken(ctx, "this also doesn't exist") 45 if !errors.Is(err, token.ErrTokenNotFound) { 46 t.Errorf("expected store.D key to return a wrap of token.ErrTokenNotFound. got: %q", err) 47 } 48 if err := store.PutToken(ctx, "_bad_key", "not.a.key"); err == nil { 49 t.Errorf("putting an invalid json web token should error. got nil") 50 } 51 52 p1 := &profile.Profile{ 53 ID: profile.IDB58DecodeOrEmpty(testkeys.GetKeyData(1).EncodedPeerID), 54 Peername: "local_user", 55 } 56 t1Raw, err := tokens.CreateToken(p1, 0) 57 if err != nil { 58 t.Fatalf("creating token: %q", err) 59 } 60 61 if err := store.PutToken(ctx, "_root", t1Raw); err != nil { 62 t.Errorf("putting root key shouldn't error. got: %q", err) 63 } 64 65 results, err = store.ListTokens(ctx, 0, -1) 66 if err != nil { 67 t.Errorf("listing all tokens of an empty store shouldn't error. got: %q ", err) 68 } 69 if len(results) != 1 { 70 t.Errorf("result length mismatch listing keys after adding `root` key. expected 1, got: %d", len(results)) 71 } 72 73 p2 := &profile.Profile{ 74 ID: profile.IDB58DecodeOrEmpty(testkeys.GetKeyData(2).EncodedPeerID), 75 Peername: "user_2", 76 } 77 t2Raw, err := tokens.CreateToken(p2, time.Millisecond*10) 78 if err != nil { 79 t.Fatalf("creating token: %q", err) 80 } 81 82 secondKey := "http://registry.qri.cloud" 83 if err := store.PutToken(ctx, secondKey, t2Raw); err != nil { 84 t.Errorf("putting a second token with key=%q shouldn't error. got: %q", secondKey, err) 85 } 86 87 results, err = store.ListTokens(ctx, 0, -1) 88 if err != nil { 89 t.Errorf("listing all tokens of an empty store shouldn't error. got: %q ", err) 90 } 91 if len(results) != 2 { 92 t.Errorf("result length mismatch listing keys after adding second key. expected 2, got: %d", len(results)) 93 } 94 95 expect := []token.RawToken{ 96 { 97 Key: "_root", 98 Raw: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJRbVdZZ0Q0OXI5SG51WEVwcFFFcTFhN1NVVXJ5amE0UU5zOUU2WENIMlBheUNEIiwic3ViIjoiUW1XWWdENDlyOUhudVhFcHBRRXExYTdTVVVyeWphNFFOczlFNlhDSDJQYXlDRCIsImNsaWVudFR5cGUiOiJ1c2VyIn0.OatuPGP9SCYMevcOWP29bHe5R-vMLgmtVEsVjIp1VN8oF-qZDLxDrMFJpqBVRAMrv4BLtILS_6qQS4VhKVedEoCrXA7Wt78Tg0JQZTawmbQNi0Z280hiM7sJjoaqIBTeq00x6NPuFAQ8hBflslqzN0cjdSkt7wnYFGUG9p_9RLiiW2r7fv4VYxZ4fJX3G8rr3YWEzcZVkpgKHR95kPe77-oT69KPqAb4CxPmhoZ7nF69qX9-tPgtC0LqQSzYYtJkxgRTrVZhE7hndAxNJZM2z_Dkd4saiya4x8lNChfh7OkacbUFbjYRRp3v2Sh5JwUbBffl8axjz-NhbKo_kBa_HQ", 99 }, 100 { 101 Key: secondKey, 102 Raw: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOi02MjEzNTU5NjgwMCwiaXNzIjoiUW1QZUZUTkhjWkRyM1pGRWZGZmVweFM1UHFIQW1mQlJHUU5QSjM4OUN3aDFhcyIsInN1YiI6IlFtUGVGVE5IY1pEcjNaRkVmRmZlcHhTNVBxSEFtZkJSR1FOUEozODlDd2gxYXMiLCJjbGllbnRUeXBlIjoidXNlciJ9.YozNjhC9gAk6jzSz4d2v1sdtiNIxB_LuFNxFtQTaUQvwhlQAm-vRq2Eb-8R-Y7cz_akkqvxzF0MY-cFV_80V8XxY8_oqYgULxi-HM81LkMwUcx6XzVeO6OxHssLnhz-9yiphElQNdn6u61Z-YLbdbS9Tjr1sjTh4biyRkUKdOba8fKn8VVZ1spUVfV-jUgDk69hxjIs0BBmrElX-GlkW9NLhuAvZzPqYVI6t7l9m2rvJedu-S9ZdMhEMzI_ZhbWwtmcLO_U_NPRJxjYujo5Uq4gr_UYEvEH9rvEw4L9Pcic2dPSnHJuOyrZa76KDKJj5xClZr9lKXeNV8YOypGu8sQ", 103 }, 104 } 105 106 if diff := cmp.Diff(expect, results); diff != "" { 107 t.Log(results) 108 t.Errorf("mistmatched list keys results. (-want +got):\n%s", diff) 109 } 110 111 results, err = store.ListTokens(ctx, 1, 1) 112 if err != nil { 113 t.Errorf("listing all tokens of an empty store shouldn't error. got: %q ", err) 114 } 115 if len(results) != 1 { 116 t.Errorf("result length mismatch listing keys after adding `root` key. expected 1, got: %d", len(results)) 117 } 118 119 if diff := cmp.Diff(expect[1:], results); diff != "" { 120 t.Errorf("mistmatched list keys with offset=1, limit=1. results. (-want +got):\n%s", diff) 121 } 122 123 if err := store.DeleteToken(ctx, secondKey); err != nil { 124 t.Errorf("store.DeleteToken shouldn't error for existing key. got: %q", err) 125 } 126 127 _, err = store.RawToken(ctx, secondKey) 128 if !errors.Is(err, token.ErrTokenNotFound) { 129 t.Errorf("store.RawToken() for a just-deleted key must return a wrap of token.ErrTokenNotFound. got: %q", err) 130 } 131 }