database.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. <?php
  2. /*
  3. FusionPBX
  4. Version: MPL 1.1
  5. The contents of this file are subject to the Mozilla Public License Version
  6. 1.1 (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.mozilla.org/MPL/
  9. Software distributed under the License is distributed on an "AS IS" basis,
  10. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. for the specific language governing rights and limitations under the
  12. License.
  13. The Original Code is FusionPBX
  14. The Initial Developer of the Original Code is
  15. Mark J Crane <[email protected]>
  16. Portions created by the Initial Developer are Copyright (C) 2008-2024
  17. the Initial Developer. All Rights Reserved.
  18. Contributor(s):
  19. Mark J Crane <[email protected]>
  20. */
  21. /**
  22. * plugin_database
  23. *
  24. * @method plugin_database validates the authentication using information from the database
  25. */
  26. class plugin_database {
  27. /**
  28. * Define variables and their scope
  29. */
  30. public $domain_name;
  31. public $domain_uuid;
  32. public $user_uuid;
  33. public $contact_uuid;
  34. public $contact_organization;
  35. public $contact_name_given;
  36. public $contact_name_family;
  37. public $contact_image;
  38. public $username;
  39. public $password;
  40. public $key;
  41. public $debug;
  42. public $user_email;
  43. /**
  44. * database checks the local database to authenticate the user or key
  45. * @return array [authorized] => true or false
  46. */
  47. function database(authentication $auth, settings $settings) {
  48. //pre-process some settings
  49. $theme_favicon = $settings->get('theme', 'favicon', PROJECT_PATH.'/themes/default/favicon.ico');
  50. $theme_logo = $settings->get('theme', 'logo', PROJECT_PATH.'/themes/default/images/logo_login.png');
  51. $theme_login_logo_width = $settings->get('theme', 'login_logo_width', 'auto; max-width: 300px');
  52. $theme_login_logo_height = $settings->get('theme', 'login_logo_height', 'auto; max-height: 300px');
  53. $theme_message_delay = 1000 * (float)$settings->get('theme', 'message_delay', 3000);
  54. $background_videos = $settings->get('theme', 'background_video', null);
  55. $theme_background_video = (isset($background_videos) && is_array($background_videos)) ? $background_videos[0] : null;
  56. $login_domain_name_visible = $settings->get('login', 'domain_name_visible');
  57. $login_domain_name = $settings->get('login', 'domain_name');
  58. $login_destination = $settings->get('login', 'destination');
  59. $users_unique = $settings->get('users', 'unique', '');
  60. //check if already authorized
  61. if (isset($_SESSION['authentication']['plugin']['database']) && $_SESSION['authentication']['plugin']['database']["authorized"]) {
  62. return;
  63. }
  64. //show the authentication code view
  65. if (empty($_REQUEST["username"]) && empty($_REQUEST["key"])) {
  66. //get the domain
  67. $domain_array = explode(":", $_SERVER["HTTP_HOST"]);
  68. $domain_name = $domain_array[0];
  69. //create token
  70. //$object = new token;
  71. //$token = $object->create('login');
  72. //add multi-lingual support
  73. $language = new text;
  74. $text = $language->get(null, '/core/authentication');
  75. //initialize a template object
  76. $view = new template();
  77. $view->engine = 'smarty';
  78. $view->template_dir = $_SERVER["DOCUMENT_ROOT"].PROJECT_PATH.'/core/authentication/resources/views/';
  79. $view->cache_dir = sys_get_temp_dir();
  80. $view->init();
  81. //add translations
  82. $view->assign("login_title", $text['button-login']);
  83. $view->assign("label_username", $text['label-username']);
  84. $view->assign("label_password", $text['label-password']);
  85. $view->assign("label_domain", $text['label-domain']);
  86. $view->assign("button_login", $text['button-login']);
  87. //assign default values to the template
  88. $view->assign("project_path", PROJECT_PATH);
  89. $view->assign("login_destination_url", $login_destination);
  90. $view->assign("login_domain_name_visible", $login_domain_name_visible);
  91. $view->assign("login_domain_names", $login_domain_name);
  92. $view->assign("favicon", $theme_favicon);
  93. $view->assign("login_logo_width", $theme_login_logo_width);
  94. $view->assign("login_logo_height", $theme_login_logo_height);
  95. $view->assign("login_logo_source", $theme_logo);
  96. $view->assign("message_delay", $theme_message_delay);
  97. $view->assign("background_video", $theme_background_video);
  98. if (!empty($_SESSION['username'])) {
  99. $view->assign("login_password_description", $text['label-password_description']);
  100. $view->assign("username", $_SESSION['username']);
  101. $view->assign("button_cancel", $text['button-cancel']);
  102. }
  103. //messages
  104. $view->assign('messages', message::html(true, ' '));
  105. //add the token name and hash to the view
  106. //$view->assign("token_name", $token['name']);
  107. //$view->assign("token_hash", $token['hash']);
  108. //show the views
  109. $content = $view->render('login.htm');
  110. echo $content;
  111. exit;
  112. }
  113. //validate the token
  114. //$token = new token;
  115. //if (!$token->validate($_SERVER['PHP_SELF'])) {
  116. // message::add($text['message-invalid_token'],'negative');
  117. // header('Location: domains.php');
  118. // exit;
  119. //}
  120. //add the authentication details
  121. if (isset($_REQUEST["username"])) {
  122. $this->username = $_REQUEST["username"];
  123. $_SESSION['username'] = $this->username;
  124. }
  125. if (isset($_REQUEST["password"])) {
  126. $this->password = $_REQUEST["password"];
  127. }
  128. if (isset($_REQUEST["key"])) {
  129. $this->key = $_REQUEST["key"];
  130. }
  131. if (isset($_REQUEST["domain_name"])) {
  132. $domain_name = $_REQUEST["domain_name"];
  133. $this->domain_name = $_REQUEST["domain_name"];
  134. }
  135. //get the domain name
  136. $auth->get_domain();
  137. $this->username = $_SESSION['username'] ?? null;
  138. //$this->domain_uuid = $_SESSION['domain_uuid'] ?? null;
  139. //$this->domain_name = $_SESSION['domain_name'] ?? null;
  140. //debug information
  141. //echo "domain_uuid: ".$this->domain_uuid."<br />\n";
  142. //view_array($this->domain_uuid, false);
  143. //echo "domain_name: ".$this->domain_name."<br />\n";
  144. //echo "username: ".$this->username."<br />\n";
  145. //set the default status
  146. $user_authorized = false;
  147. //check if contacts app exists
  148. $contacts_exists = file_exists($_SERVER["DOCUMENT_ROOT"].PROJECT_PATH.'/app/contacts/') ? true : false;
  149. //check the username and password if they don't match then redirect to the login
  150. $sql = "select ";
  151. $sql .= " d.domain_name, ";
  152. $sql .= " u.user_uuid, ";
  153. $sql .= " u.contact_uuid, ";
  154. $sql .= " u.username, ";
  155. $sql .= " u.password, ";
  156. $sql .= " u.user_email, ";
  157. $sql .= " u.salt, ";
  158. $sql .= " u.api_key, ";
  159. $sql .= " u.domain_uuid ";
  160. if ($contacts_exists) {
  161. $sql .= ",";
  162. $sql .= "c.contact_organization, ";
  163. $sql .= "c.contact_name_given, ";
  164. $sql .= "c.contact_name_family, ";
  165. $sql .= "a.contact_attachment_uuid ";
  166. }
  167. $sql .= "from ";
  168. $sql .= " v_domains as d, ";
  169. $sql .= " v_users as u ";
  170. if ($contacts_exists) {
  171. $sql .= "left join v_contacts as c on u.contact_uuid = c.contact_uuid and u.contact_uuid is not null ";
  172. $sql .= "left join v_contact_attachments as a on u.contact_uuid = a.contact_uuid and u.contact_uuid is not null and a.attachment_primary = 1 and a.attachment_filename is not null and a.attachment_content is not null ";
  173. }
  174. $sql .= "where ";
  175. $sql .= " u.domain_uuid = d.domain_uuid ";
  176. $sql .= " and (";
  177. $sql .= " user_type = 'default' ";
  178. $sql .= " or user_type is null";
  179. $sql .= " ) ";
  180. if (isset($this->key) && strlen($this->key) > 30) {
  181. $sql .= "and u.api_key = :api_key ";
  182. $parameters['api_key'] = $this->key;
  183. }
  184. else {
  185. $sql .= "and (\n";
  186. $sql .= " lower(u.username) = lower(:username)\n";
  187. $sql .= " or lower(u.user_email) = lower(:username)\n";
  188. $sql .= ")\n";
  189. $parameters['username'] = $this->username;
  190. }
  191. if ($users_unique === "global") {
  192. //unique username - global (example: email address)
  193. }
  194. else {
  195. //unique username - per domain
  196. $sql .= "and u.domain_uuid = :domain_uuid ";
  197. $parameters['domain_uuid'] = $this->domain_uuid;
  198. }
  199. $sql .= "and (user_enabled = 'true' or user_enabled is null) ";
  200. $row = $settings->database()->select($sql, $parameters, 'row');
  201. if (!empty($row) && is_array($row) && @sizeof($row) != 0) {
  202. //validate the password
  203. $valid_password = false;
  204. if (isset($this->key) && strlen($this->key) > 30 && $this->key === $row["api_key"]) {
  205. $valid_password = true;
  206. }
  207. else if (substr($row["password"], 0, 1) === '$') {
  208. if (isset($this->password) && !empty($this->password)) {
  209. if (password_verify($this->password, $row["password"])) {
  210. $valid_password = true;
  211. }
  212. }
  213. }
  214. else {
  215. //deprecated - compare the password provided by the user with the one in the database
  216. if (md5($row["salt"].$this->password) === $row["password"]) {
  217. $row["password"] = crypt($this->password, '$1$'.$password_salt.'$');
  218. $valid_password = true;
  219. }
  220. }
  221. //set the domain and user settings
  222. if ($valid_password) {
  223. //set the domain_uuid
  224. $this->domain_uuid = $row["domain_uuid"];
  225. $this->domain_name = $row["domain_name"];
  226. //set the domain session variables
  227. $_SESSION["domain_uuid"] = $this->domain_uuid;
  228. $_SESSION["domain_name"] = $this->domain_name;
  229. //set the domain setting
  230. if ($users_unique === "global" && $row["domain_uuid"] !== $this->domain_uuid) {
  231. $domain = new domains();
  232. $domain->set();
  233. }
  234. //set the variables
  235. $this->user_uuid = $row['user_uuid'];
  236. $this->username = $row['username'];
  237. $this->user_email = $row['user_email'];
  238. $this->contact_uuid = $row['contact_uuid'];
  239. if ($contacts_exists) {
  240. $this->contact_organization = $row['contact_organization'];
  241. $this->contact_name_given = $row['contact_name_given'];
  242. $this->contact_name_family = $row['contact_name_family'];
  243. $this->contact_image = $row['contact_attachment_uuid'];
  244. }
  245. //debug info
  246. //echo "user_uuid ".$this->user_uuid."<br />\n";
  247. //echo "username ".$this->username."<br />\n";
  248. //echo "contact_uuid ".$this->contact_uuid."<br />\n";
  249. //set a few session variables
  250. $_SESSION["user_uuid"] = $row['user_uuid'];
  251. $_SESSION["username"] = $row['username'];
  252. $_SESSION["user_email"] = $row['user_email'];
  253. $_SESSION["contact_uuid"] = $row["contact_uuid"];
  254. }
  255. //check to to see if the the password hash needs to be updated
  256. if ($valid_password) {
  257. //set the password hash cost
  258. $options = array('cost' => 10);
  259. //check if a newer hashing algorithm is available or the cost has changed
  260. if (password_needs_rehash($row["password"], PASSWORD_DEFAULT, $options)) {
  261. //build user insert array
  262. $array = [];
  263. $array['users'][0]['user_uuid'] = $this->user_uuid;
  264. $array['users'][0]['domain_uuid'] = $this->domain_uuid;
  265. $array['users'][0]['user_email'] = $this->user_email;
  266. $array['users'][0]['password'] = password_hash($this->password, PASSWORD_DEFAULT, $options);
  267. $array['users'][0]['salt'] = null;
  268. //build user group insert array
  269. $array['user_groups'][0]['user_group_uuid'] = uuid();
  270. $array['user_groups'][0]['domain_uuid'] = $this->domain_uuid;
  271. $array['user_groups'][0]['group_name'] = 'user';
  272. $array['user_groups'][0]['user_uuid'] = $this->user_uuid;
  273. //grant temporary permissions
  274. $p = permissions::new();
  275. $p->add('user_edit', 'temp');
  276. //execute insert
  277. $settings->database()->app_name = 'authentication';
  278. $settings->database()->app_uuid = 'a8a12918-69a4-4ece-a1ae-3932be0e41f1';
  279. $settings->database()->save($array);
  280. unset($array);
  281. //revoke temporary permissions
  282. $p->delete('user_edit', 'temp');
  283. }
  284. }
  285. //result array
  286. if ($valid_password) {
  287. $result["plugin"] = "database";
  288. $result["domain_name"] = $this->domain_name;
  289. $result["username"] = $this->username;
  290. $result["user_uuid"] = $this->user_uuid;
  291. $result["domain_uuid"] = $_SESSION['domain_uuid'];
  292. $result["contact_uuid"] = $this->contact_uuid;
  293. if ($contacts_exists) {
  294. $result["contact_organization"] = $this->contact_organization;
  295. $result["contact_name_given"] = $this->contact_name_given;
  296. $result["contact_name_family"] = $this->contact_name_family;
  297. $result["contact_image"] = $this->contact_image;
  298. }
  299. $result["user_email"] = $this->user_email;
  300. $result["sql"] = $sql;
  301. $result["authorized"] = $valid_password;
  302. }
  303. //return the results
  304. return $result ?? false;
  305. }
  306. return;
  307. }
  308. }
  309. ?>