Skip to content
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
12 changes: 7 additions & 5 deletions Core/Models/Account.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,17 @@ internal bool PasswordExpired
DateTime lastPassword = Passwords.Keys.Max();
int delay = ((DateTime.Now.Year - lastPassword.Year) * 12) + DateTime.Now.Month - lastPassword.Month;

return delay >= PasswordUpdateReminderDelay;
return delay > PasswordUpdateReminderDelay;
}
}

internal bool PasswordLeaked => Options.ContainsFlag(AccountOption.WarnIfPasswordLeaked) && Database.PasswordFactory.PasswordLeaked(Password);
internal bool PasswordLeaked { get; set; } = false;

public void Apply(Change change)
internal void Apply(Change change)
{
switch (change.ActionType)
{
case LogEventType.ItemUpdated:
case ActivityEventType.ItemUpdated:
switch (change.FieldName)
{
case nameof(Label):
Expand All @@ -178,7 +178,7 @@ public void Apply(Change change)
}
break;
default:
throw new InvalidEnumArgumentException(nameof(change.ActionType), (int)change.ActionType, typeof(LogEventType));
throw new InvalidEnumArgumentException(nameof(change.ActionType), (int)change.ActionType, typeof(ActivityEventType));
}
}

Expand All @@ -193,5 +193,7 @@ public override string ToString()

return account + $"({string.Join(", ", Identifiers)})";
}

public bool HasChanged() => Database.HasChanged(ItemId);
}
}
114 changes: 114 additions & 0 deletions Core/Models/Activity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using Upsilon.Apps.Passkey.Core.Utils;
using Upsilon.Apps.Passkey.Interfaces.Enums;
using Upsilon.Apps.Passkey.Interfaces.Models;

namespace Upsilon.Apps.Passkey.Core.Models
{
internal class Activity : IActivity
{
#region IActivity interface

public DateTime DateTime => new(DateTimeTicks);

public string ItemId { get; } = string.Empty;

public ActivityEventType EventType { get; set; } = ActivityEventType.None;

public bool NeedsReview { get; set; } = true;

public string Message => _buildMessage();

#endregion

public long DateTimeTicks { get; set; }
public string[] Data { get; set; } = [];

public Activity(long dateTimeTicks, string itemId, ActivityEventType eventType, string[] data, bool needsReview)
{
DateTimeTicks = dateTimeTicks;
ItemId = itemId;
EventType = eventType;
Data = data;
NeedsReview = needsReview;
}

public Activity(string activity)
{
string[] info = activity.Split('|');

if (info.Length > 0)
{
DateTimeTicks = Convert.ToInt64(info[0], 16);
}

if (info.Length > 1)
{
ItemId = info[1];
}

if (info.Length > 2
&& byte.TryParse(info[2], out byte eventType))
{
EventType = (ActivityEventType)eventType;
}

if (info.Length > 3)
{
NeedsReview = !string.IsNullOrEmpty(info[3]);
}

if (info.Length > 4)
{
activity = string.Join("|", info[4..])
.Replace("|", "/|")
.Replace("\\/|", "\\|");
info = activity.Split("/|");
Data = [.. info.Select(x => x.Replace("\\|", "|"))];
}
}

public override string ToString()
{
string activity = $"{DateTimeTicks:X}|{ItemId}|{(int)EventType}|{(NeedsReview ? "1" : "")}";

string[] data = [.. Data.Select(x => x.Replace("|", "\\|"))];
if (data.Length != 0)
{
activity += $"|{string.Join("|", data)}";
}

return activity;
}

private string _buildMessage()
{
string message = EventType switch
{
ActivityEventType.MergeAndSaveThenRemoveAutoSaveFile => $"User {Data[0]}'s autosave merged and saved",
ActivityEventType.MergeWithoutSavingAndKeepAutoSaveFile => $"User {Data[0]}'s autosave merged without saving",
ActivityEventType.DontMergeAndRemoveAutoSaveFile => $"User {Data[0]}'s autosave not merged and removed",
ActivityEventType.DontMergeAndKeepAutoSaveFile => $"User {Data[0]}'s autosave not merged and keeped",
ActivityEventType.DatabaseCreated => $"User {Data[0]}'s database created",
ActivityEventType.DatabaseOpened => $"User {Data[0]}'s database opened",
ActivityEventType.DatabaseSaved => $"User {Data[0]}'s database saved",
ActivityEventType.DatabaseClosed => $"User {Data[0]}'s database closed",
ActivityEventType.LoginSessionTimeoutReached => $"User {Data[0]}'s login session timeout reached",
ActivityEventType.LoginFailed => $"User {Data[0]} login failed at level {Data[1]}",
ActivityEventType.UserLoggedIn => $"User {Data[0]} logged in",
ActivityEventType.UserLoggedOut => $"User {Data[0]} logged out {(!string.IsNullOrEmpty(Data[1]) ? "without saving" : "")}",
ActivityEventType.ImportingDataStarted => $"Importing data from file : '{Data[0]}'",
ActivityEventType.ImportingDataSucceded => $"Import completed successfully",
ActivityEventType.ImportingDataFailed => $"Import failed because {Data[0]}",
ActivityEventType.ExportingDataStarted => $"Exporting data to file : '{Data[0]}'",
ActivityEventType.ExportingDataSucceded => $"Export completed successfully",
ActivityEventType.ExportingDataFailed => $"Export failed because {Data[0]}",
ActivityEventType.ItemUpdated => $"{(Data.Length > 3 ? $"{Data[3]}'s " : "")}{Data[0]}'s {Data[1].ToSentenceCase().ToLower()} has been {(string.IsNullOrWhiteSpace(Data[2]) ? $"updated" : $"set to {Data[2]}")}",
ActivityEventType.ItemAdded => $"{Data[2]} has been added to {Data[0]}",
ActivityEventType.ItemDeleted => $"{Data[2]} has been removed from {Data[0]}",
_ => ToString(),
};

return message.Trim();
}
}
}
37 changes: 26 additions & 11 deletions Core/Models/AutoSave.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal T UpdateValue<T>(string itemId,
newValue.SerializeWith(Database.SerializationCenter),
readableValue,
needsReview,
LogEventType.ItemUpdated);
ActivityEventType.ItemUpdated);
}

