Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
gitahernandez committed Feb 8, 2024
1 parent 8ad9d5d commit 1b3281a
Show file tree
Hide file tree
Showing 7 changed files with 502 additions and 0 deletions.
236 changes: 236 additions & 0 deletions api/authentication/authetication.go
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
}
29 changes: 29 additions & 0 deletions api/entities/entities.go
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"`
}
Loading

0 comments on commit 1b3281a

Please sign in to comment.