@@ -19,6 +19,8 @@ import (
19
19
"encoding/json"
20
20
"fmt"
21
21
22
+ // "github.com/Keyfactor/keyfactor-go-client-sdk/v24/api/keyfactor/v2"
23
+ "github.com/Keyfactor/keyfactor-go-client-sdk/v2/api/keyfactor"
22
24
"github.com/rs/zerolog/log"
23
25
"github.com/spf13/cobra"
24
26
)
@@ -41,9 +43,9 @@ var migratePamCmd = &cobra.Command{
41
43
42
44
// load specified flags
43
45
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
45
47
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" )
47
49
48
50
// Debug + expEnabled checks
49
51
informDebug (debugFlag )
@@ -66,6 +68,9 @@ var migratePamCmd = &cobra.Command{
66
68
return cErr
67
69
}
68
70
71
+ // TODO: assign error and check
72
+ legacyClient , _ := initClient (false )
73
+
69
74
log .Info ().Msg ("looking up usage of <<from>> PAM Provider" )
70
75
71
76
log .Debug ().Msg ("call: PAMProviderGetPamProviders()" )
@@ -84,6 +89,7 @@ var migratePamCmd = &cobra.Command{
84
89
fmt .Println (string (jobject ))
85
90
86
91
// TODO: ensure only 1 returned PAM Provider definition
92
+ fromPamProvider := listPamProvidersInUse [0 ]
87
93
88
94
// get PAM Type definition for PAM Provider migrating <<FROM>>
89
95
log .Debug ().Msg ("call: PAMProviderGetPamProviders()" )
@@ -105,46 +111,66 @@ var migratePamCmd = &cobra.Command{
105
111
// the inner map tracks PAM instance GUIDs to that instances value for the field
106
112
// map[fieldname] -> map[InstanceGuid] = set value
107
113
inUsePamParamValues := map [string ]map [string ]string {}
114
+ fromProviderLevelParamValues := map [string ]string {}
115
+ var fromPamType keyfactor.CSSCMSDataModelModelsProviderType
116
+ var toPamType keyfactor.CSSCMSDataModelModelsProviderType
108
117
for _ , pamType := range pamTypes {
109
- if * pamType .Id == * listPamProvidersInUse [0 ].ProviderType .Id {
118
+ if * pamType .Id == * fromPamProvider .ProviderType .Id {
119
+ fromPamType = pamType
110
120
// TODO: remove debugging
121
+ fmt .Println ("vvv FROM TYPE vvv" )
111
122
jobject , _ := json .MarshalIndent (pamType , "" , " " )
112
123
fmt .Println (string (jobject ))
113
124
jobject , _ = json .MarshalIndent (pamType .AdditionalProperties ["Parameters" ], "" , " " )
114
125
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!" )
126
150
}
127
151
}
152
+
128
153
jobject , _ = json .MarshalIndent (inUsePamParamValues , "" , " " )
129
154
fmt .Println (string (jobject ))
130
155
131
156
// step through list of every defined param value
132
157
// 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
134
160
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
137
165
if * pamParam .ProviderTypeParam .InstanceLevel {
138
- fieldName := * pamParam .ProviderTypeParam .Name
139
- usageGuid := * pamParam .InstanceGuid
140
166
inUsePamParamValues [fieldName ][usageGuid ] = * pamParam .Value
167
+ } else {
168
+ fromProviderLevelParamValues [fieldName ] = * pamParam .Value
141
169
}
142
170
}
143
171
jobject , _ = json .MarshalIndent (inUsePamParamValues , "" , " " )
144
172
fmt .Println (string (jobject ))
145
173
146
- return nil
147
-
148
174
// TODO: make sure every field has the same number of GUIDs tracked
149
175
// tally GUID count for logging
150
176
@@ -155,6 +181,55 @@ var migratePamCmd = &cobra.Command{
155
181
// mark GUID ID for pam type
156
182
// mark integer IDs for each Parameter type
157
183
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
+
158
233
// POST new PAM Provider
159
234
// create new PAM Instance of designated <<TO>> type
160
235
// set area = 1 or previous value
@@ -166,18 +241,67 @@ var migratePamCmd = &cobra.Command{
166
241
// providertypeparam should be set to all matching values from GET TYPES
167
242
// ignoring datatype
168
243
244
+ //
245
+ // TODO: POST PAM PROVIDER
246
+ //
247
+
169
248
// foreach store GUID pass in as a parameter-----
170
249
// GET Store by GUID (instance GUID matches Store Id GUID)
171
250
// output some store info to confirm
172
251
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 ^^^" )
174
262
175
263
// foreach property key (properties is an object not an array)
176
264
// if value is an object, and object has an InstanceGuid
177
265
// property object is a match for a secret
178
266
// instead, can check if there is a ProviderId set, and if that
179
267
// matches integer id of original Provider <<FROM>>
180
268
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
+
181
305
// update property object
182
306
// foreach ProviderTypeParameterValues
183
307
// where ProviderTypeParam.Name = first map key (map is map[fieldname]map[InstanceGuid]value)
@@ -198,10 +322,80 @@ var migratePamCmd = &cobra.Command{
198
322
},
199
323
}
200
324
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
+
201
394
func init () {
202
395
var from string
203
396
var to string
204
397
var appendName string
398
+ var store string
205
399
206
400
RootCmd .AddCommand (migrateCmd )
207
401
@@ -231,7 +425,16 @@ func init() {
231
425
"Text to append to current PAM Provider Name in newly created Provider" ,
232
426
)
233
427
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
+
234
436
migratePamCmd .MarkFlagRequired ("from" )
235
437
migratePamCmd .MarkFlagRequired ("to" )
236
438
migratePamCmd .MarkFlagRequired ("append-name" )
439
+ migratePamCmd .MarkFlagRequired ("store" )
237
440
}
0 commit comments