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

[Web] manage keycloak identity provider

This commit is contained in:
FreddleSpl0it 2023-03-12 19:08:09 +01:00 committed by DerLinkman
parent 6adad79e5c
commit f6869da3a0
No known key found for this signature in database
GPG Key ID: F109FD97469550A2
7 changed files with 167 additions and 0 deletions

View File

@ -86,6 +86,8 @@ $cors_settings['allowed_origins'] = str_replace(", ", "\n", $cors_settings['allo
$cors_settings['allowed_methods'] = explode(", ", $cors_settings['allowed_methods']);
$f2b_data = fail2ban('get');
// identity provider
$identity_provider_settings = identity_provider('get');
$template = 'admin.twig';
$template_data = [
@ -117,6 +119,7 @@ $template_data = [
'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'],
'cors_settings' => $cors_settings,
'is_https' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on',
'identity_provider_settings' => $identity_provider_settings,
'lang_admin' => json_encode($lang['admin']),
'lang_datatables' => json_encode($lang['datatables'])
];

View File

@ -2067,6 +2067,74 @@ function uuid4() {
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
function identity_provider($_action, $_data = null) {
global $pdo;
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
switch ($_action) {
case 'get':
$settings = array();
$stmt = $pdo->prepare("SELECT * FROM `identity_provider`;");
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach($rows as $row){
$settings[$row["key"]] = $row["value"];
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $settings),
'msg' => 'admin_api_modified'
);
return $settings;
case 'edit':
$required_settings = array('server_url', 'authsource', 'realm', 'client_id', 'client_secret', 'redirect_url', 'version');
foreach($required_settings as $setting){
if (!$_data[$setting]){
return false;
}
}
try {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => '2'
);
$stmt = $pdo->prepare("INSERT INTO identity_provider (`key`, `value`) VALUES (:key, :value) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);");
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => '3'
);
} catch (Exception $e){
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $e->getMessage()),
'msg' => 'post'
);
return;
}
foreach($_data as $key => $value){
if (!in_array($key, $required_settings)){
continue;
}
$stmt->bindParam(':key', $key);
$stmt->bindParam(':value', $value);
$stmt->execute();
}
return true;
break;
}
}
function get_logs($application, $lines = false) {
if ($lines === false) {

View File

@ -568,6 +568,20 @@ function init_db_schema() {
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"identity_provider" => array(
"cols" => array(
"key" => "VARCHAR(255) NOT NULL",
"value" => "VARCHAR(255) NOT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP"
),
"keys" => array(
"primary" => array(
"" => array("key")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"logs" => array(
"cols" => array(
"id" => "INT NOT NULL AUTO_INCREMENT",

View File

@ -180,6 +180,24 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.auth.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
// Init Keycloak Provider
$identity_provider_settings = identity_provider('get');
$keycloak_provider = null;
if ($identity_provider_settings['server_url'] && $identity_provider_settings['realm'] && $identity_provider_settings['client_id'] &&
$identity_provider_settings['client_secret'] && $identity_provider_settings['redirect_url'] && $identity_provider_settings['version']){
$keycloak_provider = new Stevenmaguire\OAuth2\Client\Provider\Keycloak([
'authServerUrl' => $identity_provider_settings['server_url'],
'realm' => $identity_provider_settings['realm'],
'clientId' => $identity_provider_settings['client_id'],
'clientSecret' => $identity_provider_settings['client_secret'],
'redirectUri' => $identity_provider_settings['redirect_url'],
'version' => $identity_provider_settings['version'],
// 'encryptionAlgorithm' => 'RS256', // optional
// 'encryptionKeyPath' => '../key.pem' // optional
// 'encryptionKey' => 'contents_of_key_or_certificate' // optional
]);
}
// IMAP lib
// use Ddeboer\Imap\Server;
// $imap_server = new Server('dovecot', 143, '/imap/tls/novalidate-cert');

View File

@ -1710,6 +1710,8 @@ if (isset($_GET['query'])) {
if ($score)
$score = array("score" => preg_replace("/\s+/", "", $score));
process_get_return($score);
case "identity_provider":
process_get_return(identity_provider('get'));
break;
break;
// return no route found if no case is matched
@ -2082,6 +2084,8 @@ if (isset($_GET['query'])) {
break;
case "cors":
process_edit_return(cors('edit', $attr));
case "identity_provider":
process_edit_return(identity_provider('edit', $attr));
break;
// return no route found if no case is matched
default:

View File

@ -7,6 +7,7 @@
<a class="nav-link dropdown-toggle active" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false">{{ lang.admin.access }}</a>
<ul class="dropdown-menu">
<li><button class="dropdown-item active" data-bs-target="#tab-config-admins" aria-selected="false" aria-controls="tab-config-admins" role="tab" data-bs-toggle="tab">{{ lang.admin.admins }}</button></li>
<li><button class="dropdown-item" data-bs-target="#tab-config-identity-providers" aria-selected="false" aria-controls="tab-config-identity-providers" role="tab" data-bs-toggle="tab">Identity Providers</button></li>
<!-- <li><button class="dropdown-item" data-bs-target="#tab-config-ldap-admins" aria-controls="tab-config-ldap-admins" role="tab" data-bs-toggle="tab">{{ lang.admin.admins_ldap }}</button></li> -->
<li><button class="dropdown-item" data-bs-target="#tab-config-oauth2" aria-selected="false" aria-controls="tab-config-oauth2" role="tab" data-bs-toggle="tab">{{ lang.admin.oauth2_apps }}</button></li>
<li><button class="dropdown-item" data-bs-target="#tab-config-rspamd" aria-selected="false" aria-controls="tab-config-rspamd" role="tab" data-bs-toggle="tab">Rspamd UI</button></li>
@ -40,6 +41,7 @@
<div class="col-md-12">
<div class="tab-content" style="padding-top:20px">
{% include 'admin/tab-config-admins.twig' %}
{% include 'admin/tab-config-identity-providers.twig' %}
{# {% include 'admin/tab-ldap.twig' %} #}
{% include 'admin/tab-config-oauth2.twig' %}
{% include 'admin/tab-config-rspamd.twig' %}

View File

@ -0,0 +1,58 @@
<div role="tabpanel" class="tab-pane fade" id="tab-config-identity-providers" role="tabpanel" aria-labelledby="tab-config-identity-providers">
<div class="card mb-4">
<div class="card-header d-flex fs-5">
<button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-identity-providers" data-bs-toggle="collapse" aria-controls="collapse-tab-config-identity-providers">
{{ lang.admin.oauth2_apps }}
</button>
<span class="d-none d-md-block">{{ lang.admin.oauth2_apps }}</span>
</div>
<div id="collapse-tab-config-identity-providers" class="card-body collapse" data-bs-parent="#admin-content">
<form class="form-horizontal" autocapitalize="none" data-id="keycloak_sso" autocorrect="off" role="form" method="post">
<input type="hidden" name="authsource" value="keycloak">
<div class="row mb-2">
<label class="control-label col-sm-3 text-sm-end" for="keycloak_url">Server URL:</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="keycloak_url" name="server_url" value="{{ identity_provider_settings.server_url }}" required>
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3 text-sm-end" for="keycloak_realm">Realm:</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="keycloak_realm" name="realm" value="{{ identity_provider_settings.realm }}" required>
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3 text-sm-end" for="keycloak_client_id">Client Id:</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="keycloak_client_id" name="client_id" value="{{ identity_provider_settings.client_id }}" required>
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3 text-sm-end" for="keycloak_client_secret">Client Secret:</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="keycloak_client_secret" name="client_secret" value="{{ identity_provider_settings.client_secret }}" required>
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3 text-sm-end" for="keycloak_redirect_url">Redirect Url:</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="keycloak_redirect_url" name="redirect_url" value="{{ identity_provider_settings.redirect_url }}" required>
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-3 text-sm-end" for="keycloak_version">Keycloak Version:</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="keycloak_version" name="version" value="{{ identity_provider_settings.version }}" required>
</div>
</div>
<div class="row mt-4 mb-2">
<div class="offset-sm-3 col-sm-9">
<div class="btn-group">
<button class="btn btn-sm d-block d-sm-inline btn-success" data-item="keycloak_sso" data-action="edit_selected" data-id="keycloak_sso" data-api-url='edit/identity_provider' data-api-attr='{}' href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>