1 #include <QLoggingCategory>
2 #include <QSignalSpy>
3 #include <QTest>
4 #include <cstdint>
5 #include <functional>
6 
7 #include "pushnotificationstestutils.h"
8 #include "pushnotifications.h"
9 
10 Q_LOGGING_CATEGORY(lcFakeWebSocketServer, "nextcloud.test.fakewebserver", QtInfoMsg)
11 
FakeWebSocketServer(quint16 port,QObject * parent)12 FakeWebSocketServer::FakeWebSocketServer(quint16 port, QObject *parent)
13     : QObject(parent)
14     , _webSocketServer(new QWebSocketServer(QStringLiteral("Fake Server"), QWebSocketServer::NonSecureMode, this))
15 {
16     if (!_webSocketServer->listen(QHostAddress::Any, port)) {
17         Q_UNREACHABLE();
18     }
19     connect(_webSocketServer, &QWebSocketServer::newConnection, this, &FakeWebSocketServer::onNewConnection);
20     connect(_webSocketServer, &QWebSocketServer::closed, this, &FakeWebSocketServer::closed);
21     qCInfo(lcFakeWebSocketServer) << "Open fake websocket server on port:" << port;
22     _processTextMessageSpy = std::make_unique<QSignalSpy>(this, &FakeWebSocketServer::processTextMessage);
23 }
24 
~FakeWebSocketServer()25 FakeWebSocketServer::~FakeWebSocketServer()
26 {
27     close();
28 }
29 
authenticateAccount(const OCC::AccountPtr account,std::function<void (OCC::PushNotifications * pushNotifications)> beforeAuthentication,std::function<void (void)> afterAuthentication)30 QWebSocket *FakeWebSocketServer::authenticateAccount(const OCC::AccountPtr account, std::function<void(OCC::PushNotifications *pushNotifications)> beforeAuthentication, std::function<void(void)> afterAuthentication)
31 {
32     const auto pushNotifications = account->pushNotifications();
33     Q_ASSERT(pushNotifications);
34     QSignalSpy readySpy(pushNotifications, &OCC::PushNotifications::ready);
35 
36     beforeAuthentication(pushNotifications);
37 
38     // Wait for authentication
39     if (!waitForTextMessages()) {
40         return nullptr;
41     }
42 
43     // Right authentication data should be sent
44     if (textMessagesCount() != 2) {
45         return nullptr;
46     }
47 
48     const auto socket = socketForTextMessage(0);
49     const auto userSent = textMessage(0);
50     const auto passwordSent = textMessage(1);
51 
52     if (userSent != account->credentials()->user() || passwordSent != account->credentials()->password()) {
53         return nullptr;
54     }
55 
56     // Sent authenticated
57     socket->sendTextMessage("authenticated");
58 
59     // Wait for ready signal
60     readySpy.wait();
61     if (readySpy.count() != 1 || !account->pushNotifications()->isReady()) {
62         return nullptr;
63     }
64 
65     afterAuthentication();
66 
67     return socket;
68 }
69 
close()70 void FakeWebSocketServer::close()
71 {
72     if (_webSocketServer->isListening()) {
73         qCInfo(lcFakeWebSocketServer) << "Close fake websocket server";
74 
75         _webSocketServer->close();
76         qDeleteAll(_clients.begin(), _clients.end());
77     }
78 }
79 
processTextMessageInternal(const QString & message)80 void FakeWebSocketServer::processTextMessageInternal(const QString &message)
81 {
82     auto client = qobject_cast<QWebSocket *>(sender());
83     emit processTextMessage(client, message);
84 }
85 
onNewConnection()86 void FakeWebSocketServer::onNewConnection()
87 {
88     qCInfo(lcFakeWebSocketServer) << "New connection on fake websocket server";
89 
90     auto socket = _webSocketServer->nextPendingConnection();
91 
92     connect(socket, &QWebSocket::textMessageReceived, this, &FakeWebSocketServer::processTextMessageInternal);
93     connect(socket, &QWebSocket::disconnected, this, &FakeWebSocketServer::socketDisconnected);
94 
95     _clients << socket;
96 }
97 
socketDisconnected()98 void FakeWebSocketServer::socketDisconnected()
99 {
100     qCInfo(lcFakeWebSocketServer) << "Socket disconnected";
101 
102     auto client = qobject_cast<QWebSocket *>(sender());
103 
104     if (client) {
105         _clients.removeAll(client);
106         client->deleteLater();
107     }
108 }
109 
waitForTextMessages() const110 bool FakeWebSocketServer::waitForTextMessages() const
111 {
112     return _processTextMessageSpy->wait();
113 }
114 
textMessagesCount() const115 uint32_t FakeWebSocketServer::textMessagesCount() const
116 {
117     return _processTextMessageSpy->count();
118 }
119 
textMessage(int messageNumber) const120 QString FakeWebSocketServer::textMessage(int messageNumber) const
121 {
122     Q_ASSERT(0 <= messageNumber && messageNumber < _processTextMessageSpy->count());
123     return _processTextMessageSpy->at(messageNumber).at(1).toString();
124 }
125 
socketForTextMessage(int messageNumber) const126 QWebSocket *FakeWebSocketServer::socketForTextMessage(int messageNumber) const
127 {
128     Q_ASSERT(0 <= messageNumber && messageNumber < _processTextMessageSpy->count());
129     return _processTextMessageSpy->at(messageNumber).at(0).value<QWebSocket *>();
130 }
131 
clearTextMessages()132 void FakeWebSocketServer::clearTextMessages()
133 {
134     _processTextMessageSpy->clear();
135 }
136 
createAccount(const QString & username,const QString & password)137 OCC::AccountPtr FakeWebSocketServer::createAccount(const QString &username, const QString &password)
138 {
139     auto account = OCC::Account::create();
140 
141     QStringList typeList;
142     typeList.append("files");
143     typeList.append("activities");
144     typeList.append("notifications");
145 
146     QString websocketUrl("ws://localhost:12345");
147 
148     QVariantMap endpointsMap;
149     endpointsMap["websocket"] = websocketUrl;
150 
151     QVariantMap notifyPushMap;
152     notifyPushMap["type"] = typeList;
153     notifyPushMap["endpoints"] = endpointsMap;
154 
155     QVariantMap capabilitiesMap;
156     capabilitiesMap["notify_push"] = notifyPushMap;
157 
158     account->setCapabilities(capabilitiesMap);
159 
160     auto credentials = new CredentialsStub(username, password);
161     account->setCredentials(credentials);
162 
163     return account;
164 }
165 
CredentialsStub(const QString & user,const QString & password)166 CredentialsStub::CredentialsStub(const QString &user, const QString &password)
167     : _user(user)
168     , _password(password)
169 {
170 }
171 
authType() const172 QString CredentialsStub::authType() const
173 {
174     return "";
175 }
176 
user() const177 QString CredentialsStub::user() const
178 {
179     return _user;
180 }
181 
password() const182 QString CredentialsStub::password() const
183 {
184     return _password;
185 }
186 
createQNAM() const187 QNetworkAccessManager *CredentialsStub::createQNAM() const
188 {
189     return nullptr;
190 }
191 
ready() const192 bool CredentialsStub::ready() const
193 {
194     return false;
195 }
196 
fetchFromKeychain()197 void CredentialsStub::fetchFromKeychain() { }
198 
askFromUser()199 void CredentialsStub::askFromUser() { }
200 
stillValid(QNetworkReply *)201 bool CredentialsStub::stillValid(QNetworkReply * /*reply*/)
202 {
203     return false;
204 }
205 
persist()206 void CredentialsStub::persist() { }
207 
invalidateToken()208 void CredentialsStub::invalidateToken() { }
209 
forgetSensitiveData()210 void CredentialsStub::forgetSensitiveData() { }
211