return newValue;
Expand All @@ -39,7 +39,7 @@ internal T AddValue<T>(string itemId,
bool needsReview,
T value) where T : notnull
{
_addChange(itemId, string.Empty, value.SerializeWith(Database.SerializationCenter), readableValue, needsReview, LogEventType.ItemAdded);
_addChange(itemId, string.Empty, value.SerializeWith(Database.SerializationCenter), readableValue, needsReview, ActivityEventType.ItemAdded);

return value;
}
Expand All @@ -49,7 +49,7 @@ internal T DeleteValue<T>(string itemId,
bool needsReview,
T value) where T : notnull
{
_addChange(itemId, string.Empty, value.SerializeWith(Database.SerializationCenter), readableValue, needsReview, LogEventType.ItemDeleted);
_addChange(itemId, string.Empty, value.SerializeWith(Database.SerializationCenter), readableValue, needsReview, ActivityEventType.ItemDeleted);

return value;
}
Expand All @@ -59,7 +59,7 @@ private void _addChange(string itemId,
string newValue,
string readableValue,
bool needsReview,
LogEventType action)
ActivityEventType action)
{
_addChange(itemId,
fieldName,
Expand All @@ -76,7 +76,7 @@ private void _addChange(string itemId,
string newValue,
string readableValue,
bool needsReview,
LogEventType action)
ActivityEventType action)
{
string changeKey = $"{itemId}\t{fieldName}";
if (!Changes.ContainsKey(changeKey))
Expand All @@ -97,12 +97,14 @@ private void _addChange(string itemId,
_mergeChanges(changeKey, currentChange);

Database.FileLocker.Save(this, Database.AutoSaveFileEntry, Database.Passkeys);
string itemName = string.Empty;
string parentName = string.Empty;

if (itemId == Database.User?.ItemId)
{
if (Database.User is not null)
{
itemId = Database.User.ToString();
itemName = Database.User.ToString();
}
}
else if (itemId.StartsWith('S'))
Expand All @@ -111,7 +113,7 @@ private void _addChange(string itemId,

if (s is not null)
{
itemId = s.ToString();
itemName = s.ToString();
}
}
else if (itemId.StartsWith('A'))
Expand All @@ -120,20 +122,33 @@ private void _addChange(string itemId,

if (a is not null)
{
itemId = a.ToString();
itemName = a.ToString();

if (action == ActivityEventType.ItemUpdated)
{
parentName = a.Service.ToString();
}
}
}

Database.Logs.AddLog(data: [itemId, fieldName, readableValue],
string[] data = [itemName, fieldName, readableValue];

if (!string.IsNullOrEmpty(parentName))
{
data = [.. data, parentName];
}

Database.ActivityCenter.AddActivity(itemId: itemId,
eventType: action,
data,
needsReview);
}

private void _mergeChanges(string changeKey, Change currentChange)
{
Change? lastUpdate = Changes[changeKey].LastOrDefault(x => x.ActionType == LogEventType.ItemUpdated);
Change? lastUpdate = Changes[changeKey].LastOrDefault(x => x.ActionType == ActivityEventType.ItemUpdated);

if (currentChange.ActionType != LogEventType.ItemUpdated
if (currentChange.ActionType != ActivityEventType.ItemUpdated
|| lastUpdate is null)
{
Changes[changeKey].Add(currentChange);
Expand Down
2 changes: 1 addition & 1 deletion Core/Models/Change.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Upsilon.Apps.Passkey.Core.Models
internal sealed class Change
{
public long Index { get; set; } = long.MaxValue;
public LogEventType ActionType { get; set; } = LogEventType.None;
public ActivityEventType ActionType { get; set; } = ActivityEventType.None;
public string ItemId { get; set; } = string.Empty;
public string FieldName { get; set; } = string.Empty;
public string? OldValue { get; set; } = null;
Expand Down
Loading