From fc9d51190c7b9597a2c9892a5a304436ef8c880b Mon Sep 17 00:00:00 2001 From: "EPAM\\Felipe_Hernandez" Date: Tue, 20 Feb 2024 17:57:41 -0500 Subject: [PATCH] feat: reactor http client definition --- api/authentication/authentication_test.go | 41 ++++++-- api/authentication/authetication.go | 97 ++++------------- api/managed_account/managed_account.go | 12 +-- api/managed_account/managed_account_test.go | 111 +++++++++----------- api/secrets/secrets.go | 4 +- api/secrets/secrets_test.go | 49 +++++++-- api/utils/httpclient.go | 75 ++++++++++++- api/utils/validator.go | 43 ++++++-- main.go | 8 +- 9 files changed, 263 insertions(+), 177 deletions(-) diff --git a/api/authentication/authentication_test.go b/api/authentication/authentication_test.go index fe7221f..7a96081 100644 --- a/api/authentication/authentication_test.go +++ b/api/authentication/authentication_test.go @@ -7,13 +7,13 @@ import ( "go-client-library-passwordsafe/api/entities" "go-client-library-passwordsafe/api/logging" "go-client-library-passwordsafe/api/utils" - "log" - "os" "reflect" "net/http" "net/http/httptest" "testing" + + "go.uber.org/zap" ) type UserTestConfig struct { @@ -34,13 +34,16 @@ type GetPasswordSafeAuthenticationConfig struct { response *entities.SignApinResponse } -var logger = log.New(os.Stdout, "DEBUG: ", log.Ldate|log.Ltime) -var logLogger = logging.NewLogLogger(logger) -var httpClient, _ = utils.GetHttpClient(5, true, "", "") -var authenticate, _ = Authenticate(httpClient, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", logLogger, 300) - func TestSignOut(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + + var authenticate, _ = Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := UserTestConfig{ name: "TestSignOut", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -56,7 +59,15 @@ func TestSignOut(t *testing.T) { } func TestSignAppin(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + + var authenticate, _ = Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := UserTestConfig{ name: "TestSignAppin", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -80,7 +91,15 @@ func TestSignAppin(t *testing.T) { } func TestGetToken(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + var authenticate, _ = Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := GetTokenConfig{ name: "TestGetToken", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -109,7 +128,15 @@ func TestGetToken(t *testing.T) { } func TestGetPasswordSafeAuthentication(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + var authenticate, _ = Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := GetPasswordSafeAuthenticationConfig{ name: "TestGetPasswordSafeAuthentication", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/api/authentication/authetication.go b/api/authentication/authetication.go index 5dd38df..8dcd6b0 100644 --- a/api/authentication/authetication.go +++ b/api/authentication/authetication.go @@ -8,9 +8,9 @@ import ( "fmt" "go-client-library-passwordsafe/api/entities" "go-client-library-passwordsafe/api/logging" + "go-client-library-passwordsafe/api/utils" "io" - "net/http" "net/url" "time" @@ -21,13 +21,13 @@ type AuthenticationObj struct { ApiUrl string clientId string clientSecret string - httpClient *http.Client + HttpClient utils.HttpClientObj ExponentialBackOff *backoff.ExponentialBackOff log logging.Logger } // Authenticate in PS API -func Authenticate(httpClient *http.Client, endpointUrl string, clientId string, clientSecret string, logger logging.Logger, retryMaxElapsedTimeSeconds int) (*AuthenticationObj, error) { +func Authenticate(httpClient utils.HttpClientObj, endpointUrl string, clientId string, clientSecret string, logger logging.Logger, retryMaxElapsedTimeSeconds int) (*AuthenticationObj, error) { backoffDefinition := backoff.NewExponentialBackOff() backoffDefinition.InitialInterval = 1 * time.Second @@ -39,7 +39,7 @@ func Authenticate(httpClient *http.Client, endpointUrl string, clientId string, authenticationObj := &AuthenticationObj{ ApiUrl: endpointUrl, - httpClient: client, + HttpClient: client, clientId: clientId, clientSecret: clientSecret, ExponentialBackOff: backoffDefinition, @@ -50,12 +50,12 @@ func Authenticate(httpClient *http.Client, endpointUrl string, clientId string, } // GetPasswordSafeAuthentication call get token and sign app endpoint -func (c *AuthenticationObj) GetPasswordSafeAuthentication() (entities.SignApinResponse, error) { - accessToken, err := c.GetToken(fmt.Sprintf("%v%v", c.ApiUrl, "Auth/connect/token"), c.clientId, c.clientSecret) +func (authenticationObj *AuthenticationObj) GetPasswordSafeAuthentication() (entities.SignApinResponse, error) { + accessToken, err := authenticationObj.GetToken(fmt.Sprintf("%v%v", authenticationObj.ApiUrl, "Auth/connect/token"), authenticationObj.clientId, authenticationObj.clientSecret) if err != nil { return entities.SignApinResponse{}, err } - signApinResponse, err := c.SignAppin(fmt.Sprintf("%v%v", c.ApiUrl, "Auth/SignAppIn"), accessToken) + signApinResponse, err := authenticationObj.SignAppin(fmt.Sprintf("%v%v", authenticationObj.ApiUrl, "Auth/SignAppIn"), accessToken) if err != nil { return entities.SignApinResponse{}, err } @@ -63,7 +63,7 @@ func (c *AuthenticationObj) GetPasswordSafeAuthentication() (entities.SignApinRe } // GetToken get token from PS API -func (c *AuthenticationObj) GetToken(endpointUrl string, clientId string, clientSecret string) (string, error) { +func (authenticationObj *AuthenticationObj) GetToken(endpointUrl string, clientId string, clientSecret string) (string, error) { params := url.Values{} params.Add("client_id", clientId) @@ -78,9 +78,9 @@ func (c *AuthenticationObj) GetToken(endpointUrl string, clientId string, client buffer.WriteString(params.Encode()) technicalError = backoff.Retry(func() error { - body, technicalError, businessError, _ = c.CallSecretSafeAPI(endpointUrl, "POST", buffer, "GetToken", "") + body, technicalError, businessError, _ = authenticationObj.HttpClient.CallSecretSafeAPI(endpointUrl, "POST", buffer, "GetToken", "") return technicalError - }, c.ExponentialBackOff) + }, authenticationObj.ExponentialBackOff) if technicalError != nil { return "", technicalError @@ -102,7 +102,7 @@ func (c *AuthenticationObj) GetToken(endpointUrl string, clientId string, client err = json.Unmarshal([]byte(responseString), &data) if err != nil { - c.log.Error(err.Error()) + authenticationObj.log.Error(err.Error()) return "", err } @@ -111,7 +111,7 @@ func (c *AuthenticationObj) GetToken(endpointUrl string, clientId string, client } // SignAppin Signs app in PS API -func (c *AuthenticationObj) SignAppin(endpointUrl string, accessToken string) (entities.SignApinResponse, error) { +func (authenticationObj *AuthenticationObj) SignAppin(endpointUrl string, accessToken string) (entities.SignApinResponse, error) { var userObject entities.SignApinResponse var body io.ReadCloser @@ -120,12 +120,12 @@ func (c *AuthenticationObj) SignAppin(endpointUrl string, accessToken string) (e var scode int err := backoff.Retry(func() error { - body, technicalError, businessError, scode = c.CallSecretSafeAPI(endpointUrl, "POST", bytes.Buffer{}, "SignAppin", accessToken) + body, technicalError, businessError, scode = authenticationObj.HttpClient.CallSecretSafeAPI(endpointUrl, "POST", bytes.Buffer{}, "SignAppin", accessToken) if scode == 0 { return nil } return technicalError - }, c.ExponentialBackOff) + }, authenticationObj.ExponentialBackOff) if err != nil { return entities.SignApinResponse{}, err @@ -148,7 +148,7 @@ func (c *AuthenticationObj) SignAppin(endpointUrl string, accessToken string) (e err = json.Unmarshal(bodyBytes, &userObject) if err != nil { - c.log.Error(err.Error()) + authenticationObj.log.Error(err.Error()) return entities.SignApinResponse{}, err } @@ -157,78 +157,21 @@ func (c *AuthenticationObj) SignAppin(endpointUrl string, accessToken string) (e // SignOut signs out Secret Safe API. // Warn: should only be called one time for all data sources. -func (c *AuthenticationObj) SignOut(url string) error { - c.log.Debug(url) +func (authenticationObj *AuthenticationObj) SignOut(url string) error { + authenticationObj.log.Debug(url) var technicalError error var businessError error technicalError = backoff.Retry(func() error { - _, technicalError, businessError, _ = c.CallSecretSafeAPI(url, "POST", bytes.Buffer{}, "SignOut", "") + _, technicalError, businessError, _ = authenticationObj.HttpClient.CallSecretSafeAPI(url, "POST", bytes.Buffer{}, "SignOut", "") return technicalError - }, c.ExponentialBackOff) + }, authenticationObj.ExponentialBackOff) if businessError != nil { - c.log.Error(businessError.Error()) + authenticationObj.log.Error(businessError.Error()) return businessError } return nil } - -// CallSecretSafeAPI prepares http call -func (c *AuthenticationObj) CallSecretSafeAPI(url string, httpMethod string, body bytes.Buffer, method string, accesToken string) (io.ReadCloser, error, error, int) { - response, technicalError, businessError, scode := c.HttpRequest(url, httpMethod, body, accesToken) - if technicalError != nil { - messageLog := fmt.Sprintf("Error in %v %v \n", method, technicalError) - c.log.Error(messageLog) - } - - if businessError != nil { - messageLog := fmt.Sprintf("Error in %v: %v \n", method, businessError) - c.log.Error(messageLog) - } - return response, technicalError, businessError, scode -} - -// HttpRequest makes http request to he server -func (c *AuthenticationObj) HttpRequest(url string, method string, body bytes.Buffer, accesToken string) (closer io.ReadCloser, technicalError error, businessError error, scode int) { - - req, err := http.NewRequest(method, url, &body) - if err != nil { - return nil, err, nil, 0 - } - req.Header = http.Header{ - "Content-Type": {"application/json"}, - } - - if accesToken != "" { - req.Header.Set("Authorization", "Bearer "+accesToken) - } - - resp, err := c.httpClient.Do(req) - if err != nil { - c.log.Error(err.Error()) - return nil, err, nil, 0 - } - - if resp.StatusCode >= http.StatusInternalServerError || resp.StatusCode == http.StatusRequestTimeout { - err = fmt.Errorf("error %v: StatusCode: %v, %v, %v", method, scode, err, body) - c.log.Error(err.Error()) - return nil, err, nil, resp.StatusCode - } - - if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { - respBody := new(bytes.Buffer) - _, err = respBody.ReadFrom(resp.Body) - if err != nil { - c.log.Error(err.Error()) - return nil, err, nil, 0 - } - - err = fmt.Errorf("got a non 200 status code: %v - %v", resp.StatusCode, respBody) - return nil, nil, err, resp.StatusCode - } - - return resp.Body, nil, nil, resp.StatusCode -} diff --git a/api/managed_account/managed_account.go b/api/managed_account/managed_account.go index c75ba09..e515030 100644 --- a/api/managed_account/managed_account.go +++ b/api/managed_account/managed_account.go @@ -129,7 +129,7 @@ func (managedAccounObj *ManagedAccountstObj) ManagedAccountGet(systemName string var businessError error technicalError = backoff.Retry(func() error { - body, technicalError, businessError, _ = managedAccounObj.authenticationObj.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "ManagedAccountGet", "") + body, technicalError, businessError, _ = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "ManagedAccountGet", "") if technicalError != nil { return technicalError } @@ -175,7 +175,7 @@ func (managedAccounObj *ManagedAccountstObj) ManagedAccountCreateRequest(systemN var businessError error technicalError = backoff.Retry(func() error { - body, technicalError, businessError, _ = managedAccounObj.authenticationObj.CallSecretSafeAPI(url, "POST", *b, "ManagedAccountCreateRequest", "") + body, technicalError, businessError, _ = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "POST", *b, "ManagedAccountCreateRequest", "") return technicalError }, managedAccounObj.authenticationObj.ExponentialBackOff) @@ -203,14 +203,14 @@ func (managedAccounObj *ManagedAccountstObj) ManagedAccountCreateRequest(systemN // enpoint and returns secret value by request Id. func (managedAccounObj *ManagedAccountstObj) CredentialByRequestId(requestId string, url string) (string, error) { messageLog := fmt.Sprintf("%v %v", "GET", url) - managedAccounObj.log.Debug(messageLog) + managedAccounObj.log.Debug(strings.Replace(messageLog, requestId, "****", -1)) var body io.ReadCloser var technicalError error var businessError error technicalError = backoff.Retry(func() error { - body, technicalError, businessError, _ = managedAccounObj.authenticationObj.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "CredentialByRequestId", "") + body, technicalError, businessError, _ = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "CredentialByRequestId", "") return technicalError }, managedAccounObj.authenticationObj.ExponentialBackOff) @@ -241,7 +241,7 @@ func (managedAccounObj *ManagedAccountstObj) CredentialByRequestId(requestId str // ManagedAccountRequestCheckIn calls Secret Safe API "Requests//checkin enpoint. func (managedAccounObj *ManagedAccountstObj) ManagedAccountRequestCheckIn(requestId string, url string) (string, error) { messageLog := fmt.Sprintf("%v %v", "PUT", url) - managedAccounObj.log.Debug(messageLog) + managedAccounObj.log.Debug(strings.Replace(messageLog, requestId, "****", -1)) data := "{}" b := bytes.NewBufferString(data) @@ -250,7 +250,7 @@ func (managedAccounObj *ManagedAccountstObj) ManagedAccountRequestCheckIn(reques var businessError error technicalError = backoff.Retry(func() error { - _, technicalError, businessError, _ = managedAccounObj.authenticationObj.CallSecretSafeAPI(url, "PUT", *b, "ManagedAccountRequestCheckIn", "") + _, technicalError, businessError, _ = managedAccounObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "PUT", *b, "ManagedAccountRequestCheckIn", "") return technicalError }, managedAccounObj.authenticationObj.ExponentialBackOff) diff --git a/api/managed_account/managed_account_test.go b/api/managed_account/managed_account_test.go index 0e75e39..9033cc8 100644 --- a/api/managed_account/managed_account_test.go +++ b/api/managed_account/managed_account_test.go @@ -9,13 +9,12 @@ import ( "go-client-library-passwordsafe/api/entities" "go-client-library-passwordsafe/api/logging" "go-client-library-passwordsafe/api/utils" - "log" - "os" - "strings" - "net/http" "net/http/httptest" + "strings" "testing" + + "go.uber.org/zap" ) type ManagedAccountTestConfig struct { @@ -30,13 +29,18 @@ type ManagedAccountTestConfigStringResponse struct { response string } -var logger = log.New(os.Stdout, "DEBUG: ", log.Ldate|log.Ltime) -var logLogger = logging.NewLogLogger(logger) -var httpClient, _ = utils.GetHttpClient(5, true, "", "") -var authenticate, _ = authentication.Authenticate(httpClient, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", logLogger, 300) - func TestManagedAccountGet(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + + var authenticate, _ = authentication.Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) + testConfig := ManagedAccountTestConfig{ name: "TestManagedAccountGet", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -49,7 +53,7 @@ func TestManagedAccountGet(t *testing.T) { }, } authenticate.ApiUrl = testConfig.server.URL + "/" - managedAccountObj, _ := NewManagedAccountObj(*authenticate, logLogger) + managedAccountObj, _ := NewManagedAccountObj(*authenticate, zapLogger) response, err := managedAccountObj.ManagedAccountGet("fake_system_name", "fake_account_name", testConfig.server.URL) if response != *testConfig.response { @@ -62,7 +66,15 @@ func TestManagedAccountGet(t *testing.T) { } func TestManagedAccountCreateRequest(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + var authenticate, _ = authentication.Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := ManagedAccountTestConfigStringResponse{ name: "TestManagedAccountCreateRequest", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -73,7 +85,7 @@ func TestManagedAccountCreateRequest(t *testing.T) { } authenticate.ApiUrl = testConfig.server.URL + "/" - managedAccountObj, _ := NewManagedAccountObj(*authenticate, logLogger) + managedAccountObj, _ := NewManagedAccountObj(*authenticate, zapLogger) response, err := managedAccountObj.ManagedAccountCreateRequest(1, 10, testConfig.server.URL) if response != testConfig.response { @@ -86,7 +98,15 @@ func TestManagedAccountCreateRequest(t *testing.T) { } func TestCredentialByRequestId(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + + var authenticate, _ = authentication.Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := ManagedAccountTestConfigStringResponse{ name: "TestCredentialByRequestId", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -97,7 +117,7 @@ func TestCredentialByRequestId(t *testing.T) { } authenticate.ApiUrl = testConfig.server.URL + "/" - managedAccountObj, _ := NewManagedAccountObj(*authenticate, logLogger) + managedAccountObj, _ := NewManagedAccountObj(*authenticate, zapLogger) response, err := managedAccountObj.CredentialByRequestId("124", testConfig.server.URL) if response != testConfig.response { @@ -110,7 +130,15 @@ func TestCredentialByRequestId(t *testing.T) { } func TestManagedAccountRequestCheckIn(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + + var authenticate, _ = authentication.Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := ManagedAccountTestConfigStringResponse{ name: "TestManagedAccountRequestCheckIn", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -121,7 +149,7 @@ func TestManagedAccountRequestCheckIn(t *testing.T) { } authenticate.ApiUrl = testConfig.server.URL + "/" - managedAccountObj, _ := NewManagedAccountObj(*authenticate, logLogger) + managedAccountObj, _ := NewManagedAccountObj(*authenticate, zapLogger) response, err := managedAccountObj.ManagedAccountRequestCheckIn("124", testConfig.server.URL) if response != testConfig.response { @@ -134,7 +162,15 @@ func TestManagedAccountRequestCheckIn(t *testing.T) { } func TestManageAccountFlow(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + + var authenticate, _ = authentication.Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := ManagedAccountTestConfigStringResponse{ name: "TestManageAccountFlow", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -167,7 +203,7 @@ func TestManageAccountFlow(t *testing.T) { } authenticate.ApiUrl = testConfig.server.URL - managedAccountObj, _ := NewManagedAccountObj(*authenticate, logLogger) + managedAccountObj, _ := NewManagedAccountObj(*authenticate, zapLogger) secretDictionary := make(map[string]string) managedAccounList := strings.Split("oauthgrp_nocert/Test1,oauthgrp_nocert/client_id", ",") @@ -182,50 +218,3 @@ func TestManageAccountFlow(t *testing.T) { t.Errorf("Test case Failed: %v", err) } } - -func TestManageAccountFlowNotFound(t *testing.T) { - - testConfig := ManagedAccountTestConfigStringResponse{ - name: "TestManageAccountFlowFailedManagedAccounts", - server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Mocking Response accorging to the endpoint path - switch r.URL.Path { - - case "/Auth/SignAppin": - w.Write([]byte(`{"UserId":1, "EmailAddress":"Felipe"}`)) - - case "/Auth/Signout": - w.Write([]byte(``)) - - case fmt.Sprintf("/ManagedAccounts"): - w.WriteHeader(http.StatusNotFound) - w.Write([]byte(`"Managed Account not found"`)) - - case "/Requests": - w.Write([]byte(`124`)) - - case "/Credentials/124": - w.Write([]byte(`"fake_credential"`)) - - case "/Requests/124/checkin": - w.Write([]byte(``)) - - default: - http.NotFound(w, r) - } - })), - response: `got a non 200 status code: 404 - "Managed Account not found"`, - } - - authenticate.ApiUrl = testConfig.server.URL - managedAccountObj, _ := NewManagedAccountObj(*authenticate, logLogger) - - secretDictionary := make(map[string]string) - managedAccounList := strings.Split("oauthgrp_nocert/Test1,oauthgrp_nocert/client_id", ",") - - _, err := managedAccountObj.ManageAccountFlow(managedAccounList, "/", secretDictionary) - - if err.Error() != testConfig.response { - t.Errorf("Test case Failed %v, %v", err.Error(), testConfig.response) - } -} diff --git a/api/secrets/secrets.go b/api/secrets/secrets.go index bf52438..24804cc 100644 --- a/api/secrets/secrets.go +++ b/api/secrets/secrets.go @@ -95,7 +95,7 @@ func (secretObj *SecretObj) SecretGetSecretByPath(secretPath string, secretTitle url := fmt.Sprintf("%s%s?%s", secretObj.authenticationObj.ApiUrl, endpointPath, params.Encode()) technicalError = backoff.Retry(func() error { - body, technicalError, businessError, scode = secretObj.authenticationObj.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "SecretGetSecretByPath", "") + body, technicalError, businessError, scode = secretObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "SecretGetSecretByPath", "") return technicalError }, secretObj.authenticationObj.ExponentialBackOff) @@ -142,7 +142,7 @@ func (secretObj *SecretObj) SecretGetFileSecret(secretId string, endpointPath st url := fmt.Sprintf("%s%s%s%s", secretObj.authenticationObj.ApiUrl, endpointPath, secretId, "/file/download") technicalError = backoff.Retry(func() error { - body, technicalError, businessError, _ = secretObj.authenticationObj.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "SecretGetFileSecret", "") + body, technicalError, businessError, _ = secretObj.authenticationObj.HttpClient.CallSecretSafeAPI(url, "GET", bytes.Buffer{}, "SecretGetFileSecret", "") return technicalError }, secretObj.authenticationObj.ExponentialBackOff) diff --git a/api/secrets/secrets_test.go b/api/secrets/secrets_test.go index 5e8184c..4310415 100644 --- a/api/secrets/secrets_test.go +++ b/api/secrets/secrets_test.go @@ -8,13 +8,13 @@ import ( "go-client-library-passwordsafe/api/entities" "go-client-library-passwordsafe/api/logging" "go-client-library-passwordsafe/api/utils" - "log" - "os" "strings" "net/http" "net/http/httptest" "testing" + + "go.uber.org/zap" ) type SecretTestConfig struct { @@ -29,13 +29,16 @@ type SecretTestConfigStringResponse struct { response string } -var logger = log.New(os.Stdout, "DEBUG: ", log.Ldate|log.Ltime) -var logLogger = logging.NewLogLogger(logger) -var httpClient, _ = utils.GetHttpClient(5, true, "", "") -var authenticate, _ = authentication.Authenticate(httpClient, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", logLogger, 300) - func TestSecretGetSecretByPath(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + + var authenticate, _ = authentication.Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := SecretTestConfig{ name: "TestSecretGetSecretByPath", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -50,7 +53,7 @@ func TestSecretGetSecretByPath(t *testing.T) { } authenticate.ApiUrl = testConfig.server.URL + "/" - secretObj, _ := NewSecretObj(*authenticate, logLogger) + secretObj, _ := NewSecretObj(*authenticate, zapLogger) response, err := secretObj.SecretGetSecretByPath("path1/path2", "fake_title", "/", "secrets-safe/secrets") @@ -64,7 +67,15 @@ func TestSecretGetSecretByPath(t *testing.T) { } func TestSecretGetFileSecret(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + + var authenticate, _ = authentication.Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := SecretTestConfig{ name: "TestSecretGetFileSecret", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -73,7 +84,7 @@ func TestSecretGetFileSecret(t *testing.T) { } authenticate.ApiUrl = testConfig.server.URL + "/" - secretObj, _ := NewSecretObj(*authenticate, logLogger) + secretObj, _ := NewSecretObj(*authenticate, zapLogger) response, err := secretObj.SecretGetFileSecret("1", testConfig.server.URL) if response != "fake_password" { @@ -86,7 +97,15 @@ func TestSecretGetFileSecret(t *testing.T) { } func TestSecretFlow(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + var authenticate, _ = authentication.Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := SecretTestConfigStringResponse{ name: "TestSecretFlow", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -113,7 +132,7 @@ func TestSecretFlow(t *testing.T) { } authenticate.ApiUrl = testConfig.server.URL + "/" - secretObj, _ := NewSecretObj(*authenticate, logLogger) + secretObj, _ := NewSecretObj(*authenticate, zapLogger) secretList := strings.Split("oauthgrp_nocert/Test1,oauthgrp_nocert/client_id", ",") response, err := secretObj.GetSecretFlow(secretList, "/") @@ -128,7 +147,15 @@ func TestSecretFlow(t *testing.T) { } func TestSecretFlow_SecretNotFound(t *testing.T) { + logger, _ := zap.NewDevelopment() + defer logger.Sync() + + // create a zap logger wrapper + zapLogger := logging.NewZapLogger(logger) + + httpClientObj, _ := utils.GetHttpClient(5, false, "", "", zapLogger) + var authenticate, _ = authentication.Authenticate(*httpClientObj, "https://fake.api.com:443/BeyondTrust/api/public/v3/", "fakeone_a654+9sdf7+8we4f", "fakeone_aasd156465sfdef", zapLogger, 300) testConfig := SecretTestConfigStringResponse{ name: "TestSecretFlow", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -152,7 +179,7 @@ func TestSecretFlow_SecretNotFound(t *testing.T) { } authenticate.ApiUrl = testConfig.server.URL + "/" - secretObj, _ := NewSecretObj(*authenticate, logLogger) + secretObj, _ := NewSecretObj(*authenticate, zapLogger) secretList := strings.Split("oauthgrp_nocert/Test1,oauthgrp_nocert/client_id", ",") _, err := secretObj.GetSecretFlow(secretList, "/") diff --git a/api/utils/httpclient.go b/api/utils/httpclient.go index ac9eda8..dcdc915 100644 --- a/api/utils/httpclient.go +++ b/api/utils/httpclient.go @@ -1,13 +1,22 @@ package utils import ( + "bytes" "crypto/tls" + "fmt" + logging "go-client-library-passwordsafe/api/logging" + "io" "net/http" "net/http/cookiejar" "time" ) -func GetHttpClient(clientTimeOut int, verifyCa bool, certificate string, certificate_key string) (*http.Client, error) { +type HttpClientObj struct { + httpClient *http.Client + log logging.Logger +} + +func GetHttpClient(clientTimeOut int, verifyCa bool, certificate string, certificate_key string, logger logging.Logger) (*HttpClientObj, error) { var cert tls.Certificate if certificate != "" && certificate_key != "" { @@ -40,7 +49,69 @@ func GetHttpClient(clientTimeOut int, verifyCa bool, certificate string, certifi Timeout: time.Second * time.Duration(clientTimeOut), } + httpClientObj := &HttpClientObj{ + httpClient: client, + } + defer tr.CloseIdleConnections() - return client, nil + return httpClientObj, nil +} + +// CallSecretSafeAPI prepares http call +func (client *HttpClientObj) CallSecretSafeAPI(url string, httpMethod string, body bytes.Buffer, method string, accesToken string) (io.ReadCloser, error, error, int) { + response, technicalError, businessError, scode := client.HttpRequest(url, httpMethod, body, accesToken) + if technicalError != nil { + messageLog := fmt.Sprintf("Error in %v %v \n", method, technicalError) + client.log.Error(messageLog) + } + + if businessError != nil { + messageLog := fmt.Sprintf("Error in %v: %v \n", method, businessError) + client.log.Debug(messageLog) + } + return response, technicalError, businessError, scode +} + +// HttpRequest makes http request to he server +func (client *HttpClientObj) HttpRequest(url string, method string, body bytes.Buffer, accesToken string) (closer io.ReadCloser, technicalError error, businessError error, scode int) { + + req, err := http.NewRequest(method, url, &body) + if err != nil { + return nil, err, nil, 0 + } + req.Header = http.Header{ + "Content-Type": {"application/json"}, + } + + if accesToken != "" { + req.Header.Set("Authorization", "Bearer "+accesToken) + } + + resp, err := client.httpClient.Do(req) + if err != nil { + client.log.Error(err.Error()) + return nil, err, nil, 0 + } + + fmt.Println(resp) + if resp.StatusCode >= http.StatusInternalServerError || resp.StatusCode == http.StatusRequestTimeout { + err = fmt.Errorf("error %v: StatusCode: %v, %v, %v", method, scode, err, body) + client.log.Error(err.Error()) + return nil, err, nil, resp.StatusCode + } + + if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { + respBody := new(bytes.Buffer) + _, err = respBody.ReadFrom(resp.Body) + if err != nil { + client.log.Error(err.Error()) + return nil, err, nil, 0 + } + + err = fmt.Errorf("got a non 200 status code: %v - %v", resp.StatusCode, respBody) + return nil, nil, err, resp.StatusCode + } + + return resp.Body, nil, nil, resp.StatusCode } diff --git a/api/utils/validator.go b/api/utils/validator.go index 0850ddb..acc4033 100644 --- a/api/utils/validator.go +++ b/api/utils/validator.go @@ -5,15 +5,17 @@ package utils import ( "errors" logging "go-client-library-passwordsafe/api/logging" + "strings" + "unicode/utf8" validator "github.com/go-playground/validator/v10" ) type UserInputValidaton struct { - ClientId string `validate:"required,min=3,max=100"` - ClientSecret string `validate:"required,min=3,max=100"` + ClientId string `validate:"required,min=36,max=36"` + ClientSecret string `validate:"required,min=35,max=64"` ApiUrl string `validate:"required,http_url"` - ClientTimeOutinSeconds int `validate:"gte=3"` + ClientTimeOutinSeconds int `validate:"gte=1,lte=300"` Separator string `validate:"required,min=1,max=1"` VerifyCa bool `validate:"required"` } @@ -21,7 +23,7 @@ type UserInputValidaton struct { var validate *validator.Validate // ValidateInputs validate inputs -func ValidateInputs(clientId string, clientSecret string, apiUrl string, clientTimeOutinSeconds int, separator string, verifyCa bool, logger logging.Logger, certificate string, certificate_key string) error { +func ValidateInputs(clientId string, clientSecret string, apiUrl string, clientTimeOutinSeconds int, separator *string, verifyCa bool, logger logging.Logger, certificate string, certificate_key string) error { validate = validator.New(validator.WithRequiredStructEnabled()) @@ -30,10 +32,14 @@ func ValidateInputs(clientId string, clientSecret string, apiUrl string, clientT ClientSecret: clientSecret, ApiUrl: apiUrl, ClientTimeOutinSeconds: clientTimeOutinSeconds, - Separator: separator, + Separator: *separator, VerifyCa: verifyCa, } + if strings.TrimSpace(*separator) == "" { + *separator = "/" + } + err := validate.Struct(userInput) if err != nil { logger.Error(err.Error()) @@ -44,13 +50,36 @@ func ValidateInputs(clientId string, clientSecret string, apiUrl string, clientT message := "" if certificate != "" && certificate_key != "" { - if len(certificate) < 100 || len(certificate) > 4000 { + + certificateLengthInBits := utf8.RuneCountInString(certificate) * 8 + + if certificateLengthInBits > 32768 { message = "Invalid length for certificate" return errors.New(message) - } else if len(certificate_key) < 100 || len(certificate_key) > 4000 { + } + + certificateKeyLengthInBits := utf8.RuneCountInString(certificate_key) * 8 + + if certificateKeyLengthInBits > 32768 { message = "Invalid length for certificate key" return errors.New(message) } + + if !strings.HasPrefix(certificate, "-----BEGIN CERTIFICATE-----") || !strings.HasSuffix(certificate, "-----END CERTIFICATE-----") { + message = "Invalid certificate content" + return errors.New(message) + } + + if !strings.HasPrefix(certificate_key, "-----BEGIN PRIVATE KEY-----") || !strings.HasSuffix(certificate_key, "-----END PRIVATE KEY-----") { + message = "Invalid certificate key content" + return errors.New(message) + } + + } + + if !strings.Contains(apiUrl, "/BeyondTrust/api/public/v3/") { + message = "Invalid API URL" + return errors.New(message) } logger.Debug("Validation passed!") diff --git a/main.go b/main.go index 910a2ef..521f23f 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,7 @@ import ( "go-client-library-passwordsafe/api/authentication" logging "go-client-library-passwordsafe/api/logging" managed_accounts "go-client-library-passwordsafe/api/managed_account" - secrets "go-client-library-passwordsafe/api/secrets" + "go-client-library-passwordsafe/api/secrets" "go-client-library-passwordsafe/api/utils" "strings" @@ -39,7 +39,7 @@ func main() { maxElapsedTime := 15 // validate inputs - errors_in_inputs := utils.ValidateInputs(clientId, clientSecret, apiUrl, clientTimeOutinSeconds, separator, verifyCa, zapLogger, certificate, certificate_key) + errors_in_inputs := utils.ValidateInputs(clientId, clientSecret, apiUrl, clientTimeOutinSeconds, &separator, verifyCa, zapLogger, certificate, certificate_key) if errors_in_inputs != nil { @@ -48,10 +48,10 @@ func main() { } // creating a http client - httpClient, _ := utils.GetHttpClient(clientTimeOutinSeconds, verifyCa, certificate, certificate_key) + httpClientObj, _ := utils.GetHttpClient(clientTimeOutinSeconds, verifyCa, certificate, certificate_key, zapLogger) // instantiating authenticate obj, injecting httpClient object - authenticate, _ := authentication.Authenticate(httpClient, apiUrl, clientId, clientSecret, zapLogger, maxElapsedTime) + authenticate, _ := authentication.Authenticate(*httpClientObj, apiUrl, clientId, clientSecret, zapLogger, maxElapsedTime) // authenticating in PS API _, err := authenticate.GetPasswordSafeAuthentication()