Skip to content

Commit

Permalink
Merge pull request #10 from YassinLokhat/5-add-warnings-feature
Browse files Browse the repository at this point in the history
5 add warnings feature
  • Loading branch information
YassinLokhat authored Feb 3, 2025
2 parents 1a61a35 + ab0101b commit 83e561d
Show file tree
Hide file tree
Showing 15 changed files with 411 additions and 27 deletions.
33 changes: 33 additions & 0 deletions Core/Enums/WarningType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Upsilon.Apps.PassKey.Core.Enums
{
/// <summary>
/// Represent a type of warning.
/// </summary>
[Flags]
public enum WarningType
{
/// <summary>
/// A set of logs needs to be reviewed.
/// </summary>
LogReviewWarning = 0b0001,
/// <summary>
/// A set of accounts password expired and need to be updated.
/// </summary>
PasswordUpdateReminderWarning = 0b0010,
/// <summary>
/// Some accounts share the same passwords.
/// </summary>
DuplicatedPasswordsWarning = 0b0100,
/// <summary>
/// Some passwords leaked and found on the ';--have i been pwned? database
/// </summary>
PasswordLeakedWarning = 0b1000,
}
}

24 changes: 24 additions & 0 deletions Core/Events/WarningDetectedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Upsilon.Apps.PassKey.Core.Interfaces;

namespace Upsilon.Apps.PassKey.Core.Events
{
/// <summary>
/// Represent a warning detected event argument.
/// </summary>
/// <remarks>
/// Creates a new event args.
/// </remarks>
/// <param name="warning">The warnings detected.</param>
public class WarningDetectedEventArgs(IWarning[] warning)
{
/// <summary>
/// The warnings detected.
/// </summary>
public IWarning[] Warnings { get; private set; } = warning;
}
}
7 changes: 7 additions & 0 deletions Core/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public interface IDatabase : IDisposable
/// </summary>
ILog[]? Logs { get; }

/// <summary>
/// The warnings detected.
/// </summary>
IWarning[]? Warnings { get; }

/// <summary>
/// Try to load the current user.
/// </summary>
Expand Down Expand Up @@ -102,13 +107,15 @@ static IDatabase Open(ICryptographyCenter cryptographicCenter,
string autoSaveFile,
string logFile,
string username,
EventHandler<WarningDetectedEventArgs> warningDetectedHandler,
EventHandler<AutoSaveDetectedEventArgs>? autoSaveHandler = null)
=> Database.Open(cryptographicCenter,
serializationCenter,
databaseFile,
autoSaveFile,
logFile,
username,
warningDetectedHandler,
autoSaveHandler);
}
}
9 changes: 8 additions & 1 deletion Core/Interfaces/IUser.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Upsilon.Apps.PassKey.Core.Interfaces
using Upsilon.Apps.PassKey.Core.Enums;

