diff --git a/frontend/src/components/domains/DomainList.vue b/frontend/src/components/domains/DomainList.vue index 5104b9d48..ad891ecdc 100644 --- a/frontend/src/components/domains/DomainList.vue +++ b/frontend/src/components/domains/DomainList.vue @@ -97,11 +97,6 @@ hide-details /> - - - @@ -130,7 +125,6 @@ import { bus } from '@/main' import domainApi from '@/api/domains' import AdministratorList from './AdministratorList' import ConfirmDialog from '@/components/layout/ConfirmDialog' -import DNSDetail from '@/components/domains/DNSDetail' import DNSStatusChip from './DNSStatusChip' import DomainAliasForm from '@/components/domains/DomainAliasForm' import MenuItems from '@/components/tools/MenuItems' @@ -139,7 +133,6 @@ export default { components: { AdministratorList, ConfirmDialog, - 'dns-detail': DNSDetail, 'dns-status-chip': DNSStatusChip, DomainAliasForm, MenuItems @@ -168,7 +161,6 @@ export default { showAdminList: false, showConfirmDialog: false, showAliasForm: false, - showDNSdetail: false, search: '', selected: [], expanded: [], @@ -226,10 +218,6 @@ export default { this.$set(this.aliases, item.name, resp.data) }) }, - openDNSDetail (domain) { - this.selectedDomain = domain - this.showDNSdetail = true - }, openAdminList (domain) { this.selectedDomain = domain this.showAdminList = true diff --git a/modoboa/core/api/v2/tests.py b/modoboa/core/api/v2/tests.py index 3c8db027f..35cf5091f 100644 --- a/modoboa/core/api/v2/tests.py +++ b/modoboa/core/api/v2/tests.py @@ -1,9 +1,11 @@ """Core API related tests.""" import copy +from unittest import mock from django.urls import reverse +from modoboa.core import models from modoboa.lib.tests import ModoAPITestCase CORE_SETTINGS = { @@ -106,3 +108,66 @@ class ParametersAPITestCase(ModoAPITestCase): }) resp = self.client.put(url, data, format="json") self.assertEqual(resp.status_code, 200) + + +class AccountViewSetTestCase(ModoAPITestCase): + + def test_me(self): + url = reverse("v2:account-me") + resp = self.client.get(url) + self.assertEqual(resp.status_code, 200) + me = resp.json() + self.assertEqual(me["pk"], 1) + + @mock.patch("django_otp.match_token") + def test_tfa_verify_code(self, match_mock): + user = models.User.objects.get(username="admin") + user.totpdevice_set.create(name="Device") + user.tfa_enabled = True + user.save() + + url = reverse("v2:account-tfa-verify-code") + match_mock.side_effect = [user.totpdevice_set.first()] + data = {"code": "1234"} + resp = self.client.post(url, data, format="json") + self.assertEqual(resp.status_code, 200) + self.assertIn("access", resp.json()) + + +class AuthenticationTestCase(ModoAPITestCase): + + @mock.patch("django_otp.match_token") + def test_2fa(self, match_mock): + url = reverse("v2:token_obtain_pair") + me_url = reverse("v2:account-me") + data = { + "username": "admin", + "password": "password" + } + resp = self.client.post(url, data, format="json") + self.assertEqual(resp.status_code, 200) + + self.client.credentials( + HTTP_AUTHORIZATION="Bearer {}".format(resp.json()["access"])) + resp = self.client.get(me_url) + self.assertEqual(resp.status_code, 200) + + # Now we enable 2FA + user = models.User.objects.get(username="admin") + user.totpdevice_set.create(name="Device") + user.tfa_enabled = True + user.save() + resp = self.client.get(me_url) + self.assertEqual(resp.status_code, 418) + + # Verify code + url = reverse("v2:account-tfa-verify-code") + match_mock.side_effect = [user.totpdevice_set.first()] + data = {"code": "1234"} + resp = self.client.post(url, data, format="json") + self.assertEqual(resp.status_code, 200) + + self.client.credentials( + HTTP_AUTHORIZATION="Bearer {}".format(resp.json()["access"])) + resp = self.client.get(me_url) + self.assertEqual(resp.status_code, 200) diff --git a/modoboa/core/commands/templates/settings.py.tpl b/modoboa/core/commands/templates/settings.py.tpl index 284d3f93e..aec036ed0 100644 --- a/modoboa/core/commands/templates/settings.py.tpl +++ b/modoboa/core/commands/templates/settings.py.tpl @@ -188,7 +188,7 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'media') REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework_simplejwt.authentication.JWTAuthentication', + 'modoboa.core.drf_authentication.JWTAuthenticationWith2FA', 'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.SessionAuthentication', ), diff --git a/modoboa/parameters/tools.py b/modoboa/parameters/tools.py index fb7409e73..f3a6fd5d6 100644 --- a/modoboa/parameters/tools.py +++ b/modoboa/parameters/tools.py @@ -89,17 +89,6 @@ class Registry(object): }) return result - def get_fields(self, level, for_app=None): - """Return fields to include in serializer.""" - result = [] - for app, fields in list(self._registry2[level].items()): - if app and for_app != app: - continue - for section, sconfig in list(fields["structure"].items()): - for name, config in list(sconfig["params"].items()): - result.append(("{}-{}".format(app, name), config["field"])) - return result - def get_serializer_class(self, level, app): """Return serializer class for given level and app.""" if app not in self._registry2[level]: @@ -169,7 +158,7 @@ class Registry(object): registry = Registry() -class Manager(object): +class Manager: """A manager for parameters.""" def __init__(self, level, parameters): diff --git a/test_project/test_project/settings.py b/test_project/test_project/settings.py index 223cfaf90..dd65fe6ba 100644 --- a/test_project/test_project/settings.py +++ b/test_project/test_project/settings.py @@ -184,6 +184,7 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'media') REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'modoboa.core.drf_authentication.JWTAuthenticationWith2FA', 'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.SessionAuthentication', ),