Initial commit
Dieser Commit ist enthalten in:
229
infrastructure/services/browser_protection_service.py
Normale Datei
229
infrastructure/services/browser_protection_service.py
Normale Datei
@ -0,0 +1,229 @@
|
||||
"""Service for applying browser protection during automation."""
|
||||
from typing import Optional
|
||||
from playwright.sync_api import Page
|
||||
|
||||
from domain.value_objects.browser_protection_style import BrowserProtectionStyle, ProtectionLevel
|
||||
|
||||
|
||||
class BrowserProtectionService:
|
||||
"""Handles browser protection during automation to prevent user interference."""
|
||||
|
||||
SHIELD_ELEMENT_ID = "accountforge-shield"
|
||||
PROTECTION_STYLE_ID = "accountforge-protection-styles"
|
||||
|
||||
def protect_browser(self, page: Page, style: BrowserProtectionStyle) -> None:
|
||||
"""Apply protection to the browser page based on the configured style."""
|
||||
if style.level == ProtectionLevel.NONE:
|
||||
return
|
||||
|
||||
# Generate and inject protection script
|
||||
script = self._generate_protection_script(style)
|
||||
page.evaluate(script)
|
||||
|
||||
# Speichere Script für Wiederanwendung
|
||||
escaped_script = script.replace('`', '\\`')
|
||||
page.evaluate(f"""
|
||||
window.__accountforge_protection = `{escaped_script}`;
|
||||
""")
|
||||
|
||||
def remove_protection(self, page: Page) -> None:
|
||||
"""Remove all protection from the browser page."""
|
||||
page.evaluate(f"""
|
||||
// Remove shield element
|
||||
const shield = document.getElementById('{self.SHIELD_ELEMENT_ID}');
|
||||
if (shield) shield.remove();
|
||||
|
||||
// Remove style element
|
||||
const styles = document.getElementById('{self.PROTECTION_STYLE_ID}');
|
||||
if (styles) styles.remove();
|
||||
|
||||
// Note: Event listeners will be removed on page navigation
|
||||
""")
|
||||
|
||||
def _generate_protection_script(self, style: BrowserProtectionStyle) -> str:
|
||||
"""Generate JavaScript code for protection based on style configuration."""
|
||||
script_parts = []
|
||||
|
||||
# Create shield overlay
|
||||
if style.level in [ProtectionLevel.MEDIUM, ProtectionLevel.STRONG]:
|
||||
script_parts.append(self._create_shield_script(style))
|
||||
|
||||
# Add visual effects
|
||||
if style.show_border or (style.level == ProtectionLevel.LIGHT):
|
||||
script_parts.append(self._create_visual_effects_script(style))
|
||||
|
||||
# Block interactions
|
||||
if style.level in [ProtectionLevel.MEDIUM, ProtectionLevel.STRONG]:
|
||||
script_parts.append(self._create_interaction_blocker_script())
|
||||
|
||||
return "\n".join(script_parts)
|
||||
|
||||
def _create_shield_script(self, style: BrowserProtectionStyle) -> str:
|
||||
"""Create the main shield overlay element."""
|
||||
badge_positions = {
|
||||
"top-left": "top: 20px; left: 20px;",
|
||||
"top-right": "top: 20px; right: 20px;",
|
||||
"bottom-left": "bottom: 20px; left: 20px;",
|
||||
"bottom-right": "bottom: 20px; right: 20px;"
|
||||
}
|
||||
badge_position_css = badge_positions.get(style.badge_position, badge_positions["top-right"])
|
||||
|
||||
blur_css = "backdrop-filter: blur(2px);" if style.blur_effect else ""
|
||||
|
||||
return f"""
|
||||
// Create shield overlay
|
||||
const shield = document.createElement('div');
|
||||
shield.id = '{self.SHIELD_ELEMENT_ID}';
|
||||
shield.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100vw; height: 100vh;
|
||||
background: {style.get_overlay_color()};
|
||||
{blur_css}
|
||||
z-index: 2147483647;
|
||||
cursor: not-allowed;
|
||||
user-select: none;
|
||||
pointer-events: all;
|
||||
`;
|
||||
|
||||
// Add info badge if configured
|
||||
if ({str(style.show_badge).lower()}) {{
|
||||
const badge = document.createElement('div');
|
||||
badge.style.cssText = `
|
||||
position: absolute;
|
||||
{badge_position_css}
|
||||
background: rgba(220, 38, 38, 0.95);
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
animation: fadeIn 0.3s ease-in;
|
||||
`;
|
||||
badge.textContent = '{style.badge_text}';
|
||||
shield.appendChild(badge);
|
||||
}}
|
||||
|
||||
document.documentElement.appendChild(shield);
|
||||
"""
|
||||
|
||||
def _create_visual_effects_script(self, style: BrowserProtectionStyle) -> str:
|
||||
"""Create visual effects like animated border."""
|
||||
return f"""
|
||||
// Add styles for visual effects
|
||||
const styleElement = document.createElement('style');
|
||||
styleElement.id = '{self.PROTECTION_STYLE_ID}';
|
||||
styleElement.textContent = `
|
||||
@keyframes pulse {{
|
||||
0% {{ opacity: 0.4; }}
|
||||
50% {{ opacity: 0.8; }}
|
||||
100% {{ opacity: 0.4; }}
|
||||
}}
|
||||
|
||||
@keyframes fadeIn {{
|
||||
from {{ opacity: 0; transform: translateY(-10px); }}
|
||||
to {{ opacity: 1; transform: translateY(0); }}
|
||||
}}
|
||||
|
||||
{self._get_border_css(style) if style.show_border else ''}
|
||||
`;
|
||||
document.head.appendChild(styleElement);
|
||||
|
||||
{self._get_border_element_script(style) if style.show_border else ''}
|
||||
"""
|
||||
|
||||
def _get_border_css(self, style: BrowserProtectionStyle) -> str:
|
||||
"""Get CSS for animated border."""
|
||||
return f"""
|
||||
#accountforge-border {{
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
border: 3px solid {style.border_color};
|
||||
box-shadow: inset 0 0 30px rgba(255, 0, 0, 0.2);
|
||||
pointer-events: none;
|
||||
animation: pulse 2s infinite;
|
||||
z-index: 2147483646;
|
||||
}}
|
||||
"""
|
||||
|
||||
def _get_border_element_script(self, style: BrowserProtectionStyle) -> str:
|
||||
"""Get script to create border element."""
|
||||
return f"""
|
||||
// Create animated border
|
||||
const border = document.createElement('div');
|
||||
border.id = 'accountforge-border';
|
||||
document.body.appendChild(border);
|
||||
"""
|
||||
|
||||
def _create_interaction_blocker_script(self) -> str:
|
||||
"""Create script to block all user interactions."""
|
||||
return f"""
|
||||
// Verhindere das Shield selbst entfernt zu werden
|
||||
(function() {{
|
||||
// Prüfe ob Shield schon existiert
|
||||
if (document.getElementById('{self.SHIELD_ELEMENT_ID}')) {{
|
||||
return;
|
||||
}}
|
||||
|
||||
// Block all interaction events
|
||||
const blockedEvents = [
|
||||
'click', 'dblclick', 'mousedown', 'mouseup', 'mousemove',
|
||||
'keydown', 'keypress', 'keyup',
|
||||
'touchstart', 'touchend', 'touchmove',
|
||||
'contextmenu', 'wheel', 'scroll', 'input', 'change', 'focus'
|
||||
];
|
||||
|
||||
const eventBlocker = function(e) {{
|
||||
// Prüfe ob das Event vom Shield selbst kommt
|
||||
const shield = document.getElementById('{self.SHIELD_ELEMENT_ID}');
|
||||
if (shield && (e.target === shield || shield.contains(e.target))) {{
|
||||
return;
|
||||
}}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
return false;
|
||||
}};
|
||||
|
||||
// Add event listeners with capture phase
|
||||
blockedEvents.forEach(eventType => {{
|
||||
document.addEventListener(eventType, eventBlocker, true);
|
||||
window.addEventListener(eventType, eventBlocker, true);
|
||||
}});
|
||||
|
||||
// Disable text selection
|
||||
document.body.style.userSelect = 'none';
|
||||
document.body.style.webkitUserSelect = 'none';
|
||||
document.body.style.mozUserSelect = 'none';
|
||||
document.body.style.msUserSelect = 'none';
|
||||
|
||||
// Disable all input fields
|
||||
const disableInputs = () => {{
|
||||
document.querySelectorAll('input, textarea, select, button').forEach(el => {{
|
||||
el.style.pointerEvents = 'none';
|
||||
el.setAttribute('disabled', 'true');
|
||||
el.setAttribute('readonly', 'true');
|
||||
}});
|
||||
}};
|
||||
|
||||
// Initial disable
|
||||
disableInputs();
|
||||
|
||||
// Re-disable on DOM changes
|
||||
const inputObserver = new MutationObserver(disableInputs);
|
||||
inputObserver.observe(document.body, {{ childList: true, subtree: true }});
|
||||
|
||||
// Disable drag
|
||||
document.ondragstart = function() {{ return false; }};
|
||||
document.onselectstart = function() {{ return false; }};
|
||||
|
||||
// Prevent focus on any element
|
||||
document.addEventListener('focusin', function(e) {{
|
||||
e.target.blur();
|
||||
}}, true);
|
||||
}})();
|
||||
"""
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren