From e45e5ed6f931883b2b964644cce5c654c570bfc9 Mon Sep 17 00:00:00 2001 From: Evgeniy Kulikov Date: Mon, 25 Jan 2021 17:26:08 +0300 Subject: [PATCH] Refactoring and add tests - rename `header.go` to `bearer.go` - simplify header / cookie fetching of bearer token value - simplify `BearerToken` method - increase tests Signed-off-by: Evgeniy Kulikov --- bearer.go | 79 ++++++++++++++++++++++++++++ bearer_test.go | 139 +++++++++++++++++++++++++++++++++++++++++++++++++ header.go | 76 --------------------------- 3 files changed, 218 insertions(+), 76 deletions(-) create mode 100644 bearer.go create mode 100644 bearer_test.go delete mode 100644 header.go diff --git a/bearer.go b/bearer.go new file mode 100644 index 0000000..f98b41d --- /dev/null +++ b/bearer.go @@ -0,0 +1,79 @@ +package main + +import ( + "bytes" + "encoding/base64" + + "github.com/pkg/errors" + + "github.com/nspcc-dev/neofs-api-go/pkg/token" + "github.com/valyala/fasthttp" +) + +type fromHandler = func(h *fasthttp.RequestHeader) []byte + +const bearerToken = "Bearer" + +// BearerToken usage: +// +// if tkn, err = BearerToken(c); err != nil && tkn == nil { +// log.Error("could not fetch bearer token", zap.Error(err)) +// c.Error("could not fetch bearer token", fasthttp.StatusBadRequest) +// return +// } +var _ = BearerToken + +func fromHeader(h *fasthttp.RequestHeader) []byte { + auth := h.Peek(fasthttp.HeaderAuthorization) + if auth == nil || !bytes.HasPrefix(auth, []byte(bearerToken)) { + return nil + } + + if auth = bytes.TrimPrefix(auth, []byte(bearerToken+" ")); len(auth) == 0 { + return nil + } + + return auth +} + +func fromCookie(h *fasthttp.RequestHeader) []byte { + auth := h.Cookie(bearerToken) + if len(auth) == 0 { + return nil + } + + return auth +} + +func BearerToken(ctx *fasthttp.RequestCtx) (*token.BearerToken, error) { + // ignore empty value + if ctx == nil { + panic(nil) + return nil, nil + } + + var ( + lastErr error + + buf []byte + tkn = new(token.BearerToken) + ) + + for _, parse := range []fromHandler{fromHeader, fromCookie} { + if buf = parse(&ctx.Request.Header); buf == nil { + continue + } else if data, err := base64.StdEncoding.DecodeString(string(buf)); err != nil { + lastErr = errors.Wrap(err, "could not fetch marshaled from base64") + continue + } else if err = tkn.Unmarshal(data); err != nil { + lastErr = errors.Wrap(err, "could not unmarshal bearer token") + continue + } else if tkn == nil { + continue + } + + return tkn, nil + } + + return nil, lastErr +} diff --git a/bearer_test.go b/bearer_test.go new file mode 100644 index 0000000..f9d998d --- /dev/null +++ b/bearer_test.go @@ -0,0 +1,139 @@ +package main + +import ( + "encoding/base64" + "testing" + + "github.com/nspcc-dev/neofs-api-go/pkg/owner" + + "github.com/nspcc-dev/neofs-api-go/pkg/token" + + "github.com/stretchr/testify/require" + "github.com/valyala/fasthttp" +) + +func makeTestCookie(value []byte) *fasthttp.RequestHeader { + header := new(fasthttp.RequestHeader) + header.SetCookie(bearerToken, string(value)) + return header +} + +func makeTestHeader(value []byte) *fasthttp.RequestHeader { + header := new(fasthttp.RequestHeader) + if value != nil { + header.Set(fasthttp.HeaderAuthorization, bearerToken+" "+string(value)) + } + return header +} + +func Test_fromCookie(t *testing.T) { + cases := []struct { + name string + actual []byte + expect []byte + }{ + {name: "empty"}, + {name: "normal", actual: []byte("TOKEN"), expect: []byte("TOKEN")}, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.expect, fromCookie(makeTestCookie(tt.actual))) + }) + } +} + +func Test_fromHeader(t *testing.T) { + cases := []struct { + name string + actual []byte + expect []byte + }{ + {name: "empty"}, + {name: "normal", actual: []byte("TOKEN"), expect: []byte("TOKEN")}, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.expect, fromHeader(makeTestHeader(tt.actual))) + }) + } +} + +func TestBearerToken(t *testing.T) { + uid := owner.NewID() + + tkn := new(token.BearerToken) + tkn.SetOwner(uid) + + data, err := tkn.Marshal() + + require.NoError(t, err) + + t64 := base64.StdEncoding.EncodeToString(data) + require.NotEmpty(t, t64) + + cases := []struct { + name string + + cookie string + header string + + error string + expect *token.BearerToken + }{ + {name: "empty"}, + + {name: "bad base64 header", header: "WRONG BASE64", error: "could not fetch marshaled from base64"}, + {name: "bad base64 cookie", cookie: "WRONG BASE64", error: "could not fetch marshaled from base64"}, + + {name: "header token unmarshal error", header: "dGVzdAo=", error: "could not unmarshal bearer token"}, + {name: "cookie token unmarshal error", cookie: "dGVzdAo=", error: "could not unmarshal bearer token"}, + + { + name: "bad header and cookie", + header: "WRONG BASE64", + cookie: "dGVzdAo=", + error: "could not unmarshal bearer token", + }, + + { + name: "bad header, but good cookie", + header: "dGVzdAo=", + cookie: t64, + expect: tkn, + }, + + {name: "ok for header", header: t64, expect: tkn}, + {name: "ok for cookie", cookie: t64, expect: tkn}, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + ctx := makeTestRequest(tt.cookie, tt.header) + actual, err := BearerToken(ctx) + + if tt.error == "" { + require.NoError(t, err) + require.Equal(t, tt.expect, actual) + + return + } + + require.Contains(t, err.Error(), tt.error) + }) + } +} + +func makeTestRequest(cookie, header string) *fasthttp.RequestCtx { + ctx := new(fasthttp.RequestCtx) + + if cookie != "" { + ctx.Request.Header.SetCookie(bearerToken, cookie) + } + + if header != "" { + ctx.Request.Header.Set(fasthttp.HeaderAuthorization, bearerToken+" "+header) + } + return ctx +} diff --git a/header.go b/header.go deleted file mode 100644 index c359054..0000000 --- a/header.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "bytes" - "encoding/base64" - - "github.com/nspcc-dev/neofs-api-go/pkg/token" - "github.com/pkg/errors" - "github.com/valyala/fasthttp" -) - -const bearerToken = "Bearer" - -// BearerToken usage: -// -// if tkn, err = BearerToken(c); err != nil && tkn == nil { -// log.Error("could not fetch bearer token", zap.Error(err)) -// c.Error("could not fetch bearer token", fasthttp.StatusBadRequest) -// return -// } -var _ = BearerToken - -func headerAuth(h *fasthttp.RequestHeader) (*token.BearerToken, error) { - auth := h.Peek(fasthttp.HeaderAuthorization) - if auth == nil || !bytes.Contains(auth, []byte(bearerToken)) { - return nil, nil - } - - auth = bytes.ReplaceAll(auth, []byte(bearerToken+" "), nil) - - data, err := base64.StdEncoding.DecodeString(string(auth)) - if err != nil { - return nil, errors.Wrap(err, "could not fetch marshaled from base64") - } - - tkn := new(token.BearerToken) - if err = tkn.Unmarshal(data); err != nil { - return nil, errors.Wrap(err, "could unmarshal bearer token") - } - - return tkn, nil -} - -func cookieAuth(h *fasthttp.RequestHeader) (*token.BearerToken, error) { - auth := h.Cookie(bearerToken) - if auth == nil { - return nil, nil - } - - data, err := base64.StdEncoding.DecodeString(string(auth)) - if err != nil { - return nil, errors.Wrap(err, "could not fetch marshaled from base64") - } - - tkn := new(token.BearerToken) - if err = tkn.Unmarshal(data); err != nil { - return nil, errors.Wrap(err, "could unmarshal bearer token") - } - - return tkn, nil -} - -func BearerToken(ctx *fasthttp.RequestCtx) (*token.BearerToken, error) { - // ignore empty value - if ctx == nil { - return nil, nil - } - - if tkn, err := headerAuth(&ctx.Request.Header); err != nil { - return nil, err - } else if tkn != nil { - return tkn, nil - } - - return cookieAuth(&ctx.Request.Header) -}