Skip to content

Commit

Permalink
test: refactored tests for authenticators
Browse files Browse the repository at this point in the history
* addressed code duplication in tests
* used sub tests to improve readability
* covered a few additional edged cases (passing unexpected types, empty
  realm)

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
  • Loading branch information
fredbi committed Dec 8, 2023
1 parent 42df208 commit 4bad8c4
Show file tree
Hide file tree
Showing 5 changed files with 694 additions and 632 deletions.
365 changes: 192 additions & 173 deletions security/apikey_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package security

import (
"context"
"fmt"
"net/http"
"testing"

Expand All @@ -25,181 +26,199 @@ import (
)

const (
apiToken = "token123"
apiTokenPrincipal = "admin"
apiKeyParam = "api_key"
apiKeyHeader = "X-API-KEY"
)

var tokenAuth = TokenAuthentication(func(token string) (interface{}, error) {
if token == apiToken {
return apiTokenPrincipal, nil
}
return nil, errors.Unauthenticated("token")
})

var tokenAuthCtx = TokenAuthenticationCtx(func(ctx context.Context, token string) (context.Context, interface{}, error) {
if token == apiToken {
return context.WithValue(ctx, extra, extraWisdom), apiTokenPrincipal, nil
}
return context.WithValue(ctx, reason, expReason), nil, errors.Unauthenticated("token")
})

func TestInvalidApiKeyAuthInitialization(t *testing.T) {
assert.Panics(t, func() { APIKeyAuth("api_key", "qery", tokenAuth) })
func TestApiKeyAuth(t *testing.T) {
tokenAuth := TokenAuthentication(func(token string) (interface{}, error) {
if token == validToken {
return principal, nil
}
return nil, errors.Unauthenticated("token")
})

t.Run("with invalid initialization", func(t *testing.T) {
assert.Panics(t, func() { APIKeyAuth(apiKeyParam, "qery", tokenAuth) })
})

t.Run("with token in query param", func(t *testing.T) {
ta := APIKeyAuth(apiKeyParam, query, tokenAuth)

t.Run("with valid token", func(t *testing.T) {
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf("%s?%s=%s", authPath, apiKeyParam, validToken), nil)
require.NoError(t, err)

ok, usr, err := ta.Authenticate(req)
assert.True(t, ok)
assert.Equal(t, principal, usr)
require.NoError(t, err)
})

t.Run("with invalid token", func(t *testing.T) {
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf("%s?%s=%s", authPath, apiKeyParam, invalidToken), nil)
require.NoError(t, err)

ok, usr, err := ta.Authenticate(req)
assert.True(t, ok)
assert.Equal(t, nil, usr)
require.Error(t, err)
})

t.Run("with missing token", func(t *testing.T) {
// put the token in the header, but query param is expected
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, authPath, nil)
require.NoError(t, err)
req.Header.Set(apiKeyHeader, validToken)

ok, usr, err := ta.Authenticate(req)
assert.False(t, ok)
assert.Equal(t, nil, usr)
require.NoError(t, err)
})
})

t.Run("with token in header", func(t *testing.T) {
ta := APIKeyAuth(apiKeyHeader, header, tokenAuth)

t.Run("with valid token", func(t *testing.T) {
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, authPath, nil)
require.NoError(t, err)
req.Header.Set(apiKeyHeader, validToken)

ok, usr, err := ta.Authenticate(req)
assert.True(t, ok)
assert.Equal(t, principal, usr)
require.NoError(t, err)
})

t.Run("with invalid token", func(t *testing.T) {
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, authPath, nil)
require.NoError(t, err)
req.Header.Set(apiKeyHeader, invalidToken)

ok, usr, err := ta.Authenticate(req)
assert.True(t, ok)
assert.Equal(t, nil, usr)
require.Error(t, err)
})

t.Run("with missing token", func(t *testing.T) {
// put the token in the query param, but header is expected
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf("%s?%s=%s", authPath, apiKeyParam, validToken), nil)
require.NoError(t, err)

ok, usr, err := ta.Authenticate(req)
assert.False(t, ok)
assert.Equal(t, nil, usr)
require.NoError(t, err)
})
})
}

func TestValidApiKeyAuth(t *testing.T) {
ta := APIKeyAuth("api_key", "query", tokenAuth)
ta2 := APIKeyAuth("X-API-KEY", "header", tokenAuth)

req1, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah?api_key=token123", nil)
require.NoError(t, err)

ok, usr, err := ta.Authenticate(req1)
assert.True(t, ok)
assert.Equal(t, apiTokenPrincipal, usr)
require.NoError(t, err)

req2, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah", nil)
require.NoError(t, err)
req2.Header.Set("X-API-KEY", apiToken)

ok, usr, err = ta2.Authenticate(req2)
assert.True(t, ok)
assert.Equal(t, apiTokenPrincipal, usr)
require.NoError(t, err)
}

