-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
135 additions
and
136 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,177 +1,176 @@ | ||
#include "googlecloudoauth2.h" | ||
|
||
#include "jwt-cpp/jwt.h" | ||
|
||
#include <QFile> | ||
#include <QJsonDocument> | ||
#include <QJsonObject> | ||
#include <QLoggingCategory> | ||
#include <QNetworkReply> | ||
#include <QNetworkRequest> | ||
#include <QUrl> | ||
#include <QUrlQuery> | ||
|
||
#include <QLoggingCategory> | ||
|
||
#include "jwt-cpp/jwt.h" | ||
|
||
Q_LOGGING_CATEGORY(GC_OAUTH, "gc.oauth", QtInfoMsg) | ||
|
||
using namespace Qt::StringLiterals; | ||
|
||
GoogleCloudOAuth2::GoogleCloudOAuth2(QObject *parent) | ||
: QObject(parent), m_nam(new QNetworkAccessManager(this)) {} | ||
|
||
void GoogleCloudOAuth2::setAccountCredentialFile(const QString &filename) { | ||
QFile file(filename); | ||
if (file.open(QFile::ReadOnly)) { | ||
setAccountCredentialData(file.readAll()); | ||
} else { | ||
qCWarning(GC_OAUTH) << "Failed to open credentials file" << filename | ||
<< file.errorString(); | ||
} | ||
: QObject(parent) | ||
, m_nam(new QNetworkAccessManager(this)) | ||
{ | ||
} | ||
|
||
void GoogleCloudOAuth2::setAccountCredentialData(const QByteArray &data) { | ||
QJsonParseError error; | ||
QJsonDocument doc = QJsonDocument::fromJson(data, &error); | ||
if (!error.error) { | ||
setAccountCredential(doc.object()); | ||
} else { | ||
qCWarning(GC_OAUTH) << "Failed to parse credentials data" | ||
<< error.errorString(); | ||
} | ||
void GoogleCloudOAuth2::setAccountCredentialFile(const QString &filename) | ||
{ | ||
QFile file(filename); | ||
if (file.open(QFile::ReadOnly)) { | ||
setAccountCredentialData(file.readAll()); | ||
} else { | ||
qCWarning(GC_OAUTH) << "Failed to open credentials file" << filename << file.errorString(); | ||
} | ||
} | ||
|
||
void GoogleCloudOAuth2::setAccountCredential( | ||
const QJsonObject &accountCredential) { | ||
m_credentialsFile = accountCredential; | ||
void GoogleCloudOAuth2::setAccountCredentialData(const QByteArray &data) | ||
{ | ||
QJsonParseError error; | ||
QJsonDocument doc = QJsonDocument::fromJson(data, &error); | ||
if (!error.error) { | ||
setAccountCredential(doc.object()); | ||
} else { | ||
qCWarning(GC_OAUTH) << "Failed to parse credentials data" << error.errorString(); | ||
} | ||
} | ||
|
||
void GoogleCloudOAuth2::setAccountCredential(const QJsonObject &accountCredential) | ||
{ | ||
m_credentialsFile = accountCredential; | ||
} | ||
|
||
QJsonObject GoogleCloudOAuth2::accountCredential() const { | ||
return m_credentialsFile; | ||
QJsonObject GoogleCloudOAuth2::accountCredential() const | ||
{ | ||
return m_credentialsFile; | ||
} | ||
|
||
QNetworkAccessManager *GoogleCloudOAuth2::networkAccessManager() const { | ||
return m_nam; | ||
QNetworkAccessManager *GoogleCloudOAuth2::networkAccessManager() const | ||
{ | ||
return m_nam; | ||
} | ||
|
||
void GoogleCloudOAuth2::setNetworkAccessManager(QNetworkAccessManager *nam) { | ||
if (m_nam) { | ||
delete m_nam; | ||
} | ||
m_nam = nam; | ||
void GoogleCloudOAuth2::setNetworkAccessManager(QNetworkAccessManager *nam) | ||
{ | ||
if (m_nam) { | ||
delete m_nam; | ||
} | ||
m_nam = nam; | ||
} | ||
|
||
QNetworkRequest GoogleCloudOAuth2::defaultRequest(const QUrl &url) const { | ||
QNetworkRequest req(url); | ||
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); | ||
// req.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); // Not | ||
// yet Google? | ||
req.setRawHeader(QByteArrayLiteral("AUTHORIZATION"), m_accessTokenHeader); | ||
QNetworkRequest GoogleCloudOAuth2::defaultRequest(const QUrl &url) const | ||
{ | ||
QNetworkRequest req(url); | ||
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); | ||
// req.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); // Not | ||
// yet Google? | ||
req.setRawHeader("AUTHORIZATION"_ba, m_accessTokenHeader); | ||
|
||
return req; | ||
return req; | ||
} | ||
|
||
QByteArray GoogleCloudOAuth2::accessTokenHeader() const { | ||
return m_accessTokenHeader; | ||
QByteArray GoogleCloudOAuth2::accessTokenHeader() const | ||
{ | ||
return m_accessTokenHeader; | ||
} | ||
|
||
void GoogleCloudOAuth2::setScopes(const QStringList &scopes) { | ||
m_scopes = scopes; | ||
void GoogleCloudOAuth2::setScopes(const QStringList &scopes) | ||
{ | ||
m_scopes = scopes; | ||
} | ||
|
||
void GoogleCloudOAuth2::getAccessToken() { | ||
const QString tokenUri = m_credentialsFile[u"token_uri"].toString(); | ||
const QString privateKey = m_credentialsFile[u"private_key"].toString(); | ||
|
||
try { | ||
auto algo = jwt::algorithm::rs256{std::string(), privateKey.toStdString()}; | ||
|
||
auto token = | ||
jwt::create() | ||
.set_type("JWT") | ||
.set_issuer( | ||
m_credentialsFile[u"client_email"].toString().toStdString()) | ||
.set_payload_claim("scope", | ||
jwt::claim(m_scopes.join(u' ').toStdString())) | ||
.set_audience(tokenUri.toStdString()) | ||
.set_issued_at(std::chrono::system_clock::now()) | ||
.set_expires_at(std::chrono::system_clock::now() + | ||
std::chrono::seconds{3600}) | ||
.sign(algo); | ||
|
||
QUrlQuery query; | ||
query.addQueryItem(u"grant_type"_s, | ||
u"urn:ietf:params:oauth:grant-type:jwt-bearer"_s); | ||
query.addQueryItem(u"assertion"_s, QString::fromStdString(token)); | ||
|
||
QNetworkRequest req(QUrl{tokenUri}); | ||
req.setHeader(QNetworkRequest::ContentTypeHeader, | ||
"application/x-www-form-urlencoded"_ba); | ||
|
||
QNetworkReply *reply = | ||
m_nam->post(req, query.toString(QUrl::FullyEncoded).toLatin1()); | ||
connect(reply, &QNetworkReply::finished, this, [=, this] { | ||
reply->deleteLater(); | ||
const QByteArray data = reply->readAll(); | ||
m_token = QJsonObject(); | ||
m_accessTokenHeader.clear(); | ||
GoogleCloudReply gcr; | ||
|
||
if (!reply->error()) { | ||
QJsonParseError error; | ||
QJsonDocument doc = QJsonDocument::fromJson(data, &error); | ||
if (!error.error) { | ||
m_token = doc.object(); | ||
m_accessTokenHeader = QByteArrayLiteral("Bearer ") + | ||
m_token.value(QStringLiteral("access_token")) | ||
.toString() | ||
.toLatin1(); | ||
m_expires = | ||
m_token.value(QLatin1String("expires_in")).toInt() * 1000 - | ||
30'000; | ||
m_expiresTimer.start(); | ||
qCDebug(GC_OAUTH) << "Got Access Token" << m_token; | ||
} else { | ||
gcr.error = true; | ||
qCWarning(GC_OAUTH) << "Failed to parse google token file" << data | ||
<< error.errorString(); | ||
} | ||
} else { | ||
void GoogleCloudOAuth2::getAccessToken() | ||
{ | ||
const QString tokenUri = m_credentialsFile[u"token_uri"].toString(); | ||
const QString privateKey = m_credentialsFile[u"private_key"].toString(); | ||
|
||
try { | ||
auto algo = jwt::algorithm::rs256{std::string(), privateKey.toStdString()}; | ||
|
||
auto token = | ||
jwt::create() | ||
.set_type("JWT") | ||
.set_issuer(m_credentialsFile[u"client_email"].toString().toStdString()) | ||
.set_payload_claim("scope", jwt::claim(m_scopes.join(u' ').toStdString())) | ||
.set_audience(tokenUri.toStdString()) | ||
.set_issued_at(std::chrono::system_clock::now()) | ||
.set_expires_at(std::chrono::system_clock::now() + std::chrono::seconds{3600}) | ||
.sign(algo); | ||
|
||
QUrlQuery query; | ||
query.addQueryItem(u"grant_type"_s, u"urn:ietf:params:oauth:grant-type:jwt-bearer"_s); | ||
query.addQueryItem(u"assertion"_s, QString::fromStdString(token)); | ||
|
||
QNetworkRequest req(QUrl{tokenUri}); | ||
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"_ba); | ||
|
||
QNetworkReply *reply = m_nam->post(req, query.toString(QUrl::FullyEncoded).toLatin1()); | ||
connect(reply, &QNetworkReply::finished, this, [=, this] { | ||
reply->deleteLater(); | ||
const QByteArray data = reply->readAll(); | ||
m_token = QJsonObject(); | ||
m_accessTokenHeader.clear(); | ||
GoogleCloudReply gcr; | ||
|
||
if (!reply->error()) { | ||
QJsonParseError error; | ||
QJsonDocument doc = QJsonDocument::fromJson(data, &error); | ||
if (!error.error) { | ||
m_token = doc.object(); | ||
m_accessTokenHeader = | ||
"Bearer "_ba + m_token.value(u"access_token").toString().toLatin1(); | ||
m_expires = m_token.value(u"expires_in").toInt() * 1000 - 30'000; | ||
m_expiresTimer.start(); | ||
qCDebug(GC_OAUTH) << "Got Access Token" << m_token; | ||
} else { | ||
gcr.error = true; | ||
qCWarning(GC_OAUTH) | ||
<< "Failed to parse google token file" << data << error.errorString(); | ||
} | ||
} else { | ||
gcr.error = true; | ||
qCWarning(GC_OAUTH) << "Get Access Token failed" << reply->error(); | ||
} | ||
|
||
for (const auto &code : m_codeToRun) { | ||
if (code.receiver) { | ||
code.code(gcr); | ||
} | ||
} | ||
m_codeToRun.clear(); | ||
|
||
m_gettingToken = false; | ||
}); | ||
m_gettingToken = true; | ||
} catch (const jwt::error::rsa_exception &e) { | ||
qCDebug(GC_OAUTH) << "FAILED RSA:" << e.what(); | ||
GoogleCloudReply gcr; | ||
gcr.error = true; | ||
qCWarning(GC_OAUTH) << "Get Access Token failed" << reply->error(); | ||
} | ||
|
||
for (const auto &code : m_codeToRun) { | ||
if (code.receiver) { | ||
code.code(gcr); | ||
for (const auto &code : m_codeToRun) { | ||
if (code.receiver) { | ||
code.code(gcr); | ||
} | ||
} | ||
} | ||
m_codeToRun.clear(); | ||
|
||
m_gettingToken = false; | ||
}); | ||
m_gettingToken = true; | ||
} catch (const jwt::error::rsa_exception &e) { | ||
qCDebug(GC_OAUTH) << "FAILED RSA:" << e.what(); | ||
GoogleCloudReply gcr; | ||
gcr.error = true; | ||
for (const auto &code : m_codeToRun) { | ||
if (code.receiver) { | ||
code.code(gcr); | ||
} | ||
m_codeToRun.clear(); | ||
} | ||
m_codeToRun.clear(); | ||
} | ||
} | ||
|
||
void GoogleCloudOAuth2::getAccessToken( | ||
const QObject *receiver, | ||
std::function<void(const GoogleCloudReply &)> code) { | ||
if (!m_token.isEmpty() && !m_expiresTimer.hasExpired(m_expires)) { | ||
code(GoogleCloudReply()); | ||
} else { | ||
m_codeToRun.push_back(GoogleCloudCode{receiver, code}); | ||
if (!m_gettingToken) { | ||
getAccessToken(); | ||
void GoogleCloudOAuth2::getAccessToken(const QObject *receiver, | ||
std::function<void(const GoogleCloudReply &)> code) | ||
{ | ||
if (!m_token.isEmpty() && !m_expiresTimer.hasExpired(m_expires)) { | ||
code(GoogleCloudReply()); | ||
} else { | ||
m_codeToRun.push_back(GoogleCloudCode{receiver, code}); | ||
if (!m_gettingToken) { | ||
getAccessToken(); | ||
} | ||
} | ||
} | ||
} |