auth
2015-07-18
Parent:8ecb60d29b0d
auth/client_test.go
Send events when logins are verified. Add an ActionLoginVerified constant to use as the action when a login has been verified. On second thought, this should probably just be "verified", huh? Then we can reuse it across models. Oops. We also added a call to send a login verified event to NSQ when the login is verified.
| paddy@31 | 1 package auth |
| paddy@31 | 2 |
| paddy@31 | 3 import ( |
| paddy@113 | 4 "bytes" |
| paddy@116 | 5 "encoding/json" |
| paddy@39 | 6 "fmt" |
| paddy@139 | 7 "github.com/gorilla/mux" |
| paddy@113 | 8 "io/ioutil" |
| paddy@113 | 9 "net/http" |
| paddy@113 | 10 "net/http/httptest" |
| paddy@41 | 11 "net/url" |
| paddy@153 | 12 "os" |
| paddy@82 | 13 "sort" |
| paddy@113 | 14 "strings" |
| paddy@31 | 15 "testing" |
| paddy@41 | 16 "time" |
| paddy@31 | 17 |
| paddy@107 | 18 "code.secondbit.org/uuid.hg" |
| paddy@31 | 19 ) |
| paddy@31 | 20 |
| paddy@39 | 21 const ( |
| paddy@39 | 22 clientChangeSecret = 1 << iota |
| paddy@39 | 23 clientChangeOwnerID |
| paddy@39 | 24 clientChangeName |
| paddy@39 | 25 clientChangeLogo |
| paddy@39 | 26 clientChangeWebsite |
| paddy@39 | 27 ) |
| paddy@39 | 28 |
| paddy@151 | 29 func init() { |
| paddy@153 | 30 if os.Getenv("PG_TEST_DB") != "" { |
| paddy@153 | 31 p, err := NewPostgres(os.Getenv("PG_TEST_DB")) |
| paddy@153 | 32 if err != nil { |
| paddy@153 | 33 panic(err) |
| paddy@153 | 34 } |
| paddy@151 | 35 clientStores = append(clientStores, &p) |
| paddy@151 | 36 } |
| paddy@151 | 37 } |
| paddy@151 | 38 |
| paddy@57 | 39 var clientStores = []clientStore{NewMemstore()} |
| paddy@31 | 40 |
| paddy@33 | 41 func compareClients(client1, client2 Client) (success bool, field string, val1, val2 interface{}) { |
| paddy@33 | 42 if !client1.ID.Equal(client2.ID) { |
| paddy@33 | 43 return false, "ID", client1.ID, client2.ID |
| paddy@33 | 44 } |
| paddy@33 | 45 if client1.Secret != client2.Secret { |
| paddy@33 | 46 return false, "secret", client1.Secret, client2.Secret |
| paddy@33 | 47 } |
| paddy@33 | 48 if !client1.OwnerID.Equal(client2.OwnerID) { |
| paddy@33 | 49 return false, "owner ID", client1.OwnerID, client2.OwnerID |
| paddy@33 | 50 } |
| paddy@33 | 51 if client1.Name != client2.Name { |
| paddy@33 | 52 return false, "name", client1.Name, client2.Name |
| paddy@33 | 53 } |
| paddy@33 | 54 if client1.Logo != client2.Logo { |
| paddy@33 | 55 return false, "logo", client1.Logo, client2.Logo |
| paddy@33 | 56 } |
| paddy@33 | 57 if client1.Website != client2.Website { |
| paddy@33 | 58 return false, "website", client1.Website, client2.Website |
| paddy@33 | 59 } |
| paddy@41 | 60 if client1.Type != client2.Type { |
| paddy@41 | 61 return false, "type", client1.Type, client2.Type |
| paddy@41 | 62 } |
| paddy@41 | 63 return true, "", nil, nil |
| paddy@41 | 64 } |
| paddy@41 | 65 |
| paddy@41 | 66 func compareEndpoints(endpoint1, endpoint2 Endpoint) (success bool, field string, val1, val2 interface{}) { |
| paddy@41 | 67 if !endpoint1.ID.Equal(endpoint2.ID) { |
| paddy@41 | 68 return false, "ID", endpoint1.ID, endpoint2.ID |
| paddy@41 | 69 } |
| paddy@41 | 70 if !endpoint1.ClientID.Equal(endpoint2.ClientID) { |
| paddy@41 | 71 return false, "OwnerID", endpoint1.ClientID, endpoint2.ClientID |
| paddy@41 | 72 } |
| paddy@41 | 73 if !endpoint1.Added.Equal(endpoint2.Added) { |
| paddy@41 | 74 return false, "Added", endpoint1.Added, endpoint2.Added |
| paddy@41 | 75 } |
| paddy@116 | 76 if endpoint1.URI != endpoint2.URI { |
| paddy@41 | 77 return false, "URI", endpoint1.URI, endpoint2.URI |
| paddy@41 | 78 } |
| paddy@33 | 79 return true, "", nil, nil |
| paddy@33 | 80 } |
| paddy@33 | 81 |
| paddy@31 | 82 func TestClientStoreSuccess(t *testing.T) { |
| paddy@36 | 83 t.Parallel() |
| paddy@31 | 84 client := Client{ |
| paddy@41 | 85 ID: uuid.NewID(), |
| paddy@41 | 86 Secret: "secret", |
| paddy@41 | 87 OwnerID: uuid.NewID(), |
| paddy@41 | 88 Name: "name", |
| paddy@41 | 89 Logo: "logo", |
| paddy@41 | 90 Website: "website", |
| paddy@31 | 91 } |
| paddy@31 | 92 for _, store := range clientStores { |
| paddy@116 | 93 context := Context{clients: store} |
| paddy@116 | 94 err := context.SaveClient(client) |
| paddy@31 | 95 if err != nil { |
| paddy@41 | 96 t.Fatalf("Error saving client to %T: %s", store, err) |
| paddy@31 | 97 } |
| paddy@116 | 98 err = context.SaveClient(client) |
| paddy@33 | 99 if err != ErrClientAlreadyExists { |
| paddy@41 | 100 t.Fatalf("Expected ErrClientAlreadyExists, got %v from %T", err, store) |
| paddy@33 | 101 } |
| paddy@116 | 102 retrieved, err := context.GetClient(client.ID) |
| paddy@31 | 103 if err != nil { |
| paddy@41 | 104 t.Fatalf("Error retrieving client from %T: %s", store, err) |
| paddy@31 | 105 } |
| paddy@33 | 106 success, field, expectation, result := compareClients(client, retrieved) |
| paddy@33 | 107 if !success { |
| paddy@41 | 108 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result) |
| paddy@33 | 109 } |
| paddy@116 | 110 clients, err := context.ListClientsByOwner(client.OwnerID, 25, 0) |
| paddy@31 | 111 if err != nil { |
| paddy@41 | 112 t.Fatalf("Error retrieving clients by owner from %T: %s", store, err) |
| paddy@31 | 113 } |
| paddy@31 | 114 if len(clients) != 1 { |
| paddy@41 | 115 t.Fatalf("Expected 1 client in response from %T, got %+v", store, clients) |
| paddy@31 | 116 } |
| paddy@33 | 117 success, field, expectation, result = compareClients(client, clients[0]) |
| paddy@33 | 118 if !success { |
| paddy@41 | 119 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result) |
| paddy@33 | 120 } |
| paddy@151 | 121 deleted := true |
| paddy@151 | 122 err = context.UpdateClient(client.ID, ClientChange{Deleted: &deleted}) |
| paddy@31 | 123 if err != nil { |
| paddy@41 | 124 t.Fatalf("Error deleting client from %T: %s", store, err) |
| paddy@31 | 125 } |
| paddy@116 | 126 retrieved, err = context.GetClient(client.ID) |
| paddy@31 | 127 if err != ErrClientNotFound { |
| paddy@41 | 128 t.Fatalf("Expected ErrClientNotFound from %T, got %+v and %s", store, retrieved, err) |
| paddy@31 | 129 } |
| paddy@116 | 130 clients, err = context.ListClientsByOwner(client.OwnerID, 25, 0) |
| paddy@31 | 131 if err != nil { |
| paddy@41 | 132 t.Fatalf("Error listing clients by owner from %T: %s", store, err) |
| paddy@31 | 133 } |
| paddy@31 | 134 if len(clients) != 0 { |
| paddy@41 | 135 t.Fatalf("Expected 0 clients in response from %T, got %+v", store, clients) |
| paddy@41 | 136 } |
| paddy@41 | 137 } |
| paddy@41 | 138 } |
| paddy@41 | 139 |
| paddy@41 | 140 func TestEndpointStoreSuccess(t *testing.T) { |
| paddy@41 | 141 t.Parallel() |
| paddy@41 | 142 client := Client{ |
| paddy@41 | 143 ID: uuid.NewID(), |
| paddy@41 | 144 Secret: "secret", |
| paddy@41 | 145 OwnerID: uuid.NewID(), |
| paddy@41 | 146 Name: "name", |
| paddy@41 | 147 Logo: "logo", |
| paddy@41 | 148 Website: "website", |
| paddy@41 | 149 } |
| paddy@41 | 150 endpoint1 := Endpoint{ |
| paddy@41 | 151 ID: uuid.NewID(), |
| paddy@41 | 152 ClientID: client.ID, |
| paddy@149 | 153 Added: time.Now().Round(time.Millisecond), |
| paddy@116 | 154 URI: "https://www.example.com/", |
| paddy@41 | 155 } |
| paddy@41 | 156 endpoint2 := Endpoint{ |
| paddy@41 | 157 ID: uuid.NewID(), |
| paddy@41 | 158 ClientID: client.ID, |
| paddy@149 | 159 Added: time.Now().Round(time.Millisecond), |
| paddy@116 | 160 URI: "https://www.example.com/my/full/path", |
| paddy@41 | 161 } |
| paddy@41 | 162 for _, store := range clientStores { |
| paddy@116 | 163 context := Context{clients: store} |
| paddy@116 | 164 err := context.SaveClient(client) |
| paddy@41 | 165 if err != nil { |
| paddy@41 | 166 t.Fatalf("Error saving client to %T: %s", store, err) |
| paddy@41 | 167 } |
| paddy@151 | 168 err = context.AddEndpoints([]Endpoint{endpoint1}) |
| paddy@41 | 169 if err != nil { |
| paddy@41 | 170 t.Fatalf("Error adding endpoint to client in %T: %s", store, err) |
| paddy@41 | 171 } |
| paddy@116 | 172 endpoints, err := context.ListEndpoints(client.ID, 10, 0) |
| paddy@41 | 173 if err != nil { |
| paddy@41 | 174 t.Fatalf("Error retrieving endpoints from %T: %s", store, err) |
| paddy@41 | 175 } |
| paddy@41 | 176 if len(endpoints) != 1 { |
| paddy@41 | 177 t.Fatalf("Expected %d endpoints, got %+v from %T", 1, endpoints, store) |
| paddy@41 | 178 } |
| paddy@41 | 179 success, field, expectation, result := compareEndpoints(endpoint1, endpoints[0]) |
| paddy@41 | 180 if !success { |
| paddy@41 | 181 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result) |
| paddy@41 | 182 } |
| paddy@151 | 183 err = context.AddEndpoints([]Endpoint{endpoint2}) |
| paddy@41 | 184 if err != nil { |
| paddy@41 | 185 t.Fatalf("Error adding endpoint to client in %T: %s", store, err) |
| paddy@41 | 186 } |
| paddy@116 | 187 endpoints, err = context.ListEndpoints(client.ID, 10, 0) |
| paddy@41 | 188 if err != nil { |
| paddy@41 | 189 t.Fatalf("Error retrieving endpoints from %T: %s", store, err) |
| paddy@41 | 190 } |
| paddy@41 | 191 if len(endpoints) != 2 { |
| paddy@41 | 192 t.Fatalf("Expected %d endpoints, got %+v from %T", 2, endpoints, store) |
| paddy@41 | 193 } |
| paddy@41 | 194 sortedEnd := sortedEndpoints(endpoints) |
| paddy@41 | 195 sort.Sort(sortedEnd) |
| paddy@41 | 196 endpoints = []Endpoint(sortedEnd) |
| paddy@41 | 197 success, field, expectation, result = compareEndpoints(endpoint1, endpoints[0]) |
| paddy@41 | 198 if !success { |
| paddy@41 | 199 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result) |
| paddy@41 | 200 } |
| paddy@41 | 201 success, field, expectation, result = compareEndpoints(endpoint2, endpoints[1]) |
| paddy@41 | 202 if !success { |
| paddy@41 | 203 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result) |
| paddy@41 | 204 } |
| paddy@116 | 205 err = context.RemoveEndpoint(client.ID, endpoint1.ID) |
| paddy@41 | 206 if err != nil { |
| paddy@41 | 207 t.Fatalf("Error removing endpoint from client in %T: %s", store, err) |
| paddy@41 | 208 } |
| paddy@116 | 209 endpoints, err = context.ListEndpoints(client.ID, 10, 0) |
| paddy@41 | 210 if err != nil { |
| paddy@41 | 211 t.Fatalf("Error listing endpoints in %T: %s", store, err) |
| paddy@41 | 212 } |
| paddy@41 | 213 if len(endpoints) != 1 { |
| paddy@41 | 214 t.Fatalf("Expected %d endpoints, got %+v from %T", 1, endpoints, store) |
| paddy@41 | 215 } |
| paddy@41 | 216 success, field, expectation, result = compareEndpoints(endpoint2, endpoints[0]) |
| paddy@41 | 217 if !success { |
| paddy@41 | 218 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result) |
| paddy@41 | 219 } |
| paddy@116 | 220 err = context.RemoveEndpoint(client.ID, endpoint2.ID) |
| paddy@41 | 221 if err != nil { |
| paddy@41 | 222 t.Fatalf("Error removing endpoint from client in %T: %s", store, err) |
| paddy@41 | 223 } |
| paddy@116 | 224 endpoints, err = context.ListEndpoints(client.ID, 10, 0) |
| paddy@41 | 225 if err != nil { |
| paddy@41 | 226 t.Fatalf("Error listing endpoints in %T: %s", store, err) |
| paddy@41 | 227 } |
| paddy@41 | 228 if len(endpoints) != 0 { |
| paddy@41 | 229 t.Fatalf("Expected %d endpoints, got %+v from %T", 0, endpoints, store) |
| paddy@31 | 230 } |
| paddy@31 | 231 } |
| paddy@31 | 232 } |
| paddy@39 | 233 |
| paddy@39 | 234 func TestClientUpdates(t *testing.T) { |
| paddy@39 | 235 t.Parallel() |
| paddy@41 | 236 variations := 1 << 5 |
| paddy@39 | 237 client := Client{ |
| paddy@41 | 238 ID: uuid.NewID(), |
| paddy@41 | 239 Secret: "secret", |
| paddy@41 | 240 OwnerID: uuid.NewID(), |
| paddy@41 | 241 Name: "name", |
| paddy@41 | 242 Logo: "logo", |
| paddy@41 | 243 Website: "website", |
| paddy@39 | 244 } |
| paddy@39 | 245 for i := 0; i < variations; i++ { |
| paddy@41 | 246 var secret, name, logo, website string |
| paddy@39 | 247 change := ClientChange{} |
| paddy@151 | 248 client.ID = uuid.NewID() |
| paddy@39 | 249 expectation := client |
| paddy@39 | 250 result := client |
| paddy@39 | 251 if i&clientChangeSecret != 0 { |
| paddy@39 | 252 secret = fmt.Sprintf("secret-%d", i) |
| paddy@39 | 253 change.Secret = &secret |
| paddy@39 | 254 expectation.Secret = secret |
| paddy@39 | 255 } |
| paddy@39 | 256 if i&clientChangeOwnerID != 0 { |
| paddy@39 | 257 change.OwnerID = uuid.NewID() |
| paddy@39 | 258 expectation.OwnerID = change.OwnerID |
| paddy@39 | 259 } |
| paddy@39 | 260 if i&clientChangeName != 0 { |
| paddy@39 | 261 name = fmt.Sprintf("name-%d", i) |
| paddy@39 | 262 change.Name = &name |
| paddy@39 | 263 expectation.Name = name |
| paddy@39 | 264 } |
| paddy@39 | 265 if i&clientChangeLogo != 0 { |
| paddy@39 | 266 logo = fmt.Sprintf("logo-%d", i) |
| paddy@39 | 267 change.Logo = &logo |
| paddy@39 | 268 expectation.Logo = logo |
| paddy@39 | 269 } |
| paddy@39 | 270 if i&clientChangeWebsite != 0 { |
| paddy@39 | 271 website = fmt.Sprintf("website-%d", i) |
| paddy@39 | 272 change.Website = &website |
| paddy@39 | 273 expectation.Website = website |
| paddy@39 | 274 } |
| paddy@39 | 275 result.ApplyChange(change) |
| paddy@39 | 276 match, field, expected, got := compareClients(expectation, result) |
| paddy@39 | 277 if !match { |
| paddy@41 | 278 t.Fatalf("Expected field `%s` to be `%v`, got `%v`", field, expected, got) |
| paddy@39 | 279 } |
| paddy@39 | 280 for _, store := range clientStores { |
| paddy@116 | 281 context := Context{clients: store} |
| paddy@116 | 282 err := context.SaveClient(client) |
| paddy@39 | 283 if err != nil { |
| paddy@41 | 284 t.Fatalf("Error saving client in %T: %s", store, err) |
| paddy@39 | 285 } |
| paddy@116 | 286 err = context.UpdateClient(client.ID, change) |
| paddy@39 | 287 if err != nil { |
| paddy@41 | 288 t.Fatalf("Error updating client in %T: %s", store, err) |
| paddy@39 | 289 } |
| paddy@116 | 290 retrieved, err := context.GetClient(client.ID) |
| paddy@39 | 291 if err != nil { |
| paddy@116 | 292 t.Fatalf("Error getting client from %T: %s", store, err) |
| paddy@39 | 293 } |
| paddy@39 | 294 match, field, expected, got = compareClients(expectation, retrieved) |
| paddy@39 | 295 if !match { |
| paddy@41 | 296 t.Fatalf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store) |
| paddy@39 | 297 } |
| paddy@151 | 298 deleted := true |
| paddy@151 | 299 err = context.UpdateClient(client.ID, ClientChange{Deleted: &deleted}) |
| paddy@39 | 300 if err != nil { |
| paddy@41 | 301 t.Fatalf("Error deleting client from %T: %s", store, err) |
| paddy@39 | 302 } |
| paddy@39 | 303 } |
| paddy@39 | 304 } |
| paddy@39 | 305 } |
| paddy@41 | 306 |
| paddy@41 | 307 func TestClientEndpointChecks(t *testing.T) { |
| paddy@41 | 308 t.Parallel() |
| paddy@41 | 309 client := Client{ |
| paddy@41 | 310 ID: uuid.NewID(), |
| paddy@41 | 311 Secret: "secret", |
| paddy@41 | 312 OwnerID: uuid.NewID(), |
| paddy@41 | 313 Name: "name", |
| paddy@41 | 314 Logo: "logo", |
| paddy@41 | 315 Website: "website", |
| paddy@41 | 316 } |
| paddy@41 | 317 endpoint1 := Endpoint{ |
| paddy@41 | 318 ID: uuid.NewID(), |
| paddy@41 | 319 ClientID: client.ID, |
| paddy@149 | 320 Added: time.Now().Round(time.Millisecond), |
| paddy@116 | 321 URI: "https://www.example.com/first", |
| paddy@41 | 322 } |
| paddy@41 | 323 endpoint2 := Endpoint{ |
| paddy@41 | 324 ID: uuid.NewID(), |
| paddy@41 | 325 ClientID: client.ID, |
| paddy@149 | 326 Added: time.Now().Round(time.Millisecond), |
| paddy@116 | 327 URI: "https://www.example.com/my/full/path", |
| paddy@41 | 328 } |
| paddy@41 | 329 candidates := map[string]bool{ |
| paddy@41 | 330 "https://www.example.com/": false, |
| paddy@41 | 331 "https://www.example.com/first": true, |
| paddy@58 | 332 "https://www.example.com/first/extra/path": false, |
| paddy@41 | 333 "https://www.example.com/my": false, |
| paddy@41 | 334 "https://www.example.com/my/full/path": true, |
| paddy@41 | 335 } |
| paddy@41 | 336 for _, store := range clientStores { |
| paddy@116 | 337 context := Context{clients: store} |
| paddy@116 | 338 err := context.SaveClient(client) |
| paddy@41 | 339 if err != nil { |
| paddy@41 | 340 t.Fatalf("Error saving client in %T: %s", store, err) |
| paddy@41 | 341 } |
| paddy@151 | 342 err = context.AddEndpoints([]Endpoint{endpoint1}) |
| paddy@41 | 343 if err != nil { |
| paddy@41 | 344 t.Fatalf("Error saving endpoint in %T: %s", store, err) |
| paddy@41 | 345 } |
| paddy@151 | 346 err = context.AddEndpoints([]Endpoint{endpoint2}) |
| paddy@41 | 347 if err != nil { |
| paddy@41 | 348 t.Fatalf("Error saving endpoint in %T: %s", store, err) |
| paddy@41 | 349 } |
| paddy@41 | 350 for candidate, expectation := range candidates { |
| paddy@116 | 351 result, err := context.CheckEndpoint(client.ID, candidate) |
| paddy@54 | 352 if err != nil { |
| paddy@54 | 353 t.Fatalf("Error checking endpoint %s in %T: %s", candidate, store, err) |
| paddy@54 | 354 } |
| paddy@54 | 355 if result != expectation { |
| paddy@54 | 356 expectStr := "no" |
| paddy@54 | 357 resultStr := "a" |
| paddy@54 | 358 if expectation { |
| paddy@54 | 359 expectStr = "a" |
| paddy@54 | 360 resultStr = "no" |
| paddy@54 | 361 } |
| paddy@54 | 362 t.Errorf("Expected %s match for %s in %T, got %s match", expectStr, candidate, store, resultStr) |
| paddy@54 | 363 } |
| paddy@54 | 364 } |
| paddy@54 | 365 } |
| paddy@54 | 366 } |
| paddy@54 | 367 |
| paddy@54 | 368 func TestClientEndpointChecksStrict(t *testing.T) { |
| paddy@54 | 369 t.Parallel() |
| paddy@54 | 370 client := Client{ |
| paddy@54 | 371 ID: uuid.NewID(), |
| paddy@54 | 372 Secret: "secret", |
| paddy@54 | 373 OwnerID: uuid.NewID(), |
| paddy@54 | 374 Name: "name", |
| paddy@54 | 375 Logo: "logo", |
| paddy@54 | 376 Website: "website", |
| paddy@54 | 377 } |
| paddy@54 | 378 endpoint1 := Endpoint{ |
| paddy@54 | 379 ID: uuid.NewID(), |
| paddy@54 | 380 ClientID: client.ID, |
| paddy@149 | 381 Added: time.Now().Round(time.Millisecond), |
| paddy@116 | 382 URI: "https://www.example.com/first", |
| paddy@54 | 383 } |
| paddy@54 | 384 endpoint2 := Endpoint{ |
| paddy@54 | 385 ID: uuid.NewID(), |
| paddy@54 | 386 ClientID: client.ID, |
| paddy@149 | 387 Added: time.Now().Round(time.Millisecond), |
| paddy@116 | 388 URI: "https://www.example.com/my/full/path", |
| paddy@54 | 389 } |
| paddy@54 | 390 candidates := map[string]bool{ |
| paddy@54 | 391 "https://www.example.com/": false, |
| paddy@54 | 392 "https://www.example.com/first": true, |
| paddy@54 | 393 "https://www.example.com/first/extra/path": false, |
| paddy@54 | 394 "https://www.example.com/my": false, |
| paddy@54 | 395 "https://www.example.com/my/full/path": true, |
| paddy@54 | 396 } |
| paddy@54 | 397 for _, store := range clientStores { |
| paddy@116 | 398 context := Context{clients: store} |
| paddy@116 | 399 err := context.SaveClient(client) |
| paddy@54 | 400 if err != nil { |
| paddy@54 | 401 t.Fatalf("Error saving client in %T: %s", store, err) |
| paddy@54 | 402 } |
| paddy@151 | 403 err = context.AddEndpoints([]Endpoint{endpoint1}) |
| paddy@54 | 404 if err != nil { |
| paddy@54 | 405 t.Fatalf("Error saving endpoint in %T: %s", store, err) |
| paddy@54 | 406 } |
| paddy@151 | 407 err = context.AddEndpoints([]Endpoint{endpoint2}) |
| paddy@54 | 408 if err != nil { |
| paddy@54 | 409 t.Fatalf("Error saving endpoint in %T: %s", store, err) |
| paddy@54 | 410 } |
| paddy@54 | 411 for candidate, expectation := range candidates { |
| paddy@116 | 412 result, err := context.CheckEndpoint(client.ID, candidate) |
| paddy@41 | 413 if err != nil { |
| paddy@41 | 414 t.Fatalf("Error checking endpoint %s in %T: %s", candidate, store, err) |
| paddy@41 | 415 } |
| paddy@41 | 416 if result != expectation { |
| paddy@41 | 417 expectStr := "no" |
| paddy@41 | 418 resultStr := "a" |
| paddy@41 | 419 if expectation { |
| paddy@41 | 420 expectStr = "a" |
| paddy@41 | 421 resultStr = "no" |
| paddy@41 | 422 } |
| paddy@41 | 423 t.Errorf("Expected %s match for %s in %T, got %s match", expectStr, candidate, store, resultStr) |
| paddy@41 | 424 } |
| paddy@41 | 425 } |
| paddy@41 | 426 } |
| paddy@41 | 427 } |
| paddy@43 | 428 |
| paddy@43 | 429 func TestClientChangeValidation(t *testing.T) { |
| paddy@43 | 430 t.Parallel() |
| paddy@43 | 431 change := ClientChange{} |
| paddy@133 | 432 if err := change.Validate(); err[0] != ErrEmptyChange { |
| paddy@43 | 433 t.Errorf("Expected %s to give an error of %s, gave %s", "empty change", ErrEmptyChange, err) |
| paddy@43 | 434 } |
| paddy@133 | 435 names := map[string][]error{ |
| paddy@133 | 436 "a": []error{ErrClientNameTooShort}, |
| paddy@133 | 437 "ab": []error{}, |
| paddy@133 | 438 "abc": []error{}, |
| paddy@133 | 439 "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopq": []error{ErrClientNameTooLong}, |
| paddy@43 | 440 } |
| paddy@43 | 441 for name, expectation := range names { |
| paddy@43 | 442 change = ClientChange{Name: &name} |
| paddy@133 | 443 errs := change.Validate() |
| paddy@133 | 444 if len(errs) != len(expectation) { |
| paddy@133 | 445 t.Errorf("Expected %s to give %d errors, gave %d", name, len(expectation), len(errs)) |
| paddy@133 | 446 t.Logf("%+v", errs) |
| paddy@133 | 447 } |
| paddy@133 | 448 for pos, err := range errs { |
| paddy@133 | 449 if err != expectation[pos] { |
| paddy@133 | 450 t.Errorf("Expected %s to give an error of %s in position %d, gave %s", name, expectation[pos], pos, err) |
| paddy@133 | 451 } |
| paddy@43 | 452 } |
| paddy@43 | 453 } |
| paddy@43 | 454 longPath := "" |
| paddy@43 | 455 for i := 0; i < 1025; i++ { |
| paddy@43 | 456 longPath = fmt.Sprintf("%s%d", longPath, i) |
| paddy@43 | 457 } |
| paddy@133 | 458 logos := map[string][]error{ |
| paddy@133 | 459 "https://www.example.com/" + longPath: []error{ErrClientLogoTooLong}, |
| paddy@133 | 460 "https://www.example.com/ab": []error{}, |
| paddy@133 | 461 "www.example.com/ab": []error{ErrClientLogoNotURL}, |
| paddy@133 | 462 "test": []error{ErrClientLogoNotURL}, |
| paddy@133 | 463 "": []error{}, |
| paddy@43 | 464 } |
| paddy@43 | 465 for logo, expectation := range logos { |
| paddy@43 | 466 change = ClientChange{Logo: &logo} |
| paddy@133 | 467 errs := change.Validate() |
| paddy@133 | 468 if len(errs) != len(expectation) { |
| paddy@133 | 469 t.Errorf("Expected %s to give %d errors, gave %d", logo, len(expectation), len(errs)) |
| paddy@133 | 470 } |
| paddy@133 | 471 for pos, err := range errs { |
| paddy@133 | 472 if err != expectation[pos] { |
| paddy@133 | 473 t.Errorf("Expected %s to give an error of %s in positiong %d, gave %s", logo, expectation[pos], pos, err) |
| paddy@133 | 474 } |
| paddy@43 | 475 } |
| paddy@43 | 476 } |
| paddy@133 | 477 websites := map[string][]error{ |
| paddy@133 | 478 "https://www.example.com/" + longPath: []error{ErrClientWebsiteTooLong}, |
| paddy@133 | 479 "https://www.example.com/ab": []error{}, |
| paddy@133 | 480 "www.example.com/ab": []error{ErrClientWebsiteNotURL}, |
| paddy@133 | 481 "test": []error{ErrClientWebsiteNotURL}, |
| paddy@133 | 482 "": []error{}, |
| paddy@43 | 483 } |
| paddy@43 | 484 for website, expectation := range websites { |
| paddy@43 | 485 change = ClientChange{Website: &website} |
| paddy@133 | 486 errs := change.Validate() |
| paddy@133 | 487 if len(errs) != len(expectation) { |
| paddy@133 | 488 t.Errorf("Expected %s to give %d errors, gave %d", website, len(expectation), len(errs)) |
| paddy@133 | 489 } |
| paddy@133 | 490 for pos, err := range errs { |
| paddy@133 | 491 if err != expectation[pos] { |
| paddy@133 | 492 t.Errorf("Expected %s to give an error of %s in position %d, gave %s", website, expectation[pos], pos, err) |
| paddy@133 | 493 } |
| paddy@43 | 494 } |
| paddy@43 | 495 } |
| paddy@43 | 496 } |
| paddy@113 | 497 |
| paddy@129 | 498 func TestGetClientAuth(t *testing.T) { |
| paddy@129 | 499 t.Parallel() |
| paddy@129 | 500 type clientAuthRequest struct { |
| paddy@129 | 501 username string |
| paddy@129 | 502 pass string |
| paddy@129 | 503 clientID string |
| paddy@129 | 504 allowPublic bool |
| paddy@129 | 505 expectedClientID uuid.ID |
| paddy@129 | 506 expectedClientSecret string |
| paddy@129 | 507 expectedValid bool |
| paddy@129 | 508 expectedCode int |
| paddy@129 | 509 expectedBody string |
| paddy@129 | 510 expectAuthenticateHeader bool |
| paddy@129 | 511 } |
| paddy@129 | 512 id := uuid.NewID() |
| paddy@129 | 513 tests := []clientAuthRequest{ |
| paddy@129 | 514 {"", "", "", false, nil, "", false, http.StatusUnauthorized, `{"error":"invalid_client"}`, false}, |
| paddy@129 | 515 {"", "", "", true, nil, "", false, http.StatusUnauthorized, `{"error":"invalid_client"}`, false}, |
| paddy@129 | 516 {"", "no clientID set", "", false, nil, "", false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 517 {"", "no clientID set", "", true, nil, "", false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 518 {"not an actual id", "invalid client ID set", "", false, nil, "", false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 519 {"not an actual id", "invalid client ID set", "", true, nil, "", false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 520 {"", "", "not an actual id", true, nil, "", false, http.StatusUnauthorized, `{"error":"invalid_client"}`, false}, |
| paddy@129 | 521 {id.String(), "secret", "", true, id, "secret", true, http.StatusOK, "", false}, |
| paddy@129 | 522 {id.String(), "secret", "", false, id, "secret", true, http.StatusOK, "", false}, |
| paddy@129 | 523 {"", "", id.String(), true, id, "", true, http.StatusOK, "", false}, |
| paddy@129 | 524 {"", "", id.String(), false, nil, "", false, http.StatusBadRequest, `{"error":"unauthorized_client"}`, false}, |
| paddy@129 | 525 } |
| paddy@129 | 526 for pos, test := range tests { |
| paddy@129 | 527 t.Logf("Running test #%d, with request %+v", pos, test) |
| paddy@129 | 528 w := httptest.NewRecorder() |
| paddy@129 | 529 r, err := http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@129 | 530 if err != nil { |
| paddy@129 | 531 t.Fatal("Can't build request:", err) |
| paddy@129 | 532 } |
| paddy@129 | 533 if test.username != "" || test.pass != "" { |
| paddy@129 | 534 r.SetBasicAuth(test.username, test.pass) |
| paddy@129 | 535 } |
| paddy@129 | 536 r.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@129 | 537 params := url.Values{} |
| paddy@129 | 538 params.Set("client_id", test.clientID) |
| paddy@129 | 539 body := bytes.NewBufferString(params.Encode()) |
| paddy@129 | 540 r.Body = ioutil.NopCloser(body) |
| paddy@129 | 541 respID, respSecret, success := getClientAuth(w, r, test.allowPublic) |
| paddy@129 | 542 if (respID == nil && test.expectedClientID != nil) || (respID != nil && test.expectedClientID == nil) || !respID.Equal(test.expectedClientID) { |
| paddy@129 | 543 t.Errorf("Expected response ID to be %v, got %v", test.expectedClientID, respID) |
| paddy@129 | 544 } |
| paddy@129 | 545 if test.expectedClientSecret != respSecret { |
| paddy@129 | 546 t.Errorf("Expected response secret to be '%s', got '%s'", test.expectedClientSecret, respSecret) |
| paddy@129 | 547 } |
| paddy@129 | 548 if test.expectedValid != success { |
| paddy@129 | 549 t.Errorf("Expected success result to be %v, got %v", test.expectedValid, success) |
| paddy@129 | 550 } |
| paddy@129 | 551 if test.expectedCode != w.Code { |
| paddy@129 | 552 t.Errorf("Expected response code to be %d, got %d", test.expectedCode, w.Code) |
| paddy@129 | 553 } |
| paddy@129 | 554 if test.expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@129 | 555 t.Errorf("Expected body to be '%s', got '%s'", test.expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@129 | 556 } |
| paddy@129 | 557 if test.expectAuthenticateHeader && w.Header().Get("WWW-Authenticate") != "Basic" { |
| paddy@129 | 558 t.Errorf(`Expected header WWW-Authenticate to be set to "Basic", got "%s"`, w.Header().Get("WWW-Authenticate")) |
| paddy@129 | 559 } |
| paddy@129 | 560 } |
| paddy@129 | 561 } |
| paddy@129 | 562 |
| paddy@113 | 563 func TestVerifyClient(t *testing.T) { |
| paddy@113 | 564 t.Parallel() |
| paddy@129 | 565 type verifyClientRequest struct { |
| paddy@129 | 566 username string |
| paddy@129 | 567 pass string |
| paddy@129 | 568 clientID string |
| paddy@129 | 569 allowPublic bool |
| paddy@129 | 570 expectedClientID uuid.ID |
| paddy@129 | 571 expectedValid bool |
| paddy@129 | 572 expectedCode int |
| paddy@129 | 573 expectedBody string |
| paddy@129 | 574 expectAuthenticateHeader bool |
| paddy@129 | 575 } |
| paddy@113 | 576 memstore := NewMemstore() |
| paddy@113 | 577 context := Context{ |
| paddy@113 | 578 clients: memstore, |
| paddy@113 | 579 } |
| paddy@113 | 580 client := Client{ |
| paddy@113 | 581 ID: uuid.NewID(), |
| paddy@113 | 582 Secret: "super secret!", |
| paddy@113 | 583 OwnerID: uuid.NewID(), |
| paddy@113 | 584 Name: "My test client", |
| paddy@113 | 585 Logo: "https://secondbit.org/logo.png", |
| paddy@113 | 586 Website: "https://secondbit.org/", |
| paddy@113 | 587 Type: "confidential", |
| paddy@113 | 588 } |
| paddy@113 | 589 err := context.SaveClient(client) |
| paddy@113 | 590 if err != nil { |
| paddy@113 | 591 t.Fatal("Could not save client:", err) |
| paddy@113 | 592 } |
| paddy@113 | 593 publicClient := Client{ |
| paddy@113 | 594 ID: uuid.NewID(), |
| paddy@113 | 595 Secret: "", |
| paddy@113 | 596 OwnerID: uuid.NewID(), |
| paddy@113 | 597 Name: "A public client", |
| paddy@113 | 598 Logo: "https://secondbit.org/logo.png", |
| paddy@113 | 599 Website: "https://secondbit.org/", |
| paddy@113 | 600 Type: "public", |
| paddy@113 | 601 } |
| paddy@113 | 602 err = context.SaveClient(publicClient) |
| paddy@113 | 603 if err != nil { |
| paddy@113 | 604 t.Fatal("Could not save client:", err) |
| paddy@113 | 605 } |
| paddy@129 | 606 id := uuid.NewID() |
| paddy@129 | 607 tests := []verifyClientRequest{ |
| paddy@129 | 608 {"", "", "", false, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, false}, |
| paddy@129 | 609 {"", "", "", true, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, false}, |
| paddy@129 | 610 {"", "no clientID set", "", false, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 611 {"", "no clientID set", "", true, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 612 {"not an actual id", "invalid client ID set", "", false, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 613 {"not an actual id", "invalid client ID set", "", true, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 614 {id.String(), "unsaved client ID set", "", false, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 615 {id.String(), "unsaved client ID set", "", true, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 616 {client.ID.String(), "wrong secret", "", false, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 617 {client.ID.String(), "wrong secret", "", true, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, true}, |
| paddy@129 | 618 {"", "", "not an actual id", true, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, false}, |
| paddy@129 | 619 {"", "", id.String(), true, nil, false, http.StatusUnauthorized, `{"error":"invalid_client"}`, false}, |
| paddy@129 | 620 {client.ID.String(), client.Secret, "", true, client.ID, true, http.StatusOK, "", false}, |
| paddy@129 | 621 {client.ID.String(), client.Secret, "", false, client.ID, true, http.StatusOK, "", false}, |
| paddy@129 | 622 {"", "", publicClient.ID.String(), true, publicClient.ID, true, http.StatusOK, "", false}, |
| paddy@129 | 623 {"", "", publicClient.ID.String(), false, nil, false, http.StatusBadRequest, `{"error":"unauthorized_client"}`, false}, |
| paddy@113 | 624 } |
| paddy@113 | 625 |
| paddy@129 | 626 for pos, test := range tests { |
| paddy@129 | 627 t.Logf("Running test #%d, with request %+v", pos, test) |
| paddy@129 | 628 w := httptest.NewRecorder() |
| paddy@129 | 629 r, err := http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@129 | 630 if err != nil { |
| paddy@129 | 631 t.Fatal("Can't build request:", err) |
| paddy@129 | 632 } |
| paddy@129 | 633 if test.username != "" || test.pass != "" { |
| paddy@129 | 634 r.SetBasicAuth(test.username, test.pass) |
| paddy@129 | 635 } |
| paddy@129 | 636 r.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@129 | 637 params := url.Values{} |
| paddy@129 | 638 params.Set("client_id", test.clientID) |
| paddy@129 | 639 body := bytes.NewBufferString(params.Encode()) |
| paddy@129 | 640 r.Body = ioutil.NopCloser(body) |
| paddy@129 | 641 respID, success := verifyClient(w, r, test.allowPublic, context) |
| paddy@129 | 642 if (respID == nil && test.expectedClientID != nil) || (respID != nil && test.expectedClientID == nil) || !respID.Equal(test.expectedClientID) { |
| paddy@129 | 643 t.Errorf("Expected response ID to be %v, got %v", test.expectedClientID, respID) |
| paddy@129 | 644 } |
| paddy@129 | 645 if test.expectedValid != success { |
| paddy@129 | 646 t.Errorf("Expected success result to be %v, got %v", test.expectedValid, success) |
| paddy@129 | 647 } |
| paddy@129 | 648 if test.expectedCode != w.Code { |
| paddy@129 | 649 t.Errorf("Expected response code to be %d, got %d", test.expectedCode, w.Code) |
| paddy@129 | 650 } |
| paddy@129 | 651 if test.expectedBody != strings.TrimSpace(w.Body.String()) { |
| paddy@129 | 652 t.Errorf("Expected body to be '%s', got '%s'", test.expectedBody, strings.TrimSpace(w.Body.String())) |
| paddy@129 | 653 } |
| paddy@129 | 654 if test.expectAuthenticateHeader && w.Header().Get("WWW-Authenticate") != "Basic" { |
| paddy@129 | 655 t.Errorf(`Expected header WWW-Authenticate to be set to "Basic", got "%s"`, w.Header().Get("WWW-Authenticate")) |
| paddy@129 | 656 } |
| paddy@113 | 657 } |
| paddy@113 | 658 } |
| paddy@116 | 659 |
| paddy@116 | 660 func TestCreateClientHandler(t *testing.T) { |
| paddy@116 | 661 t.Parallel() |
| paddy@116 | 662 memstore := NewMemstore() |
| paddy@116 | 663 c := Context{ |
| paddy@116 | 664 clients: memstore, |
| paddy@116 | 665 profiles: memstore, |
| paddy@116 | 666 } |
| paddy@116 | 667 w := httptest.NewRecorder() |
| paddy@116 | 668 r, err := http.NewRequest("POST", "https://test.auth.secondbit.org/clients", nil) |
| paddy@116 | 669 if err != nil { |
| paddy@116 | 670 t.Fatal("Can't build request:", err) |
| paddy@116 | 671 } |
| paddy@116 | 672 r.Header.Set("Content-Type", "application/json") |
| paddy@116 | 673 CreateClientHandler(w, r, c) |
| paddy@116 | 674 if w.Code != http.StatusUnauthorized { |
| paddy@116 | 675 t.Errorf("Expected status of %d, got status %d", http.StatusUnauthorized, w.Code) |
| paddy@116 | 676 } |
| paddy@116 | 677 expected := `{"errors":[{"error":"access_denied"}]}` |
| paddy@116 | 678 result := strings.TrimSpace(w.Body.String()) |
| paddy@116 | 679 if result != expected { |
| paddy@116 | 680 t.Errorf("Expected response to be `%s`, got `%s`", expected, result) |
| paddy@116 | 681 } |
| paddy@116 | 682 w = httptest.NewRecorder() |
| paddy@116 | 683 r.Header.Set("Authorization", "Not basic at all...") |
| paddy@116 | 684 CreateClientHandler(w, r, c) |
| paddy@116 | 685 if w.Code != http.StatusUnauthorized { |
| paddy@116 | 686 t.Errorf("Expected status of %d, got status %d", http.StatusUnauthorized, w.Code) |
| paddy@116 | 687 } |
| paddy@116 | 688 expected = `{"errors":[{"error":"access_denied"}]}` |
| paddy@116 | 689 result = strings.TrimSpace(w.Body.String()) |
| paddy@116 | 690 if result != expected { |
| paddy@116 | 691 t.Errorf("Expected response to be `%s`, got `%s`", expected, result) |
| paddy@116 | 692 } |
| paddy@116 | 693 w = httptest.NewRecorder() |
| paddy@116 | 694 r.Header.Set("Authorization", "Basic TotallyNotBase64Encoded") |
| paddy@116 | 695 CreateClientHandler(w, r, c) |
| paddy@116 | 696 if w.Code != http.StatusUnauthorized { |
| paddy@116 | 697 t.Errorf("Expected status of %d, got status %d", http.StatusUnauthorized, w.Code) |
| paddy@116 | 698 } |
| paddy@116 | 699 expected = `{"errors":[{"error":"access_denied"}]}` |
| paddy@116 | 700 result = strings.TrimSpace(w.Body.String()) |
| paddy@116 | 701 if result != expected { |
| paddy@116 | 702 t.Errorf("Expected response to be `%s`, got `%s`", expected, result) |
| paddy@116 | 703 } |
| paddy@116 | 704 w = httptest.NewRecorder() |
| paddy@116 | 705 r.Header.Set("Authorization", "Basic dGhpc2hhc25vY29sb24=") |
| paddy@116 | 706 CreateClientHandler(w, r, c) |
| paddy@116 | 707 if w.Code != http.StatusUnauthorized { |
| paddy@116 | 708 t.Errorf("Expected status of %d, got status %d", http.StatusUnauthorized, w.Code) |
| paddy@116 | 709 } |
| paddy@116 | 710 expected = `{"errors":[{"error":"access_denied"}]}` |
| paddy@116 | 711 result = strings.TrimSpace(w.Body.String()) |
| paddy@116 | 712 if result != expected { |
| paddy@116 | 713 t.Errorf("Expected response to be `%s`, got `%s`", expected, result) |
| paddy@116 | 714 } |
| paddy@116 | 715 profile := Profile{ |
| paddy@116 | 716 ID: uuid.NewID(), |
| paddy@116 | 717 Name: "Test User", |
| paddy@116 | 718 Passphrase: "f3a4ac4f1d657b2e6e776d24213e39406d50a87a52691a2a78891425af1271d0", |
| paddy@116 | 719 Iterations: 1, |
| paddy@116 | 720 Salt: "d82d92cfa8bfb5a08270ebbf39a3710d24b352b937fcc8959ebcb40384cc616b", |
| paddy@116 | 721 PassphraseScheme: 1, |
| paddy@116 | 722 Compromised: false, |
| paddy@116 | 723 LockedUntil: time.Time{}, |
| paddy@116 | 724 PassphraseReset: "", |
| paddy@116 | 725 PassphraseResetCreated: time.Time{}, |
| paddy@149 | 726 Created: time.Now().Round(time.Millisecond), |
| paddy@116 | 727 LastSeen: time.Time{}, |
| paddy@116 | 728 } |
| paddy@116 | 729 login := Login{ |
| paddy@116 | 730 Type: "email", |
| paddy@116 | 731 Value: "test@example.com", |
| paddy@116 | 732 ProfileID: profile.ID, |
| paddy@149 | 733 Created: time.Now().Round(time.Millisecond), |
| paddy@116 | 734 LastUsed: time.Time{}, |
| paddy@116 | 735 } |
| paddy@116 | 736 w = httptest.NewRecorder() |
| paddy@116 | 737 r.SetBasicAuth("test@example.com", "mysecurepassphrase") |
| paddy@116 | 738 CreateClientHandler(w, r, c) |
| paddy@116 | 739 if w.Code != http.StatusUnauthorized { |
| paddy@116 | 740 t.Errorf("Expected status of %d, got status %d", http.StatusUnauthorized, w.Code) |
| paddy@116 | 741 } |
| paddy@116 | 742 expected = `{"errors":[{"error":"access_denied"}]}` |
| paddy@116 | 743 result = strings.TrimSpace(w.Body.String()) |
| paddy@116 | 744 if result != expected { |
| paddy@116 | 745 t.Errorf("Expected response to be `%s`, got `%s`", expected, result) |
| paddy@116 | 746 } |
| paddy@116 | 747 err = c.SaveProfile(profile) |
| paddy@116 | 748 if err != nil { |
| paddy@116 | 749 t.Error("Error saving profile:", err) |
| paddy@116 | 750 } |
| paddy@116 | 751 err = c.AddLogin(login) |
| paddy@116 | 752 if err != nil { |
| paddy@116 | 753 t.Error("Error adding login:", err) |
| paddy@116 | 754 } |
| paddy@116 | 755 r.SetBasicAuth("test@example.com", "mysecurepassphrase") |
| paddy@116 | 756 type testStruct struct { |
| paddy@116 | 757 request string |
| paddy@116 | 758 code int |
| paddy@172 | 759 resp Response |
| paddy@116 | 760 } |
| paddy@116 | 761 tests := []testStruct{ |
| paddy@172 | 762 {``, http.StatusBadRequest, Response{Errors: []RequestError{{Slug: RequestErrInvalidFormat, Field: "/"}}}}, |
| paddy@172 | 763 {`{}`, http.StatusBadRequest, Response{Errors: []RequestError{{Slug: RequestErrMissing, Field: "/type"}, {Slug: RequestErrMissing, Field: "/name"}}}}, |
| paddy@172 | 764 {`{"type":"notarealtype"}`, http.StatusBadRequest, Response{Errors: []RequestError{{Slug: RequestErrInvalidValue, Field: "/type"}, {Slug: RequestErrMissing, Field: "/name"}}}}, |
| paddy@172 | 765 {`{"type":"notarealtype","name":"myreallylongnameislongerthatthemaximumnamelength"}`, http.StatusBadRequest, Response{Errors: []RequestError{{Slug: RequestErrInvalidValue, Field: "/type"}, {Slug: RequestErrOverflow, Field: "/name"}}}}, |
| paddy@172 | 766 {`{"type":"notarealtype","name":"a"}`, http.StatusBadRequest, Response{Errors: []RequestError{{Slug: RequestErrInvalidValue, Field: "/type"}, {Slug: RequestErrInsufficient, Field: "/name"}}}}, |
| paddy@172 | 767 {`{"type":"public"}`, http.StatusBadRequest, Response{Errors: []RequestError{{Slug: RequestErrMissing, Field: "/name"}}}}, |
| paddy@172 | 768 {`{"type":"public","name":"myreallylongnameislongerthatthemaximumnamelength"}`, http.StatusBadRequest, Response{Errors: []RequestError{{Slug: RequestErrOverflow, Field: "/name"}}}}, |
| paddy@172 | 769 {`{"type":"public","name":"a"}`, http.StatusBadRequest, Response{Errors: []RequestError{{Slug: RequestErrInsufficient, Field: "/name"}}}}, |
| paddy@172 | 770 {`{"name":"My Client"}`, http.StatusBadRequest, Response{Errors: []RequestError{{Slug: RequestErrMissing, Field: "/type"}}}}, |
| paddy@172 | 771 {`{"type":"notarealtype","name":"My Client"}`, http.StatusBadRequest, Response{Errors: []RequestError{{Slug: RequestErrInvalidValue, Field: "/type"}}}}, |
| paddy@172 | 772 {`{"type":"public","name":"My Client"}`, http.StatusCreated, Response{Clients: []Client{{Name: "My Client", OwnerID: profile.ID, Type: "public"}}}}, |
| paddy@172 | 773 {`{"type":"public","name":"My Client", "endpoints": ["https://test.secondbit.org/", "https://paddy.io"]}`, http.StatusCreated, Response{Clients: []Client{{Name: "My Client", OwnerID: profile.ID, Type: "public"}}, Endpoints: []Endpoint{{URI: "https://test.secondbit.org/"}, {URI: "https://paddy.io"}}}}, |
| paddy@172 | 774 {`{"type":"public","name":"My Client", "endpoints": [":/not a url", "https://paddy.io"]}`, http.StatusCreated, Response{Clients: []Client{{Name: "My Client", OwnerID: profile.ID, Type: "public"}}, Endpoints: []Endpoint{{URI: "https://paddy.io"}}, Errors: []RequestError{{Slug: RequestErrInvalidFormat, Field: "/endpoints/0"}}}}, |
| paddy@172 | 775 {`{"type":"public","name":"My Client", "endpoints": [":/not a url", "/relative/uri", "https://paddy.io"]}`, http.StatusCreated, Response{Clients: []Client{{Name: "My Client", OwnerID: profile.ID, Type: "public"}}, Endpoints: []Endpoint{{URI: "https://paddy.io"}}, Errors: []RequestError{{Slug: RequestErrInvalidFormat, Field: "/endpoints/0"}, {Slug: RequestErrInvalidValue, Field: "/endpoints/1"}}}}, |
| paddy@172 | 776 {`{"type":"confidential","name":"Secret Client", "endpoints": ["https://secondbit.org"]}`, http.StatusCreated, Response{Clients: []Client{{Name: "Secret Client", OwnerID: profile.ID, Type: "confidential"}}, Endpoints: []Endpoint{{URI: "https://secondbit.org"}}}}, |
| paddy@116 | 777 } |
| paddy@116 | 778 for pos, test := range tests { |
| paddy@116 | 779 t.Logf("Test #%d: `%s`", pos, test.request) |
| paddy@116 | 780 w = httptest.NewRecorder() |
| paddy@116 | 781 body := bytes.NewBufferString(test.request) |
| paddy@116 | 782 r.Body = ioutil.NopCloser(body) |
| paddy@116 | 783 CreateClientHandler(w, r, c) |
| paddy@116 | 784 if w.Code != test.code { |
| paddy@116 | 785 t.Errorf("Expected response code to be %d, got %d", test.code, w.Code) |
| paddy@116 | 786 } |
| paddy@116 | 787 t.Logf("Response: %s", w.Body.String()) |
| paddy@172 | 788 var res Response |
| paddy@116 | 789 err = json.Unmarshal(w.Body.Bytes(), &res) |
| paddy@116 | 790 if err != nil { |
| paddy@116 | 791 t.Error("Unexpected error unmarshalling response:", err) |
| paddy@116 | 792 } |
| paddy@126 | 793 if len(res.Clients) > 0 { |
| paddy@126 | 794 if res.Clients[0].Type == "confidential" && res.Clients[0].Secret == "" { |
| paddy@126 | 795 t.Log("Client:", res.Clients[0]) |
| paddy@126 | 796 t.Error("Expected confidential client to have a secret, but does not.") |
| paddy@126 | 797 } else if res.Clients[0].Type == "public" && res.Clients[0].Secret != "" { |
| paddy@126 | 798 t.Log("Client:", res.Clients[0]) |
| paddy@126 | 799 t.Error("Expected public client to not have a secret, but it does.") |
| paddy@126 | 800 } |
| paddy@126 | 801 } |
| paddy@116 | 802 fillInServerGenerated(test.resp, res) |
| paddy@116 | 803 success, field, expectation, result := compareResponses(test.resp, res) |
| paddy@116 | 804 if !success { |
| paddy@116 | 805 t.Errorf("Unexpected result for %s in response: expected %v, got %v", field, expectation, result) |
| paddy@116 | 806 } |
| paddy@116 | 807 } |
| paddy@116 | 808 } |
| paddy@128 | 809 |
| paddy@139 | 810 func TestGetClientHandler(t *testing.T) { |
| paddy@139 | 811 t.Parallel() |
| paddy@139 | 812 memstore := NewMemstore() |
| paddy@139 | 813 c := Context{ |
| paddy@139 | 814 clients: memstore, |
| paddy@139 | 815 profiles: memstore, |
| paddy@139 | 816 } |
| paddy@139 | 817 client := Client{ |
| paddy@139 | 818 ID: uuid.NewID(), |
| paddy@139 | 819 Secret: "myawesomesecret", |
| paddy@139 | 820 OwnerID: uuid.NewID(), |
| paddy@139 | 821 Name: "Test Client", |
| paddy@139 | 822 Logo: "https://auth.secondbit.org/logo.png", |
| paddy@139 | 823 Website: "https://code.secondbit.org", |
| paddy@139 | 824 Type: clientTypeConfidential, |
| paddy@139 | 825 } |
| paddy@139 | 826 err := c.SaveClient(client) |
| paddy@139 | 827 if err != nil { |
| paddy@139 | 828 t.Fatal("Can't store client in memstore:", err) |
| paddy@139 | 829 } |
| paddy@139 | 830 profile := Profile{ |
| paddy@139 | 831 ID: uuid.NewID(), |
| paddy@139 | 832 Name: "Test User", |
| paddy@139 | 833 Passphrase: "f3a4ac4f1d657b2e6e776d24213e39406d50a87a52691a2a78891425af1271d0", |
| paddy@139 | 834 Iterations: 1, |
| paddy@139 | 835 Salt: "d82d92cfa8bfb5a08270ebbf39a3710d24b352b937fcc8959ebcb40384cc616b", |
| paddy@139 | 836 PassphraseScheme: 1, |
| paddy@139 | 837 Compromised: false, |
| paddy@139 | 838 LockedUntil: time.Time{}, |
| paddy@139 | 839 PassphraseReset: "", |
| paddy@139 | 840 PassphraseResetCreated: time.Time{}, |
| paddy@149 | 841 Created: time.Now().Round(time.Millisecond), |
| paddy@139 | 842 LastSeen: time.Time{}, |
| paddy@139 | 843 } |
| paddy@139 | 844 login := Login{ |
| paddy@139 | 845 Type: "email", |
| paddy@139 | 846 Value: "test@example.com", |
| paddy@139 | 847 ProfileID: profile.ID, |
| paddy@149 | 848 Created: time.Now().Round(time.Millisecond), |
| paddy@139 | 849 LastUsed: time.Time{}, |
| paddy@139 | 850 } |
| paddy@139 | 851 err = c.SaveProfile(profile) |
| paddy@139 | 852 if err != nil { |
| paddy@139 | 853 t.Error("Error saving profile:", err) |
| paddy@139 | 854 } |
| paddy@139 | 855 err = c.AddLogin(login) |
| paddy@139 | 856 if err != nil { |
| paddy@139 | 857 t.Error("Error adding login:", err) |
| paddy@139 | 858 } |
| paddy@139 | 859 router := mux.NewRouter() |
| paddy@139 | 860 RegisterClientHandlers(router, c) |
| paddy@139 | 861 w := httptest.NewRecorder() |
| paddy@139 | 862 u := "https://test.auth.secondbit.org/clients/" + client.ID.String() |
| paddy@139 | 863 r, err := http.NewRequest("GET", u, nil) |
| paddy@139 | 864 if err != nil { |
| paddy@139 | 865 t.Fatal("Can't build request:", err) |
| paddy@139 | 866 } |
| paddy@139 | 867 r.Header.Set("Content-Type", "application/json") |
| paddy@139 | 868 router.ServeHTTP(w, r) |
| paddy@139 | 869 if w.Code != http.StatusOK { |
| paddy@139 | 870 t.Errorf("Expected response code to be %d, got %d", http.StatusOK, w.Code) |
| paddy@139 | 871 } |
| paddy@139 | 872 t.Logf("Response: %s", w.Body.String()) |
| paddy@172 | 873 var res Response |
| paddy@139 | 874 err = json.Unmarshal(w.Body.Bytes(), &res) |
| paddy@139 | 875 if err != nil { |
| paddy@139 | 876 t.Error("Unexpected error unmarshalling response:", err) |
| paddy@139 | 877 } |
| paddy@139 | 878 if len(res.Clients) != 1 { |
| paddy@139 | 879 t.Errorf("Expected %d results in response, got %d", 1, len(res.Clients)) |
| paddy@139 | 880 } |
| paddy@139 | 881 if res.Clients[0].Secret != "" { |
| paddy@139 | 882 t.Error("Expected secret not to be set, but was set to", res.Clients[0].Secret) |
| paddy@139 | 883 } |
| paddy@139 | 884 // fill in the secret, which was omitted in the response |
| paddy@139 | 885 res.Clients[0].Secret = client.Secret |
| paddy@139 | 886 success, field, expectation, result := compareClients(client, res.Clients[0]) |
| paddy@139 | 887 if !success { |
| paddy@139 | 888 t.Errorf("Unexpected result for %s in response: expected %v, got %v", field, expectation, result) |
| paddy@139 | 889 } |
| paddy@139 | 890 |
| paddy@139 | 891 // test for improperly formatted ID |
| paddy@139 | 892 u = "https://test.auth.secondbit.org/clients/notanID" |
| paddy@139 | 893 w = httptest.NewRecorder() |
| paddy@139 | 894 r, err = http.NewRequest("GET", u, nil) |
| paddy@139 | 895 if err != nil { |
| paddy@139 | 896 t.Fatal("Can't build request:", err) |
| paddy@139 | 897 } |
| paddy@139 | 898 r.Header.Set("Content-Type", "application/json") |
| paddy@139 | 899 router.ServeHTTP(w, r) |
| paddy@139 | 900 if w.Code != http.StatusBadRequest { |
| paddy@139 | 901 t.Errorf("Expected response code to be %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@139 | 902 } |
| paddy@139 | 903 t.Logf("Response: %s", w.Body.String()) |
| paddy@172 | 904 res = Response{} |
| paddy@139 | 905 err = json.Unmarshal(w.Body.Bytes(), &res) |
| paddy@139 | 906 if err != nil { |
| paddy@139 | 907 t.Error("Unexpected error unmarshalling response:", err) |
| paddy@139 | 908 } |
| paddy@139 | 909 if len(res.Errors) != 1 { |
| paddy@139 | 910 t.Errorf("Expected %d results in response, got %d", 1, len(res.Errors)) |
| paddy@139 | 911 } |
| paddy@172 | 912 e := RequestError{Slug: RequestErrInvalidFormat, Param: "id"} |
| paddy@139 | 913 success, field, expectation, result = compareErrors(e, res.Errors[0]) |
| paddy@139 | 914 if !success { |
| paddy@139 | 915 t.Errorf("Unexpected result for %s in error response: expected %v, got %v", field, expectation, result) |
| paddy@139 | 916 } |
| paddy@139 | 917 |
| paddy@139 | 918 // test for a non-existent client |
| paddy@139 | 919 u = "https://test.auth.secondbit.org/clients/" + uuid.NewID().String() |
| paddy@139 | 920 w = httptest.NewRecorder() |
| paddy@139 | 921 r, err = http.NewRequest("GET", u, nil) |
| paddy@139 | 922 if err != nil { |
| paddy@139 | 923 t.Fatal("Can't build request:", err) |
| paddy@139 | 924 } |
| paddy@139 | 925 r.Header.Set("Content-Type", "application/json") |
| paddy@139 | 926 router.ServeHTTP(w, r) |
| paddy@139 | 927 if w.Code != http.StatusNotFound { |
| paddy@139 | 928 t.Errorf("Expected response code to be %d, got %d", http.StatusNotFound, w.Code) |
| paddy@139 | 929 } |
| paddy@139 | 930 t.Logf("Response: %s", w.Body.String()) |
| paddy@172 | 931 res = Response{} |
| paddy@139 | 932 err = json.Unmarshal(w.Body.Bytes(), &res) |
| paddy@139 | 933 if err != nil { |
| paddy@139 | 934 t.Error("Unexpected error unmarshalling response:", err) |
| paddy@139 | 935 } |
| paddy@139 | 936 if len(res.Errors) != 1 { |
| paddy@139 | 937 t.Errorf("Expected %d results in response, got %d", 1, len(res.Errors)) |
| paddy@139 | 938 } |
| paddy@172 | 939 e = RequestError{Slug: RequestErrNotFound, Param: "id"} |
| paddy@139 | 940 success, field, expectation, result = compareErrors(e, res.Errors[0]) |
| paddy@139 | 941 if !success { |
| paddy@139 | 942 t.Errorf("Unexpected result for %s in error response: expected %v, got %v", field, expectation, result) |
| paddy@139 | 943 } |
| paddy@139 | 944 } |
| paddy@139 | 945 |
| paddy@139 | 946 func TestAuthenticatedGetClientHandler(t *testing.T) { |
| paddy@139 | 947 t.Parallel() |
| paddy@139 | 948 memstore := NewMemstore() |
| paddy@139 | 949 c := Context{ |
| paddy@139 | 950 clients: memstore, |
| paddy@139 | 951 profiles: memstore, |
| paddy@139 | 952 } |
| paddy@139 | 953 client := Client{ |
| paddy@139 | 954 ID: uuid.NewID(), |
| paddy@139 | 955 Secret: "myawesomesecret", |
| paddy@139 | 956 OwnerID: uuid.NewID(), |
| paddy@139 | 957 Name: "Test Client", |
| paddy@139 | 958 Logo: "https://auth.secondbit.org/logo.png", |
| paddy@139 | 959 Website: "https://code.secondbit.org", |
| paddy@139 | 960 Type: clientTypeConfidential, |
| paddy@139 | 961 } |
| paddy@139 | 962 err := c.SaveClient(client) |
| paddy@139 | 963 if err != nil { |
| paddy@139 | 964 t.Fatal("Can't store client in memstore:", err) |
| paddy@139 | 965 } |
| paddy@139 | 966 profile := Profile{ |
| paddy@139 | 967 ID: client.OwnerID, |
| paddy@139 | 968 Name: "Test User", |
| paddy@139 | 969 Passphrase: "f3a4ac4f1d657b2e6e776d24213e39406d50a87a52691a2a78891425af1271d0", |
| paddy@139 | 970 Iterations: 1, |
| paddy@139 | 971 Salt: "d82d92cfa8bfb5a08270ebbf39a3710d24b352b937fcc8959ebcb40384cc616b", |
| paddy@139 | 972 PassphraseScheme: 1, |
| paddy@139 | 973 Compromised: false, |
| paddy@139 | 974 LockedUntil: time.Time{}, |
| paddy@139 | 975 PassphraseReset: "", |
| paddy@139 | 976 PassphraseResetCreated: time.Time{}, |
| paddy@149 | 977 Created: time.Now().Round(time.Millisecond), |
| paddy@139 | 978 LastSeen: time.Time{}, |
| paddy@139 | 979 } |
| paddy@139 | 980 login := Login{ |
| paddy@139 | 981 Type: "email", |
| paddy@139 | 982 Value: "test@example.com", |
| paddy@139 | 983 ProfileID: profile.ID, |
| paddy@149 | 984 Created: time.Now().Round(time.Millisecond), |
| paddy@139 | 985 LastUsed: time.Time{}, |
| paddy@139 | 986 } |
| paddy@139 | 987 err = c.SaveProfile(profile) |
| paddy@139 | 988 if err != nil { |
| paddy@139 | 989 t.Error("Error saving profile:", err) |
| paddy@139 | 990 } |
| paddy@139 | 991 err = c.AddLogin(login) |
| paddy@139 | 992 if err != nil { |
| paddy@139 | 993 t.Error("Error adding login:", err) |
| paddy@139 | 994 } |
| paddy@139 | 995 profile2 := Profile{ |
| paddy@139 | 996 ID: uuid.NewID(), |
| paddy@139 | 997 Name: "Test User", |
| paddy@139 | 998 Passphrase: "f3a4ac4f1d657b2e6e776d24213e39406d50a87a52691a2a78891425af1271d0", |
| paddy@139 | 999 Iterations: 1, |
| paddy@139 | 1000 Salt: "d82d92cfa8bfb5a08270ebbf39a3710d24b352b937fcc8959ebcb40384cc616b", |
| paddy@139 | 1001 PassphraseScheme: 1, |
| paddy@139 | 1002 Compromised: false, |
| paddy@139 | 1003 LockedUntil: time.Time{}, |
| paddy@139 | 1004 PassphraseReset: "", |
| paddy@139 | 1005 PassphraseResetCreated: time.Time{}, |
| paddy@149 | 1006 Created: time.Now().Round(time.Millisecond), |
| paddy@139 | 1007 LastSeen: time.Time{}, |
| paddy@139 | 1008 } |
| paddy@139 | 1009 login2 := Login{ |
| paddy@139 | 1010 Type: "email", |
| paddy@139 | 1011 Value: "test2@example.com", |
| paddy@139 | 1012 ProfileID: profile2.ID, |
| paddy@149 | 1013 Created: time.Now().Round(time.Millisecond), |
| paddy@139 | 1014 LastUsed: time.Time{}, |
| paddy@139 | 1015 } |
| paddy@139 | 1016 err = c.SaveProfile(profile2) |
| paddy@139 | 1017 if err != nil { |
| paddy@139 | 1018 t.Error("Error saving profile:", err) |
| paddy@139 | 1019 } |
| paddy@139 | 1020 err = c.AddLogin(login2) |
| paddy@139 | 1021 if err != nil { |
| paddy@139 | 1022 t.Error("Error adding login:", err) |
| paddy@139 | 1023 } |
| paddy@139 | 1024 router := mux.NewRouter() |
| paddy@139 | 1025 RegisterClientHandlers(router, c) |
| paddy@139 | 1026 w := httptest.NewRecorder() |
| paddy@139 | 1027 u := "https://test.auth.secondbit.org/clients/" + client.ID.String() |
| paddy@139 | 1028 r, err := http.NewRequest("GET", u, nil) |
| paddy@139 | 1029 if err != nil { |
| paddy@139 | 1030 t.Fatal("Can't build request:", err) |
| paddy@139 | 1031 } |
| paddy@139 | 1032 r.Header.Set("Content-Type", "application/json") |
| paddy@139 | 1033 r.SetBasicAuth(login.Value, "mysecurepassphrase") |
| paddy@139 | 1034 router.ServeHTTP(w, r) |
| paddy@139 | 1035 if w.Code != http.StatusOK { |
| paddy@139 | 1036 t.Errorf("Expected response code to be %d, got %d", http.StatusOK, w.Code) |
| paddy@139 | 1037 } |
| paddy@139 | 1038 t.Logf("Response: %s", w.Body.String()) |
| paddy@172 | 1039 var res Response |
| paddy@139 | 1040 err = json.Unmarshal(w.Body.Bytes(), &res) |
| paddy@139 | 1041 if err != nil { |
| paddy@139 | 1042 t.Error("Unexpected error unmarshalling response:", err) |
| paddy@139 | 1043 } |
| paddy@139 | 1044 if len(res.Clients) != 1 { |
| paddy@139 | 1045 t.Errorf("Expected %d results in response, got %d", 1, len(res.Clients)) |
| paddy@139 | 1046 } |
| paddy@139 | 1047 success, field, expectation, result := compareClients(client, res.Clients[0]) |
| paddy@139 | 1048 if !success { |
| paddy@139 | 1049 t.Errorf("Unexpected result for %s in response: expected %v, got %v", field, expectation, result) |
| paddy@139 | 1050 } |
| paddy@139 | 1051 |
| paddy@139 | 1052 // test for improperly formatted ID |
| paddy@139 | 1053 u = "https://test.auth.secondbit.org/clients/notanID" |
| paddy@139 | 1054 w = httptest.NewRecorder() |
| paddy@139 | 1055 r, err = http.NewRequest("GET", u, nil) |
| paddy@139 | 1056 if err != nil { |
| paddy@139 | 1057 t.Fatal("Can't build request:", err) |
| paddy@139 | 1058 } |
| paddy@139 | 1059 r.Header.Set("Content-Type", "application/json") |
| paddy@139 | 1060 r.SetBasicAuth(login.Value, "mysecurepassphrase") |
| paddy@139 | 1061 router.ServeHTTP(w, r) |
| paddy@139 | 1062 if w.Code != http.StatusBadRequest { |
| paddy@139 | 1063 t.Errorf("Expected response code to be %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@139 | 1064 } |
| paddy@139 | 1065 t.Logf("Response: %s", w.Body.String()) |
| paddy@172 | 1066 res = Response{} |
| paddy@139 | 1067 err = json.Unmarshal(w.Body.Bytes(), &res) |
| paddy@139 | 1068 if err != nil { |
| paddy@139 | 1069 t.Error("Unexpected error unmarshalling response:", err) |
| paddy@139 | 1070 } |
| paddy@139 | 1071 if len(res.Errors) != 1 { |
| paddy@139 | 1072 t.Errorf("Expected %d results in response, got %d", 1, len(res.Errors)) |
| paddy@139 | 1073 } |
| paddy@172 | 1074 e := RequestError{Slug: RequestErrInvalidFormat, Param: "id"} |
| paddy@139 | 1075 success, field, expectation, result = compareErrors(e, res.Errors[0]) |
| paddy@139 | 1076 if !success { |
| paddy@139 | 1077 t.Errorf("Unexpected result for %s in error response: expected %v, got %v", field, expectation, result) |
| paddy@139 | 1078 } |
| paddy@139 | 1079 |
| paddy@139 | 1080 // test for a non-existent client |
| paddy@139 | 1081 u = "https://test.auth.secondbit.org/clients/" + uuid.NewID().String() |
| paddy@139 | 1082 w = httptest.NewRecorder() |
| paddy@139 | 1083 r, err = http.NewRequest("GET", u, nil) |
| paddy@139 | 1084 if err != nil { |
| paddy@139 | 1085 t.Fatal("Can't build request:", err) |
| paddy@139 | 1086 } |
| paddy@139 | 1087 r.Header.Set("Content-Type", "application/json") |
| paddy@139 | 1088 r.SetBasicAuth(login.Value, "mysecurepassphrase") |
| paddy@139 | 1089 router.ServeHTTP(w, r) |
| paddy@139 | 1090 if w.Code != http.StatusNotFound { |
| paddy@139 | 1091 t.Errorf("Expected response code to be %d, got %d", http.StatusNotFound, w.Code) |
| paddy@139 | 1092 } |
| paddy@139 | 1093 t.Logf("Response: %s", w.Body.String()) |
| paddy@172 | 1094 res = Response{} |
| paddy@139 | 1095 err = json.Unmarshal(w.Body.Bytes(), &res) |
| paddy@139 | 1096 if err != nil { |
| paddy@139 | 1097 t.Error("Unexpected error unmarshalling response:", err) |
| paddy@139 | 1098 } |
| paddy@139 | 1099 if len(res.Errors) != 1 { |
| paddy@139 | 1100 t.Errorf("Expected %d results in response, got %d", 1, len(res.Errors)) |
| paddy@139 | 1101 } |
| paddy@172 | 1102 e = RequestError{Slug: RequestErrNotFound, Param: "id"} |
| paddy@139 | 1103 success, field, expectation, result = compareErrors(e, res.Errors[0]) |
| paddy@139 | 1104 if !success { |
| paddy@139 | 1105 t.Errorf("Unexpected result for %s in error response: expected %v, got %v", field, expectation, result) |
| paddy@139 | 1106 } |
| paddy@139 | 1107 |
| paddy@139 | 1108 // test for a wrong password |
| paddy@139 | 1109 u = "https://test.auth.secondbit.org/clients/" + client.ID.String() |
| paddy@139 | 1110 w = httptest.NewRecorder() |
| paddy@139 | 1111 r, err = http.NewRequest("GET", u, nil) |
| paddy@139 | 1112 if err != nil { |
| paddy@139 | 1113 t.Fatal("Can't build request:", err) |
| paddy@139 | 1114 } |
| paddy@139 | 1115 r.Header.Set("Content-Type", "application/json") |
| paddy@139 | 1116 r.SetBasicAuth(login.Value, "notmypassphrase") |
| paddy@139 | 1117 router.ServeHTTP(w, r) |
| paddy@139 | 1118 if w.Code != http.StatusUnauthorized { |
| paddy@139 | 1119 t.Errorf("Expected response code to be %d, got %d", http.StatusUnauthorized, w.Code) |
| paddy@139 | 1120 } |
| paddy@139 | 1121 t.Logf("Response: %s", w.Body.String()) |
| paddy@172 | 1122 res = Response{} |
| paddy@139 | 1123 err = json.Unmarshal(w.Body.Bytes(), &res) |
| paddy@139 | 1124 if err != nil { |
| paddy@139 | 1125 t.Error("Unexpected error unmarshalling response:", err) |
| paddy@139 | 1126 } |
| paddy@139 | 1127 if len(res.Errors) != 1 { |
| paddy@139 | 1128 t.Errorf("Expected %d results in response, got %d", 1, len(res.Errors)) |
| paddy@139 | 1129 } |
| paddy@172 | 1130 e = RequestError{Slug: RequestErrAccessDenied} |
| paddy@139 | 1131 success, field, expectation, result = compareErrors(e, res.Errors[0]) |
| paddy@139 | 1132 if !success { |
| paddy@139 | 1133 t.Errorf("Unexpected result for %s in error response: expected %v, got %v", field, expectation, result) |
| paddy@139 | 1134 } |
| paddy@139 | 1135 |
| paddy@139 | 1136 // test for a wrong account |
| paddy@139 | 1137 u = "https://test.auth.secondbit.org/clients/" + client.ID.String() |
| paddy@139 | 1138 w = httptest.NewRecorder() |
| paddy@139 | 1139 r, err = http.NewRequest("GET", u, nil) |
| paddy@139 | 1140 if err != nil { |
| paddy@139 | 1141 t.Fatal("Can't build request:", err) |
| paddy@139 | 1142 } |
| paddy@139 | 1143 r.Header.Set("Content-Type", "application/json") |
| paddy@139 | 1144 r.SetBasicAuth(login2.Value, "mysecurepassphrase") |
| paddy@139 | 1145 router.ServeHTTP(w, r) |
| paddy@139 | 1146 if w.Code != http.StatusOK { |
| paddy@139 | 1147 t.Errorf("Expected response code to be %d, got %d", http.StatusOK, w.Code) |
| paddy@139 | 1148 } |
| paddy@139 | 1149 t.Logf("Response: %s", w.Body.String()) |
| paddy@172 | 1150 res = Response{} |
| paddy@139 | 1151 err = json.Unmarshal(w.Body.Bytes(), &res) |
| paddy@139 | 1152 if err != nil { |
| paddy@139 | 1153 t.Error("Unexpected error unmarshalling response:", err) |
| paddy@139 | 1154 } |
| paddy@139 | 1155 if len(res.Clients) != 1 { |
| paddy@139 | 1156 t.Errorf("Expected %d results in response, got %d", 1, len(res.Clients)) |
| paddy@139 | 1157 } |
| paddy@139 | 1158 if res.Clients[0].Secret != "" { |
| paddy@139 | 1159 t.Errorf("Expected client secret to be empty, got %s", res.Clients[0].Secret) |
| paddy@139 | 1160 } |
| paddy@139 | 1161 // fill the client's secret for comparison |
| paddy@139 | 1162 res.Clients[0].Secret = client.Secret |
| paddy@139 | 1163 success, field, expectation, result = compareClients(client, res.Clients[0]) |
| paddy@139 | 1164 if !success { |
| paddy@139 | 1165 t.Errorf("Unexpected result for %s in response: expected %v, got %v", field, expectation, result) |
| paddy@139 | 1166 } |
| paddy@139 | 1167 } |
| paddy@139 | 1168 |
| paddy@128 | 1169 // BUG(paddy): We need to test the clientCredentialsValidate function. |
| paddy@131 | 1170 // BUG(paddy): We need to test the ListClientsHandler. |