Passwordfile library 5.0.7
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
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
40{
41}
42
44{
45}
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 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)