From 3614b197c41b38a4d269ce0a0f38e2d3793f8237 Mon Sep 17 00:00:00 2001 From: Sotirios Mantziaris Date: Tue, 18 Dec 2018 23:59:58 +0200 Subject: [PATCH] Optional Security for HTTP Routes (fixes #169) (#256) Signed-off-by: Sotirios Mantziaris --- README.md | 24 ++++++- examples/first/main.go | 1 + examples/second/main.go | 19 +++++- option_test.go | 2 +- service_test.go | 2 +- sync/http/auth/apikey/apikey.go | 45 +++++++++++++ sync/http/auth/apikey/apikey_test.go | 97 ++++++++++++++++++++++++++++ sync/http/auth/auth.go | 10 +++ sync/http/component.go | 6 +- sync/http/component_test.go | 16 ++--- sync/http/handler_test.go | 2 +- sync/http/middleware.go | 39 +++++++++-- sync/http/middleware_test.go | 51 ++++++++++++--- sync/http/route.go | 39 +++++++++-- sync/http/route_test.go | 59 ++++++++++++++++- 15 files changed, 371 insertions(+), 41 deletions(-) create mode 100644 sync/http/auth/apikey/apikey.go create mode 100644 sync/http/auth/apikey/apikey_test.go create mode 100644 sync/http/auth/auth.go diff --git a/README.md b/README.md index bae49ea8b..afe3d3c84 100644 --- a/README.md +++ b/README.md @@ -252,4 +252,26 @@ The factory function type defines a factory for creating a logger. ```go type FactoryFunc func(map[string]interface{}) Logger -``` \ No newline at end of file +``` + +## Security + +The necessary abstraction are available to implement authentication in the following components: + +- HTTP + +### HTTP + +In order to use authentication, a authenticator has to be implement following the interface: + +```go +type Authenticator interface { + Authenticate(req *http.Request) (bool, error) +} +``` + +This authenticator can then be used to set up routes with authentication. + +The following authenticator are available: + +- API key authenticator, see examples diff --git a/examples/first/main.go b/examples/first/main.go index 817b02b56..3ddb42a8c 100644 --- a/examples/first/main.go +++ b/examples/first/main.go @@ -72,6 +72,7 @@ func first(ctx context.Context, req *sync.Request) (*sync.Response, error) { } secondRouteReq.Header.Add("Content-Type", "application/json") secondRouteReq.Header.Add("Accept", "application/json") + secondRouteReq.Header.Add("Authorization", "Apikey 123456") cl, err := tracehttp.New(tracehttp.Timeout(5 * time.Second)) if err != nil { return nil, err diff --git a/examples/second/main.go b/examples/second/main.go index f3be5d8f8..37ca618b4 100644 --- a/examples/second/main.go +++ b/examples/second/main.go @@ -11,6 +11,7 @@ import ( "github.com/mantzas/patron/log" "github.com/mantzas/patron/sync" patronhttp "github.com/mantzas/patron/sync/http" + "github.com/mantzas/patron/sync/http/auth/apikey" tracehttp "github.com/mantzas/patron/trace/http" "github.com/mantzas/patron/trace/kafka" "github.com/pkg/errors" @@ -55,9 +56,14 @@ func main() { log.Fatalf("failed to create processor %v", err) } + auth, err := apikey.New(&apiKeyValidator{validKey: "123456"}) + if err != nil { + log.Fatalf("failed to create authenticator %v", err) + } + // Set up routes routes := []patronhttp.Route{ - patronhttp.NewGetRoute("/", httpCmp.second, true), + patronhttp.NewAuthGetRoute("/", httpCmp.second, true, auth), } srv, err := patron.New( @@ -122,3 +128,14 @@ func (hc *httpComponent) second(ctx context.Context, req *sync.Request) (*sync.R log.Infof("request processed: %s", m) return sync.NewResponse(fmt.Sprintf("got %s from google", rsp.Status)), nil } + +type apiKeyValidator struct { + validKey string +} + +func (av apiKeyValidator) Validate(key string) (bool, error) { + if key == av.validKey { + return true, nil + } + return false, nil +} diff --git a/option_test.go b/option_test.go index 2d7472633..af6e3205e 100644 --- a/option_test.go +++ b/option_test.go @@ -18,7 +18,7 @@ func TestRoutes(t *testing.T) { }{ {"failure due to empty routes", args{rr: []http.Route{}}, true}, {"failure due to nil routes", args{rr: nil}, true}, - {"success", args{rr: []http.Route{http.NewRoute("/", "GET", nil, true)}}, false}, + {"success", args{rr: []http.Route{http.NewRoute("/", "GET", nil, true, nil)}}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/service_test.go b/service_test.go index abeaabb52..b5ff6dcb2 100644 --- a/service_test.go +++ b/service_test.go @@ -10,7 +10,7 @@ import ( ) func TestNewServer(t *testing.T) { - route := http.NewRoute("/", "GET", nil, true) + route := http.NewRoute("/", "GET", nil, true, nil) type args struct { name string opt OptionFunc diff --git a/sync/http/auth/apikey/apikey.go b/sync/http/auth/apikey/apikey.go new file mode 100644 index 000000000..9ea93b327 --- /dev/null +++ b/sync/http/auth/apikey/apikey.go @@ -0,0 +1,45 @@ +package apikey + +import ( + "errors" + "net/http" + "strings" +) + +// Validator interface for validating keys. +type Validator interface { + Validate(key string) (bool, error) +} + +// Authenticator authenticates the request based on the header on the following header key and value: +// Authorization: Apikey {api key}, where {api key} is the key. +type Authenticator struct { + val Validator +} + +// New constructor. +func New(val Validator) (*Authenticator, error) { + if val == nil { + return nil, errors.New("validator is nil") + } + return &Authenticator{val: val}, nil +} + +// Authenticate parses the header for the specified key and authenticates it. +func (a *Authenticator) Authenticate(req *http.Request) (bool, error) { + headerVal := req.Header.Get("Authorization") + if headerVal == "" { + return false, nil + } + + auth := strings.SplitN(headerVal, " ", 2) + if len(auth) != 2 { + return false, nil + } + + if strings.ToLower(auth[0]) != "apikey" { + return false, nil + } + + return a.val.Validate(auth[1]) +} diff --git a/sync/http/auth/apikey/apikey_test.go b/sync/http/auth/apikey/apikey_test.go new file mode 100644 index 000000000..e607fb5f8 --- /dev/null +++ b/sync/http/auth/apikey/apikey_test.go @@ -0,0 +1,97 @@ +package apikey + +import ( + "errors" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +type MockValidator struct { + err error + success bool +} + +func (mv MockValidator) Validate(key string) (bool, error) { + if mv.err != nil { + return false, mv.err + } + return mv.success, nil +} + +func TestNew(t *testing.T) { + type args struct { + val Validator + } + tests := []struct { + name string + args args + wantErr bool + }{ + {name: "success", args: args{val: &MockValidator{}}, wantErr: false}, + {name: "failed due to nil validator", args: args{val: nil}, wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := New(tt.args.val) + if tt.wantErr { + assert.Error(t, err) + assert.Nil(t, got) + } else { + assert.NoError(t, err) + assert.NotNil(t, got) + } + }) + } +} + +func TestAuthenticator_Authenticate(t *testing.T) { + reqOk, err := http.NewRequest("POST", "/test", nil) + assert.NoError(t, err) + reqOk.Header.Set("Authorization", "Apikey 123456") + reqMissingHeader, err := http.NewRequest("POST", "/test", nil) + assert.NoError(t, err) + reqMissingKey, err := http.NewRequest("POST", "/test", nil) + assert.NoError(t, err) + reqMissingKey.Header.Set("Authorization", "Apikey") + reqInvalidAuthMethod, err := http.NewRequest("POST", "/test", nil) + assert.NoError(t, err) + reqInvalidAuthMethod.Header.Set("Authorization", "Bearer 123456") + + type fields struct { + val Validator + } + type args struct { + req *http.Request + } + tests := []struct { + name string + fields fields + args args + want bool + wantErr bool + }{ + {name: "authenticated", fields: fields{val: &MockValidator{success: true}}, args: args{req: reqOk}, want: true, wantErr: false}, + {name: "not authenticated, validation failed", fields: fields{val: &MockValidator{success: false}}, args: args{req: reqOk}, want: false, wantErr: false}, + {name: "failed, validation returned err", fields: fields{val: &MockValidator{err: errors.New("TEST")}}, args: args{req: reqOk}, want: false, wantErr: true}, + {name: "not authenticated, header missing", fields: fields{val: &MockValidator{success: false}}, args: args{req: reqMissingHeader}, want: false, wantErr: false}, + {name: "not authenticated, missing key", fields: fields{val: &MockValidator{success: false}}, args: args{req: reqMissingKey}, want: false, wantErr: false}, + {name: "not authenticated, invalid auth method", fields: fields{val: &MockValidator{success: false}}, args: args{req: reqInvalidAuthMethod}, want: false, wantErr: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &Authenticator{ + val: tt.fields.val, + } + got, err := a.Authenticate(tt.args.req) + if tt.wantErr { + assert.Error(t, err) + assert.False(t, got) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + } + }) + } +} diff --git a/sync/http/auth/auth.go b/sync/http/auth/auth.go new file mode 100644 index 000000000..e0436d171 --- /dev/null +++ b/sync/http/auth/auth.go @@ -0,0 +1,10 @@ +package auth + +import ( + "net/http" +) + +// Authenticator interface. +type Authenticator interface { + Authenticate(req *http.Request) (bool, error) +} diff --git a/sync/http/component.go b/sync/http/component.go index b095b3f9e..17f049a46 100644 --- a/sync/http/component.go +++ b/sync/http/component.go @@ -73,11 +73,7 @@ func (c *Component) Run(ctx context.Context) error { c.Lock() log.Debug("applying tracing to routes") for i := 0; i < len(c.routes); i++ { - if c.routes[i].Trace { - c.routes[i].Handler = DefaultMiddleware(c.routes[i].Pattern, c.routes[i].Handler) - } else { - c.routes[i].Handler = RecoveryMiddleware(c.routes[i].Handler) - } + c.routes[i].Handler = Middleware(c.routes[i].Trace, c.routes[i].Auth, c.routes[i].Pattern, c.routes[i].Handler) } chFail := make(chan error) srv := c.createHTTPServer() diff --git a/sync/http/component_test.go b/sync/http/component_test.go index 72b20b93b..68229b2f4 100644 --- a/sync/http/component_test.go +++ b/sync/http/component_test.go @@ -40,8 +40,8 @@ func TestNew(t *testing.T) { } func TestComponent_ListenAndServe_DefaultRoutes_Shutdown(t *testing.T) { - rr := []Route{NewRoute("/", "GET", nil, true)} - s, err := New(Routes(rr)) + rr := []Route{NewRoute("/", "GET", nil, true, nil)} + s, err := New(Routes(rr), Port(50003)) assert.NoError(t, err) done := make(chan bool) ctx, cnl := context.WithCancel(context.Background()) @@ -56,8 +56,8 @@ func TestComponent_ListenAndServe_DefaultRoutes_Shutdown(t *testing.T) { } func TestComponent_ListenAndServeTLS_DefaultRoutes_Shutdown(t *testing.T) { - rr := []Route{NewRoute("/", "GET", nil, true)} - s, err := New(Routes(rr), Secure("testdata/server.pem", "testdata/server.key")) + rr := []Route{NewRoute("/", "GET", nil, true, nil)} + s, err := New(Routes(rr), Secure("testdata/server.pem", "testdata/server.key"), Port(50001)) assert.NoError(t, err) done := make(chan bool) ctx, cnl := context.WithCancel(context.Background()) @@ -72,12 +72,12 @@ func TestComponent_ListenAndServeTLS_DefaultRoutes_Shutdown(t *testing.T) { } func TestInfo(t *testing.T) { - rr := []Route{NewRoute("/", "GET", nil, true)} - s, err := New(Routes(rr), Secure("testdata/server.pem", "testdata/server.key")) + rr := []Route{NewRoute("/", "GET", nil, true, nil)} + s, err := New(Routes(rr), Secure("testdata/server.pem", "testdata/server.key"), Port(50005)) assert.NoError(t, err) expected := make(map[string]interface{}) expected["type"] = "https" - expected["port"] = 50000 + expected["port"] = 50005 expected["read-timeout"] = httpReadTimeout.String() expected["write-timeout"] = httpWriteTimeout.String() expected["idle-timeout"] = httpIdleTimeout.String() @@ -87,7 +87,7 @@ func TestInfo(t *testing.T) { } func TestComponent_ListenAndServeTLS_FailsInvalidCerts(t *testing.T) { - rr := []Route{NewRoute("/", "GET", nil, true)} + rr := []Route{NewRoute("/", "GET", nil, true, nil)} s, err := New(Routes(rr), Secure("testdata/server.pem", "testdata/server.pem")) assert.NoError(t, err) assert.Error(t, s.Run(context.Background())) diff --git a/sync/http/handler_test.go b/sync/http/handler_test.go index ce8210340..a30904e4c 100644 --- a/sync/http/handler_test.go +++ b/sync/http/handler_test.go @@ -228,7 +228,7 @@ func Test_extractParams(t *testing.T) { } router := httprouter.New() - route := NewRoute("/users/:id/status", "GET", proc, false) + route := NewRoute("/users/:id/status", "GET", proc, false, nil) router.HandlerFunc(route.Method, route.Pattern, route.Handler) router.ServeHTTP(httptest.NewRecorder(), req) assert.Equal(t, "1", fields["id"]) diff --git a/sync/http/middleware.go b/sync/http/middleware.go index a7eb182b2..6d3e964bb 100644 --- a/sync/http/middleware.go +++ b/sync/http/middleware.go @@ -5,6 +5,7 @@ import ( "github.com/mantzas/patron/errors" "github.com/mantzas/patron/log" + "github.com/mantzas/patron/sync/http/auth" "github.com/mantzas/patron/trace" ) @@ -51,13 +52,21 @@ func (w *responseWriter) WriteHeader(code int) { w.statusHeaderWritten = true } -// DefaultMiddleware which handles tracing and recovery. -func DefaultMiddleware(path string, next http.HandlerFunc) http.HandlerFunc { - return TracingMiddleware(path, RecoveryMiddleware(next)) +// Middleware which returns all selected middlewares. +func Middleware(trace bool, auth auth.Authenticator, path string, next http.HandlerFunc) http.HandlerFunc { + if trace { + if auth == nil { + return tracingMiddleware(path, recoveryMiddleware(next)) + } + return tracingMiddleware(path, authMiddleware(auth, recoveryMiddleware(next))) + } + if auth == nil { + return recoveryMiddleware(next) + } + return authMiddleware(auth, recoveryMiddleware(next)) } -// TracingMiddleware for handling tracing and metrics. -func TracingMiddleware(path string, next http.HandlerFunc) http.HandlerFunc { +func tracingMiddleware(path string, next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { sp, r := trace.HTTPSpan(path, r) lw := newResponseWriter(w) @@ -66,8 +75,7 @@ func TracingMiddleware(path string, next http.HandlerFunc) http.HandlerFunc { } } -// RecoveryMiddleware for recovering from failed requests. -func RecoveryMiddleware(next http.HandlerFunc) http.HandlerFunc { +func recoveryMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { defer func() { if r := recover(); r != nil { @@ -88,3 +96,20 @@ func RecoveryMiddleware(next http.HandlerFunc) http.HandlerFunc { next(w, r) } } + +func authMiddleware(auth auth.Authenticator, next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + authenticated, err := auth.Authenticate(r) + if err != nil { + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + + if !authenticated { + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + return + } + + next(w, r) + } +} diff --git a/sync/http/middleware_test.go b/sync/http/middleware_test.go index 06d85519d..2be5b46c4 100644 --- a/sync/http/middleware_test.go +++ b/sync/http/middleware_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/mantzas/patron/errors" + "github.com/mantzas/patron/sync/http/auth" "github.com/stretchr/testify/assert" ) @@ -26,26 +27,32 @@ func testPanicHandleInt(w http.ResponseWriter, r *http.Request) { } func TestMiddleware(t *testing.T) { - r, _ := http.NewRequest("POST", "/test", nil) + r, err := http.NewRequest("POST", "/test", nil) + assert.NoError(t, err) type args struct { - next http.HandlerFunc - resp *httptest.ResponseRecorder + next http.HandlerFunc + trace bool + auth auth.Authenticator } tests := []struct { name string args args expectedCode int }{ - {"default middleware success", args{next: testHandle, resp: httptest.NewRecorder()}, 202}, - {"default middleware panic string", args{next: testPanicHandleString, resp: httptest.NewRecorder()}, 500}, - {"default middleware panic error", args{next: testPanicHandleError, resp: httptest.NewRecorder()}, 500}, - {"default middleware panic other", args{next: testPanicHandleInt, resp: httptest.NewRecorder()}, 500}, + {"middleware success", args{next: testHandle, trace: false, auth: &MockAuthenticator{success: true}}, 202}, + {"middleware trace success", args{next: testHandle, trace: true, auth: &MockAuthenticator{success: true}}, 202}, + {"middleware panic string", args{next: testPanicHandleString, trace: true, auth: &MockAuthenticator{success: true}}, 500}, + {"middleware panic error", args{next: testPanicHandleError, trace: true, auth: &MockAuthenticator{success: true}}, 500}, + {"middleware panic other", args{next: testPanicHandleInt, trace: true, auth: &MockAuthenticator{success: true}}, 500}, + {"middleware auth error", args{next: testPanicHandleInt, trace: true, auth: &MockAuthenticator{err: errors.New("TEST")}}, 500}, + {"middleware auth failure", args{next: testPanicHandleInt, trace: true, auth: &MockAuthenticator{success: false}}, 401}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - DefaultMiddleware("path", tt.args.next)(tt.args.resp, r) - assert.Equal(t, tt.expectedCode, tt.args.resp.Code) + resp := httptest.NewRecorder() + Middleware(tt.args.trace, tt.args.auth, "path", tt.args.next)(resp, r) + assert.Equal(t, tt.expectedCode, resp.Code) }) } } @@ -63,3 +70,29 @@ func TestResponseWriter(t *testing.T) { assert.True(t, rw.statusHeaderWritten, "expected to be true") assert.Equal(t, "test", rc.Body.String(), "body expected to be test but was %s", rc.Body.String()) } + +// func Test_authMiddleware(t *testing.T) { +// r, err := http.NewRequest("POST", "/test", nil) +// assert.NoError(t, err) + +// type args struct { +// auth Authenticator +// next http.HandlerFunc +// resp *httptest.ResponseRecorder +// } +// tests := []struct { +// name string +// args args +// expectedCode int +// }{ +// {name: "authenticated", args: args{auth: &MockAuthenticator{success: true}}, expectedCode: 202}, +// {name: "unauthorized", args: args{auth: &MockAuthenticator{success: false}}, expectedCode: 401}, +// {name: "error", args: args{auth: &MockAuthenticator{err: errors.New("TEST")}}, expectedCode: 500}, +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// authMiddleware(tt.args.auth, testHandle)(tt.args.resp, r) +// assert.Equal(t, tt.expectedCode, tt.args.resp.Code) +// }) +// } +// } diff --git a/sync/http/route.go b/sync/http/route.go index e09ec82e5..275477b28 100644 --- a/sync/http/route.go +++ b/sync/http/route.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/mantzas/patron/sync" + "github.com/mantzas/patron/sync/http/auth" ) // Route definition of a HTTP route. @@ -12,34 +13,60 @@ type Route struct { Method string Handler http.HandlerFunc Trace bool + Auth auth.Authenticator } // NewGetRoute creates a new GET route from a generic handler. func NewGetRoute(p string, pr sync.ProcessorFunc, trace bool) Route { - return NewRoute(p, http.MethodGet, pr, trace) + return NewRoute(p, http.MethodGet, pr, trace, nil) } // NewPostRoute creates a new POST route from a generic handler. func NewPostRoute(p string, pr sync.ProcessorFunc, trace bool) Route { - return NewRoute(p, http.MethodPost, pr, trace) + return NewRoute(p, http.MethodPost, pr, trace, nil) } // NewPutRoute creates a new PUT route from a generic handler. func NewPutRoute(p string, pr sync.ProcessorFunc, trace bool) Route { - return NewRoute(p, http.MethodPut, pr, trace) + return NewRoute(p, http.MethodPut, pr, trace, nil) } // NewDeleteRoute creates a new DELETE route from a generic handler. func NewDeleteRoute(p string, pr sync.ProcessorFunc, trace bool) Route { - return NewRoute(p, http.MethodDelete, pr, trace) + return NewRoute(p, http.MethodDelete, pr, trace, nil) } // NewRoute creates a new route from a generic handler. -func NewRoute(p string, m string, pr sync.ProcessorFunc, trace bool) Route { - return Route{Pattern: p, Method: m, Handler: handler(pr), Trace: trace} +func NewRoute(p string, m string, pr sync.ProcessorFunc, trace bool, auth auth.Authenticator) Route { + return Route{Pattern: p, Method: m, Handler: handler(pr), Trace: trace, Auth: auth} } // NewRouteRaw creates a new route from a HTTP handler. func NewRouteRaw(p string, m string, h http.HandlerFunc, trace bool) Route { return Route{Pattern: p, Method: m, Handler: h, Trace: trace} } + +// NewAuthGetRoute creates a new GET route from a generic handler. +func NewAuthGetRoute(p string, pr sync.ProcessorFunc, trace bool, auth auth.Authenticator) Route { + return NewRoute(p, http.MethodGet, pr, trace, auth) +} + +// NewAuthPostRoute creates a new POST route from a generic handler. +func NewAuthPostRoute(p string, pr sync.ProcessorFunc, trace bool, auth auth.Authenticator) Route { + return NewRoute(p, http.MethodPost, pr, trace, auth) +} + +// NewAuthPutRoute creates a new PUT route from a generic handler. +func NewAuthPutRoute(p string, pr sync.ProcessorFunc, trace bool, auth auth.Authenticator) Route { + return NewRoute(p, http.MethodPut, pr, trace, auth) +} + +// NewAuthDeleteRoute creates a new DELETE route from a generic handler. +func NewAuthDeleteRoute(p string, pr sync.ProcessorFunc, trace bool, auth auth.Authenticator) Route { + return NewRoute(p, http.MethodDelete, pr, trace, auth) +} + +// NewAuthRouteRaw creates a new route from a HTTP handler. +func NewAuthRouteRaw(p string, m string, h http.HandlerFunc, trace bool, auth auth.Authenticator) Route { + return Route{Pattern: p, Method: m, Handler: h, Trace: trace, Auth: auth} +} diff --git a/sync/http/route_test.go b/sync/http/route_test.go index 45bf11742..d2add45de 100644 --- a/sync/http/route_test.go +++ b/sync/http/route_test.go @@ -7,11 +7,24 @@ import ( "github.com/stretchr/testify/assert" ) +type MockAuthenticator struct { + success bool + err error +} + +func (mo MockAuthenticator) Authenticate(req *http.Request) (bool, error) { + if mo.err != nil { + return false, mo.err + } + return mo.success, nil +} + func TestNewRoute(t *testing.T) { - r := NewRoute("/index", http.MethodGet, nil, true) + r := NewRoute("/index", http.MethodGet, nil, true, &MockAuthenticator{}) assert.Equal(t, "/index", r.Pattern) assert.Equal(t, http.MethodGet, r.Method) assert.True(t, r.Trace) + assert.NotNil(t, r.Auth) } func TestNewGetRoute(t *testing.T) { @@ -19,6 +32,7 @@ func TestNewGetRoute(t *testing.T) { assert.Equal(t, "/index", r.Pattern) assert.Equal(t, http.MethodGet, r.Method) assert.True(t, r.Trace) + assert.Nil(t, r.Auth) } func TestNewPostRoute(t *testing.T) { @@ -26,6 +40,7 @@ func TestNewPostRoute(t *testing.T) { assert.Equal(t, "/index", r.Pattern) assert.Equal(t, http.MethodPost, r.Method) assert.True(t, r.Trace) + assert.Nil(t, r.Auth) } func TestNewPutRoute(t *testing.T) { @@ -33,6 +48,7 @@ func TestNewPutRoute(t *testing.T) { assert.Equal(t, "/index", r.Pattern) assert.Equal(t, http.MethodPut, r.Method) assert.True(t, r.Trace) + assert.Nil(t, r.Auth) } func TestNewDeleteRoute(t *testing.T) { @@ -40,10 +56,51 @@ func TestNewDeleteRoute(t *testing.T) { assert.Equal(t, "/index", r.Pattern) assert.Equal(t, http.MethodDelete, r.Method) assert.True(t, r.Trace) + assert.Nil(t, r.Auth) } func TestNewRouteRaw(t *testing.T) { r := NewRouteRaw("/index", http.MethodGet, nil, false) assert.Equal(t, "/index", r.Pattern) assert.Equal(t, "GET", r.Method) assert.False(t, r.Trace) + assert.Nil(t, r.Auth) +} + +func TestNewAuthGetRoute(t *testing.T) { + r := NewAuthGetRoute("/index", nil, true, &MockAuthenticator{}) + assert.Equal(t, "/index", r.Pattern) + assert.Equal(t, http.MethodGet, r.Method) + assert.True(t, r.Trace) + assert.NotNil(t, r.Auth) +} + +func TestNewAuthPostRoute(t *testing.T) { + r := NewAuthPostRoute("/index", nil, true, &MockAuthenticator{}) + assert.Equal(t, "/index", r.Pattern) + assert.Equal(t, http.MethodPost, r.Method) + assert.True(t, r.Trace) + assert.NotNil(t, r.Auth) +} + +func TestNewAuthPutRoute(t *testing.T) { + r := NewAuthPutRoute("/index", nil, true, &MockAuthenticator{}) + assert.Equal(t, "/index", r.Pattern) + assert.Equal(t, http.MethodPut, r.Method) + assert.True(t, r.Trace) + assert.NotNil(t, r.Auth) +} + +func TestNewAuthDeleteRoute(t *testing.T) { + r := NewAuthDeleteRoute("/index", nil, true, &MockAuthenticator{}) + assert.Equal(t, "/index", r.Pattern) + assert.Equal(t, http.MethodDelete, r.Method) + assert.True(t, r.Trace) + assert.NotNil(t, r.Auth) +} +func TestNewAuthRouteRaw(t *testing.T) { + r := NewAuthRouteRaw("/index", http.MethodGet, nil, false, &MockAuthenticator{}) + assert.Equal(t, "/index", r.Pattern) + assert.Equal(t, "GET", r.Method) + assert.False(t, r.Trace) + assert.NotNil(t, r.Auth) }