Skip to content

Commit 50625d5

Browse files
authored
Merge pull request #4 from mal-lang/upgrade-maltoolbox-0.3.0
Upgrade maltoolbox 0.3.0
2 parents 0f2c786 + 96cab01 commit 50625d5

23 files changed

+853
-624
lines changed

mal_gui/connection_dialog.py

Lines changed: 54 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22
from typing import TYPE_CHECKING
3+
from PySide6.QtCore import Qt
34
from PySide6.QtWidgets import (
45
QDialog,
56
QVBoxLayout,
@@ -13,7 +14,9 @@
1314
)
1415

1516
if TYPE_CHECKING:
16-
from .object_explorer.asset_base import AssetBase
17+
from .object_explorer import AssetItem, AttackerItem
18+
from maltoolbox.language import LanguageGraph, LanguageGraphAsset
19+
from maltoolbox.model import Model, ModelAsset
1720

1821
class ConnectionDialog(QDialog):
1922
def filter_items(self, text):
@@ -26,16 +29,15 @@ def ok_button_clicked(self):
2629
class AssociationConnectionDialog(ConnectionDialog):
2730
def __init__(
2831
self,
29-
start_item: AssetBase,
30-
end_item: AssetBase,
31-
lang_graph,
32-
lcs,model,
32+
start_item: AssetItem,
33+
end_item: AssetItem,
34+
lang_graph: LanguageGraph,
35+
model: Model,
3336
parent=None
3437
):
3538
super().__init__(parent)
3639

37-
self.lang_graph = lang_graph
38-
self.lcs = lcs
40+
self.lang_graph: LanguageGraph = lang_graph
3941
self.model = model
4042