func TestInvalidApiKeyAuth(t *testing.T) {
ta := APIKeyAuth("api_key", "query", tokenAuth)
ta2 := APIKeyAuth("X-API-KEY", "header", tokenAuth)

req1, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah?api_key=token124", nil)
require.NoError(t, err)

ok, usr, err := ta.Authenticate(req1)
assert.True(t, ok)
assert.Equal(t, nil, usr)
require.Error(t, err)

req2, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah", nil)
require.NoError(t, err)
req2.Header.Set("X-API-KEY", "token124")

ok, usr, err = ta2.Authenticate(req2)
assert.True(t, ok)
assert.Equal(t, nil, usr)
require.Error(t, err)
}

func TestMissingApiKeyAuth(t *testing.T) {
ta := APIKeyAuth("api_key", "query", tokenAuth)
ta2 := APIKeyAuth("X-API-KEY", "header", tokenAuth)

req1, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah", nil)
require.NoError(t, err)
req1.Header.Set("X-API-KEY", apiToken)

ok, usr, err := ta.Authenticate(req1)
assert.False(t, ok)
assert.Equal(t, nil, usr)
require.NoError(t, err)

req2, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah?api_key=token123", nil)
require.NoError(t, err)

ok, usr, err = ta2.Authenticate(req2)
assert.False(t, ok)
assert.Equal(t, nil, usr)
require.NoError(t, err)
}

func TestInvalidApiKeyAuthInitializationCtx(t *testing.T) {
assert.Panics(t, func() { APIKeyAuthCtx("api_key", "qery", tokenAuthCtx) })
}

func TestValidApiKeyAuthCtx(t *testing.T) {
ta := APIKeyAuthCtx("api_key", "query", tokenAuthCtx)
ta2 := APIKeyAuthCtx("X-API-KEY", "header", tokenAuthCtx)

req1, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah?api_key=token123", nil)
require.NoError(t, err)
req1 = req1.WithContext(context.WithValue(req1.Context(), original, wisdom))
ok, usr, err := ta.Authenticate(req1)
assert.True(t, ok)
assert.Equal(t, apiTokenPrincipal, usr)
require.NoError(t, err)
assert.Equal(t, wisdom, req1.Context().Value(original))
assert.Equal(t, extraWisdom, req1.Context().Value(extra))
assert.Nil(t, req1.Context().Value(reason))

req2, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah", nil)
require.NoError(t, err)
req2 = req2.WithContext(context.WithValue(req2.Context(), original, wisdom))
req2.Header.Set("X-API-KEY", apiToken)

ok, usr, err = ta2.Authenticate(req2)
assert.True(t, ok)
assert.Equal(t, apiTokenPrincipal, usr)
require.NoError(t, err)
assert.Equal(t, wisdom, req2.Context().Value(original))
assert.Equal(t, extraWisdom, req2.Context().Value(extra))
assert.Nil(t, req2.Context().Value(reason))
}

func TestInvalidApiKeyAuthCtx(t *testing.T) {
ta := APIKeyAuthCtx("api_key", "query", tokenAuthCtx)
ta2 := APIKeyAuthCtx("X-API-KEY", "header", tokenAuthCtx)

req1, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah?api_key=token124", nil)
require.NoError(t, err)
req1 = req1.WithContext(context.WithValue(req1.Context(), original, wisdom))
ok, usr, err := ta.Authenticate(req1)
assert.True(t, ok)
assert.Equal(t, nil, usr)
require.Error(t, err)
assert.Equal(t, wisdom, req1.Context().Value(original))
assert.Equal(t, expReason, req1.Context().Value(reason))
assert.Nil(t, req1.Context().Value(extra))

req2, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah", nil)
require.NoError(t, err)
req2 = req2.WithContext(context.WithValue(req2.Context(), original, wisdom))
req2.Header.Set("X-API-KEY", "token124")

ok, usr, err = ta2.Authenticate(req2)
assert.True(t, ok)
assert.Equal(t, nil, usr)
require.Error(t, err)
assert.Equal(t, wisdom, req2.Context().Value(original))
assert.Equal(t, expReason, req2.Context().Value(reason))
assert.Nil(t, req2.Context().Value(extra))
}

