Skip to content

Commit

Permalink
Add Secrets DAO secrets-manager implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
lindgrenj6 committed Nov 8, 2022
1 parent 30ea7d6 commit 7b24fd5
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 41 deletions.
5 changes: 1 addition & 4 deletions dao/application_authentication_dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (a *applicationAuthenticationDaoImpl) ApplicationAuthenticationsByAuthentic
query := a.getDb().Preload("Tenant")

switch config.Get().SecretStore {
case config.DatabaseStore:
case config.DatabaseStore, config.SecretsManagerStore:
authIds := make([]int64, len(authentications))

for _, value := range authentications {
Expand All @@ -95,9 +95,6 @@ func (a *applicationAuthenticationDaoImpl) ApplicationAuthenticationsByAuthentic

query.Where("authentication_uid IN ?", authUuids)

case config.SecretsManagerStore:
return nil, m.ErrBadSecretStore

default:
return nil, m.ErrBadSecretStore
}
Expand Down
33 changes: 29 additions & 4 deletions dao/authentication_dao_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,30 @@ import (
)

/*
Common DAO code for the 1..n authentication DAOs we support.
Currently, that list is:
Common DAO code for the 1..n authentication DAOs we support. Currently, that
list is:
1. DB Dao, using postgres and an encrypted column as a backend
2. Hashicorp vault, as the name implies storing authentication things in the vault
3. Amazon Secrets Manager, a WIP that uses Amazon Secrets Manager to store authentication things
2. Hashicorp vault, as the name implies storing authentication things in the
vault
3. Amazon Secrets Manager, a WIP that uses Amazon Secrets Manager to store
authentication things
Also including the functions for the SecretDAO's since they are in effect
specialized Authentication DAOs
*/

const secretResourceType = "Tenant"

// init sets the default DAO implementation so that other packages can request it easily.
func init() {
GetAuthenticationDao = getDefaultAuthenticationDao
GetSecretDao = getDefaultSecretDao
}

// GetAuthenticationDao is a function definition that can be replaced in runtime in case some other DAO provider is
// needed.
var GetAuthenticationDao func(daoParams *RequestParams) AuthenticationDao
var GetSecretDao func(daoParams *RequestParams) SecretDao

// getDefaultAuthenticationDao gets the default DAO implementation which will have the given tenant ID.
func getDefaultAuthenticationDao(daoParams *RequestParams) AuthenticationDao {
Expand All @@ -37,3 +46,19 @@ func getDefaultAuthenticationDao(daoParams *RequestParams) AuthenticationDao {
return &noSecretStoreAuthenticationDao{}
}
}

func getDefaultSecretDao(daoParams *RequestParams) SecretDao {
switch config.Get().SecretStore {
case config.DatabaseStore:
return &secretDaoDbImpl{RequestParams: daoParams}
case config.VaultStore:
return &noSecretStoreSecretsDao{}
case config.SecretsManagerStore:
return &secretDaoSecretsManagerImpl{
RequestParams: daoParams,
secretDaoDbImpl: secretDaoDbImpl{RequestParams: daoParams},
}
default:
return &noSecretStoreSecretsDao{}
}
}
4 changes: 3 additions & 1 deletion dao/authentication_secrets_manager_dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package dao

import (
"fmt"
"strings"

"github.com/RedHatInsights/sources-api-go/config"
"github.com/RedHatInsights/sources-api-go/dao/amazon"
m "github.com/RedHatInsights/sources-api-go/model"
)
Expand Down Expand Up @@ -72,7 +74,7 @@ func (a *authenticationSecretsManagerDaoImpl) BulkCreate(auth *m.Authentication)
func (a *authenticationSecretsManagerDaoImpl) Update(auth *m.Authentication) error {
// only reach out to amazon if there is a password present, otherwise pass
// straight through to the db dao.
if auth.Password != nil {
if auth.Password != nil && !strings.HasPrefix(*auth.Password, config.Get().SecretsManagerPrefix) {
// fetch the ARN of the current password (since we overwrote it in memory)
arns := make([]*string, 1)
err := a.getDbWithModel().
Expand Down
30 changes: 0 additions & 30 deletions dao/secret_dao.go → dao/secret_db_dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,15 @@ package dao
import (
"fmt"

"github.com/RedHatInsights/sources-api-go/config"
m "github.com/RedHatInsights/sources-api-go/model"
"github.com/RedHatInsights/sources-api-go/util"
"gorm.io/gorm"
)

const secretResourceType = "Tenant"

var GetSecretDao func(daoParams *RequestParams) SecretDao

type secretDaoDbImpl struct {
*RequestParams
}

func getDefaultSecretDao(daoParams *RequestParams) SecretDao {
switch config.Get().SecretStore {
case config.DatabaseStore:
return &secretDaoDbImpl{RequestParams: daoParams}
case config.VaultStore:
return &noSecretStoreSecretsDao{}
case config.SecretsManagerStore:
return &noSecretStoreSecretsDao{}
default:
return &noSecretStoreSecretsDao{}
}
}

func init() {
GetSecretDao = getDefaultSecretDao
}

func (secret *secretDaoDbImpl) getDb() *gorm.DB {
if secret.TenantID == nil {
panic("nil tenant found in sourceDaoImpl DAO")
Expand Down Expand Up @@ -75,14 +53,6 @@ func (secret *secretDaoDbImpl) Create(authentication *m.Authentication) error {
authentication.TenantID = *secret.TenantID // the TenantID gets injected in the middleware
authentication.ResourceType = secretResourceType
authentication.ResourceID = *secret.TenantID
if authentication.Password != nil {
encryptedValue, err := util.Encrypt(*authentication.Password)
if err != nil {
return err
}

authentication.Password = &encryptedValue
}

return DB.
Debug().
Expand Down
File renamed without changes.
118 changes: 118 additions & 0 deletions dao/secret_secrets_manager_dao.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package dao

import (
"fmt"
"strings"

"github.com/RedHatInsights/sources-api-go/config"
"github.com/RedHatInsights/sources-api-go/dao/amazon"
m "github.com/RedHatInsights/sources-api-go/model"
"github.com/RedHatInsights/sources-api-go/util"
)

type secretDaoSecretsManagerImpl struct {
*RequestParams

secretDaoDbImpl
}

func (s *secretDaoSecretsManagerImpl) Create(auth *m.Authentication) error {
// only reach out to amazon if there is a password present, otherwise pass
// straight through to the db dao.
if auth.Password != nil {
sm, err := amazon.NewSecretsManagerClient()
if err != nil {
return err
}

auth.TenantID = *s.TenantID
arn, err := sm.CreateSecret(auth, *auth.Password)
if err != nil {
return err
}

err = auth.SetPassword(arn)
if err != nil {
return err
}
}

return s.secretDaoDbImpl.Create(auth)
}

func (s *secretDaoSecretsManagerImpl) Delete(id *int64) error {
auth, err := s.secretDaoDbImpl.GetById(id)
if err != nil {
return util.NewErrNotFound("secret")
}

if auth.Password != nil {
sm, err := amazon.NewSecretsManagerClient()
if err != nil {
return err
}

err = sm.DeleteSecret(*auth.Password)
if err != nil {
return err
}
}

return s.secretDaoDbImpl.Delete(id)
}

func (s *secretDaoSecretsManagerImpl) GetById(id *int64) (*m.Authentication, error) {
auth, err := s.secretDaoDbImpl.GetById(id)
if err != nil {
return nil, err
}

if auth.Password != nil {
sm, err := amazon.NewSecretsManagerClient()
if err != nil {
return nil, err
}

pass, err := sm.GetSecret(*auth.Password)
if err != nil {
return nil, err
}

// set the password in-memory to the secrets-manager password
auth.Password = pass
}

return auth, nil
}

func (s *secretDaoSecretsManagerImpl) Update(auth *m.Authentication) error {
if auth.Password != nil && !strings.HasPrefix(*auth.Password, config.Get().SecretsManagerPrefix) {
// fetch the ARN of the current password (since we overwrote it in memory)
arns := make([]*string, 1)
err := s.getDbWithModel().
Where("id = ?", auth.GetID()).
Limit(1).
Pluck("password_hash", &arns).Error
if err != nil {
return err
}
if *arns[0] == "" {
return fmt.Errorf("failed to fetch ARN for authentication %v", auth.GetID())
}

sm, err := amazon.NewSecretsManagerClient()
if err != nil {
return err
}

err = sm.UpdateSecret(*arns[0], *auth.Password)
if err != nil {
return err
}

// set the password back to the ARN so that we don't overwrite it.
auth.Password = arns[0]
}

return s.secretDaoDbImpl.Update(auth)
}
3 changes: 2 additions & 1 deletion dao/test_factories.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,14 +285,15 @@ func TestSuiteForSourceWithOwnership(performTest func(suiteData *SourceOwnership

func CreateSecretByName(name string, tenantID *int64, userID *int64) (*m.Authentication, error) {
secretDao := GetSecretDao(&RequestParams{TenantID: tenantID})
pass, _ := util.Encrypt("password")

secret := &m.Authentication{
Name: util.StringRef(name),
AuthType: "X",
Username: util.StringRef("Y"),
ResourceType: secretResourceType,
ResourceID: *tenantID,
Password: util.StringRef("password"),
Password: util.StringRef(pass),
}

if userID != nil {
Expand Down
6 changes: 5 additions & 1 deletion secret_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,18 @@ func SecretCreate(c echo.Context) error {
Name: createRequest.Name,
AuthType: createRequest.AuthType,
Username: createRequest.Username,
Password: createRequest.Password,
}

err = secret.SetExtra(createRequest.Extra)
if err != nil {
return err
}

err = secret.SetPassword(createRequest.Password)
if err != nil {
return util.NewErrBadRequest(err)
}

if createRequest.UserScoped && requestParams.UserID != nil {
secret.UserID = requestParams.UserID
}
Expand Down

0 comments on commit 7b24fd5

Please sign in to comment.