Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 75 additions & 54 deletions data/web/inc/functions.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -2436,24 +2436,29 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
case "templates":
$settings[$row["key"]] = json_decode($row["value"]);
break;
case "use_ssl":
case "use_tls":
case "login_provisioning":
case "ignore_ssl_errors":
$settings[$row["key"]] = boolval($row["value"]);
break;
case "use_ssl":
case "use_tls":
case "login_provisioning":
case "use_pkce":
case "ignore_ssl_errors":
$settings[$row["key"]] = boolval($row["value"]);
break;
default:
$settings[$row["key"]] = $row["value"];
break;
}
}
// set login_provisioning if not exists
if (!array_key_exists('login_provisioning', $settings)) {
$settings['login_provisioning'] = 1;
}
// return default client_scopes for generic-oidc if none is set
if ($settings["authsource"] == "generic-oidc" && empty($settings["client_scopes"])){
$settings["client_scopes"] = "openid profile email mailcow_template";
// set login_provisioning if not exists
if (!array_key_exists('login_provisioning', $settings)) {
$settings['login_provisioning'] = 1;
}
// set use_pkce if not exists
if (!array_key_exists('use_pkce', $settings)) {
$settings['use_pkce'] = 0;
}
// return default client_scopes for generic-oidc if none is set
if ($settings["authsource"] == "generic-oidc" && empty($settings["client_scopes"])){
$settings["client_scopes"] = "openid profile email mailcow_template";
}
if ($_extra['hide_sensitive']){
$settings['client_secret'] = '';
Expand Down Expand Up @@ -2527,13 +2532,14 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
$_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', 'ignore_ssl_error', 'login_provisioning');
break;
case "generic-oidc":
$_data['authorize_url'] = (!empty($_data['authorize_url'])) ? $_data['authorize_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['client_scopes'] = (!empty($_data['client_scopes'])) ? $_data['client_scopes'] : "openid profile email mailcow_template";
$required_settings = array('authsource', 'authorize_url', 'token_url', 'client_id', 'client_secret', 'redirect_url', 'userinfo_url', 'client_scopes', 'ignore_ssl_error', 'login_provisioning');
break;
case "generic-oidc":
$_data['authorize_url'] = (!empty($_data['authorize_url'])) ? $_data['authorize_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['client_scopes'] = (!empty($_data['client_scopes'])) ? $_data['client_scopes'] : "openid profile email mailcow_template";
$_data['use_pkce'] = isset($_data['use_pkce']) ? boolval($_data['use_pkce']) : false;
$required_settings = array('authsource', 'authorize_url', 'token_url', 'client_id', 'client_secret', 'redirect_url', 'userinfo_url', 'client_scopes', 'use_pkce', 'ignore_ssl_error', 'login_provisioning');
break;
case "ldap":
$_data['host'] = (!empty($_data['host'])) ? str_replace(" ", "", $_data['host']) : "";
$_data['port'] = (!empty($_data['port'])) ? intval($_data['port']) : 389;
Expand Down Expand Up @@ -2779,17 +2785,18 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
\GuzzleHttp\RequestOptions::VERIFY => !$settings['ignore_ssl_error'],
]
);
$provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => $settings['client_id'],
'clientSecret' => $settings['client_secret'],
'redirectUri' => $settings['redirect_url'],
'urlAuthorize' => $settings['authorize_url'],
'urlAccessToken' => $settings['token_url'],
'urlResourceOwnerDetails' => $settings['userinfo_url'],
'scopes' => $settings['client_scopes']
]);
$provider->setHttpClient($guzzyClient);
}
$provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => $settings['client_id'],
'clientSecret' => $settings['client_secret'],
'redirectUri' => $settings['redirect_url'],
'urlAuthorize' => $settings['authorize_url'],
'urlAccessToken' => $settings['token_url'],
'urlResourceOwnerDetails' => $settings['userinfo_url'],
'scopes' => $settings['client_scopes'],
'pkceMethod' => !empty($settings['use_pkce']) ? \League\OAuth2\Client\Provider\AbstractProvider::PKCE_METHOD_S256 : null
]);
$provider->setHttpClient($guzzyClient);
}
break;
case "ldap":
if ($settings['host'] && $settings['port'] && $settings['basedn'] &&
Expand Down Expand Up @@ -2826,24 +2833,33 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
'msg' => 'login_failed'
);
return false;
}

