refactor: Remove redundant contact data filtering from NordaGPT

Since NordaGPT access is now restricted to MEMBER role at the route
level (blueprints/chat/routes.py), the per-field filtering of phone
and email in nordabiz_chat.py is redundant.

Simplifies the code by removing:
- User import and loading in send_message()
- can_view_contacts parameter passing through the call chain
- Conditional phone/email inclusion in _company_to_compact_dict()
- Dynamic system prompt about contact data availability

Access control is now enforced at a single point (route decorator).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-02-01 21:42:57 +01:00
parent 6bf243d1cb
commit 57cb67fb25

View File

@ -53,9 +53,7 @@ from database import (
CompanyPerson, CompanyPerson,
CompanySocialMedia, CompanySocialMedia,
GBPAudit, GBPAudit,
CompanyWebsiteAnalysis, CompanyWebsiteAnalysis
# User for permission checks
User
) )
# Import feedback learning service for few-shot learning # Import feedback learning service for few-shot learning
@ -221,10 +219,6 @@ class NordaBizChatEngine:
) )
raise PermissionError("Access denied: You don't own this conversation") raise PermissionError("Access denied: You don't own this conversation")
# SECURITY: Load User to check permission for viewing contact data
user = db.query(User).filter_by(id=user_id).first()
can_view_contacts = user.can_view_contacts() if user else False
# RODO/GDPR: Sanitize user message - remove sensitive data before storage # RODO/GDPR: Sanitize user message - remove sensitive data before storage
# Note: NIP and email are NOT considered sensitive (public business data) # Note: NIP and email are NOT considered sensitive (public business data)
sanitized_message = user_message sanitized_message = user_message
@ -252,10 +246,7 @@ class NordaBizChatEngine:
# Build context from conversation history and relevant companies # Build context from conversation history and relevant companies
# Use ORIGINAL message for AI (so it can understand the question) # Use ORIGINAL message for AI (so it can understand the question)
# but the sanitized version is what gets stored in DB # but the sanitized version is what gets stored in DB
# SECURITY: Pass can_view_contacts to filter contact data based on role context = self._build_conversation_context(db, conversation, user_message)
context = self._build_conversation_context(
db, conversation, user_message, can_view_contacts=can_view_contacts
)
# Get AI response with cost tracking # Get AI response with cost tracking
response = self._query_ai( response = self._query_ai(
@ -370,8 +361,7 @@ class NordaBizChatEngine:
self, self,
db, db,
conversation: AIChatConversation, conversation: AIChatConversation,
current_message: str, current_message: str
can_view_contacts: bool = False
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Build context for AI with ALL companies (not pre-filtered) Build context for AI with ALL companies (not pre-filtered)
@ -383,7 +373,6 @@ class NordaBizChatEngine:
db: Database session db: Database session
conversation: Current conversation conversation: Current conversation
current_message: User's current message (for reference only) current_message: User's current message (for reference only)
can_view_contacts: Whether user can view contact data (requires MEMBER role)
Returns: Returns:
Context dict with ALL companies and categories Context dict with ALL companies and categories
@ -393,9 +382,7 @@ class NordaBizChatEngine:
context = { context = {
'conversation_type': conversation.conversation_type, 'conversation_type': conversation.conversation_type,
'total_companies': len(all_companies), 'total_companies': len(all_companies)
# SECURITY: Store permission for contact data visibility
'can_view_contacts': can_view_contacts
} }
# Get all categories with company counts # Get all categories with company counts
@ -411,9 +398,8 @@ class NordaBizChatEngine:
# Include ALL companies in compact format to minimize tokens # Include ALL companies in compact format to minimize tokens
# AI will intelligently select the most relevant ones # AI will intelligently select the most relevant ones
# SECURITY: Pass can_view_contacts to filter phone/email based on user role
context['all_companies'] = [ context['all_companies'] = [
self._company_to_compact_dict(c, can_view_contacts=can_view_contacts) self._company_to_compact_dict(c)
for c in all_companies for c in all_companies
] ]
@ -645,19 +631,13 @@ class NordaBizChatEngine:
return context return context
def _company_to_compact_dict( def _company_to_compact_dict(self, c: Company) -> Dict[str, Any]:
self, c: Company, can_view_contacts: bool = False
) -> Dict[str, Any]:
""" """
Convert company to compact dictionary for AI context. Convert company to compact dictionary for AI context.
Optimized to minimize tokens while keeping all important data. Optimized to minimize tokens while keeping all important data.
SECURITY: Phone and email are only included if user has MEMBER role or higher.
This prevents non-members from accessing contact information through AI chat.
Args: Args:
c: Company object c: Company object
can_view_contacts: Whether to include phone/email (requires MEMBER role)
Returns: Returns:
Compact dict with essential company info Compact dict with essential company info
@ -683,14 +663,10 @@ class NordaBizChatEngine:
compact['comp'] = competencies compact['comp'] = competencies
if c.website: if c.website:
compact['web'] = c.website compact['web'] = c.website
if c.phone:
# SECURITY: Contact data (phone, email) only for MEMBER role or higher compact['tel'] = c.phone
if can_view_contacts: if c.email:
if c.phone: compact['mail'] = c.email
compact['tel'] = c.phone
if c.email:
compact['mail'] = c.email
if c.address_city: if c.address_city:
compact['city'] = c.address_city compact['city'] = c.address_city
if c.year_established: if c.year_established:
@ -942,7 +918,7 @@ class NordaBizChatEngine:
🎯 TWOJA ROLA: 🎯 TWOJA ROLA:
- Analizujesz CAŁĄ bazę firm i wybierasz najlepsze dopasowania do pytania użytkownika - Analizujesz CAŁĄ bazę firm i wybierasz najlepsze dopasowania do pytania użytkownika
- Odpowiadasz zwięźle (2-3 zdania), chyba że użytkownik prosi o szczegóły - Odpowiadasz zwięźle (2-3 zdania), chyba że użytkownik prosi o szczegóły
- Podajesz konkretne nazwy firm z kontaktem (jeśli dostępny) - Podajesz konkretne nazwy firm z kontaktem
- Możesz wyszukiwać po: nazwie firmy, usługach, kompetencjach, właścicielach (w history), mieście - Możesz wyszukiwać po: nazwie firmy, usługach, kompetencjach, właścicielach (w history), mieście
- Możesz cytować rekomendacje innych członków - Możesz cytować rekomendacje innych członków
- Możesz informować o aktualnych newsach, wydarzeniach, ogłoszeniach i dyskusjach na forum - Możesz informować o aktualnych newsach, wydarzeniach, ogłoszeniach i dyskusjach na forum
@ -955,8 +931,7 @@ class NordaBizChatEngine:
- history: historia firmy, właściciele, założyciele - history: historia firmy, właściciele, założyciele
- svc: usługi - svc: usługi
- comp: kompetencje - comp: kompetencje
- web: strona www - web/tel/mail: kontakt
- tel/mail: telefon i email (dostępne tylko dla członków Izby)
- city: miasto - city: miasto
- cert: certyfikaty - cert: certyfikaty
@ -1135,19 +1110,6 @@ W dyskusji [Artur Wiertel](link) pytał o moderację. Pełna treść: [moje uwag
system_prompt += """ system_prompt += """
TRYB SZYBKI - odpowiadaj zwięźle ale z PEŁNYMI linkami do firm i tematów. TRYB SZYBKI - odpowiadaj zwięźle ale z PEŁNYMI linkami do firm i tematów.
"""
# SECURITY: Inform AI about contact data availability based on user's role
if context.get('can_view_contacts', False):
system_prompt += """
🔓 DANE KONTAKTOWE: Użytkownik jest członkiem Izby - możesz podawać numery telefonów i adresy email firm.
"""
else:
system_prompt += """
🔒 DANE KONTAKTOWE: Użytkownik NIE jest członkiem Izby - NIE podawaj telefonów ani emaili.
Zamiast tego kieruj na profil firmy: "Szczegóły kontaktowe znajdziesz na profilu: [Nazwa firmy](link)"
""" """
# Add feedback-based learning context (few-shot examples) # Add feedback-based learning context (few-shot examples)