parent
7c921a00c7
commit
76a3f4790f
|
@ -61,6 +61,7 @@ SOURCES += \
|
|||
src/components/tagging/tagview.cpp \
|
||||
src/components/tagging/tagwidget.cpp \
|
||||
src/db/databaseconnection.cpp \
|
||||
src/forms/add_operation/createoperation.cpp \
|
||||
src/forms/evidence_filter/evidencefilter.cpp \
|
||||
src/forms/evidence_filter/evidencefilterform.cpp \
|
||||
src/forms/getinfo/getinfo.cpp \
|
||||
|
@ -101,6 +102,7 @@ HEADERS += \
|
|||
src/dtos/checkConnection.h \
|
||||
src/exceptions/databaseerr.h \
|
||||
src/exceptions/fileerror.h \
|
||||
src/forms/add_operation/createoperation.h \
|
||||
src/forms/evidence_filter/evidencefilter.h \
|
||||
src/forms/evidence_filter/evidencefilterform.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 {
|
||||
public:
|
||||
Operation() {}
|
||||
Operation(QString name, QString slug) {
|
||||
this->name = name;
|
||||
this->slug = slug;
|
||||
}
|
||||
|
||||
enum OperationStatus {
|
||||
OperationStatusPlanning = 0,
|
||||
|
@ -34,6 +38,17 @@ class Operation {
|
|||
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:
|
||||
// provides a Operation from a given QJsonObject
|
||||
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);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// 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
|
||||
|
|
|
@ -99,6 +99,7 @@ void TrayManager::buildUi() {
|
|||
settingsWindow = new Settings(hotkeyManager, this);
|
||||
evidenceManagerWindow = new EvidenceManager(db, this);
|
||||
creditsWindow = new Credits(this);
|
||||
createOperationWindow = new CreateOperation(this);
|
||||
|
||||
trayIconMenu = new QMenu(this);
|
||||
chooseOpSubmenu = new QMenu(tr("Select Operation"));
|
||||
|
@ -126,7 +127,10 @@ void TrayManager::buildUi() {
|
|||
currentOperationMenuAction->setEnabled(false);
|
||||
chooseOpStatusAction = new QAction("Loading operations...", chooseOpSubmenu);
|
||||
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(newOperationAction);
|
||||
chooseOpSubmenu->addSeparator();
|
||||
|
||||
setActiveOperationLabel();
|
||||
|
@ -158,6 +162,7 @@ void TrayManager::wireUi() {
|
|||
connect(showEvidenceManagerAction, actTriggered, [this, toTop](){toTop(evidenceManagerWindow);});
|
||||
connect(showCreditsAction, actTriggered, [this, toTop](){toTop(creditsWindow);});
|
||||
connect(addCodeblockAction, actTriggered, this, &TrayManager::captureCodeblockActionTriggered);
|
||||
connect(newOperationAction, actTriggered, [this, toTop](){toTop(createOperationWindow);});
|
||||
|
||||
connect(screenshotTool, &Screenshot::onScreenshotCaptured, this,
|
||||
&TrayManager::onScreenshotCaptured);
|
||||
|
@ -180,6 +185,7 @@ void TrayManager::wireUi() {
|
|||
connect(trayIcon, &QSystemTrayIcon::messageClicked, [](){QDesktopServices::openUrl(Constants::releasePageUrl());});
|
||||
connect(trayIcon, &QSystemTrayIcon::activated, [this] {
|
||||
chooseOpStatusAction->setText("Loading operations...");
|
||||
newOperationAction->setEnabled(false);
|
||||
NetMan::getInstance().refreshOperationsList();
|
||||
});
|
||||
|
||||
|
@ -296,6 +302,7 @@ void TrayManager::onOperationListUpdated(bool success,
|
|||
|
||||
if (success) {
|
||||
chooseOpStatusAction->setText(tr("Operations loaded"));
|
||||
newOperationAction->setEnabled(true);
|
||||
cleanChooseOpSubmenu();
|
||||
|
||||
for (const auto& op : operations) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "helpers/screenshot.h"
|
||||
#include "hotkeymanager.h"
|
||||
#include "tools/UGlobalHotkey/uglobalhotkeys.h"
|
||||
#include "forms/add_operation/createoperation.h"
|
||||
|
||||
#ifndef QT_NO_SYSTEMTRAYICON
|
||||
|
||||
|
@ -75,6 +76,7 @@ class TrayManager : public QDialog {
|
|||
Settings *settingsWindow = nullptr;
|
||||
EvidenceManager *evidenceManagerWindow = nullptr;
|
||||
Credits *creditsWindow = nullptr;
|
||||
CreateOperation *createOperationWindow = nullptr;
|
||||
|
||||
// UI Elements
|
||||
QSystemTrayIcon *trayIcon = nullptr;
|
||||
|
@ -91,6 +93,7 @@ class TrayManager : public QDialog {
|
|||
|
||||
QMenu *chooseOpSubmenu = nullptr;
|
||||
QAction *chooseOpStatusAction = nullptr;
|
||||
QAction *newOperationAction = nullptr;
|
||||
QAction *selectedAction = nullptr; // note: do not delete; for reference only
|
||||
std::vector<QAction *> allOperationActions;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue