From 1c35002505f4eeaa32329fbed42a123ede85cf4b Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 2 Oct 2019 19:00:36 +0200 Subject: [PATCH] [Web] Do not allow to add domain admin for non existing domain [Web] oAuth2 implementation (wip) --- data/web/admin.php | 34 ++++- data/web/edit.php | 47 ++++++ data/web/inc/footer.inc.php | 4 +- data/web/inc/functions.customize.inc.php | 10 +- data/web/inc/functions.domain_admin.inc.php | 46 +++--- data/web/inc/functions.mailbox.inc.php | 12 ++ data/web/inc/functions.oauth2.inc.php | 152 ++++++++++++-------- data/web/inc/init_db.inc.php | 13 +- data/web/inc/prerequisites.inc.php | 8 +- data/web/inc/vars.inc.php | 4 + data/web/index.php | 8 +- data/web/js/site/admin.js | 38 +++++ data/web/json_api.php | 6 + data/web/lang/lang.de.php | 42 ++++-- data/web/lang/lang.en.php | 47 ++++-- data/web/lang/lang.nl.php | 2 +- data/web/modals/admin.php | 26 ++++ data/web/oauth/authorize.php | 66 +++++++++ data/web/oauth/profile.php | 28 ++++ data/web/oauth/token.php | 4 + 20 files changed, 478 insertions(+), 119 deletions(-) create mode 100644 data/web/oauth/authorize.php create mode 100644 data/web/oauth/profile.php create mode 100644 data/web/oauth/token.php diff --git a/data/web/admin.php b/data/web/admin.php index 155c6fc1..d78f3621 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -177,6 +177,30 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC +
+
OAuth2 Apps
+
+

+
+
+
+
+
+ + + + Add OAuth2 client +
+
+
+
+

Rspamd UI

@@ -950,10 +974,8 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC

- - - -
+ +
@@ -975,8 +997,8 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
- - + +
diff --git a/data/web/edit.php b/data/web/edit.php index acbd8841..10896077 100644 --- a/data/web/edit.php +++ b/data/web/edit.php @@ -432,6 +432,53 @@ if (isset($_SESSION['mailcow_cc_role'])) { +

OAuth2

+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ + +

- +
set('TITLE_NAME', htmlspecialchars($title_name)); $redis->set('MAIN_NAME', htmlspecialchars($main_name)); $redis->set('APPS_NAME', htmlspecialchars($apps_name)); $redis->set('HELP_TEXT', $help_text); - $redis->set('UI_IMPRESS', $ui_impress); + $redis->set('UI_FOOTER', $ui_footer); } catch (RedisException $e) { $_SESSION['return'][] = array( @@ -203,7 +203,11 @@ function customize($_action, $_item, $_data = null) { $data['main_name'] = ($main_name = $redis->get('MAIN_NAME')) ? $main_name : 'mailcow UI'; $data['apps_name'] = ($apps_name = $redis->get('APPS_NAME')) ? $apps_name : 'mailcow Apps'; $data['help_text'] = ($help_text = $redis->get('HELP_TEXT')) ? $help_text : false; - $data['ui_impress'] = ($ui_impress = $redis->get('UI_IMPRESS')) ? $ui_impress : false; + if (!empty($redis->get('UI_IMPRESS'))) { + $redis->set('UI_FOOTER', $redis->get('UI_IMPRESS')); + $redis->del('UI_IMPRESS'); + } + $data['ui_footer'] = ($ui_footer = $redis->get('UI_FOOTER')) ? $ui_footer : false; return $data; } catch (RedisException $e) { diff --git a/data/web/inc/functions.domain_admin.inc.php b/data/web/inc/functions.domain_admin.inc.php index 064ba6c8..9c29b561 100644 --- a/data/web/inc/functions.domain_admin.inc.php +++ b/data/web/inc/functions.domain_admin.inc.php @@ -83,15 +83,17 @@ function domain_admin($_action, $_data = null) { return false; } $password_hashed = hash_password($password); + $valid_domains = 0; foreach ($domains as $domain) { - if (!is_valid_domain_name($domain)) { + if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) { $_SESSION['return'][] = array( 'type' => 'danger', 'log' => array(__FUNCTION__, $_action, $_data_log), - 'msg' => 'domain_invalid' + 'msg' => array('domain_invalid', htmlspecialchars($domain)) ); - return false; + continue; } + $valid_domains++; $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`) VALUES (:username, :domain, :created, :active)"); $stmt->execute(array( @@ -101,13 +103,15 @@ function domain_admin($_action, $_data = null) { ':active' => $active )); } - $stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`) - VALUES (:username, :password_hashed, '0', :active)"); - $stmt->execute(array( - ':username' => $username, - ':password_hashed' => $password_hashed, - ':active' => $active - )); + if ($valid_domains != 0) { + $stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`) + VALUES (:username, :password_hashed, '0', :active)"); + $stmt->execute(array( + ':username' => $username, + ':password_hashed' => $password_hashed, + ':active' => $active + )); + } } else { $_SESSION['return'][] = array( @@ -117,15 +121,17 @@ function domain_admin($_action, $_data = null) { ); return false; } - $stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)"); - $stmt->execute(array( - ':username' => $username - )); - $_SESSION['return'][] = array( - 'type' => 'success', - 'log' => array(__FUNCTION__, $_action, $_data_log), - 'msg' => array('domain_admin_added', htmlspecialchars($username)) - ); + if ($valid_domains != 0) { + $stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)"); + $stmt->execute(array( + ':username' => $username + )); + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => array('domain_admin_added', htmlspecialchars($username)) + ); + } break; case 'edit': if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") { @@ -165,7 +171,7 @@ function domain_admin($_action, $_data = null) { $password2 = $_data['password2']; if (!empty($domains)) { foreach ($domains as $domain) { - if (!is_valid_domain_name($domain)) { + if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) { $_SESSION['return'][] = array( 'type' => 'danger', 'log' => array(__FUNCTION__, $_action, $_data_log), diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 99fda2d8..740fca3f 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -3806,6 +3806,18 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $stmt->execute(array( ':username' => $username )); + $stmt = $pdo->prepare("DELETE FROM `oauth_access_tokens` WHERE `user_id` = :username"); + $stmt->execute(array( + ':username' => $username + )); + $stmt = $pdo->prepare("DELETE FROM `oauth_refresh_tokens` WHERE `user_id` = :username"); + $stmt->execute(array( + ':username' => $username + )); + $stmt = $pdo->prepare("DELETE FROM `oauth_authorization_codes` WHERE `user_id` = :username"); + $stmt->execute(array( + ':username' => $username + )); $stmt = $pdo->prepare("SELECT `address`, `goto` FROM `alias` WHERE `goto` REGEXP :username"); $stmt->execute(array(':username' => '(^|,)'.$username.'($|,)')); diff --git a/data/web/inc/functions.oauth2.inc.php b/data/web/inc/functions.oauth2.inc.php index fdc908b3..7bc7dea6 100644 --- a/data/web/inc/functions.oauth2.inc.php +++ b/data/web/inc/functions.oauth2.inc.php @@ -4,9 +4,10 @@ function oauth2($_action, $_type, $_data = null) { global $redis; global $lang; if ($_SESSION['mailcow_cc_role'] != "admin") { - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) + 'log' => array(__FUNCTION__, $_action, $_type, $_data), + 'msg' => 'access_denied' ); return false; } @@ -14,30 +15,26 @@ function oauth2($_action, $_type, $_data = null) { case 'add': switch ($_type) { case 'client': - $client_id = $_data['client_id']; - $client_secret = $_data['client_secret']; + $client_id = bin2hex(random_bytes(6)); + $client_secret = bin2hex(random_bytes(12)); $redirect_uri = $_data['redirect_uri']; + $scope = 'profile'; + // For future use // $grant_type = isset($_data['grant_type']) ? $_data['grant_type'] : 'authorization_code'; // $scope = isset($_data['scope']) ? $_data['scope'] : 'profile'; - if ($grant_type != "authorization_code" && $grant_type != "password") { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) - ); - return false; - } - // For future use + // if ($grant_type != "authorization_code" && $grant_type != "password") { + // $_SESSION['return'][] = array( + // 'type' => 'danger', + // 'log' => array(__FUNCTION__, $_action, $_type, $_data), + // 'msg' => 'access_denied' + // ); + // return false; + // } if ($scope != "profile") { - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) - ); - return false; - } - if (!ctype_alnum($client_id) || !ctype_alnum($client_secret)) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) + 'log' => array(__FUNCTION__, $_action, $_type, $_data), + 'msg' => 'Invalid scope' ); return false; } @@ -46,21 +43,24 @@ function oauth2($_action, $_type, $_data = null) { $stmt->execute(array(':client_id' => $client_id)); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); if ($num_results != 0) { - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_type, $_data), 'msg' => 'Client ID exists' ); return false; } - $stmt = $pdo->prepare("INSERT INTO `oauth_clients` (`client_id`, `client_secret` ,`redirect_uri`) - VALUES (:client_id, :client_secret, :redirect_uri)"); + $stmt = $pdo->prepare("INSERT INTO `oauth_clients` (`client_id`, `client_secret`, `redirect_uri`, `scope`) + VALUES (:client_id, :client_secret, :redirect_uri, :scope)"); $stmt->execute(array( ':client_id' => $client_id, ':client_secret' => $client_secret, - ':redirect_uri' => $redirect_uri + ':redirect_uri' => $redirect_uri, + ':scope' => $scope )); - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_type, $_data), 'msg' => 'Added client access' ); break; @@ -73,47 +73,73 @@ function oauth2($_action, $_type, $_data = null) { foreach ($ids as $id) { $is_now = oauth2('details', 'client', $id); if (!empty($is_now)) { - $client_id = (!empty($_data['client_id'])) ? $_data['client_id'] : $is_now['client_id']; - $client_secret = (!empty($_data['client_secret'])) ? $_data['client_secret'] : $is_now['client_secret']; - $redirect_uri = (!empty($_data['redirect_uri'])) ? $_data['redirect_uri'] : $is_now['redirect_uri']; + $redirect_uri = (!empty($_data['redirect_uri'])) ? $_data['redirect_uri'] : $is_now['redirect_uri']; } else { - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) + 'log' => array(__FUNCTION__, $_action, $_type, $_data), + 'msg' => 'access_denied' ); return false; } - if (!ctype_alnum($client_id) || !ctype_alnum($client_secret)) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => 'Client ID and secret must be alphanumeric' + if (isset($_data['revoke_tokens'])) { + $stmt = $pdo->prepare("DELETE FROM `oauth_access_tokens` + WHERE `client_id` IN ( + SELECT `client_id` FROM `oauth_clients` WHERE `id` = :id + )"); + $stmt->execute(array( + ':id' => $id + )); + $stmt = $pdo->prepare("DELETE FROM `oauth_refresh_tokens` + WHERE `client_id` IN ( + SELECT `client_id` FROM `oauth_clients` WHERE `id` = :id + )"); + $stmt->execute(array( + ':id' => $id + )); + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_type, $_data), + 'msg' => array('object_modified', htmlspecialchars($id)) ); - return false; + continue; + } + if (isset($_data['renew_secret'])) { + $client_secret = bin2hex(random_bytes(12)); + $stmt = $pdo->prepare("UPDATE `oauth_clients` SET `client_secret` = :client_secret WHERE `id` = :id"); + $stmt->execute(array( + ':client_secret' => $client_secret, + ':id' => $id + )); + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_type, $_data), + 'msg' => array('object_modified', htmlspecialchars($id)) + ); + continue; } if (empty($redirect_uri)) { - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_type, $_data), 'msg' => 'Redirect/Callback URL cannot be empty' ); - return false; + continue; } $stmt = $pdo->prepare("UPDATE `oauth_clients` SET - `client_id` = :client_id, - `client_secret` = :client_secret, `redirect_uri` = :redirect_uri WHERE `id` = :id"); $stmt->execute(array( ':id' => $id, - ':client_id' => $client_id, - ':client_secret' => $client_secret, ':redirect_uri' => $redirect_uri )); + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_type, $_data), + 'msg' => array('object_modified', htmlspecialchars($id)) + ); } - $_SESSION['return'] = array( - 'type' => 'success', - 'msg' => sprintf($lang['success']['object_modified'], htmlspecialchars(implode(', ', $ids))) - ); break; } break; @@ -123,39 +149,45 @@ function oauth2($_action, $_type, $_data = null) { (array)$ids = $_data['id']; foreach ($ids as $id) { if (!is_numeric($id)) { - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) + 'log' => array(__FUNCTION__, $_action, $_type, $_data), + 'msg' => 'access_denied' ); - return false; + continue; } - $stmt = $pdo->prepare("DELETE FROM `oauth_clients` WHERE `id` = :id"); + $stmt = $pdo->prepare("DELETE FROM `oauth_clients` + WHERE `id` = :id"); $stmt->execute(array( ':id' => $id )); } - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'success', - 'msg' => sprintf($lang['success']['items_deleted'], implode(', ', $ids)) + 'log' => array(__FUNCTION__, $_action, $_type, $_data), + 'msg' => array('items_deleted', htmlspecialchars($id)) ); break; case 'access_token': (array)$access_tokens = $_data['access_token']; foreach ($access_tokens as $access_token) { if (!ctype_alnum($access_token)) { - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) + 'log' => array(__FUNCTION__, $_action, $_type, $_data), + 'msg' => 'access_denied' ); return false; } - $stmt = $pdo->prepare("DELETE FROM `oauth_access_tokens` WHERE `access_token` = :access_token"); + $stmt = $pdo->prepare("DELETE FROM `oauth_access_tokens` + WHERE `access_token` = :access_token"); $stmt->execute(array( ':access_token' => $access_token )); } - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_type, $_data), 'msg' => sprintf($lang['success']['items_deleted'], implode(', ', $access_tokens)) ); break; @@ -163,9 +195,10 @@ function oauth2($_action, $_type, $_data = null) { (array)$refresh_tokens = $_data['refresh_token']; foreach ($refresh_tokens as $refresh_token) { if (!ctype_alnum($refresh_token)) { - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'danger', - 'msg' => sprintf($lang['danger']['access_denied']) + 'log' => array(__FUNCTION__, $_action, $_type, $_data), + 'msg' => 'access_denied' ); return false; } @@ -174,8 +207,9 @@ function oauth2($_action, $_type, $_data = null) { ':refresh_token' => $refresh_token )); } - $_SESSION['return'] = array( + $_SESSION['return'][] = array( 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_type, $_data), 'msg' => sprintf($lang['success']['items_deleted'], implode(', ', $refresh_tokens)) ); break; diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php index c97eeb56..e1daed63 100644 --- a/data/web/inc/init_db.inc.php +++ b/data/web/inc/init_db.inc.php @@ -3,7 +3,7 @@ function init_db_schema() { try { global $pdo; - $db_version = "27092019_1040"; + $db_version = "29092019_1040"; $stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); @@ -1055,6 +1055,17 @@ BEGIN DELETE FROM spamalias WHERE validity < UNIX_TIMESTAMP(); END; // +DELIMITER ;'; + $events[] = 'DROP EVENT IF EXISTS clean_oauth2; +DELIMITER // +CREATE EVENT clean_oauth2 +ON SCHEDULE EVERY 1 DAY DO +BEGIN + DELETE FROM oauth_refresh_tokens WHERE expires < NOW(); + DELETE FROM oauth_access_tokens WHERE expires < NOW(); + DELETE FROM oauth_authorization_codes WHERE expires < NOW(); +END; +// DELIMITER ;'; foreach ($events as $event) { $pdo->exec($event); diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index 35688315..e0cc5993 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -105,13 +105,15 @@ class mailcowPdo extends OAuth2\Storage\Pdo { $oauth2_scope_storage = new OAuth2\Storage\Memory(array('default_scope' => 'profile', 'supported_scopes' => array('profile'))); $oauth2_storage = new mailcowPdo(array('dsn' => $dsn, 'username' => $database_user, 'password' => $database_pass)); $oauth2_server = new OAuth2\Server($oauth2_storage, array( - 'always_issue_new_refresh_token' => true, - 'refresh_token_lifetime' => 2678400, + 'refresh_token_lifetime' => $REFRESH_TOKEN_LIFETIME, + 'access_lifetime' => $ACCESS_TOKEN_LIFETIME, )); $oauth2_server->setScopeUtil(new OAuth2\Scope($oauth2_scope_storage)); $oauth2_server->addGrantType(new OAuth2\GrantType\AuthorizationCode($oauth2_storage)); $oauth2_server->addGrantType(new OAuth2\GrantType\UserCredentials($oauth2_storage)); -$oauth2_server->addGrantType(new OAuth2\GrantType\RefreshToken($oauth2_storage)); +$oauth2_server->addGrantType(new OAuth2\GrantType\RefreshToken($oauth2_storage, array( + 'always_issue_new_refresh_token' => true +))); function exception_handler($e) { if ($e instanceof PDOException) { diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php index 90b7e377..eceee8ae 100644 --- a/data/web/inc/vars.inc.php +++ b/data/web/inc/vars.inc.php @@ -129,6 +129,10 @@ $DOCKER_TIMEOUT = 60; // Anonymize IPs logged via UI $ANONYMIZE_IPS = true; +// OAuth2 settings +$REFRESH_TOKEN_LIFETIME = 2678400; +$ACCESS_TOKEN_LIFETIME = 86400; + // MAILBOX_DEFAULT_ATTRIBUTES define default attributes for new mailboxes // These settings will not change existing mailboxes diff --git a/data/web/index.php b/data/web/index.php index 19372351..ffaec3c1 100644 --- a/data/web/index.php +++ b/data/web/index.php @@ -1,7 +1,13 @@ '; }); + } else if (table == 'oauth2clientstable') { + $.each(data, function (i, item) { + item.action = ''; + item.scope = "profile"; + item.grant_types = 'refresh_token password authorization_code'; + item.chkbox = ''; + }); } else if (table == 'domainadminstable') { $.each(data, function (i, item) { item.selected_domains = escapeHtml(item.selected_domains); @@ -299,6 +336,7 @@ jQuery(function($){ draw_admins(); draw_fwd_hosts(); draw_relayhosts(); + draw_oauth2_clients(); draw_transport_maps(); draw_queue(); // Relayhost diff --git a/data/web/json_api.php b/data/web/json_api.php index fea4565b..ea4304af 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -142,6 +142,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u case "mailbox": process_add_return(mailbox('add', 'mailbox', $attr)); break; + case "oauth2-client": + process_add_return(oauth2('add', 'client', $attr)); + break; case "domain": process_add_return(mailbox('add', 'domain', $attr)); break; @@ -1056,6 +1059,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u case "alias": process_delete_return(mailbox('delete', 'alias', array('id' => $items))); break; + case "oauth2-client": + process_delete_return(oauth2('delete', 'client', array('id' => $items))); + break; case "relayhost": process_delete_return(relayhost('delete', array('id' => $items))); break; diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php index 22c07caa..ef1b682e 100644 --- a/data/web/lang/lang.de.php +++ b/data/web/lang/lang.de.php @@ -711,7 +711,39 @@ $lang['admin']['help_text'] = "Hilfstext unter Login-Maske (HTML zulässig)"; $lang['admin']['title_name'] = '"mailcow UI" Webseiten Titel'; $lang['admin']['main_name'] = '"mailcow UI" Name'; $lang['admin']['apps_name'] = '"mailcow Apps" Name'; -$lang['admin']['ui_impress'] = 'Impressum, Footer (HTML zulässig)'; +$lang['admin']['ui_footer'] = 'Footer (HTML zulässig)'; + +$lang['admin']['oauth2_info'] = 'Die OAuth2 Implementierung untersützt den Grant Type "Authorization Code" mit Refresh Tokens.
+Der Server wird automatisch einen neuen Refresh Token ausstellen, sobald ein vorheriger Token gegen einen Access Token eingetauscht wurde.

+→ Der Standard Scope lautet profile. Nur Mailbox-Benutzer können sich gegen OAuth2 authentifizieren. Wird kein Scope angegeben, verwendet das System per Standard profile.
+→ Der state Parameter wird im Zuge des Autorisierungsprozesses benötigt.

+Die Pfade für die OAuth2 API lauten wie folgt:
+
    +
  • Authorization Endpoint: /oauth/authorize
  • +
  • Token Endpoint: /oauth/token
  • +
  • Resource Page: /oauth/profile
  • +
+Die Regenerierung des Client Secrets wird vorhandene Authorization Codes nicht invalidieren, dennoch wird der Renew des Access Tokens durch einen Refresh Token nicht mehr gelingen.

+Das Entfernen aller Client Tokens verursacht die umgehende Terminierung aller aktiven OAuth2 Sessions. Clients müssen sich erneut gegen die OAuth2 Anwendung authentifizieren.'; + +$lang['admin']['oauth2_client_id'] = "Client ID"; +$lang['admin']['oauth2_client_secret'] = "Client Secret"; +$lang['admin']['oauth2_redirect_uri'] = "Redirect URI"; +$lang['admin']['oauth2_revoke_tokens'] = 'Alle Client Tokens entfernen'; +$lang['admin']['oauth2_renew_secret'] = 'Neues Client Secret generieren'; +$lang['edit']['client_id'] = 'Client ID'; +$lang['edit']['client_secret'] = 'Client Secret'; +$lang['edit']['scope'] = 'Scope'; +$lang['edit']['grant_types'] = 'Grant types'; +$lang['edit']['redirect_uri'] = 'Redirect/Callback URL'; +$lang['oauth2']['scope_ask_permission'] = 'Eine Anwendung hat um die folgenden Berechtigungen gebeten'; +$lang['oauth2']['profile'] = 'Profil'; +$lang['oauth2']['profile_desc'] = 'Persönliche Informationen anzeigen: Benutzername, Name, Erstellzeitpunkt, Änderungszeitpunkt, Status'; +$lang['oauth2']['permit'] = 'Anwendung authorisieren'; +$lang['oauth2']['authorize_app'] = 'Anwendung authorisieren'; +$lang['oauth2']['deny'] = 'Ablehnen'; +$lang['oauth2']['access_denied'] = 'Bitte als Mailbox-Nutzer einloggen, um den Zugriff via OAuth2 zu erlauben.'; + $lang['admin']['customize'] = "UI Anpassung"; $lang['admin']['change_logo'] = "Logo ändern"; @@ -836,14 +868,6 @@ $lang['mailbox']['add_tls_policy_map'] = "TLS-Richtlinieneintrag hinzufügen"; $lang['danger']['tls_policy_map_parameter_invalid'] = "Parameter ist ungültig"; $lang['danger']['temp_error'] = "Temporärer Fehler"; -$lang['oauth2']['scope_ask_permission'] = 'Eine Anwendung hat um die folgenden Berechtigungen gebeten'; -$lang['oauth2']['profile'] = 'Profil'; -$lang['oauth2']['profile_desc'] = 'Persönliche Informationen anzeigen: Benutzername, Name, Erstellzeitpunkt, Änderungszeitpunkt, Status'; -$lang['oauth2']['permit'] = 'Anwendung authorisieren'; -$lang['oauth2']['authorize_app'] = 'Anwendung authorisieren'; -$lang['oauth2']['deny'] = 'Ablehnen'; -$lang['oauth2']['access_denied'] = 'Bitte als Mailbox-Nutzer einloggen, um den Zugriff via OAuth2 zu erlauben.'; - $lang['admin']['sys_mails'] = 'System-E-Mails'; $lang['admin']['subject'] = 'Betreff'; $lang['admin']['from'] = 'Absender'; diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php index 795e531c..de5aaaef 100644 --- a/data/web/lang/lang.en.php +++ b/data/web/lang/lang.en.php @@ -346,11 +346,6 @@ $lang['mailbox']['sogo_visible'] = 'Alias is visible in SOGo'; $lang['mailbox']['sogo_visible_y'] = 'Show alias in SOGo'; $lang['mailbox']['sogo_visible_n'] = 'Hide alias in SOGo'; $lang['edit']['syncjob'] = 'Edit sync job'; -$lang['edit']['client_id'] = 'Client ID'; -$lang['edit']['client_secret'] = 'Client secret'; -$lang['edit']['scope'] = 'Scope'; -$lang['edit']['grant_types'] = 'Grant types'; -$lang['edit']['redirect_uri'] = 'Redirect/Callback URL'; $lang['edit']['hostname'] = 'Hostname'; $lang['edit']['encryption'] = 'Encryption'; $lang['edit']['maxage'] = 'Maximum age of messages in days that will be polled from remote
(0 = ignore age)'; @@ -679,6 +674,38 @@ $lang['admin']['credentials_transport_warning'] = 'Warning: Adding a new $lang['admin']['destination'] = 'Destination'; $lang['admin']['nexthop'] = 'Next hop'; +$lang['admin']['oauth2_info'] = 'The OAuth2 implementation supports the grant type "Authorization Code" and issues refresh tokens.
+The server also automatically issues new refresh tokens, after a refresh token has been used.

