2022-06-08 13:51:56 -07:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2021-06-22 22:26:26 -04:00
package target
import (
"context"
2021-09-30 08:47:34 -04:00
"database/sql"
2021-06-22 22:26:26 -04:00
"fmt"
"strings"
2023-11-15 19:55:46 -05:00
"github.com/hashicorp/boundary/globals"
2021-10-26 09:57:22 -04:00
"github.com/hashicorp/boundary/internal/credential"
2021-06-22 22:26:26 -04:00
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
2022-06-08 13:51:56 -07:00
"github.com/hashicorp/go-secure-stdlib/strutil"
2021-06-22 22:26:26 -04:00
)
2022-06-08 13:51:56 -07:00
// AddTargetCredentialSources adds the credential source ids by purpose to the targetId in the repository.
// The target and the list of credential sources attached to the target, after ids are added,
2021-06-22 22:26:26 -04:00
// will be returned on success.
// The targetVersion must match the current version of the targetId in the repository.
2023-03-30 15:19:30 -04:00
func ( r * Repository ) AddTargetCredentialSources ( ctx context . Context , targetId string , targetVersion uint32 , idsByPurpose CredentialSources , _ ... Option ) ( Target , error ) {
2021-07-27 12:48:36 -04:00
const op = "target.(Repository).AddTargetCredentialSources"
2021-06-22 22:26:26 -04:00
if targetId == "" {
2023-03-30 15:19:30 -04:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "missing target id" )
2021-06-22 22:26:26 -04:00
}
if targetVersion == 0 {
2023-03-30 15:19:30 -04:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "missing version" )
2021-06-22 22:26:26 -04:00
}
t := allocTargetView ()
t . PublicId = targetId
if err := r . reader . LookupByPublicId ( ctx , & t ); err != nil {
2023-03-30 15:19:30 -04:00
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( fmt . Sprintf ( "failed for %s" , targetId )))
2021-06-22 22:26:26 -04:00
}
var metadata oplog . Metadata
2021-09-29 13:13:49 -04:00
alloc , ok := subtypeRegistry . allocFunc ( t . Subtype ())
if ! ok {
2023-03-30 15:19:30 -04:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , fmt . Sprintf ( "%s is an unsupported target type %s" , t . PublicId , t . Type ))
2021-06-22 22:26:26 -04:00
}
2022-06-08 13:51:56 -07:00
addCredLibs , addStaticCreds , err := r . createSources ( ctx , targetId , t . Subtype (), idsByPurpose )
if err != nil {
2023-03-30 15:19:30 -04:00
return nil , errors . Wrap ( ctx , err , op )
2021-10-12 10:53:33 -04:00
}
2021-09-29 13:13:49 -04:00
target := alloc ()
2022-06-08 13:51:56 -07:00
if err := target . SetPublicId ( ctx , t . PublicId ); err != nil {
2023-03-30 15:19:30 -04:00
return nil , errors . Wrap ( ctx , err , op )
2022-06-08 13:51:56 -07:00
}
2021-09-29 13:13:49 -04:00
target . SetVersion ( targetVersion + 1 )
metadata = target . Oplog ( oplog . OpType_OP_TYPE_UPDATE )
metadata [ "op-type" ] = append ( metadata [ "op-type" ], oplog . OpType_OP_TYPE_CREATE . String ())
2022-07-20 16:24:46 -07:00
oplogWrapper , err := r . kms . GetWrapper ( ctx , t . GetProjectId (), kms . KeyPurposeOplog )
2021-06-22 22:26:26 -04:00
if err != nil {
2023-03-30 15:19:30 -04:00
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to get oplog wrapper" ))
2021-06-22 22:26:26 -04:00
}
2022-12-06 12:28:55 -08:00
var updatedTarget Target
2021-06-22 22:26:26 -04:00
_ , err = r . writer . DoTx (
ctx ,
db . StdRetryCnt ,
db . ExpBackoff {},
func ( reader db . Reader , w db . Writer ) error {
2022-06-08 13:51:56 -07:00
numOplogMsgs := 1 + len ( addCredLibs ) + len ( addStaticCreds )
msgs := make ([] * oplog . Message , 0 , numOplogMsgs )
2022-01-05 14:38:38 -05:00
targetTicket , err := w . GetTicket ( ctx , target )
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to get ticket" ))
2021-06-22 22:26:26 -04:00
}
2022-12-06 12:28:55 -08:00
updatedTarget = target . Clone ()
2021-06-22 22:26:26 -04:00
var targetOplogMsg oplog . Message
rowsUpdated , err := w . Update ( ctx , updatedTarget , [] string { "Version" }, nil , db . NewOplogMsg ( & targetOplogMsg ), db . WithVersion ( & targetVersion ))
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to update target version" ))
2021-06-22 22:26:26 -04:00
}
if rowsUpdated == 0 {
2021-07-29 18:52:56 -06:00
return errors . New ( ctx , errors . VersionMismatch , op , "invalid target version" )
2021-06-22 22:26:26 -04:00
}
if rowsUpdated > 1 {
2021-07-29 18:52:56 -06:00
return errors . New ( ctx , errors . MultipleRecords , op , fmt . Sprintf ( "updated target and %d rows updated" , rowsUpdated ))
2021-06-22 22:26:26 -04:00
}
msgs = append ( msgs , & targetOplogMsg )
2022-06-08 13:51:56 -07:00
if len ( addCredLibs ) > 0 {
2024-08-01 11:18:53 -04:00
i := make ([] * CredentialLibrary , 0 , len ( addCredLibs ))
2022-06-08 13:51:56 -07:00
for _ , cl := range addCredLibs {
i = append ( i , cl )
}
credLibsOplogMsgs := make ([] * oplog . Message , 0 , len ( addCredLibs ))
if err := w . CreateItems ( ctx , i , db . NewOplogMsgs ( & credLibsOplogMsgs )); err != nil {
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create target credential library" ))
}
msgs = append ( msgs , credLibsOplogMsgs ... )
}
if len ( addStaticCreds ) > 0 {
2024-08-01 11:18:53 -04:00
i := make ([] * StaticCredential , 0 , len ( addStaticCreds ))
2022-06-08 13:51:56 -07:00
for _ , c := range addStaticCreds {
i = append ( i , c )
}
credStaticOplogMsgs := make ([] * oplog . Message , 0 , len ( addStaticCreds ))
if err := w . CreateItems ( ctx , i , db . NewOplogMsgs ( & credStaticOplogMsgs )); err != nil {
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create target static credential" ))
}
msgs = append ( msgs , credStaticOplogMsgs ... )
2021-06-22 22:26:26 -04:00
}
if err := w . WriteOplogEntryWith ( ctx , oplogWrapper , targetTicket , metadata , msgs ); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to write oplog" ))
2021-06-22 22:26:26 -04:00
}
2023-03-30 15:19:30 -04:00
hostSources , err := fetchHostSources ( ctx , reader , targetId )
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-30 08:48:03 -04:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to retrieve host sources after adding" ))
2021-06-22 22:26:26 -04:00
}
2023-03-30 15:19:30 -04:00
updatedTarget . SetHostSources ( hostSources )
credSources , err := fetchCredentialSources ( ctx , reader , targetId )
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to retrieve credential sources after adding" ))
2021-06-22 22:26:26 -04:00
}
2023-03-30 15:19:30 -04:00
updatedTarget . SetCredentialSources ( credSources )
2022-12-06 12:28:55 -08:00
address , err := fetchAddress ( ctx , reader , targetId )
if err != nil && ! errors . IsNotFoundError ( err ) {
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to retrieve target address after adding" ))
}
if address != nil {
updatedTarget . SetAddress ( address . GetAddress ())
}
2021-06-22 22:26:26 -04:00
return nil
},
)
if err != nil {
2023-03-30 15:19:30 -04:00
return nil , errors . Wrap ( ctx , err , op )
2021-06-22 22:26:26 -04:00
}
2023-03-30 15:19:30 -04:00
return updatedTarget , nil
2021-06-22 22:26:26 -04:00
}
2021-07-27 12:48:36 -04:00
// DeleteTargetCredentialSources deletes credential sources from a target in the repository.
2021-06-22 22:26:26 -04:00
// The target's current db version must match the targetVersion or an error will be returned.
2022-06-08 13:51:56 -07:00
func ( r * Repository ) DeleteTargetCredentialSources ( ctx context . Context , targetId string , targetVersion uint32 , idsByPurpose CredentialSources , _ ... Option ) ( int , error ) {
2021-07-27 12:48:36 -04:00
const op = "target.(Repository).DeleteTargetCredentialSources"
2021-06-22 22:26:26 -04:00
if targetId == "" {
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , "missing target id" )
2021-06-22 22:26:26 -04:00
}
if targetVersion == 0 {
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , "missing version" )
2021-06-22 22:26:26 -04:00
}
t := allocTargetView ()
t . PublicId = targetId
if err := r . reader . LookupByPublicId ( ctx , & t ); err != nil {
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . Wrap ( ctx , err , op , errors . WithMsg ( fmt . Sprintf ( "failed for %s" , targetId )))
2021-06-22 22:26:26 -04:00
}
var metadata oplog . Metadata
2021-09-29 13:13:49 -04:00
2022-06-08 13:51:56 -07:00
deleteCredLibs , deleteStaticCred , err := r . createSources ( ctx , targetId , t . Subtype (), idsByPurpose )
if err != nil {
return db . NoRowsAffected , errors . Wrap ( ctx , err , op )
}
2021-09-29 13:13:49 -04:00
alloc , ok := subtypeRegistry . allocFunc ( t . Subtype ())
if ! ok {
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , fmt . Sprintf ( "%s is an unsupported target type %s" , t . PublicId , t . Type ))
2021-06-22 22:26:26 -04:00
}
2021-09-29 13:13:49 -04:00
target := alloc ()
2022-06-08 13:51:56 -07:00
if err := target . SetPublicId ( ctx , t . PublicId ); err != nil {
return db . NoRowsAffected , errors . Wrap ( ctx , err , op )
}
2021-09-29 13:13:49 -04:00
target . SetVersion ( targetVersion + 1 )
metadata = target . Oplog ( oplog . OpType_OP_TYPE_UPDATE )
metadata [ "op-type" ] = append ( metadata [ "op-type" ], oplog . OpType_OP_TYPE_DELETE . String ())
2021-06-22 22:26:26 -04:00
2022-07-20 16:24:46 -07:00
oplogWrapper , err := r . kms . GetWrapper ( ctx , t . GetProjectId (), kms . KeyPurposeOplog )
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to get oplog wrapper" ))
2021-06-22 22:26:26 -04:00
}
var rowsDeleted int
_ , err = r . writer . DoTx (
ctx ,
db . StdRetryCnt ,
db . ExpBackoff {},
func ( reader db . Reader , w db . Writer ) error {
msgs := make ([] * oplog . Message , 0 , 2 )
2022-01-05 14:38:38 -05:00
targetTicket , err := w . GetTicket ( ctx , target )
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to get ticket" ))
2021-06-22 22:26:26 -04:00
}
updatedTarget := target .( Cloneable ). Clone ()
var targetOplogMsg oplog . Message
rowsUpdated , err := w . Update ( ctx , updatedTarget , [] string { "Version" }, nil , db . NewOplogMsg ( & targetOplogMsg ), db . WithVersion ( & targetVersion ))
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to update target version" ))
2021-06-22 22:26:26 -04:00
}
if rowsUpdated == 0 {
2021-07-29 18:52:56 -06:00
return errors . New ( ctx , errors . VersionMismatch , op , "invalid target version" )
2021-06-22 22:26:26 -04:00
}
if rowsUpdated > 1 {
2021-07-29 18:52:56 -06:00
return errors . New ( ctx , errors . MultipleRecords , op , fmt . Sprintf ( "updated target and %d rows updated" , rowsUpdated ))
2021-06-22 22:26:26 -04:00
}
msgs = append ( msgs , & targetOplogMsg )
2022-06-08 13:51:56 -07:00
if len ( deleteCredLibs ) > 0 {
2024-08-01 11:18:53 -04:00
i := make ([] * CredentialLibrary , 0 , len ( deleteCredLibs ))
2022-06-08 13:51:56 -07:00
for _ , cl := range deleteCredLibs {
i = append ( i , cl )
}
credLibsOplogMsgs := make ([] * oplog . Message , 0 , len ( deleteCredLibs ))
cnt , err := w . DeleteItems ( ctx , i , db . NewOplogMsgs ( & credLibsOplogMsgs ))
if err != nil {
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to delete target credential libraries" ))
}
if cnt != len ( deleteCredLibs ) {
return errors . New ( ctx , errors . MultipleRecords , op , fmt . Sprintf ( "credential libraries deleted %d did not match request for %d" , rowsDeleted , len ( deleteCredLibs )))
}
rowsDeleted += cnt
msgs = append ( msgs , credLibsOplogMsgs ... )
2021-06-22 22:26:26 -04:00
}
2022-06-08 13:51:56 -07:00
if len ( deleteStaticCred ) > 0 {
2024-08-01 11:18:53 -04:00
i := make ([] * StaticCredential , 0 , len ( deleteStaticCred ))
2022-06-08 13:51:56 -07:00
for _ , cl := range deleteStaticCred {
i = append ( i , cl )
}
staticCredOplogMsgs := make ([] * oplog . Message , 0 , len ( deleteStaticCred ))
cnt , err := w . DeleteItems ( ctx , i , db . NewOplogMsgs ( & staticCredOplogMsgs ))
if err != nil {
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to delete target static credential" ))
}
if cnt != len ( deleteStaticCred ) {
return errors . New ( ctx , errors . MultipleRecords , op , fmt . Sprintf ( "static credential deleted %d did not match request for %d" , rowsDeleted , len ( deleteCredLibs )))
}
rowsDeleted += cnt
msgs = append ( msgs , staticCredOplogMsgs ... )
2021-06-22 22:26:26 -04:00
}
if err := w . WriteOplogEntryWith ( ctx , oplogWrapper , targetTicket , metadata , msgs ); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to write oplog" ))
2021-06-22 22:26:26 -04:00
}
return nil
},
)
if err != nil {
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . Wrap ( ctx , err , op )
2021-06-22 22:26:26 -04:00
}
return rowsDeleted , nil
}
2021-07-27 12:48:36 -04:00
// SetTargetCredentialSources will set the target's credential sources. Set will add
// and/or delete credential sources as need to reconcile the existing credential sources
// with the request. If clIds is empty, all the credential sources will be cleared from the target.
2022-06-08 13:51:56 -07:00
func ( r * Repository ) SetTargetCredentialSources ( ctx context . Context , targetId string , targetVersion uint32 , ids CredentialSources , _ ... Option ) ([] HostSource , [] CredentialSource , int , error ) {
2021-07-27 12:48:36 -04:00
const op = "target.(Repository).SetTargetCredentialSources"
2021-06-22 22:26:26 -04:00
if targetId == "" {
2021-07-29 18:52:56 -06:00
return nil , nil , db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , "missing target id" )
2021-06-22 22:26:26 -04:00
}
if targetVersion == 0 {
2021-07-29 18:52:56 -06:00
return nil , nil , db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , "missing version" )
2021-06-22 22:26:26 -04:00
}
2022-06-08 13:51:56 -07:00
var (
addCredLibs [] * CredentialLibrary
delCredLibs [] * CredentialLibrary
addStaticCred [] * StaticCredential
delStaticCred [] * StaticCredential
)
2021-10-26 09:57:22 -04:00
2022-06-08 13:51:56 -07:00
byPurpose := map [ credential . Purpose ][] string {
2022-07-19 15:16:24 -07:00
credential . BrokeredPurpose : ids . BrokeredCredentialIds ,
credential . InjectedApplicationPurpose : ids . InjectedApplicationCredentialIds ,
2022-06-08 13:51:56 -07:00
}
for p , ids := range byPurpose {
addL , delL , addS , delS , err := r . changes ( ctx , targetId , ids , p )
2021-10-26 09:57:22 -04:00
if err != nil {
return nil , nil , db . NoRowsAffected , errors . Wrap ( ctx , err , op )
}
2022-06-08 13:51:56 -07:00
addCredLibs = append ( addCredLibs , addL ... )
delCredLibs = append ( delCredLibs , delL ... )
addStaticCred = append ( addStaticCred , addS ... )
delStaticCred = append ( delStaticCred , delS ... )
2021-06-22 22:26:26 -04:00
}
2021-10-26 09:57:22 -04:00
2022-06-08 13:51:56 -07:00
if len ( addCredLibs ) + len ( delCredLibs ) + len ( addStaticCred ) + len ( delStaticCred ) == 0 {
2021-06-22 22:26:26 -04:00
// Nothing needs to be changed, return early
2021-07-30 08:48:03 -04:00
hostSets , err := fetchHostSources ( ctx , r . reader , targetId )
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , db . NoRowsAffected , errors . Wrap ( ctx , err , op )
2021-06-22 22:26:26 -04:00
}
2021-07-27 12:48:36 -04:00
credSources , err := fetchCredentialSources ( ctx , r . reader , targetId )
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , db . NoRowsAffected , errors . Wrap ( ctx , err , op )
2021-06-22 22:26:26 -04:00
}
2021-07-27 12:48:36 -04:00
return hostSets , credSources , db . NoRowsAffected , nil
2021-06-22 22:26:26 -04:00
}
t := allocTargetView ()
t . PublicId = targetId
if err := r . reader . LookupByPublicId ( ctx , & t ); err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , db . NoRowsAffected , errors . Wrap ( ctx , err , op , errors . WithMsg ( fmt . Sprintf ( "failed for %s" , targetId )))
2021-06-22 22:26:26 -04:00
}
var metadata oplog . Metadata
2021-09-29 13:13:49 -04:00
alloc , ok := subtypeRegistry . allocFunc ( t . Subtype ())
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , nil , db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , fmt . Sprintf ( "%s is an unsupported target type %s" , t . PublicId , t . Type ))
2021-06-22 22:26:26 -04:00
}
2021-10-12 10:53:33 -04:00
2022-06-08 13:51:56 -07:00
vetCredentialSources , ok := subtypeRegistry . vetCredentialSourcesFunc ( t . Subtype ())
2021-10-12 10:53:33 -04:00
if ! ok {
return nil , nil , db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , fmt . Sprintf ( "%s is an unsupported target type %s" , t . PublicId , t . Type ))
}
2022-06-08 13:51:56 -07:00
// Validate add sources on target
if err := vetCredentialSources ( ctx , addCredLibs , addStaticCred ); err != nil {
2021-10-12 10:53:33 -04:00
return nil , nil , db . NoRowsAffected , err
}
2021-09-29 13:13:49 -04:00
target := alloc ()
2022-06-08 13:51:56 -07:00
if err := target . SetPublicId ( ctx , t . PublicId ); err != nil {
return nil , nil , db . NoRowsAffected , errors . Wrap ( ctx , err , op )
}
2021-09-29 13:13:49 -04:00
target . SetVersion ( targetVersion + 1 )
metadata = target . Oplog ( oplog . OpType_OP_TYPE_UPDATE )
2022-07-20 16:24:46 -07:00
oplogWrapper , err := r . kms . GetWrapper ( ctx , t . GetProjectId (), kms . KeyPurposeOplog )
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , db . NoRowsAffected , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to get oplog wrapper" ))
2021-06-22 22:26:26 -04:00
}
var rowsAffected int
_ , err = r . writer . DoTx (
ctx ,
db . StdRetryCnt ,
db . ExpBackoff {},
func ( reader db . Reader , w db . Writer ) error {
msgs := make ([] * oplog . Message , 0 , 2 )
2022-01-05 14:38:38 -05:00
targetTicket , err := w . GetTicket ( ctx , target )
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to get ticket" ))
2021-06-22 22:26:26 -04:00
}
updatedTarget := target .( Cloneable ). Clone ()
var targetOplogMsg oplog . Message
rowsUpdated , err := w . Update ( ctx , updatedTarget , [] string { "Version" }, nil , db . NewOplogMsg ( & targetOplogMsg ), db . WithVersion ( & targetVersion ))
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to update target version" ))
2021-06-22 22:26:26 -04:00
}
if rowsUpdated == 0 {
2021-07-29 18:52:56 -06:00
return errors . New ( ctx , errors . VersionMismatch , op , "invalid target version" )
2021-06-22 22:26:26 -04:00
}
if rowsUpdated > 1 {
2021-07-29 18:52:56 -06:00
return errors . New ( ctx , errors . MultipleRecords , op , fmt . Sprintf ( "updated target and %d rows updated" , rowsUpdated ))
2021-06-22 22:26:26 -04:00
}
msgs = append ( msgs , & targetOplogMsg )
// add new credential libraries
if len ( addCredLibs ) > 0 {
2024-08-01 11:18:53 -04:00
i := make ([] * CredentialLibrary , 0 , len ( addCredLibs ))
2022-06-08 13:51:56 -07:00
for _ , cl := range addCredLibs {
i = append ( i , cl )
}
2021-06-22 22:26:26 -04:00
addMsgs := make ([] * oplog . Message , 0 , len ( addCredLibs ))
2022-06-08 13:51:56 -07:00
if err := w . CreateItems ( ctx , i , db . NewOplogMsgs ( & addMsgs )); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to add target credential libraries" ))
2021-06-22 22:26:26 -04:00
}
rowsAffected += len ( addMsgs )
msgs = append ( msgs , addMsgs ... )
metadata [ "op-type" ] = append ( metadata [ "op-type" ], oplog . OpType_OP_TYPE_CREATE . String ())
}
// delete existing credential libraries not part of set
2022-06-08 13:51:56 -07:00
if len ( delCredLibs ) > 0 {
2024-08-01 11:18:53 -04:00
i := make ([] * CredentialLibrary , 0 , len ( delCredLibs ))
2022-06-08 13:51:56 -07:00
for _ , cl := range delCredLibs {
i = append ( i , cl )
}
delMsgs := make ([] * oplog . Message , 0 , len ( delCredLibs ))
rowsDeleted , err := w . DeleteItems ( ctx , i , db . NewOplogMsgs ( & delMsgs ))
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to delete target credential libraries" ))
2021-06-22 22:26:26 -04:00
}
if rowsDeleted != len ( delMsgs ) {
2022-06-08 13:51:56 -07:00
return errors . New ( ctx , errors . MultipleRecords , op , fmt . Sprintf ( "target credential libraries deleted %d did not match request for %d" , rowsDeleted , len ( delCredLibs )))
2021-06-22 22:26:26 -04:00
}
rowsAffected += rowsDeleted
msgs = append ( msgs , delMsgs ... )
metadata [ "op-type" ] = append ( metadata [ "op-type" ], oplog . OpType_OP_TYPE_DELETE . String ())
}
2022-06-08 13:51:56 -07:00
// add new static credential
if len ( addStaticCred ) > 0 {
2024-08-01 11:18:53 -04:00
i := make ([] * StaticCredential , 0 , len ( addStaticCred ))
2022-06-08 13:51:56 -07:00
for _ , cl := range addStaticCred {
i = append ( i , cl )
}
addMsgs := make ([] * oplog . Message , 0 , len ( addStaticCred ))
if err := w . CreateItems ( ctx , i , db . NewOplogMsgs ( & addMsgs )); err != nil {
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to add target static credential " ))
}
rowsAffected += len ( addMsgs )
msgs = append ( msgs , addMsgs ... )
metadata [ "op-type" ] = append ( metadata [ "op-type" ], oplog . OpType_OP_TYPE_CREATE . String ())
}
// delete existing static credentials not part of set
if len ( delStaticCred ) > 0 {
2024-08-01 11:18:53 -04:00
i := make ([] * StaticCredential , 0 , len ( delStaticCred ))
2022-06-08 13:51:56 -07:00
for _ , cl := range delStaticCred {
i = append ( i , cl )
}
delMsgs := make ([] * oplog . Message , 0 , len ( delStaticCred ))
rowsDeleted , err := w . DeleteItems ( ctx , i , db . NewOplogMsgs ( & delMsgs ))
if err != nil {
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to delete target static credential" ))
}
if rowsDeleted != len ( delMsgs ) {
return errors . New ( ctx , errors . MultipleRecords , op , fmt . Sprintf ( "target static credential deleted %d did not match request for %d" , rowsDeleted , len ( delStaticCred )))
}
rowsAffected += rowsDeleted
msgs = append ( msgs , delMsgs ... )
metadata [ "op-type" ] = append ( metadata [ "op-type" ], oplog . OpType_OP_TYPE_DELETE . String ())
}
2021-06-22 22:26:26 -04:00
if err := w . WriteOplogEntryWith ( ctx , oplogWrapper , targetTicket , metadata , msgs ); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to write oplog" ))
2021-06-22 22:26:26 -04:00
}
2023-03-30 15:19:30 -04:00
hostSources , err := fetchHostSources ( ctx , reader , targetId )
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to retrieve current target host sets after add/delete" ))
2021-06-22 22:26:26 -04:00
}
2023-03-30 15:19:30 -04:00
updatedTarget . SetHostSources ( hostSources )
credSources , err := fetchCredentialSources ( ctx , reader , targetId )
2021-06-22 22:26:26 -04:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to retrieve current target credential sources after add/delete" ))
2021-06-22 22:26:26 -04:00
}
2023-03-30 15:19:30 -04:00
updatedTarget . SetCredentialSources ( credSources )
2021-06-22 22:26:26 -04:00
return nil
},
)
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , db . NoRowsAffected , errors . Wrap ( ctx , err , op )
2021-06-22 22:26:26 -04:00
}
2023-03-30 15:19:30 -04:00
return t . HostSource , t . CredentialSources , rowsAffected , nil
2021-06-22 22:26:26 -04:00
}
2021-10-26 09:57:22 -04:00
type changeQueryResult struct {
2022-06-08 13:51:56 -07:00
Action string
Type string
SourceId string
2021-06-22 22:26:26 -04:00
}
2022-06-08 13:51:56 -07:00
func ( r * Repository ) changes ( ctx context . Context , targetId string , ids [] string , purpose credential . Purpose ) (
addCredLib , delCredLib [] * CredentialLibrary ,
addStaticCred , delStaticCred [] * StaticCredential ,
err error ,
) {
2021-06-22 22:26:26 -04:00
const op = "target.(Repository).changes"
2021-10-26 09:57:22 -04:00
// TODO ensure that all cls have the same purpose as the given purpose?
2021-06-22 22:26:26 -04:00
var inClauseSpots [] string
2022-10-25 18:29:38 -05:00
var params [] any
2021-10-26 09:57:22 -04:00
params = append ( params , sql . Named ( "target_id" , targetId ), sql . Named ( "purpose" , purpose ))
2022-06-08 13:51:56 -07:00
for idx , id := range ids {
params = append ( params , sql . Named ( fmt . Sprintf ( "%d" , idx + 1 ), id ))
2021-10-26 09:57:22 -04:00
inClauseSpots = append ( inClauseSpots , fmt . Sprintf ( "@%d" , idx + 1 ))
2021-06-22 22:26:26 -04:00
}
inClause := strings . Join ( inClauseSpots , "," )
if inClause == "" {
inClause = "''"
}
2021-10-26 09:57:22 -04:00
query := fmt . Sprintf ( setChangesQuery , inClause )
2021-06-22 22:26:26 -04:00
rows , err := r . reader . Query ( ctx , query , params )
if err != nil {
2022-06-08 13:51:56 -07:00
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "query failed" ))
2021-06-22 22:26:26 -04:00
}
defer rows . Close ()
for rows . Next () {
2021-10-26 09:57:22 -04:00
var chg changeQueryResult
2022-01-05 14:38:38 -05:00
if err := r . reader . ScanRows ( ctx , rows , & chg ); err != nil {
2022-06-08 13:51:56 -07:00
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "scan row failed" ))
2021-06-22 22:26:26 -04:00
}
2022-06-09 15:35:55 -07:00
switch CredentialSourceType ( chg . Type ) {
case LibraryCredentialSourceType :
2023-06-06 17:09:08 -04:00
lib , err := NewCredentialLibrary ( ctx , targetId , chg . SourceId , purpose )
2022-06-08 13:51:56 -07:00
if err != nil {
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op )
}
switch chg . Action {
case "delete" :
delCredLib = append ( delCredLib , lib )
default :
addCredLib = append ( addCredLib , lib )
}
2022-06-09 15:35:55 -07:00
case StaticCredentialSourceType :
2023-06-06 17:09:08 -04:00
cred , err := NewStaticCredential ( ctx , targetId , chg . SourceId , purpose )
2022-06-08 13:51:56 -07:00
if err != nil {
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op )
}
switch chg . Action {
case "delete" :
delStaticCred = append ( delStaticCred , cred )
default :
addStaticCred = append ( addStaticCred , cred )
}
}
}
2024-01-22 14:03:40 -05:00
if err := rows . Err (); err != nil {
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "next rows error" ))
}
2022-06-08 13:51:56 -07:00
return addCredLib , delCredLib , addStaticCred , delStaticCred , nil
2021-06-22 22:26:26 -04:00
}
2021-07-27 12:48:36 -04:00
func fetchCredentialSources ( ctx context . Context , r db . Reader , targetId string ) ([] CredentialSource , error ) {
const op = "target.fetchCredentialSources"
2022-06-08 13:51:56 -07:00
var sources [] * TargetCredentialSource
2022-10-25 18:29:38 -05:00
if err := r . SearchWhere ( ctx , & sources , "target_id = ?" , [] any { targetId }); err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op )
2021-06-22 22:26:26 -04:00
}
2022-06-08 13:51:56 -07:00
if len ( sources ) == 0 {
2021-06-22 22:26:26 -04:00
return nil , nil
}
2022-06-08 13:51:56 -07:00
ret := make ([] CredentialSource , len ( sources ))
for i , source := range sources {
ret [ i ] = source
2021-07-27 12:48:36 -04:00
}
return ret , nil
2021-06-22 22:26:26 -04:00
}
2022-06-08 13:51:56 -07:00
2023-11-15 19:55:46 -05:00
func ( r * Repository ) createSources ( ctx context . Context , tId string , tSubtype globals . Subtype , credSources CredentialSources ) ([] * CredentialLibrary , [] * StaticCredential , error ) {
2022-06-08 13:51:56 -07:00
const op = "target.(Repository).createSources"
// Get a list of unique ids being attached to the target, to be used for looking up the source type (library or static)
2022-07-19 15:16:24 -07:00
ids := strutil . MergeSlices ( credSources . BrokeredCredentialIds , credSources . InjectedApplicationCredentialIds )
2022-06-08 13:51:56 -07:00
totalCreds := len ( ids )
ids = strutil . RemoveDuplicates ( ids , false )
if len ( ids ) == 0 {
return nil , nil , errors . New ( ctx , errors . InvalidParameter , op , "missing credential sources" )
}
// Fetch credentials from database to determine the type of credential
var credView [] * credentialSourceView
2022-10-25 18:29:38 -05:00
if err := r . reader . SearchWhere ( ctx , & credView , "public_id in (?)" , [] any { ids }); err != nil {
2022-06-08 13:51:56 -07:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "can't retrieve credentials" ))
}
if len ( ids ) != len ( credView ) {
return nil , nil , errors . New ( ctx , errors . NotSpecificIntegrity , op ,
fmt . Sprintf ( "mismatch between request and returned source ids, expected %d got %d" , len ( ids ), len ( credView )))
}
// Create a map between credential source ID and it's type (library or static).
// This will allow for a quick lookup when calling the corresponding New below
2022-06-09 15:35:55 -07:00
credTypeById := make ( map [ string ] CredentialSourceType , len ( ids ))
2022-06-08 13:51:56 -07:00
for _ , cv := range credView {
2022-06-09 15:35:55 -07:00
credTypeById [ cv . GetPublicId ()] = CredentialSourceType ( cv . GetType ())
2022-06-08 13:51:56 -07:00
}
credLibs := make ([] * CredentialLibrary , 0 , totalCreds )
staticCred := make ([] * StaticCredential , 0 , totalCreds )
byPurpose := map [ credential . Purpose ][] string {
2022-07-19 15:16:24 -07:00
credential . BrokeredPurpose : credSources . BrokeredCredentialIds ,
credential . InjectedApplicationPurpose : credSources . InjectedApplicationCredentialIds ,
2022-06-08 13:51:56 -07:00
}
for purpose , ids := range byPurpose {
for _ , id := range ids {
switch credTypeById [ id ] {
2022-06-09 15:35:55 -07:00
case LibraryCredentialSourceType :
2023-06-06 17:09:08 -04:00
lib , err := NewCredentialLibrary ( ctx , tId , id , purpose )
2022-06-08 13:51:56 -07:00
if err != nil {
return nil , nil , errors . Wrap ( ctx , err , op )
}
credLibs = append ( credLibs , lib )
2022-06-09 15:35:55 -07:00
case StaticCredentialSourceType :
2023-06-06 17:09:08 -04:00
cred , err := NewStaticCredential ( ctx , tId , id , purpose )
2022-06-08 13:51:56 -07:00
if err != nil {
return nil , nil , errors . Wrap ( ctx , err , op )
}
staticCred = append ( staticCred , cred )
}
}
}
vetCredentialSources , ok := subtypeRegistry . vetCredentialSourcesFunc ( tSubtype )
if ! ok {
return nil , nil , errors . New ( ctx , errors . InvalidParameter , op , fmt . Sprintf ( "is an unsupported target type %s" , tSubtype ))
}
if err := vetCredentialSources ( ctx , credLibs , staticCred ); err != nil {
return nil , nil , errors . Wrap ( ctx , err , op )
}
return credLibs , staticCred , nil
}