1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2024-12-23 02:04:46 +02:00

Merge pull request #6194 from mailcow/feat/nightly-enhancements

[Nightly] Enhancements
This commit is contained in:
FreddleSpl0it 2024-12-05 13:18:39 +01:00 committed by GitHub
commit b6174fae23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 396 additions and 224 deletions

View File

@ -22,6 +22,24 @@ if (file_exists('../../../web/inc/vars.local.inc.php')) {
} }
require_once '../../../web/inc/lib/vendor/autoload.php'; require_once '../../../web/inc/lib/vendor/autoload.php';
// Init Redis
$redis = new Redis();
try {
if (!empty(getenv('REDIS_SLAVEOF_IP'))) {
$redis->connect(getenv('REDIS_SLAVEOF_IP'), getenv('REDIS_SLAVEOF_PORT'));
}
else {
$redis->connect('redis-mailcow', 6379);
}
}
catch (Exception $e) {
error_log("MAILCOWAUTH: " . $e . PHP_EOL);
http_response_code(500); // Internal Server Error
echo json_encode($return);
exit;
}
// Init database // Init database
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name; $dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [ $opt = [
@ -44,6 +62,8 @@ require_once 'functions.inc.php';
require_once 'functions.auth.inc.php'; require_once 'functions.auth.inc.php';
require_once 'sessions.inc.php'; require_once 'sessions.inc.php';
require_once 'functions.mailbox.inc.php'; require_once 'functions.mailbox.inc.php';
require_once 'functions.ratelimit.inc.php';
require_once 'functions.acl.inc.php';
$isSOGoRequest = $post['real_rip'] == getenv('IPV4_NETWORK') . '.248'; $isSOGoRequest = $post['real_rip'] == getenv('IPV4_NETWORK') . '.248';
@ -57,7 +77,6 @@ if ($isSOGoRequest) {
error_log('MAILCOWAUTH: SOGo SSO auth for user ' . $post['username']); error_log('MAILCOWAUTH: SOGo SSO auth for user ' . $post['username']);
$result = true; $result = true;
} }
} }
if ($result === false){ if ($result === false){
$result = apppass_login($post['username'], $post['password'], $protocol, array( $result = apppass_login($post['username'], $post['password'], $protocol, array(
@ -67,7 +86,10 @@ if ($result === false){
if ($result) error_log('MAILCOWAUTH: App auth for user ' . $post['username']); if ($result) error_log('MAILCOWAUTH: App auth for user ' . $post['username']);
} }
if ($result === false){ if ($result === false){
$result = user_login($post['username'], $post['password'], $protocol, array('is_internal' => true)); // Init Identity Provider
$iam_provider = identity_provider('init');
$iam_settings = identity_provider('get');
$result = user_login($post['username'], $post['password'], array('is_internal' => true));
if ($result) error_log('MAILCOWAUTH: User auth for user ' . $post['username']); if ($result) error_log('MAILCOWAUTH: User auth for user ' . $post['username']);
} }

View File

@ -195,7 +195,8 @@ while (true) {
'local_part' => explode('@', $user['email'])[0], 'local_part' => explode('@', $user['email'])[0],
'name' => $user['firstName'] . " " . $user['lastName'], 'name' => $user['firstName'] . " " . $user['lastName'],
'authsource' => 'keycloak', 'authsource' => 'keycloak',
'template' => $mbox_template 'template' => $mbox_template,
'hasAccess' => true
)); ));
} else if ($row && intval($iam_settings['periodic_sync']) == 1) { } else if ($row && intval($iam_settings['periodic_sync']) == 1) {
// mailbox user does exist, sync attribtues... // mailbox user does exist, sync attribtues...
@ -203,7 +204,8 @@ while (true) {
mailbox('edit', 'mailbox_from_template', array( mailbox('edit', 'mailbox_from_template', array(
'username' => $user['email'], 'username' => $user['email'],
'name' => $user['firstName'] . " " . $user['lastName'], 'name' => $user['firstName'] . " " . $user['lastName'],
'template' => $mbox_template 'template' => $mbox_template,
'hasAccess' => true
)); ));
} else { } else {
// skip mailbox user // skip mailbox user

View File

@ -159,7 +159,8 @@ foreach ($response as $user) {
'local_part' => explode('@', $user[$iam_settings['username_field']][0])[0], 'local_part' => explode('@', $user[$iam_settings['username_field']][0])[0],
'name' => $user['displayname'][0], 'name' => $user['displayname'][0],
'authsource' => 'ldap', 'authsource' => 'ldap',
'template' => $mbox_template 'template' => $mbox_template,
'hasAccess' => true
)); ));
} else if ($row && intval($iam_settings['periodic_sync']) == 1) { } else if ($row && intval($iam_settings['periodic_sync']) == 1) {
// mailbox user does exist, sync attribtues... // mailbox user does exist, sync attribtues...
@ -167,7 +168,8 @@ foreach ($response as $user) {
mailbox('edit', 'mailbox_from_template', array( mailbox('edit', 'mailbox_from_template', array(
'username' => $user[$iam_settings['username_field']][0], 'username' => $user[$iam_settings['username_field']][0],
'name' => $user['displayname'][0], 'name' => $user['displayname'][0],
'template' => $mbox_template 'template' => $mbox_template,
'hasAccess' => true
)); ));
} else { } else {
// skip mailbox user // skip mailbox user

View File

@ -2,7 +2,7 @@
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
var loginForm = document.forms.namedItem("loginForm"); var loginForm = document.forms.namedItem("loginForm");
if (loginForm) { if (loginForm) {
window.location.href = '/'; window.location.href = '/user';
} }
angularReady = false; angularReady = false;

View File

@ -86,8 +86,6 @@ $cors_settings['allowed_origins'] = str_replace(", ", "\n", $cors_settings['allo
$cors_settings['allowed_methods'] = explode(", ", $cors_settings['allowed_methods']); $cors_settings['allowed_methods'] = explode(", ", $cors_settings['allowed_methods']);
$f2b_data = fail2ban('get'); $f2b_data = fail2ban('get');
// identity provider
$iam_settings = identity_provider('get');
// mbox templates // mbox templates
$mbox_templates = mailbox('get', 'mailbox_templates'); $mbox_templates = mailbox('get', 'mailbox_templates');

View File

@ -55,6 +55,7 @@ $pdo = new PDO($dsn, $database_user, $database_pass, $opt);
// Init Identity Provider // Init Identity Provider
$iam_provider = identity_provider('init'); $iam_provider = identity_provider('init');
$iam_settings = identity_provider('get');
$login_user = strtolower(trim($_SERVER['PHP_AUTH_USER'])); $login_user = strtolower(trim($_SERVER['PHP_AUTH_USER']));
$login_pass = trim(htmlspecialchars_decode($_SERVER['PHP_AUTH_PW'])); $login_pass = trim(htmlspecialchars_decode($_SERVER['PHP_AUTH_PW']));

View File

@ -119,7 +119,6 @@ if (isset($_SESSION['mailcow_cc_role'])) {
$quarantine_category = mailbox('get', 'quarantine_category', $mailbox); $quarantine_category = mailbox('get', 'quarantine_category', $mailbox);
$get_tls_policy = mailbox('get', 'tls_policy', $mailbox); $get_tls_policy = mailbox('get', 'tls_policy', $mailbox);
$rlyhosts = relayhost('get'); $rlyhosts = relayhost('get');
$iam_settings = identity_provider('get');
$template = 'edit/mailbox.twig'; $template = 'edit/mailbox.twig';
$template_data = [ $template_data = [
'acl' => $_SESSION['acl'], 'acl' => $_SESSION['acl'],

View File

@ -1,5 +1,5 @@
<?php <?php
function acl($_action, $_scope = null, $_data = null) { function acl($_action, $_scope = null, $_data = null, $_extra = null) {
global $pdo; global $pdo;
global $lang; global $lang;
$_data_log = $_data; $_data_log = $_data;
@ -23,8 +23,8 @@ function acl($_action, $_scope = null, $_data = null) {
$acl_post[$acl_val] = 1; $acl_post[$acl_val] = 1;
} }
// Users cannot change their own ACL // Users cannot change their own ACL
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username) if (!$_extra['hasAccess'] && (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)
|| ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) { || ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin'))) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log), 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
@ -34,7 +34,7 @@ function acl($_action, $_scope = null, $_data = null) {
} }
// Read all available acl options by calling acl(get) // Read all available acl options by calling acl(get)
// Set all available acl options we cannot find in the post data to 0, else 1 // Set all available acl options we cannot find in the post data to 0, else 1
$is_now = acl('get', 'user', $username); $is_now = acl('get', 'user', $username, $_extra);
if (!empty($is_now)) { if (!empty($is_now)) {
foreach ($is_now as $acl_now_name => $acl_now_val) { foreach ($is_now as $acl_now_name => $acl_now_val) {
$set_acls[$acl_now_name] = (isset($acl_post[$acl_now_name])) ? 1 : 0; $set_acls[$acl_now_name] = (isset($acl_post[$acl_now_name])) ? 1 : 0;
@ -130,7 +130,7 @@ function acl($_action, $_scope = null, $_data = null) {
case 'get': case 'get':
switch ($_scope) { switch ($_scope) {
case 'user': case 'user':
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { if (!$_extra['hasAccess'] && !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false; return false;
} }
$stmt = $pdo->prepare("SELECT * FROM `user_acl` WHERE `username` = :username"); $stmt = $pdo->prepare("SELECT * FROM `user_acl` WHERE `username` = :username");

View File

@ -162,6 +162,8 @@ function domainadmin_login($user, $pass){
} }
function user_login($user, $pass, $extra = null){ function user_login($user, $pass, $extra = null){
global $pdo; global $pdo;
global $iam_provider;
global $iam_settings;
$is_internal = $extra['is_internal']; $is_internal = $extra['is_internal'];
@ -186,12 +188,11 @@ function user_login($user, $pass, $extra = null){
// user does not exist, try call idp login and create user if possible via rest flow // user does not exist, try call idp login and create user if possible via rest flow
if (!$row){ if (!$row){
$iam_settings = identity_provider('get');
if ($iam_settings['authsource'] == 'keycloak' && intval($iam_settings['mailpassword_flow']) == 1){ if ($iam_settings['authsource'] == 'keycloak' && intval($iam_settings['mailpassword_flow']) == 1){
$result = keycloak_mbox_login_rest($user, $pass, $iam_settings, array('is_internal' => $is_internal, 'create' => true)); $result = keycloak_mbox_login_rest($user, $pass, array('is_internal' => $is_internal, 'create' => true));
if ($result !== false) return $result; if ($result !== false) return $result;
} else if ($iam_settings['authsource'] == 'ldap') { } else if ($iam_settings['authsource'] == 'ldap') {
$result = ldap_mbox_login($user, $pass, $iam_settings, array('is_internal' => $is_internal, 'create' => true)); $result = ldap_mbox_login($user, $pass, array('is_internal' => $is_internal, 'create' => true));
if ($result !== false) return $result; if ($result !== false) return $result;
} }
} }
@ -202,9 +203,8 @@ function user_login($user, $pass, $extra = null){
switch ($row['authsource']) { switch ($row['authsource']) {
case 'keycloak': case 'keycloak':
// user authsource is keycloak, try using via rest flow // user authsource is keycloak, try using via rest flow
$iam_settings = identity_provider('get');
if (intval($iam_settings['mailpassword_flow']) == 1){ if (intval($iam_settings['mailpassword_flow']) == 1){
$result = keycloak_mbox_login_rest($user, $pass, $iam_settings, array('is_internal' => $is_internal)); $result = keycloak_mbox_login_rest($user, $pass, array('is_internal' => $is_internal));
if ($result !== false) { if ($result !== false) {
// check for tfa authenticators // check for tfa authenticators
$authenticators = get_tfa($user); $authenticators = get_tfa($user);
@ -243,8 +243,7 @@ function user_login($user, $pass, $extra = null){
break; break;
case 'ldap': case 'ldap':
// user authsource is ldap // user authsource is ldap
$iam_settings = identity_provider('get'); $result = ldap_mbox_login($user, $pass, array('is_internal' => $is_internal));
$result = ldap_mbox_login($user, $pass, $iam_settings, array('is_internal' => $is_internal));
if ($result !== false) { if ($result !== false) {
// check for tfa authenticators // check for tfa authenticators
$authenticators = get_tfa($user); $authenticators = get_tfa($user);
@ -397,8 +396,10 @@ function apppass_login($user, $pass, $app_passwd_data, $extra = null){
// Keycloak REST Api Flow - auth user by mailcow_password attribute // Keycloak REST Api Flow - auth user by mailcow_password attribute
// This password will be used for direct UI, IMAP and SMTP Auth // This password will be used for direct UI, IMAP and SMTP Auth
// To use direct user credentials, only Authorization Code Flow is valid // To use direct user credentials, only Authorization Code Flow is valid
function keycloak_mbox_login_rest($user, $pass, $iam_settings, $extra = null){ function keycloak_mbox_login_rest($user, $pass, $extra = null){
global $pdo; global $pdo;
global $iam_provider;
global $iam_settings;
$is_internal = $extra['is_internal']; $is_internal = $extra['is_internal'];
$create = $extra['create']; $create = $extra['create'];
@ -448,36 +449,49 @@ function keycloak_mbox_login_rest($user, $pass, $iam_settings, $extra = null){
return false; return false;
} }
// get mapped template, if not set return false // get mapped template
// also return false if no mappers were defined
$user_template = $user_res['attributes']['mailcow_template'][0]; $user_template = $user_res['attributes']['mailcow_template'][0];
if ($create && (empty($iam_settings['mappers']) || !$user_template)){ $mapper_key = array_search($user_template, $iam_settings['mappers']);
return false;
} else if (!$create) { if (!$create) {
// login success - dont create mailbox // login success
if ($mapper_key !== false) {
// update user
mailbox('edit', 'mailbox_from_template', array(
'username' => $user,
'name' => $user_res['name'],
'template' => $iam_settings['templates'][$mapper_key],
'hasAccess' => true
));
}
return 'user'; return 'user';
} }
// check if matching attribute exist // check if matching attribute exist
$mapper_key = array_search($user_template, $iam_settings['mappers']); if (empty($iam_settings['mappers']) || !$user_template) return false;
if ($mapper_key === false) return false; if ($mapper_key === false) return false;
// create mailbox // create mailbox
$create_res = mailbox('add', 'mailbox_from_template', array( $create_res = mailbox('add', 'mailbox_from_template', array(
'domain' => explode('@', $user)[1], 'domain' => explode('@', $user)[1],
'local_part' => explode('@', $user)[0], 'local_part' => explode('@', $user)[0],
'name' => $user_res['firstName'] . " " . $user_res['lastName'], 'name' => $user_res['name'],
'authsource' => 'keycloak', 'authsource' => 'keycloak',
'template' => $iam_settings['templates'][$mapper_key] 'template' => $iam_settings['templates'][$mapper_key],
'hasAccess' => true
)); ));
if (!$create_res) return false; if (!$create_res){
clear_session();
return false;
}
return 'user'; return 'user';
} }
function ldap_mbox_login($user, $pass, $iam_settings, $extra = null){ function ldap_mbox_login($user, $pass, $extra = null){
global $pdo; global $pdo;
global $iam_provider;
global $iam_settings;
$iam_provider = identity_provider();
$is_internal = $extra['is_internal']; $is_internal = $extra['is_internal'];
$create = $extra['create']; $create = $extra['create'];
@ -534,18 +548,26 @@ function ldap_mbox_login($user, $pass, $iam_settings, $extra = null){
return false; return false;
} }
// get mapped template, if not set return false // get mapped template
// also return false if no mappers were defined
$user_template = $user_res[$iam_settings['attribute_field']][0]; $user_template = $user_res[$iam_settings['attribute_field']][0];
if ($create && (empty($iam_settings['mappers']) || !$user_template)){ $mapper_key = array_search($user_template, $iam_settings['mappers']);
return false;
} else if (!$create) { if (!$create) {
// login success - dont create mailbox // login success
if ($mapper_key !== false) {
// update user
mailbox('edit', 'mailbox_from_template', array(
'username' => $user,
'name' => $user_res['displayname'][0],
'template' => $iam_settings['templates'][$mapper_key],
'hasAccess' => true
));
}
return 'user'; return 'user';
} }
// check if matching attribute exist // check if matching attribute exist
$mapper_key = array_search($user_template, $iam_settings['mappers']); if (empty($iam_settings['mappers']) || !$user_template) return false;
if ($mapper_key === false) return false; if ($mapper_key === false) return false;
// create mailbox // create mailbox
@ -554,9 +576,13 @@ function ldap_mbox_login($user, $pass, $iam_settings, $extra = null){
'local_part' => explode('@', $user)[0], 'local_part' => explode('@', $user)[0],
'name' => $user_res['displayname'][0], 'name' => $user_res['displayname'][0],
'authsource' => 'ldap', 'authsource' => 'ldap',
'template' => $iam_settings['templates'][$mapper_key] 'template' => $iam_settings['templates'][$mapper_key],
'hasAccess' => true
)); ));
if (!$create_res) return false; if (!$create_res){
clear_session();
return false;
}
return 'user'; return 'user';
} }

View File

@ -1072,6 +1072,8 @@ function set_tfa($_data) {
global $pdo; global $pdo;
global $yubi; global $yubi;
global $tfa; global $tfa;
global $iam_settings;
$_data_log = $_data; $_data_log = $_data;
$access_denied = null; $access_denied = null;
!isset($_data_log['confirm_password']) ?: $_data_log['confirm_password'] = '*'; !isset($_data_log['confirm_password']) ?: $_data_log['confirm_password'] = '*';
@ -1100,7 +1102,6 @@ function set_tfa($_data) {
$row = $stmt->fetch(PDO::FETCH_ASSOC); $row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row) { if ($row) {
if ($row['authsource'] == 'ldap'){ if ($row['authsource'] == 'ldap'){
$iam_settings = identity_provider('get');
if (!ldap_mbox_login($username, $_data["confirm_password"], $iam_settings)) $access_denied = true; if (!ldap_mbox_login($username, $_data["confirm_password"], $iam_settings)) $access_denied = true;
else $access_denied = false; else $access_denied = false;
} else { } else {
@ -2129,20 +2130,13 @@ function uuid4() {
function identity_provider($_action = null, $_data = null, $_extra = null) { function identity_provider($_action = null, $_data = null, $_extra = null) {
global $pdo; global $pdo;
global $iam_provider; global $iam_provider;
global $iam_settings;
$data_log = $_data; $data_log = $_data;
if (isset($data_log['client_secret'])) $data_log['client_secret'] = '*'; if (isset($data_log['client_secret'])) $data_log['client_secret'] = '*';
if (isset($data_log['access_token'])) $data_log['access_token'] = '*'; if (isset($data_log['access_token'])) $data_log['access_token'] = '*';
switch ($_action) { switch ($_action) {
case NULL:
if ($iam_provider) {
return $iam_provider;
} else {
$iam_provider = identity_provider("init");
return $iam_provider;
}
break;
case 'get': case 'get':
$settings = array(); $settings = array();
$stmt = $pdo->prepare("SELECT * FROM `identity_provider`;"); $stmt = $pdo->prepare("SELECT * FROM `identity_provider`;");
@ -2228,6 +2222,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
return false; return false;
} }
$_data['ignore_ssl_error'] = isset($_data['ignore_ssl_error']) ? boolval($_data['ignore_ssl_error']) : false;
switch ($_data['authsource']) { switch ($_data['authsource']) {
case "keycloak": case "keycloak":
$_data['server_url'] = (!empty($_data['server_url'])) ? rtrim($_data['server_url'], '/') : null; $_data['server_url'] = (!empty($_data['server_url'])) ? rtrim($_data['server_url'], '/') : null;
@ -2236,16 +2231,17 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
$_data['import_users'] = isset($_data['import_users']) ? intval($_data['import_users']) : 0; $_data['import_users'] = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15; $_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval']; $_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
$required_settings = array('authsource', 'server_url', 'realm', 'client_id', 'client_secret', 'redirect_url', 'version', 'mailpassword_flow', 'periodic_sync', 'import_users', 'sync_interval'); $required_settings = array('authsource', 'server_url', 'realm', 'client_id', 'client_secret', 'redirect_url', 'version', 'mailpassword_flow', 'periodic_sync', 'import_users', 'sync_interval', 'ignore_ssl_error');
break; break;
case "generic-oidc": case "generic-oidc":
$_data['authorize_url'] = (!empty($_data['authorize_url'])) ? $_data['authorize_url'] : null; $_data['authorize_url'] = (!empty($_data['authorize_url'])) ? $_data['authorize_url'] : null;
$_data['token_url'] = (!empty($_data['token_url'])) ? $_data['token_url'] : null; $_data['token_url'] = (!empty($_data['token_url'])) ? $_data['token_url'] : null;
$_data['userinfo_url'] = (!empty($_data['userinfo_url'])) ? $_data['userinfo_url'] : null; $_data['userinfo_url'] = (!empty($_data['userinfo_url'])) ? $_data['userinfo_url'] : null;
$_data['client_scopes'] = (!empty($_data['client_scopes'])) ? $_data['client_scopes'] : "openid profile email"; $_data['client_scopes'] = (!empty($_data['client_scopes'])) ? $_data['client_scopes'] : "openid profile email";
$required_settings = array('authsource', 'authorize_url', 'token_url', 'client_id', 'client_secret', 'redirect_url', 'userinfo_url', 'client_scopes'); $required_settings = array('authsource', 'authorize_url', 'token_url', 'client_id', 'client_secret', 'redirect_url', 'userinfo_url', 'client_scopes', 'ignore_ssl_error');
break; break;
case "ldap": case "ldap":
$_data['host'] = (!empty($_data['host'])) ? str_replace(" ", "", $_data['host']) : "";
$_data['port'] = (!empty($_data['port'])) ? intval($_data['port']) : 389; $_data['port'] = (!empty($_data['port'])) ? intval($_data['port']) : 389;
$_data['username_field'] = (!empty($_data['username_field'])) ? strtolower($_data['username_field']) : "mail"; $_data['username_field'] = (!empty($_data['username_field'])) ? strtolower($_data['username_field']) : "mail";
$_data['attribute_field'] = (!empty($_data['attribute_field'])) ? strtolower($_data['attribute_field']) : ""; $_data['attribute_field'] = (!empty($_data['attribute_field'])) ? strtolower($_data['attribute_field']) : "";
@ -2254,7 +2250,6 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
$_data['import_users'] = isset($_data['import_users']) ? intval($_data['import_users']) : 0; $_data['import_users'] = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
$_data['use_ssl'] = isset($_data['use_ssl']) ? boolval($_data['use_ssl']) : false; $_data['use_ssl'] = isset($_data['use_ssl']) ? boolval($_data['use_ssl']) : false;
$_data['use_tls'] = isset($_data['use_tls']) && !$_data['use_ssl'] ? boolval($_data['use_tls']) : false; $_data['use_tls'] = isset($_data['use_tls']) && !$_data['use_ssl'] ? boolval($_data['use_tls']) : false;
$_data['ignore_ssl_error'] = isset($_data['ignore_ssl_error']) ? boolval($_data['ignore_ssl_error']) : false;
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15; $_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval']; $_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
$required_settings = array('authsource', 'host', 'port', 'basedn', 'username_field', 'filter', 'attribute_field', 'binddn', 'bindpass', 'periodic_sync', 'import_users', 'sync_interval', 'use_ssl', 'use_tls', 'ignore_ssl_error'); $required_settings = array('authsource', 'host', 'port', 'basedn', 'username_field', 'filter', 'attribute_field', 'binddn', 'bindpass', 'periodic_sync', 'import_users', 'sync_interval', 'use_ssl', 'use_tls', 'ignore_ssl_error');
@ -2362,7 +2357,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
$options[LDAP_OPT_X_TLS_REQUIRE_CERT] = LDAP_OPT_X_TLS_NEVER; $options[LDAP_OPT_X_TLS_REQUIRE_CERT] = LDAP_OPT_X_TLS_NEVER;
} }
$provider = new \LdapRecord\Connection([ $provider = new \LdapRecord\Connection([
'hosts' => [$_data['host']], 'hosts' => explode(",", $_data['host']),
'port' => $_data['port'], 'port' => $_data['port'],
'base_dn' => $_data['basedn'], 'base_dn' => $_data['basedn'],
'username' => $_data['binddn'], 'username' => $_data['binddn'],
@ -2414,55 +2409,71 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
return true; return true;
break; break;
case "init": case "init":
$iam_settings = identity_provider('get'); $settings = identity_provider('get');
$provider = null; $provider = null;
switch ($iam_settings['authsource']) { switch ($settings['authsource']) {
case "keycloak": case "keycloak":
if ($iam_settings['server_url'] && $iam_settings['realm'] && $iam_settings['client_id'] && if ($settings['server_url'] && $settings['realm'] && $settings['client_id'] &&
$iam_settings['client_secret'] && $iam_settings['redirect_url'] && $iam_settings['version']){ $settings['client_secret'] && $settings['redirect_url'] && $settings['version']){
$guzzyClient = new GuzzleHttp\Client([
'defaults' => [
\GuzzleHttp\RequestOptions::CONNECT_TIMEOUT => 5,
\GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => true],
\GuzzleHttp\RequestOptions::VERIFY => !$settings['ignore_ssl_error'],
]
);
$provider = new Stevenmaguire\OAuth2\Client\Provider\Keycloak([ $provider = new Stevenmaguire\OAuth2\Client\Provider\Keycloak([
'authServerUrl' => $iam_settings['server_url'], 'authServerUrl' => $settings['server_url'],
'realm' => $iam_settings['realm'], 'realm' => $settings['realm'],
'clientId' => $iam_settings['client_id'], 'clientId' => $settings['client_id'],
'clientSecret' => $iam_settings['client_secret'], 'clientSecret' => $settings['client_secret'],
'redirectUri' => $iam_settings['redirect_url'], 'redirectUri' => $settings['redirect_url'],
'version' => $iam_settings['version'], 'version' => $settings['version'],
// 'encryptionAlgorithm' => 'RS256', // optional // 'encryptionAlgorithm' => 'RS256', // optional
// 'encryptionKeyPath' => '../key.pem' // optional // 'encryptionKeyPath' => '../key.pem' // optional
// 'encryptionKey' => 'contents_of_key_or_certificate' // optional // 'encryptionKey' => 'contents_of_key_or_certificate' // optional
]); ]);
$provider->setHttpClient($guzzyClient);
} }
break; break;
case "generic-oidc": case "generic-oidc":
if ($iam_settings['client_id'] && $iam_settings['client_secret'] && $iam_settings['redirect_url'] && if ($settings['client_id'] && $settings['client_secret'] && $settings['redirect_url'] &&
$iam_settings['authorize_url'] && $iam_settings['token_url'] && $iam_settings['userinfo_url']){ $settings['authorize_url'] && $settings['token_url'] && $settings['userinfo_url']){
$guzzyClient = new GuzzleHttp\Client([
'defaults' => [
\GuzzleHttp\RequestOptions::CONNECT_TIMEOUT => 5,
\GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => true],
\GuzzleHttp\RequestOptions::VERIFY => !$settings['ignore_ssl_error'],
]
);
$provider = new \League\OAuth2\Client\Provider\GenericProvider([ $provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => $iam_settings['client_id'], 'clientId' => $settings['client_id'],
'clientSecret' => $iam_settings['client_secret'], 'clientSecret' => $settings['client_secret'],
'redirectUri' => $iam_settings['redirect_url'], 'redirectUri' => $settings['redirect_url'],
'urlAuthorize' => $iam_settings['authorize_url'], 'urlAuthorize' => $settings['authorize_url'],
'urlAccessToken' => $iam_settings['token_url'], 'urlAccessToken' => $settings['token_url'],
'urlResourceOwnerDetails' => $iam_settings['userinfo_url'], 'urlResourceOwnerDetails' => $settings['userinfo_url'],
'scopes' => $iam_settings['client_scopes'] 'scopes' => $settings['client_scopes']
]); ]);
$provider->setHttpClient($guzzyClient);
} }
break; break;
case "ldap": case "ldap":
if ($iam_settings['host'] && $iam_settings['port'] && $iam_settings['basedn'] && if ($settings['host'] && $settings['port'] && $settings['basedn'] &&
$iam_settings['binddn'] && $iam_settings['bindpass']){ $settings['binddn'] && $settings['bindpass']){
$options = array(); $options = array();
if ($iam_settings['ignore_ssl_error']) { if ($settings['ignore_ssl_error']) {
$options[LDAP_OPT_X_TLS_REQUIRE_CERT] = LDAP_OPT_X_TLS_NEVER; $options[LDAP_OPT_X_TLS_REQUIRE_CERT] = LDAP_OPT_X_TLS_NEVER;
} }
$provider = new \LdapRecord\Connection([ $provider = new \LdapRecord\Connection([
'hosts' => [$iam_settings['host']], 'hosts' => explode(",", $settings['host']),
'port' => $iam_settings['port'], 'port' => $settings['port'],
'base_dn' => $iam_settings['basedn'], 'base_dn' => $settings['basedn'],
'username' => $iam_settings['binddn'], 'username' => $settings['binddn'],
'password' => $iam_settings['bindpass'], 'password' => $settings['bindpass'],
'use_ssl' => $iam_settings['use_ssl'], 'use_ssl' => $settings['use_ssl'],
'use_tls' => $iam_settings['use_tls'], 'use_tls' => $settings['use_tls'],
'options' => $options 'options' => $options
]); ]);
try { try {
@ -2473,12 +2484,9 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
} }
break; break;
} }
return $provider; return $provider;
break; break;
case "verify-sso": case "verify-sso":
$provider = $_data['iam_provider'];
$iam_settings = identity_provider('get');
if ($iam_settings['authsource'] != 'keycloak' && $iam_settings['authsource'] != 'generic-oidc'){ if ($iam_settings['authsource'] != 'keycloak' && $iam_settings['authsource'] != 'generic-oidc'){
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
@ -2489,10 +2497,10 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
} }
try { try {
$token = $provider->getAccessToken('authorization_code', ['code' => $_GET['code']]); $token = $iam_provider->getAccessToken('authorization_code', ['code' => $_GET['code']]);
$_SESSION['iam_token'] = $token->getToken(); $plain_token = $token->getToken();
$_SESSION['iam_refresh_token'] = $token->getRefreshToken(); $plain_refreshtoken = $token->getRefreshToken();
$info = $provider->getResourceOwner($token)->toArray(); $info = $iam_provider->getResourceOwner($token)->toArray();
} catch (Throwable $e) { } catch (Throwable $e) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
@ -2504,6 +2512,10 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
// check if email address is given // check if email address is given
if (empty($info['email'])) return false; if (empty($info['email'])) return false;
// get mapped template
$user_template = $info['mailcow_template'];
$mapper_key = array_search($user_template, $iam_settings['mappers']);
// token valid, get mailbox // token valid, get mailbox
$stmt = $pdo->prepare("SELECT * FROM `mailbox` $stmt = $pdo->prepare("SELECT * FROM `mailbox`
INNER JOIN domain on mailbox.domain = domain.domain INNER JOIN domain on mailbox.domain = domain.domain
@ -2516,7 +2528,18 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
$row = $stmt->fetch(PDO::FETCH_ASSOC); $row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row){ if ($row){
// success // success
if ($mapper_key !== false) {
// update user
mailbox('edit', 'mailbox_from_template', array(
'username' => $info['email'],
'name' => $info['name'],
'template' => $iam_settings['templates'][$mapper_key],
'hasAccess' => true
));
}
set_user_loggedin_session($info['email']); set_user_loggedin_session($info['email']);
$_SESSION['iam_token'] = $plain_token;
$_SESSION['iam_refresh_token'] = $plain_refreshtoken;
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']), 'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']),
@ -2525,9 +2548,6 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
return true; return true;
} }
// get mapped template, if not set return false
// also return false if no mappers were defined
$user_template = $info['mailcow_template'];
if (empty($iam_settings['mappers']) || empty($user_template)){ if (empty($iam_settings['mappers']) || empty($user_template)){
clear_session(); clear_session();
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
@ -2537,9 +2557,6 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
); );
return false; return false;
} }
// check if matching attribute exist
$mapper_key = array_search($user_template, $iam_settings['mappers']);
if ($mapper_key === false) { if ($mapper_key === false) {
clear_session(); clear_session();
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
@ -2554,9 +2571,10 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
$create_res = mailbox('add', 'mailbox_from_template', array( $create_res = mailbox('add', 'mailbox_from_template', array(
'domain' => explode('@', $info['email'])[1], 'domain' => explode('@', $info['email'])[1],
'local_part' => explode('@', $info['email'])[0], 'local_part' => explode('@', $info['email'])[0],
'name' => $info['firstName'] . " " . $info['lastName'], 'name' => $info['name'],
'authsource' => $iam_settings['authsource'], 'authsource' => $iam_settings['authsource'],
'template' => $iam_settings['templates'][$mapper_key] 'template' => $iam_settings['templates'][$mapper_key],
'hasAccess' => true
)); ));
if (!$create_res){ if (!$create_res){
clear_session(); clear_session();
@ -2569,6 +2587,8 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
} }
set_user_loggedin_session($info['email']); set_user_loggedin_session($info['email']);
$_SESSION['iam_token'] = $plain_token;
$_SESSION['iam_refresh_token'] = $plain_refreshtoken;
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']), 'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']),
@ -2577,13 +2597,11 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
return true; return true;
break; break;
case "refresh-token": case "refresh-token":
$provider = $_data['iam_provider'];
try { try {
$token = $provider->getAccessToken('refresh_token', ['refresh_token' => $_SESSION['iam_refresh_token']]); $token = $iam_provider->getAccessToken('refresh_token', ['refresh_token' => $_SESSION['iam_refresh_token']]);
$_SESSION['iam_token'] = $token->getToken(); $plain_token = $token->getToken();
$_SESSION['iam_refresh_token'] = $token->getRefreshToken(); $plain_refreshtoken = $token->getRefreshToken();
$info = $provider->getResourceOwner($token)->toArray(); $info = $iam_provider->getResourceOwner($token)->toArray();
} catch (Throwable $e) { } catch (Throwable $e) {
clear_session(); clear_session();
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
@ -2604,22 +2622,20 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
return false; return false;
} }
$_SESSION['mailcow_cc_username'] = $info['email']; set_user_loggedin_session($info['email']);
$_SESSION['mailcow_cc_role'] = "user"; $_SESSION['iam_token'] = $plain_token;
$_SESSION['iam_refresh_token'] = $plain_refreshtoken;
return true; return true;
break; break;
case "get-redirect": case "get-redirect":
$iam_settings = identity_provider('get');
if ($iam_settings['authsource'] != 'keycloak' && $iam_settings['authsource'] != 'generic-oidc') if ($iam_settings['authsource'] != 'keycloak' && $iam_settings['authsource'] != 'generic-oidc')
return false; return false;
$provider = $_data['iam_provider']; $authUrl = $iam_provider->getAuthorizationUrl();
$authUrl = $provider->getAuthorizationUrl(); $_SESSION['oauth2state'] = $iam_provider->getState();
$_SESSION['oauth2state'] = $provider->getState();
return $authUrl; return $authUrl;
break; break;
case "get-keycloak-admin-token": case "get-keycloak-admin-token":
// get access_token for service account of mailcow client // get access_token for service account of mailcow client
$iam_settings = identity_provider('get');
if ($iam_settings['authsource'] !== 'keycloak') return false; if ($iam_settings['authsource'] !== 'keycloak') return false;
if (isset($iam_settings['access_token'])) { if (isset($iam_settings['access_token'])) {
// check if access_token is valid // check if access_token is valid

View File

@ -1045,7 +1045,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$password2 = ''; $password2 = '';
$password_hashed = ''; $password_hashed = '';
} }
if (!$_extra['iam_create_login'] && ((!isset($_SESSION['acl']['unlimited_quota']) || $_SESSION['acl']['unlimited_quota'] != "1") && $quota_m === 0)) { if (!$_extra['hasAccess'] && ((!isset($_SESSION['acl']['unlimited_quota']) || $_SESSION['acl']['unlimited_quota'] != "1") && $quota_m === 0)) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -1075,6 +1075,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$quarantine_category = (isset($_data['quarantine_category'])) ? strval($_data['quarantine_category']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']); $quarantine_category = (isset($_data['quarantine_category'])) ? strval($_data['quarantine_category']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']);
$quota_b = ($quota_m * 1048576); $quota_b = ($quota_m * 1048576);
$attribute_hash = (!empty($_data['attribute_hash'])) ? $_data['attribute_hash'] : ''; $attribute_hash = (!empty($_data['attribute_hash'])) ? $_data['attribute_hash'] : '';
if (in_array($authsource, array('keycloak', 'generic-oidc', 'ldap'))){
$force_pw_update = 0;
}
$mailbox_attrs = json_encode( $mailbox_attrs = json_encode(
array( array(
'force_pw_update' => strval($force_pw_update), 'force_pw_update' => strval($force_pw_update),
@ -1101,7 +1104,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
return false; return false;
} }
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain) && !$_extra['iam_create_login']) { if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain) && !$_extra['hasAccess']) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -1364,6 +1367,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attribute_hash = sha1(json_encode($mbox_template_data["attributes"])); $attribute_hash = sha1(json_encode($mbox_template_data["attributes"]));
$mbox_template_data = json_decode($mbox_template_data["attributes"], true); $mbox_template_data = json_decode($mbox_template_data["attributes"], true);
$mbox_template_data['domain'] = $_data['domain']; $mbox_template_data['domain'] = $_data['domain'];
$mbox_template_data['name'] = $_data['name'];
$mbox_template_data['local_part'] = $_data['local_part']; $mbox_template_data['local_part'] = $_data['local_part'];
$mbox_template_data['authsource'] = $_data['authsource']; $mbox_template_data['authsource'] = $_data['authsource'];
$mbox_template_data['attribute_hash'] = $attribute_hash; $mbox_template_data['attribute_hash'] = $attribute_hash;
@ -1381,7 +1385,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
} }
} }
return mailbox('add', 'mailbox', $mailbox_attributes, array('iam_create_login' => true)); return mailbox('add', 'mailbox', $mailbox_attributes, array('hasAccess' => $_data['hasAccess']));
break; break;
case 'resource': case 'resource':
$domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46); $domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46);
@ -1749,7 +1753,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
else { else {
$usernames = $_data['username']; $usernames = $_data['username'];
} }
if (!isset($_SESSION['acl']['tls_policy']) || $_SESSION['acl']['tls_policy'] != "1" ) { if (!$_extra['hasAccess'] && (!isset($_SESSION['acl']['tls_policy']) || $_SESSION['acl']['tls_policy'] != "1")) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -1758,7 +1762,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
return false; return false;
} }
foreach ($usernames as $username) { foreach ($usernames as $username) {
if (!filter_var($username, FILTER_VALIDATE_EMAIL) || !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) { if (!$_extra['hasAccess'] && (!filter_var($username, FILTER_VALIDATE_EMAIL) || !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username))) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -1766,7 +1770,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
continue; continue;
} }
$is_now = mailbox('get', 'tls_policy', $username); $is_now = mailbox('get', 'tls_policy', $username, $_extra);
if (!empty($is_now)) { if (!empty($is_now)) {
$tls_enforce_in = (isset($_data['tls_enforce_in'])) ? intval($_data['tls_enforce_in']) : $is_now['tls_enforce_in']; $tls_enforce_in = (isset($_data['tls_enforce_in'])) ? intval($_data['tls_enforce_in']) : $is_now['tls_enforce_in'];
$tls_enforce_out = (isset($_data['tls_enforce_out'])) ? intval($_data['tls_enforce_out']) : $is_now['tls_enforce_out']; $tls_enforce_out = (isset($_data['tls_enforce_out'])) ? intval($_data['tls_enforce_out']) : $is_now['tls_enforce_out'];
@ -1803,7 +1807,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
else { else {
$usernames = $_data['username']; $usernames = $_data['username'];
} }
if (!isset($_SESSION['acl']['quarantine_notification']) || $_SESSION['acl']['quarantine_notification'] != "1" ) { if (!$_extra['hasAccess'] && (!isset($_SESSION['acl']['quarantine_notification']) || $_SESSION['acl']['quarantine_notification'] != "1")) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -1812,7 +1816,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
return false; return false;
} }
foreach ($usernames as $username) { foreach ($usernames as $username) {
if (!filter_var($username, FILTER_VALIDATE_EMAIL) || !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) { if (!$_extra['hasAccess'] && (!filter_var($username, FILTER_VALIDATE_EMAIL) || !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username))) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -1820,7 +1824,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
continue; continue;
} }
$is_now = mailbox('get', 'quarantine_notification', $username); $is_now = mailbox('get', 'quarantine_notification', $username, $_extra);
if (!empty($is_now)) { if (!empty($is_now)) {
$quarantine_notification = (isset($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : $is_now['quarantine_notification']; $quarantine_notification = (isset($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : $is_now['quarantine_notification'];
} }
@ -1862,7 +1866,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
else { else {
$usernames = $_data['username']; $usernames = $_data['username'];
} }
if (!isset($_SESSION['acl']['quarantine_category']) || $_SESSION['acl']['quarantine_category'] != "1" ) { if (!$_extra['hasAccess'] && (!isset($_SESSION['acl']['quarantine_category']) || $_SESSION['acl']['quarantine_category'] != "1")) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -1871,7 +1875,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
return false; return false;
} }
foreach ($usernames as $username) { foreach ($usernames as $username) {
if (!filter_var($username, FILTER_VALIDATE_EMAIL) || !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) { if (!$_extra['hasAccess'] && (!filter_var($username, FILTER_VALIDATE_EMAIL) || !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username))) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -1879,7 +1883,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
continue; continue;
} }
$is_now = mailbox('get', 'quarantine_category', $username); $is_now = mailbox('get', 'quarantine_category', $username, $_extra);
if (!empty($is_now)) { if (!empty($is_now)) {
$quarantine_category = (isset($_data['quarantine_category'])) ? $_data['quarantine_category'] : $is_now['quarantine_category']; $quarantine_category = (isset($_data['quarantine_category'])) ? $_data['quarantine_category'] : $is_now['quarantine_category'];
} }
@ -2923,7 +2927,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
continue; continue;
} }
$is_now = mailbox('get', 'mailbox_details', $username); $is_now = mailbox('get', 'mailbox_details', $username, $_extra);
if (isset($_data['protocol_access'])) { if (isset($_data['protocol_access'])) {
$_data['protocol_access'] = (array)$_data['protocol_access']; $_data['protocol_access'] = (array)$_data['protocol_access'];
$_data['imap_access'] = (in_array('imap', $_data['protocol_access'])) ? 1 : 0; $_data['imap_access'] = (in_array('imap', $_data['protocol_access'])) ? 1 : 0;
@ -2934,12 +2938,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
if (!empty($is_now)) { if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active']; $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
(int)$force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($is_now['attributes']['force_pw_update']); (int)$force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($is_now['attributes']['force_pw_update']);
(int)$sogo_access = (isset($_data['sogo_access']) && isset($_SESSION['acl']['sogo_access']) && $_SESSION['acl']['sogo_access'] == "1") ? intval($_data['sogo_access']) : intval($is_now['attributes']['sogo_access']); (int)$sogo_access = ((isset($_data['sogo_access']) && isset($_SESSION['acl']['sogo_access']) && $_SESSION['acl']['sogo_access'] == "1") || $_extra['hasAccess']) ? intval($_data['sogo_access']) : intval($is_now['attributes']['sogo_access']);
(int)$imap_access = (isset($_data['imap_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['imap_access']) : intval($is_now['attributes']['imap_access']); (int)$imap_access = ((isset($_data['imap_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") || $_extra['hasAccess']) ? intval($_data['imap_access']) : intval($is_now['attributes']['imap_access']);
(int)$pop3_access = (isset($_data['pop3_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['pop3_access']) : intval($is_now['attributes']['pop3_access']); (int)$pop3_access = ((isset($_data['pop3_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") || $_extra['hasAccess']) ? intval($_data['pop3_access']) : intval($is_now['attributes']['pop3_access']);
(int)$smtp_access = (isset($_data['smtp_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['smtp_access']) : intval($is_now['attributes']['smtp_access']); (int)$smtp_access = ((isset($_data['smtp_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") || $_extra['hasAccess']) ? intval($_data['smtp_access']) : intval($is_now['attributes']['smtp_access']);
(int)$sieve_access = (isset($_data['sieve_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['sieve_access']) : intval($is_now['attributes']['sieve_access']); (int)$sieve_access = ((isset($_data['sieve_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") || $_extra['hasAccess']) ? intval($_data['sieve_access']) : intval($is_now['attributes']['sieve_access']);
(int)$relayhost = (isset($_data['relayhost']) && isset($_SESSION['acl']['mailbox_relayhost']) && $_SESSION['acl']['mailbox_relayhost'] == "1") ? intval($_data['relayhost']) : intval($is_now['attributes']['relayhost']); (int)$relayhost = ((isset($_data['relayhost']) && isset($_SESSION['acl']['mailbox_relayhost']) && $_SESSION['acl']['mailbox_relayhost'] == "1") || $_extra['hasAccess']) ? intval($_data['relayhost']) : intval($is_now['attributes']['relayhost']);
(int)$quota_m = (isset_has_content($_data['quota'])) ? intval($_data['quota']) : ($is_now['quota'] / 1048576); (int)$quota_m = (isset_has_content($_data['quota'])) ? intval($_data['quota']) : ($is_now['quota'] / 1048576);
$name = (!empty($_data['name'])) ? ltrim(rtrim($_data['name'], '>'), '<') : $is_now['name']; $name = (!empty($_data['name'])) ? ltrim(rtrim($_data['name'], '>'), '<') : $is_now['name'];
$domain = $is_now['domain']; $domain = $is_now['domain'];
@ -2952,6 +2956,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
if (in_array($_data['authsource'], array('mailcow', 'keycloak', 'generic-oidc', 'ldap'))){ if (in_array($_data['authsource'], array('mailcow', 'keycloak', 'generic-oidc', 'ldap'))){
$authsource = $_data['authsource']; $authsource = $_data['authsource'];
} }
if (in_array($authsource, array('keycloak', 'generic-oidc', 'ldap'))){
$force_pw_update = 0;
}
$pw_recovery_email = (isset($_data['pw_recovery_email']) && $authsource == 'mailcow') ? $_data['pw_recovery_email'] : $is_now['attributes']['recovery_email']; $pw_recovery_email = (isset($_data['pw_recovery_email']) && $authsource == 'mailcow') ? $_data['pw_recovery_email'] : $is_now['attributes']['recovery_email'];
} }
else { else {
@ -2963,7 +2970,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
continue; continue;
} }
// if already 0 == ok // if already 0 == ok
if ((!isset($_SESSION['acl']['unlimited_quota']) || $_SESSION['acl']['unlimited_quota'] != "1") && ($quota_m == 0 && $is_now['quota'] != 0)) { if (!$_extra['hasAccess'] && (!isset($_SESSION['acl']['unlimited_quota']) || $_SESSION['acl']['unlimited_quota'] != "1") && ($quota_m == 0 && $is_now['quota'] != 0)) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -2971,7 +2978,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
return false; return false;
} }
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) { if (!$_extra['hasAccess'] && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -2979,7 +2986,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
continue; continue;
} }
$DomainData = mailbox('get', 'domain_details', $domain); $DomainData = mailbox('get', 'domain_details', $domain, $_extra);
if ($quota_m > ($is_now['max_new_quota'] / 1048576)) { if ($quota_m > ($is_now['max_new_quota'] / 1048576)) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
@ -2998,7 +3005,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
} }
$extra_acls = array(); $extra_acls = array();
if (isset($_data['extended_sender_acl'])) { if (isset($_data['extended_sender_acl'])) {
if (!isset($_SESSION['acl']['extend_sender_acl']) || $_SESSION['acl']['extend_sender_acl'] != "1" ) { if (!$_extra['hasAccess'] && (!isset($_SESSION['acl']['extend_sender_acl']) || $_SESSION['acl']['extend_sender_acl'] != "1")) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -3493,7 +3500,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
} }
$attribute_hash = sha1(json_encode($mbox_template_data["attributes"])); $attribute_hash = sha1(json_encode($mbox_template_data["attributes"]));
$is_now = mailbox('get', 'mailbox_details', $_data['username']); $is_now = mailbox('get', 'mailbox_details', $_data['username'], array('hasAccess' => $_data['hasAccess']));
$name = ltrim(rtrim($_data['name'], '>'), '<'); $name = ltrim(rtrim($_data['name'], '>'), '<');
if ($is_now['attributes']['attribute_hash'] == $attribute_hash && $is_now['name'] == $name) if ($is_now['attributes']['attribute_hash'] == $attribute_hash && $is_now['name'] == $name)
return true; return true;
@ -3529,19 +3536,20 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
} }
$mailbox_attributes['quota'] = intval($mailbox_attributes['quota'] / 1048576); $mailbox_attributes['quota'] = intval($mailbox_attributes['quota'] / 1048576);
$result = mailbox('edit', 'mailbox', $mailbox_attributes); $result = mailbox('edit', 'mailbox', $mailbox_attributes, array('hasAccess' => $_data['hasAccess']));
if ($result === false) return $result; if ($result === false) return $result;
$result = mailbox('edit', 'tls_policy', $tls_attributes); $result = mailbox('edit', 'tls_policy', $tls_attributes, array('hasAccess' => $_data['hasAccess']));
if ($result === false) return $result; if ($result === false) return $result;
$result = mailbox('edit', 'quarantine_notification', $quarantine_attributes); $result = mailbox('edit', 'quarantine_notification', $quarantine_attributes, array('hasAccess' => $_data['hasAccess']));
if ($result === false) return $result; if ($result === false) return $result;
$result = mailbox('edit', 'quarantine_category', $quarantine_attributes); $result = mailbox('edit', 'quarantine_category', $quarantine_attributes, array('hasAccess' => $_data['hasAccess']));
if ($result === false) return $result; if ($result === false) return $result;
$result = ratelimit('edit', 'mailbox', $ratelimit_attributes); $result = ratelimit('edit', 'mailbox', $ratelimit_attributes, array('hasAccess' => $_data['hasAccess']));
if ($result === false) return $result; if ($result === false) return $result;
$result = acl('edit', 'user', $acl_attributes); $result = acl('edit', 'user', $acl_attributes, array('hasAccess' => $_data['hasAccess']));
if ($result === false) return $result; if ($result === false) return $result;
$_SESSION['return'] = array();
return true; return true;
break; break;
case 'mailbox_templates': case 'mailbox_templates':
@ -4077,7 +4085,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
case 'tls_policy': case 'tls_policy':
$attrs = array(); $attrs = array();
if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) { if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { if (!$_extra['hasAccess'] && !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false; return false;
} }
} }
@ -4096,7 +4104,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
case 'quarantine_notification': case 'quarantine_notification':
$attrs = array(); $attrs = array();
if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) { if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { if (!$_extra['hasAccess'] && !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false; return false;
} }
} }
@ -4112,7 +4120,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
case 'quarantine_category': case 'quarantine_category':
$attrs = array(); $attrs = array();
if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) { if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { if (!$_extra['hasAccess'] && (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data))) {
return false; return false;
} }
} }
@ -4627,7 +4635,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
case 'domain_details': case 'domain_details':
$domaindata = array(); $domaindata = array();
$_data = idn_to_ascii(strtolower(trim($_data)), 0, INTL_IDNA_VARIANT_UTS46); $_data = idn_to_ascii(strtolower(trim($_data)), 0, INTL_IDNA_VARIANT_UTS46);
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { if (!$_extra['hasAccess'] && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false; return false;
} }
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain"); $stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain");
@ -4793,7 +4801,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
} }
break; break;
case 'mailbox_details': case 'mailbox_details':
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { if (!$_extra['hasAccess'] && !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false; return false;
} }
$mailboxdata = array(); $mailboxdata = array();
@ -4891,7 +4899,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
else if ($SaslLogs['service'] == 'pop3') { else if ($SaslLogs['service'] == 'pop3') {
$last_pop3_login = strtotime($SaslLogs['datetime']); $last_pop3_login = strtotime($SaslLogs['datetime']);
} }
else if ($SaslLogs['service'] == 'SSO') { else if ($SaslLogs['service'] == 'SSO') {
$last_sso_login = strtotime($SaslLogs['datetime']); $last_sso_login = strtotime($SaslLogs['datetime']);
} }
} }
@ -4904,7 +4912,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
if (!isset($last_pop3_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) { if (!isset($last_pop3_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
$last_pop3_login = 0; $last_pop3_login = 0;
} }
if (!isset($last_sso_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) { if (!isset($last_sso_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
$last_sso_login = 0; $last_sso_login = 0;
} }
$mailboxdata['last_imap_login'] = $last_imap_login; $mailboxdata['last_imap_login'] = $last_imap_login;
@ -4956,7 +4964,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
return $mailboxdata; return $mailboxdata;
break; break;
case 'mailbox_templates': case 'mailbox_templates':
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin" && !$_extra['iam_create_login']) { if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin" && !$_extra['hasAccess']) {
return false; return false;
} }
$_data = (isset($_data)) ? intval($_data) : null; $_data = (isset($_data)) ? intval($_data) : null;

View File

@ -4,7 +4,7 @@ function ratelimit($_action, $_scope, $_data = null, $_extra = null) {
$_data_log = $_data; $_data_log = $_data;
switch ($_action) { switch ($_action) {
case 'edit': case 'edit':
if ((!isset($_SESSION['acl']['ratelimit']) || $_SESSION['acl']['ratelimit'] != "1") && !$_extra['iam_create_login']) { if ((!isset($_SESSION['acl']['ratelimit']) || $_SESSION['acl']['ratelimit'] != "1") && !$_extra['hasAccess']) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@ -92,8 +92,8 @@ function ratelimit($_action, $_scope, $_data = null, $_extra = null) {
); );
continue; continue;
} }
if ((!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object) if (((!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)
|| ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) && !$_extra['iam_create_login']) { || ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin'))) && !$_extra['hasAccess']) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log), 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
@ -139,7 +139,7 @@ function ratelimit($_action, $_scope, $_data = null, $_extra = null) {
case 'get': case 'get':
switch ($_scope) { switch ($_scope) {
case 'domain': case 'domain':
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data) && !$_extra['hasAccess']) {
return false; return false;
} }
try { try {
@ -164,7 +164,7 @@ function ratelimit($_action, $_scope, $_data = null, $_extra = null) {
return false; return false;
break; break;
case 'mailbox': case 'mailbox':
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data) if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data && !$_extra['hasAccess'])
|| ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) { || ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) {
return false; return false;
} }

View File

@ -180,6 +180,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
// Init Identity Provider // Init Identity Provider
$iam_provider = identity_provider('init'); $iam_provider = identity_provider('init');
$iam_settings = identity_provider('get');
// IMAP lib // IMAP lib
// use Ddeboer\Imap\Server; // use Ddeboer\Imap\Server;

View File

@ -3,18 +3,18 @@
if ($iam_provider){ if ($iam_provider){
if (isset($_GET['iam_sso'])){ if (isset($_GET['iam_sso'])){
// redirect for sso // redirect for sso
$redirect_uri = identity_provider('get-redirect', array('iam_provider' => $iam_provider)); $redirect_uri = identity_provider('get-redirect');
$redirect_uri = !empty($redirect_uri) ? $redirect_uri : '/'; $redirect_uri = !empty($redirect_uri) ? $redirect_uri : '/';
header('Location: ' . $redirect_uri); header('Location: ' . $redirect_uri);
die(); die();
} }
if ($_SESSION['iam_token'] && $_SESSION['iam_refresh_token']) { if ($_SESSION['iam_token'] && $_SESSION['iam_refresh_token']) {
// Session found, try to refresh // Session found, try to refresh
$isRefreshed = identity_provider('refresh-token', array('iam_provider' => $iam_provider)); $isRefreshed = identity_provider('refresh-token');
if (!$isRefreshed){ if (!$isRefreshed){
// Session could not be refreshed, redirect to provider // Session could not be refreshed, redirect to provider
$redirect_uri = identity_provider('get-redirect', array('iam_provider' => $iam_provider)); $redirect_uri = identity_provider('get-redirect');
$redirect_uri = !empty($redirect_uri) ? $redirect_uri : '/'; $redirect_uri = !empty($redirect_uri) ? $redirect_uri : '/';
header('Location: ' . $redirect_uri); header('Location: ' . $redirect_uri);
die(); die();
@ -23,7 +23,7 @@ if ($iam_provider){
// Check given state against previously stored one to mitigate CSRF attack // Check given state against previously stored one to mitigate CSRF attack
// Recieved access token in $_GET['code'] // Recieved access token in $_GET['code']
// extract info and verify user // extract info and verify user
identity_provider('verify-sso', array('iam_provider' => $iam_provider)); identity_provider('verify-sso');
} }
} }

View File

@ -32,7 +32,7 @@ $_SESSION['index_query_string'] = $_SERVER['QUERY_STRING'];
$has_iam_sso = false; $has_iam_sso = false;
if ($iam_provider){ if ($iam_provider){
$has_iam_sso = identity_provider("get-redirect", array('iam_provider' => $iam_provider)) ? true : false; $has_iam_sso = identity_provider("get-redirect") ? true : false;
} }

View File

@ -1707,8 +1707,13 @@ if (isset($_GET['query'])) {
if ($score) if ($score)
$score = array("score" => preg_replace("/\s+/", "", $score)); $score = array("score" => preg_replace("/\s+/", "", $score));
process_get_return($score); process_get_return($score);
case "identity_provider": break;
process_get_return(identity_provider('get')); case "identity-provider":
if($_SESSION['mailcow_cc_role'] === 'admin') {
process_get_return($iam_settings);
} else {
process_get_return(null);
}
break; break;
break; break;
// return no route found if no case is matched // return no route found if no case is matched
@ -2086,7 +2091,6 @@ if (isset($_GET['query'])) {
break; break;
case "cors": case "cors":
process_edit_return(cors('edit', $attr)); process_edit_return(cors('edit', $attr));
case "identity_provider":
case "identity-provider": case "identity-provider":
process_edit_return(identity_provider('edit', $attr)); process_edit_return(identity_provider('edit', $attr));
break; break;

View File

@ -225,6 +225,7 @@
"iam_description": "Configure an external Provider for Authentication<br>User's mailboxes will be automatically created upon their first login, provided that an attribute mapping has been set.", "iam_description": "Configure an external Provider for Authentication<br>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 <code>Service account</code> and the permission to <code>view-users</code>.", "iam_extra_permission": "For the following settings to work, the mailcow client in Keycloak needs a <code>Service account</code> and the permission to <code>view-users</code>.",
"iam_host": "Host", "iam_host": "Host",
"iam_host_info": "Enter one or more LDAP hosts, separated by commas.",
"iam_import_users": "Import Users", "iam_import_users": "Import Users",
"iam_mapping": "Attribute Mapping", "iam_mapping": "Attribute Mapping",
"iam_bindpass": "Bind Password", "iam_bindpass": "Bind Password",

View File

@ -9,7 +9,9 @@
<div id="collapse-tab-config-identity-provider" class="card-body collapse" data-bs-parent="#admin-content"> <div id="collapse-tab-config-identity-provider" class="card-body collapse" data-bs-parent="#admin-content">
<p class="offset-sm-3 mb-4">{{ lang.admin.iam_description|raw }}</p> <p class="offset-sm-3 mb-4">{{ lang.admin.iam_description|raw }}</p>
<div class="row mb-4"> <div class="row mb-4">
<label class="control-label col-md-3 text-sm-end" for="iam_realm">{{ lang.admin.iam }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_realm">{{ lang.admin.iam }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<select <select
data-style="btn btn-secondary" data-style="btn btn-secondary"
@ -26,25 +28,33 @@
<form class="form-horizontal" autocapitalize="none" data-id="iam_keycloak" autocorrect="off" role="form" method="post"> <form class="form-horizontal" autocapitalize="none" data-id="iam_keycloak" autocorrect="off" role="form" method="post">
<input type="hidden" name="authsource" value="keycloak"> <input type="hidden" name="authsource" value="keycloak">
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_keycloak_url">{{ lang.admin.iam_server_url }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_keycloak_url">{{ lang.admin.iam_server_url }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_keycloak_url" name="server_url" value="{{ iam_settings.server_url }}" required> <input type="text" class="form-control" id="iam_keycloak_url" name="server_url" value="{{ iam_settings.server_url }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_keycloak_realm">{{ lang.admin.iam_realm }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_keycloak_realm">{{ lang.admin.iam_realm }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_keycloak_realm" name="realm" value="{{ iam_settings.realm }}" required> <input type="text" class="form-control" id="iam_keycloak_realm" name="realm" value="{{ iam_settings.realm }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_keycloak_clientid">{{ lang.admin.iam_client_id }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_keycloak_clientid">{{ lang.admin.iam_client_id }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_keycloak_clientid" name="client_id" value="{{ iam_settings.client_id }}" required> <input type="text" class="form-control" id="iam_keycloak_clientid" name="client_id" value="{{ iam_settings.client_id }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_keycloak_clientsecret">{{ lang.admin.iam_client_secret }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_keycloak_clientsecret">{{ lang.admin.iam_client_secret }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<div class="reveal-password-input input-group"> <div class="reveal-password-input input-group">
<input type="password" class="password-field form-control" id="iam_keycloak_clientsecret" name="client_secret" value="{{ iam_settings.client_secret }}" required> <input type="password" class="password-field form-control" id="iam_keycloak_clientsecret" name="client_secret" value="{{ iam_settings.client_secret }}" required>
@ -53,19 +63,25 @@
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_keycloak_redirecturl">{{ lang.admin.iam_redirect_url }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_keycloak_redirecturl">{{ lang.admin.iam_redirect_url }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_keycloak_redirecturl" name="redirect_url" value="{{ iam_settings.redirect_url }}" required> <input type="text" class="form-control" id="iam_keycloak_redirecturl" name="redirect_url" value="{{ iam_settings.redirect_url }}" required>
</div> </div>
</div> </div>
<div class="row mb-4"> <div class="row mb-4">
<label class="control-label col-md-3 text-sm-end" for="iam_keycloak_version">{{ lang.admin.iam_version }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_keycloak_version">{{ lang.admin.iam_version }}:</label>
</div>
<div class="col-sm-4"> <div class="col-sm-4">
<input type="text" class="form-control" id="iam_keycloak_version" name="version" value="{{ iam_settings.version }}" required> <input type="text" class="form-control" id="iam_keycloak_version" name="version" value="{{ iam_settings.version }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_mapping }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_mapping }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<div class="row px-2 align-items-center"> <div class="row px-2 align-items-center">
<span class="col-5 p-0 pe-2">Attribute</span> <span class="col-5 p-0 pe-2">Attribute</span>
@ -121,13 +137,15 @@
{% endif %} {% endif %}
</div> </div>
<div class="row mb-2 mt-4"> <div class="row mb-2 mt-4">
<label class="control-label col-md-3 text-sm-end"></label> <div class="col-md-3 d-flex align-items-center justify-content-md-end"></div>
<div class="col-12 col-md-9"> <div class="col-12 col-md-9">
<span>{{ lang.admin.iam_extra_permission|raw }}</span> <span>{{ lang.admin.iam_extra_permission|raw }}</span>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_rest_flow }}</label> <div class="col-md-3 d-flex align-items-start justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_rest_flow }}</label>
</div>
<div class="col-12 col-md-9"> <div class="col-12 col-md-9">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="mailpassword_flow" value="1" {% if iam_settings.mailpassword_flow == 1 %}checked{% endif %}> <input class="form-check-input" type="checkbox" role="switch" name="mailpassword_flow" value="1" {% if iam_settings.mailpassword_flow == 1 %}checked{% endif %}>
@ -140,7 +158,19 @@
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_periodic_full_sync }}</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.ignore_ssl_error }}</label>
</div>
<div class="col-12 col-md-9">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="ignore_ssl_error" value="1" {% if iam_settings.ignore_ssl_error == 1 %}checked{% endif %}>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_periodic_full_sync }}</label>
</div>
<div class="col-12 col-md-9"> <div class="col-12 col-md-9">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="periodic_sync" value="1" {% if iam_settings.periodic_sync == 1 %}checked{% endif %}> <input class="form-check-input" type="checkbox" role="switch" name="periodic_sync" value="1" {% if iam_settings.periodic_sync == 1 %}checked{% endif %}>
@ -148,7 +178,9 @@
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_import_users }}</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_import_users }}</label>
</div>
<div class="col-12 col-md-9"> <div class="col-12 col-md-9">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="import_users" value="1" {% if iam_settings.import_users == 1 %}checked{% endif %}> <input class="form-check-input" type="checkbox" role="switch" name="import_users" value="1" {% if iam_settings.import_users == 1 %}checked{% endif %}>
@ -156,7 +188,9 @@
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_sync_interval }}</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_sync_interval }}</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input class="form-control" type="number" min="1" name="sync_interval" style="width: 80px;" {% if iam_settings.sync_interval %}value="{{ iam_settings.sync_interval }}"{% else %}value="15"{% endif %}> <input class="form-control" type="number" min="1" name="sync_interval" style="width: 80px;" {% if iam_settings.sync_interval %}value="{{ iam_settings.sync_interval }}"{% else %}value="15"{% endif %}>
</div> </div>
@ -176,31 +210,41 @@
<form class="form-horizontal" autocapitalize="none" data-id="iam_generic" autocorrect="off" role="form" method="post"> <form class="form-horizontal" autocapitalize="none" data-id="iam_generic" autocorrect="off" role="form" method="post">
<input type="hidden" name="authsource" value="generic-oidc"> <input type="hidden" name="authsource" value="generic-oidc">
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_authorize_url">{{ lang.admin.iam_authorize_url }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_authorize_url">{{ lang.admin.iam_authorize_url }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_authorize_url" name="authorize_url" value="{{ iam_settings.authorize_url }}" required> <input type="text" class="form-control" id="iam_authorize_url" name="authorize_url" value="{{ iam_settings.authorize_url }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_token_url">{{ lang.admin.iam_token_url }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_token_url">{{ lang.admin.iam_token_url }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_token_url" name="token_url" value="{{ iam_settings.token_url }}" required> <input type="text" class="form-control" id="iam_token_url" name="token_url" value="{{ iam_settings.token_url }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_userinfo_url">{{ lang.admin.iam_userinfo_url }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_userinfo_url">{{ lang.admin.iam_userinfo_url }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_userinfo_url" name="userinfo_url" value="{{ iam_settings.userinfo_url }}" required> <input type="text" class="form-control" id="iam_userinfo_url" name="userinfo_url" value="{{ iam_settings.userinfo_url }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_client_id">{{ lang.admin.iam_client_id }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_client_id">{{ lang.admin.iam_client_id }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_client_id" name="client_id" value="{{ iam_settings.client_id }}" required> <input type="text" class="form-control" id="iam_client_id" name="client_id" value="{{ iam_settings.client_id }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_client_secret">{{ lang.admin.iam_client_secret }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_client_secret">{{ lang.admin.iam_client_secret }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<div class="reveal-password-input input-group"> <div class="reveal-password-input input-group">
<input type="password" class="password-field form-control" id="iam_client_secret" name="client_secret" value="{{ iam_settings.client_secret }}" required> <input type="password" class="password-field form-control" id="iam_client_secret" name="client_secret" value="{{ iam_settings.client_secret }}" required>
@ -209,19 +253,25 @@
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_redirect_url">{{ lang.admin.iam_redirect_url }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_redirect_url">{{ lang.admin.iam_redirect_url }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_redirect_url" name="redirect_url" value="{{ iam_settings.redirect_url }}" required> <input type="text" class="form-control" id="iam_redirect_url" name="redirect_url" value="{{ iam_settings.redirect_url }}" required>
</div> </div>
</div> </div>
<div class="row mb-4"> <div class="row mb-4">
<label class="control-label col-md-3 text-sm-end" for="iam_client_scopes">{{ lang.admin.iam_client_scopes }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_client_scopes">{{ lang.admin.iam_client_scopes }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" placeholder="openid profile email" class="form-control" id="iam_client_scopes" name="client_scopes" value="{{ iam_settings.client_scopes }}"> <input type="text" placeholder="openid profile email" class="form-control" id="iam_client_scopes" name="client_scopes" value="{{ iam_settings.client_scopes }}">
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_mapping }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_mapping }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<div class="row px-2 align-items-center"> <div class="row px-2 align-items-center">
<span class="col-5 p-0 pe-2">Attribute</span> <span class="col-5 p-0 pe-2">Attribute</span>
@ -276,6 +326,16 @@
</div> </div>
{% endif %} {% endif %}
</div> </div>
<div class="row mb-4">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.ignore_ssl_error }}</label>
</div>
<div class="col-12 col-md-9">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="ignore_ssl_error" value="1" {% if iam_settings.ignore_ssl_error == 1 %}checked{% endif %}>
</div>
</div>
</div>
<div class="row mt-4 mb-2"> <div class="row mt-4 mb-2">
<div class="offset-md-3 col-12 col-md-9 d-flex flex-wrap"> <div class="offset-md-3 col-12 col-md-9 d-flex flex-wrap">
<div class="btn-group mb-2"> <div class="btn-group mb-2">
@ -291,19 +351,26 @@
<form class="form-horizontal" autocapitalize="none" data-id="iam_ldap" autocorrect="off" role="form" method="post"> <form class="form-horizontal" autocapitalize="none" data-id="iam_ldap" autocorrect="off" role="form" method="post">
<input type="hidden" name="authsource" value="ldap"> <input type="hidden" name="authsource" value="ldap">
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_ldap_host">{{ lang.admin.iam_host }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<div class="col-12 col-md-9 col-lg-4"> <i style="font-size: 16px; cursor: pointer;" class="bi bi-patch-question-fill m-2 ms-0" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="bottom" title="{{ lang.admin.iam_host_info }}"></i>
<input type="text" class="form-control" id="iam_ldap_host" name="host" value="{{ iam_settings.host }}" required> <label class="control-label" for="iam_ldap_host">{{ lang.admin.iam_host }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4 d-flex">
<input type="text" class="form-control" id="iam_ldap_host" name="host" value="{{ iam_settings.host }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_ldap_port">{{ lang.admin.iam_port }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_ldap_port">{{ lang.admin.iam_port }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="number" class="form-control" id="iam_ldap_port" name="port" value="{{ iam_settings.port }}" required> <input type="number" class="form-control" id="iam_ldap_port" name="port" value="{{ iam_settings.port }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_use_ssl }}</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_use_ssl }}</label>
</div>
<div class="col-12 col-md-9"> <div class="col-12 col-md-9">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="use_ssl" value="1" {% if iam_settings.use_ssl == 1 %}checked{% endif %}> <input class="form-check-input" type="checkbox" role="switch" name="use_ssl" value="1" {% if iam_settings.use_ssl == 1 %}checked{% endif %}>
@ -311,7 +378,9 @@
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_use_tls }}</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_use_tls }}</label>
</div>
<div class="col-12 col-md-9"> <div class="col-12 col-md-9">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="use_tls" value="1" {% if iam_settings.use_tls == 1 %}checked{% endif %}> <input class="form-check-input" type="checkbox" role="switch" name="use_tls" value="1" {% if iam_settings.use_tls == 1 %}checked{% endif %}>
@ -319,7 +388,9 @@
</div> </div>
</div> </div>
<div class="row mb-4"> <div class="row mb-4">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.ignore_ssl_error }}</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.ignore_ssl_error }}</label>
</div>
<div class="col-12 col-md-9"> <div class="col-12 col-md-9">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="ignore_ssl_error" value="1" {% if iam_settings.ignore_ssl_error == 1 %}checked{% endif %}> <input class="form-check-input" type="checkbox" role="switch" name="ignore_ssl_error" value="1" {% if iam_settings.ignore_ssl_error == 1 %}checked{% endif %}>
@ -327,37 +398,49 @@
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_ldap_basedn">{{ lang.admin.iam_basedn }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_ldap_basedn">{{ lang.admin.iam_basedn }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_ldap_basedn" name="basedn" value="{{ iam_settings.basedn }}" required> <input type="text" class="form-control" id="iam_ldap_basedn" name="basedn" value="{{ iam_settings.basedn }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_ldap_username_field">{{ lang.admin.iam_username_field }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_ldap_username_field">{{ lang.admin.iam_username_field }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" placeholder="mail" id="iam_ldap_username_field" name="username_field" value="{{ iam_settings.username_field }}"> <input type="text" class="form-control" placeholder="mail" id="iam_ldap_username_field" name="username_field" value="{{ iam_settings.username_field }}">
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_ldap_filter">{{ lang.admin.filter }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_ldap_filter">{{ lang.admin.filter }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" placeholder="" id="iam_ldap_filter" name="filter" value="{{ iam_settings.filter }}"> <input type="text" class="form-control" placeholder="" id="iam_ldap_filter" name="filter" value="{{ iam_settings.filter }}">
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_ldap_attribute_field">{{ lang.admin.iam_attribute_field }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_ldap_attribute_field">{{ lang.admin.iam_attribute_field }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_ldap_attribute_field" name="attribute_field" value="{{ iam_settings.attribute_field }}" required> <input type="text" class="form-control" id="iam_ldap_attribute_field" name="attribute_field" value="{{ iam_settings.attribute_field }}" required>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end" for="iam_ldap_binddn">{{ lang.admin.iam_binddn }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_ldap_binddn">{{ lang.admin.iam_binddn }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input type="text" class="form-control" id="iam_ldap_binddn" name="binddn" value="{{ iam_settings.binddn }}" required> <input type="text" class="form-control" id="iam_ldap_binddn" name="binddn" value="{{ iam_settings.binddn }}" required>
</div> </div>
</div> </div>
<div class="row mb-4"> <div class="row mb-4">
<label class="control-label col-md-3 text-sm-end" for="iam_ldap_bindpass">{{ lang.admin.iam_bindpass }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="iam_ldap_bindpass">{{ lang.admin.iam_bindpass }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<div class="reveal-password-input input-group"> <div class="reveal-password-input input-group">
<input type="password" class="password-field form-control" id="iam_ldap_bindpass" name="bindpass" value="{{ iam_settings.bindpass }}" required> <input type="password" class="password-field form-control" id="iam_ldap_bindpass" name="bindpass" value="{{ iam_settings.bindpass }}" required>
@ -366,7 +449,9 @@
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_mapping }}:</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_mapping }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<div class="row px-2 align-items-center"> <div class="row px-2 align-items-center">
<span class="col-5 p-0 pe-2">Attribute</span> <span class="col-5 p-0 pe-2">Attribute</span>
@ -422,7 +507,9 @@
{% endif %} {% endif %}
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_periodic_full_sync }}</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_periodic_full_sync }}</label>
</div>
<div class="col-12 col-md-9"> <div class="col-12 col-md-9">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="periodic_sync" value="1" {% if iam_settings.periodic_sync == 1 %}checked{% endif %}> <input class="form-check-input" type="checkbox" role="switch" name="periodic_sync" value="1" {% if iam_settings.periodic_sync == 1 %}checked{% endif %}>
@ -430,7 +517,9 @@
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_import_users }}</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_import_users }}</label>
</div>
<div class="col-12 col-md-9"> <div class="col-12 col-md-9">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="import_users" value="1" {% if iam_settings.import_users == 1 %}checked{% endif %}> <input class="form-check-input" type="checkbox" role="switch" name="import_users" value="1" {% if iam_settings.import_users == 1 %}checked{% endif %}>
@ -438,7 +527,9 @@
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-md-3 text-sm-end">{{ lang.admin.iam_sync_interval }}</label> <div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label">{{ lang.admin.iam_sync_interval }}</label>
</div>
<div class="col-12 col-md-9 col-lg-4"> <div class="col-12 col-md-9 col-lg-4">
<input class="form-control" type="number" min="1" name="sync_interval" style="width: 80px;" {% if iam_settings.sync_interval %}value="{{ iam_settings.sync_interval }}"{% else %}value="15"{% endif %}> <input class="form-control" type="number" min="1" name="sync_interval" style="width: 80px;" {% if iam_settings.sync_interval %}value="{{ iam_settings.sync_interval }}"{% else %}value="15"{% endif %}>
</div> </div>

View File

@ -99,10 +99,10 @@
</div> </div>
</div> </div>
{% if mailboxdata.authsource == "mailcow" %}
<legend class="mt-4">{{ lang.user.authentication }}</legend> <legend class="mt-4">{{ lang.user.authentication }}</legend>
<hr> <hr>
{# Password Change #} {# Password Change #}
{% if mailboxdata.authsource == "mailcow" %}
<div class="row"> <div class="row">
<div class="col-12 col-md-3 d-flex"></div> <div class="col-12 col-md-3 d-flex"></div>
<div class="col-12 col-md-9 d-flex flex-wrap"> <div class="col-12 col-md-9 d-flex flex-wrap">

View File

@ -127,6 +127,7 @@ services:
- ./data/web/inc/sessions.inc.php:/mailcowauth/sessions.inc.php:z - ./data/web/inc/sessions.inc.php:/mailcowauth/sessions.inc.php:z
- ./data/web/inc/functions.mailbox.inc.php:/mailcowauth/functions.mailbox.inc.php:z - ./data/web/inc/functions.mailbox.inc.php:/mailcowauth/functions.mailbox.inc.php:z
- ./data/web/inc/functions.ratelimit.inc.php:/mailcowauth/functions.ratelimit.inc.php:z - ./data/web/inc/functions.ratelimit.inc.php:/mailcowauth/functions.ratelimit.inc.php:z
- ./data/web/inc/functions.acl.inc.php:/mailcowauth/functions.acl.inc.php:z
- rspamd-vol-1:/var/lib/rspamd - rspamd-vol-1:/var/lib/rspamd
- mysql-socket-vol-1:/var/run/mysqld/ - mysql-socket-vol-1:/var/run/mysqld/
- ./data/conf/sogo/:/etc/sogo/:z - ./data/conf/sogo/:/etc/sogo/:z