Skip to content

Commit fddeeb8

Browse files
committed
feat(cli/stores): Allow for bulk updating of certificate stores via CSV
Signed-off-by: spbsoluble <1661003+spbsoluble@users.noreply.github.com>
1 parent cb802e3 commit fddeeb8

File tree

4 files changed

+68
-73
lines changed

4 files changed

+68
-73
lines changed

cmd/migrate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ var migratePamCmd = &cobra.Command{
392392
}
393393

394394
// check Store Password for PAM field, and process migration if applicable
395-
var storePassword *api.UpdateStorePasswordConfig
395+
var storePassword *api.StorePasswordConfig
396396
if certStore.Password.IsManaged { // managed secret, i.e. PAM Provider in use
397397

398398
// check if Pam Secret is using our migrating provider

cmd/storesBulkOperations.go

Lines changed: 64 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -82,27 +82,6 @@ func formatProperties(json *gabs.Container, reqPropertiesForStoreType []string)
8282
return json
8383
}
8484

85-
func serializeStoreFromTypeDef(storeTypeName string, input string) (string, error) {
86-
// check if storetypename is an integer
87-
storeTypes, _ := readStoreTypesConfig("", DefaultGitRef, DefaultGitRepo, offline)
88-
log.Debug().
89-
Str("storeTypeName", storeTypeName).
90-
Msg("checking if storeTypeName is an integer")
91-
sTypeId, err := strconv.Atoi(storeTypeName)
92-
if err == nil {
93-
log.Debug().
94-
Int("storeTypeId", sTypeId).
95-
Msg("storeTypeName is an integer")
96-
}
97-
for _, st := range storeTypes {
98-
log.Debug().
99-
Interface("st", st).
100-
Msg("iterating through store types")
101-
}
102-
return "", nil
103-
104-
}
105-
10685
var importStoresCmd = &cobra.Command{
10786
Use: "import",
10887
Short: "Import a file with certificate store definitions and create them in Keyfactor Command.",
@@ -163,6 +142,7 @@ If you do not wish to include credentials in your CSV file they can be provided
163142
serverUsername, _ := cmd.Flags().GetString("server-username")
164143
serverPassword, _ := cmd.Flags().GetString("server-password")
165144
storePassword, _ := cmd.Flags().GetString("store-password")
145+
allowUpdates, _ := cmd.Flags().GetBool("sync")
166146

167147
if serverUsername == "" {
168148
serverUsername = os.Getenv(EnvStoresImportCSVServerUsername)
@@ -174,12 +154,6 @@ If you do not wish to include credentials in your CSV file they can be provided
174154
storePassword = os.Getenv(EnvStoresImportCSVStorePassword)
175155
}
176156

177-
//// Flag Checks
178-
//inputErr := storeTypeIdentifierFlagCheck(cmd)
179-
//if inputErr != nil {
180-
// return inputErr
181-
//}
182-
183157
// expEnabled checks
184158
isExperimental := false
185159
debugErr := warnExperimentalFeature(expEnabled, isExperimental)
@@ -351,10 +325,14 @@ If you do not wish to include credentials in your CSV file they can be provided
351325
log.Debug().Msgf("ContainerId is 0, omitting from request")
352326
reqJson.Set(nil, "ContainerId")
353327
}
328+
329+
storeId := reqJson.S("Id").String()
330+
if storeId != "" && allowUpdates {
331+
log.Debug().Str("storeId", storeId).Msgf("Store Id present in row, will attempt update operation")
332+
}
354333
//log.Debug().Msgf("Request JSON: %s", reqJson.String())
355334

356335
// parse properties
357-
var createStoreReqParameters api.CreateStoreFctArgs
358336
props := unmarshalPropertiesString(reqJson.S("Properties").String())
359337

