Skip to content

Commit d3fe406

Browse files
committed
feat(migrate): WIP all data retrieved and transformed
1 parent 18700b0 commit d3fe406

File tree

1 file changed

+225
-22
lines changed

1 file changed

+225
-22
lines changed

cmd/migrate.go

Lines changed: 225 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
"encoding/json"
2020
"fmt"
2121

22+
// "github.com/Keyfactor/keyfactor-go-client-sdk/v24/api/keyfactor/v2"
23+
"github.com/Keyfactor/keyfactor-go-client-sdk/v2/api/keyfactor"
2224
"github.com/rs/zerolog/log"
2325
"github.com/spf13/cobra"
2426
)
@@ -41,9 +43,9 @@ var migratePamCmd = &cobra.Command{
4143

4244
// load specified flags
4345
migrateFrom, _ := cmd.Flags().GetString("from") // defined pam provider
44-
migrateTo, _ := cmd.Flags().GetString("to") // target pam provider typefffffff
46+
migrateTo, _ := cmd.Flags().GetString("to") // target pam provider type
4547
appendName, _ := cmd.Flags().GetString("append-name") // text to append to <<FROM>> name
46-
// TODO: define stores flag to pass in GUIDs of stores to migrate
48+
storeUsingPam, _ := cmd.Flags().GetString("store")
4749

4850
// Debug + expEnabled checks
4951
informDebug(debugFlag)
@@ -66,6 +68,9 @@ var migratePamCmd = &cobra.Command{
6668
return cErr
6769
}
6870

71+
// TODO: assign error and check
72+
legacyClient, _ := initClient(false)
73+
6974
log.Info().Msg("looking up usage of <<from>> PAM Provider")
7075

7176
log.Debug().Msg("call: PAMProviderGetPamProviders()")
@@ -84,6 +89,7 @@ var migratePamCmd = &cobra.Command{
8489
fmt.Println(string(jobject))
8590

8691
// TODO: ensure only 1 returned PAM Provider definition
92+
fromPamProvider := listPamProvidersInUse[0]
8793

8894
// get PAM Type definition for PAM Provider migrating <<FROM>>
8995
log.Debug().Msg("call: PAMProviderGetPamProviders()")
@@ -105,46 +111,66 @@ var migratePamCmd = &cobra.Command{
105111
// the inner map tracks PAM instance GUIDs to that instances value for the field
106112
// map[fieldname] -> map[InstanceGuid] = set value
107113
inUsePamParamValues := map[string]map[string]string{}
114+
fromProviderLevelParamValues := map[string]string{}
115+
var fromPamType keyfactor.CSSCMSDataModelModelsProviderType
116+
var toPamType keyfactor.CSSCMSDataModelModelsProviderType
108117
for _, pamType := range pamTypes {
109-
if *pamType.Id == *listPamProvidersInUse[0].ProviderType.Id {
118+
if *pamType.Id == *fromPamProvider.ProviderType.Id {
119+
fromPamType = pamType
110120
// TODO: remove debugging
121+
fmt.Println("vvv FROM TYPE vvv")
111122
jobject, _ := json.MarshalIndent(pamType, "", " ")
112123
fmt.Println(string(jobject))
113124
jobject, _ = json.MarshalIndent(pamType.AdditionalProperties["Parameters"], "", " ")
114125
fmt.Println(string(jobject))
115-
// TODO: check typing, have to access "Parameters" instead of ProviderTypeParams
116-
for _, pamParamType := range pamType.AdditionalProperties["Parameters"].([]interface{}) {
117-
jobject, _ := json.MarshalIndent(pamParamType, "", " ")
118-
fmt.Println(string(jobject))
119-
if pamParamType.(map[string]interface{})["InstanceLevel"].(bool) {
120-
// found definition of an instance level param for the type in question
121-
// create key in map for the field name
122-
inUsePamParamValues[pamParamType.(map[string]interface{})["Name"].(string)] = map[string]string{}
123-
fmt.Println("made it!")
124-
}
125-
}
126+
fmt.Println("^^^ FROM TYPE ^^^")
127+
}
128+
129+
if *pamType.Name == migrateTo {
130+
toPamType = pamType
131+
// TODO: remove debugging
132+
fmt.Println("vvv TO TYPE vvv")
133+
jobject, _ := json.MarshalIndent(pamType, "", " ")
134+
fmt.Println(string(jobject))
135+
jobject, _ = json.MarshalIndent(pamType.AdditionalProperties["Parameters"], "", " ")
136+
fmt.Println(string(jobject))
137+
fmt.Println("^^^ TO TYPE ^^^")
138+
}
139+
}
140+
141+
// TODO: check typing, have to access "Parameters" instead of ProviderTypeParams
142+
for _, pamParamType := range fromPamType.AdditionalProperties["Parameters"].([]interface{}) {
143+
jobject, _ := json.MarshalIndent(pamParamType, "", " ")
144+
fmt.Println(string(jobject))
145+
if pamParamType.(map[string]interface{})["InstanceLevel"].(bool) {
146+
// found definition of an instance level param for the type in question
147+
// create key in map for the field name
148+
inUsePamParamValues[pamParamType.(map[string]interface{})["Name"].(string)] = map[string]string{}
149+
fmt.Println("made it!")
126150
}
127151
}
152+
128153
jobject, _ = json.MarshalIndent(inUsePamParamValues, "", " ")
129154
fmt.Println(string(jobject))
130155

131156
// step through list of every defined param value
132157
// record unique GUIDs of every param value on InstanceLevel : true
133-
// don't count InstanceLevel : false because those are Secret (DataType:2) instances for the Provider itself, not actual usages
158+
// InstanceLevel : true is for per-secret fields
159+
// InstanceLevel : false is provider level secrets - these are also recorded for migration
134160
for _, pamParam := range listPamProvidersInUse[0].ProviderTypeParamValues {
135-
jobject, _ = json.MarshalIndent(pamParam, "", " ")
136-
fmt.Println(string(jobject))
161+
// jobject, _ = json.MarshalIndent(pamParam, "", " ")
162+
// fmt.Println(string(jobject))
163+
fieldName := *pamParam.ProviderTypeParam.Name
164+
usageGuid := *pamParam.InstanceGuid
137165
if *pamParam.ProviderTypeParam.InstanceLevel {
138-
fieldName := *pamParam.ProviderTypeParam.Name
139-
usageGuid := *pamParam.InstanceGuid
140166
inUsePamParamValues[fieldName][usageGuid] = *pamParam.Value
167+
} else {
168+
fromProviderLevelParamValues[fieldName] = *pamParam.Value
141169
}
142170
}
143171
jobject, _ = json.MarshalIndent(inUsePamParamValues, "", " ")
144172
fmt.Println(string(jobject))
145173

146-
return nil
147-
148174
// TODO: make sure every field has the same number of GUIDs tracked
149175
// tally GUID count for logging
150176

@@ -155,6 +181,55 @@ var migratePamCmd = &cobra.Command{
155181
// mark GUID ID for pam type
156182
// mark integer IDs for each Parameter type
157183

184+
// TODO: check that migration target PAM Provider was not already created
185+
186+
fmt.Println("creating new Provider of migration target PAM Type")
187+
var migrationPamProvider keyfactor.CSSCMSDataModelModelsProvider
188+
migrationPamProvider.Name = fromPamProvider.Name + appendName
189+
migrationPamProvider.ProviderType = keyfactor.CSSCMSDataModelModelsProviderType{
190+
Id: toPamType.Id,
191+
}
192+
var onevalue int32 = 1
193+
migrationPamProvider.Area = &onevalue
194+
migrationPamProvider.SecuredAreaId = nil
195+
196+
// need to init AdditionalProperties map when setting value
197+
migrationPamProvider.AdditionalProperties = map[string]interface{}{
198+
"Remote": false, // this property is not on the model for some reason
199+
}
200+
201+
fmt.Println("getting migration target PAM Type parameter definitions, InstanceLevel : false")
202+
// TODO: check typing, have to access "Parameters" instead of ProviderTypeParams
203+
for _, pamParamType := range fromPamType.AdditionalProperties["Parameters"].([]interface{}) {
204+
if !pamParamType.(map[string]interface{})["InstanceLevel"].(bool) {
205+
// found a provider level parameter
206+
// need to find the value to map over
207+
// then create an object with that value and TypeParam settings
208+
paramName := pamParamType.(map[string]interface{})["Name"].(string)
209+
paramValue := selectProviderParamValue(paramName, fromPamProvider.ProviderTypeParamValues)
210+
paramTypeId := selectProviderTypeParamId(paramName, toPamType.AdditionalProperties["Parameters"].([]interface{}))
211+
falsevalue := false
212+
providerLevelParameter := keyfactor.CSSCMSDataModelModelsPamProviderTypeParamValue{
213+
Value: &paramValue,
214+
ProviderTypeParam: &keyfactor.CSSCMSDataModelModelsProviderTypeParam{
215+
Id: &paramTypeId,
216+
Name: &paramName,
217+
InstanceLevel: &falsevalue,
218+
},
219+
}
220+
// TODO: might need to explicit filter for CyberArk expected params, i.e. not map over Safe
221+
// append filled out provider type parameter object, which contains the Provider-level parameter values
222+
migrationPamProvider.ProviderTypeParamValues = append(migrationPamProvider.ProviderTypeParamValues, providerLevelParameter)
223+
}
224+
}
225+
226+
msg := "Created new PAM Provider definition to be created."
227+
fmt.Println(msg)
228+
log.Info().Msg(msg)
229+
jobject, _ = json.MarshalIndent(migrationPamProvider, "", " ")
230+
fmt.Println(string(jobject))
231+
fmt.Println("^^^ PAM Provider to be created ^^^")
232+
158233
// POST new PAM Provider
159234
// create new PAM Instance of designated <<TO>> type
160235
// set area = 1 or previous value
@@ -166,18 +241,67 @@ var migratePamCmd = &cobra.Command{
166241
// providertypeparam should be set to all matching values from GET TYPES
167242
// ignoring datatype
168243

244+
//
245+
// TODO: POST PAM PROVIDER
246+
//
247+
169248
// foreach store GUID pass in as a parameter-----
170249
// GET Store by GUID (instance GUID matches Store Id GUID)
171250
// output some store info to confirm
172251

173-
// parse Properties field into interactable object
252+
// TODO: assign error and check
253+
certStore, _ := legacyClient.GetCertificateStoreByID(storeUsingPam)
254+
255+
jobject, _ = json.MarshalIndent(certStore, "", " ")
256+
fmt.Println(string(jobject))
257+
fmt.Println("^^^ found cert store ^^^")
258+
259+
jobject, _ = json.MarshalIndent(certStore.Properties, "", " ")
260+
fmt.Println(string(jobject))
261+
fmt.Println("^^^ cert store properties ^^^")
174262

175263
// foreach property key (properties is an object not an array)
176264
// if value is an object, and object has an InstanceGuid
177265
// property object is a match for a secret
178266
// instead, can check if there is a ProviderId set, and if that
179267
// matches integer id of original Provider <<FROM>>
180268

269+
for propName, prop := range certStore.Properties {
270+
propSecret, isSecret := prop.(map[string]interface{})
271+
if isSecret {
272+
formattedSecret := map[string]map[string]interface{}{
273+
"Value": map[string]interface{}{},
274+
}
275+
isManaged := propSecret["IsManaged"].(bool)
276+
if isManaged { // managed secret, i.e. PAM Provider in use
277+
278+
// check if Pam Secret is using our migrating provider
279+
if *fromPamProvider.Id == int32(propSecret["ProviderId"].(float64)) {
280+
// reformat to required POST format for properties
281+
formattedSecret["Value"] = reformatPamSecretForPost(propSecret)
282+
} else {
283+
// Pam Secret that Needs to be migrated
284+
formattedSecret["Value"] = buildMigratedPamSecret(propSecret, fromProviderLevelParamValues, 0)
285+
}
286+
} else {
287+
// non-managed secret i.e. a KF-encrypted secret, or no value
288+
// still needs to be reformatted to required POST format
289+
formattedSecret["Value"] = map[string]interface{}{
290+
"SecretValue": propSecret["Value"],
291+
}
292+
}
293+
294+
// update Properties object with newly formatted secret, compliant with POST requirements
295+
certStore.Properties[propName] = formattedSecret
296+
297+
jobject, _ = json.MarshalIndent(certStore.Properties, "", " ")
298+
fmt.Println(string(jobject))
299+
fmt.Println("^^^ SECRETS REFORMATTED ^^^")
300+
}
301+
}
302+
303+
return nil
304+
181305
// update property object
182306
// foreach ProviderTypeParameterValues
183307
// where ProviderTypeParam.Name = first map key (map is map[fieldname]map[InstanceGuid]value)
@@ -198,10 +322,80 @@ var migratePamCmd = &cobra.Command{
198322
},
199323
}
200324

325+
func selectProviderParamValue(name string, providerParameters []keyfactor.CSSCMSDataModelModelsPamProviderTypeParamValue) string {
326+
for _, parameter := range providerParameters {
327+
if name == *parameter.ProviderTypeParam.Name {
328+
return *parameter.Value
329+
}
330+
}
331+
return "NOTFOUND" // TODO: throw error when not found
332+
}
333+
334+
// TODO(check, remove): might need to select DisplayName as well, if required for input in API
335+
func selectProviderTypeParamId(name string, pamTypeParameterDefinitions []interface{}) int32 {
336+
for _, parameterDefinition := range pamTypeParameterDefinitions {
337+
if name == parameterDefinition.(map[string]interface{})["Name"].(string) {
338+
return int32(parameterDefinition.(map[string]interface{})["Id"].(float64)) // interface returns value as float64, needs to be cast to int32
339+
}
340+
}
341+
342+
return -1 // TODO: throw error when not found
343+
}
344+
345+
func reformatPamSecretForPost(secretProp map[string]interface{}) map[string]interface{} {
346+
reformatted := map[string]interface{}{
347+
"Provider": secretProp["ProviderId"],
348+
}
349+
350+
providerParams := secretProp["ProviderTypeParameterValues"].([]interface{})
351+
reformattedParams := map[string]string{}
352+
353+
for _, param := range providerParams {
354+
providerTypeParam := param.(map[string]interface{})["ProviderTypeParam"].(map[string]interface{})
355+
name := providerTypeParam["Name"].(string)
356+
value := param.(map[string]interface{})["Value"].(string)
357+
reformattedParams[name] = value
358+
}
359+
360+
reformatted["Parameters"] = reformattedParams
361+
return reformatted
362+
}
363+
364+
// Inputs:
365+
// secretProp: existing Pam config for property
366+
// migratingValues: map of existing values for matched GUID of this field
367+
// fromProvider: previous provider, to get type level values
368+
// pamProvider: newly created Pam Provider for the migration, with Provider Id
369+
func buildMigratedPamSecret(secretProp map[string]interface{}, fromProviderLevelValues map[string]string, providerId int32) map[string]interface{} {
370+
migrated := map[string]interface{}{
371+
"Provider": providerId,
372+
}
373+
374+
providerParams := secretProp["ProviderTypeParameterValues"].([]interface{})
375+
reformattedParams := map[string]string{}
376+
377+
// NOTE: this is making an assumption that the property names have not changed
378+
// and should be mapped back to the same value
379+
for _, param := range providerParams {
380+
providerTypeParam := param.(map[string]interface{})["ProviderTypeParam"].(map[string]interface{})
381+
name := providerTypeParam["Name"].(string)
382+
value := param.(map[string]interface{})["Value"].(string)
383+
reformattedParams[name] = value
384+
}
385+
386+
// TODO: this logic needs to not be hard-coded, and evaluated for actual migrations other than legacy CyberArk
387+
reformattedParams["Safe"] = fromProviderLevelValues["Safe"]
388+
389+
migrated["Properties"] = reformattedParams
390+
391+
return migrated
392+
}
393+
201394
func init() {
202395
var from string
203396
var to string
204397
var appendName string
398+
var store string
205399

206400
RootCmd.AddCommand(migrateCmd)
207401

@@ -231,7 +425,16 @@ func init() {
231425
"Text to append to current PAM Provider Name in newly created Provider",
232426
)
233427

428+
migratePamCmd.Flags().StringVarP(
429+
&store,
430+
"store",
431+
"s",
432+
"",
433+
"GUID of a Certificate Store, using a PAM Provider that should be migrated",
434+
)
435+
234436
migratePamCmd.MarkFlagRequired("from")
235437
migratePamCmd.MarkFlagRequired("to")
236438
migratePamCmd.MarkFlagRequired("append-name")
439+
migratePamCmd.MarkFlagRequired("store")
237440
}

0 commit comments

Comments
 (0)