2
0

functions.auth.inc.php 25 KB

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