@@ -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+
201394func 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