Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET Core refactor #7

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@ WORKDIR /usr/src/app

RUN nuget install NUnit.Console -Version 3.6.0 -OutputDirectory testrunner

RUN apt-get update \
&& apt-get -y install curl libunwind8 gettext apt-transport-https \
&& curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg \
&& mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg \
&& sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-debian-jessie-prod jessie main" > /etc/apt/sources.list.d/dotnetdev.list'
RUN apt-get update \
&& apt-get -y install dotnet-sdk-2.0.0 \
&& export PATH=$PATH:$HOME/dotnet

COPY . /usr/src/app

RUN nuget restore Toolhouse.Monitoring.sln
RUN dotnet restore Toolhouse.Monitoring.sln
RUN msbuild /p:Configuration=Release Toolhouse.Monitoring.sln
CMD [ "mono", "./testrunner/NUnit.ConsoleRunner.3.6.0/tools/nunit3-console.exe", "./Toolhouse.Monitoring.Tests/bin/Release/Toolhouse.Monitoring.Tests.dll" ]
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

using NUnit.Framework;

using Toolhouse.Monitoring.Handlers;

namespace Toolhouse.Monitoring.Tests
{
[TestFixture]
public class AbstractHandlerTest
public class AuthCheckerTests
{
[Test]
public void TestBasicAuthSucceedsWithValidUserAndPassword()
Expand All @@ -17,7 +15,7 @@ public void TestBasicAuthSucceedsWithValidUserAndPassword()
var hashedPassword = "fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9";

Assert.IsTrue(
AbstractHttpHandler.CheckAuthHeader(header, username, hashedPassword)
AuthChecker.CheckAuthHeader(header, username, hashedPassword)
);
}

Expand All @@ -29,7 +27,7 @@ public void TestBasicAuthSucceedsWithEmptyPassword()
var hashedPassword = "";

Assert.IsTrue(
AbstractHttpHandler.CheckAuthHeader(header, username, hashedPassword)
AuthChecker.CheckAuthHeader(header, username, hashedPassword)
);
}

Expand All @@ -40,7 +38,7 @@ public void TestBasicAuthSucceedsWithNoUsernameConfigured()
var username = "";
var hashedPassword = "";
Assert.IsTrue(
AbstractHttpHandler.CheckAuthHeader(header, username, hashedPassword)
AuthChecker.CheckAuthHeader(header, username, hashedPassword)
);
}

Expand All @@ -52,7 +50,7 @@ public void TestBasicAuthFailsWithInvalidUser()
var hashedPassword = "fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9";

Assert.IsFalse(
AbstractHttpHandler.CheckAuthHeader(header, username, hashedPassword)
AuthChecker.CheckAuthHeader(header, username, hashedPassword)
);
}

Expand All @@ -63,7 +61,7 @@ public void TestBasicAuthFailsWithMissingHeader()
var username = "foo";
var hashedPassword = "fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9";

Assert.IsFalse(AbstractHttpHandler.CheckAuthHeader(header, username, hashedPassword));
Assert.IsFalse(AuthChecker.CheckAuthHeader(header, username, hashedPassword));
}
}
}
5 changes: 2 additions & 3 deletions Toolhouse.Monitoring.Tests/Toolhouse.Monitoring.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.6.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.3.6.0\lib\net45\nunit.framework.dll</HintPath>
Expand Down Expand Up @@ -71,14 +70,14 @@
<Otherwise />
</Choose>
<ItemGroup>
<Compile Include="AbstractHandlerTest.cs" />
<Compile Include="HttpRequestMetricsTests.cs" />
<Compile Include="LabelsTests.cs" />
<Compile Include="AuthCheckerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Toolhouse.Monitoring\Toolhouse.Monitoring.csproj">
<Project>{5bb9187c-5743-4b74-af87-00f8fd62c968}</Project>
<Project>{ce343bf6-986f-4494-b043-7c1d3d2f4322}</Project>
<Name>Toolhouse.Monitoring</Name>
</ProjectReference>
</ItemGroup>
Expand Down
19 changes: 11 additions & 8 deletions Toolhouse.Monitoring.sln
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2003
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolhouse.Monitoring", "Toolhouse.Monitoring\Toolhouse.Monitoring.csproj", "{5BB9187C-5743-4B74-AF87-00F8FD62C968}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolhouse.Monitoring.Tests", "Toolhouse.Monitoring.Tests\Toolhouse.Monitoring.Tests.csproj", "{E410CE9B-97A0-459A-8C71-5A43000E8D57}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Toolhouse.Monitoring", "Toolhouse.Monitoring\Toolhouse.Monitoring.csproj", "{CE343BF6-986F-4494-B043-7C1D3D2F4322}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5BB9187C-5743-4B74-AF87-00F8FD62C968}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5BB9187C-5743-4B74-AF87-00F8FD62C968}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5BB9187C-5743-4B74-AF87-00F8FD62C968}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5BB9187C-5743-4B74-AF87-00F8FD62C968}.Release|Any CPU.Build.0 = Release|Any CPU
{E410CE9B-97A0-459A-8C71-5A43000E8D57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E410CE9B-97A0-459A-8C71-5A43000E8D57}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E410CE9B-97A0-459A-8C71-5A43000E8D57}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E410CE9B-97A0-459A-8C71-5A43000E8D57}.Release|Any CPU.Build.0 = Release|Any CPU
{CE343BF6-986F-4494-B043-7C1D3D2F4322}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CE343BF6-986F-4494-B043-7C1D3D2F4322}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CE343BF6-986F-4494-B043-7C1D3D2F4322}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CE343BF6-986F-4494-B043-7C1D3D2F4322}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0DE16BA8-E611-4362-88A0-193E6927EDE6}
EndGlobalSection
EndGlobal
129 changes: 129 additions & 0 deletions Toolhouse.Monitoring/AuthChecker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

