security(permissions): block person profiles for guests, limited company view
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions

- Person profiles (/osoba/, /profil/) now require membership
- Company detail shows limited "business card" for guests: name, logo,
  category, short description — full content hidden behind membership CTA
- Banner "Złóż deklarację członkowską" for guests on company page
- Edit/AI buttons hidden for guests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-03-19 16:44:31 +01:00
parent 7b31e6ba44
commit bc2839e77c
2 changed files with 33 additions and 6 deletions

View File

@ -232,10 +232,8 @@ def company_detail(company_id):
flash('Zaloguj się, aby zobaczyć szczegóły firmy.', 'warning')
return redirect(url_for('auth.login'))
# Sprawdź czy użytkownik jest członkiem NORDA (ma firmę lub flagę is_norda_member)
if not current_user.is_norda_member and not current_user.company_id:
flash('Dostęp do katalog firm jest dostępny tylko dla członków Izby NORDA. Złóż deklarację członkowską, aby uzyskać pełny dostęp.', 'info')
return redirect(url_for('membership.apply'))
# Guest flag — logged in but not a member (sees limited company profile)
is_guest = not current_user.is_norda_member and not current_user.company_id and not current_user.has_role(SystemRole.MEMBER)
db = SessionLocal()
try:
@ -400,7 +398,8 @@ def company_detail(company_id):
can_edit_profile=can_edit_profile,
company_managers=company_managers,
is_admin=current_user.is_authenticated and current_user.is_admin,
zopk_links=zopk_links
zopk_links=zopk_links,
is_guest=is_guest,
)
finally:
db.close()
@ -422,8 +421,13 @@ def company_detail_by_slug(slug):
@bp.route('/osoba/<int:person_id>')
@login_required
def person_detail(person_id):
"""Person detail page - shows registry data and portal data if available"""
if not current_user.is_norda_member and not current_user.has_role(SystemRole.MEMBER):
flash('Profile osób są dostępne tylko dla członków Izby NORDA.', 'info')
return redirect(url_for('membership.apply'))
db = SessionLocal()
try:
# Get person with their company relationships
@ -527,9 +531,14 @@ def person_detail(person_id):
@bp.route('/profil/<int:user_id>')
@login_required
def user_profile(user_id):
"""User profile page - redirects to person_detail if person_id exists,
otherwise shows a simple profile from User data."""
if not current_user.is_norda_member and not current_user.has_role(SystemRole.MEMBER):
flash('Profile osób są dostępne tylko dla członków Izby NORDA.', 'info')
return redirect(url_for('membership.apply'))
db = SessionLocal()
try:
user = db.query(User).filter_by(id=user_id).first()

View File

@ -965,7 +965,8 @@
<h1 class="company-name">{{ company.name }}</h1>
<!-- AI Enrichment Button + Edit Profile -->
<!-- AI Enrichment Button + Edit Profile (hidden for guests) -->
{% if not is_guest %}
<div style="margin: var(--spacing-md) 0; display: flex; gap: var(--spacing-sm); flex-wrap: wrap; align-items: center;">
{% if can_edit_profile %}
<a href="{{ url_for('company_edit', company_id=company.id) }}" class="ai-enrich-btn" style="text-decoration: none;">
@ -1038,6 +1039,7 @@
</span>
{% endif %}
</div>
{% endif %}{# not is_guest #}
{% if company.description_short %}
<p class="company-description">{{ company.description_short }}</p>
@ -1045,6 +1047,21 @@
</div>
{% if is_guest %}
{# ===== GUEST VIEW: limited company profile with membership CTA ===== #}
<div style="margin-top: var(--spacing-xl); padding: var(--spacing-xl); background: linear-gradient(135deg, #f0f9ff, #eff6ff); border: 2px solid #93c5fd; border-radius: var(--radius-lg); text-align: center;">
<div style="font-size: 2.5em; margin-bottom: var(--spacing-md);">🔒</div>
<h2 style="color: #1e40af; margin-bottom: var(--spacing-sm);">Pełne informacje o firmie dostępne dla członków Izby</h2>
<p style="color: var(--text-secondary); max-width: 500px; margin: 0 auto var(--spacing-lg); line-height: 1.6;">
Aby zobaczyć dane kontaktowe, osoby w firmie, usługi, rekomendacje i więcej — złóż deklarację członkowską i dołącz do Izby Norda Biznes.
</p>
<a href="{{ url_for('membership.apply') }}" class="btn btn-primary" style="font-size: var(--font-size-lg); padding: var(--spacing-sm) var(--spacing-xl);">
Złóż deklarację członkowską →
</a>
</div>
{% else %}
{# ===== FULL MEMBER VIEW ===== #}
<!-- Convert social_media list to dict for contact bar -->
{% set sm_bar = {} %}
{% if social_media %}
@ -5240,4 +5257,5 @@ function syncFacebookData(companyId, btn) {
})();
</script>
{% endif %}{# end is_guest else #}
{% endblock %}