Beispiel #1
0
        /// <summary>
        /// Migrates from version 1 to 2.
        /// </summary>
        private async Task <UserDataBundle> UpgradeToVersion2Async(string encryptedData)
        {
            // Version 1 can be loaded as version 2.
            // The difference between 1 and 2 is a vulnerability in the encryption engine.
            // To fix it, the data should be decrypted and re-encrypted.
            _logger.LogEvent(Version1ToVersion2EventName, string.Empty);
            const int oldVersion = 1;
            const int newVersion = 2;

            string         jsonData       = _encryptionProvider.DecryptString(encryptedData);
            UserDataBundle userDataBundle = _serializationProvider.DeserializeObject <UserDataBundle>(jsonData);

            var tasks = new List <Task>();

            for (int i = 0; i < userDataBundle.Accounts.Count; i++)
            {
                Account account = userDataBundle.Accounts[i];
                for (int j = 0; j < account.Data.Count; j++)
                {
                    AccountData accountData = account.Data[j];
                    if (accountData is IUpgradableAccountData upgradableAccountData)
                    {
                        tasks.Add(upgradableAccountData.UpgradeAsync(oldVersion, newVersion));
                    }
                }
            }

            await Task.WhenAll(tasks).ConfigureAwait(false);

            return(userDataBundle);
        }
Beispiel #2
0
        public void CloneObjectTest()
        {
            var data = new UserDataBundle();

            data.Categories.Add(new Category(Guid.NewGuid(), "Category"));

            var dataCopy = ExportProvider.GetExport <ISerializationProvider>().CloneObject(data);

            Assert.AreEqual(data.GetHashCode(), data.GetHashCode());
            Assert.AreNotEqual(data.GetHashCode(), dataCopy.GetHashCode());
        }
Beispiel #3
0
        /// <summary>
        /// Creates the default instance of <see cref="UserDataBundle"/>.
        /// </summary>
        private void CreateNewUserDataBundle()
        {
            _data = new UserDataBundle();
            _data.Categories.Add(new Category(new Guid(Constants.CategoryAllId), LanguageManager.Instance.Core.CategoryAll));
            _data.Categories.Add(new Category(GenerateUniqueId(), LanguageManager.Instance.Core.CategoryFinancial));
            _data.Categories.Add(new Category(GenerateUniqueId(), LanguageManager.Instance.Core.CategoryPersonal));
            _data.Categories.Add(new Category(GenerateUniqueId(), LanguageManager.Instance.Core.CategoryProfessional));
            _data.Categories.Add(new Category(GenerateUniqueId(), LanguageManager.Instance.Core.CategorySocial));

            SortData();
        }
Beispiel #4
0
        public async Task LoadOrCreateLocalUserDataBundleAsync(CancellationToken cancellationToken)
        {
            using (await _sempahore.WaitAsync(cancellationToken).ConfigureAwait(false))
            {
                await EnsureInitializedAsync().ConfigureAwait(false);

                if (await _localUserDataFolder.TryGetItemAsync(Constants.UserDataBundleFileName) == null)
                {
                    // The local data file doesn't exist, let's create one !
                    _logger.LogEvent(NewBundleEvent, string.Empty);
                    CreateNewUserDataBundle();
                    await SaveLocalDataInternalAsync(synchronize : false).ConfigureAwait(false);
                }

                // Open and decrypt the file from the hard drive.
                StorageFile dataFile = await _localUserDataFolder.GetFileAsync(Constants.UserDataBundleFileName);

                if (!dataFile.IsAvailable)
                {
                    throw new FileLoadException("The file isn't available.");
                }

                // Loads the user data bundle and migrate it (if needed)
                (bool updated, UserDataBundle data) = await _upgradeService.UpgradeUserDataBundleAsync(dataFile).ConfigureAwait(false);

                _logger.LogEvent(LoadedEvent, string.Empty);
                if (_data == null)
                {
                    // There was no previously loaded data file, let's use the one we loaded.
                    _data = data;
                }
                else
                {
                    // A user data file is already in memory. Maybe we just created it, but usually it probably mean we synchronized the file with the
                    // cloud and should now open the newly downloaded file.
                    // Therefore, we need to "merge" what we have on the hard drive (that comes from the cloud) with
                    // what we already had in RAM.
                    if (MergeUserDataBundle(data))
                    {
                        updated = true;
                    }
                }

                if (updated)
                {
                    // Something has changed during the merge, and/or the data have been migrated from an older version.
                    // So let's save the data to be sure we save the merge and let's (re)synchronize to be
                    // sure the server has the changes from the merge too.
                    await SaveLocalDataInternalAsync(synchronize : true).ConfigureAwait(false);
                }
            }
        }
