示例#1
0
        /// <summary>
        /// Creates a ViewModel wrapping a brand new KdbxGroup as a child of the specified parent group.
        /// </summary>
        /// <param name="resourceProvider">IResourceProvider for localizing strings.</param>
        /// <param name="navigationViewModel">A ViewModel used for tracking navigation history.</param>
        /// <param name="persistenceService">A service used for persisting the document.</param>
        /// <param name="clipboardService">A service used for accessing the clipboard.</param>
        /// <param name="settingsService">A service used for accessing app settings.</param>
        /// <param name="document">A KdbxDocument representing the database we are working on.</param>
        /// <param name="parentGroup">The IKeePassGroup to use as a parent for the new group.</param>
        /// <param name="rng">A random number generator used to protect strings in memory.</param>
        public EntryDetailsViewModel(
            IResourceProvider resourceProvider,
            IDatabaseNavigationViewModel navigationViewModel,
            IDatabasePersistenceService persistenceService,
            ISensitiveClipboardService clipboardService,
            IAppSettingsService settingsService,
            KdbxDocument document,
            IKeePassGroup parentGroup,
            IRandomNumberGenerator rng
            ) : this(
                resourceProvider,
                navigationViewModel,
                persistenceService,
                clipboardService,
                settingsService,
                document,
                new KdbxEntry(parentGroup, rng, document.Metadata),
                true,
                false,
                rng
                )
        {
            if (parentGroup == null)
            {
                throw new ArgumentNullException(nameof(parentGroup));
            }

            if (rng == null)
            {
                throw new ArgumentNullException(nameof(rng));
            }
        }
示例#2
0
        /// <summary>
        /// Handles PropertyChanged events from the persistence service.
        /// </summary>
        /// <param name="sender">The persistence service.</param>
        /// <param name="e">EventArgs for the property change.</param>
        private void PersistenceServicePropertyChangedHandler(object sender, PropertyChangedEventArgs e)
        {
            IDatabasePersistenceService service = sender as IDatabasePersistenceService;

            DebugHelper.Assert(service != null);

            if (e.PropertyName == nameof(service.IsSaving))
            {
                MessageBus.Publish(new SavingStateChangeMessage(service.IsSaving));
            }
        }
