auth
auth/http_test.go
The great documentation and exported interface cleanup. Modify all our *Store interfaces to be unexported, as there's no real good reason they need to be exported, especially as they can be implemented without being exported. The interfaces shouldn't matter to 99% of users of the package, so let's not pollute our package API. Further, all methods of the interfaces are now unexported, for pretty much the same reasoning. Add a doc.go file with documentation explaining the choices the package is making and what it provides. Implement documentation on all our exported types and methods and functions, which makes golint happy. The only remaining golint warning is about NewMemstore, which will stay the way it is. The memstore type is useful outside tests for things like standing up a server quickly when we don't care about the storage, and because the type is unexported, we _need_ a New function to create an instance that can be passed to the Context.
| paddy@52 | 1 package auth |
| paddy@52 | 2 |
| paddy@52 | 3 import ( |
| paddy@52 | 4 "html/template" |
| paddy@52 | 5 "net/http" |
| paddy@52 | 6 "net/http/httptest" |
| paddy@53 | 7 "net/url" |
| paddy@52 | 8 "testing" |
| paddy@56 | 9 "time" |
| paddy@56 | 10 |
| paddy@56 | 11 "code.secondbit.org/uuid" |
| paddy@52 | 12 ) |
| paddy@52 | 13 |
| paddy@53 | 14 const ( |
| paddy@53 | 15 scopeSet = 1 << iota |
| paddy@53 | 16 stateSet |
| paddy@53 | 17 uriSet |
| paddy@56 | 18 uriExact |
| paddy@53 | 19 ) |
| paddy@53 | 20 |
| paddy@52 | 21 func TestGetGrantCodeSuccess(t *testing.T) { |
| paddy@52 | 22 t.Parallel() |
| paddy@52 | 23 store := NewMemstore() |
| paddy@52 | 24 testContext := Context{ |
| paddy@52 | 25 template: template.Must(template.New(getGrantTemplateName).Parse("Get auth grant")), |
| paddy@52 | 26 clients: store, |
| paddy@52 | 27 grants: store, |
| paddy@52 | 28 profiles: store, |
| paddy@52 | 29 tokens: store, |
| paddy@52 | 30 } |
| paddy@56 | 31 client := Client{ |
| paddy@56 | 32 ID: uuid.NewID(), |
| paddy@56 | 33 Secret: "super secret!", |
| paddy@56 | 34 OwnerID: uuid.NewID(), |
| paddy@56 | 35 Name: "My test client", |
| paddy@56 | 36 Logo: "https://secondbit.org/logo.png", |
| paddy@56 | 37 Website: "https://secondbit.org", |
| paddy@56 | 38 Type: "public", |
| paddy@56 | 39 } |
| paddy@56 | 40 uri, err := url.Parse("https://test.secondbit.org/redirect") |
| paddy@56 | 41 if err != nil { |
| paddy@56 | 42 t.Fatal("Can't parse URL:", err) |
| paddy@56 | 43 } |
| paddy@56 | 44 endpoint := Endpoint{ |
| paddy@56 | 45 ID: uuid.NewID(), |
| paddy@56 | 46 ClientID: client.ID, |
| paddy@56 | 47 URI: *uri, |
| paddy@56 | 48 Added: time.Now(), |
| paddy@56 | 49 } |
| paddy@56 | 50 err = testContext.SaveClient(client) |
| paddy@56 | 51 if err != nil { |
| paddy@56 | 52 t.Fatal("Can't store client:", err) |
| paddy@56 | 53 } |
| paddy@56 | 54 err = testContext.AddEndpoint(client.ID, endpoint) |
| paddy@56 | 55 if err != nil { |
| paddy@56 | 56 t.Fatal("Can't store endpoint:", err) |
| paddy@56 | 57 } |
| paddy@52 | 58 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@52 | 59 if err != nil { |
| paddy@52 | 60 t.Fatal("Can't build request:", err) |
| paddy@52 | 61 } |
| paddy@56 | 62 for i := 0; i < 1<<4; i++ { |
| paddy@53 | 63 w := httptest.NewRecorder() |
| paddy@53 | 64 params := url.Values{} |
| paddy@53 | 65 // see OAuth 2.0 spec, section 4.1.1 |
| paddy@53 | 66 params.Set("response_type", "code") |
| paddy@56 | 67 params.Set("client_id", client.ID.String()) |
| paddy@53 | 68 if i&uriSet != 0 { |
| paddy@56 | 69 if i&uriExact != 0 { |
| paddy@56 | 70 params.Set("redirect_uri", endpoint.URI.String()) |
| paddy@56 | 71 } else { |
| paddy@56 | 72 params.Set("redirect_uri", endpoint.URI.String()+"/inexact") |
| paddy@56 | 73 } |
| paddy@53 | 74 } |
| paddy@53 | 75 if i&scopeSet != 0 { |
| paddy@53 | 76 params.Set("scope", "testscope") |
| paddy@53 | 77 } |
| paddy@53 | 78 if i&stateSet != 0 { |
| paddy@53 | 79 params.Set("state", "my super secure state string") |
| paddy@53 | 80 } |
| paddy@53 | 81 req.URL.RawQuery = params.Encode() |
| paddy@53 | 82 GetGrantHandler(w, req, testContext) |
| paddy@53 | 83 if w.Code != http.StatusOK { |
| paddy@53 | 84 t.Errorf("Expected status code to be %d, got %d for %s", http.StatusOK, w.Code, req.URL.String()) |
| paddy@53 | 85 } |
| paddy@53 | 86 if w.Body.String() != "Get auth grant" { |
| paddy@53 | 87 t.Errorf("Expected body to be `%s`, got `%s` for %s", "Get auth grant", w.Body.String(), req.URL.String()) |
| paddy@53 | 88 } |
| paddy@52 | 89 } |
| paddy@52 | 90 } |
| paddy@56 | 91 |
| paddy@56 | 92 func TestGetGrantCodeInvalidURI(t *testing.T) { |
| paddy@56 | 93 t.Parallel() |
| paddy@56 | 94 store := NewMemstore() |
| paddy@56 | 95 testContext := Context{ |
| paddy@56 | 96 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")), |
| paddy@56 | 97 clients: store, |
| paddy@56 | 98 grants: store, |
| paddy@56 | 99 profiles: store, |
| paddy@56 | 100 tokens: store, |
| paddy@56 | 101 } |
| paddy@56 | 102 client := Client{ |
| paddy@56 | 103 ID: uuid.NewID(), |
| paddy@56 | 104 Secret: "super secret!", |
| paddy@56 | 105 OwnerID: uuid.NewID(), |
| paddy@56 | 106 Name: "My test client", |
| paddy@56 | 107 Type: "public", |
| paddy@56 | 108 } |
| paddy@56 | 109 uri, err := url.Parse("https://test.secondbit.org/redirect") |
| paddy@56 | 110 if err != nil { |
| paddy@56 | 111 t.Fatal("Can't parse URL:", err) |
| paddy@56 | 112 } |
| paddy@56 | 113 endpoint := Endpoint{ |
| paddy@56 | 114 ID: uuid.NewID(), |
| paddy@56 | 115 ClientID: client.ID, |
| paddy@56 | 116 URI: *uri, |
| paddy@56 | 117 Added: time.Now(), |
| paddy@56 | 118 } |
| paddy@56 | 119 err = testContext.SaveClient(client) |
| paddy@56 | 120 if err != nil { |
| paddy@56 | 121 t.Fatal("Can't store client:", err) |
| paddy@56 | 122 } |
| paddy@56 | 123 err = testContext.AddEndpoint(client.ID, endpoint) |
| paddy@56 | 124 if err != nil { |
| paddy@56 | 125 t.Fatal("Can't store endpoint:", err) |
| paddy@56 | 126 } |
| paddy@56 | 127 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@56 | 128 if err != nil { |
| paddy@56 | 129 t.Fatal("Can't build request:", err) |
| paddy@56 | 130 } |
| paddy@56 | 131 w := httptest.NewRecorder() |
| paddy@56 | 132 params := url.Values{} |
| paddy@56 | 133 params.Set("response_type", "code") |
| paddy@56 | 134 params.Set("client_id", client.ID.String()) |
| paddy@56 | 135 params.Set("redirect_uri", "https://test.secondbit.org/wrong") |
| paddy@56 | 136 req.URL.RawQuery = params.Encode() |
| paddy@56 | 137 GetGrantHandler(w, req, testContext) |
| paddy@56 | 138 if w.Code != http.StatusBadRequest { |
| paddy@56 | 139 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@56 | 140 } |
| paddy@56 | 141 if w.Body.String() != "The redirect_uri specified is not valid." { |
| paddy@56 | 142 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String()) |
| paddy@56 | 143 } |
| paddy@56 | 144 } |