401 lines
9.5 KiB
Go
401 lines
9.5 KiB
Go
package http_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/influxdata/influxdb/v2"
|
|
platformhttp "github.com/influxdata/influxdb/v2/http"
|
|
"github.com/influxdata/influxdb/v2/jsonweb"
|
|
"github.com/influxdata/influxdb/v2/kit/platform"
|
|
kithttp "github.com/influxdata/influxdb/v2/kit/transport/http"
|
|
"github.com/influxdata/influxdb/v2/mock"
|
|
"github.com/influxdata/influxdb/v2/session"
|
|
"go.uber.org/zap/zaptest"
|
|
)
|
|
|
|
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjbG91ZDIuaW5mbHV4ZGF0YS5jb20iLCJhdWQiOiJnYXRld2F5LmluZmx1eGRhdGEuY29tIiwiaWF0IjoxNTY4NjI4OTgwLCJraWQiOiJzb21lLWtleSIsInBlcm1pc3Npb25zIjpbeyJhY3Rpb24iOiJ3cml0ZSIsInJlc291cmNlIjp7InR5cGUiOiJidWNrZXRzIiwiaWQiOiIwMDAwMDAwMDAwMDAwMDAxIiwib3JnSUQiOiIwMDAwMDAwMDAwMDAwMDAyIn19XX0.74vjbExiOd702VSIMmQWaDT_GFvUI0-_P-SfQ_OOHB0"
|
|
|
|
var one = platform.ID(1)
|
|
|
|
func TestAuthenticationHandler(t *testing.T) {
|
|
type fields struct {
|
|
AuthorizationService influxdb.AuthorizationService
|
|
SessionService influxdb.SessionService
|
|
UserService influxdb.UserService
|
|
TokenParser *jsonweb.TokenParser
|
|
}
|
|
type args struct {
|
|
token string
|
|
session string
|
|
}
|
|
type wants struct {
|
|
code int
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
wants wants
|
|
}{
|
|
{
|
|
name: "session provided",
|
|
fields: fields{
|
|
AuthorizationService: mock.NewAuthorizationService(),
|
|
SessionService: &mock.SessionService{
|
|
FindSessionFn: func(ctx context.Context, key string) (*influxdb.Session, error) {
|
|
return &influxdb.Session{}, nil
|
|
},
|
|
RenewSessionFn: func(ctx context.Context, session *influxdb.Session, expiredAt time.Time) error {
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
session: "abc123",
|
|
},
|
|
wants: wants{
|
|
code: http.StatusOK,
|
|
},
|
|
},
|
|
{
|
|
name: "session does not exist",
|
|
fields: fields{
|
|
AuthorizationService: mock.NewAuthorizationService(),
|
|
SessionService: &mock.SessionService{
|
|
FindSessionFn: func(ctx context.Context, key string) (*influxdb.Session, error) {
|
|
return nil, fmt.Errorf("session not found")
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
session: "abc123",
|
|
},
|
|
wants: wants{
|
|
code: http.StatusUnauthorized,
|
|
},
|
|
},
|
|
{
|
|
name: "token provided",
|
|
fields: fields{
|
|
AuthorizationService: &mock.AuthorizationService{
|
|
FindAuthorizationByTokenFn: func(ctx context.Context, token string) (*influxdb.Authorization, error) {
|
|
return &influxdb.Authorization{}, nil
|
|
},
|
|
},
|
|
SessionService: mock.NewSessionService(),
|
|
},
|
|
args: args{
|
|
token: "abc123",
|
|
},
|
|
wants: wants{
|
|
code: http.StatusOK,
|
|
},
|
|
},
|
|
{
|
|
name: "token does not exist",
|
|
fields: fields{
|
|
AuthorizationService: &mock.AuthorizationService{
|
|
FindAuthorizationByTokenFn: func(ctx context.Context, token string) (*influxdb.Authorization, error) {
|
|
return nil, fmt.Errorf("authorization not found")
|
|
},
|
|
},
|
|
SessionService: mock.NewSessionService(),
|
|
},
|
|
args: args{
|
|
token: "abc123",
|
|
},
|
|
wants: wants{
|
|
code: http.StatusUnauthorized,
|
|
},
|
|
},
|
|
{
|
|
name: "associated user is inactive",
|
|
fields: fields{
|
|
AuthorizationService: &mock.AuthorizationService{
|
|
FindAuthorizationByTokenFn: func(ctx context.Context, token string) (*influxdb.Authorization, error) {
|
|
return &influxdb.Authorization{UserID: one}, nil
|
|
},
|
|
},
|
|
SessionService: mock.NewSessionService(),
|
|
UserService: &mock.UserService{
|
|
FindUserByIDFn: func(ctx context.Context, id platform.ID) (*influxdb.User, error) {
|
|
if !id.Valid() {
|
|
panic("user service should only be called with valid user ID")
|
|
}
|
|
|
|
return &influxdb.User{Status: "inactive"}, nil
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
token: "abc123",
|
|
},
|
|
wants: wants{
|
|
code: http.StatusForbidden,
|
|
},
|
|
},
|
|
{
|
|
name: "no auth provided",
|
|
fields: fields{
|
|
AuthorizationService: mock.NewAuthorizationService(),
|
|
SessionService: mock.NewSessionService(),
|
|
},
|
|
args: args{},
|
|
wants: wants{
|
|
code: http.StatusUnauthorized,
|
|
},
|
|
},
|
|
{
|
|
name: "jwt provided",
|
|
fields: fields{
|
|
AuthorizationService: &mock.AuthorizationService{
|
|
FindAuthorizationByTokenFn: func(ctx context.Context, token string) (*influxdb.Authorization, error) {
|
|
return nil, fmt.Errorf("authorization not found")
|
|
},
|
|
},
|
|
SessionService: mock.NewSessionService(),
|
|
UserService: &mock.UserService{
|
|
FindUserByIDFn: func(ctx context.Context, id platform.ID) (*influxdb.User, error) {
|
|
// ensure that this is not reached as jwt token authorizer produces
|
|
// invalid user id
|
|
if !id.Valid() {
|
|
panic("user service should only be called with valid user ID")
|
|
}
|
|
|
|
return nil, errors.New("user not found")
|
|
},
|
|
},
|
|
TokenParser: jsonweb.NewTokenParser(jsonweb.KeyStoreFunc(func(string) ([]byte, error) {
|
|
return []byte("correct-key"), nil
|
|
})),
|
|
},
|
|
args: args{
|
|
token: token,
|
|
},
|
|
wants: wants{
|
|
code: http.StatusOK,
|
|
},
|
|
},
|
|
{
|
|
name: "jwt provided - bad signature",
|
|
fields: fields{
|
|
AuthorizationService: &mock.AuthorizationService{
|
|
FindAuthorizationByTokenFn: func(ctx context.Context, token string) (*influxdb.Authorization, error) {
|
|
panic("token lookup attempted")
|
|
},
|
|
},
|
|
SessionService: mock.NewSessionService(),
|
|
TokenParser: jsonweb.NewTokenParser(jsonweb.KeyStoreFunc(func(string) ([]byte, error) {
|
|
return []byte("incorrect-key"), nil
|
|
})),
|
|
},
|
|
args: args{
|
|
token: token,
|
|
},
|
|
wants: wants{
|
|
code: http.StatusUnauthorized,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
|
|
h := platformhttp.NewAuthenticationHandler(zaptest.NewLogger(t), kithttp.NewErrorHandler(zaptest.NewLogger(t)))
|
|
h.AuthorizationService = tt.fields.AuthorizationService
|
|
h.SessionService = tt.fields.SessionService
|
|
h.UserService = &mock.UserService{
|
|
FindUserByIDFn: func(ctx context.Context, id platform.ID) (*influxdb.User, error) {
|
|
return &influxdb.User{}, nil
|
|
},
|
|
}
|
|
|
|
if tt.fields.UserService != nil {
|
|
h.UserService = tt.fields.UserService
|
|
}
|
|
|
|
if tt.fields.TokenParser != nil {
|
|
h.TokenParser = tt.fields.TokenParser
|
|
}
|
|
|
|
h.Handler = handler
|
|
|
|
w := httptest.NewRecorder()
|
|
r := httptest.NewRequest("POST", "http://any.url", nil)
|
|
|
|
if tt.args.session != "" {
|
|
session.SetCookieSession(tt.args.session, r)
|
|
}
|
|
|
|
if tt.args.token != "" {
|
|
platformhttp.SetToken(tt.args.token, r)
|
|
}
|
|
|
|
h.ServeHTTP(w, r)
|
|
|
|
if got, want := w.Code, tt.wants.code; got != want {
|
|
t.Errorf("expected status code to be %d got %d", want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProbeAuthScheme(t *testing.T) {
|
|
type args struct {
|
|
token string
|
|
session string
|
|
}
|
|
type wants struct {
|
|
scheme string
|
|
err error
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
wants wants
|
|
}{
|
|
{
|
|
name: "session provided",
|
|
args: args{
|
|
session: "abc123",
|
|
},
|
|
wants: wants{
|
|
scheme: "session",
|
|
},
|
|
},
|
|
{
|
|
name: "token provided",
|
|
args: args{
|
|
token: "abc123",
|
|
},
|
|
wants: wants{
|
|
scheme: "token",
|
|
},
|
|
},
|
|
{
|
|
name: "no auth provided",
|
|
args: args{},
|
|
wants: wants{
|
|
err: fmt.Errorf("unknown authentication scheme"),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := httptest.NewRequest("POST", "http://any.url", nil)
|
|
|
|
if tt.args.session != "" {
|
|
session.SetCookieSession(tt.args.session, r)
|
|
}
|
|
|
|
if tt.args.token != "" {
|
|
platformhttp.SetToken(tt.args.token, r)
|
|
}
|
|
|
|
scheme, err := platformhttp.ProbeAuthScheme(r)
|
|
if (err != nil) != (tt.wants.err != nil) {
|
|
t.Errorf("unexpected error got %v want %v", err, tt.wants.err)
|
|
return
|
|
}
|
|
|
|
if got, want := scheme, tt.wants.scheme; got != want {
|
|
t.Errorf("expected scheme to be %s got %s", want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAuthenticationHandler_NoAuthRoutes(t *testing.T) {
|
|
type route struct {
|
|
method string
|
|
path string
|
|
}
|
|
type fields struct {
|
|
excludedRoutes []route
|
|
}
|
|
type args struct {
|
|
method string
|
|
path string
|
|
}
|
|
type wants struct {
|
|
code int
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
wants wants
|
|
}{
|
|
{
|
|
name: "route is no auth route",
|
|
fields: fields{
|
|
excludedRoutes: []route{
|
|
{
|
|
method: "GET",
|
|
path: "/debug/pprof",
|
|
},
|
|
},
|
|
},
|
|
args: args{
|
|
method: "GET",
|
|
path: "/debug/pprof",
|
|
},
|
|
wants: wants{
|
|
code: http.StatusOK,
|
|
},
|
|
},
|
|
{
|
|
name: "route is an auth route",
|
|
fields: fields{
|
|
excludedRoutes: []route{},
|
|
},
|
|
args: args{
|
|
method: "POST",
|
|
path: "/api/v2/write",
|
|
},
|
|
wants: wants{
|
|
code: http.StatusUnauthorized,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
|
|
h := platformhttp.NewAuthenticationHandler(zaptest.NewLogger(t), kithttp.NewErrorHandler(zaptest.NewLogger(t)))
|
|
h.AuthorizationService = mock.NewAuthorizationService()
|
|
h.SessionService = mock.NewSessionService()
|
|
h.Handler = handler
|
|
|
|
for _, rte := range tt.fields.excludedRoutes {
|
|
h.RegisterNoAuthRoute(rte.method, rte.path)
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
r := httptest.NewRequest(tt.args.method, tt.args.path, nil)
|
|
|
|
h.ServeHTTP(w, r)
|
|
|
|
if got, want := w.Code, tt.wants.code; got != want {
|
|
t.Errorf("expected status code to be %d got %d", want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|