Passwordfile library 5.0.11
C++ library to read/write passwords from/to encrypted files
Loading...
Searching...
No Matches
passwordfiletests.cpp
Go to the documentation of this file.
2#include "../io/entry.h"
4
5#include "./utils.h"
6
7#include <cppunit/TestFixture.h>
8#include <cppunit/extensions/HelperMacros.h>
9
10using namespace std;
11using namespace Io;
12using namespace CppUtilities;
13using namespace CppUtilities::Literals;
14using namespace CPPUNIT_NS;
15
19class 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
26public:
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();
35};
36
38
42
46
51{
52 testReading("read", testFilePath("testfile1.pwmgr"), "123456", testFilePath("testfile2.pwmgr"), string(), false, false);
53}
54
55void 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 auto *const newAccount = 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
224 // remove newAccount again to check what happens if the file size decreases
225 const auto fileSize = file.size();
226 delete newAccount;
227 file.save(PasswordFileSaveFlags::Encryption | PasswordFileSaveFlags::PasswordHashing);
228 file.close();
229 file.clearEntries();
230 file.open();
231 CPPUNIT_ASSERT_LESS(fileSize, file.size());
232 file.load();
233
234 auto path = std::list<std::string>{ "newAccount" };
235 CPPUNIT_ASSERT(file.rootEntry());
236 CPPUNIT_ASSERT(!file.rootEntry()->entryByPath(path));
237}
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
Entry * entryByPath(std::list< std::string > &path, bool includeThis=true, const EntryType *creationType=nullptr)
Returns an entry specified by the provided path.
Definition entry.cpp:350
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...
const NodeEntry * rootEntry() const
Returns the root entry if present or nullptr otherwise.
void clearEntries()
Removes the root element if one is present.
void open(PasswordFileOpenFlags options=PasswordFileOpenFlags::Default)
Opens the file.
void load()
Reads the contents of the file.
void close()
Closes the file if currently opened.
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...
void setPassword(const std::string &password)
Sets the current password.
bool isEncryptionUsed()
Returns an indication whether encryption is used and the file is open; returns always false otherwise...
std::size_t size()
Returns the size of the file if the file is open; otherwise returns zero.
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.
void setPath(const std::string &value)
Sets the current file path.
std::string & extendedHeader()
Returns the extended header.
void doBackup()
Creates a backup of the file.
PasswordFileSaveFlags saveOptions() const
Returns the save options used the last time when saving the file.
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)