11using System ;
22using System . Collections . Generic ;
3+ using System . Diagnostics ;
34using System . Globalization ;
45using System . IO ;
56using System . Linq ;
7+ using System . Reflection ;
8+ using System . Runtime . Serialization ;
69using System . Text ;
710using System . Text . RegularExpressions ;
811using System . Threading . Tasks ;
@@ -198,6 +201,13 @@ public ConnectedContext(AuthCommon auth)
198201 Description = "Change the sharing permissions of an individual record" ,
199202 Action = ShareRecordCommand
200203 } ) ;
204+ Commands . Add ( "import" ,
205+ new ParseableCommand < ImportCommandOptions >
206+ {
207+ Order = 33 ,
208+ Description = "Imports records from JSON file" ,
209+ Action = ImportCommand
210+ } ) ;
201211
202212 if ( auth . AuthContext . Enforcements . TryGetValue ( "allow_secrets_manager" , out var value ) )
203213 {
@@ -995,16 +1005,7 @@ private void AssignRecordFields(KeeperRecord record, CmdLineRecordField[] fields
9951005 {
9961006 if ( typedField . GetValueAt ( idx ) is IFieldTypeSerialize typedValue )
9971007 {
998- foreach ( var pair in field . Value . Split ( ',' ) )
999- {
1000- var pos = pair . IndexOf ( '=' ) ;
1001- var element = pos > 0 ? pair . Substring ( 0 , pos ) : pair ;
1002- var value = pos > 0 ? pair . Substring ( pos + 1 ) : "" ;
1003- if ( ! typedValue . SetElementValue ( element , value ) )
1004- {
1005- throw new Exception ( $ "Field type { field . FieldName } : Invalid element name: { element } .") ;
1006- }
1007- }
1008+ typedValue . SetValueAsString ( field . Value ) ;
10081009 }
10091010 else
10101011 {
@@ -1037,7 +1038,7 @@ private void VerifyTypedFields(CmdLineRecordField[] fields, RecordType recordTyp
10371038 {
10381039 field . FieldLabel = rtf . FieldLabel ;
10391040 field . IsRecordField = true ;
1040- if ( rtf . RecordField . Multiple == RecordFieldMultiple . Default )
1041+ if ( rtf . RecordField . Multiple == RecordFieldMultiple . Always )
10411042 {
10421043 recordFields . Remove ( field . FieldName ) ;
10431044 }
@@ -1057,7 +1058,7 @@ private void VerifyTypedFields(CmdLineRecordField[] fields, RecordType recordTyp
10571058
10581059 if ( string . IsNullOrEmpty ( field . FieldIndex ) ) continue ;
10591060
1060- if ( recordField . Multiple != RecordFieldMultiple . Default )
1061+ if ( recordField . Multiple != RecordFieldMultiple . Always )
10611062 {
10621063 throw new Exception ( $ "Record field \" { field . FieldName } \" does not support multiple values") ;
10631064 }
@@ -1956,7 +1957,7 @@ record = folder.Records.Select(x => _vault.GetRecord(x)).FirstOrDefault(x =>
19561957 var clientKey = t . Item2 ;
19571958
19581959 Console . WriteLine ( "Successfully generated Client Device\n " ) ;
1959- Console . WriteLine ( $ "One-Time Access Token: { clientKey } ") ;
1960+ Console . WriteLine ( $ "One-Time Access Token: { clientKey } ") ;
19601961 var ipLock = device . LockIp ? "Enabled" : "Disabled" ;
19611962 Console . WriteLine ( $ "IP Lock: { ipLock } ") ;
19621963 var firstAccessOn = device . FirstAccessExpireOn . HasValue ? device . FirstAccessExpireOn . Value . ToString ( "G" ) : "Taken" ;
@@ -1966,7 +1967,7 @@ record = folder.Records.Select(x => _vault.GetRecord(x)).FirstOrDefault(x =>
19661967 }
19671968 else if ( action == "delete-client" )
19681969 {
1969- if ( string . IsNullOrEmpty ( arguments . ClientName ) )
1970+ if ( string . IsNullOrEmpty ( arguments . ClientName ) )
19701971 {
19711972 Console . Write ( "\" client-name\" parameter is required" ) ;
19721973 return ;
@@ -2461,6 +2462,53 @@ record = r;
24612462 }
24622463 }
24632464
2465+ private async Task ImportCommand ( ImportCommandOptions options )
2466+ {
2467+ void Logger ( Severity severity , string message )
2468+ {
2469+ if ( severity == Severity . Warning || severity == Severity . Error )
2470+ {
2471+ Console . WriteLine ( message ) ;
2472+ }
2473+ Debug . WriteLine ( message ) ;
2474+ }
2475+
2476+ if ( ! File . Exists ( options . FileName ) )
2477+ {
2478+ throw new Exception ( $ "File \" { options . FileName } \" does not exist") ;
2479+ }
2480+ var json = File . ReadAllText ( options . FileName ) ;
2481+ var j_options = new ZeroDep . JsonOptions {
2482+ DateTimeStyles = DateTimeStyles . None ,
2483+ } ;
2484+ j_options . SerializationOptions &= ~ ZeroDep . JsonSerializationOptions . AutoParseDateTime ;
2485+ var j = ZeroDep . Json . Deserialize < Dictionary < string , object > > ( json , j_options ) ;
2486+ var import = KeeperImport . LoadJsonDictionary ( j ) ;
2487+ var result = await _vault . ImportJson ( import , Logger ) ;
2488+ var table = new Tabulate ( 2 )
2489+ {
2490+ LeftPadding = 4
2491+ } ;
2492+ table . SetColumnRightAlign ( 0 , true ) ;
2493+ if ( result . SharedFolderCount > 0 )
2494+ {
2495+ table . AddRow ( "Shared Folders:" , result . SharedFolderCount ) ;
2496+ }
2497+ if ( result . FolderCount > 0 )
2498+ {
2499+ table . AddRow ( "Folders:" , result . FolderCount ) ;
2500+ }
2501+ if ( result . TypedRecordCount > 0 )
2502+ {
2503+ table . AddRow ( "Records:" , result . TypedRecordCount ) ;
2504+ }
2505+ if ( result . LegacyRecordCount > 0 )
2506+ {
2507+ table . AddRow ( "Legacy Records:" , result . LegacyRecordCount ) ;
2508+ }
2509+ table . Dump ( ) ;
2510+ }
2511+
24642512 private Task RecordTypeInfoCommand ( RecordTypeInfoOptions options )
24652513 {
24662514 Tabulate table = null ;
@@ -2475,7 +2523,7 @@ private Task RecordTypeInfoCommand(RecordTypeInfoOptions options)
24752523 table . AddRow ( f . Name , f . Type ? . Name ,
24762524 f . Multiple == RecordFieldMultiple . Optional
24772525 ? "optional"
2478- : ( f . Multiple == RecordFieldMultiple . Default ? "default" : "" ) ,
2526+ : ( f . Multiple == RecordFieldMultiple . Always ? "default" : "" ) ,
24792527 f . Type ? . Description ?? "" ) ;
24802528 }
24812529 }
@@ -2534,8 +2582,18 @@ private Task RecordTypeInfoCommand(RecordTypeInfoOptions options)
25342582 {
25352583 if ( typeof ( IFieldTypeSerialize ) . IsAssignableFrom ( fieldInfo . Type . Type ) )
25362584 {
2537- IFieldTypeSerialize fts = ( IFieldTypeSerialize ) Activator . CreateInstance ( fieldInfo . Type . Type ) ;
2538- table . AddRow ( "Value Elements:" , string . Join ( ", " , fts . Elements . Select ( x => $ "\" { x } \" ") ) ) ;
2585+ var elements = new List < string > ( ) ;
2586+ var properties = fieldInfo . Type . Type . GetProperties ( ) ;
2587+ foreach ( var prop in properties )
2588+ {
2589+ var attribute = prop . GetCustomAttribute < DataMemberAttribute > ( true ) ;
2590+ if ( attribute != null )
2591+ {
2592+ elements . Add ( attribute . Name ) ;
2593+ }
2594+ }
2595+
2596+ table . AddRow ( "Value Elements:" , string . Join ( ", " , elements . Select ( x => $ "\" { x } \" ") ) ) ;
25392597 }
25402598 }
25412599 }
@@ -2901,6 +2959,12 @@ class MakeFolderOptions : FolderOptions
29012959 public bool ? CanEdit { get ; set ; }
29022960 }
29032961
2962+ class ImportCommandOptions
2963+ {
2964+ [ Value ( 0 , Required = true , HelpText = "JSON import filename" ) ]
2965+ public string FileName { get ; set ; }
2966+ }
2967+
29042968 class SecretManagerOptions
29052969 {
29062970 [ Option ( "folder" , Required = false , HelpText = "Shared Folder UID or name. \" share\" , \" unshare\" only" ) ]
0 commit comments