From 38d702b6fe17eb848aa572201150614b4d4e1f81 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Fri, 12 Jul 2019 17:29:40 -0400 Subject: [PATCH] log some events --- src/Android/Android.csproj | 1 + src/Android/MainActivity.cs | 29 ++++++++++++- src/Android/Receivers/EventUploadReceiver.cs | 16 +++++++ src/App/App.xaml.cs | 3 ++ src/App/Pages/Vault/AddEditPageViewModel.cs | 31 ++++++++++++-- src/App/Pages/Vault/ViewPageViewModel.cs | 45 ++++++++++++++++++-- src/App/Utilities/AppHelpers.cs | 3 ++ src/Core/Enums/EventType.cs | 1 + 8 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 src/Android/Receivers/EventUploadReceiver.cs diff --git a/src/Android/Android.csproj b/src/Android/Android.csproj index 64d9816bc..4cd7ff816 100644 --- a/src/Android/Android.csproj +++ b/src/Android/Android.csproj @@ -120,6 +120,7 @@ + diff --git a/src/Android/MainActivity.cs b/src/Android/MainActivity.cs index 292e50149..2e8480d87 100644 --- a/src/Android/MainActivity.cs +++ b/src/Android/MainActivity.cs @@ -39,8 +39,10 @@ namespace Bit.Droid private IAppIdService _appIdService; private IStorageService _storageService; private IStateService _stateService; + private IEventService _eventService; private PendingIntent _lockAlarmPendingIntent; private PendingIntent _clearClipboardPendingIntent; + private PendingIntent _eventUploadPendingIntent; private AppOptions _appOptions; private string _activityKey = $"{nameof(MainActivity)}_{Java.Lang.JavaSystem.CurrentTimeMillis().ToString()}"; private Java.Util.Regex.Pattern _otpPattern = @@ -48,6 +50,9 @@ namespace Bit.Droid protected override void OnCreate(Bundle savedInstanceState) { + var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver)); + _eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent, + PendingIntentFlags.UpdateCurrent); var alarmIntent = new Intent(this, typeof(LockAlarmReceiver)); _lockAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent, PendingIntentFlags.UpdateCurrent); @@ -65,6 +70,7 @@ namespace Bit.Droid _appIdService = ServiceContainer.Resolve("appIdService"); _storageService = ServiceContainer.Resolve("storageService"); _stateService = ServiceContainer.Resolve("stateService"); + _eventService = ServiceContainer.Resolve("eventService"); TabLayoutResource = Resource.Layout.Tabbar; ToolbarResource = Resource.Layout.Toolbar; @@ -91,10 +97,10 @@ namespace Bit.Droid { if(message.Command == "scheduleLockTimer") { + var alarmManager = GetSystemService(AlarmService) as AlarmManager; var lockOptionMinutes = (int)message.Data; var lockOptionMs = lockOptionMinutes * 60000; var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + lockOptionMs + 10; - var alarmManager = GetSystemService(AlarmService) as AlarmManager; alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _lockAlarmPendingIntent); } else if(message.Command == "cancelLockTimer") @@ -102,6 +108,14 @@ namespace Bit.Droid var alarmManager = GetSystemService(AlarmService) as AlarmManager; alarmManager.Cancel(_lockAlarmPendingIntent); } + else if(message.Command == "startEventTimer") + { + StartEventAlarm(); + } + else if(message.Command == "stopEventTimer") + { + var task = StopEventAlarmAsync(); + } else if(message.Command == "finishMainActivity") { Xamarin.Forms.Device.BeginInvokeOnMainThread(() => Finish()); @@ -372,5 +386,18 @@ namespace Bit.Droid var alarmManager = GetSystemService(AlarmService) as AlarmManager; alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent); } + + private void StartEventAlarm() + { + var alarmManager = GetSystemService(AlarmService) as AlarmManager; + alarmManager.SetInexactRepeating(AlarmType.ElapsedRealtime, 120000, 300000, _eventUploadPendingIntent); + } + + private async Task StopEventAlarmAsync() + { + var alarmManager = GetSystemService(AlarmService) as AlarmManager; + alarmManager.Cancel(_eventUploadPendingIntent); + await _eventService.UploadEventsAsync(); + } } } diff --git a/src/Android/Receivers/EventUploadReceiver.cs b/src/Android/Receivers/EventUploadReceiver.cs new file mode 100644 index 000000000..37b865a41 --- /dev/null +++ b/src/Android/Receivers/EventUploadReceiver.cs @@ -0,0 +1,16 @@ +using Android.Content; +using Bit.Core.Abstractions; +using Bit.Core.Utilities; + +namespace Bit.Droid.Receivers +{ + [BroadcastReceiver(Name = "com.x8bit.bitwarden.EventUploadReceiver", Exported = false)] + public class EventUploadReceiver : BroadcastReceiver + { + public async override void OnReceive(Context context, Intent intent) + { + var eventService = ServiceContainer.Resolve("eventService"); + await eventService.UploadEventsAsync(); + } + } +} diff --git a/src/App/App.xaml.cs b/src/App/App.xaml.cs index 3d5a9455a..0404f19b8 100644 --- a/src/App/App.xaml.cs +++ b/src/App/App.xaml.cs @@ -173,6 +173,7 @@ namespace Bit.App SyncIfNeeded(); } } + _messagingService.Send("startEventTimer"); } protected async override void OnSleep() @@ -184,6 +185,7 @@ namespace Bit.App } SetTabsPageFromAutofill(); await HandleLockingAsync(); + _messagingService.Send("stopEventTimer"); } protected override void OnResume() @@ -198,6 +200,7 @@ namespace Bit.App private async void ResumedAsync() { _messagingService.Send("cancelLockTimer"); + _messagingService.Send("startEventTimer"); await ClearCacheIfNeededAsync(); await TryClearCiphersCacheAsync(); Prime(); diff --git a/src/App/Pages/Vault/AddEditPageViewModel.cs b/src/App/Pages/Vault/AddEditPageViewModel.cs index cf3977a25..831e8f114 100644 --- a/src/App/Pages/Vault/AddEditPageViewModel.cs +++ b/src/App/Pages/Vault/AddEditPageViewModel.cs @@ -23,6 +23,7 @@ namespace Bit.App.Pages private readonly IPlatformUtilsService _platformUtilsService; private readonly IAuditService _auditService; private readonly IMessagingService _messagingService; + private readonly IEventService _eventService; private CipherView _cipher; private bool _showNotesSeparator; private bool _showPassword; @@ -34,6 +35,7 @@ namespace Bit.App.Pages private int _folderSelectedIndex; private int _ownershipSelectedIndex; private bool _hasCollections; + private string _previousCipherId; private List _writeableCollections; private string[] _additionalCipherProperties = new string[] { @@ -74,6 +76,7 @@ namespace Bit.App.Pages _auditService = ServiceContainer.Resolve("auditService"); _messagingService = ServiceContainer.Resolve("messagingService"); _collectionService = ServiceContainer.Resolve("collectionService"); + _eventService = ServiceContainer.Resolve("eventService"); GeneratePasswordCommand = new Command(GeneratePassword); TogglePasswordCommand = new Command(TogglePassword); ToggleCardCodeCommand = new Command(ToggleCardCode); @@ -365,9 +368,16 @@ namespace Bit.App.Pages } if(Cipher.Fields != null) { - Fields.ResetWithRange(Cipher.Fields?.Select(f => new AddEditPageFieldViewModel(f))); + Fields.ResetWithRange(Cipher.Fields?.Select(f => new AddEditPageFieldViewModel(Cipher, f))); } } + + if(EditMode && _previousCipherId != CipherId) + { + var task = _eventService.CollectAsync(EventType.Cipher_ClientViewed, CipherId); + } + _previousCipherId = CipherId; + return true; } @@ -591,7 +601,7 @@ namespace Bit.App.Pages Fields = new ExtendedObservableCollection(); } var type = _fieldTypeOptions.FirstOrDefault(f => f.Value == typeSelection).Key; - Fields.Add(new AddEditPageFieldViewModel(new FieldView + Fields.Add(new AddEditPageFieldViewModel(Cipher, new FieldView { Type = type, Name = string.IsNullOrWhiteSpace(name) ? null : name @@ -602,11 +612,19 @@ namespace Bit.App.Pages public void TogglePassword() { ShowPassword = !ShowPassword; + if(EditMode && ShowPassword) + { + var task = _eventService.CollectAsync(EventType.Cipher_ClientToggledPasswordVisible, CipherId); + } } public void ToggleCardCode() { ShowCardCode = !ShowCardCode; + if(EditMode && ShowCardCode) + { + var task = _eventService.CollectAsync(EventType.Cipher_ClientToggledCardCodeVisible, CipherId); + } } public async Task UpdateTotpKeyAsync(string key) @@ -720,6 +738,7 @@ namespace Bit.App.Pages public class AddEditPageFieldViewModel : ExtendedViewModel { private FieldView _field; + private CipherView _cipher; private bool _showHiddenValue; private bool _booleanValue; private string[] _additionalFieldProperties = new string[] @@ -729,8 +748,9 @@ namespace Bit.App.Pages nameof(IsTextType), }; - public AddEditPageFieldViewModel(FieldView field) + public AddEditPageFieldViewModel(CipherView cipher, FieldView field) { + _cipher = cipher; Field = field; ToggleHiddenValueCommand = new Command(ToggleHiddenValue); BooleanValue = IsBooleanType && field.Value == "true"; @@ -775,6 +795,11 @@ namespace Bit.App.Pages public void ToggleHiddenValue() { ShowHiddenValue = !ShowHiddenValue; + if(ShowHiddenValue && _cipher?.Id != null) + { + var eventService = ServiceContainer.Resolve("eventService"); + var task = eventService.CollectAsync(EventType.Cipher_ClientToggledHiddenFieldVisible, _cipher.Id); + } } public void TriggerFieldChanged() diff --git a/src/App/Pages/Vault/ViewPageViewModel.cs b/src/App/Pages/Vault/ViewPageViewModel.cs index b898b5e1c..dfad94037 100644 --- a/src/App/Pages/Vault/ViewPageViewModel.cs +++ b/src/App/Pages/Vault/ViewPageViewModel.cs @@ -22,6 +22,7 @@ namespace Bit.App.Pages private readonly IPlatformUtilsService _platformUtilsService; private readonly IAuditService _auditService; private readonly IMessagingService _messagingService; + private readonly IEventService _eventService; private CipherView _cipher; private List _fields; private bool _canAccessPremium; @@ -32,6 +33,7 @@ namespace Bit.App.Pages private string _totpSec; private bool _totpLow; private DateTime? _totpInterval = null; + private string _previousCipherId; public ViewPageViewModel() { @@ -42,6 +44,7 @@ namespace Bit.App.Pages _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _auditService = ServiceContainer.Resolve("auditService"); _messagingService = ServiceContainer.Resolve("messagingService"); + _eventService = ServiceContainer.Resolve("eventService"); CopyCommand = new Command((id) => CopyAsync(id, null)); CopyUriCommand = new Command(CopyUri); CopyFieldCommand = new Command(CopyField); @@ -217,7 +220,7 @@ namespace Bit.App.Pages } Cipher = await cipher.DecryptAsync(); CanAccessPremium = await _userService.CanAccessPremiumAsync(); - Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(f)).ToList(); + Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(Cipher, f)).ToList(); if(Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) && (Cipher.OrganizationUseTotp || CanAccessPremium)) @@ -236,6 +239,11 @@ namespace Bit.App.Pages return true; }); } + if(_previousCipherId != CipherId) + { + var task = _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientViewed, CipherId); + } + _previousCipherId = CipherId; finishedLoadingAction?.Invoke(); return true; } @@ -248,11 +256,20 @@ namespace Bit.App.Pages public void TogglePassword() { ShowPassword = !ShowPassword; + if(ShowPassword) + { + var task = _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientToggledPasswordVisible, CipherId); + } } public void ToggleCardCode() { ShowCardCode = !ShowCardCode; + if(ShowCardCode) + { + var task = _eventService.CollectAsync( + Core.Enums.EventType.Cipher_ClientToggledCardCodeVisible, CipherId); + } } public async Task DeleteAsync() @@ -434,7 +451,7 @@ namespace Bit.App.Pages { name = AppResources.URI; } - else if(id == "FieldValue") + else if(id == "FieldValue" || id == "H_FieldValue") { name = AppResources.Value; } @@ -456,6 +473,18 @@ namespace Bit.App.Pages { _platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, name)); } + if(id == "LoginPassword") + { + await _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, CipherId); + } + else if(id == "CardCode") + { + await _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, CipherId); + } + else if(id == "H_FieldValue") + { + await _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedHiddenField, CipherId); + } } } @@ -466,7 +495,7 @@ namespace Bit.App.Pages private void CopyField(FieldView field) { - CopyAsync("FieldValue", field.Value); + CopyAsync(field.Type == Core.Enums.FieldType.Hidden ? "H_FieldValue" : "FieldValue", field.Value); } private void LaunchUri(LoginUriView uri) @@ -481,10 +510,12 @@ namespace Bit.App.Pages public class ViewPageFieldViewModel : ExtendedViewModel { private FieldView _field; + private CipherView _cipher; private bool _showHiddenValue; - public ViewPageFieldViewModel(FieldView field) + public ViewPageFieldViewModel(CipherView cipher, FieldView field) { + _cipher = cipher; Field = field; ToggleHiddenValueCommand = new Command(ToggleHiddenValue); } @@ -526,6 +557,12 @@ namespace Bit.App.Pages public void ToggleHiddenValue() { ShowHiddenValue = !ShowHiddenValue; + if(ShowHiddenValue) + { + var eventService = ServiceContainer.Resolve("eventService"); + var task = eventService.CollectAsync( + Core.Enums.EventType.Cipher_ClientToggledHiddenFieldVisible, _cipher.Id); + } } } } diff --git a/src/App/Utilities/AppHelpers.cs b/src/App/Utilities/AppHelpers.cs index 40e9c2e1e..6e4b3298d 100644 --- a/src/App/Utilities/AppHelpers.cs +++ b/src/App/Utilities/AppHelpers.cs @@ -16,6 +16,7 @@ namespace Bit.App.Utilities public static async Task CipherListOptions(ContentPage page, CipherView cipher) { var platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); + var eventService = ServiceContainer.Resolve("eventService"); var options = new List { AppResources.View, AppResources.Edit }; if(cipher.Type == Core.Enums.CipherType.Login) { @@ -79,6 +80,7 @@ namespace Bit.App.Utilities await platformUtilsService.CopyToClipboardAsync(cipher.Login.Password); platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.Password)); + var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, cipher.Id); } else if(selection == AppResources.CopyTotp) { @@ -106,6 +108,7 @@ namespace Bit.App.Utilities await platformUtilsService.CopyToClipboardAsync(cipher.Card.Code); platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, AppResources.SecurityCode)); + var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, cipher.Id); } else if(selection == AppResources.CopyNotes) { diff --git a/src/Core/Enums/EventType.cs b/src/Core/Enums/EventType.cs index 8f4e28ad2..d9411e31c 100644 --- a/src/Core/Enums/EventType.cs +++ b/src/Core/Enums/EventType.cs @@ -43,5 +43,6 @@ Organization_Updated = 1600, Organization_PurgedVault = 1601, + // Organization_ClientExportedVault = 1602, } }