storj/satellite/oidc/oauth_generates_test.go
Mya 4a110b266e satellite/console: added oidc endpoints
This change adds endpoints for supporting OpenID Connect (OIDC) and
OAuth requests. This allows application developers to easily
develop apps with Storj using common mechanisms for authentication
and authorization.

Change-Id: I2a76d48bd1241367aa2d1e3309f6f65d6d6ea4dc
2022-03-16 12:01:26 +00:00

181 lines
5.0 KiB
Go

// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
package oidc_test
import (
"context"
"database/sql"
"testing"
"time"
"github.com/go-oauth2/oauth2/v4"
"github.com/go-oauth2/oauth2/v4/models"
"github.com/stretchr/testify/require"
"storj.io/common/macaroon"
"storj.io/common/uuid"
"storj.io/storj/satellite/console"
"storj.io/storj/satellite/oidc"
)
type mockGenerateService struct {
GetAPIKeyInfoFunc func(ctx context.Context, uuid uuid.UUID, name string) (*console.APIKeyInfo, error)
CreateAPIKeyFunc func(ctx context.Context, uuid uuid.UUID, name string) (*console.APIKeyInfo, *macaroon.APIKey, error)
GetUserFunc func(ctx context.Context, uuid uuid.UUID) (*console.User, error)
}
func (m *mockGenerateService) GetAPIKeyInfoByName(ctx context.Context, projectID uuid.UUID, name string) (*console.APIKeyInfo, error) {
if m.GetAPIKeyInfoFunc == nil {
return nil, nil
}
return m.GetAPIKeyInfoFunc(ctx, projectID, name)
}
func (m *mockGenerateService) GetUser(ctx context.Context, id uuid.UUID) (u *console.User, err error) {
if m.GetUserFunc == nil {
return nil, nil
}
return m.GetUserFunc(ctx, id)
}
func (m *mockGenerateService) CreateAPIKey(ctx context.Context, id uuid.UUID, name string) (*console.APIKeyInfo, *macaroon.APIKey, error) {
if m.CreateAPIKeyFunc == nil {
return nil, nil, nil
}
return m.CreateAPIKeyFunc(ctx, id, name)
}
var _ oidc.GenerateService = &mockGenerateService{}
func TestUUIDGenerate(t *testing.T) {
ctx := context.Background()
generate := oidc.UUIDAuthorizeGenerate{}
uuid, err := generate.Token(ctx, nil)
require.NoError(t, err)
require.NotEqual(t, "", uuid)
}
func TestMacaroonGenerate(t *testing.T) {
secret, err := macaroon.NewSecret()
require.NoError(t, err)
apiKey, err := macaroon.NewAPIKey(secret)
require.NoError(t, err)
getSuccess := func(ctx context.Context, uuid uuid.UUID, name string) (*console.APIKeyInfo, error) {
return &console.APIKeyInfo{
ID: uuid,
ProjectID: uuid,
Name: name,
Head: apiKey.Head(),
Secret: secret,
}, nil
}
getFailure := func(ctx context.Context, uuid uuid.UUID, name string) (*console.APIKeyInfo, error) {
return nil, sql.ErrNoRows
}
createSuccess := func(ctx context.Context, uuid uuid.UUID, name string) (*console.APIKeyInfo, *macaroon.APIKey, error) {
return &console.APIKeyInfo{
ID: uuid,
ProjectID: uuid,
Name: name,
Head: apiKey.Head(),
Secret: secret,
}, apiKey, nil
}
user, err := uuid.New()
require.NoError(t, err)
project, err := uuid.New()
require.NoError(t, err)
missingProjectScope := `object:list object:read object:write object:delete`
fullScope := "project:" + project.String() + " bucket:test cubbyhole:plaintext " + missingProjectScope
multipleProjectScopes := "project:" + project.String() + " " + fullScope
testCases := []struct {
name string
scope string
get func(ctx context.Context, uuid uuid.UUID, name string) (*console.APIKeyInfo, error)
create func(ctx context.Context, uuid uuid.UUID, name string) (*console.APIKeyInfo, *macaroon.APIKey, error)
refresh bool
err string
}{
{"missing project", missingProjectScope, getSuccess, nil, false, "missing project"},
{"multiple projects", multipleProjectScopes, getSuccess, nil, false, "multiple project scopes provided"},
{"create secret - access", fullScope, getFailure, createSuccess, false, ""},
{"create secret - access and refresh", fullScope, getFailure, createSuccess, true, ""},
{"existing secret - access", fullScope, getSuccess, nil, false, ""},
{"existing secret - access and refresh", fullScope, getSuccess, nil, true, ""},
}
ctx := context.Background()
mock := &mockGenerateService{
GetUserFunc: func(ctx context.Context, uuid uuid.UUID) (*console.User, error) {
return &console.User{
ID: user,
}, nil
},
}
generate := &oidc.MacaroonAccessGenerate{Service: mock}
token := &models.Token{
AccessCreateAt: time.Now(),
AccessExpiresIn: time.Minute,
RefreshCreateAt: time.Now(),
RefreshExpiresIn: time.Minute,
}
request := &oauth2.GenerateBasic{
Client: oidc.OAuthClient{},
UserID: user.String(),
TokenInfo: token,
}
for _, testCase := range testCases {
t.Log(testCase.name)
token.Refresh = ""
token.Scope = testCase.scope
mock.GetAPIKeyInfoFunc = testCase.get
mock.CreateAPIKeyFunc = testCase.create
// initial generation
access, refresh, err := generate.Token(ctx, request, testCase.refresh)
if testCase.err != "" {
require.Error(t, err)
require.Equal(t, testCase.err, err.Error())
continue
}
require.NoError(t, err)
require.NotEqual(t, "", access)
if !testCase.refresh {
require.Equal(t, "", refresh)
continue
}
require.NotEqual(t, "", refresh)
// test regeneration
token.Refresh = refresh
refreshed, refresh, err := generate.Token(ctx, request, testCase.refresh)
require.NoError(t, err)
require.Equal(t, token.Refresh, refresh)
// ensure the refreshed token isn't the same as the original
require.NotEqual(t, access, refreshed)
}
}