namespace Toolhouse.Monitoring
{
public abstract class AuthChecker
{
private static readonly Regex sha256HashRegex = new Regex(@"^[0-9a-f]{64}$");

private readonly string _username;
private readonly string _passwordSha256;

public AuthChecker(string username, string passwordSha256)
{
_username = username;
_passwordSha256 = passwordSha256;
}

public bool CheckAuthentication()
{
var username = GetBasicAuthUsername();
var passwordSha256 = GetBasicAuthPasswordSha256();

if (username == "")
{
// No username is configured.
return true;
}

if (!CheckAuthHeader(GetAuthHeader(), username, passwordSha256))
{
OnInvalidAuthHeader();
return false;
}

return true;
}

public static bool CheckAuthHeader(string authHeader, string username, string passwordSha256)
{
var parsed = ParseBasicAuthHeader(authHeader);
var headerUsername = parsed.Item1;
var headerPassword = parsed.Item2;

var usernameMatches = string.Equals(username, headerUsername, StringComparison.OrdinalIgnoreCase);
var passwordConfigured = !string.IsNullOrEmpty(passwordSha256);
var passwordMatches = (
!passwordConfigured ||
string.Equals(passwordSha256, HashPassword(headerPassword), StringComparison.OrdinalIgnoreCase)
);

return usernameMatches && passwordMatches;
}

/// <returns>
/// A tuple with username and password elements.
/// </returns>
public static Tuple<string, string> ParseBasicAuthHeader(string header)
{
// RFC for Basic Auth: https://tools.ietf.org/html/rfc2617#section-2
// Header looks like this:
// Authorization: Basic <user:pass base64 encoded>

if (header == null || !header.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
{
return Tuple.Create("", "");
}

var userColonPassBytes = Convert.FromBase64String(header.Substring("Basic ".Length).Trim());
var userColonPass = Encoding.UTF8.GetString(userColonPassBytes);

var colonPos = userColonPass.IndexOf(':');
var user = colonPos >= 0 ? userColonPass.Substring(0, colonPos) : userColonPass;
var password = colonPos >= 0 ? userColonPass.Substring(colonPos + 1) : "";

return Tuple.Create(user, password);
}

protected abstract string GetAuthHeader();

/// <returns>
/// Username used for HTTP basic auth.
/// </returns>
protected virtual string GetBasicAuthUsername()
{
return (_username ?? "").Trim();
}

/// <returns>
/// Hex representation of the SHA256 hash of the password to use for basic auth, or
/// an empty string ("") if no password is configured.
/// </returns>
protected virtual string GetBasicAuthPasswordSha256()
{
var hash = (_passwordSha256 ?? "")
.Trim()
.ToLower();

if (hash == "")
{
// Assume no password configured.
return "";
}

if (!sha256HashRegex.IsMatch(hash))
{
throw new Exception(string.Format(
"Invalid password hash specified for {0}: Should be a 64-character hex string",
hash
));
}

return hash;
}

protected virtual void OnInvalidAuthHeader()
{
}

private static string HashPassword(string password)
{
var bytes = Encoding.UTF8.GetBytes(password);
var hashBytes = SHA256.Create().ComputeHash(bytes);
return BitConverter.ToString(hashBytes).Replace("-", "");
}
}
}
13 changes: 5 additions & 8 deletions Toolhouse.Monitoring/Dependencies/DatabaseDependency.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
using System;
using System.Configuration;
using System.Data.SqlClient;
using System.Text.RegularExpressions;

namespace Toolhouse.Monitoring.Dependencies
{
public class DatabaseDependency : IDependency
{
public DatabaseDependency(string name, string connectionStringName)
public DatabaseDependency(string name, string connectionString)
{
this.Name = name;
this.ConnectionStringName = connectionStringName;
this.ConnectionString = connectionString;
}

public string ConnectionStringName
public string ConnectionString
{
get;
private set;
Expand All @@ -27,10 +25,9 @@ public string Name

public DependencyStatus Check()
{
var connectionString = ConfigurationManager.ConnectionStrings[this.ConnectionStringName].ConnectionString;
var builder = new SqlConnectionStringBuilder(connectionString);
var builder = new SqlConnectionStringBuilder(ConnectionString);

using (var conn = new SqlConnection(connectionString))
using (var conn = new SqlConnection(ConnectionString))
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT 1+1";
Expand Down
Loading