Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc.
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "manager.h"
19 : #include "jamidht/conversationrepository.h"
20 : #include "jamidht/gitserver.h"
21 : #include "jamidht/jamiaccount.h"
22 : #include "../../test_runner.h"
23 : #include "archiver.h"
24 : #include "base64.h"
25 : #include "jami.h"
26 : #include "fileutils.h"
27 : #include "account_const.h"
28 : #include "account_schema.h"
29 : #include "common.h"
30 :
31 : #include <dhtnet/connectionmanager.h>
32 :
33 : #include <cppunit/TestAssert.h>
34 : #include <cppunit/TestFixture.h>
35 : #include <cppunit/extensions/HelperMacros.h>
36 :
37 : #include <git2.h>
38 : #include <condition_variable>
39 : #include <string>
40 : #include <fstream>
41 : #include <streambuf>
42 : #include <filesystem>
43 :
44 : using namespace std::string_literals;
45 : using namespace std::literals::chrono_literals;
46 : using namespace libjami::Account;
47 :
48 : namespace jami {
49 : namespace test {
50 :
51 : class AccountArchiveTest : public CppUnit::TestFixture
52 : {
53 : public:
54 8 : AccountArchiveTest()
55 8 : {
56 : // Init daemon
57 8 : libjami::init(libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
58 8 : if (not Manager::instance().initialized)
59 1 : CPPUNIT_ASSERT(libjami::start("dring-sample.yml"));
60 8 : }
61 16 : ~AccountArchiveTest() { libjami::fini(); }
62 2 : static std::string name() { return "AccountArchive"; }
63 : void setUp();
64 : void tearDown();
65 :
66 : std::string aliceId;
67 : std::string bobId;
68 : std::string bob2Id;
69 :
70 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
71 : std::mutex mtx;
72 : std::unique_lock<std::mutex> lk {mtx};
73 : std::condition_variable cv;
74 : bool aliceReady = false;
75 :
76 : private:
77 : void testExportImportNoPassword();
78 : void testExportImportNoPasswordDoubleGunzip();
79 : void testExportImportPassword();
80 : void testExportImportPasswordDoubleGunzip();
81 : void testExportDht();
82 : void testExportDhtWrongPassword();
83 : void testChangePassword();
84 : void testChangeDhtPort();
85 :
86 2 : CPPUNIT_TEST_SUITE(AccountArchiveTest);
87 1 : CPPUNIT_TEST(testExportImportNoPassword);
88 1 : CPPUNIT_TEST(testExportImportNoPasswordDoubleGunzip);
89 1 : CPPUNIT_TEST(testExportImportPassword);
90 1 : CPPUNIT_TEST(testExportImportPasswordDoubleGunzip);
91 1 : CPPUNIT_TEST(testExportDht);
92 1 : CPPUNIT_TEST(testExportDhtWrongPassword);
93 1 : CPPUNIT_TEST(testChangePassword);
94 1 : CPPUNIT_TEST(testChangeDhtPort);
95 4 : CPPUNIT_TEST_SUITE_END();
96 : };
97 :
98 : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AccountArchiveTest, AccountArchiveTest::name());
99 :
100 : void
101 8 : AccountArchiveTest::setUp()
102 : {
103 16 : auto actors = load_actors_and_wait_for_announcement("actors/alice-bob-password.yml");
104 8 : aliceId = actors["alice"];
105 8 : bobId = actors["bob"];
106 :
107 :
108 8 : confHandlers.insert(
109 16 : libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>(
110 13 : [&](const std::string& accountId,
111 : const std::map<std::string, std::string>& details) {
112 13 : if (accountId != aliceId) {
113 5 : return;
114 : }
115 : try {
116 16 : aliceReady |= accountId == aliceId
117 16 : && details.at(jami::Conf::CONFIG_ACCOUNT_REGISTRATION_STATUS) == "REGISTERED"
118 16 : && details.at(libjami::Account::VolatileProperties::DEVICE_ANNOUNCED) == "true";
119 0 : } catch (const std::out_of_range&) {}
120 8 : cv.notify_one();
121 : }));
122 8 : libjami::registerSignalHandlers(confHandlers);
123 8 : }
124 :
125 : void
126 8 : AccountArchiveTest::tearDown()
127 : {
128 8 : if (!bob2Id.empty())
129 4 : wait_for_removal_of({aliceId, bob2Id, bobId});
130 : else
131 21 : wait_for_removal_of({aliceId, bobId});
132 8 : }
133 :
134 : void
135 1 : AccountArchiveTest::testExportImportNoPassword()
136 : {
137 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
138 :
139 1 : CPPUNIT_ASSERT(aliceAccount->exportArchive("test.gz"));
140 :
141 2 : std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
142 1 : details[ConfProperties::ARCHIVE_PATH] = "test.gz";
143 :
144 1 : auto accountId = jami::Manager::instance().addAccount(details);
145 1 : wait_for_announcement_of(accountId);
146 1 : auto alice2Account = Manager::instance().getAccount<JamiAccount>(accountId);
147 1 : CPPUNIT_ASSERT(alice2Account->getUsername() == aliceAccount->getUsername());
148 1 : std::remove("test.gz");
149 1 : wait_for_removal_of(accountId);
150 1 : }
151 :
152 : void
153 1 : AccountArchiveTest::testExportImportNoPasswordDoubleGunzip()
154 : {
155 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
156 :
157 1 : CPPUNIT_ASSERT(aliceAccount->exportArchive("test.gz"));
158 2 : auto dat = fileutils::loadFile("test.gz");
159 1 : archiver::compressGzip(dat, "test.gz");
160 :
161 2 : std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
162 1 : details[ConfProperties::ARCHIVE_PATH] = "test.gz";
163 :
164 1 : auto accountId = jami::Manager::instance().addAccount(details);
165 1 : wait_for_announcement_of(accountId);
166 1 : auto alice2Account = Manager::instance().getAccount<JamiAccount>(accountId);
167 1 : CPPUNIT_ASSERT(alice2Account->getUsername() == aliceAccount->getUsername());
168 1 : std::remove("test.gz");
169 1 : wait_for_removal_of(accountId);
170 1 : }
171 :
172 : void
173 1 : AccountArchiveTest::testExportImportPassword()
174 : {
175 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
176 :
177 1 : CPPUNIT_ASSERT(bobAccount->exportArchive("test.gz", "password", "test"));
178 :
179 2 : std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
180 1 : details[ConfProperties::ARCHIVE_PATH] = "test.gz";
181 1 : details[ConfProperties::ARCHIVE_PASSWORD] = "test";
182 :
183 1 : auto accountId = jami::Manager::instance().addAccount(details);
184 1 : wait_for_announcement_of(accountId);
185 1 : auto bob2Account = Manager::instance().getAccount<JamiAccount>(accountId);
186 1 : CPPUNIT_ASSERT(bob2Account->getUsername() == bobAccount->getUsername());
187 1 : std::remove("test.gz");
188 1 : wait_for_removal_of(accountId);
189 1 : }
190 :
191 : void
192 1 : AccountArchiveTest::testExportImportPasswordDoubleGunzip()
193 : {
194 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
195 :
196 1 : CPPUNIT_ASSERT(bobAccount->exportArchive("test.gz", "password", "test"));
197 2 : auto dat = fileutils::loadFile("test.gz");
198 1 : archiver::compressGzip(dat, "test.gz");
199 :
200 2 : std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
201 1 : details[ConfProperties::ARCHIVE_PATH] = "test.gz";
202 1 : details[ConfProperties::ARCHIVE_PASSWORD] = "test";
203 :
204 1 : auto accountId = jami::Manager::instance().addAccount(details);
205 1 : wait_for_announcement_of(accountId);
206 1 : auto bob2Account = Manager::instance().getAccount<JamiAccount>(accountId);
207 1 : CPPUNIT_ASSERT(bob2Account->getUsername() == bobAccount->getUsername());
208 1 : std::remove("test.gz");
209 1 : wait_for_removal_of(accountId);
210 1 : }
211 :
212 : void
213 1 : AccountArchiveTest::testExportDht()
214 : {
215 1 : std::mutex mtx;
216 1 : std::unique_lock lk {mtx};
217 1 : std::condition_variable cv;
218 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
219 1 : std::string pin;
220 1 : confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ExportOnRingEnded>(
221 1 : [&](const std::string& accountId, int status, const std::string& p) {
222 1 : if (accountId == bobId && status == 0)
223 1 : pin = p;
224 1 : cv.notify_one();
225 1 : }));
226 1 : libjami::registerSignalHandlers(confHandlers);
227 1 : CPPUNIT_ASSERT(libjami::exportOnRing(bobId, "test"));
228 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !pin.empty(); }));
229 :
230 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
231 :
232 2 : std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
233 1 : details[ConfProperties::ARCHIVE_PIN] = pin;
234 1 : details[ConfProperties::ARCHIVE_PASSWORD] = "test";
235 :
236 1 : bob2Id = jami::Manager::instance().addAccount(details);
237 1 : wait_for_announcement_of(bob2Id);
238 1 : auto bob2Account = Manager::instance().getAccount<JamiAccount>(bob2Id);
239 1 : CPPUNIT_ASSERT(bob2Account->getUsername() == bobAccount->getUsername());
240 1 : }
241 :
242 : void
243 1 : AccountArchiveTest::testExportDhtWrongPassword()
244 : {
245 1 : std::mutex mtx;
246 1 : std::unique_lock lk {mtx};
247 1 : std::condition_variable cv;
248 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
249 : int status;
250 1 : confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ExportOnRingEnded>(
251 1 : [&](const std::string& accountId, int s, const std::string&) {
252 1 : if (accountId == bobId)
253 1 : status = s;
254 1 : cv.notify_one();
255 1 : }));
256 1 : libjami::registerSignalHandlers(confHandlers);
257 1 : CPPUNIT_ASSERT(libjami::exportOnRing(bobId, "wrong"));
258 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return status == 1; }));
259 1 : }
260 :
261 : void
262 1 : AccountArchiveTest::testChangePassword()
263 : {
264 : // Test incorrect password, should fail
265 1 : CPPUNIT_ASSERT(!libjami::changeAccountPassword(aliceId, "wrong", "new"));
266 : // Test correct password, should succeed
267 1 : CPPUNIT_ASSERT(libjami::changeAccountPassword(aliceId, "", "new"));
268 : // Now it should fail
269 1 : CPPUNIT_ASSERT(!libjami::changeAccountPassword(aliceId, "", "new"));
270 : // Remove password again (should succeed)
271 1 : CPPUNIT_ASSERT(libjami::changeAccountPassword(aliceId, "new", ""));
272 1 : }
273 :
274 : void
275 1 : AccountArchiveTest::testChangeDhtPort()
276 : {
277 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
278 :
279 1 : std::map<std::string, std::string> newDetails;
280 1 : newDetails[libjami::Account::ConfProperties::DHT_PORT] = "1412";
281 1 : aliceReady = false;
282 1 : libjami::setAccountDetails(aliceId, newDetails);
283 4 : cv.wait_for(lk, 20s, [&] { return aliceReady; });
284 :
285 1 : CPPUNIT_ASSERT(aliceAccount->dht()->getBoundPort() == 1412);
286 :
287 1 : newDetails[libjami::Account::ConfProperties::DHT_PORT] = "1413";
288 1 : aliceReady = false;
289 1 : libjami::setAccountDetails(aliceId, newDetails);
290 4 : cv.wait_for(lk, 20s, [&] { return aliceReady; });
291 :
292 1 : CPPUNIT_ASSERT(aliceAccount->dht()->getBoundPort() == 1413);
293 1 : }
294 :
295 : } // namespace test
296 : } // namespace jami
297 :
298 1 : RING_TEST_RUNNER(jami::test::AccountArchiveTest::name())
|