Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All notable user-facing changes to this project will be documented in this file.

## Unreleased

- The public Wall of Shame and validator wallet endpoints no longer reveal the profile identity (name, avatar, profile link) of operators who set their account to non-visible; the validator still appears, identified only by its on-chain operator address (dedbeae)
- New Wall of Shame page under Validators publicly flags every active validator that isn't reporting metrics or logs in the last five minutes. The page lists all active validators (SHAME rows first) with their moniker, addresses, operator profile link, network badge, and binary ON/SHAME badges for both metrics and logs. Backed by a five-minute cron that cross-references on-chain active validators against our observability stack (98d083e)
- Stewards reviewing submissions can now copy a compact AI-ready bundle (user, contribution, mission, state, submitter notes, evidence URLs, staff reply, proposal, and internal CRM notes) from a copy icon next to each submission's title; the copy aborts with a warning if internal notes can't be loaded so the clipboard payload is never silently incomplete. Evidence URL types gain an admin-editable "Allow duplicate" flag that exempts URLs of that type (for example shared GitHub repositories) from duplicate detection across both manual and automated review (ac68ec7)
- Contributions explorer now shows highlighted contributions again, the page footer is flush to the bottom and CTAs users to submit a contribution, and the highlights section collapses to a single compact line when empty so contributions get more room. The Submit Contribution form no longer shows misleading "Please add a description" errors for evidence URLs, detects URL types correctly when users omit `https://`, and disables the submit button when a required GitHub or X URL has no linked social account (b249787)
Expand Down
5 changes: 4 additions & 1 deletion backend/ethereum_auth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import string
from datetime import timedelta

from django.conf import settings
from django.contrib.auth import get_user_model
from django.utils import timezone
from rest_framework import status
Expand Down Expand Up @@ -170,8 +171,10 @@ def login(request):
})

except Exception as e:
logger.exception("Authentication failed")
error_detail = f'Authentication failed: {str(e)}' if settings.DEBUG else 'Authentication failed.'
return Response(
{'error': f'Authentication failed: {str(e)}'},
{'error': error_detail},
status=status.HTTP_400_BAD_REQUEST
)

Expand Down
5 changes: 5 additions & 0 deletions backend/tally/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,11 @@ def get_required_env(key):
SESSION_COOKIE_DOMAIN = None # Allow cookies on localhost
SESSION_SAVE_EVERY_REQUEST = True # Save session on every request to extend expiry

# Only send the session/CSRF cookies over HTTPS in production. In DEBUG the
# dev server runs over plain HTTP, so keep these off locally.
SESSION_COOKIE_SECURE = not DEBUG
CSRF_COOKIE_SECURE = not DEBUG

# Dynamic session cookie name based on port to prevent conflicts in multi-port development
# This allows running multiple instances on different ports without session conflicts
import sys
Expand Down
4 changes: 2 additions & 2 deletions backend/validators/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Meta:
read_only_fields = ['id', 'created_at', 'updated_at']

def get_operator_user(self, obj):
if obj.operator and obj.operator.user:
if obj.operator and obj.operator.user and obj.operator.user.visible:
user = obj.operator.user
return {
'id': user.id,
Expand Down Expand Up @@ -89,7 +89,7 @@ class Meta:
read_only_fields = fields

def get_operator_user(self, obj):
if obj.operator and obj.operator.user:
if obj.operator and obj.operator.user and obj.operator.user.visible:
user = obj.operator.user
return {
'id': user.id,
Expand Down
40 changes: 40 additions & 0 deletions backend/validators/tests/test_grafana_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,46 @@ def test_grouped_validator_reasons_across_networks(self):
{('asimov', 'metrics'), ('bradbury', 'logs')},
)

def test_hidden_operator_identity_is_not_exposed(self):
"""A hidden (visible=False) operator must not have their profile
identity surfaced on the public Wall of Shame, but the validator wallet
itself should still appear (falling back to the on-chain address)."""
user = User.objects.create_user(
email='hidden-operator@example.com',
password='password',
name='Hidden Operator',
address='0xhiddenoperator00000000000000000000000000'[:42],
visible=False,
)
validator = Validator.objects.create(user=user)
wallet = ValidatorWallet.objects.create(
address='0xhiddenwallet0000000000000000000000000000'[:42],
network='asimov',
operator=validator,
operator_address=user.address,
status='active',
moniker='hidden-asimov',
metrics_status='shame',
logs_status='on',
)

response = self.client.get('/api/v1/validators/wallets/wall-of-shame/')

# operator_user must be withheld in both payloads for the hidden user.
for entry in response.data['wallets']:
if entry['address'] == wallet.address:
self.assertIsNone(entry['operator_user'])

group = next(
item for item in response.data['validators']
if item['operator_address'] == user.address
)
self.assertIsNone(group['operator_user'])
# The validator still appears (so a misbehaving node isn't hidden),
# identified only by its on-chain operator address.
self.assertEqual(group['status'], 'shame')
self.assertEqual(group['operator_address'], user.address)

def test_outdated_version_is_warning_during_grace_period(self):
TargetNodeVersion.objects.create(
version='2.0.0',
Expand Down
2 changes: 1 addition & 1 deletion backend/validators/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ def _days_since(started_at, now):

@staticmethod
def _operator_user_payload(wallet):
if wallet.operator and wallet.operator.user:
if wallet.operator and wallet.operator.user and wallet.operator.user.visible:
user = wallet.operator.user
return {
'id': user.id,
Expand Down