diff --git a/CHANGELOG.md b/CHANGELOG.md
index 36feb3a02..9a9ae976e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,19 @@
## Changelog
-### 2.3.0 - Not yet released
+### 2.3.0
+
+#### New features
+
+* Sieve filter management has been added to the core repository and has been migrated to New Admin.
+* Postifix auto reply has been added to the core repository and has been migrated to New Admin.
+* Added Oauth2 authentication.
+* Added Webauthn (Fido2) as a second authentication method.
+* New Admin have been migrated to Vue 3.
+
+#### Fixes
+
+* Few security fixes (see our policy https://github.com/modoboa/modoboa/security/policy).
+* Removed SORBS DNSBL provider since it has been shutdown (https://www.sorbs.net/).
+* Few documentation update.
+* Fixed some issues with the password scheme setting
-* Added Oauth2 authentication
-* Added Webauthn (Fido2) as a second authentication method
diff --git a/frontend/public/config.json b/frontend/public/config.json
index 7c7f4ef46..c3d464b21 100644
--- a/frontend/public/config.json
+++ b/frontend/public/config.json
@@ -3,5 +3,6 @@
"API_DOC_URL": "/api/schema-v2/swagger/",
"OAUTH_AUTHORITY_URL": "https://localhost:8000/api/o",
"OAUTH_CLIENT_ID": "LVQbfIIX3khWR3nDvix1u9yEGHZUxcx53bhJ7FlD",
- "OAUTH_REDIRECT_URI": "https://localhost:3000/login/logged"
+ "OAUTH_REDIRECT_URI": "https://localhost:3000/login/logged",
+ "OAUTH_POST_REDIRECT_URI": "https://localhost:3000/"
}
diff --git a/frontend/src/api/auth.js b/frontend/src/api/auth.js
index 14beca4c3..3c37bfcf9 100644
--- a/frontend/src/api/auth.js
+++ b/frontend/src/api/auth.js
@@ -25,4 +25,7 @@ export default {
deleteFido(id) {
return repository.delete(`/fido/${id}/`)
},
+ editFido(id, data) {
+ return repository.put(`/fido/${id}/`, data)
+ },
}
diff --git a/frontend/src/components/account/FidoAuthForm.vue b/frontend/src/components/account/FidoAuthForm.vue
index 014c36f29..9325d0358 100644
--- a/frontend/src/components/account/FidoAuthForm.vue
+++ b/frontend/src/components/account/FidoAuthForm.vue
@@ -92,11 +92,6 @@
:label="$gettext('name')"
:rules="[rules.required]"
/>
-
@@ -139,14 +134,12 @@ const headers = [
{ title: $gettext('Added On'), key: 'added_on' },
{ title: $gettext('Last Used'), key: 'last_used' },
{ title: $gettext('Use count'), key: 'use_count' },
- { title: $gettext('Enabled'), key: 'enabled' },
{ title: $gettext('Actions'), key: 'actions' },
]
const confirmDeletion = ref()
const confirmEdition = ref()
const editForm = ref()
const editName = ref('')
-const editEnabled = ref(false)
const tokens = ref()
const showBackupTokenDialog = ref(false)
@@ -159,21 +152,18 @@ async function startFidoRegistration() {
const creationOption = await authApi.beginFidoRegistration()
if (creationOption) {
const options = parseCreationOptionsFromJSON(creationOption.data)
- try {
- const device = await create(options)
- const result = device.toJSON()
- result.name = name.value
- authStore.addFidoCred(result).then((res) => {
- if (res.status === 200) {
- tokens.value = res.data.tokens
- showBackupTokenDialog.value = true
- }
- })
- fidoForm.value.reset()
- } catch {
- registrationLoading.value = false
- return
- }
+ const device = await create(options)
+ const result = device.toJSON()
+ result.name = name.value
+ authStore.addFidoCred(result).then((res) => {
+ if (res.status === 200) {
+ tokens.value = res.data.tokens
+ showBackupTokenDialog.value = true
+ }
+ })
+ fidoForm.value.reset()
+ registrationLoading.value = false
+ return
}
registrationLoading.value = false
}
@@ -205,7 +195,6 @@ async function checkForm() {
async function editCred(cred) {
editName.value = cred.name
- editEnabled.value = cred.enabled
const result = await confirmEdition.value.open(
$gettext('Edition'),
@@ -219,9 +208,8 @@ async function editCred(cred) {
if (!result) {
return
}
-
- data = { name: editName.value, enabled: editEnabled.value }
- //TODO : send it !
+ const data = { name: editName.value }
+ await authStore.editFidoCred(cred.id, data)
}
onMounted(() => {
diff --git a/frontend/src/stores/auth.store.js b/frontend/src/stores/auth.store.js
index 1b9a0b46c..16774b4e9 100644
--- a/frontend/src/stores/auth.store.js
+++ b/frontend/src/stores/auth.store.js
@@ -21,6 +21,7 @@ export const useAuthStore = defineStore('auth', () => {
authority: config.OAUTH_AUTHORITY_URL,
client_id: config.OAUTH_CLIENT_ID,
redirect_uri: config.OAUTH_REDIRECT_URI,
+ post_logout_redirect_uri: config.OAUTH_POST_REDIRECT_URI,
response_type: 'code',
scope: 'openid read write',
automaticSilentRenew: true,
@@ -71,6 +72,18 @@ export const useAuthStore = defineStore('auth', () => {
})
}
+ async function editFidoCred(id, data) {
+ return authApi.editFido(id, data).then((res) => {
+ for (const i = 0; i < fidoCreds.value.length; i++) {
+ if (fidoCreds.value[i].id === id) {
+ fidoCreds.value[i] = res.data
+ break
+ }
+ }
+ return res
+ })
+ }
+
function getAccessToken() {
return manager.getUser().then((user) => {
if (!user) {
@@ -172,6 +185,7 @@ export const useAuthStore = defineStore('auth', () => {
getFidoCreds,
addFidoCred,
deleteFidoCreds,
+ editFidoCred,
initialize,
login,
$reset,
diff --git a/modoboa/core/api/v2/serializers.py b/modoboa/core/api/v2/serializers.py
index 5b7c23202..aa40d674b 100644
--- a/modoboa/core/api/v2/serializers.py
+++ b/modoboa/core/api/v2/serializers.py
@@ -241,8 +241,13 @@ class CoreGlobalParametersSerializer(serializers.Serializer):
class FIDOSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserFidoKeys
- fields = ["id", "name", "enabled", "added_on", "last_used", "use_count"]
- # TODO : read-only fields for everything other than name and enabled :)
+ fields = ["id", "name", "added_on", "last_used", "use_count"]
+ extra_kwargs = {
+ "id": {"read_only": True},
+ "added_on": {"read_only": True},
+ "last_used": {"read_only": True},
+ "use_count": {"read_only": True},
+ }
class FidoRegistrationSerializer(serializers.Serializer):
@@ -368,7 +373,6 @@ def send_sms_code(secret, backend, user):
class PasswordRecoverySmsSerializer(serializers.Serializer):
-
email = serializers.EmailField()
def validate(self, data, *args, **kwargs):
@@ -404,7 +408,6 @@ class PasswordRecoverySmsSerializer(serializers.Serializer):
class PasswordRecoverySmsConfirmSerializer(serializers.Serializer):
-
sms_totp = serializers.CharField(min_length=6, max_length=6)
def validate(self, data):
@@ -436,7 +439,6 @@ class PasswordRecoverySmsConfirmSerializer(serializers.Serializer):
class PasswordRecoverySmsResendSerializer(serializers.Serializer):
-
def validate(self, data):
try:
self.context["totp_secret"] = self.context["request"].session["totp_secret"]
@@ -463,7 +465,6 @@ class PasswordRecoverySmsResendSerializer(serializers.Serializer):
class PasswordRecoveryConfirmSerializer(serializers.Serializer):
-
new_password1 = serializers.CharField()
new_password2 = serializers.CharField()
@@ -481,7 +482,6 @@ class PasswordRecoveryConfirmSerializer(serializers.Serializer):
return user
def validate(self, data):
-
# Validate password
if (
data["new_password1"] == ""
diff --git a/modoboa/core/management/commands/load_initial_data.py b/modoboa/core/management/commands/load_initial_data.py
index 1ef969cda..ae51e60b3 100644
--- a/modoboa/core/management/commands/load_initial_data.py
+++ b/modoboa/core/management/commands/load_initial_data.py
@@ -114,10 +114,12 @@ class Command(BaseCommand):
allowed_host = "localhost"
frontend_application = app_model.objects.filter(name="Modoboa frontend")
frontend_path = getattr(settings, "NEW_ADMIN_URL", "new-admin")
- redirect_uri = f"https://{allowed_host}/{frontend_path}/login/logged"
+ base_uri = f"https://{allowed_host}/{frontend_path}"
+ redirect_uri = f"{base_uri}/login/logged"
client_id = ""
if not frontend_application.exists():
if options["dev"]:
+ base_uri = "https://localhost:3000/"
redirect_uri = "https://localhost:3000/login/logged"
client_id = "LVQbfIIX3khWR3nDvix1u9yEGHZUxcx53bhJ7FlD"
else:
@@ -128,6 +130,8 @@ class Command(BaseCommand):
f"--redirect-uris={redirect_uri}",
"--name='Modoboa frontend'",
f"--client-id={client_id}",
+ f"--post-logout-redirect-uris={base_uri}",
+ "--skip-authorization",
"public",
"authorization-code",
)
@@ -147,6 +151,7 @@ class Command(BaseCommand):
"OAUTH_AUTHORITY_URL": "https://{allowed_host}/api/o",
"OAUTH_CLIENT_ID": "{client_id}",
"OAUTH_REDIRECT_URI": "{redirect_uri}"
+ "OAUTH_POST_REDIRECT_URI": "{base_uri}"
}}
"""
)
diff --git a/modoboa/core/migrations/0028_userfidokeys.py b/modoboa/core/migrations/0028_userfidokeys.py
index 2ee9150c0..aae5f240e 100644
--- a/modoboa/core/migrations/0028_userfidokeys.py
+++ b/modoboa/core/migrations/0028_userfidokeys.py
@@ -6,7 +6,6 @@ import django.db.models.deletion
class Migration(migrations.Migration):
-
dependencies = [
("core", "0027_alter_user_language"),
]
@@ -25,7 +24,6 @@ class Migration(migrations.Migration):
),
),
("name", models.CharField(max_length=255)),
- ("enabled", models.BooleanField(default=True)),
("added_on", models.DateTimeField(auto_now_add=True)),
("last_used", models.DateTimeField(default=None, null=True)),
("use_count", models.IntegerField(default=0)),
diff --git a/modoboa/core/models.py b/modoboa/core/models.py
index c509cc150..42041efe0 100644
--- a/modoboa/core/models.py
+++ b/modoboa/core/models.py
@@ -420,7 +420,6 @@ reversion.register(User)
class UserFidoKeys(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
- enabled = models.BooleanField(default=True)
added_on = models.DateTimeField(auto_now_add=True)
last_used = models.DateTimeField(null=True, default=None)
use_count = models.IntegerField(default=0)