Skip to content

Commit

Permalink
[Web] add ldap idp
Browse files Browse the repository at this point in the history
  • Loading branch information
FreddleSpl0it committed Feb 20, 2024
1 parent d479d18 commit a06c783
Show file tree
Hide file tree
Showing 27 changed files with 906 additions and 236 deletions.
17 changes: 10 additions & 7 deletions data/conf/phpfpm/crons/keycloak-sync.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
logMsg("danger", $e->getMessage());
logMsg("err", $e->getMessage());
session_destroy();
exit;
}
Expand Down Expand Up @@ -68,11 +68,12 @@ function logMsg($priority, $message, $task = "Keycloak Sync") {
$_SESSION['acl']['sogo_access'] = "1";
$_SESSION['acl']['protocol_access'] = "1";
$_SESSION['acl']['mailbox_relayhost'] = "1";
$_SESSION['acl']['unlimited_quota'] = "1";

// Init Keycloak Provider
$iam_provider = identity_provider('init');
$iam_settings = identity_provider('get');
if (intval($iam_settings['periodic_sync']) != 1 && $iam_settings['import_users'] != 1) {
if ($iam_settings['authsource'] != "keycloak" || (intval($iam_settings['periodic_sync']) != 1 && intval($iam_settings['import_users']) != 1)) {
session_destroy();
exit;
}
Expand Down Expand Up @@ -127,18 +128,18 @@ function logMsg($priority, $message, $task = "Keycloak Sync") {
curl_close($ch);

if ($code != 200){
logMsg("danger", "Recieved HTTP {$code}");
logMsg("err", "Recieved HTTP {$code}");
session_destroy();
exit;
}
try {
$response = json_decode($response, true);
} catch (Exception $e) {
logMsg("danger", $e->getMessage());
logMsg("err", $e->getMessage());
break;
}
if (!is_array($response)){
logMsg("danger", "Recieved malformed response from keycloak api");
logMsg("err", "Recieved malformed response from keycloak api");
break;
}
if (count($response) == 0) {
Expand Down Expand Up @@ -181,7 +182,7 @@ function logMsg($priority, $message, $task = "Keycloak Sync") {
}
}
if (!$mbox_template){
logMsg("warning", "No matching mapper found for mailbox_template");
logMsg("warning", "No matching attribute mapping found for user " . $user['email']);
continue;
}

Expand All @@ -191,14 +192,16 @@ function logMsg($priority, $message, $task = "Keycloak Sync") {
mailbox('add', 'mailbox_from_template', array(
'domain' => explode('@', $user['email'])[1],
'local_part' => explode('@', $user['email'])[0],
'name' => $user['firstName'] . " " . $user['lastName'],
'authsource' => 'keycloak',
'template' => $mbox_template
));
} else if ($row) {
} else if ($row && intval($iam_settings['periodic_sync']) == 1) {
// mailbox user does exist, sync attribtues...
logMsg("info", "Syncing attributes for user " . $user['email']);
mailbox('edit', 'mailbox_from_template', array(
'username' => $user['email'],
'name' => $user['firstName'] . " " . $user['lastName'],
'template' => $mbox_template
));
} else {
Expand Down
176 changes: 176 additions & 0 deletions data/conf/phpfpm/crons/ldap-sync.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<?php

require_once(__DIR__ . '/../web/inc/vars.inc.php');
if (file_exists(__DIR__ . '/../web/inc/vars.local.inc.php')) {
include_once(__DIR__ . '/../web/inc/vars.local.inc.php');
}
require_once __DIR__ . '/../web/inc/lib/vendor/autoload.php';

// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
logMsg("err", $e->getMessage());
session_destroy();
exit;
}

// 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) {
echo "Exiting: " . $e->getMessage();
session_destroy();
exit;
}

function logMsg($priority, $message, $task = "LDAP Sync") {
global $redis;

$finalMsg = array(
"time" => time(),
"priority" => $priority,
"task" => $task,
"message" => $message
);
$redis->lPush('CRON_LOG', json_encode($finalMsg));
}

// Load core functions first
require_once __DIR__ . '/../web/inc/functions.inc.php';
require_once __DIR__ . '/../web/inc/functions.auth.inc.php';
require_once __DIR__ . '/../web/inc/sessions.inc.php';
require_once __DIR__ . '/../web/inc/functions.mailbox.inc.php';
require_once __DIR__ . '/../web/inc/functions.ratelimit.inc.php';
require_once __DIR__ . '/../web/inc/functions.acl.inc.php';

$_SESSION['mailcow_cc_username'] = "admin";
$_SESSION['mailcow_cc_role'] = "admin";
$_SESSION['acl']['tls_policy'] = "1";
$_SESSION['acl']['quarantine_notification'] = "1";
$_SESSION['acl']['quarantine_category'] = "1";
$_SESSION['acl']['ratelimit'] = "1";
$_SESSION['acl']['sogo_access'] = "1";
$_SESSION['acl']['protocol_access'] = "1";
$_SESSION['acl']['mailbox_relayhost'] = "1";
$_SESSION['acl']['unlimited_quota'] = "1";

// Init Provider
$iam_provider = identity_provider('init');
$iam_settings = identity_provider('get');
if ($iam_settings['authsource'] != "ldap" || (intval($iam_settings['periodic_sync']) != 1 && intval($iam_settings['import_users']) != 1)) {
session_destroy();
exit;
}

// Set pagination variables
$start = 0;
$max = 25;

// lock sync if already running
$lock_file = '/tmp/iam-sync.lock';
if (file_exists($lock_file)) {
$lock_file_parts = explode("\n", file_get_contents($lock_file));
$pid = $lock_file_parts[0];
if (count($lock_file_parts) > 1){
$last_execution = $lock_file_parts[1];
$elapsed_time = (time() - $last_execution) / 60;
if ($elapsed_time < intval($iam_settings['sync_interval'])) {
logMsg("warning", "Sync not ready (".number_format((float)$elapsed_time, 2, '.', '')."min / ".$iam_settings['sync_interval']."min)");
session_destroy();
exit;
}
}

if (posix_kill($pid, 0)) {
logMsg("warning", "Sync is already running");
session_destroy();
exit;
} else {
unlink($lock_file);
}
}
$lock_file_handle = fopen($lock_file, 'w');
fwrite($lock_file_handle, getmypid());
fclose($lock_file_handle);

// Get ldap users
$response = $iam_provider->query()
->where($iam_settings['username_field'], "*")
->where($iam_settings['attribute_field'], "*")
->select([$iam_settings['username_field'], $iam_settings['attribute_field'], 'displayname'])
->paginate($max);

// Process the users
foreach ($response as $user) {
$mailcow_template = $user[$iam_settings['attribute_field']][0];

// try get mailbox user
$stmt = $pdo->prepare("SELECT `mailbox`.* FROM `mailbox`
INNER JOIN domain on mailbox.domain = domain.domain
WHERE `kind` NOT REGEXP 'location|thing|group'
AND `domain`.`active`='1'
AND `username` = :user");
$stmt->execute(array(':user' => $user[$iam_settings['username_field']][0]));
$row = $stmt->fetch(PDO::FETCH_ASSOC);

// check if matching attribute mapping exists
$mbox_template = null;
foreach ($iam_settings['mappers'] as $index => $mapper){
if ($mapper == $mailcow_template) {
$mbox_template = $iam_settings['templates'][$index];
break;
}
}
if (!$mbox_template){
logMsg("warning", "No matching attribute mapping found for user " . $user[$iam_settings['username_field']][0]);
continue;
}

if (!$row && intval($iam_settings['import_users']) == 1){
// mailbox user does not exist, create...
logMsg("info", "Creating user " . $user[$iam_settings['username_field']][0]);
mailbox('add', 'mailbox_from_template', array(
'domain' => explode('@', $user[$iam_settings['username_field']][0])[1],
'local_part' => explode('@', $user[$iam_settings['username_field']][0])[0],
'name' => $user['displayname'][0],
'authsource' => 'ldap',
'template' => $mbox_template
));
} else if ($row && intval($iam_settings['periodic_sync']) == 1) {
// mailbox user does exist, sync attribtues...
logMsg("info", "Syncing attributes for user " . $user[$iam_settings['username_field']][0]);
mailbox('edit', 'mailbox_from_template', array(
'username' => $user[$iam_settings['username_field']][0],
'name' => $user['displayname'][0],
'template' => $mbox_template
));
} else {
// skip mailbox user
logMsg("info", "Skipping user " . $user[$iam_settings['username_field']][0]);
}

sleep(0.025);
}

logMsg("info", "DONE!");
// add last execution time to lock file
$lock_file_handle = fopen($lock_file, 'w');
fwrite($lock_file_handle, getmypid() . "\n" . time());
fclose($lock_file_handle);
session_destroy();
47 changes: 47 additions & 0 deletions data/conf/sogo/custom-sogo.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,54 @@
// redirect to mailcow login form
document.addEventListener('DOMContentLoaded', function () {
var loginForm = document.forms.namedItem("loginForm");
if (loginForm) {
window.location.href = '/';
}

angularReady = false;
// Wait for the Angular components to be initialized
function waitForAngularComponents(callback) {
const targetNode = document.body;

const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length > 0) {
const toolbarElement = document.body.querySelector('md-toolbar');
if (toolbarElement) {
observer.disconnect();
callback();
}
}
});
});

const config = { childList: true, subtree: true };
observer.observe(targetNode, config);
}

// Usage
waitForAngularComponents(function() {
if (!angularReady){
angularReady = true;

const toolbarElement = document.body.querySelector('.md-toolbar-tools.sg-toolbar-group-last.layout-align-end-center.layout-row');

var htmlCode = '<a class="md-icon-button md-button md-ink-ripple" aria-label="mailcow" href="/user" aria-hidden="false" tabindex="-1">' +
'<md-icon class="material-icons" role="img" aria-label="build">build</md-icon>' +
'</a><a class="md-icon-button md-button md-ink-ripple" aria-label="mailcow" href="#" onclick="logout.submit()" aria-hidden="false" tabindex="-1">' +
'<md-icon class="material-icons" role="img" aria-label="settings_power">settings_power</md-icon>' +
'</a><form action="/" method="post" id="logout"><input type="hidden" name="logout"></form>';

toolbarElement.insertAdjacentHTML('beforeend', htmlCode);
}
});
});

// Custom SOGo JS

// Change the visible font-size in the editor, this does not change the font of a html message by default
CKEDITOR.addCss("body {font-size: 16px !important}");

// Enable scayt by default
//CKEDITOR.config.scayt_autoStartup = true;

Loading

0 comments on commit a06c783

Please sign in to comment.