Build auth API
This commit is contained in:
commit
dafe603a06
43 changed files with 1153 additions and 0 deletions
67
src/Robware.Api.Auth.Tests/AuthControllerTests.cs
Normal file
67
src/Robware.Api.Auth.Tests/AuthControllerTests.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Robware.Api.Auth.Controllers;
|
||||
using Robware.Api.Auth.Models;
|
||||
using Robware.Auth;
|
||||
using Xunit;
|
||||
|
||||
namespace Robware.Api.Auth.Tests {
|
||||
public class AuthControllerTests {
|
||||
private class TestUser : User {
|
||||
public TestUser(string username, string password) {
|
||||
Username = username;
|
||||
Password = password;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Authenticate_WithSuccessfulLoginRequest_ReturnsUser() {
|
||||
var logger = Substitute.For<ILogger<AuthController>>();
|
||||
var authenticator = Substitute.For<IAuthenticator>();
|
||||
authenticator.Authenticate("username", "password").Returns((AuthenticationResult.Success, new TestUser("username", "password")));
|
||||
|
||||
var request = new LoginRequest {
|
||||
Username = "username",
|
||||
Password = "password"
|
||||
};
|
||||
|
||||
var expectation = new TestUser("username", "password");
|
||||
|
||||
var controller = new AuthController(logger, authenticator);
|
||||
(await controller.Authenticate(request)).Value.Should().BeEquivalentTo(expectation);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Authenticate_WithIncorrectPassword_Returns401() {
|
||||
var logger = Substitute.For<ILogger<AuthController>>();
|
||||
var authenticator = Substitute.For<IAuthenticator>();
|
||||
authenticator.Authenticate("username", "password").Returns((AuthenticationResult.IncorrectPassword, null));
|
||||
|
||||
var request = new LoginRequest {
|
||||
Username = "username",
|
||||
Password = "password"
|
||||
};
|
||||
|
||||
var controller = new AuthController(logger, authenticator);
|
||||
(await controller.Authenticate(request)).Result.Should().BeOfType<UnauthorizedResult>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Authenticate_WithIncorrectPassword_Returns404() {
|
||||
var logger = Substitute.For<ILogger<AuthController>>();
|
||||
var authenticator = Substitute.For<IAuthenticator>();
|
||||
authenticator.Authenticate("username", "password").Returns((AuthenticationResult.NotFound, null));
|
||||
|
||||
var request = new LoginRequest {
|
||||
Username = "username",
|
||||
Password = "password"
|
||||
};
|
||||
|
||||
var controller = new AuthController(logger, authenticator);
|
||||
(await controller.Authenticate(request)).Result.Should().BeOfType<NotFoundResult>();
|
||||
}
|
||||
}
|
||||
}
|
22
src/Robware.Api.Auth.Tests/Robware.Api.Auth.Tests.csproj
Normal file
22
src/Robware.Api.Auth.Tests/Robware.Api.Auth.Tests.csproj
Normal file
|
@ -0,0 +1,22 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="NSubstitute" Version="4.2.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robware.Api.Auth\Robware.Api.Auth.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
53
src/Robware.Api.Auth.sln
Normal file
53
src/Robware.Api.Auth.sln
Normal file
|
@ -0,0 +1,53 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29613.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Robware.Api.Auth", "Robware.Api.Auth\Robware.Api.Auth.csproj", "{D21F1402-6526-4BD7-8ADA-F8DC626D6D5A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robware.Api.Auth.Tests", "Robware.Api.Auth.Tests\Robware.Api.Auth.Tests.csproj", "{88716F73-0264-44D9-970D-4134C644C7EF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robware.Auth", "Robware.Auth\Robware.Auth.csproj", "{8740FE72-12D7-4039-9EB3-0417E529A10E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robware.Auth.Tests", "Robware.Auth.Tests\Robware.Auth.Tests.csproj", "{E229DE31-8DBB-4AED-9461-A04C8DE0F074}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robware.Data", "Robware.Data\Robware.Data.csproj", "{69989FA2-BEE8-491D-97B9-856D4916D154}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "..\build\_build.csproj", "{19A36DA9-BFBF-4988-B7C7-4808D6B57246}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{19A36DA9-BFBF-4988-B7C7-4808D6B57246}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{19A36DA9-BFBF-4988-B7C7-4808D6B57246}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D21F1402-6526-4BD7-8ADA-F8DC626D6D5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D21F1402-6526-4BD7-8ADA-F8DC626D6D5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D21F1402-6526-4BD7-8ADA-F8DC626D6D5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D21F1402-6526-4BD7-8ADA-F8DC626D6D5A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{88716F73-0264-44D9-970D-4134C644C7EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{88716F73-0264-44D9-970D-4134C644C7EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{88716F73-0264-44D9-970D-4134C644C7EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{88716F73-0264-44D9-970D-4134C644C7EF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8740FE72-12D7-4039-9EB3-0417E529A10E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8740FE72-12D7-4039-9EB3-0417E529A10E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8740FE72-12D7-4039-9EB3-0417E529A10E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8740FE72-12D7-4039-9EB3-0417E529A10E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E229DE31-8DBB-4AED-9461-A04C8DE0F074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E229DE31-8DBB-4AED-9461-A04C8DE0F074}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E229DE31-8DBB-4AED-9461-A04C8DE0F074}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E229DE31-8DBB-4AED-9461-A04C8DE0F074}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{69989FA2-BEE8-491D-97B9-856D4916D154}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{69989FA2-BEE8-491D-97B9-856D4916D154}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{69989FA2-BEE8-491D-97B9-856D4916D154}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{69989FA2-BEE8-491D-97B9-856D4916D154}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {75A89B46-CAE8-45F8-9BEF-3B7A6FD0BC72}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
35
src/Robware.Api.Auth/Controllers/AuthController.cs
Normal file
35
src/Robware.Api.Auth/Controllers/AuthController.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Robware.Api.Auth.Models;
|
||||
using Robware.Auth;
|
||||
|
||||
namespace Robware.Api.Auth.Controllers {
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class AuthController : ControllerBase {
|
||||
private readonly ILogger<AuthController> _logger;
|
||||
private readonly IAuthenticator _authenticator;
|
||||
|
||||
public AuthController(ILogger<AuthController> logger, IAuthenticator authenticator) {
|
||||
_logger = logger;
|
||||
_authenticator = authenticator;
|
||||
}
|
||||
|
||||
[HttpPost(nameof(Authenticate))]
|
||||
public async Task<ActionResult<User>> Authenticate(LoginRequest request) {
|
||||
var (result, user) = await _authenticator.Authenticate(request.Username, request.Password);
|
||||
switch (result) {
|
||||
case AuthenticationResult.Success:
|
||||
return user;
|
||||
case AuthenticationResult.NotFound:
|
||||
return NotFound();
|
||||
case AuthenticationResult.IncorrectPassword:
|
||||
return Unauthorized();
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
6
src/Robware.Api.Auth/Models/LoginRequest.cs
Normal file
6
src/Robware.Api.Auth/Models/LoginRequest.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Robware.Api.Auth.Models {
|
||||
public class LoginRequest {
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
12
src/Robware.Api.Auth/Program.cs
Normal file
12
src/Robware.Api.Auth/Program.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Robware.Api.Auth {
|
||||
public class Program {
|
||||
public static void Main(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder => {
|
||||
webBuilder.UseStartup<Startup>();
|
||||
}).Build().Run();
|
||||
}
|
||||
}
|
28
src/Robware.Api.Auth/Properties/launchSettings.json
Normal file
28
src/Robware.Api.Auth/Properties/launchSettings.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:61069",
|
||||
"sslPort": 44309
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Robware.Api.Auth": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "weatherforecast",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
||||
}
|
||||
}
|
||||
}
|
13
src/Robware.Api.Auth/Robware.Api.Auth.csproj
Normal file
13
src/Robware.Api.Auth/Robware.Api.Auth.csproj
Normal file
|
@ -0,0 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robware.Auth\Robware.Auth.csproj" />
|
||||
<ProjectReference Include="..\Robware.Data\Robware.Data.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
44
src/Robware.Api.Auth/Startup.cs
Normal file
44
src/Robware.Api.Auth/Startup.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Robware.Auth;
|
||||
using Robware.Data;
|
||||
|
||||
namespace Robware.Api.Auth {
|
||||
public class Startup {
|
||||
public Startup(IConfiguration configuration) {
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services) {
|
||||
services.AddControllers();
|
||||
|
||||
services.AddSingleton<ICryptographyProvider, CryptographyProvider>()
|
||||
.AddSingleton<IAuthenticator, Authenticator>()
|
||||
.AddSingleton<IDatabaseProvider>(new MySQLDatabaseProvider(Configuration.GetConnectionString("database")))
|
||||
.AddSingleton<IUsers, UserRepository>();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
|
||||
if (env.IsDevelopment()) {
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints => {
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
12
src/Robware.Api.Auth/appsettings.Development.json
Normal file
12
src/Robware.Api.Auth/appsettings.Development.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"database": "Server=localhost;User ID=user;Password=pass;Database=db"
|
||||
}
|
||||
}
|
20
src/Robware.Api.Auth/appsettings.json
Normal file
20
src/Robware.Api.Auth/appsettings.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"Kestrel": {
|
||||
"EndPoints": {
|
||||
"Http": {
|
||||
"Url": "http://0.0.0.0:5003"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStrings": {
|
||||
"database": "<DatabaseConnectionString>"
|
||||
}
|
||||
}
|
50
src/Robware.Auth.Tests/AuthenticatorTests.cs
Normal file
50
src/Robware.Auth.Tests/AuthenticatorTests.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using Robware.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace Robware.Auth.Tests {
|
||||
public class AuthenticatorTests {
|
||||
[Fact]
|
||||
public async Task Authenticate_ForUserThatExistsWithCorrectPassword_ReturnsOkResultWithUser() {
|
||||
var users = Substitute.For<IUsers>();
|
||||
var crypto = Substitute.For<ICryptographyProvider>();
|
||||
crypto.Encrypt("password").Returns("password");
|
||||
|
||||
var user = new TestUser("test", "password");
|
||||
users.GetByEmail("test").Returns(user);
|
||||
|
||||
var auth = new Authenticator(users, crypto);
|
||||
(await auth.Authenticate("test", "password")).Should().BeEquivalentTo((AuthenticationResult.Success, user));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Authenticate_ForUserThatExistsWithIncorrectPassword_ReturnsIncorrectPassword() {
|
||||
var users = Substitute.For<IUsers>();
|
||||
var crypto = Substitute.For<ICryptographyProvider>();
|
||||
crypto.Encrypt("password").Returns("password");
|
||||
|
||||
var user = new TestUser("test", "password");
|
||||
users.GetByEmail("test").Returns(user);
|
||||
|
||||
var auth = new Authenticator(users, crypto);
|
||||
|
||||
(await auth.Authenticate("test", "wrong")).Should().BeEquivalentTo((AuthenticationResult.IncorrectPassword, null as User));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Authenticate_ForUserThatDoesntExist_ReturnsNotFound() {
|
||||
var users = Substitute.For<IUsers>();
|
||||
users.GetByEmail("test").Throws(new UserNotFoundException(""));
|
||||
|
||||
var crypto = Substitute.For<ICryptographyProvider>();
|
||||
crypto.Encrypt("password").Returns("password");
|
||||
|
||||
var auth = new Authenticator(users, crypto);
|
||||
|
||||
(await auth.Authenticate("test", "password")).Should().BeEquivalentTo((AuthenticationResult.NotFound, null as User));
|
||||
}
|
||||
}
|
||||
}
|
12
src/Robware.Auth.Tests/CryoptographyProviderTests.cs
Normal file
12
src/Robware.Auth.Tests/CryoptographyProviderTests.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace Robware.Auth.Tests {
|
||||
public class CryoptographyProviderTests {
|
||||
[Fact]
|
||||
public void Encrypt_WithInput_ReturnsHash() {
|
||||
var provider = new CryptographyProvider();
|
||||
provider.Encrypt("password").Should().Be("5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8");
|
||||
}
|
||||
}
|
||||
}
|
23
src/Robware.Auth.Tests/Robware.Auth.Tests.csproj
Normal file
23
src/Robware.Auth.Tests/Robware.Auth.Tests.csproj
Normal file
|
@ -0,0 +1,23 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="NSubstitute" Version="4.2.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robware.Auth\Robware.Auth.csproj" />
|
||||
<ProjectReference Include="..\Robware.Data\Robware.Data.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
8
src/Robware.Auth.Tests/TestUser.cs
Normal file
8
src/Robware.Auth.Tests/TestUser.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Robware.Auth.Tests {
|
||||
internal class TestUser : User {
|
||||
public TestUser(string username, string password) {
|
||||
Username = username;
|
||||
Password = password;
|
||||
}
|
||||
}
|
||||
}
|
31
src/Robware.Auth/Authenticator.cs
Normal file
31
src/Robware.Auth/Authenticator.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Robware.Auth {
|
||||
public class Authenticator : IAuthenticator {
|
||||
private readonly IUsers _users;
|
||||
private readonly ICryptographyProvider _crypto;
|
||||
|
||||
public Authenticator(IUsers users, ICryptographyProvider crypto) {
|
||||
_users = users;
|
||||
_crypto = crypto;
|
||||
}
|
||||
|
||||
public async Task<(AuthenticationResult Result, User User)> Authenticate(string username, string password) {
|
||||
try {
|
||||
var user = await _users.GetByEmail(username);
|
||||
return _crypto.Encrypt(password) == user.Password ? (AuthenticationResult.Success, user) : (AuthenticationResult.IncorrectPassword, null);
|
||||
}
|
||||
catch (UserNotFoundException) {
|
||||
return (AuthenticationResult.NotFound, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum AuthenticationResult {
|
||||
Unknown,
|
||||
Success,
|
||||
NotFound,
|
||||
IncorrectPassword
|
||||
}
|
||||
}
|
19
src/Robware.Auth/CryptographyProvider.cs
Normal file
19
src/Robware.Auth/CryptographyProvider.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Robware.Auth {
|
||||
public class CryptographyProvider : ICryptographyProvider {
|
||||
public string Encrypt(string input) {
|
||||
using (var sha256 = SHA256.Create()) {
|
||||
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(input));
|
||||
|
||||
var builder = new StringBuilder();
|
||||
foreach (var b in hash)
|
||||
builder.Append(b.ToString("x2"));
|
||||
var hashString = builder.ToString();
|
||||
|
||||
return hashString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
src/Robware.Auth/IAuthenticator.cs
Normal file
7
src/Robware.Auth/IAuthenticator.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Robware.Auth {
|
||||
public interface IAuthenticator {
|
||||
Task<(AuthenticationResult Result, User User)> Authenticate(string username, string password);
|
||||
}
|
||||
}
|
5
src/Robware.Auth/ICryptographyProvider.cs
Normal file
5
src/Robware.Auth/ICryptographyProvider.cs
Normal file
|
@ -0,0 +1,5 @@
|
|||
namespace Robware.Auth {
|
||||
public interface ICryptographyProvider {
|
||||
string Encrypt(string input);
|
||||
}
|
||||
}
|
7
src/Robware.Auth/IUsers.cs
Normal file
7
src/Robware.Auth/IUsers.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Robware.Auth {
|
||||
public interface IUsers {
|
||||
Task<User> GetByEmail(string email);
|
||||
}
|
||||
}
|
7
src/Robware.Auth/Robware.Auth.csproj
Normal file
7
src/Robware.Auth/Robware.Auth.csproj
Normal file
|
@ -0,0 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
6
src/Robware.Auth/User.cs
Normal file
6
src/Robware.Auth/User.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Robware.Auth {
|
||||
public class User {
|
||||
public string Username { get; protected set; }
|
||||
public string Password { get; protected set; }
|
||||
}
|
||||
}
|
9
src/Robware.Auth/UserNotFoundException.cs
Normal file
9
src/Robware.Auth/UserNotFoundException.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace Robware.Auth {
|
||||
public class UserNotFoundException : Exception {
|
||||
public UserNotFoundException(string username) : base("Could not find user " + username) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
11
src/Robware.Data/DatabaseUser.cs
Normal file
11
src/Robware.Data/DatabaseUser.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using Robware.Auth;
|
||||
using Robware.Data.States;
|
||||
|
||||
namespace Robware.Data {
|
||||
public class DatabaseUser : User {
|
||||
public DatabaseUser(UserState state) {
|
||||
Username = state.User_Email;
|
||||
Password = state.User_Password;
|
||||
}
|
||||
}
|
||||
}
|
7
src/Robware.Data/IDatabaseProvider.cs
Normal file
7
src/Robware.Data/IDatabaseProvider.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
using System.Data;
|
||||
|
||||
namespace Robware.Data {
|
||||
public interface IDatabaseProvider {
|
||||
IDbConnection NewConnection();
|
||||
}
|
||||
}
|
12
src/Robware.Data/MySQLDatabaseProvider.cs
Normal file
12
src/Robware.Data/MySQLDatabaseProvider.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System.Data;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Robware.Data {
|
||||
public class MySQLDatabaseProvider : IDatabaseProvider {
|
||||
private readonly string _connectionString;
|
||||
|
||||
public MySQLDatabaseProvider(string connectionString) => _connectionString = connectionString;
|
||||
|
||||
public IDbConnection NewConnection() => new MySqlConnection(_connectionString);
|
||||
}
|
||||
}
|
16
src/Robware.Data/Robware.Data.csproj
Normal file
16
src/Robware.Data/Robware.Data.csproj
Normal file
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapper" Version="2.0.35" />
|
||||
<PackageReference Include="MySqlConnector" Version="0.63.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robware.Auth\Robware.Auth.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
10
src/Robware.Data/States/UserState.cs
Normal file
10
src/Robware.Data/States/UserState.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Robware.Data.States {
|
||||
public class UserState {
|
||||
public string User_Id { get; set; }
|
||||
public string User_Email { get; set; }
|
||||
public string User_Password { get; set; }
|
||||
public string User_Created { get; set; }
|
||||
public string User_Deleted { get; set; }
|
||||
public string Group_Id { get; set; }
|
||||
}
|
||||
}
|
29
src/Robware.Data/UserRepository.cs
Normal file
29
src/Robware.Data/UserRepository.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using Robware.Auth;
|
||||
using Robware.Data.States;
|
||||
|
||||
namespace Robware.Data {
|
||||
public class UserRepository : IUsers {
|
||||
private readonly IDatabaseProvider _dbProvider;
|
||||
|
||||
public UserRepository(IDatabaseProvider dbProvider) {
|
||||
_dbProvider = dbProvider;
|
||||
}
|
||||
|
||||
public async Task<User> GetByEmail(string email) {
|
||||
const string query = "SELECT * FROM users WHERE user_email=@email";
|
||||
|
||||
using (var connection = _dbProvider.NewConnection()) {
|
||||
connection.Open();
|
||||
var result = await connection.QueryAsync<UserState>(query, new { email });
|
||||
|
||||
if (!result.Any())
|
||||
throw new UserNotFoundException(email);
|
||||
|
||||
return new DatabaseUser(result.Single());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue