functions.auth.inc.php 25 KB

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