Skip to content

Commit 5610c53

Browse files
committed
Keeper Import
1 parent 062a8fe commit 5610c53

19 files changed

+8001
-368
lines changed

Commander/Commander.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
<Reference Include="System.Security" />
8484
<Reference Include="System.Web.Extensions" />
8585
<Reference Include="System.Windows.Forms" />
86+
<Reference Include="System.Xml" />
8687
</ItemGroup>
8788
<ItemGroup>
8889
<Compile Include="CommanderExtensions.cs" />
@@ -95,6 +96,7 @@
9596
<Compile Include="StorageUtils.cs" />
9697
<Compile Include="Program.cs" />
9798
<Compile Include="Properties\AssemblyInfo.cs" />
99+
<Compile Include="ZeroDepJson.cs" />
98100
</ItemGroup>
99101
<ItemGroup>
100102
<None Include="App.config" />

Commander/ConnectedCommands.cs

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.Globalization;
45
using System.IO;
56
using System.Linq;
7+
using System.Reflection;
8+
using System.Runtime.Serialization;
69
using System.Text;
710
using System.Text.RegularExpressions;
811
using 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")]

Commander/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using KeeperSecurity.Utils;
1616
using KeeperSecurity.Vault;
1717
using Cli;
18+
using System.Collections.Generic;
1819

1920
namespace Commander
2021
{

0 commit comments

Comments
 (0)