示例#3
0
        public async Task Init()
        {
            // Get database from test attributes
            Utils.DatabaseInfo dbInfo = await Utils.GetDatabaseInfoForTest(TestContext);

            this.dbPassword = dbInfo.Password;
            this.dbKeyFile  = dbInfo.Keyfile;

            // Assert that databases named *ReadOnly* are actually readonly after a clone
            if (dbInfo.Database.Name.IndexOf("ReadOnly", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                Assert.IsFalse(
                    await dbInfo.Database.CheckWritableAsync(),
                    $"This file is expected to be read-only; please verify this before testing: {dbInfo.Database.Name}"
                    );
            }

            this.saveFile = (await dbInfo.Database.AsIStorageFile.CopyAsync(
                                 ApplicationData.Current.TemporaryFolder,
                                 $"PersistenceTestDb-{Guid.NewGuid()}.kdbx",
                                 NameCollisionOption.ReplaceExisting
                                 )).AsWrapper();

            // Use a KdbxReader to parse the database and get a corresponding writer
            KdbxReader reader = new KdbxReader();

            using (IRandomAccessStream stream = await this.saveFile.AsIStorageFile.OpenReadAsync())
            {
                await reader.ReadHeaderAsync(stream, CancellationToken.None);

                KdbxDecryptionResult decryption = await reader.DecryptFileAsync(stream, dbInfo.Password, dbInfo.Keyfile, CancellationToken.None);

                Assert.AreEqual(KdbxParserCode.Success, decryption.Result.Code);
                this.document = decryption.GetDocument();
            }

            // Construct services we can use for the test
            this.writer             = reader.GetWriter();
            this.persistenceService = new DefaultFilePersistenceService(
                this.writer,
                this.writer,
                new StorageFileDatabaseCandidate(this.saveFile, true),
                new MockSyncContext(),
                true
                );
            this.credentialStorage = new MockCredentialProvider();
            this.masterKeyVm       = new MasterKeyChangeViewModel(
                this.document,
                this.saveFile,
                new DatabaseCredentialProvider(this.persistenceService, this.credentialStorage),
                new MockFileService()
                );
            this.settingsVm = new DatabaseSettingsViewModel(this.writer);
        }
示例#4
0
 /// <summary>
 /// Creates a ViewModel wrapping a brand new KdbxGroup as a child of the specified parent group.
 /// </summary>
 /// <param name="navigationViewModel">A ViewModel used for tracking navigation history.</param>
 /// <param name="persistenceService">A service used for persisting the document.</param>
 /// <param name="document">A KdbxDocument representing the database we are working on.</param>
 /// <param name="parentGroup">The IKeePassGroup to use as a parent for the new group.</param>
 public GroupDetailsViewModel(
     IDatabaseNavigationViewModel navigationViewModel,
     IDatabasePersistenceService persistenceService,
     KdbxDocument document,
     IKeePassGroup parentGroup
     ) : base(navigationViewModel, persistenceService, document, new KdbxGroup(parentGroup), true, false)
 {
     if (parentGroup == null)
     {
         throw new ArgumentNullException("parentGroup");
     }
 }
示例#5
0
 /// <summary>
 /// Creates a ViewModel wrapping an existing KdbxGroup.
 /// <param name="navigationViewModel">A ViewModel used for tracking navigation history.</param>
 /// <param name="persistenceService">A service used for persisting the document.</param>
 /// <param name="document">A KdbxDocument representing the database we are working on.</param>
 /// <param name="groupToEdit">The group being viewed.</param>
 /// <param name="isReadOnly">Whether to open the group in read-only mode.</param>
 public GroupDetailsViewModel(
     IDatabaseNavigationViewModel navigationViewModel,
     IDatabasePersistenceService persistenceService,
     KdbxDocument document,
     IKeePassGroup groupToEdit,
     bool isReadOnly
     ) : base(navigationViewModel, persistenceService, document, groupToEdit, false, isReadOnly)
 {
     if (groupToEdit == null)
     {
         throw new ArgumentNullException("groupToEdit");
     }
 }
示例#6
0
        /// <summary>
        /// Initializes the EventArgs with the provided parameters.
        /// </summary>
        /// <param name="document">A model representing the decrypted XML document.</param>
        /// <param name="candidate">The file corresponding to the opened document.</param>
        /// <param name="persistenceService">A service that can persist the document.</param>
        /// <param name="rng">A random number generator that can encrypt protected strings for the document.</param>
        /// <param name="keyChangeVmFactory">Factory used to generate the value of <see cref="KeyChangeViewModel"/>.</param>
        public DocumentReadyEventArgs(
            KdbxDocument document,
            IDatabaseCandidate candidate,
            IDatabasePersistenceService persistenceService,
            IRandomNumberGenerator rng,
            IMasterKeyChangeViewModelFactory keyChangeVmFactory)
        {
            Document           = document;
            Candidate          = candidate;
            PersistenceService = persistenceService;
            Rng = rng;

            KeyChangeViewModel = keyChangeVmFactory?.Assemble(document, PersistenceService, candidate.File) ?? throw new ArgumentNullException(nameof(keyChangeVmFactory));
        }
示例#7
0
 /// <summary>
 /// Creates a ViewModel wrapping an existing KdbxGroup.
 /// </summary>
 /// <param name="resourceProvider">IResourceProvider for localizing strings.</param>
 /// <param name="navigationViewModel">A ViewModel used for tracking navigation history.</param>
 /// <param name="persistenceService">A service used for persisting the document.</param>
 /// <param name="clipboardService">A service used for accessing the clipboard.</param>
 /// <param name="settingsService">A service used to access app settings.</param>
 /// <param name="document">A KdbxDocument representing the database we are working on.</param>
 /// <param name="entryToEdit">The entry being viewed.</param>
 /// <param name="isReadOnly">Whether to open the group in read-only mode.</param>
 /// <param name="rng">A random number generator used to protect strings in memory.</param>
 public EntryDetailsViewModel(
     IResourceProvider resourceProvider,
     IDatabaseNavigationViewModel navigationViewModel,
     IDatabasePersistenceService persistenceService,
     ISensitiveClipboardService clipboardService,
     IAppSettingsService settingsService,
     KdbxDocument document,
     IKeePassEntry entryToEdit,
     bool isReadOnly,
     IRandomNumberGenerator rng
     ) : this(resourceProvider, navigationViewModel, persistenceService, clipboardService, settingsService, document, entryToEdit, false, isReadOnly, rng)
 {
     if (entryToEdit == null)
     {
         throw new ArgumentNullException(nameof(entryToEdit));
     }
 }
示例#8
0
        /// <summary>
        /// Initializes the instance.
        /// </summary>
        /// <param name="syncContext">Context to use for marshalling to the UI thread.</param>
        /// <param name="timerFactory">Used to create a timer.</param>
        /// <param name="file">The file on disk represented by this database.</param>
        /// <param name="fileIsSample">Whether this file is a sample file.</param>
        /// <param name="document">The decrypted database.</param>
        /// <param name="resourceProvider">A IResourceProvider for the View.</param>
        /// <param name="rng">A random number generator used to protect strings.</param>
        /// <param name="navigationViewModel">A ViewModel representing the navigation of the database.</param>
        /// <param name="masterKeyViewModel">A ViewModel that allows configuring the database's master key.</param>
        /// <param name="persistenceService">A service used to save the database.</param>
        /// <param name="identityService">A service used to authenticate the user.</param>
        /// <param name="credentialStorage">A service used to update saved credentials.</param>
        /// <param name="settingsService">A service used to access app settings.</param>
        /// <param name="clipboardService">A service used to access the clipboard for credentials.</param>
        public DatabaseParentViewModel(
            ISyncContext syncContext,
            ITimerFactory timerFactory,
            ITestableFile file,
            bool fileIsSample,
            KdbxDocument document,
            IResourceProvider resourceProvider,
            IRandomNumberGenerator rng,
            IDatabaseNavigationViewModel navigationViewModel,
            IMasterKeyViewModel masterKeyViewModel,
            IDatabasePersistenceService persistenceService,
            IIdentityVerificationService identityService,
            ICredentialStorageProvider credentialStorage,
            IAppSettingsService settingsService,
            ISensitiveClipboardService clipboardService
            ) : base(document, persistenceService)
        {
            if (timerFactory == null)
            {
                throw new ArgumentNullException(nameof(timerFactory));
            }

            this.syncContext = syncContext ?? throw new ArgumentNullException(nameof(syncContext));
            this.idleTimer   = timerFactory.Assemble(TimeSpan.FromSeconds(1));

            this.file             = file ?? throw new ArgumentNullException(nameof(file));
            this.fileIsSample     = fileIsSample;
            this.document         = document ?? throw new ArgumentNullException(nameof(document));
            this.resourceProvider = resourceProvider ?? throw new ArgumentNullException(nameof(resourceProvider));
            this.rng = rng ?? throw new ArgumentNullException(nameof(rng));
            this.navigationViewModel = navigationViewModel ?? throw new ArgumentNullException(nameof(navigationViewModel));
            this.settingsViewModel   = new DatabaseSettingsViewModel(PersistenceService.SettingsProvider);
            this.masterKeyViewModel  = masterKeyViewModel ?? throw new ArgumentNullException(nameof(masterKeyViewModel));
            this.identityService     = identityService ?? throw new ArgumentNullException(nameof(identityService));
            this.credentialProvider  = credentialStorage ?? throw new ArgumentNullException(nameof(credentialStorage));
            this.settingsService     = settingsService ?? throw new ArgumentNullException(nameof(settingsService));
            this.clipboardService    = clipboardService ?? throw new ArgumentNullException(nameof(clipboardService));
        }
示例#9
0
        /// <summary>
        /// Initializes the base class from a subclass.
        /// </summary>
        /// <param name="navigationViewModel">A ViewModel used for tracking navigation history.</param>
        /// <param name="persistenceService">A service used for persisting the document.</param>
        /// <param name="document">A KdbxDocument representing the database we are working on.</param>
        /// <param name="item">The item this ViewModel wraps.</param>
        /// <param name="isNew">Whether the child is being created for the first time.</param>
        /// <param name="isReadOnly">Whether the child is being accessed as read-only.</param>
        protected NodeDetailsViewModel(
            IDatabaseNavigationViewModel navigationViewModel,
            IDatabasePersistenceService persistenceService,
            KdbxDocument document,
            T item,
            bool isNew,
            bool isReadOnly
            ) : base(document, persistenceService)
        {
            if (isNew && item.Parent == null)
            {
                throw new ArgumentException("Cannot create a new node with no parent!");
            }

            if (navigationViewModel.ActiveGroup != item.Parent)
            {
                throw new ArgumentException("The database's active group must be the node's parent!");
            }

            NavigationViewModel = navigationViewModel ?? throw new ArgumentNullException(nameof(navigationViewModel));
            Document            = document ?? throw new ArgumentNullException(nameof(document));
            IsNew = isNew;

            if (!isNew)
            {
                this.masterCopy = GetClone(item);
                WorkingCopy     = GetClone(this.masterCopy);
            }
            else
            {
                this.masterCopy = null;
                WorkingCopy     = GetClone(item);
            }

            IsReadOnly = isReadOnly;
        }
示例#10
0
        /// <summary>
        /// Passes provided parameters to the base constructor and initializes commands.
        /// </summary>
        /// <param name="resourceProvider">IResourceProvider for localizing strings.</param>
        /// <param name="navigationViewModel"></param>
        /// <param name="persistenceService"></param>
        /// <param name="clipboardService"></param>
        /// <param name="settingsService"></param>
        /// <param name="document"></param>
        /// <param name="entry"></param>
        /// <param name="isNew"></param>
        /// <param name="isReadOnly"></param>
        /// <param name="rng"></param>
        private EntryDetailsViewModel(
            IResourceProvider resourceProvider,
            IDatabaseNavigationViewModel navigationViewModel,
            IDatabasePersistenceService persistenceService,
            ISensitiveClipboardService clipboardService,
            IAppSettingsService settingsService,
            KdbxDocument document,
            IKeePassEntry entry,
            bool isNew,
            bool isReadOnly,
            IRandomNumberGenerator rng
            ) : base(navigationViewModel, persistenceService, document, entry, isNew, isReadOnly)
        {
            this.resourceProvider = resourceProvider;
            this.clipboardService = clipboardService;
            this.settingsService  = settingsService;
            this.rng = rng;

            this.copyFieldValueCommand = new TypedCommand <IProtectedString>(
                str =>
            {
                clipboardService.CopyCredential(str.ClearValue, ClipboardOperationType.Other);
            }
                );

            this.deleteFieldCommand = new TypedCommand <IProtectedString>(
                str => !IsReadOnly && PersistenceService.CanSave,
                str =>
            {
                DebugHelper.Assert(!IsReadOnly);
                WorkingCopy.Fields.Remove(str);
            }
                );

            this.editFieldCommand = new AsyncTypedCommand <IProtectedString>(
                str => PersistenceService.CanSave,
                async str =>
            {
                IsReadOnly = false;
                await UpdateFieldEditorViewModel(new FieldEditorViewModel(str, this.resourceProvider));
            }
                );

            this.newFieldCommand = new AsyncActionCommand(
                () => PersistenceService.CanSave,
                async() =>
            {
                IsReadOnly = false;
                await UpdateFieldEditorViewModel(new FieldEditorViewModel(this.rng, this.resourceProvider));
            }
                );

            this.commitFieldCommand = new AsyncActionCommand(
                () => FieldEditorViewModel?.CommitCommand.CanExecute(WorkingCopy) ?? false,
                async() =>
            {
                FieldEditorViewModel.CommitCommand.Execute(WorkingCopy);
                await UpdateFieldEditorViewModel(null);
            }
                );

            PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == nameof(IsReadOnly))
                {
                    ((TypedCommand <IProtectedString>)DeleteFieldCommand).RaiseCanExecuteChanged();
                }
                else if (e.PropertyName == nameof(WorkingCopy))
                {
                    OnPropertyChanged(nameof(WorkingCopyViewModel));
                }
            };
        }
