-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidators.py
More file actions
86 lines (63 loc) · 2.83 KB
/
Copy pathvalidators.py
File metadata and controls
86 lines (63 loc) · 2.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
"""
validators.py — Centralized validation logic for wallet addresses and party tokens.
"""
import re
import secrets
from typing import Optional, Tuple
from models import Case
# Ethereum address validation pattern (40 hex characters after 0x prefix)
WALLET_PATTERN = re.compile(r"^0x[a-fA-F0-9]{40}$")
# File upload constraints
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB
ALLOWED_MIME_TYPES = {"application/pdf", "text/plain", "application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"}
def validate_ethereum_address(address: str) -> Tuple[bool, Optional[str]]:
"""
Validates an Ethereum address format (0x + 40 hex chars).
Returns: (is_valid, error_message)
"""
if not address:
return False, "Wallet address cannot be empty"
if not WALLET_PATTERN.match(address):
return False, "Invalid wallet address format. Must be an Ethereum address (0x + 40 hex characters)."
return True, None
def validate_party_token(case: Case, party: str, token: str) -> Tuple[bool, Optional[str], Optional[str]]:
"""
Validates party identity and token.
Returns: (is_valid, error_message, party_normalized)
"""
party_lower = party.lower()
# Validate party value
if party_lower not in ["seller", "buyer"]:
return False, "Invalid party. Must be 'seller' or 'buyer'.", None
# Get expected token for the party
expected_token = case.seller_token if party_lower == "seller" else case.buyer_token
# Timing-safe comparison
if not secrets.compare_digest(token, expected_token):
return False, "Invalid or expired token.", party_lower
return True, None, party_lower
def check_party_already_responded(case: Case, party: str) -> Tuple[bool, Optional[str]]:
"""
Checks if a party has already submitted a response.
Returns: (has_responded, response_value)
"""
party_lower = party.lower()
if party_lower == "seller" and case.seller_response:
return True, case.seller_response.value
elif party_lower == "buyer" and case.buyer_response:
return True, case.buyer_response.value
return False, None
def validate_file_upload(filename: Optional[str], file_size: Optional[int], mime_type: Optional[str]) -> Tuple[bool, Optional[str]]:
"""
Validates uploaded file constraints.
Returns: (is_valid, error_message)
"""
if not filename:
return True, None # File is optional
# Check file size
if file_size and file_size > MAX_FILE_SIZE:
return False, f"File is too large. Maximum size is {MAX_FILE_SIZE / 1024 / 1024:.1f}MB."
# Check MIME type
if mime_type and mime_type not in ALLOWED_MIME_TYPES:
return False, f"File type not allowed. Accepted types: {', '.join(ALLOWED_MIME_TYPES)}"
return True, None