Skip to content

Commit

Permalink
Merge pull request #47 from CUMGroup/login
Browse files Browse the repository at this point in the history
LoginService and Controller
  • Loading branch information
AlexMi-Ha authored Feb 24, 2024
2 parents b146c3d + 2abc361 commit ce63c16
Show file tree
Hide file tree
Showing 14 changed files with 331 additions and 8 deletions.
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

0 comments on commit ce63c16

Please sign in to comment.