Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Graph2133 committed Feb 20, 2022
1 parent e34eff0 commit ad360aa
Show file tree
Hide file tree
Showing 54 changed files with 2,015 additions and 0 deletions.
110 changes: 110 additions & 0 deletions PassManager.Console/Clipboard/WindowsClipboard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;

namespace PassManager.Console.Clipboard
{
public static class WindowsClipboard
{
private const uint UNICODE = 13;

public static void SetText(string text)
{
OpenClipboard();
SetClipboardText(text);
}

private static void OpenClipboard()
{
var retryCount = 5;
while (true)
{
if (OpenClipboard(default))
{
break;
}

if (--retryCount == 0)
{
ThrowWin32Exception();
}

Thread.Sleep(50);
}
}

private static void SetClipboardText(string text)
{
IntPtr hGlobal = default;
try
{
EmptyClipboard();

var allocSize = (text.Length + 1) * 2;
hGlobal = Marshal.AllocHGlobal(allocSize);
if (hGlobal == default)
{
ThrowWin32Exception();
}

var target = GlobalLock(hGlobal);
if (target == default)
{
ThrowWin32Exception();
}

try
{
Marshal.Copy(text.ToCharArray(), 0, target, text.Length);
}
finally
{
GlobalUnlock(target);
}

if (SetClipboardData(UNICODE, hGlobal) == default)
{
ThrowWin32Exception();
}

hGlobal = default;
}
finally
{
if (hGlobal != default)
{
Marshal.FreeHGlobal(hGlobal);
}

CloseClipboard();
}
}

private static void ThrowWin32Exception()
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GlobalLock(IntPtr hMem);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GlobalUnlock(IntPtr hMem);

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool OpenClipboard(IntPtr hWndNewOwner);

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseClipboard();

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetClipboardData(uint uFormat, IntPtr data);

[DllImport("user32.dll")]
static extern bool EmptyClipboard();
}
}
78 changes: 78 additions & 0 deletions PassManager.Console/Commands/AddPasswordCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using PassManager.Console.Commands.Settings;
using PassManager.Console.Extensions;
using PassManager.Core.Repository;
using Spectre.Console;
using Spectre.Console.Cli;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace PassManager.Console.Commands
{
public sealed class AddPasswordCommand : BasePasswordAsyncCommand<AddPasswordSettings>
{
public AddPasswordCommand(IPasswordManager passwordManager) : base(passwordManager)
{
}

public override async Task<int> ExecuteAsync(CommandContext context, AddPasswordSettings settings)
{
var password = this.AskPassword();
var customSecret = this.AskCustomSecret(settings);
var tags = this.AskTags(settings);

var result = await this.passwordManager.CreatePasswordAsync(settings.Name, password, customSecret, tags);
result.PrintConsoleErrorOrMessage($"[green]Password for {settings.Name} saved[/]");

return 0;
}

private string AskPassword()
{
var passwordPrompt = new TextPrompt<string>("Enter [darkolivegreen2]password[/]:")
.Secret()
.Validate(v => !string.IsNullOrEmpty(v), "Password must be provided");

return AnsiConsole.Prompt(passwordPrompt);
}

private string AskCustomSecret(AddPasswordSettings settings)
{
string customSecret = null;
if (settings.ApplyCustomSecret)
{
var customSecretPrompt = new TextPrompt<string>("Enter [darkolivegreen2]custom secret[/]:")
.Secret()
.Validate(v => !string.IsNullOrEmpty(v), "Secret value cannot be empty");

customSecret = AnsiConsole.Prompt(customSecretPrompt);
}
return customSecret;
}

private List<string> AskTags(AddPasswordSettings settings)
{
var tags = new List<string>();
if (settings.AddTags)
{
AnsiConsole.MarkupLine("Enter tags one by one or leave value as empty and press enter to continue.");
var tagValuePrompt = new TextPrompt<string>(
"Enter [darkolivegreen2]tag value[/]:")
.AllowEmpty()
.PromptStyle("red");

while (true)
{
var tagValue = AnsiConsole.Prompt(tagValuePrompt);
if (string.IsNullOrEmpty(tagValue))
{
break;
}

tags.Add(tagValue);
}
}

return tags;
}
}
}
48 changes: 48 additions & 0 deletions PassManager.Console/Commands/BasePasswordAsyncCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using PassManager.Core.Repository;
using PassManager.Core.VirtualDirectory;
using Spectre.Console;
using Spectre.Console.Cli;
using System.Linq;
using System.Threading.Tasks;