4143
self.setWindowTitle("Select Association Type")
@@ -46,75 +48,38 @@ def __init__(
4648

4749
self.association_list_widget = QListWidget()
4850

49-
start_asset = start_item.asset
50-
end_asset = end_item.asset
51-
self.start_asset_type = start_asset.type
52-
self.end_asset_type = end_asset.type
53-
self.start_asset_name = start_asset.name
54-
self.end_asset_name = end_asset.name
51+
self.start_asset: ModelAsset = start_item.asset
52+
self.end_asset: ModelAsset = end_item.asset
53+
self.field_name = None
54+
5555
self.layout = QVBoxLayout()
56-
self.label = \
57-
QLabel(f"{self.start_asset_name} : {self.end_asset_name}")
56+
self.label = (
57+
QLabel(f"{self.start_asset.name} -> {self.end_asset.name}")
58+
)
5859
self.layout.addWidget(self.label)
5960
self.filter_edit = QLineEdit()
6061
self.filter_edit.setPlaceholderText("Type to filter...")
6162
self.filter_edit.textChanged.connect(self.filter_items)
6263
self.layout.addWidget(self.filter_edit)
63-
lang_graph_start_asset = next(
64-
(asset for asset in self.lang_graph.assets
65-
if asset.name == start_asset.type), None
66-
)
67-
if lang_graph_start_asset is None:
68-
raise LookupError(f'Failed to find asset "{start_asset.type}" '
69-
'in language graph.')
70-
lang_graph_end_asset = next(
71-
(asset for asset in self.lang_graph.assets
72-
if asset.name == end_asset.type), None
73-
)
74-
if lang_graph_end_asset is None:
75-
raise LookupError(f'Failed to find asset "{end_asset.type}" '
76-
'in language graph.')
77-
78-
self._str_to_assoc = {}
79-
for assoc in lang_graph_start_asset.associations:
80-
asset_pairs = []
81-
opposite_asset = assoc.get_opposite_asset(lang_graph_start_asset)
82-
# Check if the other side of the association matches the other end
83-
# and if the exact association does not already exist in the
84-
# model.
85-
if lang_graph_end_asset.is_subasset_of(opposite_asset):
86-
print("IDENTIFIED MATCH ++++++++++++")
87-
if lang_graph_start_asset.is_subasset_of(assoc.left_field.asset):
88-
asset_pairs.append((start_asset, end_asset))
89-
else:
90-
asset_pairs.append((end_asset, start_asset))
91-
if lang_graph_start_asset.is_subasset_of(opposite_asset):
92-
# The association could be applied either way, add the
93-
# reverse association as well.
94-
other_asset = assoc.get_opposite_asset(opposite_asset)
95-
# Check if the other side of the association matches the other end
96-
# and if the exact association does not already exist in the
97-
# model.
98-
if lang_graph_end_asset.is_subasset_of(other_asset):
99-
print("REVERSE ASSOC ++++++++++++")
100-
# We need to create the reverse association as well
101-
asset_pairs.append((end_asset, start_asset))
102-
for (left_asset, right_asset) in asset_pairs:
103-
if not self.model.association_exists_between_assets(
104-
assoc.name,
105-
left_asset,
106-
right_asset):
107-
formatted_assoc_str = left_asset.name + "." + \
108-
assoc.left_field.fieldname + "-->" + \
109-
assoc.name + "-->" + \
110-
right_asset.name + "." + \
111-
assoc.right_field.fieldname
112-
self._str_to_assoc[formatted_assoc_str] = (
113-
assoc,
114-
left_asset,
115-
right_asset
116-
)
117-
self.association_list_widget.addItem(QListWidgetItem(formatted_assoc_str))
64+
65+
possible_assocs = self.start_asset.lg_asset.associations
66+
for fieldname, association in possible_assocs.items():
67+
field = association.get_field(fieldname)
68+
69+
# If assoc ends with end_assets type, give that assoc
70+
# as option in list widget
71+
if field.asset == self.end_asset.lg_asset:
72+
assoc_list_item = QListWidgetItem(self.start_asset.name + "." + fieldname + " = " + self.end_asset.name)
73+
assoc_list_item.setData(
74+
Qt.UserRole,
75+
{
76+
'from': self.start_asset,
77+
'to': self.end_asset,
78+
'fieldname': fieldname
79+
}
80+
)
81+
self.association_list_widget.addItem(assoc_list_item)
82+
11883
self.layout.addWidget(self.association_list_widget)
11984

12085
button_layout = QHBoxLayout()
@@ -142,65 +107,53 @@ def filter_items(self, text):
142107

143108
def ok_button_clicked(self):
144109
selected_item = self.association_list_widget.currentItem()
110+
145111
if selected_item:
146-
selected_association_text = selected_item.text()
147-
# QMessageBox.information(self, "Selected Item", f"You selected: {selected_association_text}")
148-
149-
(assoc, left_asset, right_asset) = \
150-
self._str_to_assoc[selected_association_text]
151-
# TODO: Create association based on its full name instead in order
152-
# to avoid conflicts when multiple associations with the same name
153-
# exist.
154-
association = getattr(self.lcs.ns, assoc.name)()
155-
print(
156-
f'N:{assoc.name} LF:{assoc.left_field.fieldname} '
157-
f'LA:{left_asset.name} RF:{assoc.right_field.fieldname} '
158-
f'RA:{right_asset.name}'
159-
)
160-
setattr(association, assoc.left_field.fieldname, [left_asset])
161-
setattr(association, assoc.right_field.fieldname, [right_asset])
162-
selected_item.association = association
163-
# self.model.add_association(association)
112+
data = selected_item.data(Qt.UserRole)
113+
114+
from_asset: ModelAsset = data.get('from')
115+
to_asset: ModelAsset = data.get('to')
116+
self.field_name: str = data.get('fieldname')
117+
118+
print(f'{from_asset}.{self.field_name} = {to_asset} chosen')
119+
120+
164121
self.accept()
165122

166123
class EntrypointConnectionDialog(ConnectionDialog):
167124
def __init__(
168125
self,
169-
attacker_item,
170-
asset_item,
171-
lang_graph,
172-
lcs,
126+
attacker_item: AttackerItem,
127+
asset_item: AssetItem,
128+
lang_graph: LanguageGraph,
173129
model,
174130
parent=None
175131
):
176132
super().__init__(parent)
177133

178134
self.lang_graph = lang_graph
179-
self.lcs = lcs
180135
self.model = model
181136

182137
self.setWindowTitle("Select Entry Point")
183138
self.setMinimumWidth(300)
184139

185-
print(f'Attacker ITEM TYPE {attacker_item.asset_type}')
186-
print(f'Asset ITEM TYPE {asset_item.asset_type}')
187-
188140
self.attack_step_list_widget = QListWidget()
189-
attacker = attacker_item.attackerAttachment
141+
attacker = attacker_item.attacker
190142

191143
if asset_item.asset is not None:
192-
asset_type = \
193-
self.lang_graph.get_asset_by_name(asset_item.asset.type)
144+
asset_type = self.lang_graph.assets[asset_item.asset.type]
194145

195146
# Find asset attack steps already part of attacker entry points
196147
entry_point_tuple = attacker.get_entry_point_tuple(
197-
asset_item.asset)
148+
asset_item.asset
149+
)
150+
198151
if entry_point_tuple is not None:
199152
entry_point_attack_steps = entry_point_tuple[1]
200153
else:
201154
entry_point_attack_steps = []
202155

203-
for attack_step in asset_type.attack_steps:
156+
for attack_step in asset_type.attack_steps.values():
204157
if attack_step.type not in ['or', 'and']:
205158
continue
206159

mal_gui/connection_item.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
)
1111

1212
if TYPE_CHECKING:
13+
from maltoolbox.language import LanguageGraphAssociation
1314
from .model_scene import ModelScene
14-
from .object_explorer.asset_base import AssetBase
15+
from .object_explorer import AssetItem, AttackerItem
1516

1617
class IConnectionItem(QGraphicsLineItem):
1718
"""'interface' for Connection Item"""
18-
start_item: AssetBase
19-
end_item: AssetBase
19+
start_item: AssetItem
20+
end_item: AssetItem
2021
association_details: list[str]
2122