示例#11
0
 /// <summary>
 /// Initializes the ViewModel base.
 /// </summary>
 /// <param name="document">The document that will be saved.</param>
 /// <param name="persistenceService">The service to use for document writing.</param>
 protected DatabasePersistenceViewModel(KdbxDocument document, IDatabasePersistenceService persistenceService)
 {
     this.document      = document ?? throw new ArgumentNullException(nameof(document));
     PersistenceService = persistenceService ?? throw new ArgumentNullException(nameof(persistenceService));
 }
 public IDatabaseCredentialProvider Assemble(IDatabasePersistenceService persistenceService)
 {
     return(new DatabaseCredentialProvider(persistenceService, this.credentialStorage));
 }
示例#13
0
 /// <summary>
 /// Generats a ViewModel representing an existing child in the DOM.
 /// </summary>
 /// <param name="navigationViewModel"></param>
 /// <param name="persistenceService"></param>
 /// <param name="document"></param>
 /// <param name="openForReadOnly"></param>
 /// <returns>A ViewModel representing an existing child in the DOM.</returns>
 protected abstract TViewModel GetExistingViewModel(
     IDatabaseNavigationViewModel navigationViewModel,
     IDatabasePersistenceService persistenceService,
     KdbxDocument document,
     bool openForReadOnly
     );
示例#14
0
 /// <summary>
 /// Generates a ViewModel representing a new child.
 /// </summary>
 /// <param name="navigationViewModel"></param>
 /// <param name="persistenceService"></param>
 /// <param name="document"></param>
 /// <param name="parent"></param>
 /// <returns>A ViewModel representing a new child not already in the tree.</returns>
 protected abstract TViewModel GetNewViewModel(
     IDatabaseNavigationViewModel navigationViewModel,
     IDatabasePersistenceService persistenceService,
     KdbxDocument document,
     IKeePassGroup parent
     );
 /// <summary>
 /// Initializes the provider with the provided dependencies.
 /// </summary>
 /// <param name="persistenceService">Used to save the database after updating credentials.</param>
 /// <param name="credentialStorage">Used to manage previously saved credentials.</param>
 public DatabaseCredentialProvider(IDatabasePersistenceService persistenceService, ICredentialStorageProvider credentialStorage)
 {
     this.persistenceService = persistenceService ?? throw new ArgumentNullException(nameof(persistenceService));
     this.credentialStorage  = credentialStorage ?? throw new ArgumentNullException(nameof(credentialStorage));
 }