Zeeshan925

Enterprise Policy Manager GUI.py

Dec 16th, 2025
13
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 26.13 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. """
  3. Fantastic v4.1 – Enterprise Policy Manager – PySide6
  4. Finalized script with Standard "First Match Wins" logic and fully robust IP/Domain match checking.
  5. """
  6.  
  7. import sys, json, uuid, redis, math
  8. from PySide6.QtWidgets import (
  9. QApplication, QMainWindow, QWidget, QListWidget, QListWidgetItem,
  10. QPushButton, QLabel, QLineEdit, QTextEdit, QCheckBox,
  11. QVBoxLayout, QHBoxLayout, QSplitter, QDialog, QFormLayout,
  12. QDialogButtonBox, QMessageBox, QInputDialog, QProgressDialog
  13. )
  14. from PySide6.QtCore import Qt, QThread, Signal, QCoreApplication
  15. from PySide6.QtGui import QColor, QPalette
  16.  
  17. # -----------------------------
  18. # Redis Backend (omitted for brevity, assume correct)
  19. # -----------------------------
  20. class PolicyStore:
  21. def __init__(self):
  22. self.r = None
  23.  
  24. def connect(self, host, port):
  25. self.r = redis.Redis(host=host, port=int(port), decode_responses=True)
  26. self.r.ping()
  27. self.r.setnx("policy:domain_order", json.dumps(["*"]))
  28.  
  29. def domain_order(self):
  30. if not self.r: return []
  31. order = self.r.get("policy:domain_order")
  32. return list(json.loads(order)) if order else []
  33.  
  34. def save_domain_order(self, order):
  35. if not self.r: return
  36. self.r.set("policy:domain_order", json.dumps(order))
  37.  
  38. def load_domain(self, domain):
  39. if not self.r: return {"domain": domain, "items": []}
  40. raw = self.r.get(f"domain:{domain}")
  41. if not raw:
  42. return {"domain": domain, "items": []}
  43. data = json.loads(raw)
  44. for item in data.get("items", []):
  45. if "id" not in item:
  46. item["id"] = str(uuid.uuid4())
  47. return data
  48.  
  49. def save_domain(self, domain_json):
  50. if not self.r: return
  51. self.r.set(f"domain:{domain_json['domain']}", json.dumps(domain_json))
  52.  
  53. def delete_domain(self, domain):
  54. if not self.r: return
  55. self.r.delete(f"domain:{domain}")
  56.  
  57. def list_domains(self):
  58. if not self.r: return []
  59. order = self.domain_order()
  60. existing_domains = set()
  61. for key in self.r.scan_iter("domain:*"):
  62. existing_domains.add(key.split(":", 1)[1])
  63.  
  64. domains = [d for d in order if d in existing_domains]
  65. for d in existing_domains:
  66. if d not in domains:
  67. domains.append(d)
  68. return domains
  69.  
  70.  
  71. # -----------------------------
  72. # ASYNCHRONOUS WORKER (omitted for brevity, assume correct)
  73. # -----------------------------
  74. class RedisWorker(QThread):
  75. domains_loaded = Signal(list)
  76. domain_data_loaded = Signal(dict)
  77. error = Signal(str)
  78.  
  79. def __init__(self, store, operation, data=None, parent=None):
  80. super().__init__(parent)
  81. self.store = store
  82. self.operation = operation
  83. self.data = data
  84.  
  85. def run(self):
  86. try:
  87. if self.operation == "list_domains":
  88. result = self.store.list_domains()
  89. self.domains_loaded.emit(result)
  90. elif self.operation == "load_domain":
  91. domain_name = self.data
  92. result = self.store.load_domain(domain_name)
  93. self.domain_data_loaded.emit(result)
  94.  
  95. except redis.exceptions.ConnectionError:
  96. self.error.emit("Redis connection error. Please check host/port.")
  97. except Exception as e:
  98. self.error.emit(f"Worker Error: {str(e)}")
  99.  
  100. # -----------------------------
  101. # Domain Item Dialog (omitted for brevity, assume correct)
  102. # -----------------------------
  103. class DomainItemDialog(QDialog):
  104. def __init__(self, item=None, parent=None):
  105. super().__init__(parent)
  106. self.setWindowTitle("Edit Domain Item")
  107. self.item = item or {}
  108. self.layout = QFormLayout(self)
  109.  
  110. self.action_edit = QLineEdit(self.item.get("action","allow"))
  111. self.source_edit = QLineEdit(",".join(self.item.get("source_net",[])))
  112. self.applies_edit = QLineEdit(",".join(self.item.get("applies_on",[])))
  113. self.type_edit = QLineEdit(self.item.get("policy_type","custom"))
  114. self.desc_edit = QTextEdit(self.item.get("description",""))
  115. self.path_edit = QLineEdit(self.item.get("path","/"))
  116. self.wildcard_chk = QCheckBox()
  117. self.wildcard_chk.setChecked(self.item.get("wildcard",False))
  118. self.user_edit = QLineEdit(",".join(self.item.get("users",[])))
  119.  
  120. self.layout.addRow("Action", self.action_edit)
  121. self.layout.addRow("Source Net", self.source_edit)
  122. self.layout.addRow("Applies On", self.applies_edit)
  123. self.layout.addRow("Policy Type", self.type_edit)
  124. self.layout.addRow("Description", self.desc_edit)
  125. self.layout.addRow("Path", self.path_edit)
  126. self.layout.addRow("Wildcard", self.wildcard_chk)
  127. self.layout.addRow("Users (LDAP)", self.user_edit)
  128.  
  129. buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
  130. buttons.accepted.connect(self.accept)
  131. buttons.rejected.connect(self.reject)
  132. self.layout.addWidget(buttons)
  133.  
  134. def get_data(self):
  135. return {
  136. "id": self.item.get("id", str(uuid.uuid4())),
  137. "action": self.action_edit.text().strip(),
  138. "source_net": [x.strip() for x in self.source_edit.text().split(",") if x.strip()],
  139. "applies_on": [x.strip() for x in self.applies_edit.text().split(",") if x.strip()],
  140. "policy_type": self.type_edit.text().strip(),
  141. "description": self.desc_edit.toPlainText().strip(),
  142. "path": self.path_edit.text().strip(),
  143. "wildcard": self.wildcard_chk.isChecked(),
  144. "users": [x.strip() for x in self.user_edit.text().split(",") if x.strip()]
  145. }
  146.  
  147.  
  148. # -----------------------------
  149. # GUI (omitted for brevity, assume correct)
  150. # -----------------------------
  151. class PolicyGUI(QMainWindow):
  152. PAGE_SIZE = 50
  153.  
  154. def __init__(self):
  155. super().__init__()
  156. self.setWindowTitle("Fantastic v4.1 – Enterprise Policy Manager (Final)")
  157. self.resize(1500,950)
  158. self.store = PolicyStore()
  159. self.current_domain = None
  160. self.domain_page = 0
  161. self.item_page = 0
  162. self.all_domains_list = []
  163. self.worker = None
  164. self.progress = None
  165. self.init_ui()
  166.  
  167. def init_ui(self):
  168. splitter = QSplitter(Qt.Horizontal)
  169. self.setCentralWidget(splitter)
  170.  
  171. left = QWidget(); l = QVBoxLayout(left)
  172.  
  173. # Redis connect
  174. conn = QHBoxLayout()
  175. self.host = QLineEdit("127.0.0.1")
  176. self.port = QLineEdit("6379")
  177. btn_conn = QPushButton("Connect")
  178. btn_conn.clicked.connect(self.redis_connect)
  179. self.conn_indicator = QLabel(); self.conn_indicator.setFixedSize(16,16)
  180. self.update_conn_indicator(False)
  181. btn_refresh = QPushButton("Refresh Redis")
  182. btn_refresh.clicked.connect(self.reload_all)
  183. conn.addWidget(QLabel("Redis")); conn.addWidget(self.host); conn.addWidget(self.port)
  184. conn.addWidget(btn_conn); conn.addWidget(btn_refresh); conn.addWidget(self.conn_indicator)
  185. l.addLayout(conn)
  186.  
  187. # Domain list
  188. l.addWidget(QLabel("Domains (Top is highest priority)"))
  189. self.domain_list = QListWidget()
  190. self.domain_list.itemClicked.connect(self.load_domain)
  191. l.addWidget(self.domain_list)
  192. db = QHBoxLayout()
  193. b_list = [
  194. ("Add", self.add_domain),("Edit", self.edit_domain),("Delete", self.del_domain),
  195. ("Clone", self.clone_domain),("▲", lambda: self.move_domain(-1)),("▼", lambda: self.move_domain(1))
  196. ]
  197. for t,f in b_list:
  198. b=QPushButton(t)
  199. if t in ["Add", "Edit", "Delete", "Clone"]:
  200. b.clicked.connect(lambda checked, func=f: self.run_async_operation(func))
  201. else:
  202. b.clicked.connect(f)
  203. db.addWidget(b)
  204. l.addLayout(db)
  205.  
  206. # Domain items
  207. l.addWidget(QLabel("Selected Domain Rules (Top is highest priority within domain)"))
  208. self.item_list = QListWidget(); l.addWidget(self.item_list)
  209. ib = QHBoxLayout()
  210. i_list = [
  211. ("Add", self.add_item),("Edit", self.edit_item),("Delete", self.del_item),
  212. ("Clone", self.clone_item),("▲", lambda: self.move_item(-1)),("▼", lambda: self.move_item(1))
  213. ]
  214. for t,f in i_list:
  215. b=QPushButton(t)
  216. b.clicked.connect(f)
  217. ib.addWidget(b)
  218. l.addLayout(ib)
  219.  
  220. # Paging labels
  221. self.domain_page_label = QLabel("Domain Page: 1")
  222. self.item_page_label = QLabel("Items Page: 1")
  223. l.addWidget(self.domain_page_label)
  224. l.addWidget(self.item_page_label)
  225.  
  226. # Simulator Controls
  227. l.addWidget(QLabel("Policy Simulator (Evaluates ALL Domains by Order)"))
  228. sim_fields = QHBoxLayout()
  229. self.user_sim = QLineEdit(); self.user_sim.setPlaceholderText("User")
  230. self.ip_sim = QLineEdit(); self.ip_sim.setPlaceholderText("IP Address")
  231. self.domain_sim = QLineEdit(); self.domain_sim.setPlaceholderText("Domain/Path (e.g., visa.com)")
  232. sim_fields.addWidget(self.user_sim); sim_fields.addWidget(self.ip_sim); sim_fields.addWidget(self.domain_sim)
  233. l.addLayout(sim_fields)
  234.  
  235. btn_sim = QPushButton("Run Simulator")
  236. btn_sim.clicked.connect(self.run_simulator)
  237. l.addWidget(btn_sim)
  238.  
  239. # Apply changes
  240. self.btn_apply = QPushButton("Apply Changes")
  241. self.btn_apply.clicked.connect(self.apply_changes)
  242. l.addWidget(self.btn_apply)
  243.  
  244. splitter.addWidget(left)
  245.  
  246. # -----------------------------
  247. # Helper Functions (omitted for brevity, assume correct)
  248. # -----------------------------
  249. def start_worker(self, operation, data=None):
  250. if self.worker and self.worker.isRunning():
  251. QMessageBox.warning(self, "Busy", "Please wait for the current operation to finish.")
  252. return
  253.  
  254. if not self.store.r:
  255. QMessageBox.warning(self, "Connection Required", "Please connect to Redis first.")
  256. return
  257.  
  258. if self.progress is None:
  259. self.progress = QProgressDialog("Connecting to Redis...", "Cancel", 0, 0, self)
  260. self.progress.setWindowModality(Qt.WindowModal)
  261. self.progress.setAutoClose(True)
  262. self.progress.setWindowTitle("Redis Operation")
  263.  
  264. self.progress.setLabelText(f"Running {operation}...")
  265. self.progress.show()
  266.  
  267. self.worker = RedisWorker(self.store, operation, data, parent=self)
  268. self.worker.domains_loaded.connect(self.handle_domains_loaded)
  269. self.worker.domain_data_loaded.connect(self.handle_domain_data_loaded)
  270. self.worker.error.connect(self.handle_worker_error)
  271. self.worker.finished.connect(self.progress.hide)
  272. self.worker.start()
  273.  
  274. def run_async_operation(self, func):
  275. try:
  276. func()
  277. self.start_worker("list_domains")
  278. except Exception as e:
  279. if "cancelled" not in str(e):
  280. QMessageBox.critical(self, "Operation Error", str(e))
  281. if self.progress:
  282. self.progress.hide()
  283.  
  284. def handle_worker_error(self, message):
  285. if self.progress:
  286. self.progress.hide()
  287. self.update_conn_indicator(False)
  288. QMessageBox.critical(self, "Redis Worker Error", message)
  289.  
  290. def handle_domains_loaded(self, domains_list):
  291. self.all_domains_list = domains_list
  292. self.reload_domains()
  293.  
  294. def handle_domain_data_loaded(self, domain_data):
  295. self.current_domain = domain_data
  296. self.reload_items()
  297.  
  298. def redis_connect(self):
  299. try:
  300. self.store.connect(self.host.text(), self.port.text())
  301. self.update_conn_indicator(True)
  302. self.start_worker("list_domains")
  303. QMessageBox.information(self,"Redis","Connected successfully")
  304. except Exception as e:
  305. self.update_conn_indicator(False)
  306. QMessageBox.critical(self,"Redis",str(e))
  307.  
  308. def update_conn_indicator(self, status):
  309. palette = self.conn_indicator.palette()
  310. palette.setColor(QPalette.Window, QColor("green" if status else "red"))
  311. self.conn_indicator.setAutoFillBackground(True)
  312. self.conn_indicator.setPalette(palette)
  313. self.conn_indicator.show()
  314.  
  315. def reload_all(self):
  316. if self.store.r:
  317. self.start_worker("list_domains")
  318. else:
  319. QMessageBox.warning(self, "Connection Required", "Please connect to Redis first.")
  320.  
  321. def reload_domains(self):
  322. self.domain_list.clear()
  323. all_domains = self.all_domains_list
  324. start = self.domain_page*self.PAGE_SIZE
  325. end = start+self.PAGE_SIZE
  326. for d in all_domains[start:end]:
  327. self.domain_list.addItem(d)
  328. total_pages = max(1, math.ceil(len(all_domains)/self.PAGE_SIZE))
  329. self.domain_page_label.setText(f"Domain Page: {self.domain_page+1}/{total_pages}")
  330.  
  331. def reload_items(self):
  332. self.item_list.clear()
  333. if not self.current_domain: return
  334. items=self.current_domain['items']
  335. start=self.item_page*self.PAGE_SIZE
  336. end=start+self.PAGE_SIZE
  337. for itm in items[start:end]:
  338. i=QListWidgetItem(f"{itm['action']} | {','.join(itm['source_net'])} | wildcard:{itm.get('wildcard',False)} | {itm.get('description', '')[:30]}...")
  339. i.setData(Qt.UserRole,itm['id'])
  340. self.item_list.addItem(i)
  341. total_pages=max(1, math.ceil(len(items)/self.PAGE_SIZE))
  342. self.item_page_label.setText(f"Items Page: {self.item_page+1}/{total_pages} (Total: {len(items)})")
  343.  
  344. def add_domain(self):
  345. if not self.store.r: return QMessageBox.warning(self, "Connection Required", "Please connect to Redis first.")
  346. name, ok = QInputDialog.getText(self,"Add Domain","Domain:")
  347. if ok and name:
  348. name=name.strip()
  349. if name in self.store.list_domains():
  350. QMessageBox.warning(self, "Exists", "Domain already exists.")
  351. return
  352.  
  353. order=self.store.domain_order()
  354. order.append(name)
  355. self.store.save_domain_order(order)
  356. self.store.save_domain({"domain":name,"items":[]})
  357.  
  358. def edit_domain(self):
  359. if not self.store.r: return QMessageBox.warning(self, "Connection Required", "Please connect to Redis first.")
  360. sel=self.domain_list.currentItem()
  361. if not sel: return
  362. old=sel.text()
  363. new, ok = QInputDialog.getText(self,"Edit Domain","Domain:", text=old)
  364. if ok and new and new!=old:
  365. data=self.store.load_domain(old)
  366. self.store.delete_domain(old)
  367. data["domain"]=new
  368. self.store.save_domain(data)
  369. order=self.store.domain_order();
  370. try:
  371. idx=order.index(old);
  372. order[idx]=new;
  373. self.store.save_domain_order(order)
  374. except ValueError:
  375. pass
  376.  
  377. def del_domain(self):
  378. if not self.store.r: return QMessageBox.warning(self, "Connection Required", "Please connect to Redis first.")
  379. sel=self.domain_list.currentItem();
  380. if not sel: return
  381. domain=sel.text()
  382.  
  383. reply = QMessageBox.question(self, 'Confirm Delete',
  384. f"Are you sure you want to delete the domain: '{domain}' and ALL its policies?",
  385. QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
  386.  
  387. if reply == QMessageBox.Yes:
  388. self.store.delete_domain(domain)
  389. order=self.store.domain_order();
  390. try:
  391. order.remove(domain)
  392. self.store.save_domain_order(order)
  393. except ValueError:
  394. pass
  395. self.current_domain = None
  396. else:
  397. raise Exception("Delete operation cancelled.")
  398.  
  399. def clone_domain(self):
  400. if not self.store.r: return QMessageBox.warning(self, "Connection Required", "Please connect to Redis first.")
  401. sel=self.domain_list.currentItem()
  402. if not sel: return
  403. domain_name = sel.text()
  404. data = self.store.load_domain(domain_name)
  405. new_name = domain_name + "_clone"
  406. data["domain"] = new_name
  407. self.store.save_domain(data)
  408. order = self.store.domain_order(); order.append(new_name); self.store.save_domain_order(order)
  409.  
  410. def move_domain(self,d):
  411. if not self.store.r: return QMessageBox.warning(self, "Connection Required", "Please connect to Redis first.")
  412.  
  413. current_item = self.domain_list.currentItem()
  414. if not current_item: return
  415. current_domain_name = current_item.text()
  416.  
  417. order = self.store.domain_order()
  418.  
  419. try:
  420. sel_index_in_order = order.index(current_domain_name)
  421. except ValueError:
  422. QMessageBox.warning(self, "Error", "Domain not found in the priority order list. Please refresh the GUI.")
  423. return
  424.  
  425. n = sel_index_in_order + d
  426.  
  427. if 0 <= n < len(order):
  428. order[sel_index_in_order], order[n] = order[n], order[sel_index_in_order]
  429. self.store.save_domain_order(order)
  430.  
  431. self.all_domains_list = self.store.list_domains()
  432. self.reload_domains()
  433. try:
  434. current_visible_index = self.domain_list.row(current_item)
  435. self.domain_list.setCurrentRow(current_visible_index + d)
  436. except:
  437. pass
  438.  
  439. def load_domain(self,item):
  440. if not self.store.r: return QMessageBox.warning(self, "Connection Required", "Please connect to Redis first.")
  441. self.start_worker("load_domain", item.text())
  442.  
  443. def add_item(self):
  444. if not self.current_domain: return
  445. dlg=DomainItemDialog(parent=self)
  446. if dlg.exec():
  447. itm=dlg.get_data()
  448. self.current_domain['items'].append(itm)
  449. self.store.save_domain(self.current_domain)
  450. self.reload_items()
  451.  
  452. def edit_item(self):
  453. sel=self.item_list.currentItem()
  454. if not sel or not self.current_domain: return
  455. iid=sel.data(Qt.UserRole)
  456. try:
  457. itm=[i for i in self.current_domain['items'] if i['id']==iid][0]
  458. except IndexError:
  459. QMessageBox.warning(self, "Error", "Selected item not found.")
  460. return
  461.  
  462. dlg=DomainItemDialog(item=itm,parent=self)
  463. if dlg.exec():
  464. new_data=dlg.get_data()
  465. idx=self.current_domain['items'].index(itm)
  466. self.current_domain['items'][idx]=new_data
  467. self.store.save_domain(self.current_domain)
  468. self.reload_items()
  469.  
  470. def del_item(self):
  471. sel=self.item_list.currentItem()
  472. if not sel or not self.current_domain: return
  473. iid=sel.data(Qt.UserRole)
  474.  
  475. reply = QMessageBox.question(self, 'Confirm Delete',
  476. f"Are you sure you want to delete the selected policy rule?",
  477. QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
  478.  
  479. if reply == QMessageBox.Yes:
  480. self.current_domain['items']=[i for i in self.current_domain['items'] if i['id']!=iid]
  481. self.store.save_domain(self.current_domain)
  482. self.reload_items()
  483.  
  484. def clone_item(self):
  485. sel=self.item_list.currentItem()
  486. if not sel or not self.current_domain: return
  487. iid=sel.data(Qt.UserRole)
  488. itm=[i for i in self.current_domain['items'] if i['id']==iid][0]
  489. new_item=itm.copy(); new_item['id']=str(uuid.uuid4()); new_item['description']+=" (clone)"
  490. self.current_domain['items'].append(new_item)
  491. self.store.save_domain(self.current_domain)
  492. self.reload_items()
  493.  
  494. def move_item(self,d):
  495. sel=self.item_list.currentRow()
  496. items=self.current_domain['items']; n=sel+d
  497. if 0<=n<len(items):
  498. items[sel],items[n]=items[n],items[sel]
  499. self.store.save_domain(self.current_domain)
  500. self.reload_items()
  501. self.item_list.setCurrentRow(n)
  502.  
  503. def apply_changes(self):
  504. if self.current_domain:
  505. self.store.save_domain(self.current_domain)
  506. QMessageBox.information(self,"Apply","Changes applied for domain: "+self.current_domain['domain'])
  507. else:
  508. QMessageBox.warning(self,"Apply","No domain selected")
  509.  
  510.  
  511. # -----------------------------
  512. # Multi-Domain Simulator (Standard "First Match Wins" Logic)
  513. # -----------------------------
  514. def check_rule_match(self, item, user, ip, request_domain):
  515. """
  516. Enhanced Helper function to check ALL criteria: User, IP, AND Domain/Path.
  517. Returns False at the first failed check.
  518. """
  519.  
  520. # 1. USER MATCH CHECK
  521. applies = item.get("applies_on", [])
  522. users = item.get("users", [])
  523.  
  524. if applies and applies != ["-"] and user and user not in applies:
  525. return False
  526. if users and user and user not in users:
  527. return False
  528.  
  529. # 2. IP MATCH CHECK (Source Network)
  530. if item.get('source_net') and ip and ip not in item['source_net']:
  531. return False
  532.  
  533. # 3. DOMAIN/PATH MATCH CHECK
  534.  
  535. # The domain defined in the policy (e.g., 'ntm.com' or '/')
  536. rule_domain = item.get('path','/').strip("/")
  537. wildcard = item.get('wildcard', False)
  538.  
  539. # Request Domain from Simulator (e.g., 'visa.com' or 'ntm.com/login')
  540. input_domain_part = request_domain.split('/', 1)[0].strip()
  541. input_path_part = request_domain.strip().strip('/')
  542.  
  543. # Rule 1: Specific Domain Policy (e.g., in domain:ntm.com)
  544. # Check if the domain being requested matches the policy domain *or* the rule is wildcarded.
  545. if item['domain'] != '*':
  546. if input_domain_part != item['domain']:
  547. return False # Request for visa.com cannot match rule in ntm.com domain.
  548.  
  549. # Now check the path part of the request against the rule_domain/path
  550. if wildcard:
  551. # Wildcard check (e.g., rule_domain='ntm.com/path' should match 'ntm.com/path/sub')
  552. if not input_path_part.startswith(rule_domain):
  553. return False
  554. else:
  555. # Exact path match (e.g., rule_domain='ntm.com' must match exactly 'ntm.com')
  556. if input_path_part != rule_domain:
  557. return False
  558.  
  559. # Rule 2: Catch-All Policy (in domain:*)
  560. elif item['domain'] == '*':
  561. # If the domain is the catch-all, the rule applies if it passed IP/User checks above.
  562.  
  563. # The catch-all rule itself may contain a path filter (e.g. *.exe)
  564. if rule_domain != '/':
  565. # If the catch-all rule has a specific path/file filter, check it against the input path.
  566. if wildcard:
  567. if not input_path_part.endswith(rule_domain):
  568. return False
  569. else:
  570. if input_path_part != rule_domain:
  571. return False
  572.  
  573. return True
  574.  
  575. def run_simulator(self):
  576. if not self.store.r: return QMessageBox.warning(self, "Connection Required", "Please connect to Redis first.")
  577.  
  578. user = self.user_sim.text().strip()
  579. ip = self.ip_sim.text().strip()
  580. domain_path = self.domain_sim.text().strip().strip("/")
  581.  
  582. if not domain_path:
  583. QMessageBox.warning(self, "Simulator", "Please enter a Domain/Path to simulate (e.g., visa.com).")
  584. return
  585.  
  586. domain_priority_list = self.store.domain_order()
  587. matched_rule = None
  588. final_action = "deny"
  589.  
  590. # 1. TIER 1 & 2 LOOPS: Standard "First Match Wins"
  591. for domain_name in domain_priority_list:
  592. domain_data = self.store.load_domain(domain_name)
  593.  
  594. for item in domain_data.get('items', []):
  595. # Attach the domain name to the item data for the helper function to use
  596. item['domain'] = domain_name
  597.  
  598. if self.check_rule_match(item, user, ip, domain_path):
  599.  
  600. # MATCH FOUND! (First Match Wins)
  601. final_action = item.get('action', 'deny')
  602. matched_rule = {
  603. "domain": domain_name,
  604. "description": item.get('description','(no description)'),
  605. "action": final_action
  606. }
  607. # Stop the entire evaluation process immediately
  608. break
  609.  
  610. if matched_rule:
  611. break
  612.  
  613. # 2. Present Results with Enhanced Feedback
  614. if matched_rule:
  615. msg = "Policy Evaluation Complete (First Match Wins):\n\n"
  616. msg += f"MATCH FOUND in Domain: {matched_rule['domain']}\n"
  617. msg += f"Rule Description: {matched_rule['description']}\n"
  618. msg += f"\nFinal Decision: **{final_action.upper()}**"
  619. else:
  620. # This is the state where the request is denied due to a lack of matching ALLOW rule.
  621. msg = "No matching rules found across all prioritized domains.\n\n"
  622. msg += "The request was not explicitly allowed by any rule, including the domain:*** catch-all.\n"
  623. msg += "Final Decision: **DENY** (Default Explicit Deny)\n\n"
  624. msg += f"***TIP:*** Double-check that all simulator inputs (User: '{user}', IP: '{ip}', Domain: '{domain_path}') precisely match a policy rule filter."
  625.  
  626. QMessageBox.information(self,"Simulator Result", msg)
  627.  
  628.  
  629. # -----------------------------
  630. # Run
  631. # -----------------------------
  632. if __name__=='__main__':
  633. app=QApplication(sys.argv)
  634. w=PolicyGUI()
  635. w.show()
  636. sys.exit(app.exec())
Advertisement
Add Comment
Please, Sign In to add comment