functions.auth.inc.php 24 KB

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