func TestMissingApiKeyAuthCtx(t *testing.T) {
ta := APIKeyAuthCtx("api_key", "query", tokenAuthCtx)
ta2 := APIKeyAuthCtx("X-API-KEY", "header", tokenAuthCtx)

req1, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah", nil)
require.NoError(t, err)
req1 = req1.WithContext(context.WithValue(req1.Context(), original, wisdom))
req1.Header.Set("X-API-KEY", apiToken)

ok, usr, err := ta.Authenticate(req1)
assert.False(t, ok)
assert.Equal(t, nil, usr)
require.NoError(t, err)
assert.Equal(t, wisdom, req1.Context().Value(original))
assert.Nil(t, req1.Context().Value(reason))
assert.Nil(t, req1.Context().Value(extra))

req2, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/blah?api_key=token123", nil)
require.NoError(t, err)
req2 = req2.WithContext(context.WithValue(req2.Context(), original, wisdom))
ok, usr, err = ta2.Authenticate(req2)
assert.False(t, ok)
assert.Equal(t, nil, usr)
require.NoError(t, err)
assert.Equal(t, wisdom, req2.Context().Value(original))
assert.Nil(t, req2.Context().Value(reason))
assert.Nil(t, req2.Context().Value(extra))
func TestApiKeyAuthCtx(t *testing.T) {
tokenAuthCtx := TokenAuthenticationCtx(func(ctx context.Context, token string) (context.Context, interface{}, error) {
if token == validToken {
return context.WithValue(ctx, extra, extraWisdom), principal, nil
}
return context.WithValue(ctx, reason, expReason), nil, errors.Unauthenticated("token")
})
ctx := context.WithValue(context.Background(), original, wisdom)

t.Run("with invalid initialization", func(t *testing.T) {
assert.Panics(t, func() { APIKeyAuthCtx(apiKeyParam, "qery", tokenAuthCtx) })
})

t.Run("with token in query param", func(t *testing.T) {
ta := APIKeyAuthCtx(apiKeyParam, query, tokenAuthCtx)

t.Run("with valid token", func(t *testing.T) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s?%s=%s", authPath, apiKeyParam, validToken), nil)
require.NoError(t, err)
ok, usr, err := ta.Authenticate(req)
assert.True(t, ok)
assert.Equal(t, principal, usr)
require.NoError(t, err)

assert.Equal(t, wisdom, req.Context().Value(original))
assert.Equal(t, extraWisdom, req.Context().Value(extra))
assert.Nil(t, req.Context().Value(reason))
})

t.Run("with invalid token", func(t *testing.T) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s?%s=%s", authPath, apiKeyParam, invalidToken), nil)
require.NoError(t, err)
ok, usr, err := ta.Authenticate(req)
assert.True(t, ok)
assert.Equal(t, nil, usr)
require.Error(t, err)

assert.Equal(t, wisdom, req.Context().Value(original))
assert.Equal(t, expReason, req.Context().Value(reason))
assert.Nil(t, req.Context().Value(extra))
})

t.Run("with missing token", func(t *testing.T) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, authPath, nil)
require.NoError(t, err)
req.Header.Set(apiKeyHeader, validToken)

ok, usr, err := ta.Authenticate(req)
assert.False(t, ok)
assert.Equal(t, nil, usr)
require.NoError(t, err)

assert.Equal(t, wisdom, req.Context().Value(original))
assert.Nil(t, req.Context().Value(reason))
assert.Nil(t, req.Context().Value(extra))
})
})

t.Run("with token in header", func(t *testing.T) {
ta := APIKeyAuthCtx(apiKeyHeader, header, tokenAuthCtx)

t.Run("with valid token", func(t *testing.T) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, authPath, nil)
require.NoError(t, err)
req.Header.Set(apiKeyHeader, validToken)

ok, usr, err := ta.Authenticate(req)
assert.True(t, ok)
assert.Equal(t, principal, usr)
require.NoError(t, err)

assert.Equal(t, wisdom, req.Context().Value(original))
assert.Equal(t, extraWisdom, req.Context().Value(extra))
assert.Nil(t, req.Context().Value(reason))
})

t.Run("with invalid token", func(t *testing.T) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, authPath, nil)
require.NoError(t, err)
req.Header.Set(apiKeyHeader, invalidToken)

ok, usr, err := ta.Authenticate(req)
assert.True(t, ok)
assert.Equal(t, nil, usr)
require.Error(t, err)

assert.Equal(t, wisdom, req.Context().Value(original))
assert.Equal(t, expReason, req.Context().Value(reason))
assert.Nil(t, req.Context().Value(extra))
})

t.Run("with missing token", func(t *testing.T) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s?%s=%s", authPath, apiKeyParam, validToken), nil)
require.NoError(t, err)

ok, usr, err := ta.Authenticate(req)
assert.False(t, ok)
assert.Equal(t, nil, usr)
require.NoError(t, err)

assert.Equal(t, wisdom, req.Context().Value(original))
assert.Nil(t, req.Context().Value(reason))
assert.Nil(t, req.Context().Value(extra))
})
})
}
Loading

0 comments on commit 4bad8c4

Please sign in to comment.