auth

Paddy 2015-01-14 Parent:5bd46746b809 Child:e000b1c24fc0

115:fa8ee6a4507c Go to Latest

auth/client_test.go

Turn AddEndpoint into AddEndpoints. Because one is a special case of many, it makes sense to be able to add multiple endpoints in a single call to the database. So we've converted the AddEndpoint method into an AddEndpoints method and updated our tests appropriately. We also filled in the errors when creating a client through the API, and moved things around to optimize for the maximum number of errors returned in a single call.

History
paddy@31 1 package auth
paddy@31 2
paddy@31 3 import (
paddy@113 4 "bytes"
paddy@39 5 "fmt"
paddy@113 6 "io/ioutil"
paddy@113 7 "net/http"
paddy@113 8 "net/http/httptest"
paddy@41 9 "net/url"
paddy@82 10 "sort"
paddy@113 11 "strings"
paddy@31 12 "testing"
paddy@41 13 "time"
paddy@31 14
paddy@107 15 "code.secondbit.org/uuid.hg"
paddy@31 16 )
paddy@31 17
paddy@39 18 const (
paddy@39 19 clientChangeSecret = 1 << iota
paddy@39 20 clientChangeOwnerID
paddy@39 21 clientChangeName
paddy@39 22 clientChangeLogo
paddy@39 23 clientChangeWebsite
paddy@39 24 )
paddy@39 25
paddy@57 26 var clientStores = []clientStore{NewMemstore()}
paddy@31 27
paddy@33 28 func compareClients(client1, client2 Client) (success bool, field string, val1, val2 interface{}) {
paddy@33 29 if !client1.ID.Equal(client2.ID) {
paddy@33 30 return false, "ID", client1.ID, client2.ID
paddy@33 31 }
paddy@33 32 if client1.Secret != client2.Secret {
paddy@33 33 return false, "secret", client1.Secret, client2.Secret
paddy@33 34 }
paddy@33 35 if !client1.OwnerID.Equal(client2.OwnerID) {
paddy@33 36 return false, "owner ID", client1.OwnerID, client2.OwnerID
paddy@33 37 }
paddy@33 38 if client1.Name != client2.Name {
paddy@33 39 return false, "name", client1.Name, client2.Name
paddy@33 40 }
paddy@33 41 if client1.Logo != client2.Logo {
paddy@33 42 return false, "logo", client1.Logo, client2.Logo
paddy@33 43 }
paddy@33 44 if client1.Website != client2.Website {
paddy@33 45 return false, "website", client1.Website, client2.Website
paddy@33 46 }
paddy@41 47 if client1.Type != client2.Type {
paddy@41 48 return false, "type", client1.Type, client2.Type
paddy@41 49 }
paddy@41 50 return true, "", nil, nil
paddy@41 51 }
paddy@41 52
paddy@41 53 func compareEndpoints(endpoint1, endpoint2 Endpoint) (success bool, field string, val1, val2 interface{}) {
paddy@41 54 if !endpoint1.ID.Equal(endpoint2.ID) {
paddy@41 55 return false, "ID", endpoint1.ID, endpoint2.ID
paddy@41 56 }
paddy@41 57 if !endpoint1.ClientID.Equal(endpoint2.ClientID) {
paddy@41 58 return false, "OwnerID", endpoint1.ClientID, endpoint2.ClientID
paddy@41 59 }
paddy@41 60 if !endpoint1.Added.Equal(endpoint2.Added) {
paddy@41 61 return false, "Added", endpoint1.Added, endpoint2.Added
paddy@41 62 }
paddy@41 63 if endpoint1.URI.String() != endpoint2.URI.String() {
paddy@41 64 return false, "URI", endpoint1.URI, endpoint2.URI
paddy@41 65 }
paddy@33 66 return true, "", nil, nil
paddy@33 67 }
paddy@33 68
paddy@31 69 func TestClientStoreSuccess(t *testing.T) {
paddy@36 70 t.Parallel()
paddy@31 71 client := Client{
paddy@41 72 ID: uuid.NewID(),
paddy@41 73 Secret: "secret",
paddy@41 74 OwnerID: uuid.NewID(),
paddy@41 75 Name: "name",
paddy@41 76 Logo: "logo",
paddy@41 77 Website: "website",
paddy@31 78 }
paddy@31 79 for _, store := range clientStores {
paddy@57 80 err := store.saveClient(client)
paddy@31 81 if err != nil {
paddy@41 82 t.Fatalf("Error saving client to %T: %s", store, err)
paddy@31 83 }
paddy@57 84 err = store.saveClient(client)
paddy@33 85 if err != ErrClientAlreadyExists {
paddy@41 86 t.Fatalf("Expected ErrClientAlreadyExists, got %v from %T", err, store)
paddy@33 87 }
paddy@57 88 retrieved, err := store.getClient(client.ID)
paddy@31 89 if err != nil {
paddy@41 90 t.Fatalf("Error retrieving client from %T: %s", store, err)
paddy@31 91 }
paddy@33 92 success, field, expectation, result := compareClients(client, retrieved)
paddy@33 93 if !success {
paddy@41 94 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@33 95 }
paddy@57 96 clients, err := store.listClientsByOwner(client.OwnerID, 25, 0)
paddy@31 97 if err != nil {
paddy@41 98 t.Fatalf("Error retrieving clients by owner from %T: %s", store, err)
paddy@31 99 }
paddy@31 100 if len(clients) != 1 {
paddy@41 101 t.Fatalf("Expected 1 client in response from %T, got %+v", store, clients)
paddy@31 102 }
paddy@33 103 success, field, expectation, result = compareClients(client, clients[0])
paddy@33 104 if !success {
paddy@41 105 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@33 106 }
paddy@57 107 err = store.deleteClient(client.ID)
paddy@31 108 if err != nil {
paddy@41 109 t.Fatalf("Error deleting client from %T: %s", store, err)
paddy@31 110 }
paddy@57 111 err = store.deleteClient(client.ID)
paddy@33 112 if err != ErrClientNotFound {
paddy@41 113 t.Fatalf("Expected ErrClientNotFound, got %s from %T", err, store)
paddy@33 114 }
paddy@57 115 retrieved, err = store.getClient(client.ID)
paddy@31 116 if err != ErrClientNotFound {
paddy@41 117 t.Fatalf("Expected ErrClientNotFound from %T, got %+v and %s", store, retrieved, err)
paddy@31 118 }
paddy@57 119 clients, err = store.listClientsByOwner(client.OwnerID, 25, 0)
paddy@31 120 if err != nil {
paddy@41 121 t.Fatalf("Error listing clients by owner from %T: %s", store, err)
paddy@31 122 }
paddy@31 123 if len(clients) != 0 {
paddy@41 124 t.Fatalf("Expected 0 clients in response from %T, got %+v", store, clients)
paddy@41 125 }
paddy@41 126 }
paddy@41 127 }
paddy@41 128
paddy@41 129 func TestEndpointStoreSuccess(t *testing.T) {
paddy@41 130 t.Parallel()
paddy@41 131 client := Client{
paddy@41 132 ID: uuid.NewID(),
paddy@41 133 Secret: "secret",
paddy@41 134 OwnerID: uuid.NewID(),
paddy@41 135 Name: "name",
paddy@41 136 Logo: "logo",
paddy@41 137 Website: "website",
paddy@41 138 }
paddy@41 139 uri1, _ := url.Parse("https://www.example.com/")
paddy@41 140 uri2, _ := url.Parse("https://www.example.com/my/full/path")
paddy@41 141 endpoint1 := Endpoint{
paddy@41 142 ID: uuid.NewID(),
paddy@41 143 ClientID: client.ID,
paddy@41 144 Added: time.Now(),
paddy@41 145 URI: *uri1,
paddy@41 146 }
paddy@41 147 endpoint2 := Endpoint{
paddy@41 148 ID: uuid.NewID(),
paddy@41 149 ClientID: client.ID,
paddy@41 150 Added: time.Now(),
paddy@41 151 URI: *uri2,
paddy@41 152 }
paddy@41 153 for _, store := range clientStores {
paddy@57 154 err := store.saveClient(client)
paddy@41 155 if err != nil {
paddy@41 156 t.Fatalf("Error saving client to %T: %s", store, err)
paddy@41 157 }
paddy@115 158 err = store.addEndpoints(client.ID, []Endpoint{endpoint1})
paddy@41 159 if err != nil {
paddy@41 160 t.Fatalf("Error adding endpoint to client in %T: %s", store, err)
paddy@41 161 }
paddy@57 162 endpoints, err := store.listEndpoints(client.ID, 10, 0)
paddy@41 163 if err != nil {
paddy@41 164 t.Fatalf("Error retrieving endpoints from %T: %s", store, err)
paddy@41 165 }
paddy@41 166 if len(endpoints) != 1 {
paddy@41 167 t.Fatalf("Expected %d endpoints, got %+v from %T", 1, endpoints, store)
paddy@41 168 }
paddy@41 169 success, field, expectation, result := compareEndpoints(endpoint1, endpoints[0])
paddy@41 170 if !success {
paddy@41 171 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@41 172 }
paddy@115 173 err = store.addEndpoints(client.ID, []Endpoint{endpoint2})
paddy@41 174 if err != nil {
paddy@41 175 t.Fatalf("Error adding endpoint to client in %T: %s", store, err)
paddy@41 176 }
paddy@57 177 endpoints, err = store.listEndpoints(client.ID, 10, 0)
paddy@41 178 if err != nil {
paddy@41 179 t.Fatalf("Error retrieving endpoints from %T: %s", store, err)
paddy@41 180 }
paddy@41 181 if len(endpoints) != 2 {
paddy@41 182 t.Fatalf("Expected %d endpoints, got %+v from %T", 2, endpoints, store)
paddy@41 183 }
paddy@41 184 sortedEnd := sortedEndpoints(endpoints)
paddy@41 185 sort.Sort(sortedEnd)
paddy@41 186 endpoints = []Endpoint(sortedEnd)
paddy@41 187 success, field, expectation, result = compareEndpoints(endpoint1, endpoints[0])
paddy@41 188 if !success {
paddy@41 189 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@41 190 }
paddy@41 191 success, field, expectation, result = compareEndpoints(endpoint2, endpoints[1])
paddy@41 192 if !success {
paddy@41 193 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@41 194 }
paddy@57 195 err = store.removeEndpoint(client.ID, endpoint1.ID)
paddy@41 196 if err != nil {
paddy@41 197 t.Fatalf("Error removing endpoint from client in %T: %s", store, err)
paddy@41 198 }
paddy@57 199 endpoints, err = store.listEndpoints(client.ID, 10, 0)
paddy@41 200 if err != nil {
paddy@41 201 t.Fatalf("Error listing endpoints in %T: %s", store, err)
paddy@41 202 }
paddy@41 203 if len(endpoints) != 1 {
paddy@41 204 t.Fatalf("Expected %d endpoints, got %+v from %T", 1, endpoints, store)
paddy@41 205 }
paddy@41 206 success, field, expectation, result = compareEndpoints(endpoint2, endpoints[0])
paddy@41 207 if !success {
paddy@41 208 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@41 209 }
paddy@57 210 err = store.removeEndpoint(client.ID, endpoint2.ID)
paddy@41 211 if err != nil {
paddy@41 212 t.Fatalf("Error removing endpoint from client in %T: %s", store, err)
paddy@41 213 }
paddy@57 214 endpoints, err = store.listEndpoints(client.ID, 10, 0)
paddy@41 215 if err != nil {
paddy@41 216 t.Fatalf("Error listing endpoints in %T: %s", store, err)
paddy@41 217 }
paddy@41 218 if len(endpoints) != 0 {
paddy@41 219 t.Fatalf("Expected %d endpoints, got %+v from %T", 0, endpoints, store)
paddy@31 220 }
paddy@31 221 }
paddy@31 222 }
paddy@39 223
paddy@39 224 func TestClientUpdates(t *testing.T) {
paddy@39 225 t.Parallel()
paddy@41 226 variations := 1 << 5
paddy@39 227 client := Client{
paddy@41 228 ID: uuid.NewID(),
paddy@41 229 Secret: "secret",
paddy@41 230 OwnerID: uuid.NewID(),
paddy@41 231 Name: "name",
paddy@41 232 Logo: "logo",
paddy@41 233 Website: "website",
paddy@39 234 }
paddy@39 235 for i := 0; i < variations; i++ {
paddy@41 236 var secret, name, logo, website string
paddy@39 237 change := ClientChange{}
paddy@39 238 expectation := client
paddy@39 239 result := client
paddy@39 240 if i&clientChangeSecret != 0 {
paddy@39 241 secret = fmt.Sprintf("secret-%d", i)
paddy@39 242 change.Secret = &secret
paddy@39 243 expectation.Secret = secret
paddy@39 244 }
paddy@39 245 if i&clientChangeOwnerID != 0 {
paddy@39 246 change.OwnerID = uuid.NewID()
paddy@39 247 expectation.OwnerID = change.OwnerID
paddy@39 248 }
paddy@39 249 if i&clientChangeName != 0 {
paddy@39 250 name = fmt.Sprintf("name-%d", i)
paddy@39 251 change.Name = &name
paddy@39 252 expectation.Name = name
paddy@39 253 }
paddy@39 254 if i&clientChangeLogo != 0 {
paddy@39 255 logo = fmt.Sprintf("logo-%d", i)
paddy@39 256 change.Logo = &logo
paddy@39 257 expectation.Logo = logo
paddy@39 258 }
paddy@39 259 if i&clientChangeWebsite != 0 {
paddy@39 260 website = fmt.Sprintf("website-%d", i)
paddy@39 261 change.Website = &website
paddy@39 262 expectation.Website = website
paddy@39 263 }
paddy@39 264 result.ApplyChange(change)
paddy@39 265 match, field, expected, got := compareClients(expectation, result)
paddy@39 266 if !match {
paddy@41 267 t.Fatalf("Expected field `%s` to be `%v`, got `%v`", field, expected, got)
paddy@39 268 }
paddy@39 269 for _, store := range clientStores {
paddy@57 270 err := store.saveClient(client)
paddy@39 271 if err != nil {
paddy@41 272 t.Fatalf("Error saving client in %T: %s", store, err)
paddy@39 273 }
paddy@57 274 err = store.updateClient(client.ID, change)
paddy@39 275 if err != nil {
paddy@41 276 t.Fatalf("Error updating client in %T: %s", store, err)
paddy@39 277 }
paddy@57 278 retrieved, err := store.getClient(client.ID)
paddy@39 279 if err != nil {
paddy@41 280 t.Fatalf("Error getting profile from %T: %s", store, err)
paddy@39 281 }
paddy@39 282 match, field, expected, got = compareClients(expectation, retrieved)
paddy@39 283 if !match {
paddy@41 284 t.Fatalf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store)
paddy@39 285 }
paddy@57 286 err = store.deleteClient(client.ID)
paddy@39 287 if err != nil {
paddy@41 288 t.Fatalf("Error deleting client from %T: %s", store, err)
paddy@39 289 }
paddy@57 290 err = store.updateClient(client.ID, change)
paddy@39 291 if err != ErrClientNotFound {
paddy@41 292 t.Fatalf("Expected ErrClientNotFound, got %v from %T", err, store)
paddy@39 293 }
paddy@39 294 }
paddy@39 295 }
paddy@39 296 }
paddy@41 297
paddy@41 298 func TestClientEndpointChecks(t *testing.T) {
paddy@41 299 t.Parallel()
paddy@41 300 client := Client{
paddy@41 301 ID: uuid.NewID(),
paddy@41 302 Secret: "secret",
paddy@41 303 OwnerID: uuid.NewID(),
paddy@41 304 Name: "name",
paddy@41 305 Logo: "logo",
paddy@41 306 Website: "website",
paddy@41 307 }
paddy@41 308 uri1, _ := url.Parse("https://www.example.com/first")
paddy@41 309 uri2, _ := url.Parse("https://www.example.com/my/full/path")
paddy@41 310 endpoint1 := Endpoint{
paddy@41 311 ID: uuid.NewID(),
paddy@41 312 ClientID: client.ID,
paddy@41 313 Added: time.Now(),
paddy@41 314 URI: *uri1,
paddy@41 315 }
paddy@41 316 endpoint2 := Endpoint{
paddy@41 317 ID: uuid.NewID(),
paddy@41 318 ClientID: client.ID,
paddy@41 319 Added: time.Now(),
paddy@41 320 URI: *uri2,
paddy@41 321 }
paddy@41 322 candidates := map[string]bool{
paddy@41 323 "https://www.example.com/": false,
paddy@41 324 "https://www.example.com/first": true,
paddy@58 325 "https://www.example.com/first/extra/path": false,
paddy@41 326 "https://www.example.com/my": false,
paddy@41 327 "https://www.example.com/my/full/path": true,
paddy@41 328 }
paddy@41 329 for _, store := range clientStores {
paddy@57 330 err := store.saveClient(client)
paddy@41 331 if err != nil {
paddy@41 332 t.Fatalf("Error saving client in %T: %s", store, err)
paddy@41 333 }
paddy@115 334 err = store.addEndpoints(client.ID, []Endpoint{endpoint1})
paddy@41 335 if err != nil {
paddy@41 336 t.Fatalf("Error saving endpoint in %T: %s", store, err)
paddy@41 337 }
paddy@115 338 err = store.addEndpoints(client.ID, []Endpoint{endpoint2})
paddy@41 339 if err != nil {
paddy@41 340 t.Fatalf("Error saving endpoint in %T: %s", store, err)
paddy@41 341 }
paddy@41 342 for candidate, expectation := range candidates {
paddy@58 343 result, err := store.checkEndpoint(client.ID, candidate)
paddy@54 344 if err != nil {
paddy@54 345 t.Fatalf("Error checking endpoint %s in %T: %s", candidate, store, err)
paddy@54 346 }
paddy@54 347 if result != expectation {
paddy@54 348 expectStr := "no"
paddy@54 349 resultStr := "a"
paddy@54 350 if expectation {
paddy@54 351 expectStr = "a"
paddy@54 352 resultStr = "no"
paddy@54 353 }
paddy@54 354 t.Errorf("Expected %s match for %s in %T, got %s match", expectStr, candidate, store, resultStr)
paddy@54 355 }
paddy@54 356 }
paddy@54 357 }
paddy@54 358 }
paddy@54 359
paddy@54 360 func TestClientEndpointChecksStrict(t *testing.T) {
paddy@54 361 t.Parallel()
paddy@54 362 client := Client{
paddy@54 363 ID: uuid.NewID(),
paddy@54 364 Secret: "secret",
paddy@54 365 OwnerID: uuid.NewID(),
paddy@54 366 Name: "name",
paddy@54 367 Logo: "logo",
paddy@54 368 Website: "website",
paddy@54 369 }
paddy@54 370 uri1, _ := url.Parse("https://www.example.com/first")
paddy@54 371 uri2, _ := url.Parse("https://www.example.com/my/full/path")
paddy@54 372 endpoint1 := Endpoint{
paddy@54 373 ID: uuid.NewID(),
paddy@54 374 ClientID: client.ID,
paddy@54 375 Added: time.Now(),
paddy@54 376 URI: *uri1,
paddy@54 377 }
paddy@54 378 endpoint2 := Endpoint{
paddy@54 379 ID: uuid.NewID(),
paddy@54 380 ClientID: client.ID,
paddy@54 381 Added: time.Now(),
paddy@54 382 URI: *uri2,
paddy@54 383 }
paddy@54 384 candidates := map[string]bool{
paddy@54 385 "https://www.example.com/": false,
paddy@54 386 "https://www.example.com/first": true,
paddy@54 387 "https://www.example.com/first/extra/path": false,
paddy@54 388 "https://www.example.com/my": false,
paddy@54 389 "https://www.example.com/my/full/path": true,
paddy@54 390 }
paddy@54 391 for _, store := range clientStores {
paddy@57 392 err := store.saveClient(client)
paddy@54 393 if err != nil {
paddy@54 394 t.Fatalf("Error saving client in %T: %s", store, err)
paddy@54 395 }
paddy@115 396 err = store.addEndpoints(client.ID, []Endpoint{endpoint1})
paddy@54 397 if err != nil {
paddy@54 398 t.Fatalf("Error saving endpoint in %T: %s", store, err)
paddy@54 399 }
paddy@115 400 err = store.addEndpoints(client.ID, []Endpoint{endpoint2})
paddy@54 401 if err != nil {
paddy@54 402 t.Fatalf("Error saving endpoint in %T: %s", store, err)
paddy@54 403 }
paddy@54 404 for candidate, expectation := range candidates {
paddy@58 405 result, err := store.checkEndpoint(client.ID, candidate)
paddy@41 406 if err != nil {
paddy@41 407 t.Fatalf("Error checking endpoint %s in %T: %s", candidate, store, err)
paddy@41 408 }
paddy@41 409 if result != expectation {
paddy@41 410 expectStr := "no"
paddy@41 411 resultStr := "a"
paddy@41 412 if expectation {
paddy@41 413 expectStr = "a"
paddy@41 414 resultStr = "no"
paddy@41 415 }
paddy@41 416 t.Errorf("Expected %s match for %s in %T, got %s match", expectStr, candidate, store, resultStr)
paddy@41 417 }
paddy@41 418 }
paddy@41 419 }
paddy@41 420 }
paddy@43 421
paddy@43 422 func TestClientChangeValidation(t *testing.T) {
paddy@43 423 t.Parallel()
paddy@43 424 change := ClientChange{}
paddy@43 425 if err := change.Validate(); err != ErrEmptyChange {
paddy@43 426 t.Errorf("Expected %s to give an error of %s, gave %s", "empty change", ErrEmptyChange, err)
paddy@43 427 }
paddy@43 428 names := map[string]error{
paddy@43 429 "a": ErrClientNameTooShort,
paddy@43 430 "ab": nil,
paddy@43 431 "abc": nil,
paddy@43 432 "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopq": ErrClientNameTooLong,
paddy@43 433 }
paddy@43 434 for name, expectation := range names {
paddy@43 435 change = ClientChange{Name: &name}
paddy@43 436 if err := change.Validate(); err != expectation {
paddy@43 437 t.Errorf("Expected %s to give an error of %s, gave %s", name, expectation, err)
paddy@43 438 }
paddy@43 439 }
paddy@43 440 longPath := ""
paddy@43 441 for i := 0; i < 1025; i++ {
paddy@43 442 longPath = fmt.Sprintf("%s%d", longPath, i)
paddy@43 443 }
paddy@43 444 logos := map[string]error{
paddy@43 445 "https://www.example.com/" + longPath: ErrClientLogoTooLong,
paddy@43 446 "https://www.example.com/ab": nil,
paddy@43 447 "www.example.com/ab": ErrClientLogoNotURL,
paddy@43 448 "test": ErrClientLogoNotURL,
paddy@43 449 "": nil,
paddy@43 450 }
paddy@43 451 for logo, expectation := range logos {
paddy@43 452 change = ClientChange{Logo: &logo}
paddy@43 453 if err := change.Validate(); err != expectation {
paddy@43 454 t.Errorf("Expected %s to give an error of %s, gave %s", logo, expectation, err)
paddy@43 455 }
paddy@43 456 }
paddy@43 457 websites := map[string]error{
paddy@43 458 "https://www.example.com/" + longPath: ErrClientWebsiteTooLong,
paddy@43 459 "https://www.example.com/ab": nil,
paddy@43 460 "www.example.com/ab": ErrClientWebsiteNotURL,
paddy@43 461 "test": ErrClientWebsiteNotURL,
paddy@43 462 "": nil,
paddy@43 463 }
paddy@43 464 for website, expectation := range websites {
paddy@43 465 change = ClientChange{Website: &website}
paddy@43 466 if err := change.Validate(); err != expectation {
paddy@43 467 t.Errorf("Expected %s to give an error of %s, gave %s", website, expectation, err)
paddy@43 468 }
paddy@43 469 }
paddy@43 470 }
paddy@113 471
paddy@113 472 func TestVerifyClient(t *testing.T) {
paddy@113 473 t.Parallel()
paddy@113 474 memstore := NewMemstore()
paddy@113 475 context := Context{
paddy@113 476 clients: memstore,
paddy@113 477 }
paddy@113 478 client := Client{
paddy@113 479 ID: uuid.NewID(),
paddy@113 480 Secret: "super secret!",
paddy@113 481 OwnerID: uuid.NewID(),
paddy@113 482 Name: "My test client",
paddy@113 483 Logo: "https://secondbit.org/logo.png",
paddy@113 484 Website: "https://secondbit.org/",
paddy@113 485 Type: "confidential",
paddy@113 486 }
paddy@113 487 err := context.SaveClient(client)
paddy@113 488 if err != nil {
paddy@113 489 t.Fatal("Could not save client:", err)
paddy@113 490 }
paddy@113 491 publicClient := Client{
paddy@113 492 ID: uuid.NewID(),
paddy@113 493 Secret: "",
paddy@113 494 OwnerID: uuid.NewID(),
paddy@113 495 Name: "A public client",
paddy@113 496 Logo: "https://secondbit.org/logo.png",
paddy@113 497 Website: "https://secondbit.org/",
paddy@113 498 Type: "public",
paddy@113 499 }
paddy@113 500 err = context.SaveClient(publicClient)
paddy@113 501 if err != nil {
paddy@113 502 t.Fatal("Could not save client:", err)
paddy@113 503 }
paddy@113 504
paddy@113 505 // verifyClient with no auth header, no public clients
paddy@113 506 w := httptest.NewRecorder()
paddy@113 507 r, err := http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 508 if err != nil {
paddy@113 509 t.Fatal("Can't build request:", err)
paddy@113 510 }
paddy@113 511 resp, success := verifyClient(w, r, false, context)
paddy@113 512 if success {
paddy@113 513 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 514 }
paddy@113 515 if resp != nil {
paddy@113 516 t.Error("Expected nil client ID, got", resp)
paddy@113 517 }
paddy@113 518 if w.Code != http.StatusBadRequest {
paddy@113 519 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 520 }
paddy@113 521 expectedBody := `{"error":"unauthorized_client"}`
paddy@113 522 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 523 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 524 }
paddy@113 525
paddy@113 526 // verifyClient with no auth header, public clients, empty client_id
paddy@113 527 w = httptest.NewRecorder()
paddy@113 528 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 529 if err != nil {
paddy@113 530 t.Fatal("Can't build request:", err)
paddy@113 531 }
paddy@113 532 resp, success = verifyClient(w, r, true, context)
paddy@113 533 if success {
paddy@113 534 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 535 }
paddy@113 536 if resp != nil {
paddy@113 537 t.Error("Expected nil client ID, got", resp)
paddy@113 538 }
paddy@113 539 if w.Code != http.StatusUnauthorized {
paddy@113 540 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 541 }
paddy@113 542 expectedBody = `{"error":"invalid_client"}`
paddy@113 543 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 544 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 545 }
paddy@113 546
paddy@113 547 // verifyClient with auth header, no public clients, empty client id
paddy@113 548 w = httptest.NewRecorder()
paddy@113 549 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 550 if err != nil {
paddy@113 551 t.Fatal("Can't build request:", err)
paddy@113 552 }
paddy@113 553 r.SetBasicAuth("", "no client ID set")
paddy@113 554 resp, success = verifyClient(w, r, false, context)
paddy@113 555 if success {
paddy@113 556 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 557 }
paddy@113 558 if resp != nil {
paddy@113 559 t.Error("Expected nil client ID, got", resp)
paddy@113 560 }
paddy@113 561 if w.Code != http.StatusUnauthorized {
paddy@113 562 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 563 }
paddy@113 564 expectedBody = `{"error":"invalid_client"}`
paddy@113 565 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 566 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 567 }
paddy@113 568 if w.Header().Get("WWW-Authenticate") != "Basic" {
paddy@113 569 t.Errorf(`Expected header WWW-Authenticate to be set to "Basic", got "%s"`, w.Header().Get("WWW-Authenticate"))
paddy@113 570 }
paddy@113 571
paddy@113 572 // verifyClient with auth header, public clients, empty client id
paddy@113 573 w = httptest.NewRecorder()
paddy@113 574 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 575 if err != nil {
paddy@113 576 t.Fatal("Can't build request:", err)
paddy@113 577 }
paddy@113 578 r.SetBasicAuth("", "no client ID set")
paddy@113 579 resp, success = verifyClient(w, r, true, context)
paddy@113 580 if success {
paddy@113 581 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 582 }
paddy@113 583 if resp != nil {
paddy@113 584 t.Error("Expected nil client ID, got", resp)
paddy@113 585 }
paddy@113 586 if w.Code != http.StatusUnauthorized {
paddy@113 587 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 588 }
paddy@113 589 expectedBody = `{"error":"invalid_client"}`
paddy@113 590 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 591 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 592 }
paddy@113 593 if w.Header().Get("WWW-Authenticate") != "Basic" {
paddy@113 594 t.Errorf(`Expected header WWW-Authenticate to be set to "Basic", got "%s"`, w.Header().Get("WWW-Authenticate"))
paddy@113 595 }
paddy@113 596
paddy@113 597 // verifyClient with auth header, no public clients, invalid client ID
paddy@113 598 w = httptest.NewRecorder()
paddy@113 599 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 600 if err != nil {
paddy@113 601 t.Fatal("Can't build request:", err)
paddy@113 602 }
paddy@113 603 r.SetBasicAuth("not an actual id", "invalid client ID set")
paddy@113 604 resp, success = verifyClient(w, r, false, context)
paddy@113 605 if success {
paddy@113 606 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 607 }
paddy@113 608 if resp != nil {
paddy@113 609 t.Error("Expected nil client ID, got", resp)
paddy@113 610 }
paddy@113 611 if w.Code != http.StatusUnauthorized {
paddy@113 612 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 613 }
paddy@113 614 expectedBody = `{"error":"invalid_client"}`
paddy@113 615 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 616 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 617 }
paddy@113 618 if w.Header().Get("WWW-Authenticate") != "Basic" {
paddy@113 619 t.Errorf(`Expected header WWW-Authenticate to be set to "Basic", got "%s"`, w.Header().Get("WWW-Authenticate"))
paddy@113 620 }
paddy@113 621
paddy@113 622 // verifyClient with auth header, public clients, invalid client ID
paddy@113 623 w = httptest.NewRecorder()
paddy@113 624 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 625 if err != nil {
paddy@113 626 t.Fatal("Can't build request:", err)
paddy@113 627 }
paddy@113 628 r.SetBasicAuth("not an actual id", "invalid client ID set")
paddy@113 629 resp, success = verifyClient(w, r, true, context)
paddy@113 630 if success {
paddy@113 631 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 632 }
paddy@113 633 if resp != nil {
paddy@113 634 t.Error("Expected nil client ID, got", resp)
paddy@113 635 }
paddy@113 636 if w.Code != http.StatusUnauthorized {
paddy@113 637 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 638 }
paddy@113 639 expectedBody = `{"error":"invalid_client"}`
paddy@113 640 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 641 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 642 }
paddy@113 643 if w.Header().Get("WWW-Authenticate") != "Basic" {
paddy@113 644 t.Errorf(`Expected header WWW-Authenticate to be set to "Basic", got "%s"`, w.Header().Get("WWW-Authenticate"))
paddy@113 645 }
paddy@113 646
paddy@113 647 // verifyClient with auth header, no public clients, client ID valid but not in clientStore
paddy@113 648 w = httptest.NewRecorder()
paddy@113 649 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 650 if err != nil {
paddy@113 651 t.Fatal("Can't build request:", err)
paddy@113 652 }
paddy@113 653 r.SetBasicAuth(uuid.NewID().String(), "non existent client ID set")
paddy@113 654 resp, success = verifyClient(w, r, false, context)
paddy@113 655 if success {
paddy@113 656 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 657 }
paddy@113 658 if resp != nil {
paddy@113 659 t.Error("Expected nil client ID, got", resp)
paddy@113 660 }
paddy@113 661 if w.Code != http.StatusUnauthorized {
paddy@113 662 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 663 }
paddy@113 664 expectedBody = `{"error":"invalid_client"}`
paddy@113 665 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 666 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 667 }
paddy@113 668 if w.Header().Get("WWW-Authenticate") != "Basic" {
paddy@113 669 t.Errorf(`Expected header WWW-Authenticate to be set to "Basic", got "%s"`, w.Header().Get("WWW-Authenticate"))
paddy@113 670 }
paddy@113 671
paddy@113 672 // verifyClient with auth header, public clients, client ID valid but not in clientStore
paddy@113 673 w = httptest.NewRecorder()
paddy@113 674 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 675 if err != nil {
paddy@113 676 t.Fatal("Can't build request:", err)
paddy@113 677 }
paddy@113 678 r.SetBasicAuth(uuid.NewID().String(), "non existent client ID set")
paddy@113 679 resp, success = verifyClient(w, r, true, context)
paddy@113 680 if success {
paddy@113 681 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 682 }
paddy@113 683 if resp != nil {
paddy@113 684 t.Error("Expected nil client ID, got", resp)
paddy@113 685 }
paddy@113 686 if w.Code != http.StatusUnauthorized {
paddy@113 687 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 688 }
paddy@113 689 expectedBody = `{"error":"invalid_client"}`
paddy@113 690 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 691 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 692 }
paddy@113 693 if w.Header().Get("WWW-Authenticate") != "Basic" {
paddy@113 694 t.Errorf(`Expected header WWW-Authenticate to be set to "Basic", got "%s"`, w.Header().Get("WWW-Authenticate"))
paddy@113 695 }
paddy@113 696
paddy@113 697 // verifyClient with auth header, no public clients, client secret wrong
paddy@113 698 w = httptest.NewRecorder()
paddy@113 699 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 700 if err != nil {
paddy@113 701 t.Fatal("Can't build request:", err)
paddy@113 702 }
paddy@113 703 r.SetBasicAuth(client.ID.String(), "not actually the secret")
paddy@113 704 resp, success = verifyClient(w, r, false, context)
paddy@113 705 if success {
paddy@113 706 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 707 }
paddy@113 708 if resp != nil {
paddy@113 709 t.Error("Expected nil client ID, got", resp)
paddy@113 710 }
paddy@113 711 if w.Code != http.StatusUnauthorized {
paddy@113 712 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 713 }
paddy@113 714 expectedBody = `{"error":"invalid_client"}`
paddy@113 715 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 716 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 717 }
paddy@113 718 if w.Header().Get("WWW-Authenticate") != "Basic" {
paddy@113 719 t.Errorf(`Expected header WWW-Authenticate to be set to "Basic", got "%s"`, w.Header().Get("WWW-Authenticate"))
paddy@113 720 }
paddy@113 721
paddy@113 722 // verifyClient with auth header, public clients, client secret wrong
paddy@113 723 w = httptest.NewRecorder()
paddy@113 724 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 725 if err != nil {
paddy@113 726 t.Fatal("Can't build request:", err)
paddy@113 727 }
paddy@113 728 r.SetBasicAuth(client.ID.String(), "not actually the secret")
paddy@113 729 resp, success = verifyClient(w, r, true, context)
paddy@113 730 if success {
paddy@113 731 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 732 }
paddy@113 733 if resp != nil {
paddy@113 734 t.Error("Expected nil client ID, got", resp)
paddy@113 735 }
paddy@113 736 if w.Code != http.StatusUnauthorized {
paddy@113 737 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 738 }
paddy@113 739 expectedBody = `{"error":"invalid_client"}`
paddy@113 740 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 741 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 742 }
paddy@113 743 if w.Header().Get("WWW-Authenticate") != "Basic" {
paddy@113 744 t.Errorf(`Expected header WWW-Authenticate to be set to "Basic", got "%s"`, w.Header().Get("WWW-Authenticate"))
paddy@113 745 }
paddy@113 746
paddy@113 747 // verifyClient with no auth header, public clients, invalid client_id post form value
paddy@113 748 w = httptest.NewRecorder()
paddy@113 749 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 750 if err != nil {
paddy@113 751 t.Fatal("Can't build request:", err)
paddy@113 752 }
paddy@113 753 r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
paddy@113 754 params := url.Values{}
paddy@113 755 params.Set("client_id", "not an actual id")
paddy@113 756 body := bytes.NewBufferString(params.Encode())
paddy@113 757 r.Body = ioutil.NopCloser(body)
paddy@113 758 resp, success = verifyClient(w, r, true, context)
paddy@113 759 if success {
paddy@113 760 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 761 }
paddy@113 762 if resp != nil {
paddy@113 763 t.Error("Expected nil client ID, got", resp)
paddy@113 764 }
paddy@113 765 if w.Code != http.StatusUnauthorized {
paddy@113 766 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 767 }
paddy@113 768 expectedBody = `{"error":"invalid_client"}`
paddy@113 769 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 770 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 771 }
paddy@113 772
paddy@113 773 // verifyClient with no auth header, public clients, client_id valid but not in clientStore
paddy@113 774 w = httptest.NewRecorder()
paddy@113 775 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 776 if err != nil {
paddy@113 777 t.Fatal("Can't build request:", err)
paddy@113 778 }
paddy@113 779 r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
paddy@113 780 params = url.Values{}
paddy@113 781 params.Set("client_id", uuid.NewID().String())
paddy@113 782 body = bytes.NewBufferString(params.Encode())
paddy@113 783 r.Body = ioutil.NopCloser(body)
paddy@113 784 resp, success = verifyClient(w, r, true, context)
paddy@113 785 if success {
paddy@113 786 t.Error("Expected verification to fail, but succeeded with client ID:", resp)
paddy@113 787 }
paddy@113 788 if resp != nil {
paddy@113 789 t.Error("Expected nil client ID, got", resp)
paddy@113 790 }
paddy@113 791 if w.Code != http.StatusUnauthorized {
paddy@113 792 t.Errorf("Expected status code of %d, got %d", http.StatusBadRequest, w.Code)
paddy@113 793 }
paddy@113 794 expectedBody = `{"error":"invalid_client"}`
paddy@113 795 if expectedBody != strings.TrimSpace(w.Body.String()) {
paddy@113 796 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
paddy@113 797 }
paddy@113 798
paddy@113 799 // verifyClient with auth header, public clients, valid client_id and secret
paddy@113 800 w = httptest.NewRecorder()
paddy@113 801 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 802 if err != nil {
paddy@113 803 t.Fatal("Can't build request:", err)
paddy@113 804 }
paddy@113 805 r.SetBasicAuth(client.ID.String(), client.Secret)
paddy@113 806 resp, success = verifyClient(w, r, true, context)
paddy@113 807 if !success {
paddy@113 808 t.Error("Expected verification to succeed, but it failed")
paddy@113 809 }
paddy@113 810 if !client.ID.Equal(resp) {
paddy@113 811 t.Errorf("Expected client ID to be %s, got %s", client.ID, resp)
paddy@113 812 }
paddy@113 813 if w.Code != http.StatusOK {
paddy@113 814 t.Errorf("Expected status code of %d, got %d", http.StatusOK, w.Code)
paddy@113 815 }
paddy@113 816 if w.Body.String() != "" {
paddy@113 817 t.Errorf(`Expected empty body, got "%s"`, w.Body.String())
paddy@113 818 }
paddy@113 819
paddy@113 820 // verifyClient with auth header, no public clients, valid client_id and secret
paddy@113 821 w = httptest.NewRecorder()
paddy@113 822 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 823 if err != nil {
paddy@113 824 t.Fatal("Can't build request:", err)
paddy@113 825 }
paddy@113 826 r.SetBasicAuth(client.ID.String(), client.Secret)
paddy@113 827 resp, success = verifyClient(w, r, true, context)
paddy@113 828 if !success {
paddy@113 829 t.Error("Expected verification to succeed, but it failed")
paddy@113 830 }
paddy@113 831 if !client.ID.Equal(resp) {
paddy@113 832 t.Errorf("Expected client ID to be %s, got %s", client.ID, resp)
paddy@113 833 }
paddy@113 834 if w.Code != http.StatusOK {
paddy@113 835 t.Errorf("Expected status code of %d, got %d", http.StatusOK, w.Code)
paddy@113 836 }
paddy@113 837 if w.Body.String() != "" {
paddy@113 838 t.Errorf(`Expected empty body, got "%s"`, w.Body.String())
paddy@113 839 }
paddy@113 840
paddy@113 841 // verifyClient with no auth header, public clients, valid client_id and secret
paddy@113 842 w = httptest.NewRecorder()
paddy@113 843 r, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@113 844 if err != nil {
paddy@113 845 t.Fatal("Can't build request:", err)
paddy@113 846 }
paddy@113 847 r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
paddy@113 848 params = url.Values{}
paddy@113 849 params.Set("client_id", publicClient.ID.String())
paddy@113 850 body = bytes.NewBufferString(params.Encode())
paddy@113 851 r.Body = ioutil.NopCloser(body)
paddy@113 852 resp, success = verifyClient(w, r, true, context)
paddy@113 853 if !success {
paddy@113 854 t.Error("Expected verification to succeed, but it failed")
paddy@113 855 }
paddy@113 856 if !publicClient.ID.Equal(resp) {
paddy@113 857 t.Errorf("Expected client ID to be %s, got %s", publicClient.ID, resp)
paddy@113 858 }
paddy@113 859 if w.Code != http.StatusOK {
paddy@113 860 t.Errorf("Expected status code of %d, got %d", http.StatusOK, w.Code)
paddy@113 861 }
paddy@113 862 if w.Body.String() != "" {
paddy@113 863 t.Errorf(`Expected empty body, got "%s"`, w.Body.String())
paddy@113 864 }
paddy@113 865 }