namespace PassManager.Console.Commands
{
public abstract class BasePasswordAsyncCommand<T> : AsyncCommand<T> where T : CommandSettings
{
protected readonly IPasswordManager passwordManager;

protected BasePasswordAsyncCommand(IPasswordManager passwordManager)
{
this.passwordManager = passwordManager;
}

protected async Task<IndexEntry> AskUserToSelectEntry(string query)
{
var entries = await this.passwordManager.GetAllByNameAsync(query);
if (!entries.Any())
{
return null;
}

var selectionPrompt = new SelectionPrompt<string>()
.Title("Select entry:")
.PageSize(10)
.MoreChoicesText("[grey](Move up and down to reveal more password entries)[/]")
.AddChoices(entries.Select(e => e.Name).ToArray());
var selectedEntryName = AnsiConsole.Prompt(selectionPrompt);

return entries.Single(e => e.Name.Equals(selectedEntryName));
}
}

public abstract class BasePasswordAsyncCommand : AsyncCommand
{
protected readonly IPasswordManager passwordManager;

protected BasePasswordAsyncCommand(IPasswordManager passwordManager)
{
this.passwordManager = passwordManager;
}
}

}
16 changes: 16 additions & 0 deletions PassManager.Console/Commands/BasePasswordCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using PassManager.Core.Repository;
using PassManager.Core.VirtualDirectory;
using Spectre.Console.Cli;

namespace PassManager.Console.Commands
{
public abstract class BasePasswordCommand : Command
{
protected readonly IPasswordManager passwordManager;

public BasePasswordCommand(IPasswordManager passwordManager)
{
this.passwordManager = passwordManager;
}
}
}
32 changes: 32 additions & 0 deletions PassManager.Console/Commands/DeletePasswordCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using PassManager.Console.Commands.Settings;
using PassManager.Console.Extensions;
using PassManager.Core.Repository;
using Spectre.Console;
using Spectre.Console.Cli;
using System.Threading.Tasks;

namespace PassManager.Console.Commands
{
public class DeletePasswordCommand : BasePasswordAsyncCommand<DeletePasswordSettings>
{
public DeletePasswordCommand(IPasswordManager passwordManager) : base(passwordManager)
{
}

public override async Task<int> ExecuteAsync(CommandContext context, DeletePasswordSettings settings)
{
var entry = await this.AskUserToSelectEntry(settings.Query);
if (entry == null)
{
AnsiConsole.MarkupLine($"[red]Provided query is invalid or password does not exist[/]");
return 0;
}

var result = await this.passwordManager.DeletePasswordByNameAsync(entry.Name);

result.PrintConsoleErrorOrMessage("[green]Password deleted[/]");

return 0;
}
}
}
38 changes: 38 additions & 0 deletions PassManager.Console/Commands/DeleteSecretCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using PassManager.Console.Commands.Settings;
using PassManager.Console.Extensions;
using PassManager.Core.Repository;
using Spectre.Console;
using Spectre.Console.Cli;
using System.Threading.Tasks;

namespace PassManager.Console.Commands
{
public class DeleteSecretCommand : BasePasswordAsyncCommand<DeletePasswordSettings>
{
public DeleteSecretCommand(IPasswordManager passwordManager) : base(passwordManager)
{
}

public override async Task<int> ExecuteAsync(CommandContext context, DeletePasswordSettings settings)
{
var entry = await this.AskUserToSelectEntry(settings.Query);
if (entry == null)
{
AnsiConsole.MarkupLine("[red]Provided query is invalid or password does not exist[/]");
return 0;
}

if (!entry.HasCustomSecret)
{
AnsiConsole.MarkupLine("[red]Selected entry is not locked by custom secret[/]");
return 0;
}

var secret = AnsiConsole.Prompt(new TextPrompt<string>("Enter [darkolivegreen2]secret[/] to unlock entry:").Secret());
var result = await this.passwordManager.DeleteSecret(entry.Name, secret);
result.PrintConsoleErrorOrMessage("[green]Secret deleted. Password will be encrypted using default configuration[/]");

return 0;
}
}
}
44 changes: 44 additions & 0 deletions PassManager.Console/Commands/GetPasswordCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using PassManager.Console.Clipboard;
using PassManager.Console.Commands.Settings;
using PassManager.Console.Extensions;
using PassManager.Core.Repository;
using Spectre.Console;
using Spectre.Console.Cli;
using System.Threading.Tasks;

namespace PassManager.Console.Commands
{
public class GetPasswordCommand : BasePasswordAsyncCommand<DeletePasswordSettings>
{
public GetPasswordCommand(IPasswordManager passwordManager) : base(passwordManager)
{
}

public override async Task<int> ExecuteAsync(CommandContext context, DeletePasswordSettings settings)
{
var entry = await this.AskUserToSelectEntry(settings.Query);
if (entry == null)
{
AnsiConsole.MarkupLine("[red]Provided query is invalid or password does not exist[/]");
return 0;
}

string customSecret = null;
if (entry.HasCustomSecret)
{
var customSecretPrompt = new TextPrompt<string>("Enter [darkolivegreen2]secret[/]:").Secret();
customSecret = AnsiConsole.Prompt(customSecretPrompt);
}

var result = await this.passwordManager.GetPasswordAsync(entry.Name, customSecret);
if (result.Success)
{
WindowsClipboard.SetText(result.ExtensionData);
}

result.PrintConsoleErrorOrMessage("[green]Password copied to buffer[/]");

return 0;
}
}
}
Loading

0 comments on commit ad360aa

Please sign in to comment.