1package approle 2 3import ( 4 "context" 5 "fmt" 6 "sync" 7 "sync/atomic" 8 "testing" 9 "time" 10 11 "github.com/hashicorp/vault/sdk/logical" 12) 13 14func TestAppRole_TidyDanglingAccessors_Normal(t *testing.T) { 15 var resp *logical.Response 16 var err error 17 b, storage := createBackendWithStorage(t) 18 19 // Create a role 20 createRole(t, b, storage, "role1", "a,b,c") 21 22 // Create a secret-id 23 roleSecretIDReq := &logical.Request{ 24 Operation: logical.UpdateOperation, 25 Path: "role/role1/secret-id", 26 Storage: storage, 27 } 28 resp, err = b.HandleRequest(context.Background(), roleSecretIDReq) 29 if err != nil || (resp != nil && resp.IsError()) { 30 t.Fatalf("err:%v resp:%#v", err, resp) 31 } 32 33 accessorHashes, err := storage.List(context.Background(), "accessor/") 34 if err != nil { 35 t.Fatal(err) 36 } 37 if len(accessorHashes) != 1 { 38 t.Fatalf("bad: len(accessorHashes); expect 1, got %d", len(accessorHashes)) 39 } 40 41 entry1, err := logical.StorageEntryJSON( 42 "accessor/invalid1", 43 &secretIDAccessorStorageEntry{ 44 SecretIDHMAC: "samplesecretidhmac", 45 }, 46 ) 47 if err != nil { 48 t.Fatal(err) 49 } 50 51 if err := storage.Put(context.Background(), entry1); err != nil { 52 t.Fatal(err) 53 } 54 55 entry2, err := logical.StorageEntryJSON( 56 "accessor/invalid2", 57 &secretIDAccessorStorageEntry{ 58 SecretIDHMAC: "samplesecretidhmac2", 59 }, 60 ) 61 if err != nil { 62 t.Fatal(err) 63 } 64 if err := storage.Put(context.Background(), entry2); err != nil { 65 t.Fatal(err) 66 } 67 68 accessorHashes, err = storage.List(context.Background(), "accessor/") 69 if err != nil { 70 t.Fatal(err) 71 } 72 if len(accessorHashes) != 3 { 73 t.Fatalf("bad: len(accessorHashes); expect 3, got %d", len(accessorHashes)) 74 } 75 76 _, err = b.tidySecretID(context.Background(), &logical.Request{ 77 Storage: storage, 78 }) 79 if err != nil { 80 t.Fatal(err) 81 } 82 83 // It runs async so we give it a bit of time to run 84 time.Sleep(10 * time.Second) 85 86 accessorHashes, err = storage.List(context.Background(), "accessor/") 87 if err != nil { 88 t.Fatal(err) 89 } 90 if len(accessorHashes) != 1 { 91 t.Fatalf("bad: len(accessorHashes); expect 1, got %d", len(accessorHashes)) 92 } 93} 94 95func TestAppRole_TidyDanglingAccessors_RaceTest(t *testing.T) { 96 var resp *logical.Response 97 var err error 98 b, storage := createBackendWithStorage(t) 99 100 // Create a role 101 createRole(t, b, storage, "role1", "a,b,c") 102 103 // Create an initial entry 104 roleSecretIDReq := &logical.Request{ 105 Operation: logical.UpdateOperation, 106 Path: "role/role1/secret-id", 107 Storage: storage, 108 } 109 resp, err = b.HandleRequest(context.Background(), roleSecretIDReq) 110 if err != nil || (resp != nil && resp.IsError()) { 111 t.Fatalf("err:%v resp:%#v", err, resp) 112 } 113 count := 1 114 115 wg := &sync.WaitGroup{} 116 start := time.Now() 117 for time.Now().Sub(start) < 10*time.Second { 118 if time.Now().Sub(start) > 100*time.Millisecond && atomic.LoadUint32(b.tidySecretIDCASGuard) == 0 { 119 _, err = b.tidySecretID(context.Background(), &logical.Request{ 120 Storage: storage, 121 }) 122 if err != nil { 123 t.Fatal(err) 124 } 125 } 126 wg.Add(1) 127 go func() { 128 defer wg.Done() 129 roleSecretIDReq := &logical.Request{ 130 Operation: logical.UpdateOperation, 131 Path: "role/role1/secret-id", 132 Storage: storage, 133 } 134 resp, err := b.HandleRequest(context.Background(), roleSecretIDReq) 135 if err != nil || (resp != nil && resp.IsError()) { 136 t.Fatalf("err:%v resp:%#v", err, resp) 137 } 138 }() 139 140 entry, err := logical.StorageEntryJSON( 141 fmt.Sprintf("accessor/invalid%d", count), 142 &secretIDAccessorStorageEntry{ 143 SecretIDHMAC: "samplesecretidhmac", 144 }, 145 ) 146 if err != nil { 147 t.Fatal(err) 148 } 149 150 if err := storage.Put(context.Background(), entry); err != nil { 151 t.Fatal(err) 152 } 153 154 count++ 155 time.Sleep(100 * time.Microsecond) 156 } 157 158 logger := b.Logger().Named(t.Name()) 159 logger.Info("wrote entries", "count", count) 160 161 wg.Wait() 162 // Let tidy finish 163 for atomic.LoadUint32(b.tidySecretIDCASGuard) != 0 { 164 time.Sleep(100 * time.Millisecond) 165 } 166 167 logger.Info("running tidy again") 168 169 // Run tidy again 170 secret, err := b.tidySecretID(context.Background(), &logical.Request{ 171 Storage: storage, 172 }) 173 if err != nil || len(secret.Warnings) > 0 { 174 t.Fatal(err, secret.Warnings) 175 } 176 177 // Wait for tidy to start 178 for atomic.LoadUint32(b.tidySecretIDCASGuard) == 0 { 179 time.Sleep(100 * time.Millisecond) 180 } 181 182 // Let tidy finish 183 for atomic.LoadUint32(b.tidySecretIDCASGuard) != 0 { 184 time.Sleep(100 * time.Millisecond) 185 } 186 187 accessorHashes, err := storage.List(context.Background(), "accessor/") 188 if err != nil { 189 t.Fatal(err) 190 } 191 if len(accessorHashes) != count { 192 t.Fatalf("bad: len(accessorHashes); expect %d, got %d", count, len(accessorHashes)) 193 } 194 195 roleHMACs, err := storage.List(context.Background(), secretIDPrefix) 196 if err != nil { 197 t.Fatal(err) 198 } 199 secretIDs, err := storage.List(context.Background(), fmt.Sprintf("%s%s", secretIDPrefix, roleHMACs[0])) 200 if err != nil { 201 t.Fatal(err) 202 } 203 if len(secretIDs) != count { 204 t.Fatalf("bad: len(secretIDs); expect %d, got %d", count, len(secretIDs)) 205 } 206} 207