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