Skip to content

Commit

Permalink
Merge #2266 User interfaces for auth tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
politas committed Feb 3, 2018
2 parents d200a93 + 63005a2 commit bb02892
Show file tree
Hide file tree
Showing 15 changed files with 665 additions and 68 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
- [GUI] Add import downloads menu item to GUI (#2246 by: HebaruSan; reviewed: politas)
- [Core] Accept header and infrastructure for auth tokens (#2263 by: HebaruSan; reviewed: dbent)
- [CLI] Add Cmdline import command (#2264 by: HebaruSan; reviewed: politas)
- [Multiple] User interfaces for auth tokens (#2266 by: HebaruSan; reviewed: politas)

### Bugfixes

Expand All @@ -21,6 +22,7 @@ All notable changes to this project will be documented in this file.
- [Core] Fix missing filename in install -c log message (No PR, by: HebaruSan)
- [GUI] Leave out children already shown in ancestor node (#2258 by: HebaruSan; reviewed: politas)
- [GUI] Resolve provides for install-from-ckan-file (#2259 by: HebaruSan; reviewed: politas)
- [Build] Use arch=32 for OSX (#2270 by: HebaruSan; reviewed: techman83)

## v1.24.0-PRE1 (McCandless)

Expand Down
175 changes: 175 additions & 0 deletions Cmdline/Action/AuthToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using CommandLine;
using CommandLine.Text;
using log4net;

namespace CKAN.CmdLine
{
/// <summary>
/// Subcommand for managing authentication tokens
/// </summary>
public class AuthToken : ISubCommand
{
/// <summary>
/// Initialize the subcommand
/// </summary>
public AuthToken() { }

/// <summary>
/// Run the subcommand
/// </summary>
/// <param name="unparsed">Command line parameters not yet handled by parser</param>
/// <returns>
/// Exit code
/// </returns>
public int RunSubCommand(SubCommandOptions unparsed)
{
string[] args = unparsed.options.ToArray();
int exitCode = Exit.OK;

Parser.Default.ParseArgumentsStrict(args, new AuthTokenSubOptions(), (string option, object suboptions) =>
{
if (!string.IsNullOrEmpty(option) && suboptions != null)
{
CommonOptions options = (CommonOptions)suboptions;
user = new ConsoleUser(options.Headless);
manager = new KSPManager(user);
exitCode = options.Handle(manager, user);
if (exitCode == Exit.OK)
{
switch (option)
{
case "list":
exitCode = listAuthTokens(options);
break;
case "add":
exitCode = addAuthToken((AddAuthTokenOptions)options);
break;
case "remove":
exitCode = removeAuthToken((RemoveAuthTokenOptions)options);
break;
}
}
}
}, () => { exitCode = MainClass.AfterHelp(); });
return exitCode;
}

private int listAuthTokens(CommonOptions opts)
{
List<string> hosts = new List<string>(Win32Registry.GetAuthTokenHosts());
if (hosts.Count > 0)
{
int longestHostLen = hostHeader.Length;
int longestTokenLen = tokenHeader.Length;
foreach (string host in hosts)
{
longestHostLen = Math.Max(longestHostLen, host.Length);
string token;
if (Win32Registry.TryGetAuthToken(host, out token))
{
longestTokenLen = Math.Max(longestTokenLen, token.Length);
}
}
// Create format string: {0,-longestHostLen} {1,-longestTokenLen}
string fmt = string.Format("{0}0,-{2}{1} {0}1,-{3}{1}",
"{", "}", longestHostLen, longestTokenLen);
user.RaiseMessage(fmt, hostHeader, tokenHeader);
user.RaiseMessage(fmt,
new string('-', longestHostLen),
new string('-', longestTokenLen)
);
foreach (string host in hosts)
{
string token;
if (Win32Registry.TryGetAuthToken(host, out token))
{
user.RaiseMessage(fmt, host, token);
}
}
}
return Exit.OK;
}

private int addAuthToken(AddAuthTokenOptions opts)
{
if (Uri.CheckHostName(opts.host) != UriHostNameType.Unknown)
{
Win32Registry.SetAuthToken(opts.host, opts.token);
}
else
{
user.RaiseError("Invalid host name: {0}", opts.host);
}
return Exit.OK;
}

private int removeAuthToken(RemoveAuthTokenOptions opts)
{
Win32Registry.SetAuthToken(opts.host, null);
return Exit.OK;
}

private const string hostHeader = "Host";
private const string tokenHeader = "Token";

private IUser user;
private KSPManager manager;
private static readonly ILog log = LogManager.GetLogger(typeof(AuthToken));
}

internal class AuthTokenSubOptions : VerbCommandOptions
{
[VerbOption("list", HelpText = "List auth tokens")]
public CommonOptions ListOptions { get; set; }

[VerbOption("add", HelpText = "Add an auth token")]
public AddAuthTokenOptions AddOptions { get; set; }

[VerbOption("remove", HelpText = "Delete an auth token")]
public RemoveAuthTokenOptions RemoveOptions { get; set; }

[HelpVerbOption]
public string GetUsage(string verb)
{
HelpText ht = HelpText.AutoBuild(this, verb);
// Add a usage prefix line
ht.AddPreOptionsLine(" ");
if (string.IsNullOrEmpty(verb))
{
ht.AddPreOptionsLine("ckan authtoken - Manage authentication tokens");
ht.AddPreOptionsLine($"Usage: ckan authtoken <command> [options]");
}
else
{
ht.AddPreOptionsLine("authtoken " + verb + " - " + GetDescription(verb));
switch (verb)
{
case "add":
ht.AddPreOptionsLine($"Usage: ckan authtoken {verb} [options] host token");
break;
case "remove":
ht.AddPreOptionsLine($"Usage: ckan authtoken {verb} [options] host");
break;
case "list":
ht.AddPreOptionsLine($"Usage: ckan authtoken {verb} [options]");
break;
}
}
return ht;
}
}

internal class AddAuthTokenOptions : CommonOptions
{
[ValueOption(0)] public string host { get; set; }
[ValueOption(1)] public string token { get; set; }
}

internal class RemoveAuthTokenOptions : CommonOptions
{
[ValueOption(0)] public string host { get; set; }
}

}
1 change: 1 addition & 0 deletions Cmdline/CKAN-cmdline.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
<Compile Include="..\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Action\AuthToken.cs" />
<Compile Include="Action\Available.cs" />
<Compile Include="Action\Compare.cs" />
<Compile Include="Action\CompatSubCommand.cs" />
Expand Down
15 changes: 7 additions & 8 deletions Cmdline/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,19 @@ public static int Main(string[] args)
switch (args[0])
{
case "repair":
var repair = new Repair();
return repair.RunSubCommand(new SubCommandOptions(args));
return (new Repair()).RunSubCommand(new SubCommandOptions(args));

case "ksp":
var ksp = new KSP();
return ksp.RunSubCommand(new SubCommandOptions(args));
return (new KSP()).RunSubCommand(new SubCommandOptions(args));

case "compat":
var compat = new CompatSubCommand();
return compat.RunSubCommand(new SubCommandOptions(args));
return (new CompatSubCommand()).RunSubCommand(new SubCommandOptions(args));

case "repo":
var repo = new Repo();
return repo.RunSubCommand(new SubCommandOptions(args));
return (new Repo()).RunSubCommand(new SubCommandOptions(args));

case "authtoken":
return (new AuthToken()).RunSubCommand(new SubCommandOptions(args));
}
}
catch (NoGameInstanceKraken)
Expand Down
3 changes: 3 additions & 0 deletions Cmdline/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ internal class Actions : VerbCommandOptions
[VerbOption("ksp", HelpText = "Manage KSP installs")]
public SubCommandOptions KSP { get; set; }

[VerbOption("authtoken", HelpText = "Manage authentication tokens")]
public AuthTokenSubOptions AuthToken { get; set; }

[VerbOption("compat", HelpText = "Manage KSP version compatibility")]
public SubCommandOptions Compat { get; set; }

Expand Down
86 changes: 86 additions & 0 deletions ConsoleUI/AuthTokenAddDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using CKAN.ConsoleUI.Toolkit;

namespace CKAN.ConsoleUI {

/// <summary>
/// Popup for adding a new authentication token.
/// </summary>
public class AuthTokenAddDialog : ConsoleDialog {

/// <summary>
/// Initialize the popup.
/// </summary>
public AuthTokenAddDialog() : base()
{
CenterHeader = () => "Create Authentication Key";

int top = (Console.WindowHeight - height) / 2;
SetDimensions(6, top, -6, top + height - 1);

int l = GetLeft(),
r = GetRight(),
t = GetTop(),
b = GetBottom();

AddObject(new ConsoleLabel(
l + 2, t + 2, l + 2 + labelW,
() => "Host:",
() => ConsoleTheme.Current.PopupBg,
() => ConsoleTheme.Current.PopupFg
));

hostEntry = new ConsoleField(
l + 2 + labelW + wPad, t + 2, r - 3
) {
GhostText = () => "<Enter a host name>"
};
AddObject(hostEntry);

AddObject(new ConsoleLabel(
l + 2, t + 4, l + 2 + labelW,
() => "Token:",
() => ConsoleTheme.Current.PopupBg,
() => ConsoleTheme.Current.PopupFg
));

tokenEntry = new ConsoleField(
l + 2 + labelW + wPad, t + 4, r - 3
) {
GhostText = () => "<Enter an authentication token>"
};
AddObject(tokenEntry);

AddTip("Esc", "Cancel");
AddBinding(Keys.Escape, (object sender) => false);

AddTip("Enter", "Accept", validKey);
AddBinding(Keys.Enter, (object sender) => {
if (validKey()) {
Win32Registry.SetAuthToken(hostEntry.Value, tokenEntry.Value);
return false;
} else {
// Don't close window on Enter unless adding a key
return true;
}
});
}

private bool validKey()
{
string token;
return hostEntry.Value.Length > 0
&& tokenEntry.Value.Length > 0
&& Uri.CheckHostName(hostEntry.Value) != UriHostNameType.Unknown
&& !Win32Registry.TryGetAuthToken(hostEntry.Value, out token);
}

private ConsoleField hostEntry;
private ConsoleField tokenEntry;

private const int wPad = 2;
private const int labelW = 6;
private const int height = 7;
}

}
Loading

0 comments on commit bb02892

Please sign in to comment.