2
0

functions.auth.inc.php 24 KB

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