parent
7c921a00c7
commit
76a3f4790f
|
@ -61,6 +61,7 @@ SOURCES += \
|
||||||
src/components/tagging/tagview.cpp \
|
src/components/tagging/tagview.cpp \
|
||||||
src/components/tagging/tagwidget.cpp \
|
src/components/tagging/tagwidget.cpp \
|
||||||
src/db/databaseconnection.cpp \
|
src/db/databaseconnection.cpp \
|
||||||
|
src/forms/add_operation/createoperation.cpp \
|
||||||
src/forms/evidence_filter/evidencefilter.cpp \
|
src/forms/evidence_filter/evidencefilter.cpp \
|
||||||
src/forms/evidence_filter/evidencefilterform.cpp \
|
src/forms/evidence_filter/evidencefilterform.cpp \
|
||||||
src/forms/getinfo/getinfo.cpp \
|
src/forms/getinfo/getinfo.cpp \
|
||||||
|
@ -101,6 +102,7 @@ HEADERS += \
|
||||||
src/dtos/checkConnection.h \
|
src/dtos/checkConnection.h \
|
||||||
src/exceptions/databaseerr.h \
|
src/exceptions/databaseerr.h \
|
||||||
src/exceptions/fileerror.h \
|
src/exceptions/fileerror.h \
|
||||||
|
src/forms/add_operation/createoperation.h \
|
||||||
src/forms/evidence_filter/evidencefilter.h \
|
src/forms/evidence_filter/evidencefilter.h \
|
||||||
src/forms/evidence_filter/evidencefilterform.h \
|
src/forms/evidence_filter/evidencefilterform.h \
|
||||||
src/forms/getinfo/getinfo.h \
|
src/forms/getinfo/getinfo.h \
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef DTO_ASHIRT_ERROR_H
|
||||||
|
#define DTO_ASHIRT_ERROR_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "helpers/jsonhelpers.h"
|
||||||
|
|
||||||
|
namespace dto {
|
||||||
|
class AShirtError {
|
||||||
|
|
||||||
|
public:
|
||||||
|
QString error = "";
|
||||||
|
|
||||||
|
static AShirtError parseData(QByteArray data) {
|
||||||
|
return parseJSONItem<AShirtError>(data, AShirtError::fromJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static AShirtError fromJson(QJsonObject obj) {
|
||||||
|
AShirtError e;
|
||||||
|
e.error = obj["error"].toString();
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // DTO_ASHIRT_ERROR_H
|
|
@ -13,6 +13,10 @@ namespace dto {
|
||||||
class Operation {
|
class Operation {
|
||||||
public:
|
public:
|
||||||
Operation() {}
|
Operation() {}
|
||||||
|
Operation(QString name, QString slug) {
|
||||||
|
this->name = name;
|
||||||
|
this->slug = slug;
|
||||||
|
}
|
||||||
|
|
||||||
enum OperationStatus {
|
enum OperationStatus {
|
||||||
OperationStatusPlanning = 0,
|
OperationStatusPlanning = 0,
|
||||||
|
@ -34,6 +38,17 @@ class Operation {
|
||||||
return parseJSONList<Operation>(data, Operation::fromJson);
|
return parseJSONList<Operation>(data, Operation::fromJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QByteArray createOperationJson(QString name, QString slug) {
|
||||||
|
return createOperationJson(Operation(name, slug));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QByteArray createOperationJson(Operation o) {
|
||||||
|
QJsonObject obj;
|
||||||
|
obj.insert("slug", o.slug);
|
||||||
|
obj.insert("name", o.name);
|
||||||
|
return QJsonDocument(obj).toJson();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// provides a Operation from a given QJsonObject
|
// provides a Operation from a given QJsonObject
|
||||||
static Operation fromJson(QJsonObject obj) {
|
static Operation fromJson(QJsonObject obj) {
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
#include "createoperation.h"
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
#include "helpers/netman.h"
|
||||||
|
#include "helpers/stopreply.h"
|
||||||
|
#include "dtos/ashirt_error.h"
|
||||||
|
|
||||||
|
CreateOperation::CreateOperation(QWidget* parent) : QDialog(parent) {
|
||||||
|
buildUi();
|
||||||
|
wireUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateOperation::~CreateOperation() {
|
||||||
|
delete closeWindowAction;
|
||||||
|
delete submitButton;
|
||||||
|
delete _operationLabel;
|
||||||
|
delete responseLabel;
|
||||||
|
delete operationNameTextBox;
|
||||||
|
|
||||||
|
delete gridLayout;
|
||||||
|
stopReply(&createOpReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateOperation::buildUi() {
|
||||||
|
gridLayout = new QGridLayout(this);
|
||||||
|
|
||||||
|
submitButton = new LoadingButton("Submit", this);
|
||||||
|
submitButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
|
|
||||||
|
_operationLabel = new QLabel("Operation Name", this);
|
||||||
|
_operationLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||||
|
responseLabel = new QLabel(this);
|
||||||
|
operationNameTextBox = new QLineEdit(this);
|
||||||
|
|
||||||
|
// Layout
|
||||||
|
/* 0 1 2
|
||||||
|
+---------------+-------------+------------+
|
||||||
|
0 | Op Lbl | [Operation TB] |
|
||||||
|
+---------------+-------------+------------+
|
||||||
|
1 | Error Lbl |
|
||||||
|
+---------------+-------------+------------+
|
||||||
|
2 | <None> | <None> | Submit Btn |
|
||||||
|
+---------------+-------------+------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
// row 0
|
||||||
|
gridLayout->addWidget(_operationLabel, 0, 0);
|
||||||
|
gridLayout->addWidget(operationNameTextBox, 0, 1, 1, 2);
|
||||||
|
|
||||||
|
// row 1
|
||||||
|
gridLayout->addWidget(responseLabel, 1, 0, 1, 3);
|
||||||
|
|
||||||
|
// row 2
|
||||||
|
gridLayout->addWidget(submitButton, 2, 2);
|
||||||
|
|
||||||
|
closeWindowAction = new QAction(this);
|
||||||
|
closeWindowAction->setShortcut(QKeySequence::Close);
|
||||||
|
this->addAction(closeWindowAction);
|
||||||
|
|
||||||
|
this->setLayout(gridLayout);
|
||||||
|
this->resize(400, 1);
|
||||||
|
this->setWindowTitle("Create Operation");
|
||||||
|
|
||||||
|
Qt::WindowFlags flags = this->windowFlags();
|
||||||
|
flags |= Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowMinMaxButtonsHint |
|
||||||
|
Qt::WindowCloseButtonHint;
|
||||||
|
this->setWindowFlags(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateOperation::wireUi() {
|
||||||
|
connect(submitButton, &QPushButton::clicked, this, &CreateOperation::submitButtonClicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateOperation::submitButtonClicked() {
|
||||||
|
responseLabel->setText("");
|
||||||
|
auto name = operationNameTextBox->text().trimmed();
|
||||||
|
auto slug = makeSlugFromName(name);
|
||||||
|
|
||||||
|
if (slug == "") {
|
||||||
|
responseLabel->setText(
|
||||||
|
(name == "")
|
||||||
|
? "The Operation Name must not be empty"
|
||||||
|
: "The Operation Name must include letters or numbers"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
submitButton->startAnimation();
|
||||||
|
submitButton->setEnabled(false);
|
||||||
|
createOpReply = NetMan::getInstance().createOperation(name, slug);
|
||||||
|
connect(createOpReply, &QNetworkReply::finished, this, &CreateOperation::onRequestComplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CreateOperation::makeSlugFromName(QString name) {
|
||||||
|
static QRegularExpression invalidCharsRegex("[^A-Za-z0-9]+");
|
||||||
|
static QRegularExpression startOrEndDash("^-|-$");
|
||||||
|
|
||||||
|
return name.toLower().replace(invalidCharsRegex, "-").replace(startOrEndDash, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateOperation::onRequestComplete() {
|
||||||
|
bool isValid;
|
||||||
|
auto data = NetMan::extractResponse(createOpReply, isValid);
|
||||||
|
if (isValid) {
|
||||||
|
dto::Operation op = dto::Operation::parseData(data);
|
||||||
|
AppSettings::getInstance().setOperationDetails(op.slug, op.name);
|
||||||
|
operationNameTextBox->clear();
|
||||||
|
this->close();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dto::AShirtError err = dto::AShirtError::parseData(data);
|
||||||
|
if (err.error.contains("slug already exists")) {
|
||||||
|
responseLabel->setText("A similar operation name already exists. Please try a new name.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
responseLabel->setText("Got an unexpected error: " + err.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submitButton->stopAnimation();
|
||||||
|
submitButton->setEnabled(true);
|
||||||
|
|
||||||
|
tidyReply(&createOpReply);
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef FORM_CREATEOPERATION_H
|
||||||
|
#define FORM_CREATEOPERATION_H
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
#include "components/loading_button/loadingbutton.h"
|
||||||
|
|
||||||
|
class CreateOperation : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CreateOperation(QWidget *parent = nullptr);
|
||||||
|
~CreateOperation();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void buildUi();
|
||||||
|
void wireUi();
|
||||||
|
|
||||||
|
void submitButtonClicked();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onRequestComplete();
|
||||||
|
|
||||||
|
|
||||||
|
QString makeSlugFromName(QString name);
|
||||||
|
// void showEvent(QShowEvent *evt) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QNetworkReply* createOpReply = nullptr;
|
||||||
|
|
||||||
|
// ui elements
|
||||||
|
QGridLayout* gridLayout = nullptr;
|
||||||
|
QAction* closeWindowAction = nullptr;
|
||||||
|
LoadingButton* submitButton = nullptr;
|
||||||
|
QLabel* _operationLabel = nullptr;
|
||||||
|
QLabel* responseLabel = nullptr;
|
||||||
|
QLineEdit* operationNameTextBox = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FORM_CREATEOPERATION_H
|
|
@ -234,6 +234,13 @@ class NetMan : public QObject {
|
||||||
return builder->execute(nam);
|
return builder->execute(nam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// createOperation attempts to create a new operation with the given name and slug
|
||||||
|
QNetworkReply *createOperation(QString name, QString slug) {
|
||||||
|
auto builder = ashirtJSONPost("/api/operations", dto::Operation::createOperationJson(name, slug));
|
||||||
|
addASHIRTAuth(builder);
|
||||||
|
return builder->execute(nam);
|
||||||
|
}
|
||||||
|
|
||||||
/// extractResponse inspects the provided QNetworkReply and returns back the contents of the reply.
|
/// extractResponse inspects the provided QNetworkReply and returns back the contents of the reply.
|
||||||
/// In addition, it will also indicated, via the provided valid flag, if the response was valid.
|
/// In addition, it will also indicated, via the provided valid flag, if the response was valid.
|
||||||
/// A Valid response is one that has a 200 or 201 response AND had no errors flaged from Qt
|
/// A Valid response is one that has a 200 or 201 response AND had no errors flaged from Qt
|
||||||
|
|
|
@ -99,6 +99,7 @@ void TrayManager::buildUi() {
|
||||||
settingsWindow = new Settings(hotkeyManager, this);
|
settingsWindow = new Settings(hotkeyManager, this);
|
||||||
evidenceManagerWindow = new EvidenceManager(db, this);
|
evidenceManagerWindow = new EvidenceManager(db, this);
|
||||||
creditsWindow = new Credits(this);
|
creditsWindow = new Credits(this);
|
||||||
|
createOperationWindow = new CreateOperation(this);
|
||||||
|
|
||||||
trayIconMenu = new QMenu(this);
|
trayIconMenu = new QMenu(this);
|
||||||
chooseOpSubmenu = new QMenu(tr("Select Operation"));
|
chooseOpSubmenu = new QMenu(tr("Select Operation"));
|
||||||
|
@ -126,7 +127,10 @@ void TrayManager::buildUi() {
|
||||||
currentOperationMenuAction->setEnabled(false);
|
currentOperationMenuAction->setEnabled(false);
|
||||||
chooseOpStatusAction = new QAction("Loading operations...", chooseOpSubmenu);
|
chooseOpStatusAction = new QAction("Loading operations...", chooseOpSubmenu);
|
||||||
chooseOpStatusAction->setEnabled(false);
|
chooseOpStatusAction->setEnabled(false);
|
||||||
|
newOperationAction = new QAction("New Operation", chooseOpSubmenu);
|
||||||
|
newOperationAction->setEnabled(false); // only enable when we have an internet connection
|
||||||
chooseOpSubmenu->addAction(chooseOpStatusAction);
|
chooseOpSubmenu->addAction(chooseOpStatusAction);
|
||||||
|
chooseOpSubmenu->addAction(newOperationAction);
|
||||||
chooseOpSubmenu->addSeparator();
|
chooseOpSubmenu->addSeparator();
|
||||||
|
|
||||||
setActiveOperationLabel();
|
setActiveOperationLabel();
|
||||||
|
@ -158,6 +162,7 @@ void TrayManager::wireUi() {
|
||||||
connect(showEvidenceManagerAction, actTriggered, [this, toTop](){toTop(evidenceManagerWindow);});
|
connect(showEvidenceManagerAction, actTriggered, [this, toTop](){toTop(evidenceManagerWindow);});
|
||||||
connect(showCreditsAction, actTriggered, [this, toTop](){toTop(creditsWindow);});
|
connect(showCreditsAction, actTriggered, [this, toTop](){toTop(creditsWindow);});
|
||||||
connect(addCodeblockAction, actTriggered, this, &TrayManager::captureCodeblockActionTriggered);
|
connect(addCodeblockAction, actTriggered, this, &TrayManager::captureCodeblockActionTriggered);
|
||||||
|
connect(newOperationAction, actTriggered, [this, toTop](){toTop(createOperationWindow);});
|
||||||
|
|
||||||
connect(screenshotTool, &Screenshot::onScreenshotCaptured, this,
|
connect(screenshotTool, &Screenshot::onScreenshotCaptured, this,
|
||||||
&TrayManager::onScreenshotCaptured);
|
&TrayManager::onScreenshotCaptured);
|
||||||
|
@ -180,6 +185,7 @@ void TrayManager::wireUi() {
|
||||||
connect(trayIcon, &QSystemTrayIcon::messageClicked, [](){QDesktopServices::openUrl(Constants::releasePageUrl());});
|
connect(trayIcon, &QSystemTrayIcon::messageClicked, [](){QDesktopServices::openUrl(Constants::releasePageUrl());});
|
||||||
connect(trayIcon, &QSystemTrayIcon::activated, [this] {
|
connect(trayIcon, &QSystemTrayIcon::activated, [this] {
|
||||||
chooseOpStatusAction->setText("Loading operations...");
|
chooseOpStatusAction->setText("Loading operations...");
|
||||||
|
newOperationAction->setEnabled(false);
|
||||||
NetMan::getInstance().refreshOperationsList();
|
NetMan::getInstance().refreshOperationsList();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -296,6 +302,7 @@ void TrayManager::onOperationListUpdated(bool success,
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
chooseOpStatusAction->setText(tr("Operations loaded"));
|
chooseOpStatusAction->setText(tr("Operations loaded"));
|
||||||
|
newOperationAction->setEnabled(true);
|
||||||
cleanChooseOpSubmenu();
|
cleanChooseOpSubmenu();
|
||||||
|
|
||||||
for (const auto& op : operations) {
|
for (const auto& op : operations) {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "helpers/screenshot.h"
|
#include "helpers/screenshot.h"
|
||||||
#include "hotkeymanager.h"
|
#include "hotkeymanager.h"
|
||||||
#include "tools/UGlobalHotkey/uglobalhotkeys.h"
|
#include "tools/UGlobalHotkey/uglobalhotkeys.h"
|
||||||
|
#include "forms/add_operation/createoperation.h"
|
||||||
|
|
||||||
#ifndef QT_NO_SYSTEMTRAYICON
|
#ifndef QT_NO_SYSTEMTRAYICON
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@ class TrayManager : public QDialog {
|
||||||
Settings *settingsWindow = nullptr;
|
Settings *settingsWindow = nullptr;
|
||||||
EvidenceManager *evidenceManagerWindow = nullptr;
|
EvidenceManager *evidenceManagerWindow = nullptr;
|
||||||
Credits *creditsWindow = nullptr;
|
Credits *creditsWindow = nullptr;
|
||||||
|
CreateOperation *createOperationWindow = nullptr;
|
||||||
|
|
||||||
// UI Elements
|
// UI Elements
|
||||||
QSystemTrayIcon *trayIcon = nullptr;
|
QSystemTrayIcon *trayIcon = nullptr;
|
||||||
|
@ -91,6 +93,7 @@ class TrayManager : public QDialog {
|
||||||
|
|
||||||
QMenu *chooseOpSubmenu = nullptr;
|
QMenu *chooseOpSubmenu = nullptr;
|
||||||
QAction *chooseOpStatusAction = nullptr;
|
QAction *chooseOpStatusAction = nullptr;
|
||||||
|
QAction *newOperationAction = nullptr;
|
||||||
QAction *selectedAction = nullptr; // note: do not delete; for reference only
|
QAction *selectedAction = nullptr; // note: do not delete; for reference only
|
||||||
std::vector<QAction *> allOperationActions;
|
std::vector<QAction *> allOperationActions;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue