OAuth2 access token typically only has a token valid for 1 hour.
Before this patch, when the token was timing out during the sync, the
sync was aborted, and the ConnectionValidator was then requesting a new
token, so the sync can be started over.
If the discovery takes longer than the oauth2 validity, this means that
the sync can never proceed, as it would be always restarted from scratch.
With this patch, we try to transparently renew the OAuth2 token and restart
the jobs that failed because the access token was invalid.
Note that some changes were required in the GETFile job because it handled
the error itself and so it was erroring the jobs before its too late.
Issue #6814
QtKeychain provides Qt5KeychainConfig.cmake and friends nowadays, so no
need to have a less reliable and outdated find module on our end.
Also this shows that we were including keychain.h in the wrong way and
were not using the link target, so both got fixed as well.
Signed-off-by: Kevin Ottens <kevin.ottens@nextcloud.com>
The value gets already initialized by default member initialization,
so there is no need to set it again.
Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
This also fixes a couple of warnings at places (out of order init for
instance) and a potential bug in the webflow credentials / qtkeychain
integration.
Signed-off-by: Kevin Ottens <kevin.ottens@nextcloud.com>
The server reply with a code 400 when the token is invalid,
the client was understanding this error as a network error, and was retying
again with the same token.
Instead, we must rely on what the json is saying, even if the reply is
not a 200 code.
Issue https://github.com/owncloud/enterprise/issues/2777
When owncloud is restored, at boot time, it might be started before the
crendential manager. So if we detect an error, wait 10 seconds and hopefully
it'd be loaded by then.
Issues: #4274, #6522
We need to use concatPath to avoid possible double '/' in the URLs if the
account url() ends with '/'.
This has become even more of a problem since commit
d1b8370a4a which was resolving the url after
a redirect where most server actually add a '/' if the url is a folder
This is motivated by the fact that QMetaObject::noralizeSignature takes 7.35%
CPU of the LargeSyncBench. (Mostly from ABstractNetworkJob::setupConnections and
PropagateUploadFileV1::startNextChunk). It could be fixed by using normalized
signature in the connection statement, but i tought it was a good oportunity
to modernize the code.
This commit only contains calls that were automatically converted with clazy.
The query args of POST requests become the request body. If there's a
redirect, the redirected url will therefore not contain the query
arguments. Use an explicit request body to make the redirection work.
Now that csync builds as C++, this will avoid having to implement
functionalities needed by csync mandatorily in csync itself.
This library is built as part of libocsync and symbols exported
through it.
This requires a relicense of Utility as LGPL. All classes moved into
this library from src/libsync will need to be relicensed as well.
Allow upgrade path when the server removes support for oauth
Relates: https://github.com/owncloud/client/issues/5848#issuecomment-317353049
We also need to force the account to commit the config to the disk,
otherwise we may not register we are no longer using owncloud and we
risk sending the password as the token to the token refresh API call
Before commit d3b00532b1,
fetchFromKeychain was called everytime we detect that the creds are
invalid (in AccountState::slotInvalidCredentials)
But since that commit, AccountState was calling askFromUser directly,
breaking the refresh of the token.
So I made sure AccountState::slotInvalidCredentials still calls
refreshAccessToken.
Another change that was made was too be sure to clear the cookies
in HttpCredentials::invalidateToken even when we are only clearing the
access_token. That's because the session with a cookie may stay valid
longer than the access_token
The QNAM may continue to outlive both.
Rename Credentials::getQNAM() to createQNAM() while we're at it - it's
used to make a new QNAM that will subsequently be owned by the Account
object.
See d01065b9a1 for rationale.
Relates to
d40c56eda5147cf798a6
Calling forgetSensitiveData() on account deletion leads to a timer for
clearQNAMCache() being queued. Then the Account object is deleted. The
Credentials object stays alive for now because it has a deleteLater
deleter.
If the timer calls into a slot on the Credentials object, the _account
pointer will be invalid at this time.
As a workaround, move the target slot to Account - that way it will not
be called as the account object is already destroyed.
However since Account and Credentials are mutually dependent, it would
be much preferable if their lifetimes were linked, avoiding this
category of bugs.
The current behavior was introduced in
d40c56eda5 and I currently don't
understand why - maybe there's another way of dealing with the problem
that existed then.
- Add category to the all messages (they did not have it was merged right after
the patch to add category everywhere, but this code did not have it.)
- Make sure there is no warnings in the normal flow. (The wizard does a request
without authentication to determine the auth type)
Use qCInfo for anything that has general value for support and
development. Use qCWarning for any recoverable error and qCCritical
for anything that could result in data loss or would identify a serious
issue with the code.
Issue #5647
This gives more insight about the logs and allow setting fine-tuned
logging rules. The categories are set to only output Info by default
so this allows us to provide more concise logging while keeping the
ability to extract more information for a specific category when
developping or debugging customer issues.
Issue #5647
The re-enables the UI, uses Qt API for importing and
stores the certificate/key in the system keychain.
People who had set up client certs need to re-setup the account. This is ok
since it was an undocumented feature anyway.
The Qt HTTP thread calls authenticationRequired (indirectly) using a
BlockingQueuedConnection. So when we call invalidateToken from slot
connected to this signal and end up calling QNAM::clearAccessCache which
waits on the thread for 5 seconds
Backtraces:
Qt HTTP thread:
#0 0x00007ffff20c707f in pthread_cond_wait@@GLIBC_2.3.2 ()
#1 0x00007ffff43f0c0b in QWaitConditionPrivate::wait
#2 QWaitCondition::wait
#3 0x00007ffff43ea06b in QSemaphore::acquire
#4 0x00007ffff45dcf6f in QMetaObject::activate
[...]
#9 0x00007ffff45dd607 in QMetaObject::activate
#10 0x00007ffff4edbaf7 in QHttpNetworkReply::authenticationRequired
#11 0x00007ffff4e0b2b4 in QHttpNetworkConnectionPrivate::handleAuthenticateChallenge
#12 0x00007ffff4e10753 in QHttpNetworkConnectionChannel::handleStatus
#13 0x00007ffff4e11cc9 in QHttpNetworkConnectionChannel::allDone
#14 0x00007ffff4e14605 in QHttpProtocolHandler::_q_receiveReply
Main Thread:
#0 0x00007ffff20c7428 in pthread_cond_timedwait@@GLIBC_2.3.2 ()
#1 0x00007ffff43f0b56 in QWaitConditionPrivate::wait_relative (time=5000, this=0x136c580)
#2 QWaitConditionPrivate::wait (time=5000, this=0x136c580)
#3 QWaitCondition::wait (this=this@entry=0x136c788, mutex=mutex@entry=0x136c760, time=time@entry=5000)
#4 0x00007ffff43efa6e in QThread::wait (this=<optimized out>, time=time@entry=5000)
#5 0x00007ffff4e1edd3 in QNetworkAccessManagerPrivate::clearCache
#6 0x00007ffff7b6fb03 in OCC::HttpCredentials::invalidateToken()
#7 0x000000000057adb4 in OCC::AccountState::slotInvalidCredentials()
#8 0x000000000057ac76 in OCC::AccountState::slotConnectionValidatorResult(OCC::ConnectionValidator::Status, QStringList const&) ()
#9 0x00000000005ab45c in OCC::AccountState::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)
#10 0x00007ffff45dcd30 in QMetaObject::activate
#11 0x00007ffff7b78671 in OCC::ConnectionValidator::connectionResult(OCC::ConnectionValidator::Status, QStringList) ()
#12 0x00007ffff7ae2514 in OCC::ConnectionValidator::reportResult(OCC::ConnectionValidator::Status) ()
#13 0x00007ffff7ae39b7 in OCC::ConnectionValidator::slotAuthFailed(QNetworkReply*) ()
#14 0x00007ffff7b784a9 in OCC::ConnectionValidator::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) ()
#15 0x00007ffff45dcd30 in QMetaObject::activate
#16 0x00007ffff7b766dc in OCC::AbstractNetworkJob::networkError(QNetworkReply*)
#17 0x00007ffff7af9f6e in OCC::AbstractNetworkJob::slotFinished()
#18 0x00007ffff7b7654d in OCC::AbstractNetworkJob::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) ()
#20 0x00007ffff45dd607 in QMetaObject::activate
#21 0x00007ffff4edd143 in QNetworkReply::finished
#22 0x00007ffff4e3fec7 in QNetworkReplyHttpImplPrivate::finished
#23 0x00007ffff4e41818 in QNetworkReplyHttpImpl::close
#24 0x00007ffff7b7047b in OCC::HttpCredentials::slotAuthentication(QNetworkReply*, QAuthenticator*) ()
#25 0x00007ffff7b79092 in OCC::HttpCredentials::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) ()
#27 0x00007ffff45dd607 in QMetaObject::activate
#28 0x00007ffff4e1d6fb in QNetworkAccessManager::authenticationRequired
#29 0x00007ffff4e1ea07 in QNetworkAccessManagerPrivate::authenticationRequired
#30 0x00007ffff4e3c784 in QNetworkReplyHttpImplPrivate::httpAuthenticationRequired
Another case of Main Thread:
#5 0x00007ffff4e1edd3 in QNetworkAccessManagerPrivate::clearCache
#6 0x00007ffff7b6fb03 in OCC::HttpCredentials::invalidateToken()
#7 0x000000000057b1e4 in OCC::AccountState::slotInvalidCredentials() ()
#8 0x00000000005abb8a in OCC::AccountState::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) ()
#9 0x00007ffff45dcd30 in QMetaObject::activate
#10 0x00007ffff7b76ed5 in OCC::Account::invalidCredentials() ()
#11 0x00007ffff7ad55f5 in OCC::Account::handleInvalidCredentials()
#12 0x00007ffff7afa69a in OCC::AbstractNetworkJob::slotFinished()
#13 0x00007ffff7b7654d in OCC::AbstractNetworkJob::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) ()
#15 0x00007ffff45dd607 in QMetaObject::activate
#16 0x00007ffff4edd143 in QNetworkReply::finished
#17 0x00007ffff4e3fec7 in QNetworkReplyHttpImplPrivate::finished
#18 0x00007ffff4e41818 in QNetworkReplyHttpImpl::close
#19 0x00007ffff7b7047b in OCC::HttpCredentials::slotAuthentication(QNetworkReply*, QAuthenticator*) ()
#20 0x00007ffff7b79092 in OCC::HttpCredentials::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) ()
#22 0x00007ffff45dd607 in QMetaObject::activate
#23 0x00007ffff4e1d6fb in QNetworkAccessManager::authenticationRequired
Now that fetchFromKeychain is solely called from AccountState::slotInvalidCredentials
and that this one already protects the fetch call using _waitingForNewCredentials,
we can remove that extra check.
This moves the responsibility of asking the user or not for
credentials from the Credentials classes back to the AccountState.
fetch() now only extract credentials from the keychain, reports
the result to the AccountState which then decides if askFromUser()
should be called or not. The result is once more reported to the
AccounState.
This also replaces the HttpCredentials::queryPassword virtual
which now lets HttpCredentialsGui and HttpCredentialsText do it
the way that they prefer.
The original problem is that showing a popup not originated
from the main settings window while it's focused won't be
shown in front to the user.
This try not to highjack the user's attention of the user
by showing a notification when checking the connection for
valid credentials, and require the user to sign in through
the UI. There are still issues with showing that popup from
the tray icon, but the user will most likely be looking for
the popup in that case. The new sign in button directly in
the settings account works properly.