Beispiel #5
0
        public async Task <(bool updated, UserDataBundle userDataBundle)> UpgradeUserDataBundleAsync(StorageFile userDataBundleFile)
        {
            string encryptedData = await FileIO.ReadTextAsync(userDataBundleFile);

            int version = GetVersion(encryptedData);

            if (version == 1)
            {
                return(updated : true, await UpgradeToVersion2Async(encryptedData).ConfigureAwait(false));
            }

            // No migration needed. Just load the data
            UserDataBundle userDataBundle = Load(encryptedData);

            _logger.LogEvent(NoUpgradeRequiredEventName, string.Empty);
            return(updated : false, userDataBundle);
        }
Beispiel #6
0
        public async Task ClearLocalDataAsync(CancellationToken cancellationToken)
        {
            using (await _sempahore.WaitAsync(cancellationToken).ConfigureAwait(false))
            {
                await CoreHelper.RetryAsync(async() =>
                {
                    await EnsureInitializedAsync().ConfigureAwait(false);
                    IReadOnlyList <StorageFile> storageFiles = await _localUserDataFolder.GetFilesAsync();

                    for (int i = 0; i < storageFiles.Count; i++)
                    {
                        await storageFiles[i].DeleteAsync(StorageDeleteOption.PermanentDelete);
                    }
                }).ConfigureAwait(false);

                _data = null;
            }

            _logger.LogEvent(ClearLocalDataEvent, string.Empty);
        }
Beispiel #7
0
        /// <summary>
        /// Merges a given <paramref name="dataFromExternalSource"/> with <see cref="_data"/>.
        /// </summary>
        /// <returns>Returns <code>true</code> if something changed following the merge.</returns>
        private bool MergeUserDataBundle(UserDataBundle dataFromExternalSource)
        {
            var differenceDetectedInMerging = false;

            // Remove categories that exist in memory but not in the one coming from an external source.
            List <Category> categoriesToRemove = _data.Categories.Except(dataFromExternalSource.Categories).ToList();

            for (int i = 0; i < categoriesToRemove.Count; i++)
            {
                if (_data.Categories.Remove(categoriesToRemove[i]))
                {
                    differenceDetectedInMerging = true;
                }
            }

            // Update the categories that exist on the both side.
            for (var i = 0; i < _data.Categories.Count; i++)
            {
                Category category = _data.Categories[i];
                Category categoryFromExternalSource = dataFromExternalSource.Categories.FirstOrDefault(item => item == category);

                // If the local category != category from external source and that the one from the external source
                // is more recent, then let's take it.
                if (!category.ExactEquals(categoryFromExternalSource))
                {
                    differenceDetectedInMerging = true;
                    if (categoryFromExternalSource.LastModificationDate.ToUniversalTime() > category.LastModificationDate.ToUniversalTime())
                    {
                        _data.Categories[i] = categoryFromExternalSource;
                    }
                }
            }

            // Add categories that exist in the one coming from an external source but not in memory.
            List <Category> categoriesToAdd = dataFromExternalSource.Categories.Except(_data.Categories).ToList();

            if (categoriesToAdd.Count > 0)
            {
                differenceDetectedInMerging = true;
            }

            for (int i = 0; i < categoriesToAdd.Count; i++)
            {
                _data.Categories.Add(categoriesToAdd[i]);
            }

            // Same but for accounts.

            List <Account> accountsToRemove = _data.Accounts.Except(dataFromExternalSource.Accounts).ToList();

            for (int i = 0; i < accountsToRemove.Count; i++)
            {
                Account account = accountsToRemove[i];
                if (_data.Accounts.Remove(account))
                {
                    differenceDetectedInMerging = true;
                }
                account.Dispose();
            }

            for (var i = 0; i < _data.Accounts.Count; i++)
            {
                Account account = _data.Accounts[i];
                Account accountFromExternalSource = dataFromExternalSource.Accounts.FirstOrDefault(item => item == account);

                // If the local account != account from external source and that the one from the external source
                // is more recent, then let's take it.
                if (!account.ExactEquals(accountFromExternalSource))
                {
                    differenceDetectedInMerging = true;
                    if (accountFromExternalSource.LastModificationDate.ToUniversalTime() > account.LastModificationDate.ToUniversalTime())
                    {
                        _data.Accounts[i] = accountFromExternalSource;
                    }
                }
            }

            List <Account> accountsToAdd = dataFromExternalSource.Accounts.Except(_data.Accounts).ToList();

            if (accountsToAdd.Count > 0)
            {
                differenceDetectedInMerging = true;
            }

            for (int i = 0; i < accountsToAdd.Count; i++)
            {
                _data.Accounts.Add(accountsToAdd[i]);
            }

            if (differenceDetectedInMerging)
            {
                // Sort the data.
                SortData();
            }

            return(differenceDetectedInMerging);
        }