-
+
+
+
+
+
+
+ {% include 'public/lang_select.html' %}
+
- {% include 'public/lang_select.html' %}
{% endblock %}
diff --git a/allianceauth/authentication/tests/test_views.py b/allianceauth/authentication/tests/test_views.py
index 09aa4807..ec7601a9 100644
--- a/allianceauth/authentication/tests/test_views.py
+++ b/allianceauth/authentication/tests/test_views.py
@@ -1,10 +1,12 @@
import json
+import requests_mock
from unittest.mock import patch
from django.test import RequestFactory, TestCase
-from allianceauth.authentication.views import task_counts
+from allianceauth.authentication.views import task_counts, esi_check
from allianceauth.tests.auth_utils import AuthUtils
+from allianceauth.authentication.constants import ESI_ERROR_MESSAGE_OVERRIDES
MODULE_PATH = "allianceauth.authentication.views"
@@ -21,6 +23,8 @@ class TestRunningTasksCount(TestCase):
super().setUpClass()
cls.factory = RequestFactory()
cls.user = AuthUtils.create_user("bruce_wayne")
+ cls.user.is_superuser = True
+ cls.user.save()
def test_should_return_data(
self, mock_active_tasks_count, mock_queued_tasks_count
@@ -35,5 +39,164 @@ class TestRunningTasksCount(TestCase):
# then
self.assertEqual(response.status_code, 200)
self.assertDictEqual(
- jsonresponse_to_dict(response), {"tasks_running": 2, "tasks_queued": 3}
+ jsonresponse_to_dict(response), {
+ "tasks_running": 2, "tasks_queued": 3}
)
+
+ def test_su_only(
+ self, mock_active_tasks_count, mock_queued_tasks_count
+ ):
+ self.user.is_superuser = False
+ self.user.save()
+ self.user.refresh_from_db()
+ # given
+ mock_active_tasks_count.return_value = 2
+ mock_queued_tasks_count.return_value = 3
+ request = self.factory.get("/")
+ request.user = self.user
+ # when
+ response = task_counts(request)
+ # then
+ self.assertEqual(response.status_code, 302)
+
+
+class TestEsiCheck(TestCase):
+ @classmethod
+ def setUpClass(cls) -> None:
+ super().setUpClass()
+ cls.factory = RequestFactory()
+ cls.user = AuthUtils.create_user("bruce_wayne")
+ cls.user.is_superuser = True
+ cls.user.save()
+
+ @requests_mock.Mocker()
+ def test_401_data_returns_200(
+ self, m
+ ):
+ error_json = {
+ "error": "You have been banned from using ESI. Please contact Technical Support. (support@eveonline.com)"
+ }
+ status_code = 401
+ m.get(
+ "https://esi.evetech.net/latest/status/?datasource=tranquility",
+ text=json.dumps(error_json),
+ status_code=status_code
+ )
+ # given
+ request = self.factory.get("/")
+ request.user = self.user
+ # when
+ response = esi_check(request)
+ # then
+ self.assertEqual(response.status_code, 200)
+ self.assertDictEqual(
+ jsonresponse_to_dict(response), {
+ "status": status_code,
+ "data": error_json
+ }
+ )
+
+ @requests_mock.Mocker()
+ def test_504_data_returns_200(
+ self, m
+ ):
+ error_json = {
+ "error": "Gateway timeout message",
+ "timeout": 5000
+ }
+ status_code = 504
+ m.get(
+ "https://esi.evetech.net/latest/status/?datasource=tranquility",
+ text=json.dumps(error_json),
+ status_code=status_code
+ )
+ # given
+ request = self.factory.get("/")
+ request.user = self.user
+ # when
+ response = esi_check(request)
+ # then
+ self.assertEqual(response.status_code, 200)
+ self.assertDictEqual(
+ jsonresponse_to_dict(response), {
+ "status": status_code,
+ "data": error_json
+ }
+ )
+
+ @requests_mock.Mocker()
+ def test_420_data_override(
+ self, m
+ ):
+ error_json = {
+ "error": "message from CCP",
+ }
+ status_code = 420
+ m.get(
+ "https://esi.evetech.net/latest/status/?datasource=tranquility",
+ text=json.dumps(error_json),
+ status_code=status_code
+ )
+ # given
+ request = self.factory.get("/")
+ request.user = self.user
+ # when
+ response = esi_check(request)
+ # then
+ self.assertEqual(response.status_code, 200)
+ self.assertNotEqual(
+ jsonresponse_to_dict(response)["data"],
+ error_json
+ )
+ self.assertDictEqual(
+ jsonresponse_to_dict(response), {
+ "status": status_code,
+ "data": {
+ "error": ESI_ERROR_MESSAGE_OVERRIDES.get(status_code)
+ }
+ }
+ )
+
+ @requests_mock.Mocker()
+ def test_200_data_returns_200(
+ self, m
+ ):
+ good_json = {
+ "players": 5,
+ "server_version": "69420",
+ "start_time": "2030-01-01T23:59:59Z"
+ }
+ status_code = 200
+
+ m.get(
+ "https://esi.evetech.net/latest/status/?datasource=tranquility",
+ text=json.dumps(good_json),
+ status_code=status_code
+ )
+ # given
+ request = self.factory.get("/")
+ request.user = self.user
+ # when
+ response = esi_check(request)
+ # then
+ self.assertEqual(response.status_code, 200)
+ self.assertDictEqual(
+ jsonresponse_to_dict(response), {
+ "status": status_code,
+ "data": good_json
+ }
+ )
+
+ def test_su_only(
+ self,
+ ):
+ self.user.is_superuser = False
+ self.user.save()
+ self.user.refresh_from_db()
+ # given
+ request = self.factory.get("/")
+ request.user = self.user
+ # when
+ response = esi_check(request)
+ # then
+ self.assertEqual(response.status_code, 302)
diff --git a/allianceauth/authentication/urls.py b/allianceauth/authentication/urls.py
index a7dc66e3..72bc1e10 100644
--- a/allianceauth/authentication/urls.py
+++ b/allianceauth/authentication/urls.py
@@ -38,5 +38,7 @@ urlpatterns = [
name='token_refresh'
),
path('dashboard/', views.dashboard, name='dashboard'),
+ path('dashboard_bs3/', views.dashboard_bs3, name='dashboard_bs3'),
path('task-counts/', views.task_counts, name='task_counts'),
+ path('esi-check/', views.esi_check, name='esi_check'),
]
diff --git a/allianceauth/authentication/views.py b/allianceauth/authentication/views.py
index fce8d4ee..2e8f4e2c 100644
--- a/allianceauth/authentication/views.py
+++ b/allianceauth/authentication/views.py
@@ -1,6 +1,6 @@
import logging
-from allianceauth.hooks import get_hooks
+import requests
from django_registration.backends.activation.views import (
REGISTRATION_SALT, ActivationView as BaseActivationView,
RegistrationView as BaseRegistrationView,
@@ -10,7 +10,7 @@ from django_registration.signals import user_registered
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import authenticate, login
-from django.contrib.auth.decorators import login_required
+from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.models import User
from django.core import signing
from django.http import JsonResponse
@@ -23,14 +23,16 @@ from esi.decorators import token_required
from esi.models import Token
from allianceauth.eveonline.models import EveCharacter
+from allianceauth.hooks import get_hooks
+from .constants import ESI_ERROR_MESSAGE_OVERRIDES
from .core.celery_workers import active_tasks_count, queued_tasks_count
from .forms import RegistrationForm
from .models import CharacterOwnership
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
_has_auto_groups = True
- from allianceauth.eveonline.autogroups.models import *
+ from allianceauth.eveonline.autogroups.models import * # noqa: F401, F403
else:
_has_auto_groups = False
@@ -54,7 +56,7 @@ def dashboard_groups(request):
context = {
'groups': groups,
}
- return render_to_string('authentication/dashboard.groups.html', context=context, request=request)
+ return render_to_string('authentication/dashboard_groups.html', context=context, request=request)
def dashboard_characters(request):
@@ -66,7 +68,7 @@ def dashboard_characters(request):
context = {
'characters': characters
}
- return render_to_string('authentication/dashboard.characters.html', context=context, request=request)
+ return render_to_string('authentication/dashboard_characters.html', context=context, request=request)
def dashboard_admin(request):
@@ -76,6 +78,13 @@ def dashboard_admin(request):
return ""
+def dashboard_esi_check(request):
+ if request.user.is_superuser:
+ return render_to_string('allianceauth/admin-status/esi_check.html', request=request)
+ else:
+ return ""
+
+
@login_required
def dashboard(request):
_dash_items = list()
@@ -135,23 +144,30 @@ def token_refresh(request, token_id=None):
@login_required
@token_required(scopes=settings.LOGIN_TOKEN_SCOPES)
def main_character_change(request, token):
- logger.debug(f"main_character_change called by user {request.user} for character {token.character_name}")
+ logger.debug(
+ f"main_character_change called by user {request.user} for character {token.character_name}")
try:
- co = CharacterOwnership.objects.get(character__character_id=token.character_id, user=request.user)
+ co = CharacterOwnership.objects.get(
+ character__character_id=token.character_id, user=request.user)
except CharacterOwnership.DoesNotExist:
if not CharacterOwnership.objects.filter(character__character_id=token.character_id).exists():
co = CharacterOwnership.objects.create_by_token(token)
else:
messages.error(
request,
- _('Cannot change main character to %(char)s: character owned by a different account.') % ({'char': token.character_name})
+ _('Cannot change main character to %(char)s: character owned by a different account.') % (
+ {'char': token.character_name})
)
co = None
if co:
request.user.profile.main_character = co.character
request.user.profile.save(update_fields=['main_character'])
- messages.success(request, _('Changed main character to %(char)s') % {"char": co.character})
- logger.info('Changed user %(user)s main character to %(char)s' % ({'user': request.user, 'char': co.character}))
+ messages.success(request, _('Changed main character to %s') % co.character)
+ logger.info(
+ 'Changed user {user} main character to {char}'.format(
+ user=request.user, char=co.character
+ )
+ )
return redirect("authentication:dashboard")
@@ -159,9 +175,11 @@ def main_character_change(request, token):
def add_character(request, token):
if CharacterOwnership.objects.filter(character__character_id=token.character_id).filter(
owner_hash=token.character_owner_hash).filter(user=request.user).exists():
- messages.success(request, _('Added %(name)s to your account.' % ({'name': token.character_name})))
+ messages.success(request, _(
+ 'Added %(name)s to your account.' % ({'name': token.character_name})))
else:
- messages.error(request, _('Failed to add %(name)s to your account: they already have an account.' % ({'name': token.character_name})))
+ messages.error(request, _('Failed to add %(name)s to your account: they already have an account.' % (
+ {'name': token.character_name})))
return redirect('authentication:dashboard')
@@ -204,8 +222,10 @@ def sso_login(request, token):
token.delete()
messages.error(
request,
- _('Unable to authenticate as the selected character. '
- 'Please log in with the main character associated with this account.')
+ _(
+ 'Unable to authenticate as the selected character. '
+ 'Please log in with the main character associated with this account.'
+ )
)
return redirect(settings.LOGIN_URL)
@@ -278,7 +298,8 @@ class RegistrationView(BaseRegistrationView):
return super().dispatch(request, *args, **kwargs)
def register(self, form):
- user = User.objects.get(pk=self.request.session.get('registration_uid'))
+ user = User.objects.get(
+ pk=self.request.session.get('registration_uid'))
user.email = form.cleaned_data['email']
user_registered.send(self.__class__, user=user, request=self.request)
if getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
@@ -295,7 +316,8 @@ class RegistrationView(BaseRegistrationView):
def get_email_context(self, activation_key):
context = super().get_email_context(activation_key)
- context['url'] = context['site'].domain + reverse('registration_activate', args=[activation_key])
+ context['url'] = context['site'].domain + \
+ reverse('registration_activate', args=[activation_key])
return context
@@ -328,20 +350,24 @@ class ActivationView(BaseActivationView):
def registration_complete(request):
- messages.success(request, _('Sent confirmation email. Please follow the link to confirm your email address.'))
+ messages.success(request, _(
+ 'Sent confirmation email. Please follow the link to confirm your email address.'))
return redirect('authentication:login')
def activation_complete(request):
- messages.success(request, _('Confirmed your email address. Please login to continue.'))
+ messages.success(request, _(
+ 'Confirmed your email address. Please login to continue.'))
return redirect('authentication:dashboard')
def registration_closed(request):
- messages.error(request, _('Registration of new accounts is not allowed at this time.'))
+ messages.error(request, _(
+ 'Registration of new accounts is not allowed at this time.'))
return redirect('authentication:login')
+@user_passes_test(lambda u: u.is_superuser)
def task_counts(request) -> JsonResponse:
"""Return task counts as JSON for an AJAX call."""
data = {
@@ -349,3 +375,31 @@ def task_counts(request) -> JsonResponse:
"tasks_queued": queued_tasks_count()
}
return JsonResponse(data)
+
+
+def check_for_override_esi_error_message(response):
+ if response.status_code in ESI_ERROR_MESSAGE_OVERRIDES:
+ return {"error": ESI_ERROR_MESSAGE_OVERRIDES.get(response.status_code)}
+ else:
+ return response.json()
+
+
+@user_passes_test(lambda u: u.is_superuser)
+def esi_check(request) -> JsonResponse:
+ """Return if ESI ok With error messages and codes as JSON"""
+ _r = requests.get("https://esi.evetech.net/latest/status/?datasource=tranquility")
+
+ data = {
+ "status": _r.status_code,
+ "data": check_for_override_esi_error_message(_r)
+ }
+ return JsonResponse(data)
+
+
+@login_required
+def dashboard_bs3(request):
+ """Render dashboard view with BS3 theme.
+
+ This is an internal view used for testing BS3 backward compatibility in AA4 only.
+ """
+ return render(request, 'authentication/dashboard_bs3.html')
diff --git a/allianceauth/corputils/templates/corputils/corpstats.html b/allianceauth/corputils/templates/corputils/corpstats.html
index 4fb18861..988b40ae 100644
--- a/allianceauth/corputils/templates/corputils/corpstats.html
+++ b/allianceauth/corputils/templates/corputils/corpstats.html
@@ -31,7 +31,7 @@