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

LoginService and Controller #47

Merged
merged 10 commits into from
Feb 24, 2024
Merged
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
2 changes: 1 addition & 1 deletion PWManager.Application/Context/IApplicationEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface IApplicationEnvironment {

public User? CurrentUser { get; set; }

public string? CurrentGroup { get; set; }
public Group? CurrentGroup { get; set; }

public string? EncryptionKey { get; set; }
}
5 changes: 5 additions & 0 deletions PWManager.Application/Services/Interfaces/ILoginService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace PWManager.Application.Services.Interfaces {
public interface ILoginService {
public void Login(string username, string password, string dbPath);
}
}
4 changes: 2 additions & 2 deletions PWManager.CLI/Abstractions/CliEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ public class CliEnvironment : IApplicationEnvironment {
public bool IsDevelopmentMode { get; init; } = true;

public bool RunningSession { get; set; } = false;
public string Prompt => $"{CurrentUser.UserName} ({CurrentGroup}) $";
public string Prompt => $"{CurrentUser.UserName} ({CurrentGroup.Identifier}) $";

public User? CurrentUser { get; set; }

public string? CurrentGroup { get; set; }
public Group? CurrentGroup { get; set; }

public string? EncryptionKey { get; set; }
}
24 changes: 24 additions & 0 deletions PWManager.CLI/Abstractions/ConfigFileHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using PWManager.Application.Exceptions;

namespace PWManager.CLI.Abstractions {
public class ConfigFileHandler {
public static string ReadDefaultFile() {
var defaultFilePath = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);

try {
return File.ReadAllText(Path.Combine(defaultFilePath, "last.txt"));
} catch (IOException e) {
throw new UserFeedbackException("The file could not be read");
}
}
public static void WriteDefaultFile(string username, string path) {
var defaultFilePath = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);

try {
File.WriteAllText(Path.Combine(defaultFilePath, "last.txt"), username + '\n' + path);
} catch (IOException e) {
throw new UserFeedbackException("The file could not be written");
}
}
}
}
2 changes: 2 additions & 0 deletions PWManager.CLI/Controllers/InitController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using PWManager.Application.Context;
using PWManager.Application.Exceptions;
using PWManager.Application.Services.Interfaces;
using PWManager.CLI.Abstractions;
using PWManager.CLI.Enums;
using PWManager.CLI.Interfaces;
using Sharprompt;
Expand Down Expand Up @@ -39,6 +40,7 @@ public ExitCondition Handle(string[] args) {
}

_dbInit.InitDatabase(path, name, password);
ConfigFileHandler.WriteDefaultFile(name, path);
Console.WriteLine("Created your database! Enjoy");

return ExitCondition.EXIT;
Expand Down
74 changes: 74 additions & 0 deletions PWManager.CLI/Controllers/LoginController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using PWManager.Application.Context;
using PWManager.Application.Exceptions;
using PWManager.Application.Services.Interfaces;
using PWManager.CLI.Abstractions;
using PWManager.CLI.Enums;
using PWManager.CLI.Interfaces;
using Sharprompt;
using System.IO;

