Skip to content

Commit ced1cc5

Browse files
committed
Don't rely on legacy url to validate credentials
1 parent cff8d32 commit ced1cc5

File tree

7 files changed

+54
-184
lines changed

7 files changed

+54
-184
lines changed

src/gui/accountstate.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,8 @@ void AccountState::checkConnectivity(bool blockJobs)
422422
// already connected.
423423
if (blockJobs) {
424424
_connectionValidator->setClearCookies(true);
425-
mode = ConnectionValidator::ValidationMode::ValidateAuth;
426-
} else {
427-
mode = ConnectionValidator::ValidationMode::ValidateAuthAndUpdate;
428425
}
426+
mode = ConnectionValidator::ValidationMode::ValidateAuthAndUpdate;
429427
} else {
430428
// Check the server and then the auth.
431429
if (_waitingForNewCredentials) {

src/gui/connectionvalidator.cpp

Lines changed: 20 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,6 @@ namespace OCC {
3333

3434
Q_LOGGING_CATEGORY(lcConnectionValidator, "sync.connectionvalidator", QtInfoMsg)
3535

36-
// Make sure the timeout for this job is less than how often we get called
37-
// This makes sure we get tried often enough without "ConnectionValidator already running"
38-
namespace {
39-
auto timeoutToUse()
40-
{
41-
return std::min(ConnectionValidator::DefaultCallingInterval - 5s, AbstractNetworkJob::httpTimeout);
42-
};
43-
}
4436

4537
ConnectionValidator::ConnectionValidator(AccountPtr account, QObject *parent)
4638
: QObject(parent)
@@ -169,92 +161,36 @@ void ConnectionValidator::slotStatusFound(const QUrl &url, const QJsonObject &in
169161
}
170162
// now check the authentication
171163
if (_mode != ConnectionValidator::ValidationMode::ValidateServer) {
172-
checkAuthentication();
173-
} else {
174-
reportResult(Connected);
175-
}
176-
}
177-
178-
void ConnectionValidator::checkAuthentication()
179-
{
180-
// simply GET the WebDAV root, will fail if credentials are wrong.
181-
// continue in slotAuthCheck here :-)
182-
qCDebug(lcConnectionValidator) << "# Check whether authenticated propfind works.";
183-
184-
// we explicitly use a legacy dav path here
185-
auto *job = new PropfindJob(_account, _account->url(), QStringLiteral("/remote.php/webdav/"), PropfindJob::Depth::Zero, this);
186-
job->setAuthenticationJob(true); // don't retry
187-
job->setTimeout(timeoutToUse());
188-
job->setProperties({ QByteArrayLiteral("getlastmodified") });
189-
connect(job, &PropfindJob::finishedWithoutError, this, &ConnectionValidator::slotAuthSuccess);
190-
connect(job, &PropfindJob::finishedWithError, this, &ConnectionValidator::slotAuthFailed);
191-
job->start();
192-
}
193-
194-
void ConnectionValidator::slotAuthFailed()
195-
{
196-
auto job = qobject_cast<PropfindJob *>(sender());
197-
Status stat = Timeout;
198-
199-
if (job->reply()->error() == QNetworkReply::SslHandshakeFailedError) {
200-
_errors << job->errorStringParsingBody();
201-
stat = NetworkInformation::instance()->isBehindCaptivePortal() ? CaptivePortal : SslError;
202-
203-
} else if (job->reply()->error() == QNetworkReply::AuthenticationRequiredError) {
204-
qCWarning(lcConnectionValidator) << "******** Authentication failed!" << job->reply()->error() << job;
205-
_errors << tr("Authentication error");
206-
stat = CredentialsWrong;
207-
} else if (job->reply()->error() == QNetworkReply::ContentAccessDenied) {
208-
stat = ClientUnsupported;
209-
_errors << extractErrorMessage(job->reply()->readAll());
210-
} else if (job->reply()->error() != QNetworkReply::NoError) {
211-
_errors << job->errorStringParsingBody();
212-
213-
if (job->httpStatusCode() == 503) {
214-
_errors.clear();
215-
stat = ServiceUnavailable;
216-
}
217-
}
218-
219-
reportResult(stat);
220-
}
221-
222-
void ConnectionValidator::slotAuthSuccess()
223-
{
224-
_errors.clear();
225-
if (_mode != ConnectionValidator::ValidationMode::ValidateAuth) {
226164
auto *fetchSetting = new FetchServerSettingsJob(_account, this);
227165
const auto unsupportedServerError = [this] {
228166
_errors.append({tr("The configured server for this client is too old."), tr("Please update to the latest server and restart the client.")});
229167
};
230-
connect(fetchSetting, &FetchServerSettingsJob::finishedSignal, this, [unsupportedServerError, this](FetchServerSettingsJob::Result result) {
231-
switch (result) {
232-
case FetchServerSettingsJob::Result::UnsupportedServer:
233-
unsupportedServerError();
234-
reportResult(ServerVersionMismatch);
235-
break;
236-
case FetchServerSettingsJob::Result::InvalidCredentials:
237-
reportResult(CredentialsWrong);
238-
break;
239-
case FetchServerSettingsJob::Result::TimeOut:
240-
reportResult(Timeout);
241-
break;
242-
case FetchServerSettingsJob::Result::Success:
243-
if (_account->serverSupportLevel() == Account::ServerSupportLevel::Unknown) {
168+
connect(fetchSetting, &FetchServerSettingsJob::finishedSignal, this,
169+
[unsupportedServerError, this](ConnectionValidator::Status result, const QString &error) {
170+
if (!error.isEmpty()) {
171+
_errors.append(error);
172+
}
173+
switch (result) {
174+
case ServerVersionMismatch:
244175
unsupportedServerError();
176+
reportResult(ServerVersionMismatch);
177+
break;
178+
case Connected:
179+
if (_account->serverSupportLevel() == Account::ServerSupportLevel::Unknown) {
180+
unsupportedServerError();
181+
}
182+
[[fallthrough]];
183+
default:
184+
reportResult(result);
185+
break;
245186
}
246-
reportResult(Connected);
247-
break;
248-
case FetchServerSettingsJob::Result::Undefined:
249-
reportResult(Undefined);
250-
break;
251-
}
252-
});
187+
});
253188

254189
fetchSetting->start();
255190
return;
191+
} else {
192+
reportResult(Connected);
256193
}
257-
reportResult(Connected);
258194
}
259195

260196
void ConnectionValidator::reportResult(Status status)

src/gui/connectionvalidator.h

Lines changed: 1 addition & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -29,64 +29,13 @@
2929

3030
namespace OCC {
3131

32-
/**
33-
* This is a job-like class to check that the server is up and that we are connected.
34-
* There are two entry points: checkServerAndAuth and checkAuthentication
35-
* checkAuthentication is the quick version that only does the propfind
36-
* while checkServerAndAuth is doing the 4 calls.
37-
*
38-
* We cannot use the capabilites call to test the login and the password because of
39-
* https://github.com/owncloud/core/issues/12930
40-
*
41-
* Here follows the state machine
42-
43-
\code{.unparsed}
44-
*---> checkServerAndAuth (check status.php)
45-
Will asynchronously check for system proxy (if using system proxy)
46-
And then invoke slotCheckServerAndAuth
47-
CheckServerJob
48-
|
49-
+-> slotNoStatusFound --> X
50-
|
51-
+-> slotJobTimeout --> X
52-
|
53-
+-> slotStatusFound --+--> X (if credentials are still missing)
54-
|
55-
+---------------------------+
56-
|
57-
*-+-> checkAuthentication (PROPFIND on root)
58-
PropfindJob
59-
|
60-
+-> slotAuthFailed --> X
61-
|
62-
+-> slotAuthSuccess --+--> X (depending if coming from checkServerAndAuth or not)
63-
|
64-
+---------------------------+
65-
|
66-
+-> checkServerCapabilities
67-
JsonApiJob (cloud/capabilities) -> slotCapabilitiesRecieved -+
68-
|
69-
+------------------------------------------------------------------+
70-
|
71-
+-> fetchUser -+
72-
|
73-
+-> AvatarJob
74-
|
75-
+-> slotAvatarImage --> reportResult()
76-
77-
\endcode
78-
*/
7932
class OPENCLOUD_GUI_EXPORT ConnectionValidator : public QObject
8033
{
8134
Q_OBJECT
8235
public:
8336
explicit ConnectionValidator(AccountPtr account, QObject *parent = nullptr);
8437

85-
enum class ValidationMode {
86-
ValidateServer,
87-
ValidateAuth,
88-
ValidateAuthAndUpdate
89-
};
38+
enum class ValidationMode { ValidateServer, ValidateAuthAndUpdate };
9039
Q_ENUM(ValidationMode)
9140

9241
enum Status {
@@ -127,15 +76,10 @@ public Q_SLOTS:
12776
void sslErrors(const QList<QSslError> &errors);
12877

12978
protected Q_SLOTS:
130-
/// Checks authentication only.
131-
void checkAuthentication();
13279
void slotCheckServerAndAuth();
13380

13481
void slotStatusFound(const QUrl &url, const QJsonObject &info);
13582

136-
void slotAuthFailed();
137-
void slotAuthSuccess();
138-
13983
private:
14084
void reportResult(Status status);
14185

src/gui/fetchserversettings.cpp

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616

1717
#include "gui/accountstate.h"
1818
#include "gui/connectionvalidator.h"
19+
#include "gui/networkinformation.h"
1920

2021
#include "libsync/networkjobs/jsonjob.h"
2122

23+
2224
using namespace std::chrono_literals;
2325

2426
using namespace OCC;
@@ -67,37 +69,44 @@ void FetchServerSettingsJob::start()
6769
case Account::ServerSupportLevel::Supported:
6870
break;
6971
case Account::ServerSupportLevel::Unsupported:
70-
Q_EMIT finishedSignal(Result::UnsupportedServer);
72+
Q_EMIT finishedSignal(ConnectionValidator::ServerVersionMismatch);
7173
return;
7274
}
7375
auto *userJob = new JsonApiJob(_account, QStringLiteral("ocs/v2.php/cloud/user"), SimpleNetworkJob::UrlQuery{}, QNetworkRequest{}, this);
7476
userJob->setAuthenticationJob(isAuthJob());
7577
userJob->setTimeout(fetchSettingsTimeout());
7678
connect(userJob, &JsonApiJob::finishedSignal, this, [userJob, this] {
7779
if (userJob->timedOut()) {
78-
Q_EMIT finishedSignal(Result::TimeOut);
80+
Q_EMIT finishedSignal(ConnectionValidator::Timeout);
7981
} else if (userJob->httpStatusCode() == 401) {
80-
Q_EMIT finishedSignal(Result::InvalidCredentials);
82+
Q_EMIT finishedSignal(ConnectionValidator::CredentialsWrong);
8183
} else if (userJob->ocsSuccess()) {
8284
const auto userData = userJob->data().value(QStringLiteral("ocs")).toObject().value(QStringLiteral("data")).toObject();
8385
const QString displayName = userData.value(QStringLiteral("display-name")).toString();
8486
if (!displayName.isEmpty()) {
8587
_account->setDavDisplayName(displayName);
8688
}
8789
runAsyncUpdates();
88-
Q_EMIT finishedSignal(Result::Success);
90+
Q_EMIT finishedSignal(ConnectionValidator::Connected);
8991
} else {
90-
Q_EMIT finishedSignal(Result::Undefined);
92+
Q_EMIT finishedSignal(ConnectionValidator::Undefined);
9193
}
9294
});
9395
userJob->start();
9496
} else {
95-
if (job->timedOut()) {
96-
Q_EMIT finishedSignal(Result::TimeOut);
97+
if (job->reply()->error() == QNetworkReply::ContentAccessDenied) {
98+
Q_EMIT finishedSignal(ConnectionValidator::ClientUnsupported, extractErrorMessage(job->reply()->readAll()));
99+
} else if (job->reply()->error() == QNetworkReply::SslHandshakeFailedError) {
100+
Q_EMIT finishedSignal(
101+
NetworkInformation::instance()->isBehindCaptivePortal() ? ConnectionValidator::CaptivePortal : ConnectionValidator::SslError);
102+
} else if (job->timedOut()) {
103+
Q_EMIT finishedSignal(ConnectionValidator::Timeout);
97104
} else if (job->httpStatusCode() == 401) {
98-
Q_EMIT finishedSignal(Result::InvalidCredentials);
105+
Q_EMIT finishedSignal(ConnectionValidator::CredentialsWrong);
106+
} else if (job->httpStatusCode() == 503) {
107+
Q_EMIT finishedSignal(ConnectionValidator::ServiceUnavailable);
99108
} else {
100-
Q_EMIT finishedSignal(Result::Undefined);
109+
Q_EMIT finishedSignal(ConnectionValidator::Undefined);
101110
}
102111
}
103112
});

src/gui/fetchserversettings.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#pragma once
1616

17+
#include "gui/connectionvalidator.h"
1718
#include "libsync/accountfwd.h"
1819

1920
#include <QObject>
@@ -25,14 +26,12 @@ class FetchServerSettingsJob : public QObject
2526
{
2627
Q_OBJECT
2728
public:
28-
enum class Result { Success, TimeOut, InvalidCredentials, UnsupportedServer, Undefined };
29-
Q_ENUM(Result);
3029
FetchServerSettingsJob(const AccountPtr &account, QObject *parent);
3130

3231
void start();
3332

3433
Q_SIGNALS:
35-
void finishedSignal(Result);
34+
void finishedSignal(ConnectionValidator::Status, QString errorMessage = {});
3635

3736
private:
3837
void runAsyncUpdates();

test/testconnectionvalidator/testconnectionvalidator.cpp

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class TestConnectionValidator : public QObject
3030
{
3131
Q_OBJECT
3232

33-
enum class FailStage { Invalid, StatusPhp, AuthValidation, Capabilities, UserInfo };
33+
enum class FailStage { Invalid, StatusPhp, Capabilities, UserInfo };
3434

3535
// we can't use QMap direclty with QFETCH
3636
using Values = QMap<QString, QString>;
@@ -60,20 +60,17 @@ private Q_SLOTS:
6060
const auto defaultValue = Values{{QStringLiteral("maintenance"), QStringLiteral("false")}, {QStringLiteral("version"), QStringLiteral("10.11.0.0")},
6161
{QStringLiteral("productversion"), QStringLiteral("4.0.5")}};
6262

63-
QTest::newRow("stauts.php maintenance") << FailStage::StatusPhp << [value = defaultValue]() mutable {
63+
QTest::newRow("status.php maintenance") << FailStage::StatusPhp << [value = defaultValue]() mutable {
6464
value[QStringLiteral("maintenance")] = QStringLiteral("true");
6565
return value;
6666
}() << ConnectionValidator::MaintenanceMode;
67-
QTest::newRow("stauts.php ServiceUnavailable") << FailStage::StatusPhp << defaultValue << ConnectionValidator::StatusNotFound;
68-
QTest::newRow("stauts.php UnsupportedClient") << FailStage::StatusPhp << defaultValue << ConnectionValidator::ClientUnsupported;
69-
70-
QTest::newRow("auth 401") << FailStage::AuthValidation << defaultValue << ConnectionValidator::CredentialsWrong;
71-
QTest::newRow("auth timeout") << FailStage::AuthValidation << defaultValue << ConnectionValidator::Timeout;
72-
QTest::newRow("auth ServiceUnavailable") << FailStage::AuthValidation << defaultValue << ConnectionValidator::ServiceUnavailable;
73-
QTest::newRow("auth UnsupportedClient") << FailStage::AuthValidation << defaultValue << ConnectionValidator::ClientUnsupported;
67+
QTest::newRow("status.php ServiceUnavailable") << FailStage::StatusPhp << defaultValue << ConnectionValidator::StatusNotFound;
68+
QTest::newRow("status.php UnsupportedClient") << FailStage::StatusPhp << defaultValue << ConnectionValidator::ClientUnsupported;
7469

7570
QTest::newRow("capabilites timeout") << FailStage::Capabilities << defaultValue << ConnectionValidator::CredentialsWrong;
7671
QTest::newRow("capabilites 401") << FailStage::Capabilities << defaultValue << ConnectionValidator::Timeout;
72+
QTest::newRow("capabilites ServiceUnavailable") << FailStage::Capabilities << defaultValue << ConnectionValidator::ServiceUnavailable;
73+
QTest::newRow("capabilites UnsupportedClient") << FailStage::Capabilities << defaultValue << ConnectionValidator::ClientUnsupported;
7774
QTest::newRow("capabilites unsupported server") << FailStage::Capabilities << [value = defaultValue]() mutable {
7875
value[QStringLiteral("version")] = QStringLiteral("7.0");
7976
value[QStringLiteral("productversion")] = QString();
@@ -116,6 +113,10 @@ private Q_SLOTS:
116113
if (failStage == FailStage::Capabilities) {
117114
if (status == ConnectionValidator::CredentialsWrong) {
118115
return new FakeErrorReply(op, request, this, 401);
116+
} else if (status == ConnectionValidator::ClientUnsupported) {
117+
return new FakeErrorReply(op, request, this, 403);
118+
} else if (status == ConnectionValidator::ServiceUnavailable) {
119+
return new FakeErrorReply(op, request, this, 503);
119120
} else if (status == ConnectionValidator::Timeout) {
120121
return new FakeHangingReply(op, request, this);
121122
}
@@ -132,17 +133,6 @@ private Q_SLOTS:
132133
}
133134
return new FakePayloadReply(op, request, getPayload(QStringLiteral("user.json")), this);
134135
}
135-
} else if (failStage == FailStage::AuthValidation && verb == "PROPFIND") {
136-
reachedStage = FailStage::AuthValidation;
137-
if (status == ConnectionValidator::CredentialsWrong) {
138-
return new FakeErrorReply(op, request, this, 401);
139-
} else if (status == ConnectionValidator::Timeout) {
140-
return new FakeHangingReply(op, request, this);
141-
} else if (status == ConnectionValidator::ServiceUnavailable) {
142-
return new FakeErrorReply(op, request, this, 503);
143-
} else if (status == ConnectionValidator::ClientUnsupported) {
144-
return new FakeErrorReply(op, request, this, 403);
145-
}
146136
}
147137
return nullptr;
148138
});

test/testutils/syncenginetestutils.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,14 +1133,8 @@ QString getFilePathFromUrl(const QUrl &url)
11331133
const QString path = url.path();
11341134
const QString sRootUrl = OCC::TestUtils::dummyDavUrl().path();
11351135

1136-
const QString legacyConnectionValidator = QStringLiteral("/remote.php/webdav/");
1137-
1138-
1139-
QString out;
11401136
if (path.startsWith(sRootUrl)) {
1141-
out = path.mid(sRootUrl.length());
1142-
} else if (path.startsWith(legacyConnectionValidator)) {
1143-
out = path.mid(legacyConnectionValidator.length());
1137+
return OCC::Utility::stripLeadingSlash(path.mid(sRootUrl.length()));
11441138
}
1145-
return OCC::Utility::stripLeadingSlash(out);
1139+
return {};
11461140
}

0 commit comments

Comments
 (0)