Newer
Older
"encoding/json"
credentials2 "github.com/aws/aws-sdk-go/aws/credentials"
"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/logs"
"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/struct_utils"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"github.com/aws/aws-secretsmanager-caching-go/secretcache"
)
type DatabaseCredentials struct {
Username string `json:"username"`
Password string `json:"password"`
Engine string `json:"engine"`
Host string `json:"host"`
Port int `json:"port"`
InstanceIdentifier string `json:"dbInstanceIdentifier"`
ReadOnlyHost string `json:"aurora_read_only_host"`
type S3UploadCredentials struct {
AccessKeyID string `json:"accessKeyID"`
SecretKey string `json:"secretKey"`
var (
secretCache, _ = secretcache.New()
secretManagerRegion = "af-south-1"
)
var secretManagerSession *secretsmanager.SecretsManager
func GetDatabaseCredentials(secretID string, isDebug bool) (DatabaseCredentials, error) {
var credentials DatabaseCredentials
err := struct_utils.UnmarshalJSON([]byte(secret), &credentials)
if err != nil {
return DatabaseCredentials{}, err
}
return credentials, nil
}
func GetS3UploadCredentials(secretID string, isDebug bool) (*credentials2.Credentials, error) {
secret, _ := GetSecret(secretID, isDebug)
var credentials S3UploadCredentials
err := struct_utils.UnmarshalJSON([]byte(secret), &credentials)
if err != nil {
return nil, err
}
return credentials2.NewStaticCredentials(credentials.AccessKeyID, credentials.SecretKey, ""), nil
// getSecretManagerSession Instantiates a new Secrets Manager client session
func getSecretManagerSession(isDebug bool) (err error) {
if secretManagerSession != nil {
return nil
}
logs.Info("Creating a new Secrets Manager session")
awsSession, err := session.NewSession()
return err
}
// Get local config
if isDebug && os.Getenv("ENVIRONMENT") != "" {
awsAccessKey := os.Getenv("AWS_ACCESS_KEY_ID")
if len(awsAccessKey) > 0 {
logs.Info("Using access key %s", awsAccessKey)
}
awsSession, err = session.NewSessionWithOptions(session.Options{
Config: aws.Config{
Region: aws.String("af-south-1"),
CredentialsChainVerboseErrors: aws.Bool(true),
},
})
if err != nil {
return err
// Create a Secrets Manager client session
secretManagerSession = secretsmanager.New(awsSession, aws.NewConfig().WithRegion(secretManagerRegion))
return nil
}
// logError Logs any errors returned by the Secrets Manager client
func logError(err error) {
if aerr, ok := err.(awserr.Error); ok {
logs.Info(aerr.Code()+" %s", aerr.Error())
} else {
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
logs.Info(err.Error())
}
}
func GetSecret(secretID string, isDebug bool) (string, string) {
// Check if we have the secret in cache
cachedSecret, err := secretCache.GetSecretString(secretID)
if err != nil {
logs.Info("Failed to get secret key from cache")
}
if cachedSecret != "" {
return cachedSecret, ""
}
// Create a Secrets Manager client
err = getSecretManagerSession(isDebug)
if err != nil {
logs.Info("Could not create client: %+v", err)
return "", ""
}
// Create a secret
input := &secretsmanager.GetSecretValueInput{
SecretId: aws.String(string(secretID)),
VersionStage: aws.String("AWSCURRENT"), // VersionStage defaults to AWSCURRENT if unspecified
}
result, err := secretManagerSession.GetSecretValue(input)
logError(err)
return "", ""
}
// Decrypts secret using the associated KMS CMK.
// Depending on whether the secret is a string or binary, one of these fields will be populated.
var secretString, decodedBinarySecret string
if result.SecretString != nil {
secretString = *result.SecretString
} else {
decodedBinarySecretBytes := make([]byte, base64.StdEncoding.DecodedLen(len(result.SecretBinary)))
length, err := base64.StdEncoding.Decode(decodedBinarySecretBytes, result.SecretBinary)
if err != nil {
logs.Info("Base64 Decode Error: %+v", err)
return "", ""
}
decodedBinarySecret = string(decodedBinarySecretBytes[:length])
}
return secretString, decodedBinarySecret
}
// CreateSecret Creates a JSON marshaled "string secret" (can be expanded to cater for binary secrets should the need arise)
func CreateSecret(secretID string, secret any, isDebug bool) (awsSecretID string, err error) {
// Create a Secrets Manager client
err = getSecretManagerSession(isDebug)
if err != nil {
logs.Info("Could not create client: %+v", err)
return "", err
}
// Create the secret - marshaling "any" into a JSON string
secretStr, err := json.Marshal(secret)
if err != nil {
logs.Info("Could not marshal secret: %+v", err)
return "", err
}
input := &secretsmanager.CreateSecretInput{
Name: aws.String(secretID),
SecretString: aws.String(string(secretStr)),
}
result, err := secretManagerSession.CreateSecret(input)
if err != nil {
logError(err)
return "", err
}
return aws.StringValue(result.Name), nil
}
func DeleteSecret(secretID string, forceWithoutRecovery bool, isDebug bool) error {
// Create a Secrets Manager client
err := getSecretManagerSession(isDebug)
if err != nil {
logs.Info("Could not create client: %+v", err)
return err
}
// Delete the secret
input := &secretsmanager.DeleteSecretInput{
SecretId: aws.String(secretID),
ForceDeleteWithoutRecovery: aws.Bool(forceWithoutRecovery),
}
_, err = secretManagerSession.DeleteSecret(input)
if err != nil {
logError(err)
return err
}
return nil
}