functions.auth.inc.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  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. $row['attributes'] = json_decode($row['attributes'], true);
  222. switch ($row['authsource']) {
  223. case 'keycloak':
  224. // user authsource is keycloak, try using via rest flow
  225. if (intval($iam_settings['mailpassword_flow']) == 1){
  226. $result = keycloak_mbox_login_rest($user, $pass, array('is_internal' => $is_internal));
  227. if ($result !== false) {
  228. // double check if mailbox and domain is active
  229. $stmt = $pdo->prepare("SELECT * FROM `mailbox`
  230. INNER JOIN domain on mailbox.domain = domain.domain
  231. WHERE `kind` NOT REGEXP 'location|thing|group'
  232. AND `mailbox`.`active`='1'
  233. AND `domain`.`active`='1'
  234. AND `username` = :user");
  235. $stmt->execute(array(':user' => $user));
  236. $row = $stmt->fetch(PDO::FETCH_ASSOC);
  237. if (empty($row)) {
  238. return false;
  239. }
  240. if (intval($row['attributes']['force_pw_update']) == 1) {
  241. $_SESSION['pending_pw_update'] = true;
  242. }
  243. // check for tfa authenticators
  244. $authenticators = get_tfa($user);
  245. if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
  246. // authenticators found, init TFA flow
  247. $_SESSION['pending_mailcow_cc_username'] = $user;
  248. $_SESSION['pending_mailcow_cc_role'] = "user";
  249. $_SESSION['pending_tfa_methods'] = $authenticators['additional'];
  250. unset($_SESSION['ldelay']);
  251. $_SESSION['return'][] = array(
  252. 'type' => 'success',
  253. 'log' => array(__FUNCTION__, $user, '*', 'Provider: Keycloak'),
  254. 'msg' => array('logged_in_as', $user)
  255. );
  256. return "pending";
  257. } else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
  258. // no authenticators found, login successfull
  259. if (!$is_internal){
  260. unset($_SESSION['ldelay']);
  261. // Reactivate TFA if it was set to "deactivate TFA for next login"
  262. $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
  263. $stmt->execute(array(':user' => $user));
  264. $_SESSION['return'][] = array(
  265. 'type' => 'success',
  266. 'log' => array(__FUNCTION__, $user, '*', 'Provider: Keycloak'),
  267. 'msg' => array('logged_in_as', $user)
  268. );
  269. }
  270. return "user";
  271. }
  272. }
  273. return $result;
  274. } else {
  275. return false;
  276. }
  277. break;
  278. case 'ldap':
  279. // user authsource is ldap
  280. $result = ldap_mbox_login($user, $pass, array('is_internal' => $is_internal));
  281. if ($result !== false) {
  282. // double check if mailbox and domain is active
  283. $stmt = $pdo->prepare("SELECT * FROM `mailbox`
  284. INNER JOIN domain on mailbox.domain = domain.domain
  285. WHERE `kind` NOT REGEXP 'location|thing|group'
  286. AND `mailbox`.`active`='1'
  287. AND `domain`.`active`='1'
  288. AND `username` = :user");
  289. $stmt->execute(array(':user' => $user));
  290. $row = $stmt->fetch(PDO::FETCH_ASSOC);
  291. if (empty($row)) {
  292. return false;
  293. }
  294. if (intval($row['attributes']['force_pw_update']) == 1) {
  295. $_SESSION['pending_pw_update'] = true;
  296. }
  297. // check for tfa authenticators
  298. $authenticators = get_tfa($user);
  299. if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
  300. // authenticators found, init TFA flow
  301. $_SESSION['pending_mailcow_cc_username'] = $user;
  302. $_SESSION['pending_mailcow_cc_role'] = "user";
  303. $_SESSION['pending_tfa_methods'] = $authenticators['additional'];
  304. unset($_SESSION['ldelay']);
  305. $_SESSION['return'][] = array(
  306. 'type' => 'success',
  307. 'log' => array(__FUNCTION__, $user, '*', 'Provider: LDAP'),
  308. 'msg' => array('logged_in_as', $user)
  309. );
  310. return "pending";
  311. } else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
  312. // no authenticators found, login successfull
  313. if (!$is_internal){
  314. unset($_SESSION['ldelay']);
  315. // Reactivate TFA if it was set to "deactivate TFA for next login"
  316. $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
  317. $stmt->execute(array(':user' => $user));
  318. $_SESSION['return'][] = array(
  319. 'type' => 'success',
  320. 'log' => array(__FUNCTION__, $user, '*', 'Provider: LDAP'),
  321. 'msg' => array('logged_in_as', $user)
  322. );
  323. }
  324. return "user";
  325. }
  326. }
  327. return $result;
  328. break;
  329. case 'mailcow':
  330. if ($row['active'] != 1 || $row['d_active'] != 1) {
  331. return false;
  332. }
  333. // verify password
  334. if (verify_hash($row['password'], $pass) !== false) {
  335. if (intval($row['attributes']['force_pw_update']) == 1) {
  336. $_SESSION['pending_pw_update'] = true;
  337. }
  338. // check for tfa authenticators
  339. $authenticators = get_tfa($user);
  340. if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
  341. // authenticators found, init TFA flow
  342. $_SESSION['pending_mailcow_cc_username'] = $user;
  343. $_SESSION['pending_mailcow_cc_role'] = "user";
  344. $_SESSION['pending_tfa_methods'] = $authenticators['additional'];
  345. unset($_SESSION['ldelay']);
  346. $_SESSION['return'][] = array(
  347. 'type' => 'success',
  348. 'log' => array(__FUNCTION__, $user, '*', 'Provider: mailcow'),
  349. 'msg' => array('logged_in_as', $user)
  350. );
  351. return "pending";
  352. } else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
  353. // no authenticators found, login successfull
  354. if (!$is_internal){
  355. unset($_SESSION['ldelay']);
  356. // Reactivate TFA if it was set to "deactivate TFA for next login"
  357. $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
  358. $stmt->execute(array(':user' => $user));
  359. $_SESSION['return'][] = array(
  360. 'type' => 'success',
  361. 'log' => array(__FUNCTION__, $user, '*', 'Provider: mailcow'),
  362. 'msg' => array('logged_in_as', $user)
  363. );
  364. }
  365. return "user";
  366. }
  367. }
  368. break;
  369. }
  370. return false;
  371. }
  372. function apppass_login($user, $pass, $app_passwd_data, $extra = null){
  373. global $pdo;
  374. $is_internal = $extra['is_internal'];
  375. if (!filter_var($user, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $user))) {
  376. if (!$is_internal){
  377. $_SESSION['return'][] = array(
  378. 'type' => 'danger',
  379. 'log' => array(__FUNCTION__, $user, '*'),
  380. 'msg' => 'malformed_username'
  381. );
  382. }
  383. return false;
  384. }
  385. $protocol = false;
  386. if ($app_passwd_data['eas']){
  387. $protocol = 'eas';
  388. } else if ($app_passwd_data['dav']){
  389. $protocol = 'dav';
  390. } else if ($app_passwd_data['smtp']){
  391. $protocol = 'smtp';
  392. } else if ($app_passwd_data['imap']){
  393. $protocol = 'imap';
  394. } else if ($app_passwd_data['sieve']){
  395. $protocol = 'sieve';
  396. } else if ($app_passwd_data['pop3']){
  397. $protocol = 'pop3';
  398. } else if (!$is_internal) {
  399. return false;
  400. }
  401. // fetch app password data
  402. $stmt = $pdo->prepare("SELECT `app_passwd`.*, `app_passwd`.`password` as `password`, `app_passwd`.`id` as `app_passwd_id` FROM `app_passwd`
  403. INNER JOIN `mailbox` ON `mailbox`.`username` = `app_passwd`.`mailbox`
  404. INNER JOIN `domain` ON `mailbox`.`domain` = `domain`.`domain`
  405. WHERE `mailbox`.`kind` NOT REGEXP 'location|thing|group'
  406. AND `mailbox`.`active` = '1'
  407. AND `domain`.`active` = '1'
  408. AND `app_passwd`.`active` = '1'
  409. AND `app_passwd`.`mailbox` = :user"
  410. );
  411. // fetch password data
  412. $stmt->execute(array(
  413. ':user' => $user,
  414. ));
  415. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
  416. foreach ($rows as $row) {
  417. if ($protocol && $row[$protocol . '_access'] != '1'){
  418. continue;
  419. }
  420. // verify password
  421. if (verify_hash($row['password'], $pass) !== false) {
  422. $_SESSION['app_passwd_id'] = $row['app_passwd_id'];
  423. unset($_SESSION['ldelay']);
  424. return "user";
  425. }
  426. }
  427. return false;
  428. }
  429. // Keycloak REST Api Flow - auth user by mailcow_password attribute
  430. // This password will be used for direct UI, IMAP and SMTP Auth
  431. // To use direct user credentials, only Authorization Code Flow is valid
  432. function keycloak_mbox_login_rest($user, $pass, $extra = null){
  433. global $pdo;
  434. global $iam_provider;
  435. global $iam_settings;
  436. $is_internal = $extra['is_internal'];
  437. $create = $extra['create'];
  438. if (!filter_var($user, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $user))) {
  439. if (!$is_internal){
  440. $_SESSION['return'][] = array(
  441. 'type' => 'danger',
  442. 'log' => array(__FUNCTION__, $user, '*'),
  443. 'msg' => 'malformed_username'
  444. );
  445. }
  446. return false;
  447. }
  448. // get access_token for service account of mailcow client
  449. $admin_token = identity_provider("get-keycloak-admin-token");
  450. // get the mailcow_password attribute from keycloak user
  451. $url = "{$iam_settings['server_url']}/admin/realms/{$iam_settings['realm']}/users";
  452. $queryParams = array('email' => $user, 'exact' => true);
  453. $queryString = http_build_query($queryParams);
  454. $curl = curl_init();
  455. curl_setopt($curl, CURLOPT_TIMEOUT, 7);
  456. curl_setopt($curl, CURLOPT_URL, $url . '?' . $queryString);
  457. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  458. curl_setopt($curl, CURLOPT_HTTPHEADER, array(
  459. 'Authorization: Bearer ' . $admin_token,
  460. 'Content-Type: application/json'
  461. ));
  462. $user_res = json_decode(curl_exec($curl), true)[0];
  463. $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
  464. curl_close($curl);
  465. if ($code != 200) {
  466. $_SESSION['return'][] = array(
  467. 'type' => 'danger',
  468. 'log' => array(__FUNCTION__, $user, '*', 'Identity Provider returned HTTP ' . $code),
  469. 'msg' => 'generic_server_error'
  470. );
  471. return false;
  472. }
  473. if (!isset($user_res['attributes']['mailcow_password']) || !is_array($user_res['attributes']['mailcow_password'])){
  474. $_SESSION['return'][] = array(
  475. 'type' => 'danger',
  476. 'log' => array(__FUNCTION__, $user, '*', 'User has no mailcow_password attribute'),
  477. 'msg' => 'generic_server_error'
  478. );
  479. return false;
  480. }
  481. if (empty($user_res['attributes']['mailcow_password'][0])){
  482. $_SESSION['return'][] = array(
  483. 'type' => 'danger',
  484. 'log' => array(__FUNCTION__, $user, '*', "User's mailcow_password attribute is empty"),
  485. 'msg' => 'generic_server_error'
  486. );
  487. return false;
  488. }
  489. // validate mailcow_password
  490. $mailcow_password = $user_res['attributes']['mailcow_password'][0];
  491. if (!verify_hash($mailcow_password, $pass)) {
  492. return false;
  493. }
  494. // get mapped template
  495. $user_template = $user_res['attributes']['mailcow_template'][0];
  496. $mapper_key = array_search($user_template, $iam_settings['mappers']);
  497. if (!$create) {
  498. // login success
  499. if ($mapper_key !== false) {
  500. // update user
  501. $_SESSION['access_all_exception'] = '1';
  502. mailbox('edit', 'mailbox_from_template', array(
  503. 'username' => $user,
  504. 'name' => $user_res['name'],
  505. 'template' => $iam_settings['templates'][$mapper_key]
  506. ));
  507. $_SESSION['access_all_exception'] = '0';
  508. }
  509. return 'user';
  510. }
  511. // check if matching attribute exist
  512. if (empty($iam_settings['mappers']) || !$user_template || $mapper_key === false) {
  513. if (!empty($iam_settings['default_template'])) {
  514. $mbox_template = $iam_settings['default_template'];
  515. } else {
  516. $_SESSION['return'][] = array(
  517. 'type' => 'danger',
  518. 'log' => array(__FUNCTION__, $user, '*', 'No matching attribute mapping was found'),
  519. 'msg' => 'generic_server_error'
  520. );
  521. return false;
  522. }
  523. } else {
  524. $mbox_template = $iam_settings['templates'][$mapper_key];
  525. }
  526. // create mailbox
  527. $_SESSION['access_all_exception'] = '1';
  528. $create_res = mailbox('add', 'mailbox_from_template', array(
  529. 'domain' => explode('@', $user)[1],
  530. 'local_part' => explode('@', $user)[0],
  531. 'name' => $user_res['name'],
  532. 'authsource' => 'keycloak',
  533. 'template' => $mbox_template
  534. ));
  535. $_SESSION['access_all_exception'] = '0';
  536. if (!$create_res){
  537. clear_session();
  538. $_SESSION['return'][] = array(
  539. 'type' => 'danger',
  540. 'log' => array(__FUNCTION__, $user, '*', 'Could not create mailbox on login'),
  541. 'msg' => 'generic_server_error'
  542. );
  543. return false;
  544. }
  545. return 'user';
  546. }
  547. function ldap_mbox_login($user, $pass, $extra = null){
  548. global $pdo;
  549. global $iam_provider;
  550. global $iam_settings;
  551. $is_internal = $extra['is_internal'];
  552. $create = $extra['create'];
  553. if (!filter_var($user, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $user))) {
  554. if (!$is_internal){
  555. $_SESSION['return'][] = array(
  556. 'type' => 'danger',
  557. 'log' => array(__FUNCTION__, $user, '*'),
  558. 'msg' => 'malformed_username'
  559. );
  560. }
  561. return false;
  562. }
  563. if (!$iam_provider) {
  564. return false;
  565. }
  566. try {
  567. $ldap_query = $iam_provider->query();
  568. if (!empty($iam_settings['filter'])) {
  569. $ldap_query = $ldap_query->rawFilter($iam_settings['filter']);
  570. }
  571. $ldap_query = $ldap_query->where($iam_settings['username_field'], '=', $user)
  572. ->select([$iam_settings['username_field'], $iam_settings['attribute_field'], 'displayname', 'distinguishedname', 'dn']);
  573. $user_res = $ldap_query->firstOrFail();
  574. } catch (Exception $e) {
  575. // clear $_SESSION['return'] to not leak data
  576. $_SESSION['return'] = array();
  577. $_SESSION['return'][] = array(
  578. 'type' => 'danger',
  579. 'log' => array(__FUNCTION__, $user, '*', $e->getMessage()),
  580. 'msg' => 'generic_server_error'
  581. );
  582. return false;
  583. }
  584. try {
  585. if (!$iam_provider->auth()->attempt($user_res['dn'], $pass)) {
  586. return false;
  587. }
  588. } catch (Exception $e) {
  589. // clear $_SESSION['return'] to not leak data
  590. $_SESSION['return'] = array();
  591. $_SESSION['return'][] = array(
  592. 'type' => 'danger',
  593. 'log' => array(__FUNCTION__, $user, '*', $e->getMessage()),
  594. 'msg' => 'generic_server_error'
  595. );
  596. return false;
  597. }
  598. // get mapped template
  599. $user_template = $user_res[$iam_settings['attribute_field']][0];
  600. $mapper_key = array_search($user_template, $iam_settings['mappers']);
  601. if (!$create) {
  602. // login success
  603. if ($mapper_key !== false) {
  604. // update user
  605. $_SESSION['access_all_exception'] = '1';
  606. mailbox('edit', 'mailbox_from_template', array(
  607. 'username' => $user,
  608. 'name' => $user_res['displayname'][0],
  609. 'template' => $iam_settings['templates'][$mapper_key]
  610. ));
  611. $_SESSION['access_all_exception'] = '0';
  612. }
  613. return 'user';
  614. }
  615. // check if matching attribute exist
  616. if (empty($iam_settings['mappers']) || !$user_template || $mapper_key === false) {
  617. if (!empty($iam_settings['default_template'])) {
  618. $mbox_template = $iam_settings['default_template'];
  619. } else {
  620. $_SESSION['return'][] = array(
  621. 'type' => 'danger',
  622. 'log' => array(__FUNCTION__, $user, '*', 'No matching attribute mapping was found'),
  623. 'msg' => 'generic_server_error'
  624. );
  625. return false;
  626. }
  627. } else {
  628. $mbox_template = $iam_settings['templates'][$mapper_key];
  629. }
  630. // create mailbox
  631. $_SESSION['access_all_exception'] = '1';
  632. $create_res = mailbox('add', 'mailbox_from_template', array(
  633. 'domain' => explode('@', $user)[1],
  634. 'local_part' => explode('@', $user)[0],
  635. 'name' => $user_res['displayname'][0],
  636. 'authsource' => 'ldap',
  637. 'template' => $mbox_template
  638. ));
  639. $_SESSION['access_all_exception'] = '0';
  640. if (!$create_res){
  641. clear_session();
  642. $_SESSION['return'][] = array(
  643. 'type' => 'danger',
  644. 'log' => array(__FUNCTION__, $user, '*', 'Could not create mailbox on login'),
  645. 'msg' => 'generic_server_error'
  646. );
  647. return false;
  648. }
  649. return 'user';
  650. }