diff --git a/data/conf/phpfpm/crons/keycloak-sync.php b/data/conf/phpfpm/crons/keycloak-sync.php index cdc5a6e99..8c2e66584 100644 --- a/data/conf/phpfpm/crons/keycloak-sync.php +++ b/data/conf/phpfpm/crons/keycloak-sync.php @@ -154,17 +154,6 @@ while (true) { logMsg("warning", "No email address in keycloak found for user " . $user['name']); continue; } - if (!isset($user['attributes'])){ - logMsg("warning", "No attributes in keycloak found for user " . $user['email']); - continue; - } - if (!isset($user['attributes']['mailcow_template']) || - !is_array($user['attributes']['mailcow_template']) || - count($user['attributes']['mailcow_template']) == 0) { - logMsg("warning", "No mailcow_template in keycloak found for user " . $user['email']); - continue; - } - $mailcow_template = $user['attributes']['mailcow_template']; // try get mailbox user $stmt = $pdo->prepare("SELECT @@ -178,20 +167,22 @@ while (true) { $row = $stmt->fetch(PDO::FETCH_ASSOC); // check if matching attribute mapping exists - $mbox_template = null; - foreach ($iam_settings['mappers'] as $index => $mapper){ - if (in_array($mapper, $user['attributes']['mailcow_template'])) { - $mbox_template = $mapper; - break; - } - } - if (!$mbox_template){ - logMsg("warning", "No matching attribute mapping found for user " . $user['email']); - continue; - } + $user_template = $user['attributes']['mailcow_template'][0]; + $mapper_key = array_search($user_template, $iam_settings['mappers']); $_SESSION['access_all_exception'] = '1'; if (!$row && intval($iam_settings['import_users']) == 1){ + if ($mapper_key === false){ + if (!empty($iam_settings['default_template'])) { + $mbox_template = $iam_settings['default_template']; + logMsg("warning", "Using default template for user " . $user['email']); + } else { + logMsg("warning", "No matching attribute mapping found for user " . $user['email']); + continue; + } + } else { + $mbox_template = $iam_settings['templates'][$mapper_key]; + } // mailbox user does not exist, create... logMsg("info", "Creating user " . $user['email']); $create_res = mailbox('add', 'mailbox_from_template', array( @@ -206,6 +197,11 @@ while (true) { continue; } } else if ($row && intval($iam_settings['periodic_sync']) == 1) { + if ($mapper_key === false){ + logMsg("warning", "No matching attribute mapping found for user " . $user['email']); + continue; + } + $mbox_template = $iam_settings['templates'][$mapper_key]; // mailbox user does exist, sync attribtues... logMsg("info", "Syncing attributes for user " . $user['email']); mailbox('edit', 'mailbox_from_template', array( diff --git a/data/conf/phpfpm/crons/ldap-sync.php b/data/conf/phpfpm/crons/ldap-sync.php index 17000997c..648f46c95 100644 --- a/data/conf/phpfpm/crons/ldap-sync.php +++ b/data/conf/phpfpm/crons/ldap-sync.php @@ -137,17 +137,8 @@ foreach ($response as $user) { $row = $stmt->fetch(PDO::FETCH_ASSOC); // check if matching attribute mapping exists - $mbox_template = null; - foreach ($iam_settings['mappers'] as $index => $mapper){ - if ($mapper == $mailcow_template) { - $mbox_template = $iam_settings['templates'][$index]; - break; - } - } - if (!$mbox_template){ - logMsg("warning", "No matching attribute mapping found for user " . $user[$iam_settings['username_field']][0]); - continue; - } + $user_template = $user_res[$iam_settings['attribute_field']][0]; + $mapper_key = array_search($user_template, $iam_settings['mappers']); if (empty($user[$iam_settings['username_field']][0])){ logMsg("warning", "Skipping user " . $user['displayname'][0] . " due to empty LDAP ". $iam_settings['username_field'] . " property."); @@ -156,6 +147,16 @@ foreach ($response as $user) { $_SESSION['access_all_exception'] = '1'; if (!$row && intval($iam_settings['import_users']) == 1){ + if ($mapper_key === false){ + if (!empty($iam_settings['default_template'])) { + $mbox_template = $iam_settings['default_template']; + } else { + logMsg("warning", "No matching attribute mapping found for user " . $user[$iam_settings['username_field']][0]); + continue; + } + } else { + $mbox_template = $iam_settings['templates'][$mapper_key]; + } // mailbox user does not exist, create... logMsg("info", "Creating user " . $user[$iam_settings['username_field']][0]); $create_res = mailbox('add', 'mailbox_from_template', array( @@ -170,6 +171,11 @@ foreach ($response as $user) { continue; } } else if ($row && intval($iam_settings['periodic_sync']) == 1) { + if ($mapper_key === false){ + logMsg("warning", "No matching attribute mapping found for user " . $user[$iam_settings['username_field']][0]); + continue; + } + $mbox_template = $iam_settings['templates'][$mapper_key]; // mailbox user does exist, sync attribtues... logMsg("info", "Syncing attributes for user " . $user[$iam_settings['username_field']][0]); mailbox('edit', 'mailbox_from_template', array( diff --git a/data/web/inc/functions.auth.inc.php b/data/web/inc/functions.auth.inc.php index 7fd535be1..f432c3834 100644 --- a/data/web/inc/functions.auth.inc.php +++ b/data/web/inc/functions.auth.inc.php @@ -529,12 +529,18 @@ function keycloak_mbox_login_rest($user, $pass, $extra = null){ // check if matching attribute exist if (empty($iam_settings['mappers']) || !$user_template || $mapper_key === false) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $user, '*', 'No matching attribute mapping was found'), - 'msg' => 'generic_server_error' - ); - return false; + if (!empty($iam_settings['default_template'])) { + $mbox_template = $iam_settings['default_template']; + } else { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $user, '*', 'No matching attribute mapping was found'), + 'msg' => 'generic_server_error' + ); + return false; + } + } else { + $mbox_template = $iam_settings['templates'][$mapper_key]; } // create mailbox @@ -544,7 +550,7 @@ function keycloak_mbox_login_rest($user, $pass, $extra = null){ 'local_part' => explode('@', $user)[0], 'name' => $user_res['name'], 'authsource' => 'keycloak', - 'template' => $iam_settings['templates'][$mapper_key] + 'template' => $mbox_template )); $_SESSION['access_all_exception'] = '0'; if (!$create_res){ @@ -636,12 +642,18 @@ function ldap_mbox_login($user, $pass, $extra = null){ // check if matching attribute exist if (empty($iam_settings['mappers']) || !$user_template || $mapper_key === false) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $user, '*', 'No matching attribute mapping was found'), - 'msg' => 'generic_server_error' - ); - return false; + if (!empty($iam_settings['default_tempalte'])) { + $mbox_template = $iam_settings['default_tempalte']; + } else { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $user, '*', 'No matching attribute mapping was found'), + 'msg' => 'generic_server_error' + ); + return false; + } + } else { + $mbox_template = $iam_settings['templates'][$mapper_key]; } // create mailbox @@ -651,7 +663,7 @@ function ldap_mbox_login($user, $pass, $extra = null){ 'local_part' => explode('@', $user)[0], 'name' => $user_res['displayname'][0], 'authsource' => 'ldap', - 'template' => $iam_settings['templates'][$mapper_key] + 'template' => $mbox_template )); $_SESSION['access_all_exception'] = '0'; if (!$create_res){ diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 5b67dd1e4..3593ff2c4 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -2387,8 +2387,16 @@ function identity_provider($_action = null, $_data = null, $_extra = null) { } $pdo->commit(); + // add default template + if (isset($_data['default_template'])) { + $_data['default_template'] = (empty($_data['default_template'])) ? "" : $_data['default_template']; + $stmt = $pdo->prepare("INSERT INTO identity_provider (`key`, `value`) VALUES ('default_template', :value) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);"); + $stmt->bindParam(':value', $_data['default_template']); + $stmt->execute(); + } + // add mappers - if ($_data['mappers'] && $_data['templates']){ + if (isset($_data['mappers']) && isset($_data['templates'])){ $_data['mappers'] = (!is_array($_data['mappers'])) ? array($_data['mappers']) : $_data['mappers']; $_data['templates'] = (!is_array($_data['templates'])) ? array($_data['templates']) : $_data['templates']; @@ -2714,13 +2722,19 @@ function identity_provider($_action = null, $_data = null, $_extra = null) { } if (empty($iam_settings['mappers']) || empty($user_template) || $mapper_key === false){ - clear_session(); - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $info['email'], 'No matching attribute mapping was found'), - 'msg' => 'login_failed' - ); - return false; + if (!empty($iam_settings['default_template'])) { + $mbox_template = $iam_settings['default_template']; + } else { + clear_session(); + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $info['email'], 'No matching attribute mapping was found'), + 'msg' => 'login_failed' + ); + return false; + } + } else { + $mbox_template = $iam_settings['templates'][$mapper_key]; } // create mailbox @@ -2730,7 +2744,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) { 'local_part' => explode('@', $info['email'])[0], 'name' => $info['name'], 'authsource' => $iam_settings['authsource'], - 'template' => $iam_settings['templates'][$mapper_key] + 'template' => $mbox_template )); $_SESSION['access_all_exception'] = '0'; if (!$create_res){ diff --git a/data/web/js/site/admin.js b/data/web/js/site/admin.js index 5a5f37955..90c00002a 100644 --- a/data/web/js/site/admin.js +++ b/data/web/js/site/admin.js @@ -711,7 +711,7 @@ jQuery(function($){ // App links // setup eventlistener setAppHideEvent(); - function setAppHideEvent(){ + function setAppHideEvent(){ $('.app_hide').off('change'); $('.app_hide').on('change', function (e) { var value = $(this).is(':checked') ? '1' : '0'; @@ -756,13 +756,13 @@ jQuery(function($){ $('.iam_test_connection').click(async function(e){ e.preventDefault(); var data = { attr: $('form[data-id="' + $(this).data('id') + '"]').serializeObject() }; - var res = await fetch("/api/v1/edit/identity-provider-test", { + var res = await fetch("/api/v1/edit/identity-provider-test", { headers: { "Content-Type": "application/json", }, - method:'POST', - cache:'no-cache', - body: JSON.stringify(data) + method:'POST', + cache:'no-cache', + body: JSON.stringify(data) }); res = await res.json(); if (res.type === 'success'){ @@ -772,79 +772,22 @@ jQuery(function($){ }); $('.iam_rolemap_add_keycloak').click(async function(e){ - e.preventDefault(); - - var parent = $('#iam_keycloak_mapping_list') - $(parent).children().last().clone().appendTo(parent); - var newChild = $(parent).children().last(); - $(newChild).find('input').val(''); - $(newChild).find('.dropdown-toggle').remove(); - $(newChild).find('.dropdown-menu').remove(); - $(newChild).find('.bs-title-option').remove(); - $(newChild).find('select').selectpicker('destroy'); - $(newChild).find('select').selectpicker(); - - $('.iam_keycloak_rolemap_del').off('click'); - $('.iam_keycloak_rolemap_del').click(async function(e){ - e.preventDefault(); - if ($(this).parent().parent().parent().parent().children().length > 1) - $(this).parent().parent().parent().remove(); - }); + addAttributeMappingRow('#iam_keycloak_mapping_list', '.iam_keycloak_rolemap_del', e); }); $('.iam_rolemap_add_generic').click(async function(e){ - e.preventDefault(); - - var parent = $('#iam_generic_mapping_list') - $(parent).children().last().clone().appendTo(parent); - var newChild = $(parent).children().last(); - $(newChild).find('input').val(''); - $(newChild).find('.dropdown-toggle').remove(); - $(newChild).find('.dropdown-menu').remove(); - $(newChild).find('.bs-title-option').remove(); - $(newChild).find('select').selectpicker('destroy'); - $(newChild).find('select').selectpicker(); - - $('.iam_generic_rolemap_del').off('click'); - $('.iam_generic_rolemap_del').click(async function(e){ - e.preventDefault(); - if ($(this).parent().parent().parent().parent().children().length > 1) - $(this).parent().parent().parent().remove(); - }); + addAttributeMappingRow('#iam_generic_mapping_list', '.iam_generic_rolemap_del', e); }); $('.iam_rolemap_add_ldap').click(async function(e){ - e.preventDefault(); - - var parent = $('#iam_ldap_mapping_list') - $(parent).children().last().clone().appendTo(parent); - var newChild = $(parent).children().last(); - $(newChild).find('input').val(''); - $(newChild).find('.dropdown-toggle').remove(); - $(newChild).find('.dropdown-menu').remove(); - $(newChild).find('.bs-title-option').remove(); - $(newChild).find('select').selectpicker('destroy'); - $(newChild).find('select').selectpicker(); - - $('.iam_ldap_rolemap_del').off('click'); - $('.iam_ldap_rolemap_del').click(async function(e){ - e.preventDefault(); - if ($(this).parent().parent().parent().parent().children().length > 1) - $(this).parent().parent().parent().remove(); - }); + addAttributeMappingRow('#iam_ldap_mapping_list', '.iam_ldap_rolemap_del', e); }); $('.iam_keycloak_rolemap_del').click(async function(e){ - e.preventDefault(); - if ($(this).parent().parent().parent().parent().children().length > 1) - $(this).parent().parent().parent().remove(); + deleteAttributeMappingRow(this, e); }); $('.iam_generic_rolemap_del').click(async function(e){ - e.preventDefault(); - if ($(this).parent().parent().parent().parent().children().length > 1) - $(this).parent().parent().parent().remove(); + deleteAttributeMappingRow(this, e); }); $('.iam_ldap_rolemap_del').click(async function(e){ - e.preventDefault(); - if ($(this).parent().parent().parent().parent().children().length > 1) - $(this).parent().parent().parent().remove(); + deleteAttributeMappingRow(this, e); }); // selecting identity provider $('#iam_provider').on('change', function(){ @@ -863,4 +806,31 @@ jQuery(function($){ $('#keycloak_settings').addClass('d-none'); } }); + function addAttributeMappingRow(list_id, del_class, e) { + e.preventDefault(); + + var parent = $(list_id) + $(parent).children().last().clone().appendTo(parent); + var newChild = $(parent).children().last(); + $(newChild).find('input').val(''); + $(newChild).find('input').val('').prop('required', true); + $(newChild).find('.dropdown-toggle').remove(); + $(newChild).find('.dropdown-menu').remove(); + $(newChild).find('.bs-title-option').remove(); + $(newChild).find('select').selectpicker('destroy'); + $(newChild).find('select').selectpicker(); + $(newChild).find('select').selectpicker().prop('required', true); + + $(del_class).off('click'); + $(del_class).click(async function(e){ + deleteAttributeMappingRow(this, e); + }); + } + function deleteAttributeMappingRow(elem, e) { + e.preventDefault(); + if(!$(elem).parent().parent().parent().find('select').prop('required')) + return true; + if ($(elem).parent().parent().parent().parent().children().length > 1) + $(elem).parent().parent().parent().remove(); + } }); diff --git a/data/web/lang/lang.de-de.json b/data/web/lang/lang.de-de.json index 0857bc5f7..45c6e9f50 100644 --- a/data/web/lang/lang.de-de.json +++ b/data/web/lang/lang.de-de.json @@ -215,6 +215,8 @@ "iam_client_id": "Client ID", "iam_client_secret": "Client Secret", "iam_client_scopes": "Client Scopes", + "iam_default_template": "Standardvorlage", + "iam_default_template_description": "Falls für einen Benutzer kein Template hinterlegt ist, wird die Standardvorlage zum Erstellen der Mailbox verwendet, jedoch nicht zum Aktualisieren der Mailbox.", "iam_description": "Konfiguriere einen externen Identity Provider für die Authentifizierung
Die Mailboxen der Benutzer werden bei ihrer ersten Anmeldung automatisch erstellt, vorausgesetzt, dass ein Attribut Mapping festgelegt wurde.", "iam_extra_permission": "Damit die folgenden Einstellungen funktionieren, benötigt der mailcow Client in Keycloak ein Service-Konto und die Berechtigung view-users.", "iam_host": "Host", diff --git a/data/web/lang/lang.en-gb.json b/data/web/lang/lang.en-gb.json index 64baade92..1876836de 100644 --- a/data/web/lang/lang.en-gb.json +++ b/data/web/lang/lang.en-gb.json @@ -222,6 +222,8 @@ "iam_client_id": "Client ID", "iam_client_secret": "Client Secret", "iam_client_scopes": "Client Scopes", + "iam_default_template": "Default Template", + "iam_default_template_description": "If no template is assigned to a user, the default template will be used for creating the mailbox, but not for updating the mailbox.", "iam_description": "Configure an external Provider for Authentication
User's mailboxes will be automatically created upon their first login, provided that an attribute mapping has been set.", "iam_extra_permission": "For the following settings to work, the mailcow client in Keycloak needs a Service account and the permission to view-users.", "iam_host": "Host", diff --git a/data/web/templates/admin/tab-config-identity-provider.twig b/data/web/templates/admin/tab-config-identity-provider.twig index 4a85b42fc..9d4dcd286 100644 --- a/data/web/templates/admin/tab-config-identity-provider.twig +++ b/data/web/templates/admin/tab-config-identity-provider.twig @@ -93,6 +93,27 @@
+ + +
+
+
+ + {{ lang.admin.iam_default_template}} +
+
+ +
+
+
+
{% for key, role in iam_settings.mappers %}
@@ -100,7 +121,7 @@
- {% for mbox_template in mbox_templates %} {{ mbox_template.template }} @@ -114,14 +135,14 @@
{% endfor %} - {% if not iam_settings.mappers %}
- +
- + {% for mbox_template in mbox_templates %}
- {% endif %}
@@ -283,6 +303,27 @@
+ + +
+
+
+ + {{ lang.admin.iam_default_template}} +
+
+ +
+
+
+
{% for key, role in iam_settings.mappers %}
@@ -290,7 +331,7 @@
- {% for mbox_template in mbox_templates %} {{ mbox_template.template }} @@ -304,14 +345,14 @@
{% endfor %} - {% if not iam_settings.mappers %}
- +
- + {% for mbox_template in mbox_templates %}
- {% endif %}
@@ -463,6 +503,27 @@
+ + +
+
+
+ + {{ lang.admin.iam_default_template }} +
+
+ +
+
+
+
{% for key, role in iam_settings.mappers %}
@@ -470,7 +531,7 @@
- {% for mbox_template in mbox_templates %} {{ mbox_template.template }} @@ -484,14 +545,14 @@
{% endfor %} - {% if not iam_settings.mappers %}
- +
- + {% for mbox_template in mbox_templates %}
- {% endif %}