Skip to content

Commit

Permalink
Added tests for the three functions in origin_api.go
Browse files Browse the repository at this point in the history
	Tests tokens with various scopes for the `VerifyAdvertiseToken` function
	Tests the string created from `GetIssuerURL`
	Tests token creation using the `CreateAdvertiseToken` function
	Also adjusts the mocked cache in redirect_test.go to be used by both test files
  • Loading branch information
User Admin authored and User Admin committed Sep 28, 2023
1 parent cef4e20 commit 7ae7e64
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 4 deletions.
162 changes: 162 additions & 0 deletions director/origin_api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package director

import (
"crypto/elliptic"
"path/filepath"
"testing"
"time"

"github.com/jellydator/ttlcache/v3"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/pelicanplatform/pelican/config"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)

func TestVerifyAdvertiseToken(t *testing.T) {
/*
* Runs unit tests on the VerifyAdvertiseToken function
*/

viper.Reset()

tDir := t.TempDir()
kfile := filepath.Join(tDir, "t-key")

//Setup a private key and a token
viper.Set("IssuerKey", kfile)

viper.Set("NamespaceURL", "https://get-your-tokens.org")
viper.Set("DirectorURL", "https://director-url.org")

kSet, err := config.LoadPublicKey("", kfile)
ar := MockCache{
GetFn: func(key string, keyset *jwk.Set) (jwk.Set, error) {
if key != "https://get-your-tokens.org/api/v1.0/registry/test-namespace/.well-known/issuer.jwks" {
t.Errorf("expecting: https://get-your-tokens.org/api/v1.0/registry/test-namespace/.well-known/issuer.jwks, got %q", key)
}
return *keyset, nil
},
RegisterFn: func(m *MockCache) error {
m.keyset = *kSet
return nil
},
}

// Perform injections (ar.Register will create a jwk.keyset with the publickey in it)
func() {
if err = ar.Register("", jwk.WithMinRefreshInterval(15*time.Minute)); err != nil {
t.Errorf("this should never happen, should actually be impossible, including check for the linter")
}
namespaceKeysMutex.Lock()
defer namespaceKeysMutex.Unlock()
namespaceKeys.Set("test-namespace", &ar, ttlcache.DefaultTTL)
}()

// A verified token with a the correct scope - should return no error
tok, err := CreateAdvertiseToken("test-namespace")
assert.NoError(t, err)
ok, err := VerifyAdvertiseToken(tok, "test-namespace")
assert.NoError(t, err)
assert.Equal(t, true, ok, "Expected scope to be 'pelican.advertise'")

//Create token without a scope - should return an error
key, err := config.GetOriginJWK()
err = jwk.AssignKeyID(*key)
assert.NoError(t, err)

scopelessTok, err := jwt.NewBuilder().
Issuer("").
Audience([]string{"director.test"}).
Subject("origin").
Build()

signed, err := jwt.Sign(scopelessTok, jwt.WithKey(jwa.ES512, *key))

ok, err = VerifyAdvertiseToken(string(signed), "test-namespace")
assert.Equal(t, false, ok)
assert.Equal(t, "No scope is present; required to advertise to director", err.Error())

//Create a token without a string valued scope
nonStrScopeTok, err := jwt.NewBuilder().
Issuer("").
Claim("scope", 22).
Audience([]string{"director.test"}).
Subject("origin").
Build()

signed, err = jwt.Sign(nonStrScopeTok, jwt.WithKey(jwa.ES512, *key))

ok, err = VerifyAdvertiseToken(string(signed), "test-namespace")
assert.Equal(t, false, ok)
assert.Equal(t, "scope claim in token is not string-valued", err.Error())

//Create a token without a pelican.namespace scope
wrongScopeTok, err := jwt.NewBuilder().
Issuer("").
Claim("scope", "wrong.scope").
Audience([]string{"director.test"}).
Subject("origin").
Build()

signed, err = jwt.Sign(wrongScopeTok, jwt.WithKey(jwa.ES512, *key))

ok, err = VerifyAdvertiseToken(string(signed), "test-namespace")
assert.Equal(t, false, ok, "Should fail due to incorrect scope name")
assert.NoError(t, err, "Incorrect scope name should not throw and error")
}

func TestCreateAdvertiseToken(t *testing.T) {
/*
* Runs unit tests on the CreateAdvertiseToken function
*/

viper.Reset()

// Create a temp directory to store the private key file
tDir := t.TempDir()
kfile := filepath.Join(tDir, "t-key")

// Generate a private key
viper.Set("IssuerKey", kfile)
err := config.GeneratePrivateKey(kfile, elliptic.P521())
assert.NoError(t, err)

// Test without a namsepace set and check to see if it returns the expected error
tok, err := CreateAdvertiseToken("test-namespace")
assert.Equal(t, "", tok)
assert.Equal(t, "Namespace URL is not set", err.Error())
viper.Set("NamespaceURL", "https://get-your-tokens.org")

// Test without a DirectorURL set and check to see if it returns the expected error
tok, err = CreateAdvertiseToken("test-namespace")
assert.Equal(t, "", tok)
assert.Equal(t, "Director URL is not known; cannot create advertise token", err.Error())
viper.Set("DirectorURL", "https://director-url.org")

// Test the CreateAdvertiseToken with good values and test that it returns a non-nil token value and no error
tok, err = CreateAdvertiseToken("test-namespace")
assert.Equal(t, nil, err)
assert.NotEqual(t, "", tok)
}

func TestGetIssuerURL(t *testing.T) {
/*
* Runs unit tests on the GetIssuerURL function
*/
viper.Reset()

// No namespace url has been set, so an error is expected
url, err := GetIssuerURL("")
assert.Equal(t, "", url)
assert.Equal(t, "Namespace URL is not set", err.Error())

// Test to make sure the path is as expected
viper.Set("NamespaceURL", "test-path")
url, err = GetIssuerURL("test-prefix")
assert.Equal(t, nil, err)
assert.Equal(t, "test-path/api/v1.0/registry/test-prefix/.well-known/issuer.jwks", url)

}
10 changes: 6 additions & 4 deletions director/redirect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (

type MockCache struct {
GetFn func(u string, kset *jwk.Set) (jwk.Set, error)
RegisterFn func(kset *jwk.Set) error
RegisterFn func(*MockCache) error

keyset jwk.Set
}
Expand All @@ -35,7 +35,7 @@ func (m *MockCache) Get(ctx context.Context, u string) (jwk.Set, error) {

func (m *MockCache) Register(u string, options ...jwk.RegisterOption) error {
m.keyset = jwk.NewSet()
return m.RegisterFn(&m.keyset)
return m.RegisterFn(m)
}

func TestDirectorRegistration(t *testing.T) {
Expand All @@ -45,6 +45,8 @@ func TestDirectorRegistration(t *testing.T) {
* so again with an invalid token and confirms that the correct error is returned
*/

viper.Reset()

// Setup httptest recorder and context for the the unit test
w := httptest.NewRecorder()
c, r := gin.CreateTestContext(w)
Expand Down Expand Up @@ -106,8 +108,8 @@ func TestDirectorRegistration(t *testing.T) {
}
return *keyset, nil
},
RegisterFn: func(keyset *jwk.Set) error {
err := jwk.Set.AddKey(*keyset, publicKey)
RegisterFn: func(m *MockCache) error {
err := jwk.Set.AddKey(m.keyset, publicKey)
if err != nil {
t.Error(err)
}
Expand Down

0 comments on commit 7ae7e64

Please sign in to comment.