Skip to content

Commit b6693ef

Browse files
committed
auth: allow specifying custom cache
Modify `auth.AuthOptions` to accept a custom cache which takes precedence over the global cache. Signed-off-by: Sanskar Jaiswal <[email protected]>
1 parent cc3a2eb commit b6693ef

File tree

6 files changed

+195
-48
lines changed

6 files changed

+195
-48
lines changed

Diff for: auth/cache.go

+14-7
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,28 @@ limitations under the License.
1717
package auth
1818

1919
import (
20-
"sync"
20+
"errors"
2121
"time"
2222
)
2323

24-
var once sync.Once
2524
var cache Store
2625

26+
var ErrCacheAlreadyInitialized = errors.New("cache already initialized; cannot be re-initialized")
27+
var ErrEmptyCache = errors.New("cannot initialize cache with an empty store")
28+
2729
// InitCache intializes the pacakge cache with the provided cache object.
2830
// Consumers that want automatic caching when using `GetRegistryAuthenticator()`
2931
// or `GetGitCredentials()` must call this before. It should only be called once,
30-
// all subsequent calls will be a no-op.
31-
func InitCache(s Store) {
32-
once.Do(func() {
33-
cache = s
34-
})
32+
// all subsequent calls will return an error.
33+
func InitCache(s Store) error {
34+
if cache != nil {
35+
return ErrCacheAlreadyInitialized
36+
}
37+
if s == nil {
38+
return ErrEmptyCache
39+
}
40+
cache = s
41+
return nil
3542
}
3643

3744
// GetCache returns a handle to the package level cache.

Diff for: auth/git/credentials.go

+16-10
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,23 @@ func (c *Credentials) ToSecretData() map[string][]byte {
5555

5656
// GetCredentials returns authentication credentials for accessing the provided
5757
// Git repository.
58-
// If caching is enabled and cacheKey is not blank, the credentials are cached
59-
// according to the ttl advertised by the Git provider.
58+
// The authentication credentials will be cached if `authOpts.CacheOptions.Key`
59+
// is not blank and caching is enabled. Caching can be enabled by either calling
60+
// `auth.InitCache()` or specifying a cache via `authOpts.CacheOptions.Cache`.
61+
// The credentials are cached according to the ttl advertised by the registry
62+
// provider.
6063
func GetCredentials(ctx context.Context, provider string, authOpts *auth.AuthOptions) (*Credentials, error) {
6164
var creds Credentials
6265

63-
cache := auth.GetCache()
64-
if cache != nil && authOpts != nil && authOpts.CacheKey != "" {
65-
val, found := cache.Get(authOpts.CacheKey)
66-
if found {
67-
creds = val.(Credentials)
68-
return &creds, nil
66+
var cache auth.Store
67+
if authOpts != nil {
68+
cache = authOpts.GetCache()
69+
if cache != nil && authOpts.CacheOptions.Key != "" {
70+
val, found := cache.Get(authOpts.CacheOptions.Key)
71+
if found {
72+
creds = val.(Credentials)
73+
return &creds, nil
74+
}
6975
}
7076
}
7177

@@ -134,8 +140,8 @@ func GetCredentials(ctx context.Context, provider string, authOpts *auth.AuthOpt
134140
return nil, nil
135141
}
136142

137-
if cache != nil && authOpts != nil && authOpts.CacheKey != "" {
138-
if err := cache.Set(authOpts.CacheKey, creds, expiresIn); err != nil {
143+
if cache != nil && authOpts != nil && authOpts.CacheOptions.Key != "" {
144+
if err := cache.Set(authOpts.CacheOptions.Key, creds, expiresIn); err != nil {
139145
return nil, err
140146
}
141147
}

Diff for: auth/git/credentials_test.go

+66-8
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,8 @@ import (
4242

4343
func TestGetCredentials(t *testing.T) {
4444
expiresAt := time.Now().UTC().Add(time.Hour)
45-
var s auth.Store
46-
s = testutils.NewDummyCache()
47-
auth.InitCache(s)
45+
auth.InitCache(testutils.NewDummyCache())
46+
customCache := testutils.NewDummyCache()
4847

4948
tests := []struct {
5049
name string
@@ -60,7 +59,9 @@ func TestGetCredentials(t *testing.T) {
6059
name: "get credentials from github",
6160
provider: auth.ProviderGitHub,
6261
authOpts: &auth.AuthOptions{
63-
CacheKey: "github-123",
62+
CacheOptions: auth.CacheOptions{
63+
Key: "github-123",
64+
},
6465
},
6566
responseBody: `{
6667
"token": "access-token",
@@ -93,7 +94,60 @@ func TestGetCredentials(t *testing.T) {
9394
name: "get credentials from cache",
9495
provider: auth.ProviderGitHub,
9596
authOpts: &auth.AuthOptions{
96-
CacheKey: "github-123",
97+
CacheOptions: auth.CacheOptions{
98+
Key: "github-123",
99+
},
100+
},
101+
expectCacheHit: true,
102+
wantCredentials: &Credentials{
103+
Username: GitHubAccessTokenUsername,
104+
Password: "access-token",
105+
},
106+
},
107+
{
108+
name: "get credentials from github with local cache",
109+
provider: auth.ProviderGitHub,
110+
authOpts: &auth.AuthOptions{
111+
CacheOptions: auth.CacheOptions{
112+
Key: "github-local-123",
113+
Cache: customCache,
114+
},
115+
},
116+
responseBody: `{
117+
"token": "access-token",
118+
"expires_at": "2029-11-10T23:00:00Z"
119+
}`,
120+
beforeFunc: func(t *WithT, authOpts *auth.AuthOptions, serverURL string) {
121+
pk, err := createPrivateKey()
122+
t.Expect(err).ToNot(HaveOccurred())
123+
authOpts.Secret = &corev1.Secret{
124+
Data: map[string][]byte{
125+
github.ApiURLKey: []byte(serverURL),
126+
github.AppIDKey: []byte("127"),
127+
github.AppInstallationIDKey: []byte("300"),
128+
github.AppPkKey: pk,
129+
},
130+
}
131+
},
132+
afterFunc: func(t *WithT, cache auth.Store, creds Credentials) {
133+
val, ok := cache.Get("github-local-123")
134+
t.Expect(ok).To(BeTrue())
135+
credentials := val.(Credentials)
136+
t.Expect(credentials).To(Equal(creds))
137+
},
138+
wantCredentials: &Credentials{
139+
Username: GitHubAccessTokenUsername,
140+
Password: "access-token",
141+
},
142+
},
143+
{
144+
name: "get credentials from local cache",
145+
provider: auth.ProviderGitHub,
146+
authOpts: &auth.AuthOptions{
147+
CacheOptions: auth.CacheOptions{
148+
Key: "github-local-123",
149+
Cache: customCache,
150+
},
97151
},
98152
expectCacheHit: true,
99153
wantCredentials: &Credentials{
@@ -130,7 +184,9 @@ func TestGetCredentials(t *testing.T) {
130184
name: "get credentials from azure",
131185
provider: auth.ProviderAzure,
132186
authOpts: &auth.AuthOptions{
133-
CacheKey: "azure-123",
187+
CacheOptions: auth.CacheOptions{
188+
Key: "azure-123",
189+
},
134190
ProviderOptions: auth.ProviderOptions{
135191
AzureOpts: []azure.ProviderOptFunc{
136192
azure.WithCredential(&azure.FakeTokenCredential{
@@ -154,7 +210,9 @@ func TestGetCredentials(t *testing.T) {
154210
name: "get credentials from gcp",
155211
provider: auth.ProviderGCP,
156212
authOpts: &auth.AuthOptions{
157-
CacheKey: "gcp-123",
213+
CacheOptions: auth.CacheOptions{
214+
Key: "gcp-123",
215+
},
158216
},
159217
responseBody: `{
160218
"access_token": "access-token",
@@ -223,7 +281,7 @@ func TestGetCredentials(t *testing.T) {
223281
g.Expect(*creds).To(Equal(*tt.wantCredentials))
224282

225283
if tt.afterFunc != nil {
226-
tt.afterFunc(g, s, *creds)
284+
tt.afterFunc(g, tt.authOpts.GetCache(), *creds)
227285
}
228286

229287
if tt.expectCacheHit {

Diff for: auth/options.go

+24-4
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,30 @@ type AuthOptions struct {
4141
// providers.
4242
ProviderOptions ProviderOptions
4343

44-
// CacheKey is the key to use for caching the authentication credentials.
45-
// Consumers must make sure to call `InitCache()` in order for caching to
46-
// be enabled.
47-
CacheKey string
44+
// CacheOptions specifies the options to configure caching behavior of the
45+
// authentication credentials.
46+
CacheOptions CacheOptions
47+
}
48+
49+
// GetCache returns the cache to use for fetching/storing authentication
50+
// credentials.
51+
func (a *AuthOptions) GetCache() Store {
52+
if a.CacheOptions.Cache != nil {
53+
return a.CacheOptions.Cache
54+
}
55+
return GetCache()
56+
}
57+
58+
// CacheOptions contains options to configure the caching behavior of the
59+
// authentication credentials.
60+
type CacheOptions struct {
61+
// Key is the key to use for caching the authentication credentials.
62+
Key string
63+
64+
// Cache is the Store to use for caching the authentication credentials.
65+
// If specified, then the global cache specified through `auth.InitCache()`
66+
// is ignored and the credentials are cached in this Store instead.
67+
Cache Store
4868
}
4969

5070
// ProviderOptions contains options to configure various authentication

Diff for: auth/registry/authenticator.go

+16-10
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,23 @@ import (
2929

3030
// GetAuthenticator returns an authenticator that can provide credentials to
3131
// access the provided registry.
32-
// If caching is enabled and authOpts.CacheKey is not blank, the authentication
33-
// config is cached according to the ttl advertised by the registry provider.
32+
// The authentication credentials will be cached if `authOpts.CacheOptions.Key`
33+
// is not blank and caching is enabled. Caching can be enabled by either calling
34+
// `auth.InitCache()` or specifying a cache via `authOpts.CacheOptions.Cache`.
35+
// The credentials are cached according to the ttl advertised by the registry
36+
// provider.
3437
func GetAuthenticator(ctx context.Context, registry string, provider string, authOpts *auth.AuthOptions) (authn.Authenticator, error) {
3538
var authConfig authn.AuthConfig
3639

37-
cache := auth.GetCache()
38-
if cache != nil && authOpts != nil && authOpts.CacheKey != "" {
39-
val, found := cache.Get(authOpts.CacheKey)
40-
if found {
41-
authConfig = val.(authn.AuthConfig)
42-
return authn.FromConfig(authConfig), nil
40+
var cache auth.Store
41+
if authOpts != nil {
42+
cache = authOpts.GetCache()
43+
if cache != nil && authOpts.CacheOptions.Key != "" {
44+
val, found := cache.Get(authOpts.CacheOptions.Key)
45+
if found {
46+
authConfig = val.(authn.AuthConfig)
47+
return authn.FromConfig(authConfig), nil
48+
}
4349
}
4450
}
4551

@@ -79,8 +85,8 @@ func GetAuthenticator(ctx context.Context, registry string, provider string, aut
7985
return nil, err
8086
}
8187

82-
if cache != nil && authOpts != nil && authOpts.CacheKey != "" {
83-
if err := cache.Set(authOpts.CacheKey, authConfig, expiresIn); err != nil {
88+
if cache != nil && authOpts != nil && authOpts.CacheOptions.Key != "" {
89+
if err := cache.Set(authOpts.CacheOptions.Key, authConfig, expiresIn); err != nil {
8490
return nil, err
8591
}
8692
}

Diff for: auth/registry/authenticator_test.go

+59-9
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,8 @@ func TestGetAuthenticator(t *testing.T) {
5454
tokenStr, err := token.SignedString(pk)
5555
g.Expect(err).ToNot(HaveOccurred())
5656

57-
var s auth.Store
58-
s = testutils.NewDummyCache()
59-
auth.InitCache(s)
57+
auth.InitCache(testutils.NewDummyCache())
58+
customCache := testutils.NewDummyCache()
6059

6160
tests := []struct {
6261
name string
@@ -72,7 +71,9 @@ func TestGetAuthenticator(t *testing.T) {
7271
name: "get authenticator from gcp",
7372
provider: auth.ProviderGCP,
7473
authOpts: &auth.AuthOptions{
75-
CacheKey: "gcp-123",
74+
CacheOptions: auth.CacheOptions{
75+
Key: "gcp-123",
76+
},
7677
},
7778
responseBody: `{
7879
"access_token": "access-token",
@@ -94,10 +95,55 @@ func TestGetAuthenticator(t *testing.T) {
9495
},
9596
},
9697
{
97-
name: "get authenticator from cache",
98+
name: "get authenticator from global cache",
9899
provider: auth.ProviderGCP,
99100
authOpts: &auth.AuthOptions{
100-
CacheKey: "gcp-123",
101+
CacheOptions: auth.CacheOptions{
102+
Key: "gcp-123",
103+
},
104+
},
105+
expectCacheHit: true,
106+
wantAuthConfig: &authn.AuthConfig{
107+
Username: gcp.DefaultGARUsername,
108+
Password: "access-token",
109+
},
110+
},
111+
{
112+
name: "get authenticator from gcp with local cache",
113+
provider: auth.ProviderGCP,
114+
authOpts: &auth.AuthOptions{
115+
CacheOptions: auth.CacheOptions{
116+
Key: "gcp-local-123",
117+
Cache: customCache,
118+
},
119+
},
120+
responseBody: `{
121+
"access_token": "access-token",
122+
"expires_in": 10,
123+
"token_type": "Bearer"
124+
}`,
125+
beforeFunc: func(authOpts *auth.AuthOptions, serverURL string, registry *string) {
126+
authOpts.ProviderOptions.GcpOpts = []gcp.ProviderOptFunc{gcp.WithTokenURL(serverURL), gcp.WithEmailURL(serverURL)}
127+
},
128+
wantAuthConfig: &authn.AuthConfig{
129+
Username: gcp.DefaultGARUsername,
130+
Password: "access-token",
131+
},
132+
afterFunc: func(t *WithT, cache auth.Store, authConfig authn.AuthConfig) {
133+
val, ok := cache.Get("gcp-local-123")
134+
t.Expect(ok).To(BeTrue())
135+
ac := val.(authn.AuthConfig)
136+
t.Expect(ac).To(Equal(authConfig))
137+
},
138+
},
139+
{
140+
name: "get authenticator from global cache",
141+
provider: auth.ProviderGCP,
142+
authOpts: &auth.AuthOptions{
143+
CacheOptions: auth.CacheOptions{
144+
Key: "gcp-local-123",
145+
Cache: customCache,
146+
},
101147
},
102148
expectCacheHit: true,
103149
wantAuthConfig: &authn.AuthConfig{
@@ -126,7 +172,9 @@ func TestGetAuthenticator(t *testing.T) {
126172
name: "get authenticator from aws",
127173
provider: auth.ProviderAWS,
128174
authOpts: &auth.AuthOptions{
129-
CacheKey: "aws-123",
175+
CacheOptions: auth.CacheOptions{
176+
Key: "aws-123",
177+
},
130178
},
131179
responseBody: fmt.Sprintf(`{
132180
"authorizationData": [
@@ -161,7 +209,9 @@ func TestGetAuthenticator(t *testing.T) {
161209
name: "get authenticator from azure",
162210
provider: auth.ProviderAzure,
163211
authOpts: &auth.AuthOptions{
164-
CacheKey: "azure-123",
212+
CacheOptions: auth.CacheOptions{
213+
Key: "azure-123",
214+
},
165215
},
166216
responseBody: fmt.Sprintf(`{"refresh_token": "%s"}`, tokenStr),
167217
beforeFunc: func(authOpts *auth.AuthOptions, serverURL string, registry *string) {
@@ -219,7 +269,7 @@ func TestGetAuthenticator(t *testing.T) {
219269

220270
g.Expect(*ac).To(Equal(*tt.wantAuthConfig))
221271
if tt.afterFunc != nil {
222-
tt.afterFunc(g, s, *ac)
272+
tt.afterFunc(g, tt.authOpts.GetCache(), *ac)
223273
}
224274
if tt.expectCacheHit {
225275
g.Expect(count).To(Equal(0))

0 commit comments

Comments
 (0)