360338
//check if ServerUsername is present in the properties
@@ -382,6 +360,37 @@ If you do not wish to include credentials in your CSV file they can be provided
382360
}
383361
}
384362
mJSON := stripAllBOMs(reqJson.String())
363+
364+
var createStoreReqParameters api.CreateStoreFctArgs
365+
if storeId != "" && allowUpdates {
366+
updateReqParameters := api.UpdateStoreFctArgs{}
367+
conversionError := json.Unmarshal([]byte(mJSON), &updateReqParameters)
368+
if conversionError != nil {
369+
//outputError(conversionError, true, outputFormat)
370+
log.Error().Err(conversionError).Msgf(
371+
"Unable to convert the json into the request parameters object. %s",
372+
conversionError.Error(),
373+
)
374+
return conversionError
375+
}
376+
377+
updateReqParameters.Password = passwdParams
378+
updateReqParameters.Properties = props
379+
log.Info().Msgf("Calling Command to update store from row '%d'", idx)
380+
res, err := kfClient.UpdateStore(&updateReqParameters)
381+
if err != nil {
382+
log.Error().Err(err).Msgf("Error updating store from row '%d'", idx)
383+
resultsMap = append(resultsMap, []string{err.Error()})
384+
inputMap[idx-1]["Errors"] = err.Error()
385+
inputMap[idx-1]["Id"] = "error"
386+
errorCount++
387+
} else {
388+
log.Info().Msgf("Successfully updated store from row '%d' as '%s'", idx, res.Id)
389+
resultsMap = append(resultsMap, []string{fmt.Sprintf("%s", res.Id)})
390+
inputMap[idx-1]["Id"] = res.Id
391+
}
392+
continue
393+
}
385394
conversionError := json.Unmarshal([]byte(mJSON), &createStoreReqParameters)
386395

