Files
cloud-services/pkg/httphandlers/auth_apitoken_test.go

244 lines
7.0 KiB
Go

package httphandlers_test
import (
"errors"
"net/http"
"strings"
"testing"
"time"
"github.com/fiskerinc/cloud-services/pkg/adminroles"
"github.com/fiskerinc/cloud-services/pkg/cache"
"github.com/fiskerinc/cloud-services/pkg/common"
"github.com/fiskerinc/cloud-services/pkg/common/authproviders"
c "github.com/fiskerinc/cloud-services/pkg/common/context"
"github.com/fiskerinc/cloud-services/pkg/db/queries"
"github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
"github.com/fiskerinc/cloud-services/pkg/httphandlers"
"github.com/fiskerinc/cloud-services/pkg/redis"
"github.com/fiskerinc/cloud-services/pkg/testhelper"
)
type testCaseAuthAPIToken struct {
RedisClient redis.Client
Query queries.APITokensInterface
APICalls queries.APICallsInterface
RequiredRoles map[string][]adminroles.RoleID
JWTAuth bool
ExpectedProvider string
testhelper.BasicHttpTest
}
func TestAuthAPIToken(t *testing.T) {
adminroles.RoleCreate = adminroles.RoleID("efcc3025-e2d8-4212-8227-805c7be39d2c")
adminroles.RoleDelete = adminroles.RoleID("8f78dce7-f5f9-4033-a10c-c9c7408bfcfe")
someErr := errors.New("some err")
validExpiresAt := time.Now().Add(time.Hour)
client := &RedisMockAuthAPIToken{
GetCacheError: redis.ErrNilObject,
}
db := &mocks.MockAPITokens{
DBMockHelper: mocks.DBMockHelper{
Error: errors.New("token not found"),
},
}
dbCalls := &mocks.MockAPICalls{
DBMockHelper: mocks.DBMockHelper{Error: nil},
}
apiToken := "XXXXXXXXXXXX"
req := testhelper.MakeTestRequestWithHeaders(http.MethodGet, "/", map[string]string{cache.ApiKeyHeader: apiToken}, nil)
roles := map[string][]adminroles.RoleID{
authproviders.Default: {adminroles.RoleCreate},
}
tests := []testCaseAuthAPIToken{
{
BasicHttpTest: testhelper.BasicHttpTest{
Name: "Good API Token, no required permission",
Request: req,
ExpectedStatus: http.StatusOK,
ExpectedResponse: `OK`,
},
RequiredRoles: nil,
RedisClient: client,
},
{
BasicHttpTest: testhelper.BasicHttpTest{
Name: "Good API Token",
Request: req,
ExpectedStatus: http.StatusOK,
ExpectedResponse: `OK`,
},
RequiredRoles: roles,
RedisClient: client,
Query: &mocks.MockAPITokens{
GetResult: &common.APIToken{
Token: apiToken,
Roles: strings.Join([]string{string(adminroles.RoleCreate)}, ","),
},
},
APICalls: dbCalls,
},
{
BasicHttpTest: testhelper.BasicHttpTest{
Name: "Good API Token with expiration",
Request: req,
ExpectedStatus: http.StatusOK,
ExpectedResponse: `OK`,
},
RequiredRoles: roles,
RedisClient: client,
Query: &mocks.MockAPITokens{
GetResult: &common.APIToken{
Token: apiToken,
Roles: strings.Join([]string{string(adminroles.RoleCreate)}, ","),
ExpiresAt: &validExpiresAt,
},
},
APICalls: dbCalls,
},
{
BasicHttpTest: testhelper.BasicHttpTest{
Name: "Good API Token, without permission",
Request: req,
ExpectedStatus: http.StatusUnauthorized,
ExpectedResponse: `{"message":"missing permission","error":"Unauthorized"}`,
},
RequiredRoles: roles,
RedisClient: client,
Query: &mocks.MockAPITokens{
GetResult: &common.APIToken{
Token: apiToken,
Roles: strings.Join([]string{string(adminroles.RoleDelete)}, ","),
},
},
APICalls: dbCalls,
},
{
BasicHttpTest: testhelper.BasicHttpTest{
Name: "Bad API Token",
Request: req,
ExpectedStatus: http.StatusUnauthorized,
ExpectedResponse: `{"message":"token not found","error":"Unauthorized"}`,
},
RequiredRoles: roles,
RedisClient: client,
Query: db,
APICalls: dbCalls,
},
{
BasicHttpTest: testhelper.BasicHttpTest{
Name: "Unknown API Token",
Request: testhelper.MakeTestRequestWithHeaders(http.MethodGet, "/", map[string]string{cache.ApiKeyHeader: "abc"}, nil),
ExpectedStatus: http.StatusUnauthorized,
ExpectedResponse: `{"message":"token not found","error":"Unauthorized"}`,
},
RequiredRoles: roles,
RedisClient: client,
Query: db,
APICalls: dbCalls,
},
{
BasicHttpTest: testhelper.BasicHttpTest{
Name: "No headers",
Request: testhelper.MakeTestRequest(http.MethodGet, "/", nil),
ExpectedStatus: http.StatusUnauthorized,
ExpectedResponse: `{"message":"no api token header","error":"Unauthorized"}`,
},
RequiredRoles: roles,
RedisClient: client,
Query: db,
APICalls: dbCalls,
},
{
BasicHttpTest: testhelper.BasicHttpTest{
Name: "No headers, JWT auth",
Request: testhelper.MakeTestRequest(http.MethodGet, "/", nil),
ExpectedStatus: http.StatusUnauthorized,
ExpectedResponse: `{"message":"no authorization header","error":"Unauthorized"}`,
},
JWTAuth: true,
RequiredRoles: roles,
RedisClient: client,
Query: db,
APICalls: dbCalls,
},
{
BasicHttpTest: testhelper.BasicHttpTest{
Name: "No headers, JWT auth, no required roles",
Request: testhelper.MakeTestRequest(http.MethodGet, "/", nil),
ExpectedStatus: http.StatusOK,
ExpectedResponse: `OK`,
},
JWTAuth: true,
RedisClient: client,
Query: db,
APICalls: dbCalls,
},
{
BasicHttpTest: testhelper.BasicHttpTest{
Name: "Failed api calls log",
Request: req,
ExpectedStatus: http.StatusOK,
ExpectedResponse: `OK`,
},
RequiredRoles: roles,
RedisClient: client,
Query: &mocks.MockAPITokens{
GetResult: &common.APIToken{
Token: apiToken,
Roles: strings.Join([]string{string(adminroles.RoleCreate)}, ","),
ExpiresAt: &validExpiresAt,
},
},
APICalls: &mocks.MockAPICalls{
DBMockHelper: mocks.DBMockHelper{
Error: someErr,
},
},
},
}
for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
handler := setupAuthAPIToken(t, test)
testhelper.RunBasicHttpTest(t, test.BasicHttpTest, handler)
})
}
}
func setupAuthAPIToken(t *testing.T, test testCaseAuthAPIToken) http.HandlerFunc {
testHandler := func(w http.ResponseWriter, r *http.Request) {
if test.ExpectedProvider != "" {
if provider, ok := r.Context().Value(c.ProviderKey).(string); !ok || provider != test.ExpectedProvider {
t.Errorf(testhelper.TestErrorTemplate, test.Name, test.ExpectedProvider, provider)
}
}
w.Write([]byte(expectedOkBody))
}
auth := httphandlers.AuthAPIToken{
APITokens: test.Query,
APICalls: test.APICalls,
JWTAuth: test.JWTAuth,
}
return auth.GetHandler(test.RequiredRoles, testHandler)
}
type RedisMockAuthAPIToken struct {
GetCacheError error
SetCacheError error
redis.Connection
}
func (m *RedisMockAuthAPIToken) GetCache(key string, dest interface{}, expire int) error {
return m.GetCacheError
}
func (m *RedisMockAuthAPIToken) SetCache(string, interface{}, int) error {
return m.SetCacheError
}