fix: add involves edge from task to agent:nomos at creation
Plus sync vendor directory for Docker build compatibility.
This commit is contained in:
259
vendor/github.com/infisical/go-sdk/client_auth_helper.go
generated
vendored
Normal file
259
vendor/github.com/infisical/go-sdk/client_auth_helper.go
generated
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
package infisical
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
api "github.com/infisical/go-sdk/packages/api/auth"
|
||||
"github.com/infisical/go-sdk/packages/models"
|
||||
"github.com/infisical/go-sdk/packages/util"
|
||||
)
|
||||
|
||||
const renewalBufferSeconds = 5
|
||||
|
||||
// isTokenExpiringSoon checks if the token will expire within the given buffer time.
|
||||
func (c *InfisicalClient) isTokenExpiringSoon(bufferSeconds int64) bool {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
// Skip if no auth method or using plain access token (no refresh capability)
|
||||
if c.authMethod == "" || c.authMethod == util.ACCESS_TOKEN {
|
||||
return false
|
||||
}
|
||||
|
||||
// Skip if token details are not set
|
||||
if c.tokenDetails.ExpiresIn == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
timeSinceLastFetch := time.Since(c.lastFetchedTime).Seconds()
|
||||
return timeSinceLastFetch >= float64(c.tokenDetails.ExpiresIn-bufferSeconds)
|
||||
}
|
||||
|
||||
// refreshTokenSynchronously performs a blocking token refresh. This gets called by the onbeforerequest hook and the token lifecycle goroutine.
|
||||
func (c *InfisicalClient) refreshTokenSynchronously(manualTrigger bool) error {
|
||||
c.logger.Debug().Msgf("Refreshing token synchronously. Manual trigger: %v", manualTrigger)
|
||||
|
||||
// Use TryLock to prevent deadlocks when the refresh operation itself triggers HTTP requests (renewal or re-auth)
|
||||
// If we can't acquire the lock, another goroutine is already refreshing.
|
||||
if !c.refreshMu.TryLock() {
|
||||
c.logger.Debug().Msg("Another refresh is already in progress, skipping")
|
||||
return nil
|
||||
}
|
||||
defer c.refreshMu.Unlock()
|
||||
|
||||
// Double-check if refresh is still needed after acquiring the lock
|
||||
// (another goroutine might have already refreshed)
|
||||
if !c.isTokenExpiringSoon(renewalBufferSeconds) {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.mu.RLock()
|
||||
authMethod := c.authMethod
|
||||
credential := c.credential
|
||||
tokenDetails := c.tokenDetails
|
||||
firstFetchedTime := c.firstFetchedTime
|
||||
config := c.config
|
||||
c.mu.RUnlock()
|
||||
|
||||
// Check if we need re-auth (approaching max TTL) or can renew
|
||||
timeSinceFirstFetch := time.Since(firstFetchedTime).Seconds()
|
||||
timeUntilMaxTTL := float64(tokenDetails.AccessTokenMaxTTL) - timeSinceFirstFetch
|
||||
|
||||
// If time until max TTL is less than the token TTL, we need to re-auth
|
||||
needsReAuth := timeUntilMaxTTL < float64(tokenDetails.ExpiresIn)
|
||||
|
||||
c.logger.Debug().Msgf("timeSinceFirstFetch: %f, timeUntilMaxTTL: %f, needsReAuth: %v", timeSinceFirstFetch, timeUntilMaxTTL, needsReAuth)
|
||||
|
||||
if needsReAuth {
|
||||
c.logger.Debug().Msgf("Re-authentication needed. Attempting re-authentication")
|
||||
err := c.doReAuthentication(authMethod, credential, config)
|
||||
|
||||
message := "Re-authentication successful"
|
||||
if err != nil {
|
||||
message = fmt.Sprintf("Re-authentication failed. Error: %v", err)
|
||||
}
|
||||
|
||||
c.logger.Debug().Msg(message)
|
||||
return err
|
||||
}
|
||||
|
||||
// Try renewal first
|
||||
c.logger.Debug().Msgf("Attempting token renewal")
|
||||
err := c.doTokenRenewal(tokenDetails.AccessToken, credential, authMethod)
|
||||
if err != nil {
|
||||
c.logger.Debug().Msgf("Token renewal failed. Attempting re-authentication as fallback. Error: %v", err)
|
||||
// Renewal failed, try re-authentication as fallback
|
||||
if !config.SilentMode {
|
||||
util.PrintWarning(c.logger, fmt.Sprintf("Token renewal failed during pre-request check: %s. Attempting re-authentication", err.Error()))
|
||||
}
|
||||
message := "Re-authentication successful as fallback"
|
||||
err = c.doReAuthentication(authMethod, credential, config)
|
||||
|
||||
if err != nil {
|
||||
message = fmt.Sprintf("Re-authentication failed as fallback. Error: %v", err)
|
||||
}
|
||||
|
||||
c.logger.Debug().Msg(message)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
c.logger.Debug().Msgf("Token renewal successful")
|
||||
return nil
|
||||
}
|
||||
|
||||
// doTokenRenewal attempts to renew the access token.
|
||||
func (c *InfisicalClient) doTokenRenewal(accessToken string, credential interface{}, authMethod util.AuthMethod) error {
|
||||
renewedCredential, err := api.CallRenewAccessToken(c.httpClient, api.RenewAccessTokenRequest{AccessToken: accessToken})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.setAccessToken(renewedCredential, credential, authMethod)
|
||||
return nil
|
||||
}
|
||||
|
||||
// doReAuthentication performs a full re-authentication using the stored credentials.
|
||||
func (c *InfisicalClient) doReAuthentication(authMethod util.AuthMethod, credential interface{}, config Config) error {
|
||||
authStrategies := c.getAuthStrategies()
|
||||
|
||||
strategy, exists := authStrategies[authMethod]
|
||||
if !exists {
|
||||
return fmt.Errorf("unknown auth method: %s", authMethod)
|
||||
}
|
||||
|
||||
newToken, err := strategy(credential)
|
||||
if err != nil {
|
||||
if !config.SilentMode {
|
||||
util.PrintWarning(c.logger, fmt.Sprintf("Re-authentication failed during pre-request check: %s", err.Error()))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
c.setAccessToken(newToken, credential, authMethod)
|
||||
c.mu.Lock()
|
||||
c.firstFetchedTime = time.Now()
|
||||
c.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculateSleepTime determines how long to sleep before the next token refresh check
|
||||
func (c *InfisicalClient) calculateSleepTime(tokenDetails MachineIdentityCredential, bufferSeconds int64) time.Duration {
|
||||
if tokenDetails.ExpiresIn == 0 {
|
||||
return 1 * time.Second
|
||||
}
|
||||
|
||||
c.mu.RLock()
|
||||
timeSinceLastFetch := time.Since(c.lastFetchedTime).Seconds()
|
||||
c.mu.RUnlock()
|
||||
|
||||
timeUntilExpiry := float64(tokenDetails.ExpiresIn) - timeSinceLastFetch - float64(bufferSeconds)
|
||||
|
||||
if timeUntilExpiry <= 0 {
|
||||
return 1 * time.Second
|
||||
}
|
||||
|
||||
return time.Duration(timeUntilExpiry) * time.Second
|
||||
}
|
||||
|
||||
// getAuthStrategies returns the map of authentication strategies
|
||||
func (c *InfisicalClient) getAuthStrategies() map[util.AuthMethod]func(cred interface{}) (credential MachineIdentityCredential, err error) {
|
||||
return map[util.AuthMethod]func(cred interface{}) (credential MachineIdentityCredential, err error){
|
||||
util.UNIVERSAL_AUTH: func(cred interface{}) (credential MachineIdentityCredential, err error) {
|
||||
if parsedCreds, ok := cred.(models.UniversalAuthCredential); ok {
|
||||
return c.auth.UniversalAuthLogin(parsedCreds.ClientID, parsedCreds.ClientSecret)
|
||||
}
|
||||
return MachineIdentityCredential{}, fmt.Errorf("failed to parse UniversalAuthCredential")
|
||||
},
|
||||
util.KUBERNETES: func(cred interface{}) (credential MachineIdentityCredential, err error) {
|
||||
if parsedCreds, ok := cred.(models.KubernetesCredential); ok {
|
||||
return c.auth.KubernetesRawServiceAccountTokenLogin(parsedCreds.IdentityID, parsedCreds.ServiceAccountToken)
|
||||
}
|
||||
return MachineIdentityCredential{}, fmt.Errorf("failed to parse KubernetesAuthCredential")
|
||||
},
|
||||
util.AZURE: func(cred interface{}) (credential MachineIdentityCredential, err error) {
|
||||
if parsedCreds, ok := cred.(models.AzureCredential); ok {
|
||||
if parsedCreds.ClientID != "" {
|
||||
return c.auth.WithAzureClientID(parsedCreds.ClientID).AzureAuthLogin(parsedCreds.IdentityID, parsedCreds.Resource)
|
||||
}
|
||||
return c.auth.AzureAuthLogin(parsedCreds.IdentityID, parsedCreds.Resource)
|
||||
}
|
||||
return MachineIdentityCredential{}, fmt.Errorf("failed to parse AzureAuthCredential")
|
||||
},
|
||||
util.GCP_ID_TOKEN: func(cred interface{}) (credential MachineIdentityCredential, err error) {
|
||||
if parsedCreds, ok := cred.(models.GCPIDTokenCredential); ok {
|
||||
return c.auth.GcpIdTokenAuthLogin(parsedCreds.IdentityID)
|
||||
}
|
||||
return MachineIdentityCredential{}, fmt.Errorf("failed to parse GCPIDTokenCredential")
|
||||
},
|
||||
util.GCP_IAM: func(cred interface{}) (credential MachineIdentityCredential, err error) {
|
||||
if parsedCreds, ok := cred.(models.GCPIAMCredential); ok {
|
||||
return c.auth.GcpIamAuthLogin(parsedCreds.IdentityID, parsedCreds.ServiceAccountKeyFilePath)
|
||||
}
|
||||
return MachineIdentityCredential{}, fmt.Errorf("failed to parse GCPIAMCredential")
|
||||
},
|
||||
util.AWS_IAM: func(cred interface{}) (credential MachineIdentityCredential, err error) {
|
||||
if parsedCreds, ok := cred.(models.AWSIAMCredential); ok {
|
||||
return c.auth.AwsIamAuthLogin(parsedCreds.IdentityID)
|
||||
}
|
||||
return MachineIdentityCredential{}, fmt.Errorf("failed to parse AWSIAMCredential")
|
||||
},
|
||||
util.JWT_AUTH: func(cred interface{}) (credential MachineIdentityCredential, err error) {
|
||||
if parsedCreds, ok := cred.(models.JWTCredential); ok {
|
||||
return c.auth.JwtAuthLogin(parsedCreds.IdentityID, parsedCreds.JWT)
|
||||
}
|
||||
return MachineIdentityCredential{}, fmt.Errorf("failed to parse JWTCredential")
|
||||
},
|
||||
util.LDAP_AUTH: func(cred interface{}) (credential MachineIdentityCredential, err error) {
|
||||
if parsedCreds, ok := cred.(models.LDAPCredential); ok {
|
||||
return c.auth.LdapAuthLogin(parsedCreds.IdentityID, parsedCreds.Username, parsedCreds.Password)
|
||||
}
|
||||
return MachineIdentityCredential{}, fmt.Errorf("failed to parse LDAPCredential")
|
||||
},
|
||||
util.OCI_AUTH: func(cred interface{}) (credential MachineIdentityCredential, err error) {
|
||||
if parsedCreds, ok := cred.(models.OCICredential); ok {
|
||||
return c.auth.OciAuthLogin(OciAuthLoginOptions{
|
||||
IdentityID: parsedCreds.IdentityID,
|
||||
PrivateKey: parsedCreds.PrivateKey,
|
||||
Fingerprint: parsedCreds.Fingerprint,
|
||||
UserID: parsedCreds.UserID,
|
||||
TenancyID: parsedCreds.TenancyID,
|
||||
Region: parsedCreds.Region,
|
||||
Passphrase: parsedCreds.Passphrase,
|
||||
})
|
||||
}
|
||||
return MachineIdentityCredential{}, fmt.Errorf("failed to parse OCICredential")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *InfisicalClient) beforeRequestAuthInterceptor(client *resty.Client, req *resty.Request) error {
|
||||
// skip auth endpoints to prevent infinite loops.
|
||||
// note(daniel): req.URL contains just the path ("/v1/auth/..."), not the full URL with base.
|
||||
// the base URL has /api appended, but that's not part of req.URL at this point.
|
||||
if strings.Contains(req.URL, "/v1/auth/") && req.Method == http.MethodPost {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if token is expired or will expire within 5 seconds
|
||||
if c.isTokenExpiringSoon(renewalBufferSeconds) {
|
||||
if err := c.refreshTokenSynchronously(true); err != nil {
|
||||
// Don't fail the request on refresh error, we let the request fail with 401 as it normally would.
|
||||
// logging is already done within refreshTokenSynchronously
|
||||
return nil
|
||||
}
|
||||
|
||||
c.mu.RLock()
|
||||
newToken := c.tokenDetails.AccessToken
|
||||
c.mu.RUnlock()
|
||||
|
||||
if newToken != "" {
|
||||
req.SetAuthToken(newToken)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user