functions.auth.inc.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. <?php
  2. function unset_auth_session(){
  3. unset($_SESSION['keycloak_token']);
  4. unset($_SESSION['keycloak_refresh_token']);
  5. unset($_SESSION['mailcow_cc_username']);
  6. unset($_SESSION['mailcow_cc_role']);
  7. unset($_SESSION['oauth2state']);
  8. unset($_SESSION['pending_mailcow_cc_username']);
  9. unset($_SESSION['pending_mailcow_cc_role']);
  10. unset($_SESSION['pending_tfa_methods']);
  11. }
  12. function check_login($user, $pass, $app_passwd_data = false) {
  13. global $pdo;
  14. global $redis;
  15. if (!filter_var($user, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $user))) {
  16. $_SESSION['return'][] = array(
  17. 'type' => 'danger',
  18. 'log' => array(__FUNCTION__, $user, '*'),
  19. 'msg' => 'malformed_username'
  20. );
  21. return false;
  22. }
  23. // Validate admin
  24. $result = mailcow_admin_login($user, $pass);
  25. if ($result){
  26. return $result;
  27. }
  28. // Validate domain admin
  29. $result = mailcow_domainadmin_login($user, $pass);
  30. if ($result){
  31. return $result;
  32. }
  33. // Validate mailbox user
  34. // skip log & ldelay if requests comes from dovecot
  35. $is_dovecot = false;
  36. $request_ip = $_SERVER['REMOTE_ADDR'];
  37. if ($request_ip == getenv('IPV4_NETWORK').'.250'){
  38. $is_dovecot = true;
  39. }
  40. // check authsource
  41. $stmt = $pdo->prepare("SELECT authsource FROM `mailbox`
  42. INNER JOIN domain on mailbox.domain = domain.domain
  43. WHERE `kind` NOT REGEXP 'location|thing|group'
  44. AND `mailbox`.`active`='1'
  45. AND `domain`.`active`='1'
  46. AND `username` = :user");
  47. $stmt->execute(array(':user' => $user));
  48. $row = $stmt->fetch(PDO::FETCH_ASSOC);
  49. if (!$row){
  50. // mbox does not exist, call keycloak login and create mbox if possible
  51. $result = keycloak_mbox_login_rest($user, $pass, $is_dovecot, true);
  52. if ($result){
  53. return $result;
  54. }
  55. } else if ($row['authsource'] == 'keycloak'){
  56. if ($app_passwd_data){
  57. // first check if password is app_password
  58. $result = mailcow_mbox_apppass_login($user, $pass, $app_passwd_data, $is_dovecot);
  59. if ($result){
  60. return $result;
  61. }
  62. }
  63. $result = keycloak_mbox_login_rest($user, $pass, $is_dovecot);
  64. if ($result){
  65. return $result;
  66. }
  67. } else {
  68. if ($app_passwd_data){
  69. // first check if password is app_password
  70. $result = mailcow_mbox_apppass_login($user, $pass, $app_passwd_data, $is_dovecot);
  71. if ($result){
  72. return $result;
  73. }
  74. }
  75. $result = mailcow_mbox_login($user, $pass, $app_passwd_data, $is_dovecot);
  76. if ($result){
  77. return $result;
  78. }
  79. }
  80. // skip log and only return false
  81. // netfilter uses dovecot error log for banning
  82. if ($is_dovecot){
  83. return false;
  84. }
  85. if (!isset($_SESSION['ldelay'])) {
  86. $_SESSION['ldelay'] = "0";
  87. $redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
  88. error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
  89. }
  90. elseif (!isset($_SESSION['mailcow_cc_username'])) {
  91. $_SESSION['ldelay'] = $_SESSION['ldelay']+0.5;
  92. $redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
  93. error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
  94. }
  95. $_SESSION['return'][] = array(
  96. 'type' => 'danger',
  97. 'log' => array(__FUNCTION__, $user, '*'),
  98. 'msg' => 'login_failed'
  99. );
  100. sleep($_SESSION['ldelay']);
  101. return false;
  102. }
  103. function mailcow_mbox_login($user, $pass, $app_passwd_data = false, $is_internal = false){
  104. global $pdo;
  105. $stmt = $pdo->prepare("SELECT * FROM `mailbox`
  106. INNER JOIN domain on mailbox.domain = domain.domain
  107. WHERE `kind` NOT REGEXP 'location|thing|group'
  108. AND `mailbox`.`active`='1'
  109. AND `domain`.`active`='1'
  110. AND (`mailbox`.`authsource`='mailcow' OR `mailbox`.`authsource` IS NULL)
  111. AND `username` = :user");
  112. $stmt->execute(array(':user' => $user));
  113. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
  114. foreach ($rows as $row) {
  115. // verify password
  116. if (verify_hash($row['password'], $pass) !== false) {
  117. if (!array_key_exists("app_passwd_id", $row)){
  118. // password is not a app password
  119. // check for tfa authenticators
  120. $authenticators = get_tfa($user);
  121. if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
  122. // authenticators found, init TFA flow
  123. $_SESSION['pending_mailcow_cc_username'] = $user;
  124. $_SESSION['pending_mailcow_cc_role'] = "user";
  125. $_SESSION['pending_tfa_methods'] = $authenticators['additional'];
  126. unset($_SESSION['ldelay']);
  127. $_SESSION['return'][] = array(
  128. 'type' => 'success',
  129. 'log' => array(__FUNCTION__, $user, '*'),
  130. 'msg' => array('logged_in_as', $user)
  131. );
  132. return "pending";
  133. } else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
  134. // no authenticators found, login successfull
  135. if (!$is_internal){
  136. unset($_SESSION['ldelay']);
  137. // Reactivate TFA if it was set to "deactivate TFA for next login"
  138. $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
  139. $stmt->execute(array(':user' => $user));
  140. $_SESSION['return'][] = array(
  141. 'type' => 'success',
  142. 'log' => array(__FUNCTION__, $user, '*'),
  143. 'msg' => array('logged_in_as', $user)
  144. );
  145. }
  146. return "user";
  147. }
  148. }
  149. }
  150. }
  151. return false;
  152. }
  153. function mailcow_domainadmin_login($user, $pass){
  154. global $pdo;
  155. $stmt = $pdo->prepare("SELECT `password` FROM `admin`
  156. WHERE `superadmin` = '0'
  157. AND `active`='1'
  158. AND `username` = :user");
  159. $stmt->execute(array(':user' => $user));
  160. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
  161. foreach ($rows as $row) {
  162. // verify password
  163. if (verify_hash($row['password'], $pass) !== false) {
  164. // check for tfa authenticators
  165. $authenticators = get_tfa($user);
  166. if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) {
  167. $_SESSION['pending_mailcow_cc_username'] = $user;
  168. $_SESSION['pending_mailcow_cc_role'] = "domainadmin";
  169. $_SESSION['pending_tfa_methods'] = $authenticators['additional'];
  170. unset($_SESSION['ldelay']);
  171. $_SESSION['return'][] = array(
  172. 'type' => 'info',
  173. 'log' => array(__FUNCTION__, $user, '*'),
  174. 'msg' => 'awaiting_tfa_confirmation'
  175. );
  176. return "pending";
  177. }
  178. else {
  179. unset($_SESSION['ldelay']);
  180. // Reactivate TFA if it was set to "deactivate TFA for next login"
  181. $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
  182. $stmt->execute(array(':user' => $user));
  183. $_SESSION['return'][] = array(
  184. 'type' => 'success',
  185. 'log' => array(__FUNCTION__, $user, '*'),
  186. 'msg' => array('logged_in_as', $user)
  187. );
  188. return "domainadmin";
  189. }
  190. }
  191. }
  192. return false;
  193. }
  194. function mailcow_admin_login($user, $pass){
  195. global $pdo;
  196. $user = strtolower(trim($user));
  197. $stmt = $pdo->prepare("SELECT `password` FROM `admin`
  198. WHERE `superadmin` = '1'
  199. AND `active` = '1'
  200. AND `username` = :user");
  201. $stmt->execute(array(':user' => $user));
  202. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
  203. foreach ($rows as $row) {
  204. // verify password
  205. if (verify_hash($row['password'], $pass)) {
  206. // check for tfa authenticators
  207. $authenticators = get_tfa($user);
  208. if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) {
  209. // active tfa authenticators found, set pending user login
  210. $_SESSION['pending_mailcow_cc_username'] = $user;
  211. $_SESSION['pending_mailcow_cc_role'] = "admin";
  212. $_SESSION['pending_tfa_methods'] = $authenticators['additional'];
  213. unset($_SESSION['ldelay']);
  214. $_SESSION['return'][] = array(
  215. 'type' => 'info',
  216. 'log' => array(__FUNCTION__, $user, '*'),
  217. 'msg' => 'awaiting_tfa_confirmation'
  218. );
  219. return "pending";
  220. } else {
  221. unset($_SESSION['ldelay']);
  222. // Reactivate TFA if it was set to "deactivate TFA for next login"
  223. $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
  224. $stmt->execute(array(':user' => $user));
  225. $_SESSION['return'][] = array(
  226. 'type' => 'success',
  227. 'log' => array(__FUNCTION__, $user, '*'),
  228. 'msg' => array('logged_in_as', $user)
  229. );
  230. return "admin";
  231. }
  232. }
  233. }
  234. return false;
  235. }
  236. function mailcow_mbox_apppass_login($user, $pass, $app_passwd_data, $is_internal = false){
  237. global $pdo;
  238. $protocol = false;
  239. if ($app_passwd_data['eas']){
  240. $protocol = 'eas';
  241. } else if ($app_passwd_data['dav']){
  242. $protocol = 'dav';
  243. } else if ($app_passwd_data['smtp']){
  244. $protocol = 'smtp';
  245. } else if ($app_passwd_data['imap']){
  246. $protocol = 'imap';
  247. } else if ($app_passwd_data['sieve']){
  248. $protocol = 'sieve';
  249. } else if ($app_passwd_data['pop3']){
  250. $protocol = 'pop3';
  251. }
  252. // fetch app password data
  253. $stmt = $pdo->prepare("SELECT `app_passwd`.`password` as `password`, `app_passwd`.`id` as `app_passwd_id` FROM `app_passwd`
  254. INNER JOIN `mailbox` ON `mailbox`.`username` = `app_passwd`.`mailbox`
  255. INNER JOIN `domain` ON `mailbox`.`domain` = `domain`.`domain`
  256. WHERE `mailbox`.`kind` NOT REGEXP 'location|thing|group'
  257. AND `mailbox`.`active` = '1'
  258. AND `domain`.`active` = '1'
  259. AND `app_passwd`.`active` = '1'
  260. AND `app_passwd`.`mailbox` = :user
  261. :has_access_query"
  262. );
  263. // check if app password has protocol access
  264. // skip if protocol is false and the call is not external
  265. $has_access_query = '';
  266. if (!$is_internal || ($is_internal && !empty($protocol))){
  267. $has_access_query = " AND `app_passwd`.`" . $protocol . "_access` = '1'";
  268. }
  269. // fetch password data
  270. $stmt->execute(array(
  271. ':user' => $user,
  272. ':has_access_query' => $has_access_query
  273. ));
  274. $rows = array_merge($rows, $stmt->fetchAll(PDO::FETCH_ASSOC));
  275. foreach ($rows as $row) {
  276. // verify password
  277. if (verify_hash($row['password'], $pass) !== false) {
  278. if ($is_internal){
  279. // skip sasl_log, dovecot does the job
  280. return "user";
  281. }
  282. $service = strtoupper($is_app_passwd);
  283. $stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES (:service, :app_id, :username, :remote_addr)");
  284. $stmt->execute(array(
  285. ':service' => $service,
  286. ':app_id' => $row['app_passwd_id'],
  287. ':username' => $user,
  288. ':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'])
  289. ));
  290. unset($_SESSION['ldelay']);
  291. return "user";
  292. }
  293. }
  294. return false;
  295. }
  296. // ROPC Flow (deprecated oAuth2.1)
  297. // uses direct user credentials for UI, IMAP and SMTP Auth
  298. function keycloak_mbox_login_ropc($user, $pass, $is_internal = false, $create = false){
  299. global $pdo;
  300. $identity_provider_settings = identity_provider('get');
  301. $url = "{$identity_provider_settings['server_url']}/realms/{$identity_provider_settings['realm']}/protocol/openid-connect/token";
  302. $req = http_build_query(array(
  303. 'grant_type' => 'password',
  304. 'client_id' => $identity_provider_settings['client_id'],
  305. 'client_secret' => $identity_provider_settings['client_secret'],
  306. 'username' => $user,
  307. 'password' => $pass,
  308. ));
  309. $curl = curl_init();
  310. curl_setopt($curl, CURLOPT_URL, $url);
  311. curl_setopt($curl, CURLOPT_POST, 1);
  312. curl_setopt($curl, CURLOPT_POSTFIELDS, $req);
  313. curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  314. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  315. $res = json_decode(curl_exec($curl), true);
  316. $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
  317. curl_close ($curl);
  318. if ($code == 200) {
  319. // decode jwt
  320. $user_data = json_decode(base64_decode(str_replace('_', '/', str_replace('-','+',explode('.', $res['access_token'])[1]))), true);
  321. if ($user != $user_data['email']){
  322. // check if $user is email address, only accept email address as username
  323. return false;
  324. }
  325. if ($create && !empty($identity_provider_settings['mappers'])){
  326. // try to create mbox on successfull login
  327. $user_roles = $user_data['realm_access']['roles'];
  328. $mbox_template = null;
  329. // check if matching rolemapping exist
  330. foreach ($user_roles as $index => $role){
  331. if (in_array($role, $identity_provider_settings['roles'])) {
  332. $mbox_template = $identity_provider_settings['templates'][$index];
  333. break;
  334. }
  335. }
  336. if ($mbox_template){
  337. $stmt = $pdo->prepare("SELECT * FROM `templates`
  338. WHERE `template` = :template AND type = 'mailbox'");
  339. $stmt->execute(array(
  340. ":template" => $mbox_template
  341. ));
  342. $mbox_template_data = $stmt->fetch(PDO::FETCH_ASSOC);
  343. if (!empty($mbox_template_data)){
  344. $mbox_template_data = json_decode($mbox_template_data["attributes"], true);
  345. $mbox_template_data['domain'] = explode('@', $user)[1];
  346. $mbox_template_data['local_part'] = explode('@', $user)[0];
  347. $mbox_template_data['authsource'] = 'keycloak';
  348. $_SESSION['iam_create_login'] = true;
  349. $create_res = mailbox('add', 'mailbox', $mbox_template_data);
  350. $_SESSION['iam_create_login'] = false;
  351. if (!$create_res){
  352. return false;
  353. }
  354. }
  355. }
  356. }
  357. $_SESSION['return'][] = array(
  358. 'type' => 'success',
  359. 'log' => array(__FUNCTION__, $user, '*'),
  360. 'msg' => array('logged_in_as', $user)
  361. );
  362. return 'user';
  363. } else {
  364. return false;
  365. }
  366. }
  367. // Keycloak REST Api Flow - auth user by mailcow_password attribute
  368. // This password will be used for direct UI, IMAP and SMTP Auth
  369. // To use direct user credentials, only Authorization Code Flow is valid
  370. function keycloak_mbox_login_rest($user, $pass, $is_internal = false, $create = false){
  371. global $pdo;
  372. $identity_provider_settings = identity_provider('get');
  373. // get access_token for service account of mailcow client
  374. $url = "{$identity_provider_settings['server_url']}/realms/{$identity_provider_settings['realm']}/protocol/openid-connect/token";
  375. $req = http_build_query(array(
  376. 'grant_type' => 'client_credentials',
  377. 'client_id' => $identity_provider_settings['client_id'],
  378. 'client_secret' => $identity_provider_settings['client_secret']
  379. ));
  380. $curl = curl_init();
  381. curl_setopt($curl, CURLOPT_URL, $url);
  382. curl_setopt($curl, CURLOPT_POST, 1);
  383. curl_setopt($curl, CURLOPT_POSTFIELDS, $req);
  384. curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  385. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  386. $mcclient_res = json_decode(curl_exec($curl), true);
  387. $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
  388. curl_close ($curl);
  389. if ($code != 200) {
  390. return false;
  391. }
  392. // get the mailcow_password attribute from keycloak user
  393. $url = "{$identity_provider_settings['server_url']}/admin/realms/{$identity_provider_settings['realm']}/users";
  394. $queryParams = array('email' => $user, 'exact' => true);
  395. $queryString = http_build_query($queryParams);
  396. $curl = curl_init();
  397. curl_setopt($curl, CURLOPT_URL, $url . '?' . $queryString);
  398. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  399. curl_setopt($curl, CURLOPT_HTTPHEADER, array(
  400. 'Authorization: Bearer ' . $mcclient_res['access_token'],
  401. 'Content-Type: application/json'
  402. ));
  403. $user_res = json_decode(curl_exec($curl), true)[0];
  404. $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
  405. curl_close($curl);
  406. if ($code != 200) {
  407. return false;
  408. }
  409. // validate mailcow_password
  410. $mailcow_password = $user_res['attributes']['mailcow_password'][0];
  411. if (!verify_hash($mailcow_password, $pass)) {
  412. return false;
  413. }
  414. // get mapped template, if not set return false
  415. // also return false if no mappers were defined
  416. $user_template = $user_data['attributes']['mailcow_template'][0];
  417. if ($create && (empty($identity_provider_settings['mappers']) || $user_template)){
  418. return false;
  419. } else if (!$create) {
  420. // login success - dont create mailbox
  421. $_SESSION['return'][] = array(
  422. 'type' => 'success',
  423. 'log' => array(__FUNCTION__, $user, '*'),
  424. 'msg' => array('logged_in_as', $user)
  425. );
  426. return 'user';
  427. }
  428. // try to create mbox on successfull login
  429. $mbox_template = null;
  430. // check if matching rolemapping exist
  431. foreach ($identity_provider_settings['mappers'] as $index => $mapper){
  432. if (in_array($mapper, $identity_provider_settings['mappers'])) {
  433. $mbox_template = $identity_provider_settings['templates'][$index];
  434. break;
  435. }
  436. }
  437. if (!$mbox_template){
  438. // no matching template found
  439. return false;
  440. }
  441. $stmt = $pdo->prepare("SELECT * FROM `templates`
  442. WHERE `template` = :template AND type = 'mailbox'");
  443. $stmt->execute(array(
  444. ":template" => $mbox_template
  445. ));
  446. $mbox_template_data = $stmt->fetch(PDO::FETCH_ASSOC);
  447. if (empty($mbox_template_data)){
  448. return false;
  449. }
  450. $mbox_template_data = json_decode($mbox_template_data["attributes"], true);
  451. $mbox_template_data['domain'] = explode('@', $user)[1];
  452. $mbox_template_data['local_part'] = explode('@', $user)[0];
  453. $mbox_template_data['authsource'] = 'keycloak';
  454. $_SESSION['iam_create_login'] = true;
  455. $create_res = mailbox('add', 'mailbox', $mbox_template_data);
  456. $_SESSION['iam_create_login'] = false;
  457. if (!$create_res){
  458. return false;
  459. }
  460. $_SESSION['return'][] = array(
  461. 'type' => 'success',
  462. 'log' => array(__FUNCTION__, $user, '*'),
  463. 'msg' => array('logged_in_as', $user)
  464. );
  465. return 'user';
  466. }
  467. // Authorization Code Flow
  468. function keycloak_verify_token(){
  469. global $keycloak_provider;
  470. global $pdo;
  471. try {
  472. $token = $keycloak_provider->getAccessToken('authorization_code', ['code' => $_GET['code']]);
  473. } catch (Exception $e) {
  474. $_SESSION['return'][] = array(
  475. 'type' => 'danger',
  476. 'log' => array(__FUNCTION__),
  477. 'msg' => array('login_failed', $e->getMessage())
  478. );
  479. return false;
  480. }
  481. $_SESSION['keycloak_token'] = $token->getToken();
  482. $_SESSION['keycloak_refresh_token'] = $token->getRefreshToken();
  483. // decode jwt data
  484. $info = json_decode(base64_decode(str_replace('_', '/', str_replace('-','+',explode('.', $token->getToken())[1]))), true);
  485. // check if email address is given
  486. if (!$info['email']){
  487. return false;
  488. }
  489. // token valid, get mailbox
  490. $stmt = $pdo->prepare("SELECT * FROM `mailbox`
  491. INNER JOIN domain on mailbox.domain = domain.domain
  492. WHERE `kind` NOT REGEXP 'location|thing|group'
  493. AND `mailbox`.`active`='1'
  494. AND `domain`.`active`='1'
  495. AND `username` = :user
  496. AND `authsource`='keycloak'");
  497. $stmt->execute(array(':user' => $info['email']));
  498. $row = $stmt->fetch(PDO::FETCH_ASSOC);
  499. if ($row){
  500. // success
  501. $_SESSION['mailcow_cc_username'] = $info['email'];
  502. $_SESSION['mailcow_cc_role'] = "user";
  503. $_SESSION['return'][] = array(
  504. 'type' => 'success',
  505. 'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']),
  506. 'msg' => array('logged_in_as', $_SESSION['mailcow_cc_username'])
  507. );
  508. return true;
  509. }
  510. // get mapped template, if not set return false
  511. // also return false if no mappers were defined
  512. $identity_provider_settings = identity_provider('get');
  513. $user_template = $info['mailcow_template'];
  514. if (empty($identity_provider_settings['mappers']) || empty($user_template)){
  515. unset_auth_session();
  516. $_SESSION['return'][] = array(
  517. 'type' => 'danger',
  518. 'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']),
  519. 'msg' => 'login_failed'
  520. );
  521. return false;
  522. }
  523. $mbox_template = null;
  524. // check if matching rolemapping exist
  525. foreach ($identity_provider_settings['mappers'] as $index => $mapper){
  526. if ($user_template == $mapper) {
  527. $mbox_template = $identity_provider_settings['templates'][$index];
  528. break;
  529. }
  530. }
  531. if (!$mbox_template){
  532. // no matching template found
  533. unset_auth_session();
  534. $_SESSION['return'][] = array(
  535. 'type' => 'danger',
  536. 'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']),
  537. 'msg' => 'login_failed'
  538. );
  539. return false;
  540. }
  541. $stmt = $pdo->prepare("SELECT * FROM `templates`
  542. WHERE `template` = :template AND type = 'mailbox'");
  543. $stmt->execute(array(
  544. ":template" => $mbox_template
  545. ));
  546. $mbox_template_data = $stmt->fetch(PDO::FETCH_ASSOC);
  547. if (empty($mbox_template_data)){
  548. unset_auth_session();
  549. $_SESSION['return'][] = array(
  550. 'type' => 'danger',
  551. 'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']),
  552. 'msg' => 'login_failed'
  553. );
  554. return false;
  555. }
  556. // create mailbox
  557. $mbox_template_data = json_decode($mbox_template_data["attributes"], true);
  558. $mbox_template_data['domain'] = explode('@', $info['email'])[1];
  559. $mbox_template_data['local_part'] = explode('@', $info['email'])[0];
  560. $mbox_template_data['authsource'] = 'keycloak';
  561. $_SESSION['iam_create_login'] = true;
  562. $create_res = mailbox('add', 'mailbox', $mbox_template_data);
  563. $_SESSION['iam_create_login'] = false;
  564. if (!$create_res){
  565. unset_auth_session();
  566. $_SESSION['return'][] = array(
  567. 'type' => 'danger',
  568. 'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']),
  569. 'msg' => 'login_failed'
  570. );
  571. return false;
  572. }
  573. $_SESSION['mailcow_cc_username'] = $info['email'];
  574. $_SESSION['mailcow_cc_role'] = "user";
  575. $_SESSION['return'][] = array(
  576. 'type' => 'success',
  577. 'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']),
  578. 'msg' => array('logged_in_as', $_SESSION['mailcow_cc_username'])
  579. );
  580. return true;
  581. }
  582. function keycloak_refresh(){
  583. global $keycloak_provider;
  584. try {
  585. $token = $keycloak_provider->getAccessToken('refresh_token', ['refresh_token' => $_SESSION['keycloak_refresh_token']]);
  586. } catch (Exception $e) {
  587. $_SESSION['return'][] = array(
  588. 'type' => 'danger',
  589. 'log' => array(__FUNCTION__),
  590. 'msg' => array('login_failed', $e->getMessage())
  591. );
  592. return false;
  593. }
  594. $_SESSION['keycloak_token'] = $token->getToken();
  595. $_SESSION['keycloak_refresh_token'] = $token->getRefreshToken();
  596. // decode jwt data
  597. $info = json_decode(base64_decode(str_replace('_', '/', str_replace('-','+',explode('.', $_SESSION['keycloak_token'])[1]))), true);
  598. if (!$info['email']){
  599. unset_auth_session();
  600. $_SESSION['return'][] = array(
  601. 'type' => 'danger',
  602. 'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']),
  603. 'msg' => 'refresh_login_failed'
  604. );
  605. }
  606. $_SESSION['mailcow_cc_username'] = $info['email'];
  607. $_SESSION['mailcow_cc_role'] = "user";
  608. return true;
  609. }
  610. function keycloak_get_redirect(){
  611. global $keycloak_provider;
  612. $authUrl = $keycloak_provider->getAuthorizationUrl();
  613. $_SESSION['oauth2state'] = $keycloak_provider->getState();
  614. return $authUrl;
  615. }
  616. function keycloak_get_logout(){
  617. global $keycloak_provider;
  618. $logoutUrl = $keycloak_provider->getLogoutUrl();
  619. $logoutUrl = $logoutUrl . "&post_logout_redirect_uri=https://" . $_SERVER['SERVER_NAME'];
  620. return $logoutUrl;
  621. }