auth
auth/client_test.go
Remove concept of usernames. We really have no reason to use usernames, and they're complicating things more than they need to. We're going to keep logins the same, because we want to be able to support OAuth2/OpenID/whatever logins in the future, and keeping a type associated with those logins is probably for the best.
| 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@116 | 759 resp response |
| paddy@116 | 760 } |
| paddy@116 | 761 tests := []testStruct{ |
| paddy@116 | 762 {``, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInvalidFormat, Field: "/"}}}}, |
| paddy@116 | 763 {`{}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrMissing, Field: "/type"}, {Slug: requestErrMissing, Field: "/name"}}}}, |
| paddy@116 | 764 {`{"type":"notarealtype"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInvalidValue, Field: "/type"}, {Slug: requestErrMissing, Field: "/name"}}}}, |
| paddy@116 | 765 {`{"type":"notarealtype","name":"myreallylongnameislongerthatthemaximumnamelength"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInvalidValue, Field: "/type"}, {Slug: requestErrOverflow, Field: "/name"}}}}, |
| paddy@116 | 766 {`{"type":"notarealtype","name":"a"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInvalidValue, Field: "/type"}, {Slug: requestErrInsufficient, Field: "/name"}}}}, |
| paddy@116 | 767 {`{"type":"public"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrMissing, Field: "/name"}}}}, |
| paddy@116 | 768 {`{"type":"public","name":"myreallylongnameislongerthatthemaximumnamelength"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrOverflow, Field: "/name"}}}}, |
| paddy@116 | 769 {`{"type":"public","name":"a"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInsufficient, Field: "/name"}}}}, |
| paddy@116 | 770 {`{"name":"My Client"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrMissing, Field: "/type"}}}}, |
| paddy@116 | 771 {`{"type":"notarealtype","name":"My Client"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInvalidValue, Field: "/type"}}}}, |
| paddy@116 | 772 {`{"type":"public","name":"My Client"}`, http.StatusCreated, response{Clients: []Client{{Name: "My Client", OwnerID: profile.ID, Type: "public"}}}}, |
| paddy@116 | 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@116 | 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@116 | 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@126 | 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@116 | 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@139 | 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@139 | 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@139 | 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@139 | 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@139 | 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@139 | 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@139 | 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@139 | 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@139 | 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@139 | 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@139 | 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@139 | 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@139 | 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. |