Add native lambda invoce structure for first tests
This commit is contained in:
parent
d308be58b3
commit
c7dd9e72aa
@ -27,6 +27,9 @@
|
||||
#include <QNetworkReply>
|
||||
#include <QJsonDocument>
|
||||
|
||||
|
||||
#include "sigv4utils.h"
|
||||
|
||||
namespace remoteproxy {
|
||||
|
||||
AuthenticationProcess::AuthenticationProcess(QNetworkAccessManager *manager, QObject *parent) :
|
||||
@ -50,7 +53,53 @@ void AuthenticationProcess::requestDynamicCredentials()
|
||||
connect(reply, &QNetworkReply::finished, this, &AuthenticationProcess::onDynamicCredentialsReady);
|
||||
}
|
||||
|
||||
void AuthenticationProcess::startVerificationProcess()
|
||||
void AuthenticationProcess::invokeLambdaFunction(const QString accessKey, const QString &secretAccessKey, const QString &sessionToken)
|
||||
{
|
||||
// Known configurations
|
||||
QString region = "eu-west-1";
|
||||
QString lambdaFunctionName = "system-services-authorizer-dev-checkToken";
|
||||
QString invocationType = "RequestResponse";
|
||||
QString service = "lambda";
|
||||
|
||||
// {'url_path': '/2015-03-31/functions/system-services-authorizer-dev-checkToken/invocations', 'query_string': {}, 'method': 'POST', 'headers': {'X-Amz-Invocation-Type': 'RequestResponse', 'User-Agent': 'aws-cli/1.14.44 Python/3.6.5 Linux/4.15.0-1019-aws botocore/1.8.48'}, 'body': b'{"token": "...."}', 'url': 'https://lambda.eu-west-1.amazonaws.com/2015-03-31/functions/system-services-authorizer-dev-checkToken/invocations', 'context': {'client_region': 'eu-west-1', 'client_config': <botocore.config.Config object at 0x7f44560f3128>, 'has_streaming_input': True, 'auth_type': None}}
|
||||
|
||||
// Create request map
|
||||
QVariantMap requestMap;
|
||||
requestMap.insert("token", m_token);
|
||||
QByteArray payload = QJsonDocument::fromVariant(requestMap).toJson(QJsonDocument::Compact);
|
||||
|
||||
QUrl requestUrl;
|
||||
requestUrl.setScheme("https");
|
||||
requestUrl.setHost(QString("lambda.%1.amazonaws.com").arg(region));
|
||||
requestUrl.setPath(QString("/2015-03-31/functions/%1/invocations").arg(lambdaFunctionName));
|
||||
|
||||
QNetworkRequest request(requestUrl);
|
||||
request.setRawHeader("User-Agent", QString("%1/%2 JSON-RPC/%3").arg(SERVER_NAME_STRING).arg(SERVER_VERSION_STRING).arg(API_VERSION_STRING).toUtf8());
|
||||
request.setRawHeader("Content-Type", "application/json");
|
||||
request.setRawHeader("host", requestUrl.host().toUtf8());
|
||||
request.setRawHeader("x-amz-invocation-type", invocationType.toUtf8());
|
||||
|
||||
SigV4Utils::signRequest(QNetworkAccessManager::PostOperation, request, region, service, accessKey.toUtf8(), secretAccessKey.toUtf8(), sessionToken.toUtf8(), payload);
|
||||
|
||||
qCDebug(dcAuthenticationProcess()) << "Invoke lambda function" << lambdaFunctionName;
|
||||
|
||||
qCDebug(dcAuthenticationProcess()) << "--------------------------------------------";
|
||||
qCDebug(dcAuthenticationProcess()) << request.url().toString();
|
||||
|
||||
foreach (const QByteArray &rawHeader, request.rawHeaderList()) {
|
||||
qDebug(dcAuthenticationProcess()) << request.rawHeader(rawHeader);
|
||||
}
|
||||
qCDebug(dcAuthenticationProcess()) << payload;
|
||||
qCDebug(dcAuthenticationProcess()) << "--------------------------------------------";
|
||||
|
||||
m_lambdaTimer.start();
|
||||
|
||||
QNetworkReply *reply = m_manager->get(request);
|
||||
connect(reply, &QNetworkReply::finished, this, &AuthenticationProcess::onLambdaInvokeFunctionFinished);
|
||||
|
||||
}
|
||||
|
||||
void AuthenticationProcess::startVerificationProcess(const QString accessKey, const QString &secretAccessKey, const QString &sessionToken)
|
||||
{
|
||||
if (m_process->state() != QProcess::NotRunning) {
|
||||
qCWarning(dcAuthenticationProcess()) << "Authentication process already running. Killing the running process and restart.";
|
||||
@ -65,10 +114,10 @@ void AuthenticationProcess::startVerificationProcess()
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert("AWS_DEFAULT_REGION", "eu-west-1");
|
||||
if (m_dynamicCredentials) {
|
||||
qCDebug(dcAuthenticationProcess()) << "Using dynamic credentials" << m_awsAccessKeyId << m_awsSecretAccessKey << m_awsSessionToken;
|
||||
env.insert("AWS_ACCESS_KEY_ID", m_awsAccessKeyId);
|
||||
env.insert("AWS_SECRET_ACCESS_KEY", m_awsSecretAccessKey);
|
||||
env.insert("AWS_SESSION_TOKEN", m_awsSessionToken);
|
||||
qCDebug(dcAuthenticationProcess()) << "Using dynamic credentials" << accessKey << secretAccessKey << sessionToken;
|
||||
env.insert("AWS_ACCESS_KEY_ID", accessKey);
|
||||
env.insert("AWS_SECRET_ACCESS_KEY", secretAccessKey);
|
||||
env.insert("AWS_SESSION_TOKEN", sessionToken);
|
||||
}
|
||||
m_process->setProcessEnvironment(env);
|
||||
|
||||
@ -111,11 +160,49 @@ void AuthenticationProcess::onDynamicCredentialsReady()
|
||||
QVariantMap response = jsonDoc.toVariant().toMap();
|
||||
qCDebug(dcAuthentication()) << "-->" << response;
|
||||
|
||||
m_awsAccessKeyId = response.value("AccessKeyId").toString();
|
||||
m_awsSecretAccessKey = response.value("SecretAccessKey").toString();
|
||||
m_awsSessionToken = response.value("Token").toString();
|
||||
startVerificationProcess(response.value("AccessKeyId").toString(),
|
||||
response.value("SecretAccessKey").toString(),
|
||||
response.value("Token").toString());
|
||||
}
|
||||
|
||||
void AuthenticationProcess::onLambdaInvokeFunctionFinished()
|
||||
{
|
||||
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
|
||||
qCDebug(dcAuthenticationProcess()) << "Lambda invoke request finished (" << m_lambdaTimer.elapsed() << "[ms] )";
|
||||
if (reply->error()) {
|
||||
qCWarning(dcAuthenticationProcess()) << "Dynamic credentials reply error: " << reply->errorString();
|
||||
emit authenticationFinished(Authenticator::AuthenticationErrorProxyError);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
|
||||
qCDebug(dcAuthenticationProcess()) << "Invoke lambda function response ready";
|
||||
|
||||
qCDebug(dcAuthenticationProcess()) << "--------------------------------------------";
|
||||
qCDebug(dcAuthenticationProcess()) << reply->request().url().toString();
|
||||
|
||||
foreach (const QByteArray &rawHeader, reply->rawHeaderList()) {
|
||||
qDebug(dcAuthenticationProcess()) << reply->rawHeader(rawHeader);
|
||||
}
|
||||
qCDebug(dcAuthenticationProcess()) << data;
|
||||
qCDebug(dcAuthenticationProcess()) << "--------------------------------------------";
|
||||
|
||||
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
|
||||
if(error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcAuthenticationProcess()) << "Failed to parse dynamic credentials reply data" << data << ":" << error.errorString();
|
||||
emit authenticationFinished(Authenticator::AuthenticationErrorProxyError);
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap response = jsonDoc.toVariant().toMap();
|
||||
qCDebug(dcAuthentication()) << "-->" << response;
|
||||
|
||||
startVerificationProcess();
|
||||
}
|
||||
|
||||
void AuthenticationProcess::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
@ -199,8 +286,9 @@ void AuthenticationProcess::authenticate(const QString &token)
|
||||
// Request the access information
|
||||
requestDynamicCredentials();
|
||||
} else {
|
||||
// FIXME:
|
||||
// Direct call aws cli and assume the credentials will be provided static
|
||||
startVerificationProcess();
|
||||
// startVerificationProcess();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -45,17 +45,16 @@ private:
|
||||
QString m_resultFileName;
|
||||
|
||||
bool m_dynamicCredentials = true;
|
||||
QString m_awsAccessKeyId;
|
||||
QString m_awsSecretAccessKey;
|
||||
QString m_awsSessionToken;
|
||||
|
||||
QNetworkAccessManager *m_manager = nullptr;
|
||||
QProcess *m_process = nullptr;
|
||||
QElapsedTimer m_requestTimer;
|
||||
QElapsedTimer m_lambdaTimer;
|
||||
QElapsedTimer m_processTimer;
|
||||
|
||||
void requestDynamicCredentials();
|
||||
void startVerificationProcess();
|
||||
void invokeLambdaFunction(const QString accessKey, const QString &secretAccessKey, const QString &sessionToken);
|
||||
void startVerificationProcess(const QString accessKey, const QString &secretAccessKey, const QString &sessionToken);
|
||||
void cleanUp();
|
||||
|
||||
signals:
|
||||
@ -63,6 +62,7 @@ signals:
|
||||
|
||||
private slots:
|
||||
void onDynamicCredentialsReady();
|
||||
void onLambdaInvokeFunctionFinished();
|
||||
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
public slots:
|
||||
|
||||
153
libnymea-remoteproxy/authentication/sigv4utils.cpp
Normal file
153
libnymea-remoteproxy/authentication/sigv4utils.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
#include "sigv4utils.h"
|
||||
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QCryptographicHash>
|
||||
#include <QMessageAuthenticationCode>
|
||||
#include <QtDebug>
|
||||
#include <QUrlQuery>
|
||||
#include <QList>
|
||||
|
||||
namespace remoteproxy {
|
||||
|
||||
|
||||
SigV4Utils::SigV4Utils()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SigV4Utils::signRequest(QNetworkAccessManager::Operation operation, QNetworkRequest &request, const QString ®ion, const QString &service, const QByteArray &accessKeyId, const QByteArray &secretAccessKey, const QByteArray &sessionToken, const QByteArray &payload)
|
||||
{
|
||||
QByteArray dateTime;
|
||||
if (request.rawHeaderList().contains("X-Amz-Date")) {
|
||||
dateTime = request.rawHeader("X-AMZ-Date");
|
||||
} else {
|
||||
dateTime = SigV4Utils::getCurrentDateTime();
|
||||
request.setRawHeader("X-Amz-Date", dateTime);
|
||||
}
|
||||
|
||||
if (!sessionToken.isEmpty()) {
|
||||
request.setRawHeader("x-amz-security-token", sessionToken);
|
||||
}
|
||||
|
||||
QByteArray canonicalRequest = SigV4Utils::getCanonicalRequest(operation, request, payload);
|
||||
// qDebug() << "canonical request:" << qUtf8Printable(canonicalRequest);
|
||||
QByteArray stringToSign = SigV4Utils::getStringToSign(canonicalRequest, dateTime, region.toUtf8(), service.toUtf8());
|
||||
// qDebug() << "string to sign:" << stringToSign;
|
||||
QByteArray signature = SigV4Utils::getSignature(stringToSign, secretAccessKey, dateTime, region, service);
|
||||
// qDebug() << "signature:" << signature;
|
||||
QByteArray authorizeHeader = SigV4Utils::getAuthorizationHeader(accessKeyId, dateTime, region, service, request, signature);
|
||||
|
||||
request.setRawHeader("Authorization", authorizeHeader);
|
||||
}
|
||||
|
||||
QByteArray SigV4Utils::getCurrentDateTime()
|
||||
{
|
||||
return QDateTime::currentDateTime().toUTC().toString("yyyyMMddThhmmssZ").toUtf8();
|
||||
}
|
||||
|
||||
QByteArray SigV4Utils::getCanonicalQueryString(const QNetworkRequest &request, const QByteArray &accessKeyId, const QByteArray &secretAccessKey, const QByteArray &sessionToken, const QByteArray ®ion, const QByteArray &service, const QByteArray &payload)
|
||||
{
|
||||
QByteArray algorithm = "AWS4-HMAC-SHA256";
|
||||
QByteArray dateTime = getCurrentDateTime();
|
||||
QByteArray credentialScope = getCredentialScope(algorithm, dateTime, region, service);
|
||||
|
||||
QByteArray canonicalQueryString;
|
||||
canonicalQueryString += "X-Amz-Algorithm=AWS4-HMAC-SHA256";
|
||||
canonicalQueryString += "&X-Amz-Credential=" + QByteArray(accessKeyId + '/' + credentialScope).toPercentEncoding();
|
||||
canonicalQueryString += "&X-Amz-Date=" + dateTime;
|
||||
if (request.rawHeaderList().count() > 0){
|
||||
canonicalQueryString += "&X-Amz-SignedHeaders=" + request.rawHeaderList().join(';').toLower();
|
||||
}
|
||||
|
||||
QByteArray canonicalRequest = getCanonicalRequest(QNetworkAccessManager::GetOperation, request, payload);
|
||||
|
||||
QByteArray stringToSign = getStringToSign(canonicalRequest, dateTime, region, service);
|
||||
QByteArray signature = getSignature(stringToSign, secretAccessKey, dateTime, region, service);
|
||||
|
||||
canonicalQueryString += "&X-Amz-Signature=" + signature;
|
||||
|
||||
if (!sessionToken.isEmpty()) {
|
||||
canonicalQueryString += "&X-Amz-Security-Token=" + sessionToken.toPercentEncoding();
|
||||
}
|
||||
|
||||
return canonicalQueryString;
|
||||
}
|
||||
|
||||
QByteArray SigV4Utils::getSignatureKey(const QByteArray &key, const QByteArray &date, const QByteArray ®ion, const QByteArray &service)
|
||||
{
|
||||
QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256;
|
||||
return QMessageAuthenticationCode::hash("aws4_request",
|
||||
QMessageAuthenticationCode::hash(service,
|
||||
QMessageAuthenticationCode::hash(region,
|
||||
QMessageAuthenticationCode::hash(date, "AWS4"+key,
|
||||
hashAlgorithm), hashAlgorithm), hashAlgorithm), hashAlgorithm);
|
||||
}
|
||||
|
||||
QByteArray SigV4Utils::getCanonicalRequest(QNetworkAccessManager::Operation operation, const QNetworkRequest &request, const QByteArray &payload)
|
||||
{
|
||||
QByteArray canonicalRequest;
|
||||
|
||||
QByteArray method;
|
||||
switch (operation) {
|
||||
case QNetworkAccessManager::GetOperation:
|
||||
method = "GET";
|
||||
break;
|
||||
case QNetworkAccessManager::PostOperation:
|
||||
method = "POST";
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT_X(false, "Network operation not implemented", "SigV4Utils");
|
||||
}
|
||||
QByteArray uri = request.url().path(QUrl::FullyEncoded).toUtf8();
|
||||
QUrlQuery query(request.url());
|
||||
QList<QPair<QString, QString> > queryItems = query.queryItems();
|
||||
QStringList queryItemStrings;
|
||||
for (int i = 0; i < queryItems.count(); i++) {
|
||||
QPair<QString, QString> queryItem = queryItems.at(i);
|
||||
queryItemStrings.append(queryItem.first + '=' + queryItem.second);
|
||||
}
|
||||
queryItemStrings.sort(Qt::CaseInsensitive);
|
||||
|
||||
QByteArray canonicalQueryString = queryItemStrings.join('&').toUtf8();
|
||||
|
||||
QByteArray canonicalHeaders;
|
||||
foreach(const QByteArray &headerName, request.rawHeaderList()) {
|
||||
canonicalHeaders += headerName.toLower() + ':' + request.rawHeader(headerName) + '\n';
|
||||
}
|
||||
|
||||
QByteArray payloadHash = QCryptographicHash::hash(payload, QCryptographicHash::Sha256).toHex();
|
||||
|
||||
canonicalRequest = method + '\n' + uri + '\n' + canonicalQueryString + '\n' + canonicalHeaders + '\n' + request.rawHeaderList().join(';').toLower() + '\n' + payloadHash;
|
||||
return canonicalRequest;
|
||||
}
|
||||
|
||||
QByteArray SigV4Utils::getCredentialScope(const QByteArray &algorithm, const QByteArray &dateTime, const QByteArray ®ion, const QByteArray &service)
|
||||
{
|
||||
Q_UNUSED(algorithm)
|
||||
QByteArray credentialScope = dateTime.left(8) + '/' + region + '/' + service + "/aws4_request";
|
||||
return credentialScope;
|
||||
}
|
||||
|
||||
QByteArray SigV4Utils::getStringToSign(const QByteArray &canonicalRequest, const QByteArray &dateTime, const QByteArray ®ion, const QByteArray &service)
|
||||
{
|
||||
QByteArray algorithm = "AWS4-HMAC-SHA256";
|
||||
QByteArray credentialScope = getCredentialScope(algorithm, dateTime, region, service);
|
||||
|
||||
QByteArray stringToSign = algorithm + '\n' + dateTime + '\n' + credentialScope + '\n' + QCryptographicHash::hash(canonicalRequest, QCryptographicHash::Sha256).toHex();
|
||||
return stringToSign;
|
||||
}
|
||||
|
||||
QByteArray SigV4Utils::getSignature(const QByteArray &stringToSign, const QByteArray &secretAccessKey, const QByteArray &dateTime, const QString ®ion, const QString &service)
|
||||
{
|
||||
QByteArray signingKey = getSignatureKey(secretAccessKey, dateTime.left(8), region.toUtf8(), service.toUtf8());
|
||||
QByteArray signature = QMessageAuthenticationCode::hash(stringToSign, signingKey, QCryptographicHash::Sha256).toHex();
|
||||
return signature;
|
||||
}
|
||||
|
||||
QByteArray SigV4Utils::getAuthorizationHeader(const QByteArray &accessKeyId, const QByteArray &dateTime, const QString ®ion, const QString &service, const QNetworkRequest &request, const QByteArray &signature)
|
||||
{
|
||||
QByteArray authHeader = "AWS4-HMAC-SHA256 Credential=" + accessKeyId + '/' + dateTime.left(8) + '/' + region.toUtf8() + '/' + service.toUtf8() + '/' + "aws4_request, SignedHeaders=" + request.rawHeaderList().join(';').toLower() + ", Signature=" + signature;
|
||||
return authHeader;
|
||||
}
|
||||
}
|
||||
33
libnymea-remoteproxy/authentication/sigv4utils.h
Normal file
33
libnymea-remoteproxy/authentication/sigv4utils.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef SIGV4UTILS_H
|
||||
#define SIGV4UTILS_H
|
||||
|
||||
#include <QString>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
namespace remoteproxy {
|
||||
|
||||
class SigV4Utils
|
||||
{
|
||||
public:
|
||||
SigV4Utils();
|
||||
|
||||
// Signes a request by adding the "X-AMZ-Date" (if not present) and "X-AMZ-Signature" headers
|
||||
static void signRequest(QNetworkAccessManager::Operation operation, QNetworkRequest &request, const QString ®ion, const QString &service, const QByteArray &accessKeyId, const QByteArray &secretAccessKey, const QByteArray &sessionToken = QByteArray(), const QByteArray &payload = QByteArray());
|
||||
|
||||
static QByteArray getCurrentDateTime();
|
||||
|
||||
static QByteArray getCanonicalQueryString(const QNetworkRequest &request, const QByteArray &accessKeyId, const QByteArray &secretAccessKey, const QByteArray &sessionToken, const QByteArray ®ion, const QByteArray &service, const QByteArray &payload);
|
||||
static QByteArray getCanonicalRequest(QNetworkAccessManager::Operation operation, const QNetworkRequest &request, const QByteArray &payload);
|
||||
static QByteArray getCanonicalHeaders(const QNetworkRequest &request);
|
||||
static QByteArray getCredentialScope(const QByteArray &algorithm, const QByteArray &dateTime, const QByteArray ®ion, const QByteArray &service);
|
||||
static QByteArray getStringToSign(const QByteArray &canonicalRequest, const QByteArray &dateTime, const QByteArray ®ion, const QByteArray &service);
|
||||
static QByteArray getSignatureKey(const QByteArray &key, const QByteArray &date, const QByteArray ®ion, const QByteArray &service);
|
||||
static QByteArray getSignature(const QByteArray &stringToSign, const QByteArray &secretAccessKey, const QByteArray &dateTime, const QString ®ion, const QString &service);
|
||||
static QByteArray getAuthorizationHeader(const QByteArray &accessKeyId, const QByteArray &dateTime, const QString ®ion, const QString &service, const QNetworkRequest &request, const QByteArray &signature);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SIGV4UTILS_H
|
||||
@ -24,6 +24,7 @@ HEADERS += \
|
||||
authentication/userinformation.h \
|
||||
authentication/authenticationprocess.h \
|
||||
authentication/dummyauthenticator.h \
|
||||
authentication/sigv4utils.h
|
||||
|
||||
SOURCES += \
|
||||
engine.cpp \
|
||||
@ -46,6 +47,7 @@ SOURCES += \
|
||||
authentication/userinformation.cpp \
|
||||
authentication/authenticationprocess.cpp \
|
||||
authentication/dummyauthenticator.cpp \
|
||||
authentication/sigv4utils.cpp
|
||||
|
||||
|
||||
# install header file with relative subdirectory
|
||||
|
||||
Reference in New Issue
Block a user