2223
def create_label(self, text):
@@ -35,9 +36,9 @@ def restore_labels(self):
3536
class AssociationConnectionItem(IConnectionItem):
3637
def __init__(
3738
self,
38-
selected_assoc_text: str,
39-
start_item: AssetBase,
40-
end_item: AssetBase,
39+
fieldname: str,
40+
start_item: AssetItem,
41+
end_item: AssetItem,
4142
scene: ModelScene,
4243
parent = None
4344
):
@@ -55,18 +56,22 @@ def __init__(
5556
self.start_item.add_connection(self)
5657
self.end_item.add_connection(self)
5758

58-
if self.start_item.asset_type != 'Attacker'\
59-
and self.end_item.asset_type != 'Attacker':
59+
if (
60+
self.start_item.asset_type != 'Attacker'
61+
and self.end_item.asset_type != 'Attacker'
62+
):
63+
# Fetch the association and the fieldnames
64+
lg_assoc: LanguageGraphAssociation = (
65+
start_item.asset.lg_asset.associations[fieldname]
66+
)
67+
opposite_fieldname = lg_assoc.get_opposite_fieldname(fieldname)
6068

61-
self.association_details = selected_assoc_text.split("-->")
6269
# Get left field name
63-
assoc_left_field = self.association_details[0]
64-
self.left_fieldname = assoc_left_field.split(".")[1]
70+
self.left_fieldname = opposite_fieldname
6571
# Get assoc name
66-
self.assoc_name = self.association_details[1]
67-
assoc_right_field = self.association_details[2]
72+
self.assoc_name = lg_assoc.name
6873
# Get right field name
69-
self.right_fieldname = assoc_right_field.split(".")[1]
74+
self.right_fieldname = fieldname
7075

7176
# Create labels with background color
7277
self.label_assoc_left_field = \
@@ -196,8 +201,8 @@ class EntrypointConnectionItem(IConnectionItem):
196201
def __init__(
197202
self,
198203
attack_step_name,
199-
attacker_item: AssetBase,
200-
asset_item: AssetBase,
204+
attacker_item: AttackerItem,
205+
asset_item: AssetItem,
201206
scene: ModelScene,
202207
parent = None
203208
):

mal_gui/docked_windows/draggable_tree_view.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
)
1313

1414
if TYPE_CHECKING:
15-
from ..object_explorer.asset_base import AssetBase
15+
from ..object_explorer.asset_item import AssetItem
1616

1717
class DraggableTreeView(QTreeWidget):
1818
def __init__(
@@ -134,7 +134,7 @@ def hide_unhide_asset_item(self, eye_button, item):
134134
self.eye_visibility = Visibility.HIDE
135135

136136
#First Hide the connections associtaed with the asset item
137-
asset_item: AssetBase = item.assetItemReference
137+
asset_item: AssetItem = item.assetItemReference
138138

139139
if hasattr(asset_item, 'connections'):
140140
connections = asset_item.connections

mal_gui/docked_windows/item_details_window.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
from PySide6.QtWidgets import QTreeWidget,QTreeWidgetItem
22

3+
from ..object_explorer import ItemBase
4+
35
class ItemDetailsWindow(QTreeWidget):
46
def __init__(self, parent=None):
57
super(ItemDetailsWindow, self).__init__(parent)
68
self.setHeaderLabel(None)
79
self.setColumnCount(2)
810
self.setHeaderLabels(["Attribute","Value"])
911

10-
def update_item_details_window(self, asset_item):
12+
def update_item_details_window(self, item_object: ItemBase):
1113
self.clear()
12-
if asset_item is not None:
14+
if item_object is not None:
1315
# item has a method that returns a dict
14-
asset_details = asset_item.get_item_attribute_vakues()
16+
asset_details = item_object.get_item_attribute_values()
1517
for (key, value) in asset_details.items():
1618
print(f"Attribute:{key} Value:{str(value)}")
1719
item = QTreeWidgetItem([key, str(value)])

mal_gui/docked_windows/style_configuration.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from PySide6.QtCore import Signal
1313
from PySide6.QtGui import QColor
1414

15-
from ..object_explorer.asset_base import AssetBase
15+
from ..object_explorer.asset_item import AssetItem
1616

1717
class Visibility(Enum):
1818
HIDE = 1
@@ -199,7 +199,7 @@ def accept(self):
199199
super().accept()
200200
# self.update_color_callback(self.get_color_1(), self.get_color_2())
201201
for item in self.scene.items():
202-
if isinstance(item, (AssetBase)):
202+
if isinstance(item, (AssetItem)):
203203
if item.asset_type == self.selectedAssetType.text(0):
204204
item.asset_type_background_color = self.get_color_1()
205205
item.asset_name_background_color = self.get_color_2()

0 commit comments

Comments
 (0)