Skip to content

Commit b4ccb05

Browse files
committed
add cacheing for identity client + keyfetch client
Signed-off-by: Ashley Davis <ashley.davis@cyberark.com>
1 parent 9cf104d commit b4ccb05

File tree

2 files changed

+38
-7
lines changed

2 files changed

+38
-7
lines changed

internal/cyberark/identity/identity.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/http"
1010
"net/url"
1111
"sync"
12+
"time"
1213

1314
"k8s.io/klog/v2"
1415

@@ -180,6 +181,7 @@ type Client struct {
180181

181182
tokenCached token
182183
tokenCachedMutex sync.Mutex
184+
tokenCachedTime time.Time
183185
}
184186

185187
// token is a wrapper type for holding auth tokens we want to cache.
@@ -205,12 +207,23 @@ func New(httpClient *http.Client, baseURL string, subdomain string) *Client {
205207
// Tokens are cached internally and are not directly accessible to code; use Client.AuthenticateRequest to add credentials
206208
// to an *http.Request.
207209
func (c *Client) LoginUsernamePassword(ctx context.Context, username string, password []byte) error {
210+
// note: we hold the mutex for the whole login attempt to ensure that only one login attempt can be in flight at once,
211+
// and to ensure that the token cache is correctly updated
212+
c.tokenCachedMutex.Lock()
213+
defer c.tokenCachedMutex.Unlock()
214+
208215
defer func() {
209216
for i := range password {
210217
password[i] = 0x00
211218
}
212219
}()
213220

221+
if time.Since(c.tokenCachedTime) < 15*time.Minute && c.tokenCached.Username == username {
222+
// If the cached token is recent and for the same username, we can reuse it.
223+
klog.FromContext(ctx).V(2).Info("reusing cached token for user", "username", username)
224+
return nil
225+
}
226+
214227
advanceRequestBody, err := c.doStartAuthentication(ctx, username)
215228
if err != nil {
216229
return err
@@ -405,15 +418,13 @@ func (c *Client) doAdvanceAuthentication(ctx context.Context, username string, p
405418

406419
klog.FromContext(ctx).Info("successfully completed AdvanceAuthentication request to CyberArk Identity; login complete", "username", username)
407420

408-
c.tokenCachedMutex.Lock()
409-
421+
// NB: This assumes we already hold the token cache mutex, which we do in LoginUsernamePassword, so this is safe.
422+
c.tokenCachedTime = time.Now()
410423
c.tokenCached = token{
411424
Username: username,
412425
Token: advanceAuthResponse.Result.Token,
413426
}
414427

415-
c.tokenCachedMutex.Unlock()
416-
417428
return nil
418429
}
419430

internal/envelope/keyfetch/client.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"io"
99
"net/http"
1010
"net/url"
11+
"sync"
12+
"time"
1113

1214
"github.com/jetstack/venafi-connection-lib/http_client"
1315
"github.com/lestrrat-go/jwx/v3/jwk"
@@ -53,6 +55,10 @@ type Client struct {
5355

5456
// httpClient is the HTTP client used for requests
5557
httpClient *http.Client
58+
59+
cachedKey PublicKey
60+
cachedKeyMutex sync.Mutex
61+
cachedKeyTime time.Time
5662
}
5763

5864
// NewClient creates a new key fetching client.
@@ -82,6 +88,15 @@ func NewClient(ctx context.Context, discoveryClient *servicediscovery.Client, cf
8288
// FetchKey retrieves the public keys from the configured endpoint.
8389
// It returns a slice of PublicKey structs containing the key material and metadata.
8490
func (c *Client) FetchKey(ctx context.Context) (PublicKey, error) {
91+
logger := klog.FromContext(ctx).WithName("keyfetch")
92+
c.cachedKeyMutex.Lock()
93+
defer c.cachedKeyMutex.Unlock()
94+
95+
if time.Since(c.cachedKeyTime) < 15*time.Minute {
96+
klog.FromContext(ctx).WithName("keyfetch").V(2).Info("using cached key", "fetchedAt", c.cachedKeyTime.Format(time.RFC3339Nano), "kid", c.cachedKey.KeyID)
97+
return c.cachedKey, nil
98+
}
99+
85100
services, _, err := c.discoveryClient.DiscoverServices(ctx)
86101
if err != nil {
87102
return PublicKey{}, fmt.Errorf("failed to get services from discovery client: %w", err)
@@ -176,12 +191,17 @@ func (c *Client) FetchKey(ctx context.Context) (PublicKey, error) {
176191
continue
177192
}
178193

179-
klog.FromContext(ctx).WithName("keyfetch").Info("found valid RSA key", "kid", kid)
180194
// return the first valid key we find
181-
return PublicKey{
195+
196+
logger.Info("fetched valid RSA key", "kid", kid)
197+
198+
c.cachedKey = PublicKey{
182199
KeyID: kid,
183200
Key: rsaKey,
184-
}, nil
201+
}
202+
c.cachedKeyTime = time.Now()
203+
204+
return c.cachedKey, nil
185205
}
186206

187207
return PublicKey{}, fmt.Errorf("no valid RSA keys found at %s", endpoint)

0 commit comments

Comments
 (0)