Passwordfile library  5.0.6
C++ library to read/write passwords from/to encrypted files
passwordfiletests.cpp
Go to the documentation of this file.
1 #include "../io/cryptoexception.h"
2 #include "../io/entry.h"
3 #include "../io/passwordfile.h"
4 
5 #include <c++utilities/tests/testutils.h>
6 
7 #include <cppunit/TestFixture.h>
8 #include <cppunit/extensions/HelperMacros.h>
9 
10 using namespace std;
11 using namespace Io;
12 using namespace CppUtilities;
13 using namespace CppUtilities::Literals;
14 using namespace CPPUNIT_NS;
15 
19 class PasswordFileTests : public TestFixture {
20  CPPUNIT_TEST_SUITE(PasswordFileTests);
21  CPPUNIT_TEST(testReading);
22  CPPUNIT_TEST(testBasicWriting);
23  CPPUNIT_TEST(testExtendedWriting);
24  CPPUNIT_TEST_SUITE_END();
25 
26 public:
27  void setUp() override;
28  void tearDown() override;
29 
30  void testReading();
31  void testReading(const string &context, const string &testfile1path, const string &testfile1password, const string &testfile2,
32  const string &testfile2password, bool testfile2Mod, bool extendedHeaderMod);
33  void testBasicWriting();
34  void testExtendedWriting();
35 };
36 
38 
40 {
41 }
42 
44 {
45 }
46 
51 {
52  testReading("read", testFilePath("testfile1.pwmgr"), "123456", testFilePath("testfile2.pwmgr"), string(), false, false);
53 }
54 
55 void PasswordFileTests::testReading(const string &context, const string &testfile1path, const string &testfile1password, const string &testfile2,
56  const string &testfile2password, bool testfilesMod, bool extendedHeaderMod)
57 {
58  PasswordFile file;
59 
60  // open testfile 1 ...
61  file.setPath(testfile1path);
62  file.open(PasswordFileOpenFlags::ReadOnly);
63 
64  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, !testfile1password.empty(), file.isEncryptionUsed());
65  // attempt to decrypt using a wrong password
66  file.setPassword(testfile1password + "asdf");
67  if (!testfile1password.empty()) {
68  CPPUNIT_ASSERT_THROW(file.load(), CryptoException);
69  }
70  // attempt to decrypt using the correct password
71  file.setPassword(testfile1password);
72  file.load();
73  // test root entry
74  const NodeEntry *const rootEntry = file.rootEntry();
75  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testfile1"s, rootEntry->label());
76  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 4_st, rootEntry->children().size());
77 
78  // test testaccount1
79  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testaccount1"s, rootEntry->children()[0]->label());
80  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Account, rootEntry->children()[0]->type());
81  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "pin"s, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).name());
82  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "123456"s, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).value());
83  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, FieldType::Password, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).type());
84  CPPUNIT_ASSERT(
85  static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).tiedAccount() == static_cast<AccountEntry *>(rootEntry->children()[0]));
86  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, FieldType::Normal, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(1).type());
87  CPPUNIT_ASSERT_THROW_MESSAGE(context, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(2), out_of_range);
88 
89  // test testaccount2
90  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testaccount2"s, rootEntry->children()[1]->label());
91  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Account, rootEntry->children()[1]->type());
92  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 0_st, static_cast<AccountEntry *>(rootEntry->children()[1])->fields().size());
93 
94  // test testcategory1
95  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testcategory1"s, rootEntry->children()[2]->label());
96  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Node, rootEntry->children()[2]->type());
97  const NodeEntry *const category = static_cast<NodeEntry *>(rootEntry->children()[2]);
98  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 3_st, category->children().size());
99  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Node, category->children()[2]->type());
100  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 2_st, static_cast<NodeEntry *>(category->children()[2])->children().size());
101 
102  // test testaccount3
103  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testaccount3"s, rootEntry->children()[3]->label());
104 
105  if (!testfilesMod) {
106  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption, compression"s, flagsToString(file.saveOptions()));
107  } else if (extendedHeaderMod) {
108  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "foo"s, file.extendedHeader());
109  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption, password hashing"s, flagsToString(file.saveOptions()));
110  } else {
111  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.extendedHeader());
112  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "compression"s, flagsToString(file.saveOptions()));
113  }
114  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.encryptedExtendedHeader());
115 
116  // open testfile 2
117  file.setPath(testfile2);
118  file.open(PasswordFileOpenFlags::ReadOnly);
119 
120  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, !testfile2password.empty(), file.isEncryptionUsed());
121  file.setPassword(testfile2password);
122  file.load();
123  const NodeEntry *const rootEntry2 = file.rootEntry();
124  if (testfilesMod) {
125  if (extendedHeaderMod) {
126  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, static_cast<std::uint32_t>(6), file.version());
127  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption, password hashing"s, flagsToString(file.saveOptions()));
128  } else {
129  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, static_cast<std::uint32_t>(3), file.version());
130  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption"s, flagsToString(file.saveOptions()));
131  }
132  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testfile2 - modified"s, rootEntry2->label());
133  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 2_st, rootEntry2->children().size());
134  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "newAccount"s, rootEntry2->children()[1]->label());
135  } else {
136  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, static_cast<std::uint32_t>(3), file.version());
137  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testfile2"s, rootEntry2->label());
138  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 1_st, rootEntry2->children().size());
139  }
140  if (extendedHeaderMod) {
141  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "foo"s, file.extendedHeader());
142  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "bar"s, file.encryptedExtendedHeader());
143 
144  } else {
145  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.extendedHeader());
146  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.encryptedExtendedHeader());
147  }
148 }
149 
154 {
155  const string testfile1 = workingCopyPath("testfile1.pwmgr");
156  const string testfile2 = workingCopyPath("testfile2.pwmgr");
157  PasswordFile file;
158 
159  // resave testfile 1
160  file.setPath(testfile1);
161  file.open();
162  file.setPassword("123456");
163  file.load();
164  file.doBackup();
165  file.save(PasswordFileSaveFlags::Compression);
166 
167  // resave testfile 2
168  file.setPath(testfile2);
169  file.open();
170  file.load();
171  file.rootEntry()->setLabel("testfile2 - modified");
172  new AccountEntry("newAccount", file.rootEntry());
173  file.setPassword("654321");
174  file.doBackup();
175  file.save(PasswordFileSaveFlags::Encryption);
176 
177  // check results using the reading test
178  testReading("basic writing", testfile1, string(), testfile2, "654321", true, false);
179 
180  // check backup files
181  testReading("basic writing", testfile1 + ".backup", "123456", testfile2 + ".backup", string(), false, false);
182 }
183 
188 {
189  const string testfile1 = workingCopyPath("testfile1.pwmgr");
190  const string testfile2 = workingCopyPath("testfile2.pwmgr");
191  PasswordFile file;
192 
193  // resave testfile 1
194  file.setPath(testfile1);
195  file.open();
196  file.setPassword("123456");
197  file.load();
198  CPPUNIT_ASSERT_EQUAL(""s, file.extendedHeader());
199  CPPUNIT_ASSERT_EQUAL(""s, file.encryptedExtendedHeader());
200  file.doBackup();
201  file.extendedHeader() = "foo";
202  file.save(PasswordFileSaveFlags::Encryption | PasswordFileSaveFlags::PasswordHashing);
203 
204  // resave testfile 2
205  file.setPath(testfile2);
206  file.open();
207  file.load();
208  CPPUNIT_ASSERT_EQUAL(""s, file.extendedHeader());
209  CPPUNIT_ASSERT_EQUAL(""s, file.encryptedExtendedHeader());
210  file.rootEntry()->setLabel("testfile2 - modified");
211  new AccountEntry("newAccount", file.rootEntry());
212  file.setPassword("654321");
213  file.extendedHeader() = "foo";
214  file.encryptedExtendedHeader() = "bar";
215  file.doBackup();
216  file.save(PasswordFileSaveFlags::Encryption | PasswordFileSaveFlags::PasswordHashing);
217 
218  // check results using the reading test
219  testReading("extended writing", testfile1, "123456", testfile2, "654321", true, true);
220 
221  // check backup files
222  testReading("extended writing", testfile1 + ".backup", "123456", testfile2 + ".backup", string(), false, false);
223 }
The exception that is thrown when a parsing error occurs.
Definition: entry.h:170
const std::vector< Field > & fields() const
Definition: entry.h:194
The exception that is thrown when an encryption/decryption error occurs.
void setLabel(const std::string &label)
Sets the label.
Definition: entry.h:80
const std::string & label() const
Returns the label.
Definition: entry.h:70
The NodeEntry class acts as parent for other entries.
Definition: entry.h:114
const std::vector< Entry * > & children() const
Definition: entry.h:145
The PasswordFile class holds account information in the form of Entry and Field instances and provide...
Definition: passwordfile.h:40
const NodeEntry * rootEntry() const
Returns the root entry if present or nullptr otherwise.
void open(PasswordFileOpenFlags options=PasswordFileOpenFlags::Default)
Opens the file.
void load()
Reads the contents of the file.
std::uint32_t version() const
Returns the file version used the last time when saving the file (the version of the file as it is on...
Definition: passwordfile.h:198
void setPassword(const std::string &password)
Sets the current password.
Definition: passwordfile.h:133
bool isEncryptionUsed()
Returns an indication whether encryption is used and the file is open; returns always false otherwise...
void save(PasswordFileSaveFlags options=PasswordFileSaveFlags::Default)
Writes the current root entry to the file under path() replacing its previous contents.
std::string & encryptedExtendedHeader()
Returns the encrypted extended header.
Definition: passwordfile.h:181
void setPath(const std::string &value)
Sets the current file path.
std::string & extendedHeader()
Returns the extended header.
Definition: passwordfile.h:165
void doBackup()
Creates a backup of the file.
PasswordFileSaveFlags saveOptions() const
Returns the save options used the last time when saving the file.
Definition: passwordfile.h:214
The PasswordFileTests class tests the Io::PasswordFile class.
void setUp() override
void testBasicWriting()
Tests writing (and reading again) using basic features.
void testExtendedWriting()
Tests writing (and reading again) using extended features.
void testReading()
Tests reading the testfiles testfile{1,2}.pwmgr.
void tearDown() override
Contains all IO related classes.
PASSWORD_FILE_EXPORT std::string flagsToString(PasswordFileOpenFlags flags)
Returns a comma-separated string for the specified flags.
CPPUNIT_TEST_SUITE_REGISTRATION(PasswordFileTests)