auth

Paddy 2015-01-18 Parent:e000b1c24fc0 Child:0a1e16b9c141

122:eb9842ae3ff1 Go to Latest

auth/client_test.go

Enable the implict grant flow. Add the implicit grant flow. This can't be done in a grant type, because it's not specified through the grant_type parameter, for some absurd reason. Whatever. We basically achieved this by refactoring how we respond to the authorization endpoint, keying off the "response_type" parameter.

History
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@113 7 "io/ioutil"
paddy@113 8 "net/http"
paddy@113 9 "net/http/httptest"
paddy@41 10 "net/url"
paddy@82 11 "sort"
paddy@113 12 "strings"
paddy@31 13 "testing"
paddy@41 14 "time"
paddy@31 15
paddy@107 16 "code.secondbit.org/uuid.hg"
paddy@31 17 )
paddy@31 18
paddy@39 19 const (
paddy@39 20 clientChangeSecret = 1 << iota
paddy@39 21 clientChangeOwnerID
paddy@39 22 clientChangeName
paddy@39 23 clientChangeLogo
paddy@39 24 clientChangeWebsite
paddy@39 25 )
paddy@39 26
paddy@57 27 var clientStores = []clientStore{NewMemstore()}
paddy@31 28
paddy@33 29 func compareClients(client1, client2 Client) (success bool, field string, val1, val2 interface{}) {
paddy@33 30 if !client1.ID.Equal(client2.ID) {
paddy@33 31 return false, "ID", client1.ID, client2.ID
paddy@33 32 }
paddy@33 33 if client1.Secret != client2.Secret {
paddy@33 34 return false, "secret", client1.Secret, client2.Secret
paddy@33 35 }
paddy@33 36 if !client1.OwnerID.Equal(client2.OwnerID) {
paddy@33 37 return false, "owner ID", client1.OwnerID, client2.OwnerID
paddy@33 38 }
paddy@33 39 if client1.Name != client2.Name {
paddy@33 40 return false, "name", client1.Name, client2.Name
paddy@33 41 }
paddy@33 42 if client1.Logo != client2.Logo {
paddy@33 43 return false, "logo", client1.Logo, client2.Logo
paddy@33 44 }
paddy@33 45 if client1.Website != client2.Website {
paddy@33 46 return false, "website", client1.Website, client2.Website
paddy@33 47 }
paddy@41 48 if client1.Type != client2.Type {
paddy@41 49 return false, "type", client1.Type, client2.Type
paddy@41 50 }
paddy@41 51 return true, "", nil, nil
paddy@41 52 }
paddy@41 53
paddy@41 54 func compareEndpoints(endpoint1, endpoint2 Endpoint) (success bool, field string, val1, val2 interface{}) {
paddy@41 55 if !endpoint1.ID.Equal(endpoint2.ID) {
paddy@41 56 return false, "ID", endpoint1.ID, endpoint2.ID
paddy@41 57 }
paddy@41 58 if !endpoint1.ClientID.Equal(endpoint2.ClientID) {
paddy@41 59 return false, "OwnerID", endpoint1.ClientID, endpoint2.ClientID
paddy@41 60 }
paddy@41 61 if !endpoint1.Added.Equal(endpoint2.Added) {
paddy@41 62 return false, "Added", endpoint1.Added, endpoint2.Added
paddy@41 63 }
paddy@116 64 if endpoint1.URI != endpoint2.URI {
paddy@41 65 return false, "URI", endpoint1.URI, endpoint2.URI
paddy@41 66 }
paddy@33 67 return true, "", nil, nil
paddy@33 68 }
paddy@33 69
paddy@31 70 func TestClientStoreSuccess(t *testing.T) {
paddy@36 71 t.Parallel()
paddy@31 72 client := Client{
paddy@41 73 ID: uuid.NewID(),
paddy@41 74 Secret: "secret",
paddy@41 75 OwnerID: uuid.NewID(),
paddy@41 76 Name: "name",
paddy@41 77 Logo: "logo",
paddy@41 78 Website: "website",
paddy@31 79 }
paddy@31 80 for _, store := range clientStores {
paddy@116 81 context := Context{clients: store}
paddy@116 82 err := context.SaveClient(client)
paddy@31 83 if err != nil {
paddy@41 84 t.Fatalf("Error saving client to %T: %s", store, err)
paddy@31 85 }
paddy@116 86 err = context.SaveClient(client)
paddy@33 87 if err != ErrClientAlreadyExists {
paddy@41 88 t.Fatalf("Expected ErrClientAlreadyExists, got %v from %T", err, store)
paddy@33 89 }
paddy@116 90 retrieved, err := context.GetClient(client.ID)
paddy@31 91 if err != nil {
paddy@41 92 t.Fatalf("Error retrieving client from %T: %s", store, err)
paddy@31 93 }
paddy@33 94 success, field, expectation, result := compareClients(client, retrieved)
paddy@33 95 if !success {
paddy@41 96 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@33 97 }
paddy@116 98 clients, err := context.ListClientsByOwner(client.OwnerID, 25, 0)
paddy@31 99 if err != nil {
paddy@41 100 t.Fatalf("Error retrieving clients by owner from %T: %s", store, err)
paddy@31 101 }
paddy@31 102 if len(clients) != 1 {
paddy@41 103 t.Fatalf("Expected 1 client in response from %T, got %+v", store, clients)
paddy@31 104 }
paddy@33 105 success, field, expectation, result = compareClients(client, clients[0])
paddy@33 106 if !success {
paddy@41 107 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@33 108 }
paddy@116 109 err = context.DeleteClient(client.ID)
paddy@31 110 if err != nil {
paddy@41 111 t.Fatalf("Error deleting client from %T: %s", store, err)
paddy@31 112 }
paddy@116 113 err = context.DeleteClient(client.ID)
paddy@33 114 if err != ErrClientNotFound {
paddy@41 115 t.Fatalf("Expected ErrClientNotFound, got %s from %T", err, store)
paddy@33 116 }
paddy@116 117 retrieved, err = context.GetClient(client.ID)
paddy@31 118 if err != ErrClientNotFound {
paddy@41 119 t.Fatalf("Expected ErrClientNotFound from %T, got %+v and %s", store, retrieved, err)
paddy@31 120 }
paddy@116 121 clients, err = context.ListClientsByOwner(client.OwnerID, 25, 0)
paddy@31 122 if err != nil {
paddy@41 123 t.Fatalf("Error listing clients by owner from %T: %s", store, err)
paddy@31 124 }
paddy@31 125 if len(clients) != 0 {
paddy@41 126 t.Fatalf("Expected 0 clients in response from %T, got %+v", store, clients)
paddy@41 127 }
paddy@41 128 }
paddy@41 129 }
paddy@41 130
paddy@41 131 func TestEndpointStoreSuccess(t *testing.T) {
paddy@41 132 t.Parallel()
paddy@41 133 client := Client{
paddy@41 134 ID: uuid.NewID(),
paddy@41 135 Secret: "secret",
paddy@41 136 OwnerID: uuid.NewID(),
paddy@41 137 Name: "name",
paddy@41 138 Logo: "logo",
paddy@41 139 Website: "website",
paddy@41 140 }
paddy@41 141 endpoint1 := Endpoint{
paddy@41 142 ID: uuid.NewID(),
paddy@41 143 ClientID: client.ID,
paddy@41 144 Added: time.Now(),
paddy@116 145 URI: "https://www.example.com/",
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@116 151 URI: "https://www.example.com/my/full/path",
paddy@41 152 }
paddy@41 153 for _, store := range clientStores {
paddy@116 154 context := Context{clients: store}
paddy@116 155 err := context.SaveClient(client)
paddy@41 156 if err != nil {
paddy@41 157 t.Fatalf("Error saving client to %T: %s", store, err)
paddy@41 158 }
paddy@116 159 err = context.AddEndpoints(client.ID, []Endpoint{endpoint1})
paddy@41 160 if err != nil {
paddy@41 161 t.Fatalf("Error adding endpoint to client in %T: %s", store, err)
paddy@41 162 }
paddy@116 163 endpoints, err := context.ListEndpoints(client.ID, 10, 0)
paddy@41 164 if err != nil {
paddy@41 165 t.Fatalf("Error retrieving endpoints from %T: %s", store, err)
paddy@41 166 }
paddy@41 167 if len(endpoints) != 1 {
paddy@41 168 t.Fatalf("Expected %d endpoints, got %+v from %T", 1, endpoints, store)
paddy@41 169 }
paddy@41 170 success, field, expectation, result := compareEndpoints(endpoint1, endpoints[0])
paddy@41 171 if !success {
paddy@41 172 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@41 173 }
paddy@116 174 err = context.AddEndpoints(client.ID, []Endpoint{endpoint2})
paddy@41 175 if err != nil {
paddy@41 176 t.Fatalf("Error adding endpoint to client in %T: %s", store, err)
paddy@41 177 }
paddy@116 178 endpoints, err = context.ListEndpoints(client.ID, 10, 0)
paddy@41 179 if err != nil {
paddy@41 180 t.Fatalf("Error retrieving endpoints from %T: %s", store, err)
paddy@41 181 }
paddy@41 182 if len(endpoints) != 2 {
paddy@41 183 t.Fatalf("Expected %d endpoints, got %+v from %T", 2, endpoints, store)
paddy@41 184 }
paddy@41 185 sortedEnd := sortedEndpoints(endpoints)
paddy@41 186 sort.Sort(sortedEnd)
paddy@41 187 endpoints = []Endpoint(sortedEnd)
paddy@41 188 success, field, expectation, result = compareEndpoints(endpoint1, endpoints[0])
paddy@41 189 if !success {
paddy@41 190 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@41 191 }
paddy@41 192 success, field, expectation, result = compareEndpoints(endpoint2, endpoints[1])
paddy@41 193 if !success {
paddy@41 194 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@41 195 }
paddy@116 196 err = context.RemoveEndpoint(client.ID, endpoint1.ID)
paddy@41 197 if err != nil {
paddy@41 198 t.Fatalf("Error removing endpoint from client in %T: %s", store, err)
paddy@41 199 }
paddy@116 200 endpoints, err = context.ListEndpoints(client.ID, 10, 0)
paddy@41 201 if err != nil {
paddy@41 202 t.Fatalf("Error listing endpoints in %T: %s", store, err)
paddy@41 203 }
paddy@41 204 if len(endpoints) != 1 {
paddy@41 205 t.Fatalf("Expected %d endpoints, got %+v from %T", 1, endpoints, store)
paddy@41 206 }
paddy@41 207 success, field, expectation, result = compareEndpoints(endpoint2, endpoints[0])
paddy@41 208 if !success {
paddy@41 209 t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
paddy@41 210 }
paddy@116 211 err = context.RemoveEndpoint(client.ID, endpoint2.ID)
paddy@41 212 if err != nil {
paddy@41 213 t.Fatalf("Error removing endpoint from client in %T: %s", store, err)
paddy@41 214 }
paddy@116 215 endpoints, err = context.ListEndpoints(client.ID, 10, 0)
paddy@41 216 if err != nil {
paddy@41 217 t.Fatalf("Error listing endpoints in %T: %s", store, err)
paddy@41 218 }
paddy@41 219 if len(endpoints) != 0 {
paddy@41 220 t.Fatalf("Expected %d endpoints, got %+v from %T", 0, endpoints, store)
paddy@31 221 }
paddy@31 222 }
paddy@31 223 }
paddy@39 224
paddy@39 225 func TestClientUpdates(t *testing.T) {
paddy@39 226 t.Parallel()
paddy@41 227 variations := 1 << 5
paddy@39 228 client := Client{
paddy@41 229 ID: uuid.NewID(),
paddy@41 230 Secret: "secret",
paddy@41 231 OwnerID: uuid.NewID(),
paddy@41 232 Name: "name",
paddy@41 233 Logo: "logo",
paddy@41 234 Website: "website",
paddy@39 235 }
paddy@39 236 for i := 0; i < variations; i++ {
paddy@41 237 var secret, name, logo, website string
paddy@39 238 change := ClientChange{}
paddy@39 239 expectation := client
paddy@39 240 result := client
paddy@39 241 if i&clientChangeSecret != 0 {
paddy@39 242 secret = fmt.Sprintf("secret-%d", i)
paddy@39 243 change.Secret = &secret
paddy@39 244 expectation.Secret = secret
paddy@39 245 }
paddy@39 246 if i&clientChangeOwnerID != 0 {
paddy@39 247 change.OwnerID = uuid.NewID()
paddy@39 248 expectation.OwnerID = change.OwnerID
paddy@39 249 }
paddy@39 250 if i&clientChangeName != 0 {
paddy@39 251 name = fmt.Sprintf("name-%d", i)
paddy@39 252 change.Name = &name
paddy@39 253 expectation.Name = name
paddy@39 254 }
paddy@39 255 if i&clientChangeLogo != 0 {
paddy@39 256 logo = fmt.Sprintf("logo-%d", i)
paddy@39 257 change.Logo = &logo
paddy@39 258 expectation.Logo = logo
paddy@39 259 }
paddy@39 260 if i&clientChangeWebsite != 0 {
paddy@39 261 website = fmt.Sprintf("website-%d", i)
paddy@39 262 change.Website = &website
paddy@39 263 expectation.Website = website
paddy@39 264 }
paddy@39 265 result.ApplyChange(change)
paddy@39 266 match, field, expected, got := compareClients(expectation, result)
paddy@39 267 if !match {
paddy@41 268 t.Fatalf("Expected field `%s` to be `%v`, got `%v`", field, expected, got)
paddy@39 269 }
paddy@39 270 for _, store := range clientStores {
paddy@116 271 context := Context{clients: store}
paddy@116 272 err := context.SaveClient(client)
paddy@39 273 if err != nil {
paddy@41 274 t.Fatalf("Error saving client in %T: %s", store, err)
paddy@39 275 }
paddy@116 276 err = context.UpdateClient(client.ID, change)
paddy@39 277 if err != nil {
paddy@41 278 t.Fatalf("Error updating client in %T: %s", store, err)
paddy@39 279 }
paddy@116 280 retrieved, err := context.GetClient(client.ID)
paddy@39 281 if err != nil {
paddy@116 282 t.Fatalf("Error getting client from %T: %s", store, err)
paddy@39 283 }
paddy@39 284 match, field, expected, got = compareClients(expectation, retrieved)
paddy@39 285 if !match {
paddy@41 286 t.Fatalf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store)
paddy@39 287 }
paddy@116 288 err = context.DeleteClient(client.ID)
paddy@39 289 if err != nil {
paddy@41 290 t.Fatalf("Error deleting client from %T: %s", store, err)
paddy@39 291 }
paddy@116 292 err = context.UpdateClient(client.ID, change)
paddy@39 293 if err != ErrClientNotFound {
paddy@41 294 t.Fatalf("Expected ErrClientNotFound, got %v from %T", err, store)
paddy@39 295 }
paddy@39 296 }
paddy@39 297 }
paddy@39 298 }
paddy@41 299
paddy@41 300 func TestClientEndpointChecks(t *testing.T) {
paddy@41 301 t.Parallel()
paddy@41 302 client := Client{
paddy@41 303 ID: uuid.NewID(),
paddy@41 304 Secret: "secret",
paddy@41 305 OwnerID: uuid.NewID(),
paddy@41 306 Name: "name",
paddy@41 307 Logo: "logo",
paddy@41 308 Website: "website",
paddy@41 309 }
paddy@41 310 endpoint1 := Endpoint{
paddy@41 311 ID: uuid.NewID(),
paddy@41 312 ClientID: client.ID,
paddy@41 313 Added: time.Now(),
paddy@116 314 URI: "https://www.example.com/first",
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@116 320 URI: "https://www.example.com/my/full/path",
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@116 330 context := Context{clients: store}
paddy@116 331 err := context.SaveClient(client)
paddy@41 332 if err != nil {
paddy@41 333 t.Fatalf("Error saving client in %T: %s", store, err)
paddy@41 334 }
paddy@116 335 err = context.AddEndpoints(client.ID, []Endpoint{endpoint1})
paddy@41 336 if err != nil {
paddy@41 337 t.Fatalf("Error saving endpoint in %T: %s", store, err)
paddy@41 338 }
paddy@116 339 err = context.AddEndpoints(client.ID, []Endpoint{endpoint2})
paddy@41 340 if err != nil {
paddy@41 341 t.Fatalf("Error saving endpoint in %T: %s", store, err)
paddy@41 342 }
paddy@41 343 for candidate, expectation := range candidates {
paddy@116 344 result, err := context.CheckEndpoint(client.ID, candidate)
paddy@54 345 if err != nil {
paddy@54 346 t.Fatalf("Error checking endpoint %s in %T: %s", candidate, store, err)
paddy@54 347 }
paddy@54 348 if result != expectation {
paddy@54 349 expectStr := "no"
paddy@54 350 resultStr := "a"
paddy@54 351 if expectation {
paddy@54 352 expectStr = "a"
paddy@54 353 resultStr = "no"
paddy@54 354 }
paddy@54 355 t.Errorf("Expected %s match for %s in %T, got %s match", expectStr, candidate, store, resultStr)
paddy@54 356 }
paddy@54 357 }
paddy@54 358 }
paddy@54 359 }
paddy@54 360
paddy@54 361 func TestClientEndpointChecksStrict(t *testing.T) {
paddy@54 362 t.Parallel()
paddy@54 363 client := Client{
paddy@54 364 ID: uuid.NewID(),
paddy@54 365 Secret: "secret",
paddy@54 366 OwnerID: uuid.NewID(),
paddy@54 367 Name: "name",
paddy@54 368 Logo: "logo",
paddy@54 369 Website: "website",
paddy@54 370 }
paddy@54 371 endpoint1 := Endpoint{
paddy@54 372 ID: uuid.NewID(),
paddy@54 373 ClientID: client.ID,
paddy@54 374 Added: time.Now(),
paddy@116 375 URI: "https://www.example.com/first",
paddy@54 376 }
paddy@54 377 endpoint2 := Endpoint{
paddy@54 378 ID: uuid.NewID(),
paddy@54 379 ClientID: client.ID,
paddy@54 380 Added: time.Now(),
paddy@116 381 URI: "https://www.example.com/my/full/path",
paddy@54 382 }
paddy@54 383 candidates := map[string]bool{
paddy@54 384 "https://www.example.com/": false,
paddy@54 385 "https://www.example.com/first": true,
paddy@54 386 "https://www.example.com/first/extra/path": false,
paddy@54 387 "https://www.example.com/my": false,
paddy@54 388 "https://www.example.com/my/full/path": true,
paddy@54 389 }
paddy@54 390 for _, store := range clientStores {
paddy@116 391 context := Context{clients: store}
paddy@116 392 err := context.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@116 396 err = context.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@116 400 err = context.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@116 405 result, err := context.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 }
paddy@116 866
paddy@116 867 func TestCreateClientHandler(t *testing.T) {
paddy@116 868 t.Parallel()
paddy@116 869 memstore := NewMemstore()
paddy@116 870 c := Context{
paddy@116 871 clients: memstore,
paddy@116 872 profiles: memstore,
paddy@116 873 }
paddy@116 874 w := httptest.NewRecorder()
paddy@116 875 r, err := http.NewRequest("POST", "https://test.auth.secondbit.org/clients", nil)
paddy@116 876 if err != nil {
paddy@116 877 t.Fatal("Can't build request:", err)
paddy@116 878 }
paddy@116 879 r.Header.Set("Content-Type", "application/json")
paddy@116 880 CreateClientHandler(w, r, c)
paddy@116 881 if w.Code != http.StatusUnauthorized {
paddy@116 882 t.Errorf("Expected status of %d, got status %d", http.StatusUnauthorized, w.Code)
paddy@116 883 }
paddy@116 884 expected := `{"errors":[{"error":"access_denied"}]}`
paddy@116 885 result := strings.TrimSpace(w.Body.String())
paddy@116 886 if result != expected {
paddy@116 887 t.Errorf("Expected response to be `%s`, got `%s`", expected, result)
paddy@116 888 }
paddy@116 889 w = httptest.NewRecorder()
paddy@116 890 r.Header.Set("Authorization", "Not basic at all...")
paddy@116 891 CreateClientHandler(w, r, c)
paddy@116 892 if w.Code != http.StatusUnauthorized {
paddy@116 893 t.Errorf("Expected status of %d, got status %d", http.StatusUnauthorized, w.Code)
paddy@116 894 }
paddy@116 895 expected = `{"errors":[{"error":"access_denied"}]}`
paddy@116 896 result = strings.TrimSpace(w.Body.String())
paddy@116 897 if result != expected {
paddy@116 898 t.Errorf("Expected response to be `%s`, got `%s`", expected, result)
paddy@116 899 }
paddy@116 900 w = httptest.NewRecorder()
paddy@116 901 r.Header.Set("Authorization", "Basic TotallyNotBase64Encoded")
paddy@116 902 CreateClientHandler(w, r, c)
paddy@116 903 if w.Code != http.StatusUnauthorized {
paddy@116 904 t.Errorf("Expected status of %d, got status %d", http.StatusUnauthorized, w.Code)
paddy@116 905 }
paddy@116 906 expected = `{"errors":[{"error":"access_denied"}]}`
paddy@116 907 result = strings.TrimSpace(w.Body.String())
paddy@116 908 if result != expected {
paddy@116 909 t.Errorf("Expected response to be `%s`, got `%s`", expected, result)
paddy@116 910 }
paddy@116 911 w = httptest.NewRecorder()
paddy@116 912 r.Header.Set("Authorization", "Basic dGhpc2hhc25vY29sb24=")
paddy@116 913 CreateClientHandler(w, r, c)
paddy@116 914 if w.Code != http.StatusUnauthorized {
paddy@116 915 t.Errorf("Expected status of %d, got status %d", http.StatusUnauthorized, w.Code)
paddy@116 916 }
paddy@116 917 expected = `{"errors":[{"error":"access_denied"}]}`
paddy@116 918 result = strings.TrimSpace(w.Body.String())
paddy@116 919 if result != expected {
paddy@116 920 t.Errorf("Expected response to be `%s`, got `%s`", expected, result)
paddy@116 921 }
paddy@116 922 profile := Profile{
paddy@116 923 ID: uuid.NewID(),
paddy@116 924 Name: "Test User",
paddy@116 925 Passphrase: "f3a4ac4f1d657b2e6e776d24213e39406d50a87a52691a2a78891425af1271d0",
paddy@116 926 Iterations: 1,
paddy@116 927 Salt: "d82d92cfa8bfb5a08270ebbf39a3710d24b352b937fcc8959ebcb40384cc616b",
paddy@116 928 PassphraseScheme: 1,
paddy@116 929 Compromised: false,
paddy@116 930 LockedUntil: time.Time{},
paddy@116 931 PassphraseReset: "",
paddy@116 932 PassphraseResetCreated: time.Time{},
paddy@116 933 Created: time.Now(),
paddy@116 934 LastSeen: time.Time{},
paddy@116 935 }
paddy@116 936 login := Login{
paddy@116 937 Type: "email",
paddy@116 938 Value: "test@example.com",
paddy@116 939 ProfileID: profile.ID,
paddy@116 940 Created: time.Now(),
paddy@116 941 LastUsed: time.Time{},
paddy@116 942 }
paddy@116 943 w = httptest.NewRecorder()
paddy@116 944 r.SetBasicAuth("test@example.com", "mysecurepassphrase")
paddy@116 945 CreateClientHandler(w, r, c)
paddy@116 946 if w.Code != http.StatusUnauthorized {
paddy@116 947 t.Errorf("Expected status of %d, got status %d", http.StatusUnauthorized, w.Code)
paddy@116 948 }
paddy@116 949 expected = `{"errors":[{"error":"access_denied"}]}`
paddy@116 950 result = strings.TrimSpace(w.Body.String())
paddy@116 951 if result != expected {
paddy@116 952 t.Errorf("Expected response to be `%s`, got `%s`", expected, result)
paddy@116 953 }
paddy@116 954 err = c.SaveProfile(profile)
paddy@116 955 if err != nil {
paddy@116 956 t.Error("Error saving profile:", err)
paddy@116 957 }
paddy@116 958 err = c.AddLogin(login)
paddy@116 959 if err != nil {
paddy@116 960 t.Error("Error adding login:", err)
paddy@116 961 }
paddy@116 962 r.SetBasicAuth("test@example.com", "mysecurepassphrase")
paddy@116 963 type testStruct struct {
paddy@116 964 request string
paddy@116 965 code int
paddy@116 966 resp response
paddy@116 967 }
paddy@116 968 tests := []testStruct{
paddy@116 969 {``, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInvalidFormat, Field: "/"}}}},
paddy@116 970 {`{}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrMissing, Field: "/type"}, {Slug: requestErrMissing, Field: "/name"}}}},
paddy@116 971 {`{"type":"notarealtype"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInvalidValue, Field: "/type"}, {Slug: requestErrMissing, Field: "/name"}}}},
paddy@116 972 {`{"type":"notarealtype","name":"myreallylongnameislongerthatthemaximumnamelength"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInvalidValue, Field: "/type"}, {Slug: requestErrOverflow, Field: "/name"}}}},
paddy@116 973 {`{"type":"notarealtype","name":"a"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInvalidValue, Field: "/type"}, {Slug: requestErrInsufficient, Field: "/name"}}}},
paddy@116 974 {`{"type":"public"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrMissing, Field: "/name"}}}},
paddy@116 975 {`{"type":"public","name":"myreallylongnameislongerthatthemaximumnamelength"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrOverflow, Field: "/name"}}}},
paddy@116 976 {`{"type":"public","name":"a"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInsufficient, Field: "/name"}}}},
paddy@116 977 {`{"name":"My Client"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrMissing, Field: "/type"}}}},
paddy@116 978 {`{"type":"notarealtype","name":"My Client"}`, http.StatusBadRequest, response{Errors: []requestError{{Slug: requestErrInvalidValue, Field: "/type"}}}},
paddy@116 979 {`{"type":"public","name":"My Client"}`, http.StatusCreated, response{Clients: []Client{{Name: "My Client", OwnerID: profile.ID, Type: "public"}}}},
paddy@116 980 {`{"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 981 {`{"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 982 {`{"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@116 983 }
paddy@116 984 for pos, test := range tests {
paddy@116 985 t.Logf("Test #%d: `%s`", pos, test.request)
paddy@116 986 w = httptest.NewRecorder()
paddy@116 987 body := bytes.NewBufferString(test.request)
paddy@116 988 r.Body = ioutil.NopCloser(body)
paddy@116 989 CreateClientHandler(w, r, c)
paddy@116 990 if w.Code != test.code {
paddy@116 991 t.Errorf("Expected response code to be %d, got %d", test.code, w.Code)
paddy@116 992 }
paddy@116 993 t.Logf("Response: %s", w.Body.String())
paddy@116 994 var res response
paddy@116 995 err = json.Unmarshal(w.Body.Bytes(), &res)
paddy@116 996 if err != nil {
paddy@116 997 t.Error("Unexpected error unmarshalling response:", err)
paddy@116 998 }
paddy@116 999 fillInServerGenerated(test.resp, res)
paddy@116 1000 success, field, expectation, result := compareResponses(test.resp, res)
paddy@116 1001 if !success {
paddy@116 1002 t.Errorf("Unexpected result for %s in response: expected %v, got %v", field, expectation, result)
paddy@116 1003 }
paddy@116 1004 }
paddy@116 1005 }