Skip to content

Commit

Permalink
Hash Passwords
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBetteridge committed Jan 12, 2020
1 parent 00f68aa commit 48d9b3c
Show file tree
Hide file tree
Showing 13 changed files with 133 additions and 171 deletions.
1 change: 1 addition & 0 deletions Connect4.Core/Connect4.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.0" />
<PackageReference Include="BCrypt.Net-Next.StrongName" Version="3.2.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc5" />
</ItemGroup>

Expand Down
6 changes: 4 additions & 2 deletions Connect4.Core/Controllers/MakeMoveController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ public class MakeMoveController : ControllerBase
{
private readonly Database _database;
private readonly BotCreator _botCreator;
private readonly PasswordHashing _passwordHashing;

public MakeMoveController(Database database, BotCreator botCreator)
public MakeMoveController(Database database, BotCreator botCreator, PasswordHashing passwordHashing)
{
_database = database;
_botCreator = botCreator;
_passwordHashing = passwordHashing;
}

/// <summary>
Expand Down Expand Up @@ -43,7 +45,7 @@ public async Task<ActionResult> POST([FromBody]MakeMoveViewModel makeMoveViewMod
var player = await _database.LoadPlayer(makeMoveViewModel.PlayerId);
if (player == null) return BadRequest("The player with this ID does not exist");

if (player.Password != makeMoveViewModel.Password)
if (!_passwordHashing.CompareHashes(makeMoveViewModel.Password, player.Password))
{
return Forbid();
}
Expand Down
12 changes: 10 additions & 2 deletions Connect4.Core/Controllers/RegisterController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ public class RegisterController : ControllerBase
{
private readonly Database _database;
private readonly GameCreator _gameCreator;
private readonly PasswordHashing _passwordHashing;

public RegisterController(Database database, GameCreator gameCreator)
public RegisterController(Database database, GameCreator gameCreator, PasswordHashing passwordHashing)
{
_database = database;
_gameCreator = gameCreator;
_passwordHashing = passwordHashing;
}

/// <summary>
Expand All @@ -41,12 +43,18 @@ public async Task<ActionResult<Guid>> POST([FromBody] RegisterTeamViewModel regi
{
// Generate a unique playerID
var player = new Player(registerTeamViewModel.TeamName);
await _database.SavePlayer(player, registerTeamViewModel.Password);
var hashedPassword = _passwordHashing.HashPassword(registerTeamViewModel.Password);
await _database.SavePlayer(player, hashedPassword);

// Reload the player
var reloadedPlayer = await _database.LoadPlayer(player.ID);
if (reloadedPlayer == null) return BadRequest("No player with this ID exists");

if (!_passwordHashing.CompareHashes(registerTeamViewModel.Password, reloadedPlayer.Password))
{
return Forbid();
}

// Create them a game for them to develop against
if (!reloadedPlayer.CurrentGameID.HasValue)
{
Expand Down
16 changes: 16 additions & 0 deletions Connect4.Core/Services/PasswordHashing.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Connect4.Core.Services
{
public class PasswordHashing
{
public bool CompareHashes(string passwordToCheck, string storedPassword)
{
return BCrypt.Net.BCrypt.EnhancedVerify(passwordToCheck, storedPassword);
}

public string HashPassword(string plainTextPassword)
{
var costParameter = 12;
return BCrypt.Net.BCrypt.EnhancedHashPassword(plainTextPassword, costParameter);
}
}
}
1 change: 1 addition & 0 deletions Connect4.Core/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton<Services.Database>();
services.AddSingleton<Services.BotCreator>();
services.AddSingleton<Services.GameCreator>();
services.AddSingleton<Services.PasswordHashing>();

services.AddRazorPages();

Expand Down
69 changes: 39 additions & 30 deletions Connect4.ExampleBot/API.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace Connect4.ExampleBot
{
internal static class API
public class API
{
private readonly HttpClient _httpClient;

public API(Uri serverUri)
{
_httpClient = new HttpClient
{
BaseAddress = serverUri
};
}
/// <summary>
/// Get the current state of the game
/// </summary>
/// <param name="playerID"></param>
/// <param name="serverURL"></param>
/// <returns></returns>
internal static Game GetGame(Guid playerID, string serverURL)
internal async Task<Game> GetGame(Guid playerID)
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(serverURL);

var httpResponseMessage = httpClient.GetAsync($"api/GameState?playerID={playerID}").Result;
var httpResponseMessage = await _httpClient.GetAsync($"/api/GameState/{playerID}");
if (!httpResponseMessage.IsSuccessStatusCode)
{
// Something has gone wrong
var errors = httpResponseMessage.Content.ReadAsStringAsync().Result;
var errors = await httpResponseMessage.Content.ReadAsStringAsync();
throw new Exception(string.Format("Failed to call {0}. Status {1}. Reason {2}. {3}", "GameState", (int)httpResponseMessage.StatusCode, httpResponseMessage.ReasonPhrase, errors));
}
else
{
// All good
var result = httpResponseMessage.Content.ReadAsAsync<Game>().Result;
var result = await httpResponseMessage.Content.ReadAsJsonAsync<Game>();
return result;
}
}
Expand All @@ -36,14 +42,16 @@ internal static Game GetGame(Guid playerID, string serverURL)
/// </summary>
/// <param name="TeamName"></param>
/// <param name="teamPassword"></param>
/// <param name="serverURL"></param>
/// <returns></returns>
internal static Guid RegisterTeam(string TeamName, string teamPassword, string serverURL)
internal async Task<Guid> RegisterTeam(string teamName, string teamPassword)
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(serverURL);
var data = new
{
TeamName = teamName,
Password = teamPassword,
};

var httpResponseMessage = httpClient.PostAsync($"api/Register?teamName={TeamName}&password={teamPassword}", null).Result;
var httpResponseMessage = await _httpClient.PostAsJsonAsync($"/api/Register", data).ConfigureAwait(false);
if (!httpResponseMessage.IsSuccessStatusCode)
{
// Something has gone wrong
Expand All @@ -53,28 +61,30 @@ internal static Guid RegisterTeam(string TeamName, string teamPassword, string s
else
{
// All good
var result = httpResponseMessage.Content.ReadAsAsync<string>().Result;
return new Guid(result);
var result = await httpResponseMessage.Content.ReadAsJsonAsync<Guid>().ConfigureAwait(false);
return result;
}

}

/// <summary>
/// Plays a move. ColumnNumber should be between 0 and 6
/// </summary>
/// <param name="playerID"></param>
/// <param name="serverURL"></param>
/// <param name="columnNumber"></param>
internal static void MakeMove(Guid playerID, string serverURL, int columnNumber, string teamPassword)
internal async Task MakeMove(Guid playerID, int columnNumber, string teamPassword)
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(serverURL);
var data = new
{
playerID = playerID,
columnNumber = columnNumber,
password = teamPassword
};

var httpResponseMessage = httpClient.PostAsync($"api/MakeMove?playerID={playerID}&columnNumber={columnNumber}&password={teamPassword}", null).Result;
var httpResponseMessage = await _httpClient.PostAsJsonAsync($"/api/MakeMove", data);
if (!httpResponseMessage.IsSuccessStatusCode)
{
// Something has gone wrong
var errors = httpResponseMessage.Content.ReadAsStringAsync().Result;
var errors = await httpResponseMessage.Content.ReadAsStringAsync();
throw new Exception(string.Format("Failed to call {0}. Status {1}. Reason {2}. {3}", "MakeMove", (int)httpResponseMessage.StatusCode, httpResponseMessage.ReasonPhrase, errors));
}
}
Expand All @@ -84,21 +94,20 @@ internal static void MakeMove(Guid playerID, string serverURL, int columnNumber,
/// however be swapped (red => yellow and yellow => red)
/// </summary>
/// <param name="playerID"></param>
/// <param name="serverURL"></param>
internal static void NewGame(Guid playerID, string serverURL)
internal async Task NewGame(Guid playerID)
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(serverURL);
var data = new
{
playerId = playerID,
};

var httpResponseMessage = httpClient.PostAsync($"api/NewGame?playerID={playerID}", null).Result;
var httpResponseMessage = await _httpClient.PostAsJsonAsync($"/api/NewGame", data).ConfigureAwait(false);
if (!httpResponseMessage.IsSuccessStatusCode)
{
// Something has gone wrong
var errors = httpResponseMessage.Content.ReadAsStringAsync().Result;
throw new Exception(string.Format("Failed to call {0}. Status {1}. Reason {2}. {3}", "NewGame", (int)httpResponseMessage.StatusCode, httpResponseMessage.ReasonPhrase, errors));
}

}

}
}
6 changes: 0 additions & 6 deletions Connect4.ExampleBot/App.config

This file was deleted.

73 changes: 7 additions & 66 deletions Connect4.ExampleBot/Connect4.ExampleBot.csproj
Original file line number Diff line number Diff line change
@@ -1,71 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D23AC326-B0C5-4D5D-96FA-21D6DB4F8653}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Connect4.ExampleBot</RootNamespace>
<AssemblyName>Connect4.ExampleBot</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="API.cs" />
<Compile Include="Game.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

</Project>
24 changes: 24 additions & 0 deletions Connect4.ExampleBot/HttpClientExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace Connect4.ExampleBot
{
public static class HttpClientExtensions
{
public static Task<HttpResponseMessage> PostAsJsonAsync<T>(this HttpClient httpClient, string url, T data)
{
var dataAsString = JsonConvert.SerializeObject(data);
var content = new StringContent(dataAsString);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return httpClient.PostAsync(url, content);
}

public static async Task<T> ReadAsJsonAsync<T>(this HttpContent content)
{
var dataAsString = await content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(dataAsString);
}
}
}
Loading

0 comments on commit 48d9b3c

Please sign in to comment.