+→ The default scope is profile. Only mailbox users can be authenticated against OAuth2. If the scope parameter is omitted, it falls back to profile.
+→ The state parameter is required to be sent by the client as part of the authorize request.

+Pathes for requests to the OAuth2 API:
+
    +
  • Authorization endpoint: /oauth/authorize
  • +
  • Token endpoint: /oauth/token
  • +
  • Resource page: /oauth/profile
  • +
+Regenerating the client secret will not expire existing authorization codes, but they will fail to renew their token.

+Revoking client tokens will cause immediate termination of all active sessions. All clients need to re-authenticate.'; + +$lang['admin']['oauth2_client_id'] = "Client ID"; +$lang['admin']['oauth2_client_secret'] = "Client secret"; +$lang['admin']['oauth2_redirect_uri'] = "Redirect URI"; +$lang['admin']['oauth2_revoke_tokens'] = 'Revoke all client tokens'; +$lang['admin']['oauth2_renew_secret'] = 'Generate new client secret'; +$lang['edit']['client_id'] = 'Client ID'; +$lang['edit']['client_secret'] = 'Client secret'; +$lang['edit']['scope'] = 'Scope'; +$lang['edit']['grant_types'] = 'Grant types'; +$lang['edit']['redirect_uri'] = 'Redirect/Callback URL'; +$lang['oauth2']['scope_ask_permission'] = 'An application asked for the following permissions'; +$lang['oauth2']['profile'] = 'Profile'; +$lang['oauth2']['profile_desc'] = 'View personal information: username, full name, created, modified, active'; +$lang['oauth2']['permit'] = 'Authorize application'; +$lang['oauth2']['authorize_app'] = 'Authorize application'; +$lang['oauth2']['deny'] = 'Deny'; +$lang['oauth2']['access_denied'] = 'Please login as mailbox owner to grant access via OAuth2.'; + + $lang['success']['forwarding_host_removed'] = "Forwarding host %s has been removed"; $lang['success']['forwarding_host_added'] = "Forwarding host %s has been added"; $lang['success']['relayhost_removed'] = "Map entry %s has been removed"; @@ -727,7 +754,7 @@ $lang['admin']['help_text'] = "Override help text below login mask (HTML allowed $lang['admin']['title_name'] = '"mailcow UI" website title'; $lang['admin']['main_name'] = '"mailcow UI" name'; $lang['admin']['apps_name'] = '"mailcow Apps" name'; -$lang['admin']['ui_impress'] = 'Impress, Footer note (HTML allowed)'; +$lang['admin']['ui_footer'] = 'Footer (HTML allowed)'; $lang['admin']['customize'] = "Customize"; $lang['admin']['change_logo'] = "Change logo"; @@ -864,14 +891,6 @@ $lang['mailbox']['add_recipient_map_entry'] = 'Add recipient map'; $lang['danger']['tls_policy_map_parameter_invalid'] = "Policy parameter is invalid"; $lang['danger']['temp_error'] = "Temporary error"; -$lang['oauth2']['scope_ask_permission'] = 'An application asked for the following permissions'; -$lang['oauth2']['profile'] = 'Profile'; -$lang['oauth2']['profile_desc'] = 'View personal information: username, full name, created, modified, active'; -$lang['oauth2']['permit'] = 'Authorize application'; -$lang['oauth2']['authorize_app'] = 'Authorize application'; -$lang['oauth2']['deny'] = 'Deny'; -$lang['oauth2']['access_denied'] = 'Please login as mailbox owner to grant access via OAuth2.'; - $lang['admin']['sys_mails'] = 'System mails'; $lang['admin']['subject'] = 'Subject'; $lang['admin']['from'] = 'From'; diff --git a/data/web/lang/lang.nl.php b/data/web/lang/lang.nl.php index 85812380..b04e727a 100644 --- a/data/web/lang/lang.nl.php +++ b/data/web/lang/lang.nl.php @@ -710,7 +710,7 @@ $lang['admin']['help_text'] = "Hulpteksten onder inlogvenster (HTML toegestaan)" $lang['admin']['title_name'] = '"Mailcow" (website-titel)'; $lang['admin']['main_name'] = '"Mailcow"'; $lang['admin']['apps_name'] = '"Mailcow-apps"'; -$lang['admin']['ui_impress'] = 'Footer-vermelding (HTML toegestaan)'; +$lang['admin']['ui_footer'] = 'Footer-vermelding (HTML toegestaan)'; $lang['admin']['customize'] = "Uiterlijk"; $lang['admin']['change_logo'] = "Logo"; diff --git a/data/web/modals/admin.php b/data/web/modals/admin.php index 2303519e..ae23d63a 100644 --- a/data/web/modals/admin.php +++ b/data/web/modals/admin.php @@ -105,6 +105,32 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
+ +