ILIAS  release_7 Revision v7.30-3-g800a261c036
class.ilLDAPQuery.php
Go to the documentation of this file.
1<?php
2/*
3 +-----------------------------------------------------------------------------+
4 | ILIAS open source |
5 +-----------------------------------------------------------------------------+
6 | Copyright (c) 1998-2006 ILIAS open source, University of Cologne |
7 | |
8 | This program is free software; you can redistribute it and/or |
9 | modify it under the terms of the GNU General Public License |
10 | as published by the Free Software Foundation; either version 2 |
11 | of the License, or (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program; if not, write to the Free Software |
20 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21 +-----------------------------------------------------------------------------+
22*/
23
24define('IL_LDAP_BIND_DEFAULT', 0);
25define('IL_LDAP_BIND_ADMIN', 1);
26define('IL_LDAP_BIND_TEST', 2);
27define('IL_LDAP_BIND_AUTH', 10);
28
39{
40 public const LDAP_BIND_DEFAULT = 0;
41 public const LDAP_BIND_ADMIN = 1;
42 public const LDAP_BIND_TEST = 2;
43 public const LDAP_BIND_AUTH = 10;
44
49 const IL_LDAP_CONTROL_PAGEDRESULTS = '1.2.840.113556.1.4.319';
50
54 const IL_LDAP_SUPPORTED_CONTROL = 'supportedControl';
55
59 const PAGINATION_SIZE = 100;
60
61 private $ldap_server_url = null;
62 private $settings = null;
63
67 private $log = null;
68
69 private $user_fields = array();
70
75 private $lh;
76
85 public function __construct(ilLDAPServer $a_server, $a_url = '')
86 {
87 $this->settings = $a_server;
88
89 if (strlen($a_url)) {
90 $this->ldap_server_url = $a_url;
91 } else {
92 $this->ldap_server_url = $this->settings->getUrl();
93 }
94
95 $this->mapping = ilLDAPAttributeMapping::_getInstanceByServerId($this->settings->getServerId());
96 $this->log = $GLOBALS['DIC']->logger()->auth();
97
99 $this->connect();
100 }
101
102 // begin-patch ldap_multiple
107 public function getServer()
108 {
109 return $this->settings;
110 }
111
116 public function getLogger()
117 {
118 return $this->log;
119 }
120
128 public function fetchUser($a_name)
129 {
130 if (!$this->readUserData($a_name)) {
131 return array();
132 } else {
133 return $this->users;
134 }
135 }
136
137
144 public function fetchUsers()
145 {
146 // First of all check if a group restriction is enabled
147 // YES: => fetch all group members
148 // No: => fetch all users
149 if (strlen($this->settings->getGroupName())) {
150 $this->log->debug('Searching for group members.');
151
152 $groups = $this->settings->getGroupNames();
153 if (count($groups) <= 1) {
154 $this->fetchGroupMembers();
155 } else {
156 foreach ($groups as $group) {
157 $this->fetchGroupMembers($group);
158 }
159 }
160 }
161 if (!strlen($this->settings->getGroupName()) or $this->settings->isMembershipOptional()) {
162 $this->log->info('Start reading all users...');
163 $this->readAllUsers();
164 #throw new ilLDAPQueryException('LDAP: Called import of users without specifying group restrictions. NOT IMPLEMENTED YET!');
165 }
166 return $this->users ? $this->users : array();
167 }
168
180 public function query($a_search_base, $a_filter, $a_scope, $a_attributes)
181 {
182 $res = $this->queryByScope($a_scope, $a_search_base, $a_filter, $a_attributes);
183 if ($res === false) {
184 throw new ilLDAPQueryException(__METHOD__ . ' ' . ldap_error($this->lh) . ' ' .
185 sprintf(
186 'DN: %s, Filter: %s, Scope: %s',
187 $a_search_base,
188 $a_filter,
189 $a_scope
190 ));
191 }
192 return (new ilLDAPResult($this->lh, $res))->run();
193 }
194
201 public function modAdd($a_dn, $a_attribute)
202 {
203 if (@ldap_mod_add($this->lh, $a_dn, $a_attribute)) {
204 return true;
205 }
206 throw new ilLDAPQueryException(__METHOD__ . ' ' . ldap_error($this->lh));
207 }
208
215 public function modDelete($a_dn, $a_attribute)
216 {
217 if (@ldap_mod_del($this->lh, $a_dn, $a_attribute)) {
218 return true;
219 }
220 throw new ilLDAPQueryException(__METHOD__ . ' ' . ldap_error($this->lh));
221 }
222
231 private function readAllUsers()
232 {
233 // Build search base
234 if (($dn = $this->settings->getSearchBase()) && substr($dn, -1) != ',') {
235 $dn .= ',';
236 }
237 $dn .= $this->settings->getBaseDN();
238 $tmp_result = null;
239
240 if ($this->checkPaginationEnabled()) {
241 try {
242 $tmp_result = $this->runReadAllUsersPaged($dn);
243 } catch (ilLDAPPagingException $e) {
244 $this->log->warning('Using LDAP with paging failed. Trying to use fallback.');
245 $tmp_result = $this->runReadAllUsersPartial($dn);
246 }
247 } else {
248 $tmp_result = $this->runReadAllUsersPartial($dn);
249 }
250
251 if (!$tmp_result->numRows()) {
252 $this->log->notice('No users found. Aborting.');
253 }
254 $this->log->info('Found ' . $tmp_result->numRows() . ' users.');
255 $attribute = strtolower($this->settings->getUserAttribute());
256 foreach ($tmp_result->getRows() as $data) {
257 if (isset($data[$attribute])) {
258 $this->readUserData($data[$attribute], false, false);
259 } else {
260 $this->log->warning('Unknown error. No user attribute found.');
261 }
262 }
263 unset($tmp_result);
264
265 return true;
266 }
267
275 private function runReadAllUsersPaged($dn)
276 {
277 $filter = '(&' . $this->settings->getFilter();
278 $filter .= ('(' . $this->settings->getUserAttribute() . '=*))');
279 $this->log->info('Searching with ldap search and filter ' . $filter . ' in ' . $dn);
280
281 $tmp_result = new ilLDAPResult($this->lh);
282 $cookie = '';
283 $estimated_results = 0;
284 do {
285 try {
286 $res = ldap_control_paged_result($this->lh, self::PAGINATION_SIZE, true, $cookie);
287 if ($res === false) {
288 throw new ilLDAPPagingException('Result pagination failed.');
289 }
290
291 } catch (Exception $e) {
292 $this->log->warning('Result pagination failed with message: ' . $e->getMessage());
293 throw new ilLDAPPagingException($e->getMessage());
294 }
295
296 $res = $this->queryByScope(
297 $this->settings->getUserScope(),
298 $dn,
299 $filter,
300 array($this->settings->getUserAttribute())
301 );
302 $tmp_result->setResult($res);
303 $tmp_result->run();
304 try {
305 ldap_control_paged_result_response($this->lh, $res, $cookie, $estimated_results);
306 $this->log->debug('Estimated number of results: ' . $estimated_results);
307 } catch (Exception $e) {
308 $this->log->warning('Result pagination failed with message: ' . $e->getMessage());
309 throw new ilLDAPPagingException($e->getMessage());
310 }
311 } while ($cookie !== null && $cookie != '');
312
313 // finally reset cookie
314 ldap_control_paged_result($this->lh, 10000, false, $cookie);
315 return $tmp_result;
316 }
317
324 private function runReadAllUsersPartial($dn)
325 {
326 $filter = $this->settings->getFilter();
327 $page_filter = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','-');
328 $chars = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');
329 $tmp_result = new ilLDAPResult($this->lh);
330
331 foreach ($page_filter as $letter) {
332 $new_filter = '(&';
333 $new_filter .= $filter;
334
335 switch ($letter) {
336 case '-':
337 $new_filter .= ('(!(|');
338 foreach ($chars as $char) {
339 $new_filter .= ('(' . $this->settings->getUserAttribute() . '=' . $char . '*)');
340 }
341 $new_filter .= ')))';
342 break;
343
344 default:
345 $new_filter .= ('(' . $this->settings->getUserAttribute() . '=' . $letter . '*))');
346 break;
347 }
348
349 $this->log->info('Searching with ldap search and filter ' . $new_filter . ' in ' . $dn);
350 $res = $this->queryByScope(
351 $this->settings->getUserScope(),
352 $dn,
353 $new_filter,
354 array($this->settings->getUserAttribute())
355 );
356 $tmp_result->setResult($res);
357 $tmp_result->run();
358 }
359
360 return $tmp_result;
361 }
362
369 public function checkGroupMembership($a_ldap_user_name, $ldap_user_data)
370 {
371 $group_names = $this->getServer()->getGroupNames();
372
373 if (!count($group_names)) {
374 $this->getLogger()->debug('No LDAP group restrictions found');
375 return true;
376 }
377
378 $group_dn = $this->getServer()->getGroupDN();
379 if (
380 $group_dn &&
381 (substr($group_dn, -1) != ',')
382 ) {
383 $group_dn .= ',';
384 }
385 $group_dn .= $this->getServer()->getBaseDN();
386
387 foreach ($group_names as $group) {
388 $user = $a_ldap_user_name;
389 if ($this->getServer()->enabledGroupMemberIsDN()) {
390 if ($this->getServer()->enabledEscapeDN()) {
391 $user = ldap_escape($ldap_user_data['dn'], "", LDAP_ESCAPE_FILTER);
392 } else {
393 $user = $ldap_user_data['dn'];
394 }
395 }
396
397 $filter = sprintf(
398 '(&(%s=%s)(%s=%s)%s)',
399 $this->getServer()->getGroupAttribute(),
400 $group,
401 $this->getServer()->getGroupMember(),
402 $user,
403 $this->getServer()->getGroupFilter()
404 );
405 $this->getLogger()->debug('Current group search base: ' . $group_dn);
406 $this->getLogger()->debug('Current group filter: ' . $filter);
407
408 $res = $this->queryByScope(
409 $this->getServer()->getGroupScope(),
410 $group_dn,
411 $filter,
412 [$this->getServer()->getGroupMember()]
413 );
414
415 $this->getLogger()->dump($res);
416
417 $tmp_result = new ilLDAPResult($this->lh, $res);
418 $tmp_result->run();
419 $group_result = $tmp_result->getRows();
420
421 $this->getLogger()->debug('Group query returned: ');
422 $this->getLogger()->dump($group_result, ilLogLevel::DEBUG);
423
424 if (count($group_result)) {
425 return true;
426 }
427 }
428
429 // group restrictions failed check optional membership
430 if ($this->getServer()->isMembershipOptional()) {
431 $this->getLogger()->debug('Group restrictions failed, checking user filter.');
432 if ($this->readUserData($a_ldap_user_name, true, true)) {
433 $this->getLogger()->debug('User filter matches.');
434 return true;
435 }
436 }
437 $this->getLogger()->debug('Group restrictions failed.');
438 return false;
439 }
440
441
448 private function fetchGroupMembers($a_name = '')
449 {
450 $group_name = strlen($a_name) ? $a_name : $this->settings->getGroupName();
451
452 // Build filter
453 $filter = sprintf(
454 '(&(%s=%s)%s)',
455 $this->settings->getGroupAttribute(),
456 $group_name,
457 $this->settings->getGroupFilter()
458 );
459
460
461 // Build search base
462 if (($gdn = $this->settings->getGroupDN()) && substr($gdn, -1) != ',') {
463 $gdn .= ',';
464 }
465 $gdn .= $this->settings->getBaseDN();
466
467 $this->log->debug('Using filter ' . $filter);
468 $this->log->debug('Using DN ' . $gdn);
469 $res = $this->queryByScope(
470 $this->settings->getGroupScope(),
471 $gdn,
472 $filter,
473 array($this->settings->getGroupMember())
474 );
475
476 $tmp_result = new ilLDAPResult($this->lh, $res);
477 $tmp_result->run();
478 $group_data = $tmp_result->getRows();
479
480
481 if (!$tmp_result->numRows()) {
482 $this->log->info('No group found.');
483 return false;
484 }
485
486 $attribute_name = strtolower($this->settings->getGroupMember());
487
488 // All groups
489 foreach ($group_data as $data) {
490 if (is_array($data[$attribute_name])) {
491 $this->log->debug('Found ' . count($data[$attribute_name]) . ' group members for group ' . $data['dn']);
492 foreach ($data[$attribute_name] as $name) {
493 $this->readUserData($name, true, true);
494 }
495 } else {
496 $this->readUserData($data[$attribute_name], true, true);
497 }
498 }
499 unset($tmp_result);
500 return;
501 }
502
509 private function readUserData($a_name, $a_check_dn = false, $a_try_group_user_filter = false)
510 {
511 $filter = $this->settings->getFilter();
512 if ($a_try_group_user_filter) {
513 if ($this->settings->isMembershipOptional()) {
514 $filter = $this->settings->getGroupUserFilter();
515 }
516 }
517
518 // Build filter
519 if ($this->settings->enabledGroupMemberIsDN() and $a_check_dn) {
520 $dn = $a_name;
521 #$res = $this->queryByScope(IL_LDAP_SCOPE_BASE,$dn,$filter,$this->user_fields);
522
523 $fields = array_merge($this->user_fields, array('useraccountcontrol'));
524 $res = $this->queryByScope(IL_LDAP_SCOPE_BASE, strtolower($dn), $filter, $fields);
525 } else {
526 $filter = sprintf(
527 '(&(%s=%s)%s)',
528 $this->settings->getUserAttribute(),
529 $a_name,
530 $filter
531 );
532
533 // Build search base
534 if (($dn = $this->settings->getSearchBase()) && substr($dn, -1) != ',') {
535 $dn .= ',';
536 }
537 $dn .= $this->settings->getBaseDN();
538 $fields = array_merge($this->user_fields, array('useraccountcontrol'));
539 $res = $this->queryByScope($this->settings->getUserScope(), strtolower($dn), $filter, $fields);
540 }
541
542
543 $tmp_result = new ilLDAPResult($this->lh, $res);
544 $tmp_result->run();
545 if (!$tmp_result->numRows()) {
546 $this->log->info('LDAP: No user data found for: ' . $a_name);
547 unset($tmp_result);
548 return false;
549 }
550
551 if ($user_data = $tmp_result->get()) {
552 if (isset($user_data['useraccountcontrol'])) {
553 if (($user_data['useraccountcontrol'] & 0x02)) {
554 $this->log->notice('LDAP: ' . $a_name . ' account disabled.');
555 return;
556 }
557 }
558
559 $account = $user_data[strtolower($this->settings->getUserAttribute())];
560 if (is_array($account)) {
561 $user_ext = strtolower(array_shift($account));
562 } else {
563 $user_ext = strtolower($account);
564 }
565
566 // auth mode depends on ldap server settings
567 $auth_mode = $this->settings->getAuthenticationMappingKey();
568 $user_data['ilInternalAccount'] = ilObjUser::_checkExternalAuthAccount($auth_mode, $user_ext);
569 $this->users[$user_ext] = $user_data;
570 }
571 return true;
572 }
573
578 private function parseAuthMode()
579 {
580 return $this->settings->getAuthenticationMappingKey();
581 }
582
592 private function queryByScope($a_scope, $a_base_dn, $a_filter, $a_attributes)
593 {
594 $a_filter = $a_filter ? $a_filter : "(objectclass=*)";
595
596 switch ($a_scope) {
598 $res = @ldap_search($this->lh, $a_base_dn, $a_filter, $a_attributes);
599 break;
600
602 $res = @ldap_list($this->lh, $a_base_dn, $a_filter, $a_attributes);
603 break;
604
606
607 $res = @ldap_read($this->lh, $a_base_dn, $a_filter, $a_attributes);
608 break;
609
610 default:
611 $this->log->warning("LDAP: LDAPQuery: Unknown search scope");
612 }
613
614 $error = ldap_error($this->lh);
615 if (strcmp('Success', $error) !== 0) {
616 $this->getLogger()->warning($error);
617 $this->getLogger()->warning('Base DN:' . $a_base_dn);
618 $this->getLogger()->warning('Filter: ' . $a_filter);
619 }
620
621 return $res;
622 }
623
631 private function connect()
632 {
633 $this->lh = @ldap_connect($this->ldap_server_url);
634
635 // LDAP Connect
636 if (!$this->lh) {
637 throw new ilLDAPQueryException("LDAP: Cannot connect to LDAP Server: " . $this->settings->getUrl());
638 }
639 // LDAP Version
640 if (!ldap_set_option($this->lh, LDAP_OPT_PROTOCOL_VERSION, $this->settings->getVersion())) {
641 throw new ilLDAPQueryException("LDAP: Cannot set version to: " . $this->settings->getVersion());
642 }
643 // Switch on referrals
644 if ($this->settings->isActiveReferrer()) {
645 if (!ldap_set_option($this->lh, LDAP_OPT_REFERRALS, true)) {
646 throw new ilLDAPQueryException("LDAP: Cannot switch on LDAP referrals");
647 }
648 #@ldap_set_rebind_proc($this->lh,'referralRebind');
649 } else {
650 ldap_set_option($this->lh, LDAP_OPT_REFERRALS, false);
651 $this->log->debug('Switching referrals to false.');
652 }
653 // Start TLS
654 if ($this->settings->isActiveTLS()) {
655 if (!ldap_start_tls($this->lh)) {
656 throw new ilLDAPQueryException("LDAP: Cannot start LDAP TLS");
657 }
658 }
659 }
660
669 public function bind($a_binding_type = IL_LDAP_BIND_DEFAULT, $a_user_dn = '', $a_password = '')
670 {
671 switch ($a_binding_type) {
673 ldap_set_option($this->lh, LDAP_OPT_NETWORK_TIMEOUT, ilLDAPServer::DEFAULT_NETWORK_TIMEOUT);
674 // fall through
675 // no break
677 // Now bind anonymously or as user
678 if (
679 IL_LDAP_BIND_USER == $this->settings->getBindingType() &&
680 strlen($this->settings->getBindUser())
681 ) {
682 $user = $this->settings->getBindUser();
683 $pass = $this->settings->getBindPassword();
684
685 define('IL_LDAP_REBIND_USER', $user);
686 define('IL_LDAP_REBIND_PASS', $pass);
687 $this->log->debug('Bind as ' . $user);
688 } else {
689 $user = $pass = '';
690 $this->log->debug('Bind anonymous');
691 }
692 break;
693
695 $user = $this->settings->getRoleBindDN();
696 $pass = $this->settings->getRoleBindPassword();
697
698 if (!strlen($user) or !strlen($pass)) {
699 $user = $this->settings->getBindUser();
700 $pass = $this->settings->getBindPassword();
701 }
702
703 define('IL_LDAP_REBIND_USER', $user);
704 define('IL_LDAP_REBIND_PASS', $pass);
705 break;
706
708 $this->log->debug('Trying to bind as: ' . $a_user_dn);
709 $user = $a_user_dn;
710 $pass = $a_password;
711 break;
712
713
714 default:
715 throw new ilLDAPQueryException('LDAP: unknown binding type in: ' . __METHOD__);
716 }
717
718 if (!@ldap_bind($this->lh, $user, $pass)) {
719 throw new ilLDAPQueryException('LDAP: Cannot bind as ' . $user . ' with message: ' . ldap_err2str(ldap_errno($this->lh)) . ' Trying fallback...', ldap_errno($this->lh));
720 } else {
721 $this->log->debug('Bind successful.');
722 }
723 }
724
732 private function fetchUserProfileFields()
733 {
734 include_once('Services/LDAP/classes/class.ilLDAPRoleAssignmentRules.php');
735
736 $this->user_fields = array_merge(
737 array($this->settings->getUserAttribute()),
738 array('dn'),
739 $this->mapping->getFields(),
740 ilLDAPRoleAssignmentRules::getAttributeNames($this->getServer()->getServerId())
741 );
742 }
743
744
752 private function unbind()
753 {
754 if ($this->lh) {
755 @ldap_unbind($this->lh);
756 }
757 }
758
759
767 public function __destruct()
768 {
769 if ($this->lh) {
770 @ldap_unbind($this->lh);
771 }
772 }
773
778 public function checkPaginationEnabled() : bool
779 {
780 if ($this->getServer()->getVersion() != 3) {
781 $this->log->info('Pagination control unavailable for ldap v' . $this->getServer()->getVersion());
782 return false;
783 }
784
785 $result = ldap_read($this->lh, '', '(objectClass=*)', [self::IL_LDAP_SUPPORTED_CONTROL]);
786 if ($result === false) {
787 $this->log->warning('Failed to query for pagination control');
788 return false;
789 }
790 $entries = (array) (ldap_get_entries($this->lh, $result)[0] ?? []);
791 if (
792 array_key_exists(strtolower(self::IL_LDAP_SUPPORTED_CONTROL), $entries) &&
793 is_array($entries[strtolower(self::IL_LDAP_SUPPORTED_CONTROL)]) &&
794 in_array(self::IL_LDAP_CONTROL_PAGEDRESULTS, $entries[strtolower(self::IL_LDAP_SUPPORTED_CONTROL)])
795 ) {
796 $this->log->info('Using paged control');
797 return true;
798 }
799 $this->log->info('Paged control disabled');
800 return false;
801 }
802}
803
804function referralRebind($a_ds, $a_url)
805{
806 global $DIC;
807
808 $ilLog = $DIC['ilLog'];
809
810 $ilLog->write('LDAP: Called referralRebind.');
811
812 ldap_set_option($a_ds, LDAP_OPT_PROTOCOL_VERSION, 3);
813
814 if (!ldap_bind($a_ds, IL_LDAP_REBIND_USER, IL_LDAP_REBIND_PASS)) {
815 $ilLog->write('LDAP: Rebind failed');
816 }
817}
$result
if(!defined('PATH_SEPARATOR')) $GLOBALS['_PEAR_default_error_mode']
Definition: PEAR.php:64
An exception for terminatinating execution or to throw for unit testing.
const IL_LDAP_BIND_AUTH
const IL_LDAP_BIND_DEFAULT
referralRebind($a_ds, $a_url)
const IL_LDAP_BIND_ADMIN
const IL_LDAP_BIND_TEST
const IL_LDAP_BIND_USER
const IL_LDAP_SCOPE_BASE
const IL_LDAP_SCOPE_SUB
const IL_LDAP_SCOPE_ONE
static _getInstanceByServerId($a_server_id)
Get instance of class.
Class ilLDAPPagingException.
const IL_LDAP_SUPPORTED_CONTROL
const IL_LDAP_CONTROL_PAGEDRESULTS
modAdd($a_dn, $a_attribute)
Add value to an existing attribute.
fetchUsers()
Fetch all users.
queryByScope($a_scope, $a_base_dn, $a_filter, $a_attributes)
Query by scope IL_SCOPE_SUB => ldap_search IL_SCOPE_ONE => ldap_list.
fetchGroupMembers($a_name='')
Fetch group member ids.
fetchUserProfileFields()
fetch required fields of user profile data
getServer()
Get server.
checkPaginationEnabled()
Check if pagination is enabled (rfc: 2696)
bind($a_binding_type=IL_LDAP_BIND_DEFAULT, $a_user_dn='', $a_password='')
Bind to LDAP server.
connect()
Connect to LDAP server.
parseAuthMode()
Parse authentication mode.
runReadAllUsersPartial($dn)
read all users partial by alphabet
readUserData($a_name, $a_check_dn=false, $a_try_group_user_filter=false)
Read user data.
__destruct()
Destructor unbind from ldap server.
getLogger()
Get logger.
readAllUsers()
Fetch all users This function splits the query to filters like e.g (uid=a*) (uid=b*)....
__construct(ilLDAPServer $a_server, $a_url='')
Constructur.
fetchUser($a_name)
Get one user by login name.
checkGroupMembership($a_ldap_user_name, $ldap_user_data)
check group membership
query($a_search_base, $a_filter, $a_scope, $a_attributes)
Perform a query.
runReadAllUsersPaged($dn)
read all users with ldap paging
modDelete($a_dn, $a_attribute)
Delete value from an existing attribute.
Class ilLDAPPagedResult.
static getAttributeNames($a_server_id)
get all possible attribute names
const DEFAULT_NETWORK_TIMEOUT
static _checkExternalAuthAccount($a_auth, $a_account, $tryFallback=true)
check whether external account and authentication method matches with a user
global $DIC
Definition: goto.php:24
if($format !==null) $name
Definition: metadata.php:230
foreach($_POST as $key=> $value) $res
settings()
Definition: settings.php:2
$data
Definition: storeScorm.php:23