namespace PWManager.CLI.Controllers {
public class LoginController : IController {
private readonly IApplicationEnvironment _env;
private readonly ILoginService _loginService;
public LoginController(IApplicationEnvironment env, ILoginService loginService) {
_env = env;
_loginService = loginService;
}
public ExitCondition Handle(string[] args) {
if (_env.RunningSession) {
throw new UserFeedbackException("Command not available in a session!");
}

(var username, var path) = ParseArgs(args);

var lastUser = ConfigFileHandler.ReadDefaultFile();
if (String.IsNullOrWhiteSpace(username)) {
username = lastUser.Split('\n')[0];
}
if (String.IsNullOrWhiteSpace(path)) {
path = lastUser.Split('\n')[1];
}

var pass = Prompt.Password("Enter your password");
_loginService.Login(username, pass, path);

ConfigFileHandler.WriteDefaultFile(username, path);
Console.WriteLine($"Welcome {username} :)");

_env.RunningSession = true;
return ExitCondition.CONTINUE;
}

public (string, string) ParseArgs(string[] args) {
string username = "";
string path = "";

int basepointer = 0;
while (basepointer < args.Length) {
if ((args[basepointer].Equals("-u") || args[basepointer].Equals("--username"))) {
if ((args.Length - basepointer <= 1) || args[basepointer + 1].StartsWith('-')) {
username = AskForInput("Please enter your username");
} else {
username = args[basepointer + 1];
basepointer++;
}
} else if ((args[basepointer].Equals("-d") || args[basepointer].Equals("--directory"))) {
if ((args.Length - basepointer <= 1) || args[basepointer + 1].StartsWith('-')) {
path = AskForInput("Please enter the location of your databasefile");
} else {
path = args[basepointer + 1];
basepointer++;
}
}
basepointer++;
}

return (username, path);
}

public virtual string AskForInput(string prompt) {
return Prompt.Input<string>(prompt);
}
}
}
2 changes: 2 additions & 0 deletions PWManager.CLI/ExtensionMethods/RunnerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ internal static class RunnerExtensions {

public static void AddControllers(this IServiceCollection services) {
services.AddTransient<HelpController>();
services.AddTransient<LoginController>();
services.AddTransient<InitController>();
}
public static void MapControllers(this ConsoleRunner runner) {

runner.MapCommand<HelpController>(AvailableCommands.HELP);
runner.MapCommand<LoginController>(AvailableCommands.LOGIN);
runner.MapCommand<InitController>(AvailableCommands.INIT);
}
}
1 change: 1 addition & 0 deletions PWManager.Data/DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public static IServiceCollection AddDataServices(this IServiceCollection service
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ISettingsRepository, SettingsRepository>();

services.AddTransient<ILoginService, LoginService>();
services.AddTransient<IDatabaseInitializerService, DatabaseInitializerService>();
return services;
}
Expand Down
8 changes: 6 additions & 2 deletions PWManager.Data/Repositories/SettingsRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using PWManager.Domain.Repositories;
using PWManager.Domain.Services.Interfaces;
using PWManager.Domain.ValueObjects;
using System;

namespace PWManager.Data.Repositories;

Expand All @@ -20,14 +21,17 @@ public SettingsRepository(IApplicationEnvironment env, ICryptService cryptServic
}

