eintopf.info@v0.13.16/service/auth/transport_test.go (about) 1 // Copyright (C) 2022 The Eintopf authors 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package auth_test 17 18 import ( 19 "context" 20 "encoding/json" 21 "fmt" 22 "net/http" 23 "net/http/httptest" 24 "testing" 25 26 "eintopf.info/internal/xhttptest" 27 "eintopf.info/service/auth" 28 ) 29 30 type authService struct{} 31 32 func (a *authService) Login(ctx context.Context, email, password string) (string, error) { 33 if email == "admin@example.com" && password == "password" { 34 return "valid", nil 35 } 36 return "", auth.ErrInvalidCredentials 37 } 38 39 func (a *authService) Authenticate(token string) (string, error) { 40 switch token { 41 case "valid": 42 return "id", nil 43 case "errorAuthorization": 44 return "error", nil 45 case "error": 46 return "", fmt.Errorf("error") 47 default: 48 return "", nil 49 } 50 } 51 52 func (a *authService) Authorize(ctx context.Context, id string) (auth.Role, error) { 53 switch id { 54 case "id": 55 return auth.RoleAdmin, nil 56 case "error": 57 return auth.RoleNormal, fmt.Errorf("error") 58 default: 59 return "", nil 60 } 61 } 62 63 func getTestHandler() http.HandlerFunc { 64 fn := func(w http.ResponseWriter, req *http.Request) { 65 w.WriteHeader(http.StatusOK) 66 } 67 return http.HandlerFunc(fn) 68 } 69 70 func mustMarshal(v interface{}) string { 71 data, err := json.Marshal(v) 72 if err != nil { 73 panic("mustMarshal failed") 74 } 75 return string(data) 76 } 77 78 func TestRouter(t *testing.T) { 79 tests := []xhttptest.HttpTest{ 80 { 81 Name: "LoginValidCredentials", 82 URI: "/login", 83 Method: "POST", 84 Body: mustMarshal(&auth.Credentials{Email: "admin@example.com", Password: "password"}), 85 WantBody: `{"token":"valid","id":"id","role":"admin"}`, 86 WantStatus: 200, 87 }, { 88 Name: "LoginInvalidCredentials", 89 URI: "/login", 90 Method: "POST", 91 Body: mustMarshal(&auth.Credentials{Email: "u", Password: "p"}), 92 WantBody: `{"error":"invalid credentials"}`, 93 WantStatus: 400, 94 }, { 95 Name: "OptionsHandler", 96 URI: "/login", 97 Method: "OPTIONS", 98 WantStatus: 200, 99 }, 100 } 101 102 xhttptest.TestRouter(t, auth.Router(&authService{}), tests) 103 } 104 105 func TestMiddleware(t *testing.T) { 106 tests := []xhttptest.HttpTest{ 107 { 108 Name: "valid authentication header", 109 Method: "GET", 110 Headers: map[string]string{"Authorization": "valid"}, 111 WantBody: "", 112 WantStatus: 200, 113 }, { 114 Name: "missing authentication header", 115 Method: "GET", 116 WantBody: `{"error":"missing authorization header"}`, 117 WantStatus: 401, 118 }, { 119 Name: "invalid authentication header", 120 Method: "GET", 121 Headers: map[string]string{"Authorization": "invalid"}, 122 WantBody: `{"error":"invalid authentication token"}`, 123 WantStatus: 401, 124 }, { 125 Name: "error during authentication", 126 Method: "GET", 127 Headers: map[string]string{"Authorization": "error"}, 128 WantBody: `{"error":"internal error"}`, 129 WantStatus: 500, 130 }, { 131 Name: "error during authorization", 132 Method: "GET", 133 Headers: map[string]string{"Authorization": "errorAuthorization"}, 134 WantBody: `{"error":"internal error"}`, 135 WantStatus: 500, 136 }, 137 } 138 139 xhttptest.TestMiddleware(t, auth.Middleware(&authService{}), tests) 140 } 141 142 func testMiddlewareAddsInformationToContext(t *testing.T, middleware func(next http.Handler) http.Handler) { 143 testHandlerCalled := false 144 getTestHandler := func() http.HandlerFunc { 145 fn := func(w http.ResponseWriter, req *http.Request) { 146 testHandlerCalled = true 147 id, err := auth.UserIDFromContext(req.Context()) 148 if err != nil && id != "id" { 149 t.Fatalf("expected id in context to be 'id'. got %s", id) 150 } 151 role, err := auth.RoleFromContext(req.Context()) 152 if err != nil && role != auth.RoleAdmin { 153 t.Fatalf("expected role in context to be 'admin'. got %s", role) 154 } 155 } 156 return http.HandlerFunc(fn) 157 } 158 ts := httptest.NewServer(middleware(getTestHandler())) 159 defer ts.Close() 160 161 client := &http.Client{} 162 163 req, err := http.NewRequest("GET", ts.URL, nil) 164 if err != nil { 165 t.Fatalf("expected no error from http.NewRequest. got %s", err) 166 } 167 req.Header.Set("Authorization", "valid") 168 _, err = client.Do(req) 169 if err != nil { 170 t.Fatalf("expected no error from client.Do. got %s", err) 171 } 172 173 if !testHandlerCalled { 174 t.Fatal("expected nextHandler to be called") 175 } 176 } 177 func TestMiddlewareAddsInformationToContext(t *testing.T) { 178 testMiddlewareAddsInformationToContext(t, auth.Middleware(&authService{})) 179 } 180 181 func TestMiddlewareNoValidationAddsInformationToContext(t *testing.T) { 182 testMiddlewareAddsInformationToContext(t, auth.MiddlewareWithOpts(&authService{}, auth.MiddlewareOpts{Validate: false})) 183 }