Fido fixes and updated changelog

Rmeoved enabled parameter to fido objects for more simplicity, added logout redirect to frontend app, added edition to fido objects name through the frontend
This commit is contained in:
Spitfireap
2024-07-12 15:50:50 +02:00
parent a3ddcee5fe
commit a5cee06832
9 changed files with 62 additions and 41 deletions
+16 -3
View File
@@ -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
+2 -1
View File
@@ -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/"
}
+3
View File
@@ -25,4 +25,7 @@ export default {
deleteFido(id) {
return repository.delete(`/fido/${id}/`)
},
editFido(id, data) {
return repository.put(`/fido/${id}/`, data)
},
}
@@ -92,11 +92,6 @@
:label="$gettext('name')"
:rules="[rules.required]"
/>
<v-switch
v-model="editEnabled"
:label="$gettext('enabled')"
color="principal"
/>
</v-form>
</ConfirmDialog>
<v-dialog v-model="showBackupTokenDialog" max-width="800px" persistent>
@@ -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(() => {
+14
View File
@@ -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,
+7 -7
View File
@@ -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"] == ""
@@ -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}"
}}
"""
)
@@ -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)),
-1
View File
@@ -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)