Port dropbox authentication to oauth2
This commit is contained in:
parent
a2f471d75d
commit
415e6dc5e8
|
@ -1088,12 +1088,10 @@ optional_source(HAVE_GOOGLE_DRIVE
|
|||
# Dropbox support
|
||||
optional_source(HAVE_DROPBOX
|
||||
SOURCES
|
||||
internet/dropbox/dropboxauthenticator.cpp
|
||||
internet/dropbox/dropboxservice.cpp
|
||||
internet/dropbox/dropboxsettingspage.cpp
|
||||
internet/dropbox/dropboxurlhandler.cpp
|
||||
HEADERS
|
||||
internet/dropbox/dropboxauthenticator.h
|
||||
internet/dropbox/dropboxservice.h
|
||||
internet/dropbox/dropboxsettingspage.h
|
||||
internet/dropbox/dropboxurlhandler.h
|
||||
|
|
|
@ -67,7 +67,9 @@ void OAuthenticator::StartAuthorisation(const QString& oauth_endpoint,
|
|||
}
|
||||
|
||||
url.addQueryItem("redirect_uri", redirect_url.toString());
|
||||
url.addQueryItem("scope", scope);
|
||||
if (!scope.isEmpty()) { // Empty scope is valid for Dropbox.
|
||||
url.addQueryItem("scope", scope);
|
||||
}
|
||||
|
||||
NewClosure(server, SIGNAL(Finished()), this, &OAuthenticator::RedirectArrived,
|
||||
server, redirect_url);
|
||||
|
@ -115,7 +117,8 @@ void OAuthenticator::RequestAccessToken(const QByteArray& code,
|
|||
"application/x-www-form-urlencoded");
|
||||
|
||||
QNetworkReply* reply = network_.post(request, post_data.toUtf8());
|
||||
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(SslErrors(QList<QSslError>)));
|
||||
connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
|
||||
SLOT(SslErrors(QList<QSslError>)));
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(FetchAccessTokenFinished(QNetworkReply*)), reply);
|
||||
}
|
||||
|
|
|
@ -1,181 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dropboxauthenticator.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <qjson/parser.h>
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QStringList>
|
||||
#include <QTcpSocket>
|
||||
|
||||
#include "core/closure.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/network.h"
|
||||
#include "internet/core/localredirectserver.h"
|
||||
|
||||
namespace {
|
||||
static const char* kAppKey = "qh6ca27eclt9p2k";
|
||||
static const char* kAppSecret = "pg7y68h5efap8r6";
|
||||
|
||||
// OAuth 1.0 endpoints
|
||||
static const char* kRequestTokenEndpoint =
|
||||
"https://api.dropbox.com/1/oauth/request_token";
|
||||
static const char* kAuthoriseEndpoint =
|
||||
"https://www.dropbox.com/1/oauth/authorize";
|
||||
static const char* kAccessTokenEndpoint =
|
||||
"https://api.dropbox.com/1/oauth/access_token";
|
||||
|
||||
// Dropbox API endpoints
|
||||
static const char* kAccountInfoEndpoint =
|
||||
"https://api.dropbox.com/1/account/info";
|
||||
|
||||
} // namespace
|
||||
|
||||
DropboxAuthenticator::DropboxAuthenticator(QObject* parent)
|
||||
: QObject(parent), network_(new NetworkAccessManager(this)) {}
|
||||
|
||||
void DropboxAuthenticator::StartAuthorisation() {
|
||||
QUrl url(kRequestTokenEndpoint);
|
||||
QByteArray authorisation_header =
|
||||
GenerateAuthorisationHeader(QString::null, QString::null);
|
||||
QNetworkRequest request(url);
|
||||
request.setRawHeader("Authorization", authorisation_header);
|
||||
|
||||
QNetworkReply* reply = network_->post(request, QByteArray());
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(RequestTokenFinished(QNetworkReply*)), reply);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Parse a string like a=b&c=d into a map.
|
||||
QMap<QString, QString> ParseParamList(const QString& params) {
|
||||
QMap<QString, QString> ret;
|
||||
QStringList components = params.split("&");
|
||||
for (const QString& component : components) {
|
||||
QStringList pairs = component.split("=");
|
||||
if (pairs.size() != 2) {
|
||||
continue;
|
||||
}
|
||||
ret[pairs[0]] = pairs[1];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void DropboxAuthenticator::RequestTokenFinished(QNetworkReply* reply) {
|
||||
reply->deleteLater();
|
||||
QString result = reply->readAll();
|
||||
QMap<QString, QString> params = ParseParamList(result);
|
||||
token_ = params["oauth_token"];
|
||||
secret_ = params["oauth_token_secret"];
|
||||
Authorise();
|
||||
}
|
||||
|
||||
void DropboxAuthenticator::Authorise() {
|
||||
LocalRedirectServer* server = new LocalRedirectServer(this);
|
||||
server->Listen();
|
||||
|
||||
NewClosure(server, SIGNAL(Finished()), this,
|
||||
SLOT(RedirectArrived(LocalRedirectServer*)), server);
|
||||
|
||||
QUrl url(kAuthoriseEndpoint);
|
||||
url.addQueryItem("oauth_token", token_);
|
||||
url.addQueryItem("oauth_callback", server->url().toString());
|
||||
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
|
||||
void DropboxAuthenticator::RedirectArrived(LocalRedirectServer* server) {
|
||||
server->deleteLater();
|
||||
QUrl request_url = server->request_url();
|
||||
qLog(Debug) << Q_FUNC_INFO << request_url;
|
||||
uid_ = request_url.queryItemValue("uid");
|
||||
RequestAccessToken();
|
||||
}
|
||||
|
||||
void DropboxAuthenticator::RequestAccessToken() {
|
||||
QUrl url(kAccessTokenEndpoint);
|
||||
QNetworkRequest request(url);
|
||||
QByteArray authorisation_header =
|
||||
GenerateAuthorisationHeader(token_, secret_);
|
||||
request.setRawHeader("Authorization", authorisation_header);
|
||||
|
||||
QNetworkReply* reply = network_->post(request, QByteArray());
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(RequestAccessTokenFinished(QNetworkReply*)), reply);
|
||||
}
|
||||
|
||||
void DropboxAuthenticator::RequestAccessTokenFinished(QNetworkReply* reply) {
|
||||
reply->deleteLater();
|
||||
QString result = QString::fromAscii(reply->readAll());
|
||||
qLog(Debug) << result;
|
||||
QMap<QString, QString> params = ParseParamList(result);
|
||||
access_token_ = params["oauth_token"];
|
||||
access_token_secret_ = params["oauth_token_secret"];
|
||||
qLog(Debug) << Q_FUNC_INFO << access_token_ << access_token_secret_;
|
||||
RequestAccountInformation();
|
||||
}
|
||||
|
||||
QByteArray DropboxAuthenticator::GenerateAuthorisationHeader() {
|
||||
return GenerateAuthorisationHeader(access_token_, access_token_secret_);
|
||||
}
|
||||
|
||||
QByteArray DropboxAuthenticator::GenerateAuthorisationHeader(
|
||||
const QString& token, const QString& token_secret) {
|
||||
typedef QPair<QString, QString> Param;
|
||||
QByteArray signature =
|
||||
QUrl::toPercentEncoding(QString("%1&%2").arg(kAppSecret, token_secret));
|
||||
QList<Param> params;
|
||||
params << Param("oauth_consumer_key", kAppKey)
|
||||
<< Param("oauth_signature_method", "PLAINTEXT")
|
||||
<< Param("oauth_timestamp", QString::number(time(nullptr)))
|
||||
<< Param("oauth_nonce", QString::number(qrand()))
|
||||
<< Param("oauth_signature", signature);
|
||||
if (!token.isNull()) {
|
||||
params << Param("oauth_token", token);
|
||||
}
|
||||
QStringList encoded_params;
|
||||
for (const Param& p : params) {
|
||||
encoded_params << QString("%1=\"%2\"").arg(p.first, p.second);
|
||||
}
|
||||
QString authorisation_header = QString("OAuth ") + encoded_params.join(", ");
|
||||
return authorisation_header.toUtf8();
|
||||
}
|
||||
|
||||
void DropboxAuthenticator::RequestAccountInformation() {
|
||||
QUrl url(kAccountInfoEndpoint);
|
||||
QNetworkRequest request(url);
|
||||
request.setRawHeader("Authorization", GenerateAuthorisationHeader());
|
||||
qLog(Debug) << Q_FUNC_INFO << url << request.rawHeader("Authorization");
|
||||
QNetworkReply* reply = network_->get(request);
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(RequestAccountInformationFinished(QNetworkReply*)), reply);
|
||||
}
|
||||
|
||||
void DropboxAuthenticator::RequestAccountInformationFinished(
|
||||
QNetworkReply* reply) {
|
||||
reply->deleteLater();
|
||||
QJson::Parser parser;
|
||||
QVariantMap response = parser.parse(reply).toMap();
|
||||
name_ = response["display_name"].toString();
|
||||
emit Finished();
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2012, 2014, John Maguire <john.maguire@gmail.com>
|
||||
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INTERNET_DROPBOX_DROPBOXAUTHENTICATOR_H_
|
||||
#define INTERNET_DROPBOX_DROPBOXAUTHENTICATOR_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QTcpServer>
|
||||
|
||||
class LocalRedirectServer;
|
||||
class NetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
class DropboxAuthenticator : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DropboxAuthenticator(QObject* parent = nullptr);
|
||||
void StartAuthorisation();
|
||||
|
||||
const QString& access_token() const { return access_token_; }
|
||||
const QString& access_token_secret() const { return access_token_secret_; }
|
||||
const QString& uid() const { return uid_; }
|
||||
const QString& name() const { return name_; }
|
||||
|
||||
static QByteArray GenerateAuthorisationHeader(const QString& token,
|
||||
const QString& secret);
|
||||
|
||||
signals:
|
||||
void Finished();
|
||||
|
||||
private slots:
|
||||
void RequestTokenFinished(QNetworkReply* reply);
|
||||
void RedirectArrived(LocalRedirectServer* server);
|
||||
void RequestAccessTokenFinished(QNetworkReply* reply);
|
||||
void RequestAccountInformationFinished(QNetworkReply* reply);
|
||||
|
||||
private:
|
||||
void Authorise();
|
||||
void RequestAccessToken();
|
||||
QByteArray GenerateAuthorisationHeader();
|
||||
void RequestAccountInformation();
|
||||
|
||||
private:
|
||||
NetworkAccessManager* network_;
|
||||
QTcpServer server_;
|
||||
|
||||
// Temporary access token used for first authentication flow.
|
||||
QString token_;
|
||||
QString secret_;
|
||||
|
||||
// Permanent OAuth access tokens.
|
||||
QString access_token_;
|
||||
QString access_token_secret_;
|
||||
|
||||
// User's Dropbox uid & name.
|
||||
QString uid_;
|
||||
QString name_;
|
||||
};
|
||||
|
||||
#endif // INTERNET_DROPBOX_DROPBOXAUTHENTICATOR_H_
|
|
@ -30,7 +30,7 @@
|
|||
#include "core/player.h"
|
||||
#include "core/utilities.h"
|
||||
#include "core/waitforsignal.h"
|
||||
#include "internet/dropbox/dropboxauthenticator.h"
|
||||
#include "internet/core/oauthenticator.h"
|
||||
#include "internet/dropbox/dropboxurlhandler.h"
|
||||
#include "library/librarybackend.h"
|
||||
|
||||
|
@ -57,8 +57,8 @@ DropboxService::DropboxService(Application* app, InternetModel* parent)
|
|||
network_(new NetworkAccessManager(this)) {
|
||||
QSettings settings;
|
||||
settings.beginGroup(kSettingsGroup);
|
||||
access_token_ = settings.value("access_token").toString();
|
||||
access_token_secret_ = settings.value("access_token_secret").toString();
|
||||
// OAuth2 version of dropbox auth token.
|
||||
access_token_ = settings.value("access_token2").toString();
|
||||
app->player()->RegisterUrlHandler(new DropboxUrlHandler(this, this));
|
||||
}
|
||||
|
||||
|
@ -74,19 +74,14 @@ void DropboxService::Connect() {
|
|||
}
|
||||
}
|
||||
|
||||
void DropboxService::AuthenticationFinished(
|
||||
DropboxAuthenticator* authenticator) {
|
||||
void DropboxService::AuthenticationFinished(OAuthenticator* authenticator) {
|
||||
authenticator->deleteLater();
|
||||
|
||||
access_token_ = authenticator->access_token();
|
||||
access_token_secret_ = authenticator->access_token_secret();
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup(kSettingsGroup);
|
||||
|
||||
settings.setValue("access_token", access_token_);
|
||||
settings.setValue("access_token_secret", access_token_secret_);
|
||||
settings.setValue("name", authenticator->name());
|
||||
settings.setValue("access_token2", access_token_);
|
||||
|
||||
emit Connected();
|
||||
|
||||
|
@ -94,8 +89,7 @@ void DropboxService::AuthenticationFinished(
|
|||
}
|
||||
|
||||
QByteArray DropboxService::GenerateAuthorisationHeader() {
|
||||
return DropboxAuthenticator::GenerateAuthorisationHeader(
|
||||
access_token_, access_token_secret_);
|
||||
return QString("Bearer %1").arg(access_token_).toUtf8();
|
||||
}
|
||||
|
||||
void DropboxService::RequestFileList() {
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
|
||||
#include "core/tagreaderclient.h"
|
||||
|
||||
class DropboxAuthenticator;
|
||||
class NetworkAccessManager;
|
||||
class OAuthenticator;
|
||||
class QNetworkReply;
|
||||
|
||||
class DropboxService : public CloudFileService {
|
||||
|
@ -40,12 +40,12 @@ class DropboxService : public CloudFileService {
|
|||
|
||||
QUrl GetStreamingUrlFromSongId(const QUrl& url);
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void Connected();
|
||||
|
||||
public slots:
|
||||
void Connect();
|
||||
void AuthenticationFinished(DropboxAuthenticator* authenticator);
|
||||
void AuthenticationFinished(OAuthenticator* authenticator);
|
||||
|
||||
private slots:
|
||||
void RequestFileListFinished(QNetworkReply* reply);
|
||||
|
@ -60,7 +60,6 @@ class DropboxService : public CloudFileService {
|
|||
|
||||
private:
|
||||
QString access_token_;
|
||||
QString access_token_secret_;
|
||||
|
||||
NetworkAccessManager* network_;
|
||||
};
|
||||
|
|
|
@ -20,11 +20,21 @@
|
|||
#include "ui_dropboxsettingspage.h"
|
||||
|
||||
#include "core/application.h"
|
||||
#include "internet/dropbox/dropboxauthenticator.h"
|
||||
#include "internet/core/oauthenticator.h"
|
||||
#include "internet/dropbox/dropboxservice.h"
|
||||
#include "internet/core/internetmodel.h"
|
||||
#include "ui/settingsdialog.h"
|
||||
|
||||
namespace {
|
||||
static const char* kOAuthEndpoint =
|
||||
"https://www.dropbox.com/1/oauth2/authorize";
|
||||
static const char* kOAuthClientId = "qh6ca27eclt9p2k";
|
||||
static const char* kOAuthClientSecret = "pg7y68h5efap8r6";
|
||||
static const char* kOAuthTokenEndpoint =
|
||||
"https://api.dropboxapi.com/1/oauth2/token";
|
||||
static const char* kOAuthScope = "";
|
||||
}
|
||||
|
||||
DropboxSettingsPage::DropboxSettingsPage(SettingsDialog* parent)
|
||||
: SettingsPage(parent),
|
||||
ui_(new Ui::DropboxSettingsPage),
|
||||
|
@ -44,10 +54,10 @@ void DropboxSettingsPage::Load() {
|
|||
QSettings s;
|
||||
s.beginGroup(DropboxService::kSettingsGroup);
|
||||
|
||||
const QString name = s.value("name").toString();
|
||||
const QString access_token = s.value("access_token2").toString();
|
||||
|
||||
if (!name.isEmpty()) {
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn, name);
|
||||
if (!access_token.isEmpty()) {
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,13 +67,14 @@ void DropboxSettingsPage::Save() {
|
|||
}
|
||||
|
||||
void DropboxSettingsPage::LoginClicked() {
|
||||
DropboxAuthenticator* authenticator = new DropboxAuthenticator;
|
||||
NewClosure(authenticator, SIGNAL(Finished()), this,
|
||||
SLOT(Connected(DropboxAuthenticator*)), authenticator);
|
||||
OAuthenticator* authenticator =
|
||||
new OAuthenticator(kOAuthClientId, kOAuthClientSecret,
|
||||
OAuthenticator::RedirectStyle::REMOTE_WITH_STATE);
|
||||
connect(authenticator, SIGNAL(Finished()), SLOT(Connected()));
|
||||
NewClosure(authenticator, SIGNAL(Finished()), service_,
|
||||
SLOT(AuthenticationFinished(DropboxAuthenticator*)),
|
||||
authenticator);
|
||||
authenticator->StartAuthorisation();
|
||||
SLOT(AuthenticationFinished(OAuthenticator*)), authenticator);
|
||||
authenticator->StartAuthorisation(kOAuthEndpoint, kOAuthTokenEndpoint,
|
||||
kOAuthScope);
|
||||
|
||||
ui_->login_button->setEnabled(false);
|
||||
}
|
||||
|
@ -81,7 +92,6 @@ void DropboxSettingsPage::LogoutClicked() {
|
|||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedOut);
|
||||
}
|
||||
|
||||
void DropboxSettingsPage::Connected(DropboxAuthenticator* authenticator) {
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn,
|
||||
authenticator->name());
|
||||
void DropboxSettingsPage::Connected() {
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <QModelIndex>
|
||||
#include <QWidget>
|
||||
|
||||
class DropboxAuthenticator;
|
||||
class DropboxService;
|
||||
class Ui_DropboxSettingsPage;
|
||||
|
||||
|
@ -39,12 +38,12 @@ class DropboxSettingsPage : public SettingsPage {
|
|||
void Save();
|
||||
|
||||
// QObject
|
||||
bool eventFilter(QObject* object, QEvent* event);
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void LoginClicked();
|
||||
void LogoutClicked();
|
||||
void Connected(DropboxAuthenticator* authenticator);
|
||||
void Connected();
|
||||
|
||||
private:
|
||||
Ui_DropboxSettingsPage* ui_;
|
||||
|
|
Loading…
Reference in New Issue