Skip to content

Commit

Permalink
Refactor API endpoint construction and authentication***
Browse files Browse the repository at this point in the history
  • Loading branch information
ShocOne committed Feb 9, 2024
1 parent 63567bb commit 57eee4a
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 71 deletions.
51 changes: 31 additions & 20 deletions apihandlers/jamfpro/jamfpro_api_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,6 @@ import (
"go.uber.org/zap"
)

// Endpoint constants represent the URL suffixes used for Jamf API token interactions.
const (
DefaultBaseDomain = ".jamfcloud.com" // DefaultBaseDomain: represents the base domain for the jamf instance.
OAuthTokenEndpoint = "/api/oauth/token" // OAuthTokenEndpoint: The endpoint to obtain an OAuth token.
BearerTokenEndpoint = "/api/v1/auth/token" // BearerTokenEndpoint: The endpoint to obtain a bearer token.
TokenRefreshEndpoint = "/api/v1/auth/keep-alive" // TokenRefreshEndpoint: The endpoint to refresh an existing token.
TokenInvalidateEndpoint = "/api/v1/auth/invalidate-token" // TokenInvalidateEndpoint: The endpoint to invalidate an active token.
)

// ConfigMap is a map that associates endpoint URL patterns with their corresponding configurations.
// The map's keys are strings that identify the endpoint, and the values are EndpointConfig structs
// that hold the configuration for that endpoint.
Expand Down Expand Up @@ -119,48 +110,68 @@ type Logger interface {

// Functions

// GetDefaultBaseDomain returns the default base domain used for constructing API URLs.
func (j *JamfAPIHandler) GetDefaultBaseDomain() string {
return DefaultBaseDomain
}

// GetOAuthTokenEndpoint returns the endpoint for obtaining an OAuth token. Used for constructing API URLs.
func (j *JamfAPIHandler) GetOAuthTokenEndpoint() string {
return OAuthTokenEndpoint
}

// GetBearerTokenEndpoint returns the endpoint for obtaining a bearer token. Used for constructing API URLs.
func (j *JamfAPIHandler) GetBearerTokenEndpoint() string {
return BearerTokenEndpoint
}

// GetTokenRefreshEndpoint returns the endpoint for refreshing an existing token. Used for constructing API URLs.
func (j *JamfAPIHandler) GetTokenRefreshEndpoint() string {
return TokenRefreshEndpoint
}

// GetTokenInvalidateEndpoint returns the endpoint for invalidating an active token. Used for constructing API URLs.
func (j *JamfAPIHandler) GetTokenInvalidateEndpoint() string {
return TokenInvalidateEndpoint
}

// GetBaseDomain returns the appropriate base domain for URL construction.
// It uses OverrideBaseDomain if set, otherwise falls back to DefaultBaseDomain.
func (j *JamfAPIHandler) GetBaseDomain() string {
// GetAPIBearerTokenAuthenticationSupportStatus returns a boolean indicating if bearer token authentication is supported in the api handler.
func (j *JamfAPIHandler) GetAPIBearerTokenAuthenticationSupportStatus() bool {
return BearerTokenAuthenticationSupport
}

// GetAPIOAuthAuthenticationSupportStatus returns a boolean indicating if OAuth authentication is supported in the api handler.
func (j *JamfAPIHandler) GetAPIOAuthAuthenticationSupportStatus() bool {
return OAuthAuthenticationSupport
}

// GetAPIOAuthWithCertAuthenticationSupportStatus returns a boolean indicating if OAuth with client certificate authentication is supported in the api handler.
func (j *JamfAPIHandler) GetAPIOAuthWithCertAuthenticationSupportStatus() bool {
return OAuthWithCertAuthenticationSupport
}

// SetBaseDomain returns the appropriate base domain for URL construction.
// It uses j.OverrideBaseDomain if set, otherwise falls back to DefaultBaseDomain.
func (j *JamfAPIHandler) SetBaseDomain() string {
if j.OverrideBaseDomain != "" {
return j.OverrideBaseDomain
}
return DefaultBaseDomain
}

// ConstructAPIResourceEndpoint constructs the full URL for a Jamf API resource endpoint path and logs the URL.
func (j *JamfAPIHandler) ConstructAPIResourceEndpoint(endpointPath string, log logger.Logger) string {
baseDomain := j.GetBaseDomain()
url := fmt.Sprintf("https://%s%s%s", j.InstanceName, baseDomain, endpointPath)
log.Info("Constructed API resource endpoint URL", zap.String("URL", url))
func (j *JamfAPIHandler) ConstructAPIResourceEndpoint(instanceName string, endpointPath string, log logger.Logger) string {
urlBaseDomain := j.SetBaseDomain()
url := fmt.Sprintf("https://%s%s%s", instanceName, urlBaseDomain, endpointPath)
log.Info(fmt.Sprintf("Constructed %s API resource endpoint URL", APIName), zap.String("URL", url))
return url
}

// ConstructAPIAuthEndpoint constructs the full URL for a Jamf API auth endpoint path and logs the URL.
func (j *JamfAPIHandler) ConstructAPIAuthEndpoint(endpointPath string, log logger.Logger) string {
baseDomain := j.GetBaseDomain()
url := fmt.Sprintf("https://%s%s%s", j.InstanceName, baseDomain, endpointPath)
log.Info("Constructed API authentication URL", zap.String("URL", url))
func (j *JamfAPIHandler) ConstructAPIAuthEndpoint(instanceName string, endpointPath string, log logger.Logger) string {
urlBaseDomain := j.SetBaseDomain()
url := fmt.Sprintf("https://%s%s%s", instanceName, urlBaseDomain, endpointPath)
log.Info(fmt.Sprintf("Constructed %s API authentication URL", APIName), zap.String("URL", url))
return url
}

Expand Down
14 changes: 14 additions & 0 deletions apihandlers/jamfpro/jamfpro_api_handler_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package jamfpro

// Endpoint constants represent the URL suffixes used for Jamf API token interactions.
const (
APIName = "jamf pro" // APIName: represents the name of the API.
DefaultBaseDomain = ".jamfcloud.com" // DefaultBaseDomain: represents the base domain for the jamf instance.
OAuthTokenEndpoint = "/api/oauth/token" // OAuthTokenEndpoint: The endpoint to obtain an OAuth token.

Check failure

Code scanning / Golang security checks by gosec

Potential hardcoded credentials Error

Potential hardcoded credentials
BearerTokenEndpoint = "/api/v1/auth/token" // BearerTokenEndpoint: The endpoint to obtain a bearer token.

Check failure

Code scanning / Golang security checks by gosec

Potential hardcoded credentials Error

Potential hardcoded credentials
TokenRefreshEndpoint = "/api/v1/auth/keep-alive" // TokenRefreshEndpoint: The endpoint to refresh an existing token.

Check failure

Code scanning / Golang security checks by gosec

Potential hardcoded credentials Error

Potential hardcoded credentials
TokenInvalidateEndpoint = "/api/v1/auth/invalidate-token" // TokenInvalidateEndpoint: The endpoint to invalidate an active token.

Check failure

Code scanning / Golang security checks by gosec

Potential hardcoded credentials Error

Potential hardcoded credentials
BearerTokenAuthenticationSupport = true // BearerTokenAuthSuppport: A boolean to indicate if the API supports bearer token authentication.
OAuthAuthenticationSupport = true // OAuthAuthSuppport: A boolean to indicate if the API supports OAuth authentication.
OAuthWithCertAuthenticationSupport = false // OAuthWithCertAuthSuppport: A boolean to indicate if the API supports OAuth with client certificate authentication.
)
8 changes: 5 additions & 3 deletions httpclient/httpclient_api_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import (
// APIHandler is an interface for encoding, decoding, and implenting contexual api functions for different API implementations.
// It encapsulates behavior for encoding and decoding requests and responses.
type APIHandler interface {
GetBaseDomain() string
ConstructAPIResourceEndpoint(endpointPath string, log logger.Logger) string
ConstructAPIAuthEndpoint(endpointPath string, log logger.Logger) string
ConstructAPIResourceEndpoint(instanceName string, endpointPath string, log logger.Logger) string
ConstructAPIAuthEndpoint(instanceName string, endpointPath string, log logger.Logger) string
MarshalRequest(body interface{}, method string, endpoint string, log logger.Logger) ([]byte, error)
MarshalMultipartRequest(fields map[string]string, files map[string]string, log logger.Logger) ([]byte, string, error)
UnmarshalResponse(resp *http.Response, out interface{}, log logger.Logger) error
Expand All @@ -25,6 +24,9 @@ type APIHandler interface {
GetBearerTokenEndpoint() string
GetTokenRefreshEndpoint() string
GetTokenInvalidateEndpoint() string
GetAPIBearerTokenAuthenticationSupportStatus() bool
GetAPIOAuthAuthenticationSupportStatus() bool
GetAPIOAuthWithCertAuthenticationSupportStatus() bool
}

// LoadAPIHandler returns an APIHandler based on the provided API type.
Expand Down
51 changes: 6 additions & 45 deletions httpclient/httpclient_bearer_token_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ func (c *Client) SetBearerTokenAuthCredentials(credentials BearerTokenAuthCreden
// ObtainToken fetches and sets an authentication token using the stored basic authentication credentials.
func (c *Client) ObtainToken(log logger.Logger) error {

// Use the APIHandler's method to get the bearer token endpoint
bearerTokenEndpoint := c.APIHandler.GetBearerTokenEndpoint()
authenticationEndpoint := c.APIHandler.ConstructAPIAuthEndpoint(bearerTokenEndpoint, c.Logger)

// Construct the full authentication endpoint URL
authenticationEndpoint := c.APIHandler.ConstructAPIAuthEndpoint(c.InstanceName, bearerTokenEndpoint, c.Logger)

log.Debug("Attempting to obtain token for user", zap.String("Username", c.BearerTokenAuthCredentials.Username))

Expand Down Expand Up @@ -70,57 +73,15 @@ func (c *Client) ObtainToken(log logger.Logger) error {
return nil
}

/*
// RefreshToken refreshes the current authentication token.
func (c *Client) RefreshToken() error {
c.tokenLock.Lock()
defer c.tokenLock.Unlock()
tokenRefreshEndpoint := c.ConstructAPIAuthEndpoint(TokenRefreshEndpoint)
req, err := http.NewRequest("POST", tokenRefreshEndpoint, nil)
if err != nil {
log.Error("Failed to create new request for token refresh", "error", err)
return err
}
req.Header.Add("Authorization", "Bearer "+c.Token)
log.Debug("Attempting to refresh token", "URL", tokenRefreshEndpoint)
resp, err := c.httpClient.Do(req)
if err != nil {
log.Error("Failed to make request for token refresh", "error", err)
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Warn("Token refresh response status is not OK", "StatusCode", resp.StatusCode)
return c.HandleAPIError(resp)
}
tokenResp := &TokenResponse{}
err = json.NewDecoder(resp.Body).Decode(tokenResp)
if err != nil {
log.Error("Failed to decode token response", "error", err)
return err
}
log.Info("Token refreshed successfully", "Expiry", tokenResp.Expires)
c.Token = tokenResp.Token
c.Expiry = tokenResp.Expires
return nil
}
*/
// RefreshToken refreshes the current authentication token.
func (c *Client) RefreshToken(log logger.Logger) error {
c.tokenLock.Lock()
defer c.tokenLock.Unlock()

apiTokenRefreshEndpoint := c.APIHandler.GetTokenRefreshEndpoint()

tokenRefreshEndpoint := c.APIHandler.ConstructAPIAuthEndpoint(apiTokenRefreshEndpoint, c.Logger)
// Construct the full authentication endpoint URL
tokenRefreshEndpoint := c.APIHandler.ConstructAPIAuthEndpoint(c.InstanceName, apiTokenRefreshEndpoint, c.Logger)

req, err := http.NewRequest("POST", tokenRefreshEndpoint, nil)
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion httpclient/httpclient_oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ func (c *Client) SetOAuthCredentials(credentials OAuthCredentials) {
// It updates the client's Token and Expiry fields with the obtained values.
func (c *Client) ObtainOAuthToken(credentials AuthConfig, log logger.Logger) error {

// Use the APIHandler's method to get the OAuth token endpoint
oauthTokenEndpoint := c.APIHandler.GetOAuthTokenEndpoint()
authenticationEndpoint := c.APIHandler.ConstructAPIAuthEndpoint(oauthTokenEndpoint, c.Logger)

// Construct the full authentication endpoint URL
authenticationEndpoint := c.APIHandler.ConstructAPIAuthEndpoint(c.InstanceName, oauthTokenEndpoint, c.Logger)

data := url.Values{}
data.Set("client_id", credentials.ClientID)
Expand Down
4 changes: 2 additions & 2 deletions httpclient/httpclient_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (c *Client) DoRequest(method, endpoint string, body, out interface{}, log l
}

// Construct URL using the ConstructAPIResourceEndpoint function
url := apiHandler.ConstructAPIResourceEndpoint(endpoint, log)
url := apiHandler.ConstructAPIResourceEndpoint(c.InstanceName, endpoint, log)

// Initialize total request counter
c.PerfMetrics.lock.Lock()
Expand Down Expand Up @@ -370,7 +370,7 @@ func (c *Client) DoMultipartRequest(method, endpoint string, fields map[string]s
}

// Construct URL using the ConstructAPIResourceEndpoint function
url := apiHandler.ConstructAPIResourceEndpoint(endpoint, log)
url := apiHandler.ConstructAPIResourceEndpoint(c.InstanceName, endpoint, log)

// Create the request
req, err := http.NewRequest(method, url, bytes.NewBuffer(requestData))
Expand Down

0 comments on commit 57eee4a

Please sign in to comment.