-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8ad9d5d
commit 1b3281a
Showing
7 changed files
with
502 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
// Copyright 2024 BeyondTrust. All rights reserved. | ||
// Package client implements functions to call Beyondtrust Secret Safe API. | ||
package authentication | ||
|
||
import ( | ||
"bytes" | ||
"crypto/tls" | ||
"encoding/json" | ||
"fmt" | ||
"go-client-library-passwordsafe/api/entities" | ||
"go-client-library-passwordsafe/api/utils" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"net/http" | ||
"net/http/cookiejar" | ||
"net/url" | ||
"time" | ||
|
||
"github.com/cenkalti/backoff/v4" | ||
) | ||
|
||
type AuthenticationObj struct { | ||
ApiUrl string | ||
clientId string | ||
clientSecret string | ||
httpClient *http.Client | ||
ExponentialBackOff *backoff.ExponentialBackOff | ||
signApinResponse entities.SignApinResponse | ||
autenticationLogger log.Logger | ||
} | ||
|
||
// Authenticate in PS API | ||
func Authenticate(endpointUrl string, clientId string, clientSecret string, certificate string, certificate_key string, verifyCa bool, logger log.Logger, clientTimeOut int) (*AuthenticationObj, error) { | ||
var cert tls.Certificate | ||
|
||
if certificate != "" && certificate_key != "" { | ||
certi, err := tls.X509KeyPair([]byte(certificate), []byte(certificate_key)) | ||
|
||
if err != nil { | ||
utils.Logging("ERROR", err.Error(), logger) | ||
return nil, err | ||
} | ||
|
||
cert = certi | ||
} | ||
|
||
// TSL Config | ||
var tr = &http.Transport{ | ||
TLSClientConfig: &tls.Config{ | ||
Renegotiation: tls.RenegotiateOnceAsClient, | ||
InsecureSkipVerify: !verifyCa, | ||
Certificates: []tls.Certificate{cert}, | ||
MinVersion: tls.VersionTLS12, | ||
MaxVersion: tls.VersionTLS12, | ||
}, | ||
} | ||
|
||
backoffDefinition := backoff.NewExponentialBackOff() | ||
backoffDefinition.InitialInterval = 1 * time.Second | ||
backoffDefinition.MaxElapsedTime = 15 * time.Second | ||
backoffDefinition.RandomizationFactor = 0.5 | ||
|
||
var jar, _ = cookiejar.New(nil) | ||
|
||
// Client | ||
var client = &http.Client{ | ||
Transport: tr, | ||
Jar: jar, | ||
Timeout: time.Second * time.Duration(clientTimeOut), | ||
} | ||
|
||
authenticationObj := &AuthenticationObj{ | ||
ApiUrl: endpointUrl, | ||
httpClient: client, | ||
clientId: clientId, | ||
clientSecret: clientSecret, | ||
ExponentialBackOff: backoffDefinition, | ||
autenticationLogger: logger, | ||
} | ||
authenticationObj.signApinResponse, _ = authenticationObj.GetPasswordSafeAuthentication() | ||
|
||
return authenticationObj, nil | ||
} | ||
|
||
// 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) | ||
if err != nil { | ||
return entities.SignApinResponse{}, err | ||
} | ||
signApinResponse, err := c.SignAppin(fmt.Sprintf("%v%v", c.ApiUrl, "Auth/SignAppIn"), accessToken) | ||
if err != nil { | ||
return entities.SignApinResponse{}, err | ||
} | ||
return signApinResponse, nil | ||
} | ||
|
||
// GetToken get token from PS API | ||
func (c *AuthenticationObj) GetToken(endpointUrl string, clientId string, clientSecret string) (string, error) { | ||
|
||
params := url.Values{} | ||
params.Add("client_id", clientId) | ||
params.Add("client_secret", clientSecret) | ||
params.Add("grant_type", "client_credentials") | ||
|
||
var body io.ReadCloser | ||
var technicalError error | ||
var businessError error | ||
|
||
var buffer bytes.Buffer | ||
buffer.WriteString(params.Encode()) | ||
|
||
technicalError = backoff.Retry(func() error { | ||
body, technicalError, businessError, _ = c.CallSecretSafeAPI(endpointUrl, "POST", buffer, "CredentialByRequestId", "") | ||
return technicalError | ||
}, c.ExponentialBackOff) | ||
|
||
if technicalError != nil { | ||
return "", technicalError | ||
} | ||
|
||
if businessError != nil { | ||
return "", businessError | ||
} | ||
|
||
bodyBytes, err := ioutil.ReadAll(body) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
responseString := string(bodyBytes) | ||
|
||
var data entities.GetTokenResponse | ||
|
||
err = json.Unmarshal([]byte(responseString), &data) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return data.AccessToken, nil | ||
|
||
} | ||
|
||
// SignAppin Signs app in PS API | ||
func (c *AuthenticationObj) SignAppin(endpointUrl string, accessToken string) (entities.SignApinResponse, error) { | ||
|
||
var userObject entities.SignApinResponse | ||
var body io.ReadCloser | ||
var technicalError error | ||
var businessError error | ||
var scode int | ||
|
||
err := backoff.Retry(func() error { | ||
body, technicalError, businessError, scode = c.CallSecretSafeAPI(endpointUrl, "POST", bytes.Buffer{}, "SignAppin", accessToken) | ||
if scode == 0 { | ||
return nil | ||
} | ||
return technicalError | ||
}, c.ExponentialBackOff) | ||
|
||
if err != nil { | ||
return entities.SignApinResponse{}, err | ||
} | ||
|
||
if scode == 0 { | ||
return entities.SignApinResponse{}, technicalError | ||
} | ||
|
||
if businessError != nil { | ||
return entities.SignApinResponse{}, businessError | ||
} | ||
|
||
defer body.Close() | ||
bodyBytes, err := ioutil.ReadAll(body) | ||
if err != nil { | ||
return entities.SignApinResponse{}, err | ||
} | ||
|
||
json.Unmarshal(bodyBytes, &userObject) | ||
return userObject, 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) | ||
utils.Logging("ERROR", messageLog, c.autenticationLogger) | ||
} | ||
|
||
if businessError != nil { | ||
messageLog := fmt.Sprintf("Error in %v: %v \n", method, businessError) | ||
utils.Logging("ERROR", messageLog, c.autenticationLogger) | ||
} | ||
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 { | ||
utils.Logging("ERROR", err.Error(), c.autenticationLogger) | ||
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) | ||
utils.Logging("ERROR", err.Error(), c.autenticationLogger) | ||
return nil, err, nil, resp.StatusCode | ||
} | ||
|
||
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { | ||
respBody := new(bytes.Buffer) | ||
respBody.ReadFrom(resp.Body) | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Copyright 2024 BeyondTrust. All rights reserved. | ||
// Package entities implements DTO's used by Beyondtrust Secret Safe API. | ||
package entities | ||
|
||
type SignApinResponse struct { | ||
UserId int `json:"UserId"` | ||
EmailAddress string `json:"EmailAddress"` | ||
UserName string `json:"UserName"` | ||
Name string `json:"Name"` | ||
} | ||
|
||
type ManagedAccount struct { | ||
SystemId int | ||
AccountId int | ||
} | ||
|
||
type Secret struct { | ||
Id string | ||
Title string | ||
Password string | ||
SecretType string | ||
} | ||
|
||
type GetTokenResponse struct { | ||
AccessToken string `json:"access_token"` | ||
ExpiresIn int `json:"expires_in"` | ||
TokenType string `json:"token_type"` | ||
Scope string `json:"scope"` | ||
} |
Oops, something went wrong.