public Settings GetSettings() {
var settingsModel = _dbContext.Settings.First(e => e.UserId == _environment.CurrentUser.Id);
var userId = _environment.CurrentUser.Id;
var settingsList = _dbContext.Settings.Where(e => e.UserId == userId).ToList();
var settingsModel = settingsList.Any() ? settingsList.First() : null;
if (settingsModel is null) {
settingsModel = new SettingsModel {
UserId = _environment.CurrentUser.Id,
Id = Guid.NewGuid().ToString(),
MainGroupIdentifier = _cryptService.Encrypt("main")
};
UpdateSettings(settingsModel);
_dbContext.Settings.Add(settingsModel);
_dbContext.SaveChanges();
}

return SettingsModelToEntity(settingsModel);
Expand Down
7 changes: 5 additions & 2 deletions PWManager.Data/Repositories/UserRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@ public User AddUser(string username, string password) {
return UserModelToEntity(userModel);
}

public bool CheckPasswordAttempt(string username, string password) {
public User? CheckPasswordAttempt(string username, string password) {
var user = _dbContext.Users.First(e => e.UserName == username);
var hash = _cryptService.Hash(password, user.Salt);

return hash == user.MasterHash;
if(hash.Equals(user.MasterHash)) {
return UserModelToEntity(user);
}
return null;
}

private User UserModelToEntity(UserModel e) {
Expand Down
51 changes: 51 additions & 0 deletions PWManager.Data/Services/LoginService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using PWManager.Application.Context;
using PWManager.Application.Exceptions;
using PWManager.Application.Services.Interfaces;
using PWManager.Data.Abstraction;
using PWManager.Domain.Exceptions;
using PWManager.Domain.Repositories;
using PWManager.Domain.Services.Interfaces;

namespace PWManager.Data.Services {
public class LoginService : ILoginService {

private readonly IUserRepository _userRepository;
private readonly IGroupRepository _groupRepository;
private readonly ISettingsRepository _settingsRepository;
private readonly ICryptService _cryptService;
private readonly IApplicationEnvironment _env;

private readonly DataContextWrapper _dataContext;

internal LoginService(DataContextWrapper wrapper, IUserRepository userRepository, IGroupRepository groupRepository, ICryptService cryptService, ISettingsRepository settingsRepository, IApplicationEnvironment env) : this(
userRepository, groupRepository, cryptService, settingsRepository, env) {
_dataContext = wrapper;
}
public LoginService(IUserRepository userRepository, IGroupRepository groupRepository, ICryptService cryptService, ISettingsRepository settingsRepository, IApplicationEnvironment env) {
_userRepository = userRepository;
_groupRepository = groupRepository;
_settingsRepository = settingsRepository;
_cryptService = cryptService;
_env = env;
_dataContext = new DataContextWrapper();
}
public void Login(string username, string password, string dbPath) {
if(!_dataContext.DatabaseExists(dbPath)) {
throw new UserFeedbackException("Database not found.");
}

_dataContext.InitDataContext(dbPath);

var user = _userRepository.CheckPasswordAttempt(username, password);
if(user is null) {
throw new UserFeedbackException("No such user found.");
}

_env.CurrentUser = user;
_env.EncryptionKey = _cryptService.DeriveKeyFrom(password, username);

var mainGroup = _settingsRepository.GetSettings().MainGroup;
_env.CurrentGroup = _groupRepository.GetGroup(mainGroup.MainGroupIdentifier);
}
}
}
2 changes: 1 addition & 1 deletion PWManager.Domain/Repositories/IUserRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
namespace PWManager.Domain.Repositories {
public interface IUserRepository {
public User AddUser(string username, string password);
public bool CheckPasswordAttempt(string username, string password);
public User? CheckPasswordAttempt(string username, string password);
}
}
88 changes: 88 additions & 0 deletions PWManager.UnitTests/Cli/LoginControllerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using NSubstitute;
using PWManager.Application.Context;
using PWManager.Application.Services.Interfaces;
using PWManager.CLI.Controllers;
using PWManager.Data.Repositories;
using PWManager.Data.Services;
using PWManager.Domain.Entities;
using PWManager.Domain.Repositories;
using PWManager.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PWManager.UnitTests.Cli {
public class LoginControllerTest {

private LoginController _sut;

public LoginControllerTest()
{
_sut = Substitute.ForPartsOf<LoginController>(null, null);
_sut.AskForInput(Arg.Any<string>()).Returns("Test");
}

[Fact]
public void Arguments_ShouldBe_Empty_WithoutArgs() {
string[] args = { };

(var username, var path) = _sut.ParseArgs(args);

Assert.Equal(0, username.Length);
Assert.Equal(0, path.Length);
}

[Fact]
public void Arguments_Should_Return_Path() {
string[] args = { "-d", "TestPath" };

(var username, var path) = _sut.ParseArgs(args);

Assert.Equal(0, username.Length);
Assert.Equal("TestPath", path);
}

[Fact]
public void Arguments_Should_Return_Username() {
string[] args = { "-u", "TestUserName" };

(var username, var path) = _sut.ParseArgs(args);

Assert.Equal("TestUserName", username);
Assert.Equal(0, path.Length);
}


[Fact]
public void Arguments_Should_Return_Path_And_PromptName() {
string[] args = { "-d", "TestPath", "-u" };

(var username, var path) = _sut.ParseArgs(args);

Assert.Equal("Test", username);
Assert.Equal("TestPath", path);
}

[Fact]
public void Arguments_Should_Return_Username_And_PromptPath() {
string[] args = { "-d", "-u", "TestUserName" };

(var username, var path) = _sut.ParseArgs(args);

Assert.Equal("TestUserName", username);
Assert.Equal("Test", path);
}

[Fact]
public void Arguments_Should_Return_From_Prompts() {
string[] args = { "-d", "-u" };

(var username, var path) = _sut.ParseArgs(args);

Assert.Equal("Test", username);
Assert.Equal("Test", path);
}
}
}
Loading
Loading