4
0

auth-functions.php 20 KB

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