Skip to content

Commit

Permalink
Default totp settings (#148)
Browse files Browse the repository at this point in the history
* Move totp entry handling to separate class
* Add defaults. Reduce dependency on HasSettings
* Add tests
  • Loading branch information
robinvanpoppel committed May 4, 2020
1 parent 522e26c commit fd9cddf
Show file tree
Hide file tree
Showing 14 changed files with 304 additions and 308 deletions.
61 changes: 61 additions & 0 deletions KeeTrayTOTP.Tests/TOTPEntryValidatorTests.cs
@@ -0,0 +1,61 @@
using FluentAssertions;
using KeePassLib;
using KeePassLib.Security;
using KeeTrayTOTP.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace KeeTrayTOTP.Tests
{
[TestClass]
public class TOTPEntryValidatorTests
{
private readonly Mock<ISettings> _settingsMock;
private readonly TOTPEntryValidator _sut;
private readonly PwEntry _entry;

public TOTPEntryValidatorTests()
{
this._settingsMock = new Mock<ISettings>(MockBehavior.Strict);
this._settingsMock.Setup(c => c.TOTPSettingsStringName).Returns("TOTP Settings");
this._settingsMock.Setup(c => c.TOTPSeedStringName).Returns("TOTP Seed");
this._sut = new TOTPEntryValidator(_settingsMock.Object);
this._entry = new PwEntry(true, false);
}

[TestMethod]
public void CanGenerateOtp_EntryHasNoSettingsOrSeed_ReturnsFalse()
{
_entry.Strings.Set(_settingsMock.Object.TOTPSeedStringName, new ProtectedString(false, "ABCDEFG"));
var act = _sut.CanGenerateTOTP(_entry);

act.Should().BeTrue();
}

[TestMethod]
public void CanGenerateOtp_EntryOnlyHasSeed_ReturnsTrue()
{
var act = _sut.CanGenerateTOTP(_entry);

act.Should().BeFalse();
}

[TestMethod]
public void SettingsGet_EntryHasNoSettings_ReturnsDefaultSettings()
{
var act = _sut.SettingsGet(_entry);

act.Should().BeEquivalentTo("30", "6");
}

[TestMethod]
public void SettingsGet_EntryHasSettings_ShouldReturnOwnSettings()
{
_entry.Strings.Set(_settingsMock.Object.TOTPSettingsStringName, new ProtectedString(false, "60;7;https://pool.ntp.org"));

var act = _sut.SettingsGet(_entry);

act.Should().BeEquivalentTo("60", "7", "https://pool.ntp.org");
}
}
}
7 changes: 3 additions & 4 deletions KeeTrayTOTP.Tests/TrayTOTP_ColumnproviderTests.cs
Expand Up @@ -27,9 +27,9 @@ public TrayTOTP_ColumnproviderTests()
[DataRow(ValidSeed, ValidSettings, "TOTP Enabled")]
[DataRow(ValidSeed, ";6", "Error, bad settings!")]
[DataRow(ValidSeed, "30", "Error, bad settings!")]
[DataRow(ValidSeed, null, "Error, storage!")]
[DataRow(ValidSeed, null, "TOTP Enabled")]
[DataRow(InvalidSeed, ValidSettings, "Error, bad seed!")]
[DataRow(null, ValidSettings, "Error, storage!")]
[DataRow(null, ValidSettings, "Error, no seed!")]
[DataRow(null, null, "")]
[DataTestMethod]
public void GetCellDataStatus_ShouldReturnExpectedValues(string seed, string settings, string expected)
Expand All @@ -52,9 +52,8 @@ public void GetCellDataStatus_ShouldReturnExpectedValues(string seed, string set

[DataRow(ValidSeed, ";6", "Error, bad settings!")]
[DataRow(ValidSeed, "30", "Error, bad settings!")]
[DataRow(ValidSeed, null, "Error, storage!")]
[DataRow(InvalidSeed, ValidSettings, "Error, bad seed!")]
[DataRow(null, ValidSettings, "Error, storage!")]
[DataRow(null, ValidSettings, "Error, no seed!")]
[DataRow(null, null, "")]
[DataTestMethod]
public void GetCellDataCode_ShouldReturnExpectedValues(string seed, string settings, string expected)
Expand Down
13 changes: 6 additions & 7 deletions KeeTrayTOTP/FormTimeCorrection.cs
@@ -1,7 +1,9 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using KeePass.UI;
using KeeTrayTOTP.Helpers;
using KeeTrayTOTP.Libraries;

namespace KeeTrayTOTP
Expand Down Expand Up @@ -56,14 +58,11 @@ private void FormTimeCorrection_Load(object sender, EventArgs e)
{
foreach (var pe in _plugin.PluginHost.MainWindow.ActiveDatabase.RootGroup.GetEntries(true))
{
if (_plugin.SettingsCheck(pe))
string[] settings = _plugin.TOTPEntryValidator.SettingsGet(pe);
bool validUrl;
if (_plugin.TOTPEntryValidator.SettingsValidate(pe, out validUrl) && validUrl && !ComboBoxUrlTimeCorrection.Items.Contains(settings[2]) && _plugin.TimeCorrections[settings[2]] == null)
{
string[] settings = _plugin.SettingsGet(pe);
bool validUrl;
if (_plugin.SettingsValidate(pe, out validUrl) && validUrl && !ComboBoxUrlTimeCorrection.Items.Contains(settings[2]) && _plugin.TimeCorrections[settings[2]] == null)
{
ComboBoxUrlTimeCorrection.Items.Add(settings[2]);
}
ComboBoxUrlTimeCorrection.Items.Add(settings[2]);
}
}
}
Expand Down
185 changes: 185 additions & 0 deletions KeeTrayTOTP/Helpers/TOTPEntryValidator.cs
@@ -0,0 +1,185 @@
using KeePassLib;
using KeePassLib.Security;
using KeeTrayTOTP.Libraries;
using System;
using System.Collections.ObjectModel;

namespace KeeTrayTOTP.Helpers
{
// TODO: stil not the right name: Also gets settings?
public class TOTPEntryValidator
{
private static readonly ReadOnlyCollection<string> AllowedLengths = new ReadOnlyCollection<string>(new[] { "6", "7", "8", "S" });
private readonly ISettings _settings;

public TOTPEntryValidator(ISettings settings)
{
this._settings = settings;
}

/// <summary>
/// Check if specified Entry's Interval and Length are valid.
/// </summary>
/// <returns>Error(s) while validating Interval or Length.</returns>
internal bool SettingsValidate(PwEntry entry)
{
bool validInterval;
bool validLength;
bool validUrl;

return SettingsValidate(entry, out validInterval, out validLength, out validUrl);
}

/// <summary>
/// Check if specified Entry's Interval and Length are valid. The URL status is available as an out boolean.
/// </summary>
/// <param name="isUrlValid">Url Validity.</param>
/// <returns>Error(s) while validating Interval or Length.</returns>
internal bool SettingsValidate(PwEntry entry, out bool isUrlValid)
{
bool validInterval; bool validLength; //Dummies

return SettingsValidate(entry, out validInterval, out validLength, out isUrlValid);
}

/// <summary>
/// Check if specified Entry's Interval and Length are valid. All settings statuses are available as out booleans.
/// </summary>
/// <param name="isIntervalValid">Interval Validity.</param>
/// <param name="isLengthValid">Length Validity.</param>
/// <param name="isUrlValid">Url Validity.</param>
/// <returns>Error(s) while validating Interval or Length.</returns>
internal bool SettingsValidate(PwEntry entry, out bool isIntervalValid, out bool isLengthValid, out bool isUrlValid)
{
bool settingsValid;
try
{
string[] settings = SettingsGet(entry);

isIntervalValid = IntervalIsValid(settings);
isLengthValid = LengthIsValid(settings);

settingsValid = isIntervalValid && isLengthValid;

isUrlValid = UrlIsValid(settings);
}
catch (Exception)
{
isIntervalValid = false;
isLengthValid = false;
isUrlValid = false;
settingsValid = false;
}
return settingsValid;
}

/// <summary>
/// Get the entry's Settings, or return defaults
/// </summary>
/// <returns>String Array (Interval, Length, Url).</returns>
internal string[] SettingsGet(PwEntry entry)
{
return HasExplicitSettings(entry)
? entry.Strings.Get(_settings.TOTPSettingsStringName).ReadString().Split(';')
: new[] { "30", "6" };
}

/// <summary>
/// Check if the specified Entry contains a Seed.
/// </summary>
/// <returns>Presence of the Seed.</returns>
internal bool HasSeed(PwEntry entry)
{
return entry.Strings.Exists(_settings.TOTPSeedStringName);
}

/// <summary>
/// Validates the entry's Seed making sure it's a valid Base32 string.
/// </summary>
/// <returns>Validity of the Seed's characters for Base32 format.</returns>
internal bool SeedValidate(PwEntry entry)
{
return SeedGet(entry).ReadString().ExtWithoutSpaces().IsBase32();
}

/// <summary>
/// Validates the entry's Seed making sure it's a valid Base32 string. Invalid characters are available as out string.
/// </summary>
/// <param name="invalidCharacters">Password Entry.</param>
/// <returns>Validity of the Seed's characters.</returns>
internal bool SeedValidate(PwEntry entry, out string invalidCharacters)
{
return SeedGet(entry).ReadString().ExtWithoutSpaces().IsBase32(out invalidCharacters);
}

/// <summary>
/// Get the entry's Seed using the string name specified in the settings (or default).
/// </summary>
/// <returns>Protected Seed.</returns>
internal ProtectedString SeedGet(PwEntry entry)
{
return entry.Strings.Get(_settings.TOTPSeedStringName);
}

internal bool CanGenerateTOTP(PwEntry entry)
{
return HasSeed(entry) && SettingsValidate(entry) && SeedValidate(entry);
}

/// <summary>
/// Check if specified Entry contains Settings that are not null.
/// </summary>
/// <returns>Presence of Settings.</returns>
internal bool HasExplicitSettings(PwEntry entry)
{
return entry.Strings.Exists(_settings.TOTPSettingsStringName);
}

private static bool UrlIsValid(string[] settings)
{
if (settings.Length < 3)
{
return false;
}

return settings[2].StartsWith("http://") || settings[2].StartsWith("https://");
}

private static bool LengthIsValid(string[] settings)
{
if (settings.Length < 2)
{
return false;
}

if (!AllowedLengths.Contains(settings[1]))
{
return false;
}

return true;
}

private static bool IntervalIsValid(string[] settings)
{
if (settings.Length == 0)
{
return false;
}

short interval;
if (!short.TryParse(settings[0], out interval))
{
return false;
}

if (interval < 0)
{
return false;
}

return true;
}

}
}
4 changes: 4 additions & 0 deletions KeeTrayTOTP/ISettings.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace KeeTrayTOTP
{
Expand All @@ -8,7 +9,9 @@ public interface ISettings
string AutoTypeFieldName { get; set; }
bool EntryContextCopyVisible { get; set; }
bool EntryContextSetupVisible { get; set; }
int EntryListRefreshRate { get; }
bool FirstInstallShown { get; set; }
bool LegacyTrayMenuProviderEnable { get; set; }
bool NotifyContextVisible { get; set; }
bool TimeCorrectionEnable { get; set; }
IEnumerable<string> TimeCorrectionList { get; set; }
Expand All @@ -17,6 +20,7 @@ public interface ISettings
bool TOTPColumnTimerVisible { get; set; }
string TOTPSeedStringName { get; set; }
string TOTPSettingsStringName { get; set; }
int TrimTextLength { get; }
bool TrimTrayText { get; set; }
}
}
3 changes: 2 additions & 1 deletion KeeTrayTOTP/KeeTrayTOTP.csproj
Expand Up @@ -76,11 +76,12 @@
<Compile Include="Helpers\DocumentExtensions.cs" />
<Compile Include="Helpers\MenuItemHelper.cs" />
<Compile Include="Helpers\ImageExtensions.cs" />
<Compile Include="Helpers\TOTPEntryValidator.cs" />
<Compile Include="ISettings.cs" />
<Compile Include="Menu\EntryMenuItemProvider.cs" />
<Compile Include="Menu\MainMenuItemProvider.cs" />
<Compile Include="Menu\MenuItemProvider.cs" />
<Compile Include="Menu\LegacyTrayMenuItemProvider.cs" />
<Compile Include="ISettings.cs" />
<Compile Include="Libraries\DropDownLocationCalculator.cs" />
<Compile Include="FormAbout.cs">
<SubType>Form</SubType>
Expand Down

0 comments on commit fd9cddf

Please sign in to comment.