Skip to content

Commit

Permalink
feat: Support API Key Authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
btfhernandez committed Oct 17, 2024
1 parent 2503266 commit 507fc77
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ type AuthenticationObj struct {
ApiUrl url.URL
clientId string
clientSecret string
apiKey string
HttpClient utils.HttpClientObj
ExponentialBackOff *backoff.ExponentialBackOff
log logging.Logger
}

// Authenticate is responsible for Auth configuration.
// Authenticate is responsible for Auth configuration using Client Id and Client secret.
// Prerequisites - use input validation methods before using this class.
func Authenticate(httpClient utils.HttpClientObj, backoffDefinition *backoff.ExponentialBackOff, endpointUrl string, clientId string, clientSecret string, logger logging.Logger, retryMaxElapsedTimeSeconds int) (*AuthenticationObj, error) {

Expand All @@ -36,6 +37,25 @@ func Authenticate(httpClient utils.HttpClientObj, backoffDefinition *backoff.Exp
HttpClient: httpClient,
clientId: clientId,
clientSecret: clientSecret,
apiKey: "",
ExponentialBackOff: backoffDefinition,
log: logger,
}

return authenticationObj, nil
}

// Authenticate is responsible for Auth configuration using API Key.
// Prerequisites - use input validation methods before using this class.
func AuthenticateUsingApiKey(httpClient utils.HttpClientObj, backoffDefinition *backoff.ExponentialBackOff, endpointUrl string, logger logging.Logger, retryMaxElapsedTimeSeconds int, apiKey string) (*AuthenticationObj, error) {

apiUrl, _ := url.Parse(endpointUrl)
authenticationObj := &AuthenticationObj{
ApiUrl: *apiUrl,
HttpClient: httpClient,
clientId: "",
clientSecret: "",
apiKey: apiKey,
ExponentialBackOff: backoffDefinition,
log: logger,
}
Expand All @@ -45,15 +65,21 @@ func Authenticate(httpClient utils.HttpClientObj, backoffDefinition *backoff.Exp

// GetPasswordSafeAuthentication is responsible for getting a token and signing in.
func (authenticationObj *AuthenticationObj) GetPasswordSafeAuthentication() (entities.SignApinResponse, error) {
var accessToken string = ""
var err error

accessToken, err := authenticationObj.GetToken(authenticationObj.ApiUrl.JoinPath("Auth/connect/token").String(), authenticationObj.clientId, authenticationObj.clientSecret)
if err != nil {
return entities.SignApinResponse{}, err
if authenticationObj.clientId != "" && authenticationObj.clientSecret != "" {
accessToken, err = authenticationObj.GetToken(authenticationObj.ApiUrl.JoinPath("Auth/connect/token").String(), authenticationObj.clientId, authenticationObj.clientSecret)
if err != nil {
return entities.SignApinResponse{}, err
}
}
signApinResponse, err := authenticationObj.SignAppin(authenticationObj.ApiUrl.JoinPath("Auth/SignAppIn").String(), accessToken)

signApinResponse, err := authenticationObj.SignAppin(authenticationObj.ApiUrl.JoinPath("Auth/SignAppIn").String(), accessToken, authenticationObj.apiKey)
if err != nil {
return entities.SignApinResponse{}, err
}

return signApinResponse, nil
}

Expand All @@ -73,7 +99,7 @@ func (authenticationObj *AuthenticationObj) GetToken(endpointUrl string, clientI
buffer.WriteString(params.Encode())

technicalError = backoff.Retry(func() error {
body, _, technicalError, businessError = authenticationObj.HttpClient.CallSecretSafeAPI(endpointUrl, "POST", buffer, "GetToken", "")
body, _, technicalError, businessError = authenticationObj.HttpClient.CallSecretSafeAPI(endpointUrl, "POST", buffer, "GetToken", "", "")
return technicalError
}, authenticationObj.ExponentialBackOff)

Expand Down Expand Up @@ -109,7 +135,7 @@ func (authenticationObj *AuthenticationObj) GetToken(endpointUrl string, clientI
}

// SignAppin is responsible for creating a PS API session.
func (authenticationObj *AuthenticationObj) SignAppin(endpointUrl string, accessToken string) (entities.SignApinResponse, error) {
func (authenticationObj *AuthenticationObj) SignAppin(endpointUrl string, accessToken string, apiKey string) (entities.SignApinResponse, error) {

var userObject entities.SignApinResponse
var body io.ReadCloser
Expand All @@ -118,7 +144,7 @@ func (authenticationObj *AuthenticationObj) SignAppin(endpointUrl string, access
var scode int

err := backoff.Retry(func() error {
body, scode, technicalError, businessError = authenticationObj.HttpClient.CallSecretSafeAPI(endpointUrl, "POST", bytes.Buffer{}, "SignAppin", accessToken)
body, scode, technicalError, businessError = authenticationObj.HttpClient.CallSecretSafeAPI(endpointUrl, "POST", bytes.Buffer{}, "SignAppin", accessToken, apiKey)
if scode == 0 {
return nil
}
Expand Down Expand Up @@ -163,7 +189,7 @@ func (authenticationObj *AuthenticationObj) SignOut() error {
var body io.ReadCloser

technicalError = backoff.Retry(func() error {
body, _, technicalError, businessError = authenticationObj.HttpClient.CallSecretSafeAPI(authenticationObj.ApiUrl.JoinPath("Auth/Signout").String(), "POST", bytes.Buffer{}, "SignOut", "")
body, _, technicalError, businessError = authenticationObj.HttpClient.CallSecretSafeAPI(authenticationObj.ApiUrl.JoinPath("Auth/Signout").String(), "POST", bytes.Buffer{}, "SignOut", "", "")
return technicalError
}, authenticationObj.ExponentialBackOff)

Expand Down
39 changes: 38 additions & 1 deletion api/authentication/authentication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,44 @@ func TestSignAppin(t *testing.T) {
},
}

response, err := authenticate.SignAppin(testConfig.server.URL+"/"+"TestSignAppin", "")
response, err := authenticate.SignAppin(testConfig.server.URL+"/"+"TestSignAppin", "", "")

if !reflect.DeepEqual(response, *testConfig.response) {
t.Errorf("Test case Failed %v, %v", response, *testConfig.response)
}

if err != nil {
t.Errorf("Test case Failed: %v", err)
}
}

func TestSignAppinWithApiKey(t *testing.T) {
logger, _ := zap.NewDevelopment()

// create a zap logger wrapper
zapLogger := logging.NewZapLogger(logger)

httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger)

backoffDefinition := backoff.NewExponentialBackOff()
backoffDefinition.MaxElapsedTime = time.Second

var authenticate, _ = AuthenticateUsingApiKey(*httpClientObj, backoffDefinition, "https://fake.api.com:443/BeyondTrust/api/public/v3/", zapLogger, 300, "fake_api_key_")
testConfig := UserTestConfig{
name: "TestSignAppin",
server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(`{"UserId":1, "EmailAddress":"Felipe"}`))
if err != nil {
t.Error("Test case Failed")
}
})),
response: &entities.SignApinResponse{
UserId: 1,
EmailAddress: "Felipe",
},
}

response, err := authenticate.SignAppin(testConfig.server.URL+"/"+"TestSignAppin", "", "fake_api_key_")

if !reflect.DeepEqual(response, *testConfig.response) {
t.Errorf("Test case Failed %v, %v", response, *testConfig.response)
Expand Down
8 changes: 4 additions & 4 deletions api/managed_account/managed_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func (managedAccounObj *ManagedAccountstObj) ManagedAccountGet(systemName string
var businessError error

technicalError = backoff.Retry(func() error {
body, _, technicalError, businessError = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "ManagedAccountGet", "")
body, _, technicalError, businessError = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "ManagedAccountGet", "", "")
if technicalError != nil {
return technicalError
}
Expand Down Expand Up @@ -164,7 +164,7 @@ func (managedAccounObj *ManagedAccountstObj) ManagedAccountCreateRequest(systemN
var businessError error

technicalError = backoff.Retry(func() error {
body, _, technicalError, businessError = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "POST", *b, "ManagedAccountCreateRequest", "")
body, _, technicalError, businessError = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "POST", *b, "ManagedAccountCreateRequest", "", "")
return technicalError
}, managedAccounObj.authenticationObj.ExponentialBackOff)

Expand Down Expand Up @@ -200,7 +200,7 @@ func (managedAccounObj *ManagedAccountstObj) CredentialByRequestId(requestId str
var businessError error

technicalError = backoff.Retry(func() error {
body, _, technicalError, businessError = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "CredentialByRequestId", "")
body, _, technicalError, businessError = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "CredentialByRequestId", "", "")
return technicalError
}, managedAccounObj.authenticationObj.ExponentialBackOff)

Expand Down Expand Up @@ -236,7 +236,7 @@ func (managedAccounObj *ManagedAccountstObj) ManagedAccountRequestCheckIn(reques
var businessError error

technicalError = backoff.Retry(func() error {
_, _, technicalError, businessError = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "PUT", *b, "ManagedAccountRequestCheckIn", "")
_, _, technicalError, businessError = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "PUT", *b, "ManagedAccountRequestCheckIn", "", "")
return technicalError
}, managedAccounObj.authenticationObj.ExponentialBackOff)

Expand Down
4 changes: 2 additions & 2 deletions api/secrets/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (secretObj *SecretObj) SecretGetSecretByPath(secretPath string, secretTitle
secretObj.log.Debug(messageLog)

technicalError = backoff.Retry(func() error {
body, scode, technicalError, businessError = secretObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "SecretGetSecretByPath", "")
body, scode, technicalError, businessError = secretObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "SecretGetSecretByPath", "", "")
return technicalError
}, secretObj.authenticationObj.ExponentialBackOff)

Expand Down Expand Up @@ -162,7 +162,7 @@ func (secretObj *SecretObj) SecretGetFileSecret(secretId string, endpointPath st
url := secretObj.authenticationObj.ApiUrl.JoinPath(endpointPath, secretId, "/file/download").String()

technicalError = backoff.Retry(func() error {
body, _, technicalError, businessError = secretObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "SecretGetFileSecret", "")
body, _, technicalError, businessError = secretObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "SecretGetFileSecret", "", "")
return technicalError
}, secretObj.authenticationObj.ExponentialBackOff)

Expand Down
60 changes: 57 additions & 3 deletions api/utils/httpclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ package utils
import (
"bytes"
"crypto/tls"
"encoding/pem"
"errors"
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"os"
"path/filepath"
"time"

logging "github.com/BeyondTrust/go-client-library-passwordsafe/api/logging"
"golang.org/x/crypto/pkcs12"
)

// HttpClientObj responsible for http request instance.
Expand Down Expand Up @@ -63,9 +68,54 @@ func GetHttpClient(clientTimeOut int, verifyCa bool, certificate string, certifi
return httpClientObj, nil
}

func GetPFXContent(clientCertificatePath string, clientCertificateName string, clientCertificatePassword string, logger logging.Logger) (string, string, error) {

if clientCertificateName != "" {
pfxFile, err := os.ReadFile(filepath.Join(clientCertificatePath, clientCertificateName))
if err != nil {
logger.Error(err.Error())
return "", "", err
}

pfxFileBlock, err := pkcs12.ToPEM(pfxFile, clientCertificatePassword)
if err != nil {
logger.Error(err.Error())
return "", "", err
}

var keyBlock, certificateBlock *pem.Block
for _, pemBlock := range pfxFileBlock {
if pemBlock.Type == "PRIVATE KEY" {
keyBlock = pemBlock
} else if pemBlock.Type == "CERTIFICATE" {
certificateBlock = pemBlock
}
}

if keyBlock == nil {
err = errors.New("error getting Key Block")
logger.Error(err.Error())
return "", "", err
}
if certificateBlock == nil {
err = errors.New("error getting Certificate Block")
logger.Error(err.Error())
return "", "", err
}

privateKeyData := pem.EncodeToMemory(keyBlock)
certData := pem.EncodeToMemory(certificateBlock)

return string(certData), string(privateKeyData), nil
}

return "", "", errors.New("empty certificate path")

}

// CallSecretSafeAPI prepares http call
func (client *HttpClientObj) CallSecretSafeAPI(url string, httpMethod string, body bytes.Buffer, method string, accesToken string) (io.ReadCloser, int, error, error) {
response, scode, technicalError, businessError := client.HttpRequest(url, httpMethod, body, accesToken)
func (client *HttpClientObj) CallSecretSafeAPI(url string, httpMethod string, body bytes.Buffer, method string, accesToken string, apiKey string) (io.ReadCloser, int, error, error) {
response, scode, technicalError, businessError := client.HttpRequest(url, httpMethod, body, accesToken, apiKey)
if technicalError != nil {
messageLog := fmt.Sprintf("Error in %v %v \n", method, technicalError)
client.log.Error(messageLog)
Expand All @@ -79,7 +129,7 @@ func (client *HttpClientObj) CallSecretSafeAPI(url string, httpMethod string, bo
}

// HttpRequest makes http request to the server.
func (client *HttpClientObj) HttpRequest(url string, method string, body bytes.Buffer, accesToken string) (closer io.ReadCloser, scode int, technicalError error, businessError error) {
func (client *HttpClientObj) HttpRequest(url string, method string, body bytes.Buffer, accesToken string, apiKey string) (closer io.ReadCloser, scode int, technicalError error, businessError error) {

req, err := http.NewRequest(method, url, &body)
if err != nil {
Expand All @@ -93,6 +143,10 @@ func (client *HttpClientObj) HttpRequest(url string, method string, body bytes.B
req.Header.Set("Authorization", "Bearer "+accesToken)
}

if apiKey != "" {
req.Header.Set("Authorization", "PS-Auth key="+apiKey)
}

resp, err := client.HttpClient.Do(req)
if err != nil {
client.log.Error(fmt.Sprintf("%v %v", "Error Making request: ", err.Error()))
Expand Down

0 comments on commit 507fc77

Please sign in to comment.