namespace Upsilon.Apps.PassKey.Core.Interfaces
{
/// <summary>
/// Represent an user.
Expand All @@ -25,6 +27,11 @@ public interface IUser : IItem
/// </summary>
int CleaningClipboardTimeout { get; set; }

/// <summary>
/// The warnings types which will be notified if detected.
/// </summary>
WarningType WarningsToNotify { get; set; }

/// <summary>
/// The list of the user's services.
/// </summary>
Expand Down
30 changes: 30 additions & 0 deletions Core/Interfaces/IWarning.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Upsilon.Apps.PassKey.Core.Enums;

namespace Upsilon.Apps.PassKey.Core.Interfaces
{
/// <summary>
/// Represent a warning.
/// </summary>
public interface IWarning
{
/// <summary>
/// The type of the warning.
/// </summary>
WarningType WarningType { get; }

/// <summary>
/// The logs concerned to the warning, if exists.
/// </summary>
ILog[]? Logs { get; }

/// <summary>
/// The accounts concerned to the warning, if exists.
/// </summary>
IAccount[]? Accounts { get; }
}
}
19 changes: 18 additions & 1 deletion Core/Models/Account.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.ComponentModel;
using Upsilon.Apps.PassKey.Core.Enums;
using Upsilon.Apps.PassKey.Core.Interfaces;
using Upsilon.Apps.PassKey.Core.Utils;

namespace Upsilon.Apps.PassKey.Core.Models
{
Expand Down Expand Up @@ -109,7 +110,23 @@ internal Service Service
public Dictionary<DateTime, string> Passwords { get; set; } = [];
public string Notes { get; set; } = string.Empty;
public int PasswordUpdateReminderDelay { get; set; } = 0;
public AccountOption Options { get; set; } = AccountOption.None;
public AccountOption Options { get; set; }
= AccountOption.WarnIfPasswordLeaked;

internal bool PasswordExpired
{
get
{
if (PasswordUpdateReminderDelay == 0) return false;

DateTime lastPassword = Passwords.Keys.Max();
int delay = ((DateTime.Now.Year - lastPassword.Year) * 12) + DateTime.Now.Month - lastPassword.Month;

return delay >= PasswordUpdateReminderDelay;
}
}

internal bool PasswordLeaked => Options.ContainsFlag(AccountOption.WarnIfPasswordLeaked) && PasswordGenerator.PasswordLeaked(Password);

public void Apply(Change change)
{
Expand Down
103 changes: 103 additions & 0 deletions Core/Models/Database.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ internal sealed class Database : IDatabase

IUser? IDatabase.User => User;
ILog[]? IDatabase.Logs => Logs.Logs;
IWarning[]? IDatabase.Warnings => User != null ? Warnings : null;

public void Delete()
{
Expand Down Expand Up @@ -66,6 +67,22 @@ public void Delete()
_onAutoSaveDetected?.Invoke(this, eventArg);
_handleAutoSave(eventArg.MergeBehavior);
}

Warning[] logWarnings = _lookAtLogWarnings();
Warning[] passwordUpdateReminderWarnings = _lookAtPasswordUpdateReminderWarnings();
Warning[] passwordLeakedWarnings = _lookAtPasswordLeakedWarnings();
Warning[] duplicatedPasswordsWarnings = _lookAtDuplicatedPasswordsWarnings();

Warnings = [..logWarnings,
..passwordUpdateReminderWarnings,
..passwordLeakedWarnings,
..duplicatedPasswordsWarnings];

_onWarningDetected?.Invoke(this, new WarningDetectedEventArgs(
[..User.WarningsToNotify.ContainsFlag(WarningType.LogReviewWarning) ? logWarnings : [],
..User.WarningsToNotify.ContainsFlag(WarningType.PasswordUpdateReminderWarning) ? passwordUpdateReminderWarnings : [],
..User.WarningsToNotify.ContainsFlag(WarningType.PasswordLeakedWarning) ? passwordLeakedWarnings : [],
..User.WarningsToNotify.ContainsFlag(WarningType.DuplicatedPasswordsWarning) ? duplicatedPasswordsWarnings : []]));
}

return User;
Expand All @@ -78,6 +95,7 @@ public void Delete()
internal User? User;
internal AutoSave AutoSave;
internal LogCenter Logs;
internal Warning[]? Warnings;

internal string Username { get; private set; }
internal string[] Passkeys { get; private set; }
Expand All @@ -88,6 +106,7 @@ public void Delete()
internal readonly ICryptographyCenter CryptographicCenter;
internal readonly ISerializationCenter SerializationCenter;

private readonly EventHandler<WarningDetectedEventArgs>? _onWarningDetected = null;
private readonly EventHandler<AutoSaveDetectedEventArgs>? _onAutoSaveDetected = null;

private Database(ICryptographyCenter cryptographicCenter,
Expand All @@ -96,6 +115,7 @@ private Database(ICryptographyCenter cryptographicCenter,
string autoSaveFile,
string logFile,
FileMode fileMode,
EventHandler<WarningDetectedEventArgs>? warningDetectedHandler,
EventHandler<AutoSaveDetectedEventArgs>? autoSaveHandler,
string username,
string publicKey = "",
Expand Down Expand Up @@ -136,6 +156,7 @@ private Database(ICryptographyCenter cryptographicCenter,
Logs.Database = this;

_onAutoSaveDetected = autoSaveHandler;
_onWarningDetected = warningDetectedHandler;
}

internal static IDatabase Create(ICryptographyCenter cryptographicCenter,
Expand Down Expand Up @@ -166,6 +187,7 @@ internal static IDatabase Create(ICryptographyCenter cryptographicCenter,
autoSaveFile,
logFile,
FileMode.Create,
warningDetectedHandler: null,
autoSaveHandler: null,
username,
publicKey,
Expand Down Expand Up @@ -200,6 +222,7 @@ internal static IDatabase Open(ICryptographyCenter cryptographicCenter,
string autoSaveFile,
string logFile,
string username,
EventHandler<WarningDetectedEventArgs>? warningDetectedHandler = null,
EventHandler<AutoSaveDetectedEventArgs>? autoSaveHandler = null)
{
Database database = new(cryptographicCenter,
Expand All @@ -208,6 +231,7 @@ internal static IDatabase Open(ICryptographyCenter cryptographicCenter,
autoSaveFile,
logFile,
FileMode.Open,
warningDetectedHandler,
autoSaveHandler,
username);

Expand Down Expand Up @@ -260,6 +284,7 @@ private void _close(bool logCloseEvent)
AutoSave.Changes.Clear();
Username = string.Empty;
Passkeys = [];
Warnings = null;

DatabaseFileLocker?.Dispose();
DatabaseFileLocker = null;
Expand Down Expand Up @@ -301,5 +326,83 @@ private void _handleAutoSave(AutoSaveMergeBehavior mergeAutoSave)
break;
}
}

private Warning[] _lookAtLogWarnings()
{
if (User == null) throw new NullReferenceException(nameof(User));
if (Logs.Logs == null) throw new NullReferenceException(nameof(Logs.Logs));

List<Log> logs = Logs.Logs.Cast<Log>().ToList();

for (int i = 0; i < logs.Count && logs[i].Message != $"User {Username} logged in"; i++)
{
if (!logs[i].NeedsReview
|| !logs[i].Message.StartsWith($"User {Username}'s autosave "))
{
logs.RemoveAt(i);
i--;
}
}

return [new Warning([.. logs.Where(x => x.NeedsReview)])];
}

private Warning[] _lookAtPasswordUpdateReminderWarnings()
{
if (User == null) throw new NullReferenceException(nameof(User));

Account[] accounts = User.Services
.SelectMany(x => x.Accounts)
.Where(x => x.PasswordExpired)
.ToArray();

if (accounts.Length != 0)
{
return [new Warning(WarningType.PasswordUpdateReminderWarning, accounts)];
}
else
{
return [];
}
}

private Warning[] _lookAtPasswordLeakedWarnings()
{
if (User == null) throw new NullReferenceException(nameof(User));

Account[] accounts = User.Services
.SelectMany(x => x.Accounts)
.Where(x => x.PasswordLeaked)
.ToArray();

if (accounts.Length != 0)
{
return [new Warning(WarningType.PasswordLeakedWarning, accounts)];
}
else
{
return [];
}
}

private Warning[] _lookAtDuplicatedPasswordsWarnings()
{
if (User == null) throw new NullReferenceException(nameof(User));

IGrouping<string, Account>[] duplicatedPasswords = User.Services
.SelectMany(x => x.Accounts)
.GroupBy(x => x.Password)
.Where(x => x.Count() > 1)
.ToArray();

List<Warning> warnings = [];

foreach (IGrouping<string, Account> accounts in duplicatedPasswords)
{
warnings.Add(new(WarningType.DuplicatedPasswordsWarning, [.. accounts.Cast<Account>()]));
}

return [.. warnings];
}
}
}
19 changes: 19 additions & 0 deletions Core/Models/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ int IUser.CleaningClipboardTimeout
readableValue: value.ToString());
}

WarningType IUser.WarningsToNotify
{
get => WarningsToNotify;
set => WarningsToNotify = Database.AutoSave.UpdateValue(ItemId,
itemName: this.ToString(),
fieldName: nameof(WarningsToNotify),
needsReview: true,
value: value,
readableValue: value.ToString());
}

IService IUser.AddService(string serviceName)
{
Service service = new()
Expand Down Expand Up @@ -103,6 +114,11 @@ internal Database Database
public string[] Passkeys { get; set; } = [];
public int LogoutTimeout { get; set; } = 0;
public int CleaningClipboardTimeout { get; set; } = 0;
public WarningType WarningsToNotify { get; set; }
= WarningType.LogReviewWarning
| WarningType.PasswordUpdateReminderWarning
| WarningType.DuplicatedPasswordsWarning
| WarningType.PasswordLeakedWarning;

public void Apply(Change change)
{
Expand Down Expand Up @@ -142,6 +158,9 @@ private void _apply(Change change)
case nameof(CleaningClipboardTimeout):
CleaningClipboardTimeout = Database.SerializationCenter.Deserialize<int>(change.Value);
break;
case nameof(WarningsToNotify):
WarningsToNotify = Database.SerializationCenter.Deserialize<WarningType>(change.Value);
break;
default:
throw new InvalidDataException("FieldName not valid");
}
Expand Down
Loading

0 comments on commit 83e561d

Please sign in to comment.