auth-functions.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. <?php
  2. function authRegister($username, $password, $defaults, $email, $token = null)
  3. {
  4. if ($GLOBALS['authBackend'] !== '') {
  5. ombiImport($GLOBALS['authBackend']);
  6. }
  7. ssoCheck($username, $password, $token);
  8. if (createUser($username, $password, $defaults, $email)) {
  9. writeLog('success', 'Registration Function - A User has registered', $username);
  10. if ($GLOBALS['PHPMAILER-enabled'] && $email !== '') {
  11. $emailTemplate = array(
  12. 'type' => 'registration',
  13. 'body' => $GLOBALS['PHPMAILER-emailTemplateRegisterUser'],
  14. 'subject' => $GLOBALS['PHPMAILER-emailTemplateRegisterUserSubject'],
  15. 'user' => $username,
  16. 'password' => null,
  17. 'inviteCode' => null,
  18. );
  19. $emailTemplate = phpmEmailTemplate($emailTemplate);
  20. $sendEmail = array(
  21. 'to' => $email,
  22. 'user' => $username,
  23. 'subject' => $emailTemplate['subject'],
  24. 'body' => phpmBuildEmail($emailTemplate),
  25. );
  26. phpmSendEmail($sendEmail);
  27. }
  28. if (createToken($username, $email, gravatar($email), $defaults['group'], $defaults['group_id'], $GLOBALS['organizrHash'], $GLOBALS['rememberMeDays'])) {
  29. writeLoginLog($username, 'success');
  30. writeLog('success', 'Login Function - A User has logged in', $username);
  31. return true;
  32. }
  33. } else {
  34. writeLog('error', 'Registration Function - An error occurred', $username);
  35. return 'username taken';
  36. }
  37. return false;
  38. }
  39. function checkPlexToken($token = '')
  40. {
  41. try {
  42. if (($token !== '')) {
  43. $url = 'https://plex.tv/users/account.json';
  44. $headers = array(
  45. 'X-Plex-Token' => $token,
  46. 'Content-Type' => 'application/json',
  47. 'Accept' => 'application/json'
  48. );
  49. $response = Requests::get($url, $headers);
  50. if ($response->success) {
  51. return json_decode($response->body, true);
  52. }
  53. } else {
  54. return false;
  55. }
  56. } catch (Requests_Exception $e) {
  57. writeLog('success', 'Plex Token Check Function - Error: ' . $e->getMessage(), SYSTEM);
  58. }
  59. return false;
  60. }
  61. function checkPlexUser($username)
  62. {
  63. try {
  64. if (!empty($GLOBALS['plexToken'])) {
  65. $url = 'https://plex.tv/api/users';
  66. $headers = array(
  67. 'X-Plex-Token' => $GLOBALS['plexToken'],
  68. );
  69. $response = Requests::get($url, $headers);
  70. if ($response->success) {
  71. libxml_use_internal_errors(true);
  72. $userXML = simplexml_load_string($response->body);
  73. if (is_array($userXML) || is_object($userXML)) {
  74. $usernameLower = strtolower($username);
  75. foreach ($userXML as $child) {
  76. if (isset($child['username']) && strtolower($child['username']) == $usernameLower || isset($child['email']) && strtolower($child['email']) == $usernameLower) {
  77. writeLog('success', 'Plex User Check - Found User on Friends List', $username);
  78. $machineMatches = false;
  79. if ($GLOBALS['plexStrictFriends']) {
  80. foreach ($child->Server as $server) {
  81. if ((string)$server['machineIdentifier'] == $GLOBALS['plexID']) {
  82. $machineMatches = true;
  83. }
  84. }
  85. } else {
  86. $machineMatches = true;
  87. }
  88. if ($machineMatches) {
  89. writeLog('success', 'Plex User Check - User Approved for Login', $username);
  90. return true;
  91. } else {
  92. writeLog('error', 'Plex User Check - User not Approved User', $username);
  93. }
  94. }
  95. }
  96. }
  97. }
  98. }
  99. return false;
  100. } catch (Requests_Exception $e) {
  101. writeLog('error', 'Plex User Check Function - Error: ' . $e->getMessage(), $username);
  102. }
  103. return false;
  104. }
  105. function allPlexUsers($newOnly = false, $friendsOnly = false)
  106. {
  107. try {
  108. if (!empty($GLOBALS['plexToken'])) {
  109. $url = 'https://plex.tv/api/users';
  110. $headers = array(
  111. 'X-Plex-Token' => $GLOBALS['plexToken'],
  112. );
  113. $response = Requests::get($url, $headers);
  114. if ($response->success) {
  115. libxml_use_internal_errors(true);
  116. $userXML = simplexml_load_string($response->body);
  117. if (is_array($userXML) || is_object($userXML)) {
  118. $results = array();
  119. foreach ($userXML as $child) {
  120. if (((string)$child['restricted'] == '0')) {
  121. if ($newOnly) {
  122. $taken = usernameTaken((string)$child['username'], (string)$child['email']);
  123. if (!$taken) {
  124. $results[] = array(
  125. 'username' => (string)$child['username'],
  126. 'email' => (string)$child['email'],
  127. 'id' => (string)$child['id'],
  128. );
  129. }
  130. } elseif ($friendsOnly) {
  131. $machineMatches = false;
  132. foreach ($child->Server as $server) {
  133. if ((string)$server['machineIdentifier'] == $GLOBALS['plexID']) {
  134. $machineMatches = true;
  135. }
  136. }
  137. if($machineMatches){
  138. $results[] = array(
  139. 'username' => (string)$child['username'],
  140. 'email' => (string)$child['email'],
  141. 'id' => (string)$child['id'],
  142. );
  143. }
  144. }else{
  145. $results[] = array(
  146. 'username' => (string)$child['username'],
  147. 'email' => (string)$child['email'],
  148. 'id' => (string)$child['id'],
  149. );
  150. }
  151. }
  152. }
  153. return $results;
  154. }
  155. }
  156. }
  157. return false;
  158. } catch (Requests_Exception $e) {
  159. writeLog('success', 'Plex Import User Function - Error: ' . $e->getMessage(), 'SYSTEM');
  160. }
  161. return false;
  162. }
  163. function allJellyfinUsers($newOnly = false)
  164. {
  165. try {
  166. if (!empty($GLOBALS['embyURL']) && !empty($GLOBALS['embyToken'])) {
  167. $url = qualifyURL($GLOBALS['embyURL']) . '/Users?api_key=' . $GLOBALS['embyToken'];
  168. $headers = array();
  169. $response = Requests::get($url, $headers);
  170. if ($response->success) {
  171. $users = json_decode($response->body, true);
  172. if (is_array($users) || is_object($users)) {
  173. $results = array();
  174. foreach ($users as $child) {
  175. // Jellyfin doesn't list emails for some reason
  176. $email = random_ascii_string(10) . '@placeholder.eml';
  177. if ($newOnly) {
  178. $taken = usernameTaken((string)$child['Name'], $email);
  179. if (!$taken) {
  180. $results[] = array(
  181. 'username' => (string)$child['Name'],
  182. 'email' => $email
  183. );
  184. }
  185. } else {
  186. $results[] = array(
  187. 'username' => (string)$child['Name'],
  188. 'email' => $email,
  189. );
  190. }
  191. }
  192. return $results;
  193. }
  194. }
  195. }
  196. return false;
  197. } catch (Requests_Exception $e) {
  198. writeLog('success', 'Jellyfin Import User Function - Error: ' . $e->getMessage(), 'SYSTEM');
  199. }
  200. return false;
  201. }
  202. function allEmbyUsers($newOnly = false)
  203. {
  204. try {
  205. if (!empty($GLOBALS['embyURL']) && !empty($GLOBALS['embyToken'])) {
  206. $url = qualifyURL($GLOBALS['embyURL']) . '/Users?api_key=' . $GLOBALS['embyToken'];
  207. $headers = array();
  208. $response = Requests::get($url, $headers);
  209. if ($response->success) {
  210. $users = json_decode($response->body, true);
  211. if (is_array($users) || is_object($users)) {
  212. $results = array();
  213. foreach ($users as $child) {
  214. // Emby doesn't list emails for some reason
  215. $email = random_ascii_string(10) . '@placeholder.eml';
  216. if ($newOnly) {
  217. $taken = usernameTaken((string)$child['Name'], $email);
  218. if (!$taken) {
  219. $results[] = array(
  220. 'username' => (string)$child['Name'],
  221. 'email' => $email
  222. );
  223. }
  224. } else {
  225. $results[] = array(
  226. 'username' => (string)$child['Name'],
  227. 'email' => $email,
  228. );
  229. }
  230. }
  231. return $results;
  232. }
  233. }
  234. }
  235. return false;
  236. } catch (Requests_Exception $e) {
  237. writeLog('success', 'Emby Import User Function - Error: ' . $e->getMessage(), 'SYSTEM');
  238. }
  239. return false;
  240. }
  241. function plugin_auth_plex($username, $password)
  242. {
  243. try {
  244. $usernameLower = strtolower($username);
  245. //Login User
  246. $url = 'https://plex.tv/users/sign_in.json';
  247. $headers = array(
  248. 'Accept' => 'application/json',
  249. 'Content-Type' => 'application/x-www-form-urlencoded',
  250. 'X-Plex-Product' => 'Organizr',
  251. 'X-Plex-Version' => '2.0',
  252. 'X-Plex-Client-Identifier' => $GLOBALS['uuid'],
  253. );
  254. $data = array(
  255. 'user[login]' => $username,
  256. 'user[password]' => $password,
  257. );
  258. $options = array('timeout' => 30);
  259. $response = Requests::post($url, $headers, $data, $options);
  260. if ($response->success) {
  261. $json = json_decode($response->body, true);
  262. if ((is_array($json) && isset($json['user']) && isset($json['user']['username'])) && strtolower($json['user']['username']) == $usernameLower || strtolower($json['user']['email']) == $usernameLower) {
  263. //writeLog("success", $json['user']['username']." was logged into organizr using plex credentials");
  264. if ((!empty($GLOBALS['plexAdmin']) && (strtolower($GLOBALS['plexAdmin']) == strtolower($json['user']['username']) || strtolower($GLOBALS['plexAdmin']) == strtolower($json['user']['email']))) || checkPlexUser($json['user']['username'])) {
  265. return array(
  266. 'username' => $json['user']['username'],
  267. 'email' => $json['user']['email'],
  268. 'image' => $json['user']['thumb'],
  269. 'token' => $json['user']['authToken']
  270. );
  271. }
  272. }
  273. }
  274. return false;
  275. } catch (Requests_Exception $e) {
  276. writeLog('success', 'Plex Auth Function - Error: ' . $e->getMessage(), $username);
  277. }
  278. return false;
  279. }
  280. if (function_exists('ldap_connect')) {
  281. // Pass credentials to LDAP backend
  282. function plugin_auth_ldap($username, $password)
  283. {
  284. if (!empty($GLOBALS['authBaseDN']) && !empty($GLOBALS['authBackendHost'])) {
  285. $ad = new \Adldap\Adldap();
  286. // Create a configuration array.
  287. $ldapServers = explode(',', $GLOBALS['authBackendHost']);
  288. $i = 0;
  289. foreach ($ldapServers as $key => $value) {
  290. // Calculate parts
  291. $digest = parse_url(trim($value));
  292. $scheme = strtolower((isset($digest['scheme']) ? $digest['scheme'] : 'ldap'));
  293. $host = (isset($digest['host']) ? $digest['host'] : (isset($digest['path']) ? $digest['path'] : ''));
  294. $port = (isset($digest['port']) ? $digest['port'] : (strtolower($scheme) == 'ldap' ? 389 : 636));
  295. // Reassign
  296. $ldapHosts[] = $host;
  297. $ldapServersNew[$key] = $scheme . '://' . $host . ':' . $port; // May use this later
  298. if ($i == 0) {
  299. $ldapPort = $port;
  300. }
  301. $i++;
  302. }
  303. $config = [
  304. // Mandatory Configuration Options
  305. 'hosts' => $ldapHosts,
  306. 'base_dn' => $GLOBALS['authBaseDN'],
  307. 'username' => (empty($GLOBALS['ldapBindUsername'])) ? null : $GLOBALS['ldapBindUsername'],
  308. 'password' => (empty($GLOBALS['ldapBindPassword'])) ? null : decrypt($GLOBALS['ldapBindPassword']),
  309. // Optional Configuration Options
  310. 'schema' => (($GLOBALS['ldapType'] == '1') ? Adldap\Schemas\ActiveDirectory::class : (($GLOBALS['ldapType'] == '2') ? Adldap\Schemas\OpenLDAP::class : Adldap\Schemas\FreeIPA::class)),
  311. 'account_prefix' => (empty($GLOBALS['authBackendHostPrefix'])) ? null : $GLOBALS['authBackendHostPrefix'],
  312. 'account_suffix' => (empty($GLOBALS['authBackendHostSuffix'])) ? null : $GLOBALS['authBackendHostSuffix'],
  313. 'port' => $ldapPort,
  314. 'follow_referrals' => false,
  315. 'use_ssl' => $GLOBALS['ldapSSL'],
  316. 'use_tls' => $GLOBALS['ldapTLS'],
  317. 'version' => 3,
  318. 'timeout' => 5,
  319. // Custom LDAP Options
  320. 'custom_options' => [
  321. // See: http://php.net/ldap_set_option
  322. //LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD
  323. ]
  324. ];
  325. // Add a connection provider to Adldap.
  326. $ad->addProvider($config);
  327. try {
  328. // If a successful connection is made to your server, the provider will be returned.
  329. $provider = $ad->connect();
  330. //prettyPrint($provider);
  331. if ($provider->auth()->attempt($username, $password)) {
  332. try {
  333. // Try and get email from LDAP server
  334. $accountDN = ((empty($GLOBALS['authBackendHostPrefix'])) ? null : $GLOBALS['authBackendHostPrefix']) . $username . ((empty($GLOBALS['authBackendHostSuffix'])) ? null : $GLOBALS['authBackendHostSuffix']);
  335. $record = $provider->search()->findByDnOrFail($accountDN);
  336. $email = $record->getFirstAttribute('mail');
  337. } catch (Adldap\Models\ModelNotFoundException $e) {
  338. // Record wasn't found!
  339. $email = null;
  340. }
  341. // Passed.
  342. return array(
  343. 'email' => $email
  344. );
  345. } else {
  346. // Failed.
  347. return false;
  348. }
  349. } catch (\Adldap\Auth\BindException $e) {
  350. writeLog('error', 'LDAP Function - Error: ' . $e->getMessage(), $username);
  351. // There was an issue binding / connecting to the server.
  352. } catch (Adldap\Auth\UsernameRequiredException $e) {
  353. writeLog('error', 'LDAP Function - Error: ' . $e->getMessage(), $username);
  354. // The user didn't supply a username.
  355. } catch (Adldap\Auth\PasswordRequiredException $e) {
  356. writeLog('error', 'LDAP Function - Error: ' . $e->getMessage(), $username);
  357. // The user didn't supply a password.
  358. }
  359. }
  360. return false;
  361. }
  362. } else {
  363. // Ldap Auth Missing Dependency
  364. function plugin_auth_ldap_disabled()
  365. {
  366. return 'LDAP - Disabled (Dependency: php-ldap missing!)';
  367. }
  368. }
  369. // Pass credentials to FTP backend
  370. function plugin_auth_ftp($username, $password)
  371. {
  372. // Calculate parts
  373. $digest = parse_url($GLOBALS['authBackendHost']);
  374. $scheme = strtolower((isset($digest['scheme']) ? $digest['scheme'] : (function_exists('ftp_ssl_connect') ? 'ftps' : 'ftp')));
  375. $host = (isset($digest['host']) ? $digest['host'] : (isset($digest['path']) ? $digest['path'] : ''));
  376. $port = (isset($digest['port']) ? $digest['port'] : 21);
  377. // Determine Connection Type
  378. if ($scheme == 'ftps') {
  379. $conn_id = ftp_ssl_connect($host, $port, 20);
  380. } elseif ($scheme == 'ftp') {
  381. $conn_id = ftp_connect($host, $port, 20);
  382. } else {
  383. return false;
  384. }
  385. // Check if valid FTP connection
  386. if ($conn_id) {
  387. // Attempt login
  388. @$login_result = ftp_login($conn_id, $username, $password);
  389. ftp_close($conn_id);
  390. // Return Result
  391. if ($login_result) {
  392. return true;
  393. } else {
  394. return false;
  395. }
  396. } else {
  397. return false;
  398. }
  399. }
  400. // Pass credentials to Emby Backend
  401. function plugin_auth_emby_local($username, $password)
  402. {
  403. try {
  404. $url = qualifyURL($GLOBALS['embyURL']) . '/Users/AuthenticateByName';
  405. $headers = array(
  406. 'Authorization' => 'Emby UserId="e8837bc1-ad67-520e-8cd2-f629e3155721", Client="None", Device="Organizr", DeviceId="xxx", Version="1.0.0.0"',
  407. 'Content-Type' => 'application/json',
  408. );
  409. $data = array(
  410. 'Username' => $username,
  411. 'pw' => $password,
  412. 'Password' => sha1($password),
  413. 'PasswordMd5' => md5($password),
  414. );
  415. $response = Requests::post($url, $headers, json_encode($data));
  416. if ($response->success) {
  417. $json = json_decode($response->body, true);
  418. if (is_array($json) && isset($json['SessionInfo']) && isset($json['User']) && $json['User']['HasPassword'] == true) {
  419. // Login Success - Now Logout Emby Session As We No Longer Need It
  420. $headers = array(
  421. 'X-Emby-Token' => $json['AccessToken'],
  422. 'X-Mediabrowser-Token' => $json['AccessToken'],
  423. );
  424. $response = Requests::post(qualifyURL($GLOBALS['embyURL']) . '/Sessions/Logout', $headers, array());
  425. if ($response->success) {
  426. return true;
  427. }
  428. }
  429. }
  430. return false;
  431. } catch (Requests_Exception $e) {
  432. writeLog('error', 'Emby Local Auth Function - Error: ' . $e->getMessage(), $username);
  433. }
  434. return false;
  435. }
  436. // Pass credentials to JellyFin Backend
  437. function plugin_auth_jellyfin($username, $password)
  438. {
  439. try {
  440. $url = qualifyURL($GLOBALS['embyURL']) . '/Users/authenticatebyname';
  441. $headers = array(
  442. 'X-Emby-Authorization' => 'MediaBrowser Client="Organizr Auth", Device="Organizr", DeviceId="orgv2", Version="2.0"',
  443. 'Content-Type' => 'application/json',
  444. );
  445. $data = array(
  446. 'Username' => $username,
  447. 'Pw' => $password
  448. );
  449. $response = Requests::post($url, $headers, json_encode($data));
  450. if ($response->success) {
  451. $json = json_decode($response->body, true);
  452. if (is_array($json) && isset($json['SessionInfo']) && isset($json['User']) && $json['User']['HasPassword'] == true) {
  453. writeLog('success', 'JellyFin Auth Function - Found User and Logged In', $username);
  454. // Login Success - Now Logout JellyFin Session As We No Longer Need It
  455. $headers = array(
  456. 'X-Emby-Authorization' => 'MediaBrowser Client="Organizr Auth", Device="Organizr", DeviceId="orgv2", Version="2.0", Token="' . $json['AccessToken'] . '"',
  457. 'Content-Type' => 'application/json',
  458. );
  459. $response = Requests::post(qualifyURL($GLOBALS['embyURL']) . '/Sessions/Logout', $headers, array());
  460. if ($response->success) {
  461. return true;
  462. }
  463. }
  464. }
  465. return false;
  466. } catch (Requests_Exception $e) {
  467. writeLog('error', 'JellyFin Auth Function - Error: ' . $e->getMessage(), $username);
  468. }
  469. return false;
  470. }
  471. // Authenticate against emby connect
  472. function plugin_auth_emby_connect($username, $password)
  473. {
  474. // Emby disabled EmbyConnect on their API
  475. // https://github.com/MediaBrowser/Emby/issues/3553
  476. //return plugin_auth_emby_local($username, $password);
  477. try {
  478. // Get A User
  479. $connectUserName = '';
  480. $url = qualifyURL($GLOBALS['embyURL']) . '/Users?api_key=' . $GLOBALS['embyToken'];
  481. $response = Requests::get($url);
  482. if ($response->success) {
  483. $json = json_decode($response->body, true);
  484. if (is_array($json)) {
  485. foreach ($json as $key => $value) { // Scan for this user
  486. if (isset($value['ConnectUserName']) && isset($value['ConnectLinkType'])) { // Qualify as connect account
  487. if (strtolower($value['ConnectUserName']) == $username || strtolower($value['Name']) == $username) {
  488. $connectUserName = $value['ConnectUserName'];
  489. writeLog('success', 'Emby Connect Auth Function - Found User', $username);
  490. break;
  491. }
  492. }
  493. }
  494. if ($connectUserName) {
  495. writeLog('success', 'Emby Connect Auth Function - Attempting to Login with Emby ID: ' . $connectUserName, $username);
  496. $connectURL = 'https://connect.emby.media/service/user/authenticate';
  497. $headers = array(
  498. 'Accept' => 'application/json',
  499. 'X-Application' => 'Organizr/2.0'
  500. );
  501. $data = array(
  502. 'nameOrEmail' => $username,
  503. 'rawpw' => $password,
  504. );
  505. $response = Requests::post($connectURL, $headers, $data);
  506. if ($response->success) {
  507. $json = json_decode($response->body, true);
  508. if (is_array($json) && isset($json['AccessToken']) && isset($json['User']) && $json['User']['Name'] == $connectUserName) {
  509. return array(
  510. 'email' => $json['User']['Email'],
  511. //'image' => $json['User']['ImageUrl'],
  512. );
  513. } else {
  514. writeLog('error', 'Emby Connect Auth Function - Bad Response', $username);
  515. }
  516. } else {
  517. writeLog('error', 'Emby Connect Auth Function - 401 From Emby Connect', $username);
  518. }
  519. }
  520. }
  521. }
  522. return false;
  523. } catch (Requests_Exception $e) {
  524. writeLog('error', 'Emby Connect Auth Function - Error: ' . $e->getMessage(), $username);
  525. return false;
  526. }
  527. }
  528. // Authenticate Against Emby Local (first) and Emby Connect
  529. function plugin_auth_emby_all($username, $password)
  530. {
  531. // Emby disabled EmbyConnect on their API
  532. // https://github.com/MediaBrowser/Emby/issues/3553
  533. $localResult = plugin_auth_emby_local($username, $password);
  534. //return $localResult;
  535. if ($localResult) {
  536. return $localResult;
  537. } else {
  538. return plugin_auth_emby_connect($username, $password);
  539. }
  540. }