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',
),