try {
$token = $iam_provider->getAccessToken('authorization_code', ['code' => $_GET['code']]);
$plain_token = $token->getToken();
$plain_refreshtoken = $token->getRefreshToken();
$info = $iam_provider->getResourceOwner($token)->toArray();
} catch (Throwable $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $e->getMessage()),
'msg' => 'login_failed'
);
return false;
}
// check if email address is given
if (empty($info['email'])) {
$_SESSION['return'][] = array(
}

try {
if ($iam_settings['authsource'] == 'generic-oidc' && !empty($iam_settings['use_pkce'])) {
$pkceCode = $_SESSION['oauth2pkcecode'] ?? null;
if (empty($pkceCode)) {
throw new \RuntimeException('Missing PKCE verifier');
}
$iam_provider->setPkceCode($pkceCode);
}
$token = $iam_provider->getAccessToken('authorization_code', ['code' => $_GET['code']]);
$plain_token = $token->getToken();
$plain_refreshtoken = $token->getRefreshToken();
$info = $iam_provider->getResourceOwner($token)->toArray();
} catch (Throwable $e) {
unset($_SESSION['oauth2pkcecode']);
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $e->getMessage()),
'msg' => 'login_failed'
);
return false;
}
unset($_SESSION['oauth2pkcecode']);
// check if email address is given
if (empty($info['email'])) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, 'No email address found for user'),
'msg' => 'login_failed'
Expand Down Expand Up @@ -3036,11 +3052,16 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
break;
}
}
}
$authUrl = $iam_provider->getAuthorizationUrl($options);
$_SESSION['oauth2state'] = $iam_provider->getState();
return $authUrl;
break;
}
$authUrl = $iam_provider->getAuthorizationUrl($options);
$_SESSION['oauth2state'] = $iam_provider->getState();
if ($iam_settings['authsource'] == 'generic-oidc' && !empty($iam_settings['use_pkce'])) {
$_SESSION['oauth2pkcecode'] = $iam_provider->getPkceCode();
} else {
unset($_SESSION['oauth2pkcecode']);
}
return $authUrl;
break;
case "get-keycloak-admin-token":
// get access_token for service account of mailcow client
if ($iam_settings['authsource'] !== 'keycloak') return false;
Expand Down
2 changes: 2 additions & 0 deletions data/web/lang/lang.de-de.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@
"iam_token_url": "Token Endpunkt",
"iam_userinfo_url": "User info Endpunkt",
"iam_username_field": "Username Feld",
"iam_use_pkce": "PKCE verwenden",
"iam_use_pkce_info": "Optional für Generic OIDC. Aktiviere dies, wenn dein Provider PKCE für den Authorization Code Flow unterstützt.",
"iam_binddn": "Bind DN",
"iam_use_ssl": "Benutze SSL",
"iam_use_ssl_info": "Wenn SSL aktiviert ist und der Port auf 389 gesetzt wurde, wird dieser automatisch auf 636 geändert.",
Expand Down
2 changes: 2 additions & 0 deletions data/web/lang/lang.en-gb.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@
"iam_token_url": "Token endpoint",
"iam_userinfo_url": "User info endpoint",
"iam_username_field": "Username Field",
"iam_use_pkce": "Use PKCE",
"iam_use_pkce_info": "Optional for Generic OIDC. Enable this if your provider supports PKCE for the Authorization Code flow.",
"iam_binddn": "Bind DN",
"iam_use_ssl": "Use SSL",
"iam_use_ssl_info": "If enabling SSL, and port is set to 389, it will be automatically overridden to use 636.",
Expand Down
11 changes: 11 additions & 0 deletions data/web/templates/admin/tab-config-identity-provider.twig
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,17 @@
</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_use_pkce }}</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="use_pkce" value="1" {% if iam_settings.use_pkce == 1 %}checked{% endif %}>
</div>
<small class="text-muted">{{ lang.admin.iam_use_pkce_info }}</small>
</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.ignore_ssl_error }}</label>
Expand Down