387396
if conversionError != nil {
@@ -619,7 +628,15 @@ var storesExportCmd = &cobra.Command{
619628

620629
// Authenticate
621630

622-
kfClient, _ := initClient(false)
631+
kfClient, cErr := initClient(false)
632+
if cErr != nil {
633+
log.Error().Err(cErr).Msg("Error initializing client")
634+
return cErr
635+
}
636+
if kfClient == nil {
637+
log.Error().Msg("Keyfactor client is nil after initialization")
638+
return fmt.Errorf("Keyfactor client is nil after initialization")
639+
}
623640

624641
// CLI Logic
625642
log.Info().
@@ -791,7 +808,13 @@ var storesExportCmd = &cobra.Command{
791808
if _, isInt := prop.(int); isInt {
792809
prop = strconv.Itoa(prop.(int))
793810
}
794-
if name != "ServerUsername" && name != "ServerPassword" { // Don't add ServerUsername and ServerPassword to properties as they can't be exported via API
811+
switch prop.(type) {
812+
case map[string]interface{}:
813+
for k, v := range prop.(map[string]interface{}) {
814+
csvData[store.Id][fmt.Sprintf("Properties.%s.%s", name, k)] = v
815+
}
816+
817+
default:
795818
csvData[store.Id]["Properties."+name] = prop
796819
}
797820
}
@@ -1026,44 +1049,6 @@ func unmarshalPropertiesString(properties string) map[string]interface{} {
10261049
return make(map[string]interface{})
10271050
}
10281051

1029-
//func parseSecretField(secretField interface{}) interface{} {
1030-
// var secret api.StorePasswordConfig
1031-
// secretByte, errors := json.Marshal(secretField)
1032-
// if errors != nil {
1033-
// log.Printf("Error in Marshalling: %s", errors)
1034-
// fmt.Printf("Error in Marshalling: %s\n", errors)
1035-
// panic("error marshalling secret field as StorePasswordConfig")
1036-
// }
1037-
//
1038-
// errors = json.Unmarshal(secretByte, &secret)
1039-
// if errors != nil {
1040-
// log.Printf("Error in Unmarshalling: %s", errors)
1041-
// fmt.Printf("Error in Unmarshalling: %s\n", errors)
1042-
// panic("error unmarshalling secret field as StorePasswordConfig")
1043-
// }
1044-
//
1045-
// if secret.IsManaged {
1046-
// params := make(map[string]string)
1047-
// for _, p := range *secret.ProviderTypeParameterValues {
1048-
// params[*p.ProviderTypeParam.Name] = *p.Value
1049-
// }
1050-
// return map[string]interface{}{
1051-
// "Provider": secret.ProviderId,
1052-
// "Parameters": params,
1053-
// }
1054-
// } else {
1055-
// if secret.Value != "" {
1056-
// return map[string]string{
1057-
// "SecretValue": secret.Value,
1058-
// }
1059-
// } else {
1060-
// return map[string]*string{
1061-
// "SecretValue": nil,
1062-
// }
1063-
// }
1064-
// }
1065-
//}
1066-
10671052
func getJsonForRequest(headerRow []string, row []string) *gabs.Container {
10681053
log.Debug().Msgf("Getting JSON for request")
10691054
reqJson := gabs.New()
@@ -1160,6 +1145,8 @@ func init() {
11601145
file string
11611146
resultsPath string
11621147
exportAll bool
1148+
sync bool
1149+
dryRun bool
11631150
)
11641151

11651152
storesCmd.AddCommand(importStoresCmd)
@@ -1239,7 +1226,15 @@ func init() {
12391226

12401227
storesCreateFromCSVCmd.Flags().StringVarP(&file, "file", "f", "", "CSV file containing cert stores to create.")
12411228
storesCreateFromCSVCmd.MarkFlagRequired("file")
1242-
storesCreateFromCSVCmd.Flags().BoolP("dry-run", "d", false, "Do not import, just check for necessary fields.")
1229+
storesCreateFromCSVCmd.Flags().BoolVarP(
1230+
&dryRun, "dry-run", "d", false, "Do not import, "+
1231+
"just check for necessary fields.",
1232+
)
1233+
storesCreateFromCSVCmd.Flags().BoolVarP(
1234+
&sync,
1235+
"sync", "z", false, "Create or update existing stores. "+
1236+
"NOTE: Use this w/ --dry-run to view changes.",
1237+
)
12431238
storesCreateFromCSVCmd.Flags().StringVarP(
12441239
&resultsPath,
12451240
"results-path",

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ require (
99
github.com/Jeffail/gabs v1.4.0
1010
github.com/Keyfactor/keyfactor-auth-client-go v1.3.0
1111
github.com/Keyfactor/keyfactor-go-client-sdk/v2 v2.0.0
12-
github.com/Keyfactor/keyfactor-go-client/v3 v3.4.0-rc.2
12+
github.com/Keyfactor/keyfactor-go-client/v3 v3.4.0-rc.3
1313
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2
1414
github.com/creack/pty v1.1.24
1515
github.com/google/go-cmp v0.7.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ github.com/Keyfactor/keyfactor-auth-client-go v1.3.0 h1:otC213b6CYzqeN9b3CRlH1Qj
2222
github.com/Keyfactor/keyfactor-auth-client-go v1.3.0/go.mod h1:97vCisBNkdCK0l2TuvOSdjlpvQa4+GHsMut1UTyv1jo=
2323
github.com/Keyfactor/keyfactor-go-client-sdk/v2 v2.0.0 h1:ehk5crxEGVBwkC8yXsoQXcyITTDlgbxMEkANrl1dA2Q=
2424
github.com/Keyfactor/keyfactor-go-client-sdk/v2 v2.0.0/go.mod h1:11WXGG9VVKSV0EPku1IswjHbGGpzHDKqD4pe2vD7vas=
25-
github.com/Keyfactor/keyfactor-go-client/v3 v3.4.0-rc.2 h1:P4bJzB2YNc4AaWukZCRJ6+t0Ir3ITtSM+tZgO8BCb2o=
26-
github.com/Keyfactor/keyfactor-go-client/v3 v3.4.0-rc.2/go.mod h1:XGWU4V9Ta3DBE+DsqoSAODYHWPzGWtoI7m8C/2CSaK0=
25+
github.com/Keyfactor/keyfactor-go-client/v3 v3.4.0-rc.3 h1:FCX9TPIQxkOGjENmRrY+CGVKlgtnoqp4airW3PZBe/Y=
26+
github.com/Keyfactor/keyfactor-go-client/v3 v3.4.0-rc.3/go.mod h1:XGWU4V9Ta3DBE+DsqoSAODYHWPzGWtoI7m8C/2CSaK0=
2727
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
2828
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
2929
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=

0 commit comments

Comments
 (0)