functions.auth.inc.php 23 KB

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