From 6e60910a9d88426ea78b1ed5364284f3ef350a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Fischer?= Date: Wed, 25 Sep 2019 08:49:11 +0200 Subject: [PATCH 1/6] Created project --- .gitignore | 73 ++++++++++++++++++++++++++++++++++++++++++++ colnod-connector.pro | 23 ++++++++++++++ main.cpp | 8 +++++ 3 files changed, 104 insertions(+) create mode 100644 .gitignore create mode 100644 colnod-connector.pro create mode 100644 main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/colnod-connector.pro b/colnod-connector.pro new file mode 100644 index 0000000..a0e3fda --- /dev/null +++ b/colnod-connector.pro @@ -0,0 +1,23 @@ +QT -= gui + +CONFIG += c++11 console +CONFIG -= app_bundle + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..470a030 --- /dev/null +++ b/main.cpp @@ -0,0 +1,8 @@ +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + return a.exec(); +} From ad104c1e193dd0b262644353b87413c821b89ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Fischer?= Date: Wed, 25 Sep 2019 08:50:11 +0200 Subject: [PATCH 2/6] Add endpoints --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 96467dc..16b8047 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,15 @@ # colnod-connector - +Colsys Colnod connector ## Colnod -Acces Management System of Colsys: [Product info](http://produkty.colsys.cz/colnod/) \ No newline at end of file +Acces Management System of Colsys: [Product info](http://produkty.colsys.cz/colnod/) + +## Host + + +## Endpoints +1. POST request - `/AaaManager/authSession` - to login, return JSON with token +2. `/SubjectManager/getSubjects` - get subjects +3. `/TokenManager/getTokens` - get tokens +4. `/TokenManager/setTokens` - set tokens From 5af6057fde733f95d6f7ae0eb20740ba8dcf5459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Fischer?= Date: Tue, 1 Oct 2019 09:17:43 +0200 Subject: [PATCH 3/6] Project structure --- README.md | 8 ++++---- colnod-connector.pro | 26 ++++---------------------- src/app/app.pro | 8 ++++++++ src/colnod/colnod.pri | 9 +++++++++ src/config.json | 5 +++++ test/test.pro | 5 +++++ test/unitTests/config.json | 5 +++++ test/unitTests/unitTests.pro | 14 ++++++++++++++ 8 files changed, 54 insertions(+), 26 deletions(-) create mode 100644 src/app/app.pro create mode 100644 src/colnod/colnod.pri create mode 100644 src/config.json create mode 100644 test/test.pro create mode 100644 test/unitTests/config.json create mode 100644 test/unitTests/unitTests.pro diff --git a/README.md b/README.md index 16b8047..02aedbf 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Acces Management System of Colsys: [Product info](http://produkty.colsys.cz/coln ## Endpoints -1. POST request - `/AaaManager/authSession` - to login, return JSON with token -2. `/SubjectManager/getSubjects` - get subjects -3. `/TokenManager/getTokens` - get tokens -4. `/TokenManager/setTokens` - set tokens +1. POST request - `/AaaManager/authSession` - to login, return JSON with Auth token +2. GET request - `/SubjectManager/getSubjects` - get subjects +3. GET request - `/TokenManager/getTokens` - get tokens +4. POST request - `/TokenManager/setTokens` - set tokens diff --git a/colnod-connector.pro b/colnod-connector.pro index a0e3fda..41bdb50 100644 --- a/colnod-connector.pro +++ b/colnod-connector.pro @@ -1,23 +1,5 @@ -QT -= gui +TEMPLATE = subdirs -CONFIG += c++11 console -CONFIG -= app_bundle - -# The following define makes your compiler emit warnings if you use -# any Qt feature that has been marked deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. -DEFINES += QT_DEPRECATED_WARNINGS - -# You can also make your code fail to compile if it uses deprecated APIs. -# In order to do so, uncomment the following line. -# You can also select to disable deprecated APIs only up to a certain version of Qt. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -SOURCES += \ - main.cpp - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +SUBDIRS += \ + src/app \ + test diff --git a/src/app/app.pro b/src/app/app.pro new file mode 100644 index 0000000..619e67b --- /dev/null +++ b/src/app/app.pro @@ -0,0 +1,8 @@ +TEMPLATE = app + +QT += core sql network +CONFIG += debug console + +SOURCES += main.cpp + +include(../colnod/colnod.pri) diff --git a/src/colnod/colnod.pri b/src/colnod/colnod.pri new file mode 100644 index 0000000..0e9925b --- /dev/null +++ b/src/colnod/colnod.pri @@ -0,0 +1,9 @@ +INCLUDEPATH += $$PWD +#SRC_DIR = $$PWD +message($$PWD) +SOURCES += \ + $$PWD/requestmanager.cpp + +HEADERS += \ + $$PWD/requestmanager.h +message($$SOURCES) diff --git a/src/config.json b/src/config.json new file mode 100644 index 0000000..7ac48e0 --- /dev/null +++ b/src/config.json @@ -0,0 +1,5 @@ +{ + "host": "https://localhost:8080", + "username": "spaceti", + "password": "SuperSecretPassword" +} \ No newline at end of file diff --git a/test/test.pro b/test/test.pro new file mode 100644 index 0000000..13debd0 --- /dev/null +++ b/test/test.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + unitTests + diff --git a/test/unitTests/config.json b/test/unitTests/config.json new file mode 100644 index 0000000..7ac48e0 --- /dev/null +++ b/test/unitTests/config.json @@ -0,0 +1,5 @@ +{ + "host": "https://localhost:8080", + "username": "spaceti", + "password": "SuperSecretPassword" +} \ No newline at end of file diff --git a/test/unitTests/unitTests.pro b/test/unitTests/unitTests.pro new file mode 100644 index 0000000..b8f8c78 --- /dev/null +++ b/test/unitTests/unitTests.pro @@ -0,0 +1,14 @@ +QT -= gui +QT += core sql network testlib + +HEADERS += \ + unittest.h +SOURCES += \ + unittest.cpp + +include(../../src/colnod/colnod.pri) + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target From 0f75f32ddd212576ddfa8f6cbca53558746783dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Fischer?= Date: Tue, 1 Oct 2019 09:18:14 +0200 Subject: [PATCH 4/6] Move main --- main.cpp => src/app/main.cpp | 2 ++ 1 file changed, 2 insertions(+) rename main.cpp => src/app/main.cpp (60%) diff --git a/main.cpp b/src/app/main.cpp similarity index 60% rename from main.cpp rename to src/app/main.cpp index 470a030..5088a36 100644 --- a/main.cpp +++ b/src/app/main.cpp @@ -1,8 +1,10 @@ #include +#include "requestmanager.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); + //RequestManager *reqman = new RequestManager(); return a.exec(); } From 870f5487baa85702a057dcc84a728a36a146f32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Fischer?= Date: Tue, 1 Oct 2019 09:23:26 +0200 Subject: [PATCH 5/6] Class for communication with server --- src/colnod/requestmanager.cpp | 124 ++++++++++++++++++++++++++++++++++ src/colnod/requestmanager.h | 62 +++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 src/colnod/requestmanager.cpp create mode 100644 src/colnod/requestmanager.h diff --git a/src/colnod/requestmanager.cpp b/src/colnod/requestmanager.cpp new file mode 100644 index 0000000..959d78a --- /dev/null +++ b/src/colnod/requestmanager.cpp @@ -0,0 +1,124 @@ +#include "requestmanager.h" + +RequestManager::RequestManager() +{ + manager = new QNetworkAccessManager(); + token = ""; + loginStatus = false; + + QFile configFile(QDir::current().filePath("config.json")); + + if (!configFile.open(QIODevice::ReadOnly)) { + qCritical() << "Couldn't open config file config.json" << QDir::current().filePath("config.json"); + QCoreApplication::exit(-1); + return; + } + + this->config = QJsonDocument::fromJson(configFile.readAll()); + QObject::connect(manager, &QNetworkAccessManager::sslErrors, this, &RequestManager::onSSLError); +} + +void RequestManager::login(QString host, QString username, QString password) +{ + QNetworkRequest request; + QString hash = encryptedPassword(password); + authHeader = getLoginHeader(username, hash); + QJsonObject reqBody = { + {"password", hash}, + {"username", username} + }; + this->host = host; + + request.setUrl(host + "/AaaManager/authSession"); + request.setRawHeader("Authorization", authHeader.toLocal8Bit()); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=UTF-8"); + QJsonDocument jsonDocument; + jsonDocument.setObject(reqBody); + QNetworkReply* reply = manager->post(request, jsonDocument.toJson()); + QObject::connect(reply, &QNetworkReply::finished, [=]() { + if (reply->error()) + { + qDebug() << reply->errorString(); + loginStatus = false; + qDebug() << reply->readAll(); + return; + } + jsonResponse = reply->readAll(); + prepareAuthHeader(); + emit successLogin(); + reply->deleteLater(); + }); +} + +void RequestManager::getRequest(QString endpoint) +{ + QNetworkRequest request; + request.setUrl(host + endpoint); + request.setRawHeader("Authorization", authHeader.toLocal8Bit()); + QNetworkReply* reply = manager->post(request, QByteArray()); + QObject::connect(reply, &QNetworkReply::finished, [=]() { + if (reply->error()) + { + qDebug() << reply->errorString(); + qDebug() << reply->readAll(); + return; + } + + //print reply info + auto items = reply->rawHeaderPairs(); + for (auto item : items) + { + qDebug() << item; + } + auto reqHeaderName = reply->request().rawHeaderList(); + for (QString header : reqHeaderName) + { + qDebug() << reply->request().rawHeader(header.toUtf8()); + } + qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + + jsonResponse = reply->readAll(); + emit responseReady(); + }); +} + +QString RequestManager::getFromJson(QString json, QString member) +{ + QJsonParseError err; + QJsonDocument document = QJsonDocument::fromJson(json.toUtf8(),&err); + if (err.error != 0) + { + qDebug() << err.errorString(); + } + return document[member].toString(); +} + +QString RequestManager::encryptedPassword(QString password) +{ + return QCryptographicHash::hash(password.toLocal8Bit(), QCryptographicHash::Sha512).toHex().toUpper(); +} + +QString RequestManager::getLoginHeader(QString username, QString hash) +{ + QString authValue = username + ":" + hash; + return "Basic " + authValue.toLocal8Bit().toBase64(); +} + +void RequestManager::prepareAuthHeader() +{ + token = getFromJson(jsonResponse, "id"); + QString authValue = token + ":"; + authHeader = "Basic" + authValue.toLocal8Bit().toBase64(); +} + +void RequestManager::onSSLError(QNetworkReply *reply, const QList &errors) +{ + qDebug() << "Network SSL errors"; + for (const auto &error : errors) + { + if (error.error() == QSslError::SelfSignedCertificate) + reply->ignoreSslErrors(errors); + qDebug() << error.errorString(); + } + +} diff --git a/src/colnod/requestmanager.h b/src/colnod/requestmanager.h new file mode 100644 index 0000000..e2b1923 --- /dev/null +++ b/src/colnod/requestmanager.h @@ -0,0 +1,62 @@ +#ifndef REQUESTMANAGER_H +#define REQUESTMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class RequestManager : public QObject +{ + Q_OBJECT + +public: + RequestManager(); + void login(QString endpoint, QString username, QString password); + void getRequest(QString endpoint); + void fetchSubjects(QString endpoint); + const QJsonDocument &getConfig() const {return config;} + const QString &getResponse() const {return jsonResponse;} + QString getToken(){return token;} +private: + + QString getFromJson(QString json, QString member); + QString encryptedPassword(QString password); + QString getLoginHeader(QString username, QString hash); + void prepareAuthHeader(); +private slots: + void onSSLError(QNetworkReply *reply, const QList &errors); + +signals: + void successLogin(); + void responseReady(); + + +private: + QNetworkAccessManager* manager; + + QJsonDocument config; + QString authHeader; + QString host; + QString token; + QString jsonResponse; + bool loginStatus; + QString tenant; + +}; + +#endif // REQUESTMANAGER_H From bcb927eda1e94efc2db7cea2c12b10282b31268f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Fischer?= Date: Tue, 1 Oct 2019 09:23:51 +0200 Subject: [PATCH 6/6] Unit test for testing RequestManager Class --- test/unitTests/UnitTest.cpp | 66 +++++++++++++++++++++++++++++++++++++ test/unitTests/unittest.h | 26 +++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 test/unitTests/UnitTest.cpp create mode 100644 test/unitTests/unittest.h diff --git a/test/unitTests/UnitTest.cpp b/test/unitTests/UnitTest.cpp new file mode 100644 index 0000000..abf55aa --- /dev/null +++ b/test/unitTests/UnitTest.cpp @@ -0,0 +1,66 @@ +#include "unittest.h" + +void UnitTest::loginSuccessfully() +{ + RequestManager* testAccess = new RequestManager(); + QJsonDocument config = testAccess->getConfig(); + QSignalSpy spy(testAccess, SIGNAL(successLogin())); + testAccess->login(host, email, password); + spy.wait(); + QVERIFY(spy.isValid()); + QCOMPARE(spy.count(), 1); + QString json = testAccess->getResponse(); + qDebug() << json; + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8()); + QVERIFY2(doc["id"].isString(), "No parameter 'id' in response"); +} + +//POST request w/o datas +void UnitTest::getSubjects() +{ + RequestManager* testAccess = new RequestManager(); + testAccess->login(host, email, password); + QSignalSpy spy(testAccess, SIGNAL(responseReady())); + testAccess->getRequest("/SubjectManager/getSubjects"); + spy.wait(); + QVERIFY(spy.isValid()); + QCOMPARE(spy.count(), 1); + QString json = testAccess->getResponse(); + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8()); + QVERIFY(doc.isObject()); + QVERIFY2(doc["items"].isArray(), "No parameter 'id' in response"); + qDebug() << doc["items"].toArray()[0].toObject()["id"].toString(); + QVERIFY2(doc["items"].toArray()[0].toObject()["id"].isString(), "ada"); + +} + +void UnitTest::getTokens() +{ + RequestManager* testAccess = new RequestManager(); + testAccess->login(host, email, password); + QSignalSpy spy(testAccess, SIGNAL(responseReady())); + testAccess->getRequest("/TokenManager/getTokens"); + QCOMPARE(spy.count(), 0); + spy.wait(); + QVERIFY(spy.isValid()); + QCOMPARE(spy.count(), 1); + qDebug() << testAccess->getResponse(); +} + +void UnitTest::getTokensOpenManager() +{ + RequestManager* testAccess = new RequestManager(); + testAccess->login(host, email, password); + QSignalSpy spy(testAccess, SIGNAL(responseReady())); + testAccess->getRequest("/OpenManager/getTokenSubject"); + QCOMPARE(spy.count(), 0); + spy.wait(); + QVERIFY(spy.isValid()); + QCOMPARE(spy.count(), 1); + QString response = testAccess->getResponse(); + QVERIFY(!response.isEmpty()); + qDebug() << response; +} + + +QTEST_MAIN(UnitTest) diff --git a/test/unitTests/unittest.h b/test/unitTests/unittest.h new file mode 100644 index 0000000..ae27657 --- /dev/null +++ b/test/unitTests/unittest.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + + +class UnitTest : public QObject +{ + Q_OBJECT + +private slots: + + void loginSuccessfully(); + + + void getTokens(); + void getTokensOpenManager(); + void getSubjects(); + +private: + // QString host = "https://localhost:8080"; //colnod server (ssh -L 8080:192.168.254.11:8443 penta-master) + QString host = "https://192.168.1.3:8443"; //local testing server + QString password = "spaceti1"; + QString email = "spaceti"; +};